27

I committed some test code before merging in a remote branch. This merge had a lot of conflicts and took some serious time to put right. So my history looks something like this:

7ab562c Merge from remote branch
... whole load of commits brought across from the remote branch...
f3e71c2 Temporary TESTING COMMIT

The test code is fine, I really just want to change the commit message. Normally I'd go right ahead with a git rebase -i f3e71c2^ (since none of this has been pushed yet), but I've been told by a colleague that this will mess up the merge. I really don't want to mess up the merge :)

Is my colleague correct? And if so, is there anything I can do, or do I just need to live with this history?

Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
ChrisWard
  • 343
  • 1
  • 3
  • 9
  • 3
    `git commit --amend`? – kan May 14 '13 at 09:21
  • 3
    @kan: that only works for amending the latest commit, which is not the situation here. – kampu May 14 '13 at 10:35
  • 4
    Less fear recommended! If you mess up, just `git reset --hard 7ab562c` to get back to your post-merge state and try again. Also, check out `git rerere` to have git remember how to resolve a merge conflict. – chrisk May 14 '13 at 10:49
  • In our shop some of our less git-savvy users often get some very ... creative looking histories. Usually they don't care what their history looks like, they just want it to be easy to do peer reviews, so I have them do something like: `git pull` (& resolve conflicts), `git reset --soft origin/develop` (leaves the state of things as they were after the merge), then `git commit`. This is effectively squashing/rebasing their changes. – Brian Vandenberg Jul 15 '15 at 19:41

3 Answers3

35

2013: You can try a git rebase --preserve-merges --interactive, with:

-p
--preserve-merges

Instead of ignoring merges, try to recreate them.

The BUG section of the man page includes:

The todo list presented by --preserve-merges --interactive does not represent the topology of the revision graph.
Editing commits and rewording their commit messages should work fine, but attempts to reorder commits tend to produce counterintuitive results.


2019+: As I mentioned here, this option is deprecated since Git 2.22, Q2 2019.

You would need to use git rebase --rebase-merges --interactive instead.


As jthill's comment describes (since -p will better preserve merges if conflict resolutions were recorder):

You can retroactively light rerere for a merge:

git config rerere.enabled true
git checkout $merge^1

git merge $merge^2
# for Windows: 
# git merge $merge^^2

git read-tree --reset -u $merge
git commit -m-
git checkout @{-1}

As noted by Soufiane Roui in the comments:

For Windows CMD users, use double caret to point for parents of the merge commit (e.g $merge^^1 instead of $merge^1).
Because the caret is considered as an escape character.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thank's @VonC, but sadly, this didn't work for me. I `reword`ed the test commit, `pick`ed the merge, and rebase still choked on the merge, throwing out 11 conflicting files. I've used @chrisk's `git reset` suggestion to get back to where I was, and I think I'll leave it there: the time invested is not worth the fix in this case. – ChrisWard May 15 '13 at 10:39
  • @ChrisWard That means you would need to activate `rerere` (http://git-scm.com/docs/git-rerere) in order to record the merge resolution, which will *then* allow a rebase `--preserve-merge` without conflict. – VonC May 15 '13 at 12:07
  • I've now turned on `rerere`, and tried the `rebase` again. When `rebase` gets to the merge commit, I can see `rerere` recording the pre-images, and then the merge conflicts raining in. I'm guessing it's doing this because I didn't have `rerere` turned on when I made the original conflict resolutions, so it isn't aware of how I resolved the issues. Should `rerere` be able to glean that data from an already resolved commit? – ChrisWard May 15 '13 at 13:22
  • 1
    Having looked into this further, `rerere` doesn't do this 'data gleaning'. Since I've only just turned it on, my previous conflict resolutions haven't been recorded, so can't be used. I'll give this up as a bad job, and advise my colleagues to enable `rerere`, in case they ever get into this mess in the future. – ChrisWard May 15 '13 at 14:00
  • 1
    @ChrisWard true, `rerere` works only if you previously resolved merge conflicts with `rerere` activated. – VonC May 15 '13 at 14:06
  • 1
    You can, however, retroactively light rerere for a merge: `git config rerere.enabled true; git checkout $merge^1; git merge $merge^2; git read-tree --reset -u $merge; git commit -m-; git checkout @{-1}`. – jthill Apr 24 '17 at 23:02
  • @jthill Thank you: I have included your comment in the answer for more visibility. – VonC Apr 25 '17 at 04:32
  • Just a note: for windows cmd users, use double caret to point for parents of the merge commit (e.g $merge^^1 instead of $merge^1). Because the caret is considered as an escape character. – Soufiane Roui May 11 '20 at 02:31
  • @SoufianeRoui Thank you. I have included your comment in the answer for more visibility. – VonC May 11 '20 at 04:36
0

Update on this. I've tried -p as suggested by @vonc but didn't work for me.

Git docs now mentions -r or --rebase-merges, so full command looks something like this:

git rebase -i -r '<commit hash>'

https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---rebase-mergesrebase-cousinsno-rebase-cousins

sebazelonka
  • 772
  • 2
  • 6
  • 13
  • 1
    Good point. I have [updated my answer](https://stackoverflow.com/a/16541107/6309) with that new option that [I presented in 2018 here](https://stackoverflow.com/a/50555740/6309). – VonC Jul 13 '23 at 15:57
-4

If and only if your colleagues have not pushed/pulled the changes ontop of f3e71c2 elsewhere, this will work. Otherwise I don't know what will happen. Changing the commit message is entirely cosmetic (== metadata change), given that you haven't pushed the commit you want to amend yet, but this could still result in history confusion if your colleagues have pushed/ pulled any part of the history which is atop it.

(thanks to Abizern for pointing out this failure-mode)

kampu
  • 1,391
  • 1
  • 10
  • 14
  • 3
    The message is part of the commit object, which is immutable. If you change the message, you've changed the commit, which changes history. – Abizern May 14 '13 at 10:37
  • 1
    @Abizern: That's true. However that is only relevant if the commits have already been pushed elsewhere (they haven't). I've locally verified this strategy works in this situation. – kampu May 14 '13 at 10:48
  • @Abizern: However I suppose I hadn't considered that others could have already pushed the on-top commits elsewhere in the meantime. That would probably cause failure / history confusion, so I've edited accordingly. – kampu May 14 '13 at 11:27
  • 2
    This does not answer the question. – user253751 May 20 '16 at 02:49