How can I “splice” two (or more) completely unrelated linear branch ancestries into a new one?

和自甴很熟 提交于 2019-12-06 02:13:00
jub0bs

You write:

One important fact in my scenario is that both branches are completely unrelated. No commit of either branch affects patches of the other branches' commits => There will be no conflicts.

Of course, the "splicing" operation you suggest is probably a bad idea if you anticipate that many conflicts will arise. Let's assume that, indeed, nothing bad will happen.

If, at the beginning, your repo looks like this

A---B------C--D [branch1]

  X---Y--Z [branch2]

you can follow the procedure outlined below to automatically "splice" commits from both branches into a single branch, while maintaining chronological order.

"Splicing" two unrelated branches

  1. Make sure you're in a clean working state; then check out branch1 and merge branch2 into it:

    git checkout branch1
    git merge branch2
    

    That will yield

    A---B------C--D---E [HEAD=branch1]
                     /
      X---Y--Z------- [branch2]
    

    Now, I know that's not what you want, but bear with me for a second. We will use merge commit E to have access to "the ancestry on both sides" at once.

  2. Check out branch2 and reset it to commit A.

    git checkout branch2
    git reset --hard A
    

    You'll be in the following situation:

    A [HEAD=branch2]
     \
      ---B------C--D---E [branch1]
                      /
       X---Y--Z------- 
    
  3. Generate a list (in chronological order) of all the non-merge commits reachable from branch1 but not from branch2:

    git rev-list --no-merges --reverse branch2..branch1
    

    This should yield the following list of commits: X, B, Y, Z, C, D; commit E, which was created in Step 1 will not be in that list, because we used the --no-merges flag.

  4. Cherry-pick those commits on top of branch2 (A).

    git cherry-pick `git rev-list --no-merges --reverse branch2..branch1`
    

    Your repo will then look as follows:

    A--X'--B'--Y'--Z'--C'--D' [HEAD=branch2]
     \
      ---B-----C-D---E [branch1]
                    /
       X---Y-Z------ 
    
  5. Delete branch1:

    git branch -D branch1
    

    Edit: As you correctly remarked, because branch1 is not fully merged into the current branch (branch2), using just -d won't do, here; you need to use the -D flag instead.

    Your repo will then simply be

    A--X'--B'--Y'--Z'--C'--D' [HEAD=branch2]
    
  6. (Optionally) Rename branch2:

    git branch -m branch2 <more_meaningful_name>
    

Generalization to more than two branches

Let's assume you have n completely unrelated branches: branch1, branch2, ..., branchn, where branch1 corresponds to the branch whose root commit is the oldest commit in the entire repository; Let's call that commit A.

A ----- o ---- o [branch1]

   o ----- o ---- o -- o [branch2]

...

 o ----- o - o [branchn]

If you don't know which commit is A, you can identify it by running

git rev-list --reverse --max-parents=0 --all

The commit ID of A will be the first listed in the output of that command. And you can identify which branch is branch1 by running:

git branch -r --contains `git rev-list --reverse --max-parents=0 --all | head -1`

Then the procedure outlines in the two-branch case becomes:

  1. Create a commit that has access to the ancestry of all branches, by merging all branches other than branch1 into branch1.

  2. (same as in two-branch case)

  3. (same as in two-branch case)
  4. (same as in two-branch case)
  5. Delete all branches other than branch2.
  6. (same as in two-branch case)

Quick simple solution to merge is,

suppose A---B-----C-D (is branch 1) and X---Y-Z (is branch 2). Take all the patches from branch-2 (git format-patch -n) then apply all the patches on top of branch 1 (git am *.patch), and then shuffle where ever you want the sequence to be using git rebase -i HEAD~n

Hope that helps!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!