Search code examples
gitmergerebasegit-rebasesquash

What is the difference between merge --squash and rebase?


I'm trying to understand the difference between a squash and a rebase. As I understand it, one performs a squash when doing a rebase.


Solution

  • Both git merge --squash and git rebase --interactive can produce a "squashed" commit. But they serve different purposes.

    will produce a squashed commit on the destination branch, without marking any merge relationship. (Note: it does not produce a commit right away: you need an additional git commit -m "squash branch")

    This is useful if you want to throw away the source branch completely, going from (schema taken from SO question):

    git checkout stable
    
              X               stable
             /
    a---b---c---d---e---f---g tmp
    

    to:

    git merge --squash tmp
    git commit -m "squash tmp"
    
    
    # In the following graph, G is d--e--f--g squashed together
    
              X-------------G stable
             /
    a---b---c---d---e---f---g tmp
    

    and then deleting tmp branch.


    Note: git merge has a --commit option, but it cannot be used with --squash. It was never possible to use --commit and --squash together. Since Git 2.22.1 (Q3 2019), this incompatibility is made explicit:

    See commit 1d14d0c (24 May 2019) by Vishal Verma (reloadbrain). (Merged by Junio C Hamano -- gitster -- in commit 33f2790, 25 Jul 2019)

    merge: refuse --commit with --squash

    Previously, when --squash was supplied, 'option_commit' was silently dropped. This could have been surprising to a user who tried to override the no-commit behavior of squash using --commit explicitly.

    git/git builtin/merge.c#cmd_merge() now includes:

    if (option_commit > 0)
        die(_("You cannot combine --squash with --commit."));
    

    replays some or all of your commits on a new base, allowing you to squash (or more recently "fix up", see this SO question), going directly to:

    git checkout tmp
    git rebase -i stable
    
       stable
          X----------------G tmp
         /
    a---b
    

    If you choose to squash all commits of tmp (but, contrary to merge --squash, you can choose to replay some, and squashing others).

    So the differences are:

    • squash does not touch your source branch (tmp here) and creates a single commit where you want.
    • rebase allows you to go on on the same source branch (still tmp) with:
      • a new base
      • a cleaner history