2

I'm using git to merge all the time. I'm getting annoyed with what comes next and I'd like to know if there's a better way for the work flow (a command I'm missing, another way to do it, whatever).

When I git merge towards my/branch like git merge --no-ff --no-commit that/other-branch, I usually resolve conflicts. Now that this is done, I inspect all the changes in case an auto-merge went wrong (hence the --no-commit if you wondered).

Last time, I discovered an auto-merge, that -you're gonna laugh- I'd rather have had as a conflict. Indeed, the auto-merge dropped something I would have liked it not to.

A quick parenthesis here: I'm not discussing whether git was right or wrong auto-merging how it did in regards to the most recent common base. I'm just telling, well: I'd rather have git replay the game for that single file amongst the others of that big merge and mark that as a conflict for me to edit manually.

So, the current setup is:

  1. git merge --no-ff --no-commit
  2. Got conflict(s) to resolve, edited them, added them. Done. Let's move on.
  3. I inspect diffs (auto-merges included): git diff HEAD
  4. I find file foobar that was auto-merged in a way that displeases me.

So what is command #5 that:

  1. Replay merge of single file foobar in the middle of the big merge of step #1 to force it to be marked as a conflict with a three-way diff friendly file i.e., with <<<<<<< ======= >>>>>>> instead of the result of the auto-merge.

Thanks.

tomoyo255
  • 153
  • 1
  • 8

4 Answers4

5

LeGEC's answer is a good one and there's not much more to say, other than to add a warning. This should be a comment, but I want to include some formatting, which isn't possible in a comment. Also, the comment length limits are troubling.

Whenever Git's automerge would merge something some way, and you override this and provide some different result, that produces what some will call an evil merge. (See Evil merges in git?) That's not wrong really, but there is a danger: commands like git rebase --rebase-merges do not actually keep the existing merge commits, but rather re-run the git merge code. If Git would have produced (and did produce) some file as an auto-merge without conflicts, this re-run merge will produce that file again. You'll have to stop process here and redo your "evil merge" effect.

As such, you might want to go ahead and commit the "bad" result and then add an extra commit to repair it. This "cure" (avoid evil merges by adding fixup commits) is in some ways worse than the "disease" in the first place, but it allows the --rebase-merges code to work.

Alternatively, you can insert some thing(s) into the two branches as new tip commit(s) specifically so that the git merge fails with a conflict in the area you want to merge manually. This too has some drawbacks, but means that re-merging will re-use recorded resolutions (if you have turned git rerere on) or stop again for manual merging.

As a final alternative, you could add new tip commit(s) that ensure that the automerge result is what you want. Whether those are "damaged commits" in the same sense that the middle alternative produces deliberately damaged commits (so as to cause the merge failure—but note that making the "damage" a comment, in whatever language you're using, might suffice) or not might depend on the situation.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Interesting remark! Indeed, the evil merge (where the merging human ships additional changes not related to the merge or doing somewhat more than just merging, if I understand correctly) is a problem : - if the auto-merge succeeded but is overriden - NOT if there already was a conflict in the first place, as a manual resolution will be needed anyway or not with rerere if I get it. Better split evil merge into (good) merge, then change... – tomoyo255 Aug 24 '22 at 14:46
  • The warning is duly noted. Thanks for it. I'm sure it'll also be useful to other readers tempted to do evil as I did. – tomoyo255 Aug 24 '22 at 14:54
  • I upvoted both your very useful remark and LeGEC's answer. – tomoyo255 Aug 24 '22 at 14:56
  • Thanks for telling why it wouldn't be wise to use the answer I sought for :-) – tomoyo255 Aug 24 '22 at 15:00
2

First : you did the right thing. Reviewing merges is definitely a wise thing to do.


As far as edition goes : note that any way to edit the file before committing is ok, you don't specifically need to "trigger and solve a conflict" before committing your content as the result of the merge.

You can get the value of either side of the merge :

git show HEAD:that/file > that/file.ours
# during a merge, git creates a MERGE_HEAD reference that points to the incoming commit
git show MERGE_HEAD:that/file > that/file.theirs
# since you know what you merged in, the above is the same as:
git show that/other-branch:that/file > that/file.theirs

and copy the chunks you want, or use git difftool :

git difftool HEAD -- that/file
git difftool MERGE_HEAD -- that/file

If you are more confortable with a 3 way merge, once you have file.ours and file.theirs, use your diff editor to open a 3 way merge.

LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • Yes, I know you can edit (and that's finally what I did). But, I just wanted to be presented with the merge prior to auto-merge as if it were a conflict (even if for git there wasn't one, with "their[s]" and "our[s]" lines between <<<<<<< ======= >>>>>>> delimiters). – tomoyo255 Aug 24 '22 at 14:52
  • Interesting work-around with the file.ours and file.theirs. – tomoyo255 Aug 24 '22 at 14:53
2

As an addition to @torek's very fine answer, you can get close to what you want with a single command if you've got a decent merge tool that doesn't insist on doing its own automerge.

Replay merge of single file foobar in the middle of the big merge of step #1 to force it to be marked as a conflict with a three-way diff friendly file i.e., with <<<<<<< ======= >>>>>>> instead of the result of the auto-merge.

So the conflict marking is done by automerge, and there's no way I know to make it mark all changes as conflicts, . . .

but with a good merge tool, since you're interested in every change, there's no need for the markers at all. Just step through every change. Instructions for vimdiff:

git show :1:foobar >foobar
vimdiff -c '4winc w' -c 'winc J' `git checkout-index --stage=all foobar`

and now all differences from any of the top three buffers/windows, which are the base, local, incoming revisions, are highlighted in the bottom one, so you can ]c i.e. next change, and just step through picking the versions you like. b1 is base, b2 is local, b3 is incoming.

So rather than having conflict markers you have to delete you can just e.g. 2do to take the local change, 3do to take the incoming-merge change, or otherwise make it look how it should, before ]cing on to the next.

jthill
  • 55,082
  • 5
  • 77
  • 137
1

Replay merge of single file foobar in the middle of the big merge of step #1 to force it to be marked as a conflict

It can't be done. A file that is not conflicted is not conflicted — end of story. A conflicted version of this file cannot be shown, for the simple reason that there is no conflict to show.

It is perfectly reasonable to complain that sometimes automerge results in what feels like a wrong answer. For example, suppose one branch declares a type at the start of the file, and the other branch declares the same type (or a different type, or the same type slightly differently) at the end of the file. You will get both, even if, according to requirements, programming language, etc., there can in fact be only one.

But that's not a conflict and there's no point wishing that it were. Only a human can detect the problem. Typically you'll discover this at a later stage of the process, after the merge is finished, when (for example) the code doesn't compile, or a test doesn't pass; since you have done a --no-commit, you can discover it now. But you have to discover it.

matt
  • 515,959
  • 87
  • 875
  • 1,141