Git - squash entire branch - one line squash command
Solution 1
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:
- You're working on a branch called
my-feature-branch
. This branch has diverged frommaster
by several commits; this is the checked-out branch. - Your local
master
tracks remote branchorigin/master
- You want to squash all of your commits from
my-feature-branch
into a single commit ontop of the current state of origin/master (not your localmaster
, which may be out of date) - All of your changes are committed, you have no unstaged changes (they will be lost during
git reset --hard
)
My process is as follows:
Fetch, so
origin/master
is current:$ git fetch
Throw away all the commits on your local branch by resetting it to point at
origin/master
$ git reset --mixed origin/master
Merge all of your old changes from the previous state of your branch into the index
$ git merge --squash HEAD@{1}
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:
alias squash="git fetch; git reset --mixed origin/master; git merge --squash HEAD@{1}"
Solution 2
This is a perfect use case for git reset --soft
.
Assume you have a commit history
D Your latest patch
C Your second patch
B Your first patch
A Someone else's work
you have no staged changes, and git status
, git log
or git show
tell you are currently at commit D.
Then git reset --soft B
will take the cumulative changes of commits C
and D
and stage them for commit. git commit --amend
will then 'merge' these changes into commit B.
Use as follows:
git reset --soft B
git commit --amend
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 a git commit
) then those staged changes will also be merged into the commit.
Solution 3
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 using git 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.
Solution 4
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.
$ git reset --soft [hash]
Then just re add and re commit the changes in a single message.
$ git add -A
$ git commit -m 'EVERYTHING SQUASHED'
Solution 5
Edit your git configuration file ~/.gitconfig
and add the following to the alias section
[alias]
squash = "!f(){ CUR=`git rev-parse HEAD` && git reset --soft ${1} && git commit -m \"$(git log --format=%B ${1}..${CUR})\"; };f"
This 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:
git squash <refspec>
refspec can be any valid commit reference such as a commit hash, branch name, tag name, HEAD^
HEAD~3
Related videos on Youtube
troymass
Chicago Java, Spring, and ElasticSearch developer and avid Linux enthusiast. I primarily write REST services and some frontend web applications. Also familiar with C/C++, PHP, and HTML/CSS.
Updated on July 16, 2022Comments
-
troymass almost 2 years
Whilst I am working on new code, I make many small commits to track my changes. My company, however, prefers each feature to be committed as a single commit. So the solution is to squash my entire (local) branch down to a single commit.
How do I squash an entire branch without using
git rebase --interactive
and then changingpick
tosquash
for all the commits?-
twalberg over 9 yearsPerhaps using
git merge --squash
to avoid having to squash your entire branch before hand? -
jthill over 9 years@twalberg please make that an answer
-
user229044 over 9 years
git merge --squash
does not work if you're using Github pull requests to do most of your branch merging, which is a pretty common case -
twalberg over 9 years@meagar It can be made to work even then, with a little extra effort. Namely a second branch specifically for integration. You do all your work on your
dev
branch, then do agit --merge squash
onto yourintegration
branch, and useintegration
as the source for your pull request. -
user229044 over 9 years@twalberg That's not a "little extra effort", that's a ton of ugly overhead.
-
Alexey over 9 yearsThe best thing i can think of is
git reset --soft
followed bygit commit
. -
Alexey over 9 years(And maybe followed by
git diff
to check the result.)
-
-
jub0bs over 9 yearsIf you want to do a soft reset, you need to explicitly use the
--soft
flag (git reset --soft <commit>
), because Git does a mixed reset, by default. -
onlythefinestwilldo over 9 yearsJut a default mixed reset achieves the desired squashing.
-
jub0bs over 9 yearsYes, but you write: do a soft reset to to that hash; that's misleading.
-
Mariano Desanze almost 5 yearswhy
git reset --mixed
instead of--hard
? The mixed reset in my case kept all the fetched files in the working copy and that failed at the merge step. -
Rob Cannon about 4 yearsI think this is a really under appreciated answer and useful for more than just merging.
-
Chris Moschini over 3 yearsHmmm if you do this, you will no longer be able to pull or push to your remote. You'll get the error,
fatal: refusing to merge unrelated histories
-
ianinini over 3 years@ChrisMoschini, this error would only occur if the user is manipulating a patch that has already been pushed. The OP was asking about manipulating his local patches (those that have not been pushed). In my example, patches B,C, D are local patches only. Patch A represents the HEAD of the branch on the server. The patches B,C and D are converted into a single patch.