21

I have a branch which I pulled from master branch. Let's call it integration.

In the integration branch, I made various commits (C1, C2, C3). When I am done, I made a Pull Request to the master branch. From the master branch, I did a "Squash and Merge" so it results in only a single commit in the master. This all seems great.

But then later, I made some additional changes to the integration branch and when I make a pull request again, I am seeing all the commit comments of previous changes (C1, C2, C3, C4) that I have already committed. This issue is not seen if I use the default "Create an Merge commit" in my earlier commit. What did I do wrong?

some user
  • 876
  • 1
  • 12
  • 26
  • I think this reply might help you. But instead of branches, you'll have to find the common-ancestor yourself. https://stackoverflow.com/a/70994400/1759443 – Kyle Venn Feb 05 '22 at 00:45

2 Answers2

19

It's easier to understand visually. Here's your repo. master is at commit B, and your feature branch is at commit C3.

A - B [master]
     \
      C1 - C2 - C3 [feature]

A normal merge does this. A new merge commit, BC123, is added combining the content in master with those in feature. The histories are linked together. Note that feature does not move, it's still at C3.

A - B ------------- BC123 [master]
     \            /
      C1 - C2 - C3 [feature]

A squash and merge does this.

A - B ------------- BC123 [master]
     \
      C1 - C2 - C3 [feature]

BC123 contains the same merged content as before, but there's no connection to the feature branch. And again, feature does not change. feature does not get squashed, it sticks around. Instead BC123 contains the squashed changes from feature.

When you did more work on feature, commits C4 and C5 here, this happened.

A - B ------------- BC123 [master]
     \
      C1 - C2 - C3 - C4 - C5 [feature]

And when you issue a pull request, all the changes in feature not in master will appear. As far as Git is concerned that's C1 to C5. If you were to squash & merge again, there would be a new commit on master, but only with the content of C4 and C5 because Git is pretty good at figuring out duplicate content between branches.

A - B ------------- BC123 - C45 [master]
     \
      C1 - C2 - C3 - C4 - C5 [feature]

While you can work this way, it's confusing.

Long story short: once you merge a branch, don't work on it anymore. Delete it. If you need to do more work open a new branch.

This issue is not seen if I use the default "Create an Merge commit" in my earlier commit.

Going back to the merged version...

A - B ------------- BC123 [master]
     \            /
      C1 - C2 - C3 [feature]

If you do more work on feature...

A - B ------------- BC123 [master]
     \            /
      C1 - C2 - C3 - C4 - C5 [feature]

And then do a pull request, Git will show you the commits in feature which are not in master. Because of the merge, master contains C1, C2, and C3. So the PR only shows you C4 and C5. This is still confusing, and the same advice applies: once you merge a branch, delete it. Open another one if you need to do more work.

While squash & merge is simpler, merging (done right) provides a healthier history and more information for Git to work from. You'll get more out of Git if you understand how branching and merging works.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • 1
    Maybe I am being naive. The difference in the 2 illustrated merge example is really just the merge arrow. Isn't it possible to do booth squash-and-merge and add a merge arrow? There is definitely value in squash and merge. – some user Mar 09 '18 at 19:47
  • 2
    @someuser: yes, the difference is "just" the merge arrow. But that's a huge difference: it changes the *merge base* (of the later merge), which is one of the three inputs to the merge-as-a-verb action. Use a different merge base and you get a different merge result. – torek Mar 09 '18 at 21:19
  • @someuser A "squash & merge" with the merge arrow is a normal merge. Git history is not linear and it confuses people (mostly Git's fault for `git log` faking a linear history by default). If you're using squash & merge to get a simpler, linear history there's [better ways to accomplish that without losing fidelity](https://stackoverflow.com/questions/40641146/what-are-the-conceptual-differences-between-merging-stashing-and-rebasing-in-gi/40641519#40641519). Unfortunately Github does not currently support this, you have to do it manually. – Schwern Mar 09 '18 at 21:23
  • @Schwern, in the first example (squash & merge), if I decided to not use squash & merge with the 2nd PR (C4-C5) but instead use standard merge, what will happen? Will the master look like `BC123--C4-C5` and then my branch will become current with master? – some user Mar 10 '18 at 22:43
  • @someuser `master` would add a merge commit with `BC123` and `C5` as its parents. `feature` would remain at `C5`. Merging only updates the branch doing the merging. The other branch is left alone. When you `git merge feature` from `master` only `master` changes. `feature` is left unchanged. – Schwern Mar 11 '18 at 00:59
7

If I'm understanding your situation correct...

The main thing you're doing wrong is continuing to use a branch after it has been merged. After you merge, delete the branch. Additional work should be on a new branch.

The first "Squash and Merge" does not affect the integration branch; just how the integrated commits appear on the master branch. So, it makes sense that A, B, & C would still be there since you are reusing the old branch.

Good luck.

Jamie Bisotti
  • 2,605
  • 19
  • 23
  • The real issue is the same problem exists between PR of 2 repos (one being the fork of another). I would have to delete the repo to avoid the old commits showing up in my next PR. – some user Mar 09 '18 at 19:48
  • I'm not sure I follow. I think you should be able to create a new branch from master, where C1, C2, & C3 are already merged. So, you'd just need to cherry-pick C4 into the new branch and open a PR. – Jamie Bisotti Mar 09 '18 at 19:53
  • I was referring to having 2 different repos. If I did the development on fork's master and used it to generate PR. I would run into the same issue after a squash and merge. Now I have additional changes in master that I need to merge to forked parent. Cherry-pick seems like a lot of work when there are merge conflict involved. – some user Mar 09 '18 at 21:57