It's true that you would need "theirs" during rebase. This is because rebase operations are actually a series of cherry-picks, and each cherry-pick is a merge operation in which your HEAD
(current commit) is on the target branch (being built), rather than pointing to a commit on your original branch (now being copied).
That is, a normal merge looks like this:
...--o--o--*--o--o <-- yourbranch (HEAD)
o--o--o <-- theirbranch
You run git merge theirbranch
; Git compares commit *
—the merge base—to your final commit to see what you did, and then compares *
to their final commit to see what they did. Git then combines these changes, applying them all to *
, to produce the merge:
...--o--o--*--o--o---M <-- yourbranch (HEAD)
/
o--o--o <-- theirbranch
If you provide a .gitattributes
-defined merge driver, you can have Git take your version of .mvn/maven.config
—but there is a huge caveat here; see below.
When you rebase, however, you start out with this:
...--o--o--*--o--o <-- theirbranch
A--B--C <-- yourbranch (HEAD)
You run git rebase theirbranch
, and Git finds the commits since *
—these are your A
, B
, and C
commits above—and lists their hash IDs into a temporary file. (If you use interactive rebase, you will see a series of pick
commands using those hash IDs.) Now that rebase has the IDs, it starts the real work by first checking out their branch, as a detached HEAD:
...--o--o--*--o--o <-- theirbranch, HEAD
A--B--C <-- yourbranch
Git then runs as many git cherry-pick
calls as needed to copy all the commits—in this case, three of them. Each cherry-pick is a special kind of merge, doing the merge action without making a merge-style commit. The result of the merge is a copy of the commit being cherry-picked. So the first merge operates with *
as the merge base as before, with the HEAD
commit as the current commit as always, and with commit A
as "their" commit to be merged!
Hence, in this case, you would want your custom .gitattributes
-defined driver take "their" version of .mvn/maven.config
, because "their" version (in commit A
) is really your version. That huge caveat I mentioned before is getting even bigger now, though!
Assuming A
is copied successfully, you are now in this state:
A' <-- HEAD
/
...--o--o--*--o--o <-- theirbranch
A--B--C <-- yourbranch
Git now runs a second cherry-pick, which means a second merge operation: the merge base this time is commit A
; your HEAD
commit is your own copy A'
; and "their" commit is your commit B
. It's now safe to take either .mvn/maven.config
, as long as you took the right one in the first cherry-pick.
The rebase will repeat this for commit C
as well, to build C'
atop B'
. The merge base this time will be commit B
. As with the process of copying commit B
, you could use either .mvn/maven.config
here. When this copy is done you have:
A'-B'-C' <-- HEAD
/
...--o--o--*--o--o <-- theirbranch
A--B--C <-- yourbranch
and Git now finishes the rebase by peeling the name yourbranch
off commit C
and pasting it onto the last copy C'
, re-attaching your HEAD in the process:
A'-B'-C' <-- yourbranch (HEAD)
/
...--o--o--*--o--o <-- theirbranch
A--B--C [no longer needed]
The huge caveat
This particular idea, of using .gitattributes
to control which .mvn/maven.config
file is used, is at least partially) doomed from the start for a different reason: When Git is doing a merge, Git looks first at the raw hash IDs of each file from each of the three commits (merge base, ours, and theirs). There are a total of 5 possibilities here:
All three hash IDs are the same: the merged file is the base file, which is their file and our file. Everything is pretty groovy at this point: you did not even need a merge driver!
The base file is the same as theirs, but different from ours: Git just takes our file. Git completely ignores your merge driver. The idea of using a merge driver fails (well, unless you wanted "ours", but that happens even without the merge driver).
The base file is the same as ours, but different from theirs: Git just takes their file. Git completely ignores your merge driver. The idea of using a merge driver fails, as with the previous case.
The base file is different from ours or theirs, but ours and theirs are the same: Git just takes our file (it's already in place, so that's easier for Git). Git completely ignores your merge driver. This is OK for our case, since both of us (ours and theirs) made the same change, but a merge driver that was going to keep the base version would fail here.
Last, the base file differs from ours, and ours differs from theirs: Git uses your merge driver. Your merge driver succeeds! Well, it does as long as you figure out which version you want ("ours" or "theirs").
Going through the list, you will see that your .gitattributes
driver only works in one out of the five possible cases. It's not needed in another two cases. It is needed in two out of the five in which it is ignored! So there are two (out of five total) situations where your merge driver won't be used, when you wanted it to be used.