0

Alternative title: How to squash merge commits?

I have the following state in my local repository:

      C     (origin/feature)
     /
A - B - m   (feature)
       /
  Y - Z     (master)

I've just merged changes from one branch to another (e.g. from master into our feature branch, or even vice versa). When I did this, commit B and Z were the most latest commits in their respective branches.

However! Dastardly Dan just pushed his change to the branch I was merging into (C). When I now try and push, I can't. I first have to fetch the changes and merge them somehow. The question is: how?

If I do a pull with rebase, I end up having to resolve all the conflicts in the merge between B & Z. If I try to merge, I end up with two merge commits, which isn't that clean, and also shouldn't be that necessary.

      C --
     /     \
A - B - m - m2
       /
  Y - Z

Is there any way of squashing m and m2 such that it looks like:

      C --
     /     \
A - B    -- m3
       /
  Y - Z

An interactive rebase obviously doesn't show the merge commits, and a cherry-pick requires me to resolve all the merge conflicts again (between B & Z, which weren't affected by C at all).

Druckles
  • 3,161
  • 2
  • 41
  • 65
  • was merging `master` into `feature` painful ? – LeGEC Feb 14 '20 at 13:44
  • Given someone had enough time to push something in between, we should assume yes ;-). And it's a situation that occurs often enough that a better solution than remerging would be nice. – Druckles Feb 14 '20 at 14:10
  • git alone will not prevent two people from working on the same branch, and then both trying to push their work on a central repo. If you want to prevent any modification on a branch for a period of time, you should look at the features of your git server : gitlab, github or Azure Devops have options to protect branches, for example, which you could turn on at the beginning of your merge for example. – LeGEC Feb 17 '20 at 08:46

2 Answers2

0

If you are acting on a feature branch, and only have local changes, I would suggest you to throw away your local merge, and re-merge from C.

# from your local 'feature' branch :
git checkout feature

# set your local branch to Dastardly Dan's version :
#    WARNING: do save (or commit) your local changes before running reset --hard,
#        they will be lost otherwise.
git reset --hard origin/feature

# you are now in 'C' state, merge master
git merge master

Another option is to ask Dan to rebase his work (commit C in your diagram) on top of your merge (commit m) in order to reach :

A - B - m - C' <-feature, origin/feature
       /
  Y - Z <-master
LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • I'm trying specifically to avoid redoing the merge. Even for the case that it's not that much work, it's more likely to cause problems doing it twice. And the second one is not a solution without editing the history for everyone (i.e. with a `push --force`) – Druckles Feb 15 '20 at 11:10
0

In order to go from :

      C --
     /     \
A - B - m - m2
       /
  Y - Z

to:

      C --
     /     \
A - B    -- m3
       /
  Y - Z

you can use git commit-tree as suggested in this answer :

git commit-tree -p C -p Z m2^{tree}
LeGEC
  • 46,477
  • 5
  • 57
  • 104