evan_tech

Previous Entry Share Next Entry
Here's a quick tutorial on using git grafts to fill in implied svn merge history.

Say you're using git-svn to track a project, which has a trunk (we'll call it "trunk") and a branch (we'll call it "webkit_merge"). Suppose people have been merging from trunk into the branch using svn, which has a weak notion of history, especially pre 1.5: when you merge, you customarily note what was merged in the commit log so that subsequent mergers know what remains to be merged. But Git can do smart merges if it knows the real history of branches.

Sounds like a complicated surgical procedure, but it turns out to be surprisingly easy to fix.

Start by finding the commit where the merge happened:
% git log -1 --grep=Merge --parents webkit_merge
commit 146aeb3c1fbf68830925737929c162057db514bf 842b53b23115e944a22a72556748d2bcb9b7e4c4
Author: tc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Tue Sep 16 18:27:32 2008 +0000

Merge trunk@r2267 to branch. test_shell still runs. chrome still
has a sad tab on every new tab. test_shell crashes when trying to
login to gmail.

The biggest conflicts were in v8_proxy.cpp and v8_custom.cpp.

TBR=eseidel

So 146aeb3 is the merge commit, and 842b53b is the parent commit on the webkit_merge branch. Find the trunk commit that was merged as noted in that commit log:
% git svn find-rev r2267 trunk
039a95f78e94168487510bb961c0dd6a804b6a25

We want to inject that commit as a second parent of the above one. The grafts file is a list, one per line, of a commit followed by its parents. So cut'n'pasting from above:
echo "146aeb3c1fbf68830925737929c162057db514bf 842b53b23115e944a22a72556748d2bcb9b7e4c4 039a95f78e94168487510bb961c0dd6a804b6a25" > .git/info/grafts

And you can verify Git now knows the lineage of that commit:
% git log -1 --grep=Merge --parents --abbrev-commit webkit_merge | head -1
commit 146aeb3... 842b53b... 039a95f...

More easy is to just run gitk, which will have all the lines match up as you'd like.

So now, for example, to merge trunk you just can run git merge trunk, and Git will have enough history to be intelligent about what needs to be merged.

If you want, it appears you can also rewrite the actual branch history (since the graft is just a local change) using git filter-branch, but since these are all svn branches there's little reason to.