while I am working on new code I make many small commits to track my changes. My company however prefers each feature to be committed in a single commit. So the solution is to squash my entire branch down to a single commit.
How do I squash an entire branch without using git rebase --interactive
and then changing pick
to squash
for all the commits?
Probably the best option for this would be to use
git merge --squash
at merge time. That will leave your branch as it developed, which is quite often a lot easier to troubleshoot with, because you'll have some notion of "I was changing that specific functionality in commit Z", and looking at that specific commit, you have all the context of any changes you made to multiple files - looking at a single commit that is the squashed results of your development path makes it quite a bit harder to remember "Oh, yeah, I had to change this one other thing in a different file, too...". You also have the benefit of usinggit bisect
when you have your entire path available - all it could tell you in the squashed case is "this huge commit here broke something".The result of using
git merge --squash
is a single commit on the branch that you are "merging" into that contains the cumulative changes from your branch, but it leaves your original branch alone.Find the hash for the commit just before you started the branch and copy it out into the clipboard. Then do a reset to to that hash.
Then just re add and re commit the changes in a single message.
The best would be to do a hard reset and merge the previous HEAD with squash. Here is an alias:
This way you can call it like so:
Or to squash from another branch, like
dev
:This is a perfect use case for
git reset --soft
.Assume you have a commit history
you have no staged changes, and
git status
,git log
orgit show
tell you are currently at commit D.Then
git reset --soft B
will take the cumulative changes of commitsC
andD
and stage them for commit.git commit --amend
will then 'merge' these changes into commit B.Use as follows:
The second command will bring up your editor with a chance to edit the commit message.
Note that if you have staged changes before starting this process (i.e. you have done
git add XXX
but not followed up with agit commit
) then those staged changes will also be merged into the commit.My preferred method is a two-liner (excluding steps 1 and 4 below). The benefits are you do not need to know/record any commit IDs, you can write a simple alias to perform all the steps involved, and your actually moving your entire branch onto origin/master so that the actual merge into master can be a fast-forward and there cannot be any conflicts.
First, my assumptions:
my-feature-branch
. This branch has diverged frommaster
by several commits; this is the checked-out branch.master
tracks remote branchorigin/master
my-feature-branch
into a single commit ontop of the current state of origin/master (not your localmaster
, which may be out of date)git reset --hard
)My process is as follows:
Fetch, so
origin/master
is current:Throw away all the commits on your local branch by resetting it to point at
origin/master
Merge all of your old changes from the previous state of your branch into the index
Commit your changes - Git will pre-populate your editor with a commit message containing all the commit messages from the squashed commits
The simple alias I mentioned would be:
Edit your git configuration file
~/.gitconfig
and add the following to the alias sectionThis alias gets the current HEAD commit hash, resets back to the commit you specify, and creates a new commit preserving all of the commit messages.
Usage:
refspec can be any valid commit reference such as a commit hash, branch name, tag name,
HEAD^
HEAD~3