Search code examples
gitmergegit-flowgit-merge-conflictsquash

GitFlow, squashing and merging issues


I'm using GitFlow in my git repository, so I have a master, develop and (temporary) release branch.

Workflow

  1. I create a new branch from develop (e.g. fix/fix-the-bug)
  2. I squash my fix into meaningful commits
  3. I merge my fix/fix-the-bug branch into develop
  4. Once I've got enough branches merged, I create a (temporary) release/x.y.z branch from develop
  5. I version-bump my scripts in the release/x.y.z branch and tag that commit
  6. When I want to merge release/x.y.z into master, I get merge conflicts. It seems like master does not understand that commits are already present in master
  7. release/x.y.z branch gets merged into develop
  8. I delete release/x.y.z

Few things to notice, not sure if they are all correct:

  • I squash my commits into one commit when merging into master
  • There should be a git tag on master indicating the version number, but not sure if that would work correctly if I squash the commits.

Question

I'm now wondering:

  • how I can fix my repo, since I don't think I should be getting those conflicts.
  • Any further advice on the workflow (e.g. in which part I could perform the squash best) would be welcome.

Solution

  • I squash my commits into one commit when merging into master

    It sounds like you're using git merge --squash when you merge to master. This is not standard gitflow practice; you would just do a normal merge.

    The entire difference between a regular merge and a squash merge is that a squash merge does not record the relationship between the new commit on the branch you're merging into (i.e. master in this case) and the original commits on the source branch; that's why subsequent merges don't understand that the content on master already corresponds to a previous state of develop.

    The "downside" to using a regular merge is that git's default output, when logging master for example, would include all the individaul commits instead of just a list of the release commits on master; but you can fix that by using the --first-parent option.

    To put this into visuals, you start with a blank repo

    o <--(master)
    

    Without creating commits, you start the develop branch, and then a fix branch on which you do some work

    o <--(master)(develop)
     \
      A <--(fix)
    

    You merge to dev

    o <--(master)
    |
    |- M <--(develop)
    \ /
     A <--(fix)
    

    You might do more fixes

    o <--(master)
    |
    |- M - M2 <--(develop)
    | / \ /
    | |  B <--(fix2)
    \ |
     A <--(fix)
    

    Now if you squash merge to master, you'll get something like

    o -------- AB <--(master)
    |
    |- M - M2 <--(develop)
    | / \ /
    | |  B <--(fix2)
    \ |
     A <--(fix)
    

    and AB contains all the changes that A and B introduced, but as far as git is concerned, that's coincidental; and once develop contains additonal changs, even the fact that the changes are "the same" will be lost, and conflicts (as you've experienced) result.

    So instead you do a regular merge - just leave out the --squash option, assuming you were using squash merges in the first place:

    o ------- AB <--(master)
    |        /
    |- M - M2 <--(develop)
    | / \ /
    | |  B <--(fix2)
    \ |
     A <--(fix)
    

    This is how git means for merges to work; now future merge attempts will "know" that M2 (and everything it entails) is already included in master and only changes after M2 will be included as "their changs" in the merge calculation.

    This also is how gitflow intends things to be done.