Here’s an example of how to use the git filter-branch command with the –env-filter option:
git checkout mybranch
git filter-branch --env-filter "export \
GIT_AUTHOR_NAME='Your Name' \
GIT_AUTHOR_EMAIL='email@example.com' \
GIT_COMMITTER_NAME='Your Name' \
GIT_COMMITTER_EMAIL='email@example.com'" \
HEAD~4..HEAD
Notice that on line 7, the first reference (HEAD~4 in this example) needs to be the parent of the first (oldest) commit you want to change.
Verify that your changes are good by comparing the rewritten commits with the original/refs/heads/mybranch
reference that filter-branch saves. If all is good, delete the original reference:
git update-ref -d refs/original/refs/heads/mybranch
In one of my old subversion projects I did some branch merges before SVN started tracking merges with meta-data. When the project was converted to GIT, what I ended up with were some branches that seemed to lead to nowhere (they looked like they were abandoned and never merged back into the trunk). Git had no way of knowing at what point the side-branches became integrated into the trunk because in early versions of SVN there was no meta-data to track merges. I have no plans to go back to SVN, so I decided to fix this “cosmetic” issue.
Git filter-branch to the rescue. The –parent-filter allows you to rewrite the parents of a commit. All I need to do is find the trunk commit that the branches were merged into and make the side-branch tip a second parent of that commit.
git filter-branch --parent-filter \
'test $GIT_COMMIT = 9d2104a09307b44a3640ae368916edd80e966290 && \
echo "-p a3f23400f469a327310513e9a20a0d717e7bc04f \
-p 512e8f229bba27bbec1df49607e7717db760edec" \
|| cat' \
--tag-name-filter 'cat' \
a3f23400f469a327310513e9a20a0d717e7bc04f..master
Explanation: Line 2 contains the SHA1 (9d210…) of the SVN revision that is missing a parent. This is the revision into which the dangling head (512e8…) was actually merged in SVN, but which git does not show as containing 512e8.
Line 3 has the first parent (a3f23…) which was there before the filter-branch. We need to retain this parent. Line 4 has the SHA1 (512e8…) of the missing parent. Both of these will become parents of 9d210.
The cat
is there because we want to keep all commits that don’t match 9d210… unchanged.
Lastly, line 6 is there to retain the existing tags, and line 7 covers the range we want to modify (notice that this extends from the parent of 9d210 to the tip of the master branch).