7

There are two branches I am working with, master and newFeature. While building the "new feature", I added multiple commits to the newFeature branch. My understanding of git merge is that it will create a single commit on master once the branches are merged, however when merged, master now has the full commit history that is on newFeature. For example-

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=>4=>5=>6

master (expected results of merge):

    1=>2=>3=>6

Is there any way to remove the intermediary commits from newVersion during the merge, and why isn't merge working as expected?

John Smith
  • 7,243
  • 6
  • 49
  • 61
  • 1
    `git merge` creates a new commit, if needed, but **it doesn't remove any of the existing commits**. In your case it didn't create any new commit. It just moved the `master` branch to point to the commit already pointed by the `newVersion` branch. This happened because the two branches didn't actually diverged and a new commit was not necessary. – axiac Jul 18 '17 at 21:52
  • You could rebase your newVersion branch into one commit. – Tom Jul 18 '17 at 22:11

2 Answers2

9

The key to note here, is that no changes have been made to master throughout the course of your work on newVersion. In these situations, Git defaults to a "fast-forward" merge, which can essentially be thought of as taking all of the new commits from newVersion and appending them to the most recent commit on master (which does not separate the commit history that was done on newVersion). This can be overridden with the --no-ff flag, for example:

git merge newVersion --no-ff

Results in:

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=========>7
            4=>5=>6

Note that, commit 7 represents the merge, and does not replace the commit history.

Reference: https://sandofsky.com/images/fast_forward.pdf

Alternatively, if you would prefer to consolidate the entire commit history of newVersion into a single commit on master (could be useful if they were simply minor commits throughout the progression of the "new version") you could run the merge with the --squash flag. For example:

git merge --squash newVersion

Results in:

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=>7

Note that, 7 consolidates the commit history that was done in commit 4 - 6

John Smith
  • 7,243
  • 6
  • 49
  • 61
  • Rather: 1=>2=>3=>7 (and 7 has two parents: 3 and 6) – eftshift0 Jul 18 '17 at 21:49
  • 2
    That's not what `--no-ff` does. Your history with `--no-ff` will look like `1=>2=>3=>4=>5=>6=>7` where `7` is a merge commit. All a fast-forward merge does is avoid creating the merge commit. – Michael Mior Jul 18 '17 at 21:49
  • @MichaelMior I would show you a screenshot if I could, but based on the `log` the history I am seeing, everything looks good now. @Edmundo is correct however, it would actually be a new commit (ie. 7 instead of 6) – John Smith Jul 18 '17 at 21:59
  • @iliketocode If you look at `git log --graph` you should see that commit `7` has two parents - `3` and `6`. – Paul Jul 18 '17 at 22:02
  • 1
    1) I have no idea why this was downvoted. This is the correct answer, it mentions "fast forward", and explains *exactly* what happens. 2) For didactic value, in your "master (actual results of merge)" picture, I would show the branch as well, so it is clear that there is a sequence of commits `3=>4=>5=>6=>7` *in addition* to what you have shown. That part is as "valid" and important as the `3=>7` part, and will stay a part of the history forever. – AnoE Jul 18 '17 at 22:08
  • 1
    @AnoE I downvoted a previous version of the answer which had some incorrect information. The edit has since turned by downvote around :) – Michael Mior Jul 19 '17 at 00:59
1

It's not possible to have a final history that looks like what you want. Each commit in git is tied to its parent. Each commit contains a reference to its parent, so 6 explicitly references 5.

If you do actually want only the changes between 5 and 6 to be added on top of 1=>2=>3, then you can use git cherry-pick newFeature. This will create a new commit which has just the changes between 5 and 6, but applied on top of 3.

If you want all the changes from 4, 5, and 6 but you only want a single commit, then you can use the --squash flag to git merge. This will create a single new commit which contains all the changes from 4, 5, and 6. However, your history will not be 1=>2=>3=>6, but 1=>2=>3=>7 where 7 is this new commit.

Note that if you choose the --squash option, you can only do this once for each branch since by squashing, you're losing the information about what's different between the two branches.

Michael Mior
  • 28,107
  • 9
  • 89
  • 113
  • I may still need to look into it a bit, after your answer.. https://sandofsky.com/images/fast_forward.pdf – John Smith Jul 18 '17 at 22:08
  • "6 contains only the changes from 5 to 6. " That is not correct. Commits in git do not record changes. Each commit is fully self contained. It would be perfectly possible to create the history that he wants (if identifying his numbers 1, 2, 3... with the content of the commits, not the actual hash, of course). – AnoE Jul 19 '17 at 06:05
  • @AnoE You're correct that my explanation was overly simplified. My point was that a commit depends on its parent so you can't have the exact same commit be moved to have a different parent since it's hash would change. I edited the answer to clarify. – Michael Mior Jul 19 '17 at 13:55
  • @MichaelMior, yes, my comment was mostly about the formulation - this particular formulation ("a commit contains changes") is a big source of confusion for people coming from other VCSses like svn, where this would indeed be true. Your current formulation is fine. Oh, you could replace "parent" with "parents", as a merge commit has multiple ones. ;) – AnoE Jul 19 '17 at 14:02
  • Oh, and I'm not sure about your last sentence "only do this once for each branch". What do you mean by that? – AnoE Jul 19 '17 at 14:03
  • I mean that you can't use `git merge --squash` to merge the same branch multiple times as you change the branch. – Michael Mior Jul 19 '17 at 14:24