简体   繁体   中英

Why does branch B have conflicts in all merge commits from A to B after the first merge commit?

I have a primary development branch (A) which has a long history. All release commits in A are tagged as such. I checked out the root commit of A, and branched into test (B).

So I have a primary branch A, and the head of branch B pointing at the root commit of A. My goal is to create a history of all releases by merging each tagged commit from A to B.

The first merge from A into B works as expected, no conflicts.

$git checkout B
$git merge [release-commit-ID] --squash
$git commit -m "release#"

The first commit works great, but all other commits treat all merge commits as complete conflicts. I see that the root of B is the same as the root of A, but no shared history is recognized after the first squashed merge commit from the first release commit in A into B. What is causing the conflicts and how do I get shared history to be recognized?

What is causing the conflicts and how do I get shared history to be recognized?

There is no shared history (or rather, not enough). There's nothing to recognize, which is why there are conflicts.

The key is the --squash flag:

 $ git checkout B $ git merge [release-commit-ID] --squash 

The first step attaches your HEAD to the branch name B , checking out whichever commit the name B identifies:

...--*--C--D--E   <-- B (HEAD)
      \
       F--G--H   <-- somebranch

Here the name B identifies commit E (each of these letters stands in for the real hash IDs). Commit * (which I would have named B , but you used that name for your branch) is the point at which the two development streams diverge, which means that when we work backwards (as Git does) it's the point where they come together. You now run git merge --squash <hash> where <hash> identifies commit F , or G , or H , so Git compares the contents of commit * to the contents of commit E to find out what you changed:

git diff --find-renames <hash-of-*> <hash-of-E>   # what we changed

and repeats with, say, G :

git diff --find-renames <hash-of-*> <hash-of-G>   # what they changed

Git now combines these two sets of changes, applies the combined changes to commit * 's contents, and makes a new commit.

If you don't use --squash , Git records the new commit with two parents:

...--*--C--D--E--I   <-- B (HEAD)
      \         /
       F-------G--H   <-- somebranch

and now the most recent common starting point between the two lines is commit G . But if you do use --squash , Git records the new commit with just one parent:

...--*--C--D--E--I   <-- B (HEAD)
      \
       F--G--H   <-- somebranch

and now the common starting point is unchanged. All the work through G is in commit I , but commit I doesn't remember why that work is there. A future git merge from commit H has to start over at commit * .

Git won't stop you from developing on branch somebranch , but in general, after a git merge --squash , you should consider the branch you merged from to be "dead" and just stop using it at all. Once we've merged G with git merge --squash we should stop using F and G entirely. This means we must also stop using H entirely. If H is useful, we should copy it to a new, different commit:

$ git checkout -b newbranch B

giving us:

...--*--C--D--E--I   <-- B, newbranch (HEAD)
      \         /
       F-------G--H   <-- somebranch

followed by:

$ git cherry-pick somebranch   # or <hash of H>

to copy H to a very similar, but not identical, commit H' :

                   H'   <-- newbranch (HEAD)
                  /
...--*--C--D--E--I   <-- B
      \         /
       F-------G--H   <-- somebranch

We can now discard somebranch :

$ git branch -D somebranch

and rename newbranch to somebranch if we like.

(Note that we can do this copy-with-name-moving thing in one step using git rebase --onto , with git checkout somebranch; git rebase --onto B <hash of G> . No matter how you do it, though, be aware that git cherry-pick cannot copy merge commits, and anyone who is using the commits we want to kill off—here the FGH chain—must do this killing-off-with-copying-the-ones-to-save thing. So before using --squash , be sure you understand all the implications.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM