70

As the title says, I am not really clear about the differences between a git merge --squash and a git merge --no-commit.

As far as I understand the help page for git merge, both commands would leave me in an updated working-tree, where it is still possible to edit and then to do a final commit (or multiple commits).

Could someone clarify the differences of those 2 options? When would I use one instead of the other?

quaylar
  • 2,617
  • 1
  • 17
  • 31

3 Answers3

84
git merge --no-commit

This is just like a normal merge but doesn't create a merge-commit. This commit will be a merge commit: when you look at the history, your commit will appear as a normal merge.

git merge --squash

This will merge the changes into your working tree without creating a merge commit. When you commit the merged changes, it will look like a new "normal" commit on your branch: without a merge commit in the history. It's almost like you did a cherry-pick on all the merged changes.

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
rtn
  • 127,556
  • 20
  • 111
  • 121
  • So a merge-commit would show up how in the history? Since i can edit the commit-message, how would i see that it was a merge-commit? – quaylar Mar 07 '12 at 10:38
  • 3
    @quaylar A merge-commit is not really a special commit, except that it has multiple parents. You can see the parents for a commit for example with `git log --parents` (and `git log --merges` shows only such commits). – Philipp Wendler Mar 07 '12 at 10:57
  • 6
    @PhilippWendler So essentially: Having a merge-commit I'd always know by looking at the history, that this commit was the result of a merge (is the information which branches contributed to this merge also kept?). Using --squash there is no way to know that this commit was the result of a merge. Am I understanding that correctly? – quaylar Mar 07 '12 at 11:39
  • 5
    @quaylar Yep you understand it correctly. However, it's no guarantee that you can see exactly what branch that was merged since branches can be removed/renamed etc. – rtn Mar 07 '12 at 12:14
  • 4
    @quaylar I really recommend that you play around with the commands to see for yourself exactly how it looks in history. Remember that you can always undo in git, just do "git reset --hard HEAD". – rtn Mar 07 '12 at 12:19
  • 1
    While I recommend @quaylar some of the same things I did recently: read this excellent post and its follow up on nvie http://stackoverflow.com/questions/2850369/why-does-git-use-fast-forward-merging-by-default – cregox May 16 '12 at 17:14
  • 1
    What is the effect if _both_ options are used? In the example given [here](http://git-scm.com/book/en/git-tools-subtree-merging), they use `git merge --squash -s subtree --no-commit rack_branch`, which seems redundant. I would imagine this would have the same effect as if `--no-commit` was left out. – Tom Crockett Dec 03 '13 at 20:49
  • A normal merge (non-fast-forward) can only be finalized with a commit. Knowing this, there is no way for me to understand your answer. :-( – Robert Siemer Nov 13 '14 at 19:18
  • @RobertSiemer yes thats correct. Both commands will do a merge, and both commands will leave you with the changes in the index, but None of those 2 commands will do the commit. That is up to you. So what is the difference: Upon committing, version 1 will create a real merge-commit, meaning you will see in the history that another branch was merged. Upon committing, version 2 will create a normal commit, and you will not see in history that this commit was the result of a merge. – quaylar Dec 16 '14 at 11:56
  • 22
    *"This is just like a normal merge but doesn't create a merge-commit. This commit will be a merge commit: when you look at the history, your commit will appear as a normal merge."* Am I the only one who finds that that explanation makes *no* sense at all? And doesn't "squash" put all the changes into a single commit (which is quite different to cherry picking multiple commits)? – Simon East Jul 31 '15 at 06:07
  • 1
    @SimonEast has the best summary answer to actual behavior imo. --no-commit will just prevent git from auto committing a non-conflict change-list. you can easily use both commands in together and end up in a state where many commits have been "squashed" into one that you can then manually commit or manipulate before committing. – 10cool Oct 12 '18 at 21:35
  • 1
    @SimonEast the point is on the "**will be** a merge commit", since it is not yet a merge commit, right after you fire the command, but it will as soon as you commit the changes made by `git merge --no-commit`. – andreagalle Sep 13 '22 at 15:05
0

Basically, there's no such difference at the very end, however looking at the sketch here below, once you checkout master branch

          A---B---C topic
         /         \
    D---E---F---G---H master        (from the documentation https://git-scm.com/docs/git-merge)
  • git merge topic : incorporates/replay changes from the named branch (since its history diverged from the current branch) into the current branch and record the result in a new commit H along with the names of the two parent commits and a log message from the user describing the changes. Then it's up to you to push it upstream, as usual.
$ git log --all --decorate --oneline --graph

*   72e576d (HEAD -> master) H Merge branch 'topic'
|\
| * dda0d97 (topic) C
| * 3471c39 B
| * cfb8410 A
* | a1c7e23 G
* | 5c0b97b F
|/
* c463d89 E
* 2312a5b D

$ git show HEAD -m

commit 72e576d (from a1c7e23) (HEAD -> master)
Merge: a1c7e23 dda0d97

    H Merge branch 'topic'

diff --git a/A b/A
diff --git a/B b/B
diff --git a/C b/C

commit 72e576d (from dda0d97) (HEAD -> master)
Merge: a1c7e23 dda0d97

    H Merge branch 'topic'

diff --git a/F b/F
diff --git a/G b/G

  • git merge --no-commit : the same as above, but stop just before creating a new commit, to give the user a chance to inspect and further tweak the merge result before committing. Then it's up to you to commit what has been added to the index and push it upstream, as usual.
$ git log --all --decorate --oneline --graph

*   f576f37 (HEAD -> master) H
|\
| * dda0d97 (topic) C
| * 3471c39 B
| * cfb8410 A
* | a1c7e23 G
* | 5c0b97b F
|/
* c463d89 E
* 2312a5b D

$ git show HEAD -m

commit f576f37 (from a1c7e23 ) (HEAD -> master)
Merge: a1c7e23 dda0d97

    H

diff --git a/A b/A
diff --git a/B b/B
diff --git a/C b/C

commit f576f37 (from dda0d97) (HEAD -> master)
Merge: a1c7e23 dda0d97

    H

diff --git a/F b/F
diff --git a/G b/G

  • git merge --squash : working tree and index state are as if a real merge happened (except for the merge information), without committing it, thus allowing to create a single commit on top of the current branch. Again, it's up to you to commit and push upstream, as before.
$ git log --all --decorate --oneline --graph

* 118019a (HEAD -> master) H
* a1c7e23 G
* 5c0b97b F
| * dda0d97 (topic) C
| * 3471c39 B
| * cfb8410 A
|/
* c463d89 E
* 2312a5b D

$ git show HEAD -m

commit 118019a (HEAD -> master)

    H

diff --git a/A b/A
diff --git a/B b/B
diff --git a/C b/C

NOTE: fast-forward updates do not create a merge commit, thus, to ensure your branch is not changed/updated use --no-ff with --no-commit option.

andreagalle
  • 620
  • 6
  • 17
0

The difference between git merge --no-commit and git merge --squash is that in the second case the next commit will have a single parent commit.

For example, in this situation

enter image description here

the commands

git merge dev --no-commit
git commit -m "msg"

or simply git merge dev -m "msg" will produce

enter image description here

On the other hand

git merge dev --squash
git commit -m "msg"

gives you

enter image description here

The commits E and E' will give you the exact same working directory. The difference is in their number of parents.

(Personally, I wouldn't have called this operation "squashing".)

actual_panda
  • 1,178
  • 9
  • 27