I branched out from master and worked in my new branch branchA, I made a few commits in branchA and merged master now and then into branchA to keep it updated with the latest change. Now I want to squash my commits into one commit before I merge into master, what should I do? I know git merge --squash
can squash the commits. However, I don't want to merge it now because I'm waiting for code review approval. It's not good to use rebase too because I merged master into branchA, I'm wondering if there is any good way to squash the commits. Thanks!

- 1,762
- 1
- 20
- 36
-
You said that you've already sent the commits for review so you already made them public - isn't it too late for squash? You can always create a new branch and push it for review and abandon the previous review request, or make a squash and push with `--force` if you are permitted to do so on the remote server. Depending on what `review` means in your place it may also involve commit messages clarity, commits contents etc. – Arkadiusz Drabczyk Mar 14 '17 at 18:41
-
@ArkadiuszDrabczyk Yes, it is public now. But I want to have only one clean commits before it get merged in master. – litaoshen Mar 14 '17 at 19:28
1 Answers
UPDATED : Added comments at end about the case where branch
has been pushed
So you have
X --- X --- X --- X --- X <--(master)
\ \
A --- B --- M1 --- C <--(branch)
It sounds like once you're done you'd like
X --- X --- X --- X --- X --- ABC <--(master)
but you're not ready to put ABC
on the master
branch.
I assume that A
, B
, M1
, and C
have never been pushed. If this assumption is false, I wouldn't squash them by any method.
So my first thought would be, wait for the code review and then afterward do the squash merge. If A
...C
are destined to be discarded, what difference does pre-squashing them make?
But if you don't like that for some reason and really want to pre-squash, how about aiming for this:
X --- X --- X --- X --- X <--(master)
\
ABC <--(branch)
There are a few ways to get to this. Here's one:
git checkout master
git checkout -b temp
git merge --squash branch
git branch -f branch
git checkout branch
git branch -d temp
But based on discussion in comments, it now sounds like the A
, B
, and C
commits have been pushed. If that's true, then any technique for squashing them has drawbacks.
My best advice in that case is to merge as is, write a good commit message on the merge commit, and when looking at the history use git log --first-parent
. In this case the individual commits on the branch won't show in the log output, and you'll see a single commit (the merge itself) representing the entire branch just as if it were a squashed commit.
But if you don't want to follow that advice, and really must squash the changes, here's the deal: Anyone continuing work on branch
will have a problem. And you can say "nobody should do that", and if your team agrees that nobody will ever do that then it'll work out. But since branch
is present in a shared repo, nothing would stop it from happening; and then at best you're going to have to deal with messy conflict resolutions.
Suppose you follow the steps above; what you really have is
X --- X --- X --- X --- X <--(master)
\ \ \
\ \ ABC <--(branch)
\ \
A --- B --- M1 --- C <--(origin/branch)
If you try to push branch
it will fail (because origin/branch
is not reachable from branch
). You could force the push, and as soon as you do that anyone with a local branch
reference that points to C
will start having issues. Not that they can't be corrected, but it could become a hassle, especially if anyone "fixes" it the wrong way.
You could avoid that by never force-pushing branch
. You'd wait for the chance to merge your version of branch
to master, then push master
and delete your local branch
.
X --- X --- X --- X --- X --- ABC <--(master)
\ \
A --- B --- M1 --- C <--(origin/branch)
Now, even if you force deletion of the remote origin/branch
reference, other devs might still have local branch
references pointed at C
. And since that history was not merged into master
(but rather ABC
was created to update master in a single non-merge commit), they'll have trouble if ever they merge (further changes to) branch
into master
.
You could get clever with a follow-up merge using strategy "ours" or something to show Git that branch
is an ancestor of master
... but at that point, you might as well have just done a normal merge back at the very beginning, because that merge will draw A
, B
, and C
into the default log history of master
just like a normal merge would've (i.e. such that to exclude it you could use --first-parent
).
Again, all of this can be managed; but it's unnecessary hassle, which is why the best practice is: once a commit has been shared, allow it to be part of the history.

- 42,148
- 4
- 35
- 52