2

git merge merges from the other branch into the current branch.

Possible to merge from the current branch into the other branch, and stay in the current branch?

Of course the following is possible, but there are three steps.

(on brancha)
git checkout master
git merge brancha
git checkout brancha
Ksthawma
  • 1,257
  • 1
  • 16
  • 28

1 Answers1

5

Short answer: no, you must use the multi-step method.

Slightly more involved answer: yes, it's possible, but it's even more work than the multi-step method. Here's the trick: a merges does two different things; one of those two is symmetric;1 one of those two is not.

The symmetric part is "coming up with a merge result" (in terms of a tree to attach to a commit). To do this, git has a four-step process:

  1. Find the merge base. This is some particular commit2 that is both an ancestor of the current commit (HEAD) and of the named, to-be-merged commit (often the tip of some branch, but you may merge any commit-ID).
  2. Diff the merge base against HEAD.
  3. Diff the merge base against the to-be-merged commit.
  4. Combine the two diffs, with automatic results where there are no conflicts, and conflicted results (which cause the merge to stop and get help) wherever the two diffs collide.3

Having done all of that, git merge normally makes a new "merge commit" on the current branch (or at the detached HEAD), unless of course you tell it not to (git merge --no-commit) or it stopped due to conflicts.

This new commit is the non-symmetric part. Given the above (and ignoring potential rerere cases as well, since those can depend on the order of the diff pair), if you prevent the new commit, you'll get the same tree regardless of which "direction" you use to do the merge. But you'll get a different commit, because the new merge commit has three interesting properties:

  • The list of parents: the list is the same, but the order depends on the direction of the merge. A merge's first parent is always the commit being merged-into (the current branch), and its second parent is the commit being merged-in (the merge-from branch or commit).
  • The commit message: the default message says "merge branch mergefrom", so the name of the merged-from branch appears.
  • The automatic adjustment of the current branch: this is the same as for any new commit. If you're on branch master, and you make a new commit, the SHA-1 to which master points is updated so that master points to the new commit. In this case, that's the new merge commit.

If we stop the actual merge commit from being written (with --no-commit), we'll get the right tree (ignoring certain rerere cases). We can then create a new commit with the correct parents, tree, and log message, and update the other branch, rather than the one we're on, by using git write-tree and git commit-tree to write the new commit somewhere, then using git update-ref to update a reference other than the branch we're on. The git commit-tree command takes a list of parent SHA-1 IDs, in an order we control, and takes -m or -F to set the commit message, and does not advance the current branch (the new commit object's ID is simply written to standard output), so this is all feasible, but it's not the way to go.


1Aside from "ours" vs "theirs" when doing a normal (non-octopus) merge, and other corresponding subtleties such as the conflicted merge entries in the index, and the rerere possibilities.

2This glosses over the "virtual base" that recursive merge uses in certain cases, but the principle remains the same even then.

3More precisely, where the collision is not simply "make the same change", which is resolved automatically by taking the change only once instead of twice. Or, if rerere is enabled, git checks for a "re"corded "re"solution of that conflict that it can "re"use, and will then reuse it rather than stopping.

torek
  • 448,244
  • 59
  • 642
  • 775