How to concatenate two git histories?

后端 未结 1 473
囚心锁ツ
囚心锁ツ 2020-12-23 10:54

I have two git repositories that are tangentially related. Namely, content of one was a predecessor of the other. I would like to somehow prepend the full history of deposit

1条回答
  •  时光说笑
    2020-12-23 11:36

    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:

    enter image description here

    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:

    enter image description here

    Cloning this repo results in this:

    enter image description here

    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.

    enter image description here

    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 a git reset --hard HEAD to actually check the files out after you fast-import.

    0 讨论(0)
提交回复
热议问题