50

Usually, I work with branches in Git, but I don't like to see hundreds of branches in my working tree (Git history). I'm wondering if there is a method in Git to "join" all commits in a branch in only one commit (ideally with a clear commit message).

Something like this:

git checkout -b branch
<some work>
git commit -a -m "commit 1"
<some work>
git commit -a -m "commit 2"
<some work>
git commit -a -m "commit 3"
git checkout master
git SUPER-JOIN branch -m "super commit"

After this, only "super commit" will exist in the git log.

Ivan
  • 14,692
  • 17
  • 59
  • 96

3 Answers3

70

It sounds like you're looking for the --squash option of git-merge:

git checkout master
git merge --squash branch -m "super commit"
Ivan Zugec
  • 81
  • 8
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 3
    This may have worked in the past, but it does not work any longer. (git v1.7.9) The message `Fast-forward (no commit created; -m option ignored)` is output and I'm left with an uncommitted change. The `--no-ff` option looked promising but still created a merge with all the individual commits. – Brian White Jan 15 '14 at 02:00
  • @BrianWhite: Did you try both `--no-ff` and `--squash`? I don't know of any change in Git that would have changed the behaviour of this command. If you still can't get it to work, may I suggest asking a new question? – Greg Hewgill Jan 15 '14 at 02:04
  • 2
    I believe `--squash` always forbids doing an immediate commit. From my searches, the generally accepted answers are `merge --squash` plus separate commit (which technically leaves the branch "hanging", something visible in UI viewers) and `rebase --squash` plus a separate merge (which is more complicated and leaves the original branch without the individual commits). But I'll keep looking (and experimenting) to try to find something better. – Brian White Jan 15 '14 at 02:14
  • 1
    i got "Automatic merge went well; stopped before committing as requested." – krivar May 19 '14 at 07:27
44

This can be done using git rebase and squash, or using git merge --squash, see

Git merge flattening

and

git: squash/fixup earlier commit

Community
  • 1
  • 1
tonio
  • 10,355
  • 2
  • 46
  • 60
  • 2
    git merge --squash is amazing. I wish I had read this more carefully in 2011 ;) +1 – Dan Rosenstark Jan 06 '13 at 21:22
  • 7
    The problem with `git merge --squash` is that it doesn't actually create a merge commit. GUIs like GitHub's network viewer will not show the branches rejoining; one will simply end abruptly while the other continues. – Maxpm Jun 12 '13 at 17:54
16

If you are positive you want only a single commit and are fine with the branch never being marked as "merged" (perhaps because you're about to delete it with git branch -D my-squash-merged-branch and never want to see it again), use this:

git checkout master
git merge --squash branch-to-merge
git commit -m "message for commit"

However, after much testing, I believe the best way to merge most branches is:

git checkout master
git merge --no-ff branch-to-merge -m "message for commit"

This avoids the "fast-forward" merge that disallows specifying a -m "message" option for many merges. It doesn't actually provide a single commit as originally requested but at least makes it easy to see the begin/end of the branch and so make for easy reverts and the like. A git log will show all the individual commits that were merged...

commit a6672a4c3d90c35d5f39c45f307ef6b385660196
Merge: 015f8d6 f84e029
Author: Brian White <bcwhite@example.com>
Date:   Wed Jan 15 20:47:35 2014 -0500

    merged something trivial

commit f84e02915faa02afc9a31b8c93a6e7712420687d
Author: Brian White <bcwhite@example.com>
Date:   Wed Jan 15 20:47:12 2014 -0500

    added something also trivial

commit 904d5b5ff00d691d63104a77d2e2ca484732a5fb
Author: Brian White <bcwhite@example.com>
Date:   Wed Jan 15 20:46:26 2014 -0500

    added something trivial

commit 015f8d681bdaf65725067ee8058215cedb529dd6
Author: Brian White <bcwhite@example.com>
Date:   Wed Jan 15 20:23:31 2014 -0500

    optimizations to MyThing
...

... but if you look at a graph of the log (git log --graph), you can see that git does indeed recognize it as a single merge.

*   commit a6672a4c3d90c35d5f39c45f307ef6b385660196
|\  Merge: 015f8d6 f84e029
| | Author: Brian White <bcwhite@example.com>
| | Date:   Wed Jan 15 20:47:35 2014 -0500
| |
| |     merged something trivial
| |
| * commit f84e02915faa02afc9a31b8c93a6e7712420687d
| | Author: Brian White <bcwhite@example.com>
| | Date:   Wed Jan 15 20:47:12 2014 -0500
| |
| |     added something also trivial
| |
| * commit 904d5b5ff00d691d63104a77d2e2ca484732a5fb
|/  Author: Brian White <bcwhite@example.com>
|   Date:   Wed Jan 15 20:46:26 2014 -0500
|
|       added something trivial
|
* commit 015f8d681bdaf65725067ee8058215cedb529dd6
| Author: Brian White <bcwhite@example.com>
| Date:   Wed Jan 15 20:23:31 2014 -0500
|
|     optimizations to MyThing
...

If commits or other activity happens on the master branch, the graph will show the merged branch starting at the correct place and joining at the current head but of course all commits will still be shown in the log with the commits from the branch being at the top.

Brian White
  • 8,332
  • 2
  • 43
  • 67