What is the difference between merge --squash and rebase?
Solution 1
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 c--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 (stilltmp
) with:- a new base
- a cleaner history
Solution 2
Merge commits: retains all of the commits in your branch and interleaves them with commits on the base branch
Merge Squash: retains the changes but omits the individual commits from history
Rebase: This moves the entire feature branch to begin on the tip of the master branch, effectively incorporating all of the new commits in master
More on here
The first two diagrams come from About pull request merges on the GitHub Docs
Solution 3
Let's start by the following example:
Now we have 3 options to merge changes of feature branch into master branch:
Merge commits
Will keep all commits history of the feature branch and move them into the master branch
Will add extra dummy commit.Rebase and merge
Will append all commits history of the feature branch in the front of the master branch
Will NOT add extra dummy commit.Squash and merge
Will group all feature branch commits into one commit then append it in the front of the master branch
Will add extra dummy commit.
You can find below how the master branch will look after each one of them.
In all cases:
We can safely DELETE the feature branch.
Solution 4
Merge squash merges a tree (a sequence of commits) into a single commit. That is, it squashes all changes made in n commits into a single commit.
Rebasing is re-basing, that is, choosing a new base (parent commit) for a tree. Maybe the mercurial term for this is more clear: they call it transplant because it's just that: picking a new ground (parent commit, root) for a tree.
When doing an interactive rebase, you're given the option to either squash, pick, edit or skip the commits you are going to rebase.
Hope that was clear!
GiH
Updated on July 08, 2022Comments
-
GiH almost 2 years
I'm new to git and I'm trying to understand the difference between a squash and a rebase. As I understand it you perform a squash when doing a rebase.
-
Wayne Conrad about 14 years
G
isc--d--e--f--g
squashed together? -
VonC about 14 years@Wayne: yes, G in those examples represent the
tmp
commits squashed together. -
Alexander Bird almost 13 yearsIsn't the data in
G
exactly the same as ing
? So would that mean thatG
is the same asg
except that it has a different parent? -
VonC almost 13 years@Th4wn: Since Git reasons with snapshots of a all project,
G
won't represent the same content thang
, because of changes introduced byX
. -
VonC almost 13 yearsNote to self: a "cleaner history" is important, considering a good Git workflow: sandofsky.com/blog/git-workflow.html
-
naught101 over 11 years@VonC: not sure about that last comment. If you have a
git merge --no-ff temp
instead ofgit merge --squash temp
, then you get a messier history, but you can also do things likegit revert e
, much more easily. It's a messy, but honest and pragmatic history, and the main branch still remains fairly clean. -
VonC over 11 years@naught101 I agree. As explained in stackoverflow.com/a/7425751/6309 though, it is also about not breaking
git bisect
orgit blame
when used too often (as ingit pull --no-ff
: stackoverflow.com/questions/12798767/…). There isn't one approach anyway, which is why this article described three (stackoverflow.com/questions/9107861/…) -
Mr_and_Mrs_D almost 11 yearsHmm - if say commit
c
involved deleting a file that was added ina
so also present inX
thengit merge --squash
would have this file inG
- see here. So G and g would be different versions of the working tree ! I don't know of other pitfalls but probablygit merge --squash
is not the way to mirror the state oftmp
onstable
. See also this -
Martin Thoma almost 8 yearsWhen should I rebase and when should I squash?
-
Martin Thoma almost 8 yearsWhen should I rebase and when should I squash?
-
VonC almost 8 years@MartinThoma not that a rebase can also allow a squash (blog.ona.io/general/2016/02/02/…) More generally, you squash when you do not need the intermediate commits. This is a current practice when accenting a PR (Pull Request): see github.com/blog/2141-squash-your-commits
-
Yusuf almost 4 yearscan you explain what is dummy commit in 2nd picture ?? I am a beginner in git.
-
ahmednabil88 almost 4 years@Yusuf, it's just an extra commit which contains both branches updates, it's default commit message = "Megre branch XYZ into master"
-
Mauricio Scheffer almost 4 years@MartinThoma see: lwn.net/Articles/328436 lwn.net/Articles/791284
-
leticia almost 4 yearsFor "Squash and merge": there is a commit with all grouped commits plus an "extra dummy commit"?
-
ahmednabil88 almost 4 years@leticia the commit with all grouped commits = the "extra dummy commit" itself, as the above graph
-
max over 3 yearsIt does not matter which you use but I recommend rebase. Rebase changes the parent node of the feature branch but merge does not and I recommend it because it keeps the commit structure simpler but as a git user, it makes not different. stackoverflow.com/questions/2427238/….
-
BozanicJosip about 3 yearsThen I would argue that 'squash and merge' will not add an extra dummy commit, but rather will just 'rebase'/append the commit in front of the master branch. Dummy commit in the context you are describing it above is not the same in 1. and 3. as dummy commit in 1 is 'Merge branch XYZ into master which is producing this commit' and dummy commit in 3 is 'Squashed commits into this one commit which is not additional commit produced by merge'
-
IMSoP almost 3 yearsThat first diagram looks completely wrong to me. Somehow, commit D has ended up with no parent.
-
XardasLord over 2 yearsPerfect diagrams and explanation!
-
rahulaga-msft over 2 years@ahmednabil88 nice explanation !! in the case of
squash merge
(final 3rd figure above) can we say all commits from the feature branch combined into a single commit and that single commit rebased on top ofmaster
branch like any other usual rebase merge? -
becko about 2 yearsI don't see the difference between "Merge commit" and "Squash and merge". It seems the master branch looks identical in the final state in both cases.