Search code examples
gitgithubmergepull-requestsquash

My branches have diveregd after a "squash and merge". How do I fix that?


I did a 'squash and merge' to close a PR. Although all the changes are now present in master, it still says the branches have diverged and there are still changes ahead and behind.

This branch is 1 commit ahead, 28 commits behind develop. 

How do I fix this? The 28 commits behind are the ones which were squashed during 'squash and merge'. I've been through https://help.github.com/en/github/administering-a-repository/about-merge-methods-on-github#squashing-your-merge-commits , but could not find any connection.

I assumed (wrongly ?) that 'squash and merge' would do a fast forward merge to prevent the divergence. I didn't want all the commits from develop to move to master, which is why I did a "squash and merge", instead of rebasing. I think I'm missing something about squash and merge.

I've gone through similar questions on SO, but it ends up with suggestions to do an interactive rebase and squash commits before merging to master. But I don't want to lose the history in develop.


Solution

  • So your stated requirements are:

    1) There should be only one commit added to master

    2) All commits should remain on the develop branch (no loss of history)

    3) The new commit on the master branch should be related to the original commits, so that git doesn't think they've diverged

    The closest git will come to meeting these three requirements simultaneously is a simple merge.

    $ git checkout master
    $ git merge develop
    

    If you do this, it is true that when git displays master's history it will, by default, include the commits from develop; you can avoid that using the --first-parent option to commands ilke git log.

    The actual commit graph ends up looking like

    o -- x -- x -- M <--(master)
     \            /
      x --- x -- x <--(develop)
    

    so M (the merge commit) is added to master, and it "remembers" its relationship to the full history on develop.

    In fact the whole difference in doing a "squash merge" (as you noted) is that M "forgets" its relationship to develop - the 2nd parent isn't recorded.

    A rebase workflow sacrifices requirement (1) - and also requires rewriting of the commits in a way that creates new intermediate commits that may or may not be in a working state - in favor of a linear history.

    "Squash-and-merge" could "fix" the limitations of rebase, but it sacrifices requirement (3) - and unless you keep develop around as its own diverged branch forever, you then lose requirement (2).

    Personally I find those costs too high when the only payoff is a linear history; some people really seem to love that linearity and swear it's easier to understand, but I don't value it so much.

    Either way, though, those are the trade-offs each team needs to make is deciding on a change integration strategy.