I work on a few projects right now, some using the Android code-style and some their own. This is very confusing, and always causes problems when pushing a commit for merge.
I’m trying to consolidate all the projects into Android’s code-style. Specifically, no-tabs and 4 spaces indents.
Game Plan
Going over all the files and changing the tabs to spaces is, of course, not an options. More over, we do not want our name on every single git blame
code line! So, we’ll need to convert all the tabs to spaces, ensure all indentation is 4 spaces, and re-write history to hide it.
Prerequisites
- Linux or Mac OSX machine. If you know how to do it under Windows, please leave a comment.
- You’ll need
git push -f
permission to the remote repo. That’s the only way to re-write history. expand
should be installed on you machine
Switching Tabs to 4 Spaces
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
Here’s what we are doing with that command:
- find all the Java files in the repo.
- On each file, execute
expand
(usingbash
) which will convert tab characters to 4 spaces, and save into a temporary file. - The move the temporary file to overwrite the original file.
- If you also want to convert your XML files to spaces, then run the same command with
*.xml
filter.
Commit the Changes
git add .
git commit -m "converting tabs to spaces"
Now, we have a commit in our local git repo that holds all the changes. Let’s get the hash for the commit using git rev-parse HEAD
. For our example, let’s say it is 0867e54002195d16dbfb69076d1df2d8b1f83e6f
.
Re-write git History
git filter-branch --tree-filter 'git diff-tree --name-only --diff-filter=AM -r --no-commit-id 0867e54002195d16dbfb69076d1df2d8b1f83e6f' HEAD
This is the cool part: hiding all the evidence that you made the switch. This will ensure that when developers check git blame
on a file, the tabs->spaces changes will not show.
This takes a very long time to run. The bigger the repo the longer it will take.
Force push to master
git push -f origin master
Pushing to the remote repo (be it origin or upstream).
Possible Problems
filter-branch
creates a new, revised, branch in your git repo. This means a few things:- the size of your repo may be doubled!
- hash of the commits in your history will be changed! This means that if you have a link to a specific commit, this link might be dead after this process. The reason for that is that if git sees that nothing is pointing to a specific commit, it will garbage collect it.
A simple way to fix that, is to usetag
, and annotate the commit prior to starting this process. - Applying the
filter-branch
may fail (“Cannot create a new backup.”) because of an existing backup in your refs folder. Delete those withrm -rf .git/refs/original/refs/heads/master
from the root of your repo. - If you get
! [rejected] master -> master (non-fast-forward)
when pushing the changes to your git repo, it means that you do not have force push permissions in the git repo. Talk to the owner. - Previous tags are kept, and are pointing to the original commit, not the revised one. So we are not completely rewriting history.