How to concatenate two git histories?
I have two git repositor开发者_开发问答ies that are tangentially related. Namely, content of one was a predecessor of the other. I would like to somehow prepend the full history of depository A to the depository B, so that tip of A would be a parent of the very first changeset of repository B? Histories in both are pretty linear.
Is this possible?
You could try using the graft file (.git/info/grafts
) where you could overwrite the parenthood of a commit (like the first of projectB
having for parent the latest of projectA
)
See also "What are .git/info/grafts for?" and "How to prepend the past to a git repository?" for more on this manipulation.
skalee comments about the article "Git: Grafting repositories" (from SO user Ben Straub) for a concrete example.
Now what we want to do is change the first commit in the “
nuevo
” repo (“New commit #1
”) so that its parent is the last commit in the “old” repo (“Old #3”). Time for some voodoo:
git fetch ../old master:ancient_history
Git lets you fetch from any other git repository, whether this repo is related to it or not! Brilliant! This leaves us with this:
Note how we renamed the old master branch to ancient_history. If we hadn’t, git would have tried to merge the two, and probably given up in disgust.
Now we still have a problem.
The two trees aren’t connected, and in fact a git pull won’t even get the ancient_history branch at all. We need a way to make a connection between the two.Git has a facility called a graft, which basically fakes up a parent link between two commits.
To make one, just insert a line into the.git/info/grafts
file in this format:
[ref] [parent]
Both of these need to be the full hash of the commits in question. So let’s find them:
$ git rev-list master | tail -n 1
d7737bffdad86dc05bbade271a9c16f8f912d3c6
$ git rev-parse ancient_history
463d0401a3f34bd381c456c6166e514564289ab2
$ echo d7737bffdad86dc05bbade271a9c16f8f912d3c6 \
463d0401a3f34bd381c456c6166e514564289ab2 \
> .git/info/grafts
(in one line, as suggested by ssokolow)
echo $(git rev-list master | tail -n 1) $(git rev-parse ancient_history) > .git/info/grafts
There. Now our history looks like this:
Cloning this repo results in this:
Woops. It turns out that grafts only take effect for the local repository. We can fix this with judicious application of
git fast-import
:
$ git fast-export --all > ../export
$ mkdir ../nuevo-complete
$ cd ../nuevo-complete
$ git init
$ git fast-import < ../export
git-fast-import statistics: [...]
(in one line, as suggested by ssokolow)
git filter-branch $(git rev-parse ancient_history)..HEAD
This effectively converts our “fake” history link into a real one.
All the engineers will have to re-clone from this new repository, since the hashes will all be different, but that’s a small price to pay for no downtime and a complete history.
As Qix comments below:
fast-import
appears to just import the git information, but doesn't check anything out.
git init
originally puts you on master, so you need agit reset --hard HEAD
to actually check the files out after youfast-import
.
精彩评论