There are very few Q&A's on git grafts
versus replace
. The search [git] +grafts +replace only found two that felt relevant of the 5. what-are-git-info-grafts-for and git-what-is-a-graftcommit-or-a-graft-id. There is also a note on git.wiki.kernel.org:GraftPoint
Are grafts now completely overtaken by the replace
and filter-branch
, or do they still needed for some special corner cases (and backward compatibility) ?
In general, how do they differ (e.g. which are transported between repos), and how are they generically the same? I've seen that Linus doesn't appear to care about grafts at present in the discussion on commit generation numbers (of the max parents back to any root variety) "Grafts are already unreliable."
EDIT: more info found.
A search of www.kernel.org/pub/software/scm/git/docs for graft
only found 3 results:
- git-filter-branch(1),
- v1.5.4.7/git-filter-branch(1),
- v1.5.0.7/git-svn(1).
A slightly broader search found RelNotes/1.6.5.txt which contains:
- refs/replace/ hierarchy is designed to be usable as a replacement of the "grafts" mechanism, with the added advantage that it can be transferred across repositories.
Unfortunately, the gitrepository-layout(5) isn't yet up to date with the refs/replace/ repository layout info (and notes), nor any deprecation note of info/grafts.
This gets closer to supporting what I was thinking but I'd welcome any confirmation or clarification.
EDIT:
git replace --graft <commit> [<parent>…]
does the same thing as grafts and it can add or remove parents. The documentation says:(I'm leaving the old answer below as a reference.)
AFAIK, there is one use case that
grafts
can handle butreplace
cannot: adding or removing parents. It's a power tool for refactoring histories.For example, if you're importing the history from an old SVN repository into Git, there is no merge information. What you can do (and I've done it lots of times) is to read through the commit messages to find out where a SVN "merge" was done, and then use Git grafts to add a parent to the merge commit.
IIRC, I've also had some cases where I've removed the parent of a commit, in order to make it the first commit in the history. Creating a clean history based on multiple chaotic legacy repositories sometimes requires drastic measures (there are some experiences of migrating projects to Git at my blog).
Then after you've cleaned up the whole history, you would do a
git filter-branch
before publishing the new Git repository.In the same discussion about Commit Generation Number that you mention, Jakub Narębski does confirm that grafts are more aproblem than a solution:
(publishing has always been taken care of with
git filter-branch
, as illustrated by this 2008 thread on grafts workflow.)The difference between grafts and git replace is best illustrated by this SO question "Setting git parent pointer to a different parent", and the comments of (Jakub's again) answer.
It does include the reference to Git1.6.5
If you need to rewrite a parent commit using
git replace
, this is how to do it.As Philip Oakley mentioned, git replace simply replaces one commit with another. To graft on a parent to an existing commit, you need to first create a fake commit with the correct parent.
Say you have two git branchs you want to graft:
Now we want (d) to be the parent of (c). So we create a replacement for (c) with the correct parent (let's call this c1), then
git replace
(c) with (c1). In these steps each of the letters refers to the SHA1 hash representing that commit.To create the new commit:
Now you have commit (c1) which has the correct parent (d). So all we need to do is replace the existing (c) with (c1):
Now your history looks like this:
Bingo!