What are .git/info/grafts for?
From Git Wiki:
Graft points or grafts enable two otherwise different lines of development to be joined together. It works by letting users record fake ancestry information for commits. This way you can make git pretend the set of parents a commit has is different from what was recorded when the commit was created.
Reasons for Using Grafts
Grafts can be useful when moving development to git, since it allows you to make cloning of the old history imported from another SCM optional. This keeps the initial clone for users who just wants to follow the latest version down while developers can have the full development history available.
When Linus started using git for maintaining his kernel tree there didn't exist any tools to convert the old kernel history. Later, when the old kernel history was imported into git from the bkcvs gateway, grafts was created as a method for making it possible to tie the two different repositories together.
The grafts approach mentioned by Chris Johnsen for joining two repositories is no longer fully valid (with grafts alone).
With Git 2.18 (Q2 2018), the functionality of "$GIT_DIR/info/grafts
" has been superseded by the "refs/replace/
" mechanism (for some time now).
The internal code had support for it in many places, which has been cleaned up
in order to drop support of the "grafts" mechanism.
See commit a3694d9, commit f42fa47, commit 8d0d81a, commit e2d65c1, commit f9f99b3, commit 0115e03, commit fb40429, commit 041c98e, commit e24e871 (28 Apr 2018), and commit d398f2e, commit fef461e, commit c5aa6db (25 Apr 2018) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 352cf6c, 23 May 2018)
So instead of
echo "$commit-id $graft-id" >> .git/info/grafts
You now do:
git replace --graft $commit-id $graft-id
git filter-branch $graft-id..HEAD
Deprecate support for
.git/info/grafts
The grafts feature was a convenient way to "stitch together" ancient history to the fresh start of
linux.git
.Its implementation is, however, not up to Git's standards, as there are too many ways where it can lead to surprising and unwelcome behavior.
For example, when pushing from a repository with active grafts, it is possible to miss commits that have been "grafted out", resulting in a broken state on the other side.
Also, the grafts feature is limited to "rewriting" commits' list of parents, it cannot replace anything else.
The much younger feature implemented as
git replace
set out to remedy those limitations and dangerous bugs.Seeing as
git replace
is pretty mature by now (since 4228e8b (replace: add--graft
option, 2014-07-19, Git 2.1.0) it can perform the graft file's duties), it is time to deprecate support for the graft file, and to retire it eventually.
Now (again, Git 2.18, Q2 2018), you have:
replace
: add--graft
optionThe usage string for this option is:
git replace [-f] --graft <commit> [<parent>...]
First we create a new commit that is the same as
<commit>
except that its parents are[<parents>...]
Then we create a replace ref that replace with the commit we just created.
With this new option, it should be straightforward to convert grafts to replace refs.
And before Git 2.20 (Q4 2018), the recently introduced commit-graph auxiliary data is incompatible with mechanisms such as replace & grafts that "breaks" immutable nature of the object reference relationship.
Disable optimizations based on its use (and updating existing commit-graph) when these incompatible features are in use in the repository.
See commit 829a321, commit 5cef295, commit 20fd6d5, commit d653824, commit b775896, commit 950c62b (20 Aug 2018) by Derrick Stolee (derrickstolee
).
See commit 212e0f7, commit 4a6067c (20 Aug 2018) by Stefan Beller (stefanbeller
).
(Merged by Junio C Hamano -- gitster
-- in commit 6d8f8eb, 16 Oct 2018)
With Git 2.24 (Q4 2019), the "upload-pack
" (the counterpart of "git fetch
"), which needs to disable commit-graph when responding to a shallow clone/fetch request, does not panic anymore.
See commit 6abada1, commit fbab552 (12 Sep 2019) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit 098e8c6, 07 Oct 2019)
##
upload-pack
: disable commit graph more gently for shallow traversalWhen the client has asked for certain shallow options like "deepen-since", we do a custom rev-list walk that pretends to be shallow.
Before doing so, we have to disable the commit-graph, since it is not compatible with the shallow view of the repository. That's handled by 829a321 (commit-graph
: close_commit_graph before shallow walk, 2018-08-20, Git v2.19.2).
That commit literally closes and frees ourrepo->objects->commit_graph struct
.That creates an interesting problem for commits that have already been parsed using the commit graph.
Theircommit->object.parsed
flag is set, theircommit->graph_pos
is set, but theircommit->maybe_tree
may still beNULL
.
When somebody later callsrepo_get_commit_tree()
, we see that we haven't loaded the tree oid yet and try to get it from the commit graph.
But since it has been freed, we segfault!So the root of the issue is a data dependency between the commit's lazy-load of the tree oid and the fact that the commit graph can go away mid-process.
When working with git-svn:
git grafts are very useful to import a Git tree into a Subversion repository.
E.g. I created a local Git repository as a start. After working on it several days, creating lots of commits, I had to publish it into the central Subversion repository and I didn't want to lose the history.
I found the following How-to article: http://eikke.com/importing-a-git-tree-into-a-subversion-repository/