The merge operation does not affect any branch in one fundamental sense. (Of course, he makes a new commit that affects this branch in the usual way.) The trick with Git is to keep in mind the following five simultaneous ideas:
In Git, commits and their parent links are important. The names of the branches are mostly just distracting (but see paragraphs 2 and 3).
A branch name is simply a name for a specific commit, which we call a tooltip commit for that branch.
When creating a new commit, Git writes a new commit with the current commit as its parent. 1 If the new commit has several parents (see the next point), the current commit becomes its first parent. In either case, Git then updates the branch name to indicate a new commit. This is how branches develop.
Merge reconciliation is a commit with two (or more) parent commits. This "merger as a noun," as it were.
The act of creating a merge, by which I mean committing a merge, involves performing an action with three paths and then executing a new commit as usual, except that the new commit has two (or more) parents. “Extra” parents are the combined commitments. 2
The merge action - "merges like a verb" - uses the story created in the five paragraphs above. Git finds three commits:
- Current latch, aka
HEAD . (This is easy.) - Console (s) to be merged: any
git rev-parse identifier is suitable for the arguments (s) that you pass to git merge . The branch name simply finds a branch commit. - Base merger. This is where the commit history happens, and that is why you need to draw fragments of the graph.
The merging base of any two commits is freely defined as the "first (first) point where the graph returns together":
...--o--*--o--o--o <-- branch1 \ o--o--o--o <-- branch2
The name branch1 points to the end (rightmost) of the commit on the top line. The name branch2 indicates the fixation of the tip in the bottom line. The merging base of these two commits is the * mark. 3
To perform the merge action, Git, then diff (as in git diff ) the merge base will commit * against two tips, giving two differences. Git then merges the differences, taking only one copy of each change: if you both (on branch1 ) and they (on branch2 ) changed the word color to colour in README , Git just accepts the change once.
The resulting source stored in the tree becomes the tree to fix the merge. Note that up to this point it doesn’t matter whether we change branch2 to branch1 or branch1 to branch2 : we get the same merge base and get the same two tips, and therefore get the same two diffs and combine these two diffs the same way to get to the same tree. But now we are doing the actual merger with the two parents, and now it is important in which industry we are. If we are on branch1 , we make a new commit on branch1 and branch1 name branch1 :
...
The new merge union has two parents: one is the old tip, branch1 , and the other is the tip of branch2 .
Since we now have a new chart, later git merge will find a new git merge base. Let's say that we make several more commits on both branches:
...
If we now ask to merge the two branches, Git will first find the base. This is a commit that is on both branches closest to the two prompts. I again identified this commit as * and I'll see where it is: this is a commit, which used to be the end of branch2 , back when we merged.
Please note that this is the same regardless of how we merge.
However, it is important that we make the actual merger. If we use git merge --squash , which does not commit the merge, we will not get this kind of chart. It is also important that after the merge, none of the branches gets "rebased", since git rebase works by copying, and git merge works based on commit identifiers and the following parent pointers. Any copies have different commits, so any old commits will not point to the new copied commits. (It’s normal to reset the commit after the merge point - on the right, in these figures, what is not OK copies the commit that is on the left.)
If you do not prohibit git merge performing a fast-forward operation, it is also possible that git merge skipped merging compilation and instead simply moved the branch label. In this case, the two branch labels are the one you just moved and the one you asked to merge, pointing to the same message. As soon as this happens, there is no way to "untangle" the two branches, except to move the label back. To prevent git merge doing this fast forward instead of actually merging, use --no-ff .
Here is an example of a quick merge "merge" (in quotation marks because there is no actual merge). We start, as usual, with diverging branches, but there are no branch1 on the current branch that are not yet on another branch, branch2 :
...--o--* <-- branch1 \ o--o--o <-- branch2
If, sitting on branch1 , we run git merge branch2 - we notice the absence of notifications --no-ff - Git that the actual merge is not required. Instead, it inscribes "fast forward", moving the name branch1 forward until it encounters a tip fix on branch2 :
...--o--o \ o--o--o <-- branch1, branch2
In this graph, there is nowhere to fix any "isolation" between the two branches, so we could also straighten the kink:
...--o--o--o--o--o <-- branch1, branch2
until we make new commits on branch2 :
...--o--o--o--o--* <-- branch1 \ o <-- branch2
There is nothing wrong with that, but notice how impossible it is now to say that the three commits that we “moved up” to the first line were merged.
1 This is true for regular git commit and for git merge , but not for git commit --amend . The “fix” commit option commits a new commit, as usual, but instead of setting the current commit as the new commit parent, it sets the current commit parents (like many of them, as it is, that can't even be all parents) as new parents. The effect is to push the current commit aside, making it look as if the commit was changed when the old commit is actually still in the repository.
2 The case of more than two parents is called the "octopus merge," and we can ignore it. (He does not do anything that you cannot do with repeated pairwise merging.)
3 In complex graphs, there may be more than one such “first point”. In this case, all nodes with the smallest common-ancestor are merge bases, and for Git, the -s strategy merge -s strategy argument decides how to handle this case. (Of course, there is also the -s ours strategy, which ignores all other commits and simply completely bypasses the three-way merge code. But I assume that this is a normal merge.)