Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git octopus merge order of multiple branches

I had an interesting thing happen using git, wondering if anyone could explain it to me so I can understand better.

When doing a merge of multiple branches (A,B),

git merge A B 

fails as non-fast-forward, while

git merge B A 

worked well. Why would that be?

like image 344
James Maroney Avatar asked Jun 29 '11 12:06

James Maroney


1 Answers

Let's assume that A is a strict, direct child of the current branch. Then assume that B is a strict, direct child of A.

The octopus merge, which processes heads given as arguments from left to right, incrementally with respect to the tree, but independently with respect to the index succeeds without conflict if it tries to apply B and then A, but encounters a conflict if it does the convert.

As per the git-merge manual, section MERGE STRATEGIES:

octopus    This resolves cases with more than two heads, but refuses to do a    complex merge that needs manual resolution. 

For instance:

 ~                 $ git init testdir && cd testdir && echo "This is C" > myfile  Initialized empty Git repository in /home/huitseeker/testdir/.git/   ~/testdir         $ git add myfile && git commit -m "C"   [master (root-commit) f0c8c82] C   1 files changed, 1 insertions(+), 0 deletions(-)   create mode 100644 myfile   ~/testdir(master) $ git checkout -b "A" && echo "This is A1" > myfile  Switched to a new branch 'A'  ~/testdir(A)      $ git commit -m "A1" myfile  [A ac5b51c] A1   1 files changed, 1 insertions(+), 1 deletions(-)   ~/testdir(A)      $ git checkout -b "B" && echo "This is B1" >> myfile  Switched to a new branch 'B'  ~/testdir(B)      $ git commit -m "B1" myfile  [B 5bc838c] B1   1 files changed, 1 insertions(+), 0 deletions(-)   ~/testdir(B)      $ git checkout master  Switched to branch 'master'  ~/testdir(master) $ git merge B A  Fast-forwarding to: B  Already up-to-date with A  Merge made by octopus.   myfile |    3 ++-   1 files changed, 2 insertions(+), 1 deletions(-)   ~/testdir(master) $ git reset --hard HEAD^^^  HEAD is now at f0c8c82 C  ~/testdir(master) $ git merge A B  Fast-forwarding to: A  Fast-forwarding to: B  error: Entry 'myfile' would be overwritten by merge. Cannot merge.  Merge with strategy octopus failed.   ~/testdir(master) $ cat myfile  This is A1 

Indeed, when fast-forwarding to A, the label of master has not been pushed forward, though the tree has.

 ~/testdir(master) $ git status  # On branch master  # Changes to be committed:  #   (use "git reset HEAD <file>..." to unstage)  #  #  modified:   myfile  # 

If, looking at the code of what the octopus merge does, I perform this manually (look above for hashes):

 ~/testdir(master) $ git reset --hard f0c8c82  HEAD is now at f0c8c82 C       ~/testdir(master) $ git read-tree -u -m f0c8c82 ac5b51c  ~/testdir(master) $ git read-tree -u -m f0c8c82 5bc838c  error: Entry 'myfile' would be overwritten by merge. Cannot merge. 

In the other direction (merge B A), now, if you look again at the code of merge-octopus, it tries to detect the branch we are trying to add is already in the tree (second case of the for loop). Indeed, at the merge of A, it sees ac5b51c (a.k.a. A's head) is the common ancestor of A and B, and aborts without doing the second read-tree.

This behavior is consistent with the fresh version of git : though I've pointed to v.1.3.1, this is still happening with my version.

 ~/testdir(master) $ git --version  git version 1.7.5.4 

tl;dr : you want your octopus merge branches to touch distinct files

like image 123
Francois G Avatar answered Sep 20 '22 14:09

Francois G