简体   繁体   中英

Difference between git merge with commits in common and git merge with unrelated branches

Okay, first of all, do I have this right?:

Suppose you have local branches A and B, and they have no commits in common.

When I do

git checkout A
git merge B

Git creates a new commit on the A branch in addition to merging all commits from B that are not already on the A branch.

Now suppose branches C and D have at least one commit in common. When I run the same two commands, Git does not create the extra commit. It just merges the existing commits from D into C.

Supposing that's all true.... why?

When you run git merge , the actual behavior that happens depends on the graph topology of the commits between the two branch tips that you're merging.

Nothing to do

If the branch B you're merging in is already an ancestor of the branch you're on A :

u -- v -- w [B]
            \
             x -- y -- z [A]

... then all of the work from B is already included in A , so git reports Nothing to be done , and no commit is made.

Fast-forward merge

If the situation is reversed and A is a direct ancestor of B , then all that git needs to do is zip A up to where B is. Because B 's history already includes all of the work that was done on A , there's no possibility for conflict. Also, all commits from both A and B will still be reachable after the branch pointer is moved. Therefore, it isn't necessary to create a commit.

Before

u -- v -- w [A]
            \
             x -- y -- z [B]

After

u -- v -- w -- x -- y -- z [A, B]

By the way, you can actually force git to create an merge commit in this situation anyway by passing the --no-ff flag to git merge . This is valuable sometimes if you want to always keep commits on feature branches out of master , for example.

On the other hand, you can also pass git merge the --ff-only flag, which will fail if this situation is not the case. That's useful if you want to be absolutely certain that your merge is a trivial one and won't create a new commit, say when you're merging origin/master into master and you shouldn't have done any local work on master .

Recursive merge

In the more general case where A and B both have commits that aren't included in the other's history, git's real merge machinery is called in. The commit graph is walked to find a common ancestor C and a three-way merge algorithm is invoked to resolve changes. If there are any conflicts, conflict markers are left in the working copy for the user to resolve and stage. If there aren't, a new commit is created with both A and B as parents to record the merge and preserve the history of both branches.

Before

u [C] -- v -- w [A]
 \
  x -- y -- z [B]

After

u [C] -- v -- w -- m [A]
 \                /
  x -- y ------- z [B]

There are other strategies you may encounter -- an octopus merge happens when merging more than two branches at once, for example -- but these are the basics. So there you have it: git creates a merge commit or not depending on whether or not the branch topology in question requires it.

By the way, if A and B truly share no common commits, then the merge algorithm has no common ancestor to work with and falls back to fairly ugly guesswork that results in lots of spurious conflicts. There are ways around it (faking an empty common ancestor, for example) but they're reasonably advanced and uncommon.

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