How do I squash my last N commits together into one commit?
- 9Related:Git - combining multiple commits before pushing.user456814– user4568142014-06-06 08:07:03 +00:00CommentedJun 6, 2014 at 8:07
- 6For squashing upto THE first commit see this -stackoverflow.com/questions/1657017/…goelakash– goelakash2016-03-06 13:26:26 +00:00CommentedMar 6, 2016 at 13:26
- 4post squash one need to do force pushstackoverflow.com/questions/10298291/…vikramvi– vikramvi2016-07-14 13:04:41 +00:00CommentedJul 14, 2016 at 13:04
- 4In addition to the posted answers, GUI clients can do this easily. I can squash dozens of commits in GitKraken with only four clicks.Aaron Franke– Aaron Franke2019-01-14 12:56:31 +00:00CommentedJan 14, 2019 at 12:56
- All answers i tried messed up submodules.This answer didn't.ridilculous– ridilculous2023-04-03 21:32:58 +00:00CommentedApr 3, 2023 at 21:32
47 Answers47
You can do this fairly easily withoutgit rebase
orgit merge --squash
. In this example, we'll squash the last 3 commits:
git reset --soft HEAD~3
If you also want to write the new commit message from scratch, this suffices:
git reset --soft HEAD~3git commit -m "Squashed commit"
If you want to start editing the new commit message with a concatenation of the existing commit messages (i.e. similar to what a pick/squash/squash/…/squashgit rebase -i
instruction list would start you with), then you need to extract those messages and pass them togit commit
:
git reset --soft HEAD~3 && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
Both of those methods squash the last three commits into a single new commit in the same way. The soft reset just re-points HEAD to the last commit that you do not want to squash. Neither the index nor the working tree are touched by the soft reset, leaving the index in the desired state for your new commit (i.e. it already has all the changes from the commits that you are about to “throw away”).
Edit Based on Comments
Becausegit reset
andgit rebase
rewrite history, you must use the--force
flag to push the modified branch to the remote. This is what the--force
flag is meant for. You can also be extra careful and fully define your target:
git push --force-with-lease origin <branch-name>
40 Comments
git rebase --squash-recent
, or evengit commit --amend-many
.branch@{upstream}
(or just@{upstream}
for the current branch; in both cases, the last part can be abbreviated to@{u}
; seegitrevisions). This may differ fromyour “last pushed commit” (e.g. if someone else pushed something that built atop your most recent push and then you fetched that), but seems like it might be close to what you want.push -f
but otherwise it was lovely, thanks.git push --force
afterwards so that it takes the commitUsegit rebase -i <after-this-commit>
and replace "pick" on the second and subsequent commits with "squash" or "fixup", as described inthe manual:
"fixup" to discard the commit message, and squash it right away.
"squash" to combine the commit message with the previous one, and be able to edit it before squashing.
In this example,<after-this-commit>
is either the SHA1 hash or the relative location from the HEAD of the current branch from which commits are analyzed for the rebase command. For example, if the user wishes to view 5 commits from the current HEAD in the past, the command isgit rebase -i HEAD~5
.
21 Comments
<after-this-commit>
is commit X+1 i.e. parent of the oldest commit you want to squash.rebase -i
approach andreset --soft
is,rebase -i
allows me to retain the commit author, whilereset --soft
allows me to recommit. Sometimes i need to squash commits of pull requests yet maintaining the author information. Sometimes i need to reset soft on my own commits. Upvotes to both great answers anyways.Use git rebase -i <after-this-commit> and replace "pick" on the second and subsequent commits with "squash" or "fixup", as described in the manual.
uhhhh... wut?You can usegit merge --squash
for this, which is slightly more elegant thangit rebase -i
. Suppose you're on master and you want to squash the last 12 commits into one.
WARNING: First make sure you commit your work—check thatgit status
is clean (sincegit reset --hard
will throw away staged and unstaged changes)
Then:
# Reset the current branch to the commit just before the last 12:git reset --hard HEAD~12# HEAD@{1} is where the branch was just before the previous command.# This command sets the state of the index to be as it would just# after a merge from that commit:git merge --squash HEAD@{1}# Commit those squashed changes. The commit message will be helpfully# prepopulated with the commit messages of all the squashed commits:git commit
Thedocumentation forgit merge
describes the--squash
option in more detail.
Update: the only real advantage of this method over the simplergit reset --soft HEAD~12 && git commit
suggested by Chris Johnsen inhis answer is that you get the commit message prepopulated with every commit message that you're squashing.
19 Comments
git merge --squash
is also easier to use in a script. Essentially, the reasoning was that you don't need the "interactivity" ofgit rebase -i
at all for this.git merge --squash
is less likely to produce merge conflicts in the face of moves/deletes/renames compared to rebasing, especially if you're merging from a local branch. (disclaimer: based on only one experience, correct me if this isn't true in the general case!)HEAD@{1}
just to be on the safe side e.g. when your workflow is interrupted for an hour by a power outage etc.git merge --squash HEAD@{1}
and get aerror: unknown switch 'c
back you are probably running in powershell console. Probably easiest way to continue is to temporarily move to ordinary command shell throughcmd
then do yourgit merge --squash HEAD@{1}
and then go back to powershell through leaving the command shell byexit
. (I haven't bothered to figure out how to run thegit merge --squash HEAD@{1}
through powershell.)2020 Simple solution without rebase :
git reset --soft HEAD~2 git commit -m "new commit message"git push -f
2 means the last two commits will be squashed. You can replace it by any number
18 Comments
-f (force)
option inpush
is a dangerous practice, particularly if you're pushing to a shared repo (i.e public history) that'll make life dfficult for contributorsgit reset --soft $(git merge-base feature master)
and thengit commit
.force
should be actuallyencouraged with very well communicated impacts of this approach –– Force-pushing goes hand in hand with rebase and synchronization of work so it is better, in my humble opinion, that more people know about the effects of this action, rather than being an exoteric command that people are scared to use.--force
is bad. Prefer--force-with-lease
;·)Thanks tothis handy blog post I found that you can use this command to squash the last 3 commits:
git rebase -i HEAD~3
This is handy as it works even when you are on a local branch with no tracking information/remote repo.
The command will open the interactive rebase editor which then allows you to reorder, squash, reword, etc as per normal.
Using the interactive rebase editor:
Seethe Git docs on using the interactive rebase. A summary follows:
From the example above, the interactive rebase editor shows the last three commits. This constraint was determined byHEAD~3
when running the commandgit rebase -i HEAD~3
.
The commits are listed in reverse order to what you may expect. The oldest commit is displayed on line 1, and the newest commit on the last line. The lines starting with a#
are comments/documentation.
The documentation displayed is pretty clear. On any given line you can change the command frompick
to a command of your choice.
I prefer to use the commandfixup
as this "squashes" the commit's changes into the commit on the line above and discards the commit's message.
In most cases you would leave line 1 aspick
. You cannot usesquash
orfixup
as there is no earlier commit to squash the commit into. It will give an error similar to the following:error: cannot 'fixup' without a previous commit
.
You may also change the order of the commits. This allows you to squash or fixup commits that are not adjacent chronologically.
A practical everyday example
I've recently committed a new feature. Since then, I have committed two bug fixes. But now I have discovered a bug (or maybe just a spelling error) in the new feature I committed. How annoying! I don't want a new commit polluting my commit history!
The first thing I do is fix the mistake and make a new commit with the commentsquash this into my new feature!
.
I then rungit log
orgitk
and get the commit SHA of the new feature (in this case1ff9460
).
Next, I bring up the interactive rebase editor withgit rebase -i 1ff9460~
. The~
after the commit SHA tells the editor to include that commit in the editor.
Next, I move the commit containing the fix (fe7f1e0
) to underneath the feature commit, and changepick
tofixup
.
When closing the editor, the fix will get squashed into the feature commit and my commit history will look nice and clean!
This works well when all the commits are local, but if you try to change any commits already pushed to the remote you can really cause problems for other devs that have checked out the same branch!
14 Comments
pick
in line 1. If you choosesquash
orfixup
for the commit on line 1, git will show a message saying "error: cannot 'fixup' without a previous commit". Then it will give you the option to fix it: "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'." or you can just abort and start over: "Or you can abort the rebase with 'git rebase --abort'.".pick
the first line. And when you choosesquash
orfixup
on a line, it will put the changes into the commit on the line above.I recommend avoidinggit reset
when possible -- especially for Git-novices. Unless you really need to automate a process based on anumber of commits, there is a less exotic way...
- Put the to-be-squashed commits on a working branch (if they aren't already) -- use gitk for this
- Check out the target branch (e.g. 'master')
git merge --squash (working branch name)
git commit
The commit message will be prepopulated based on the squash.
16 Comments
gitk
to label the line of code that you are squashing and also label the base upon which to squash to. In the normal case, both of these labels will already exist, so step (1) can be skipped.git branch your-feature && git reset --hard HEAD~N
the most convenient way. However, it does involve git reset again, which this answer tried to avoid.Based onChris Johnsen's answer,
Add a global "squash" alias from bash: (or Git Bash on Windows)
git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'
... or using Windows' Command Prompt:
git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Your `~/.gitconfig` should now contain this alias:
[alias] squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
Usage:
git squash N
... Which automatically squashes together the lastN
commits, inclusive.
Note: The resultant commit message is a combination of all the squashed commits, in order. If you are unhappy with that, you can alwaysgit commit --amend
to modify it manually. (Or, edit the alias to match your tastes.)
12 Comments
git squash -m "New summary."
and haveN
determined automatically as the number of unpushed commits.git commit --amend
to further change the message, but this alias lets you have a good start on what should be in the commit message.In the branch you would like to combine the commits on, run:
git rebase -i HEAD~(n number of commits back to review)
example:
git rebase -i HEAD~2
This will open the text editor and you must switch the 'pick' in front of each commit with 'squash' if you would like these commits to be merged together. From documentation:
p, pick = use commit
s, squash = use commit, but meld into previous commit
For example, if you are looking to merge all the commits into one, the 'pick' is the first commit you made and all future ones (placed below the first) should be set to 'squash'. If using vim, use:x in insert mode to save and exit the editor.
Then to continue the rebase:
git add .git rebase --continue
For more on this and other ways to rewrite your commit history seethis helpful post
3 Comments
--continue
and vim:x
does.git add
the correct configuration in your files you usegit rebase --continue
to move to the next commit and start to merge.:x
is one command that will save the changes of the file when using vim seethisgit rebase -i HEAD~2
but for some reason I see the full git history - any idea why?To do this you can use following git command.
git rebase -i HEAD~n
n(=4 here) is the number of last commit. Then you got following options,
pick 01d1124 Message....pick 6340aaa Message....pick ebfd367 Message....pick 30e0ccb Message....
Update like belowpick
one commit andsquash
the others into the most recent,
p 01d1124 Message....s 6340aaa Message....s ebfd367 Message....s 30e0ccb Message....
For details click on theLink
Here is another visual example of what would follow after executing:git rebase -i HEAD~3
Comments
Based onthis article I found this method easier for my usecase.
My 'dev' branch was ahead of 'origin/dev' by 96 commits (so these commits were not pushed to the remote yet).
I wanted to squash these commits into one before pushing the change. I prefere to reset the branch to the state of 'origin/dev' (this will leave all changes from the 96 commits unstaged) and then commit the changes at once:
git reset origin/devgit add --allgit commit -m 'my commit message'
8 Comments
If you use TortoiseGit, you can the functionCombine to one commit
:
- Open TortoiseGit context menu
- Select
Show Log
- Mark the relevant commits in the log view
- Select
Combine to one commit
from the context menu
This function automatically executes all necessary single git steps.Unfortunatly only available for Windows.
6 Comments
Many answers are based ongit rebase
command, but in my experience it is somewhat complex and advanced for git-beginners.
Let's say you want to squash last 3 commits. Then following are the steps:
- Note down current commit id: Use
git log -1 --oneline
and note the commit-id of the present state (just in case you do something wrong with git reset) - Go back 3 commits: Using
git reset --soft HEAD~3
you'll go back 3 commits (and sort of forget that you've had made these three commits earlier) - Do a new commit: Now simply do
git commit -m <NEW_SINGLE_MESSAGE>
which will automatically combine the three commits under your message
In case something goes wrong with git reset, you can again return to the original state bygit reset --soft <ORIGINAL_COMMIT>
3 Comments
git reset --soft HEAD~3
as a backup. This way you have an exact, easy to use replica of where you started. Even after you've force-pushed at the endAnomies answer is good, but I felt insecure about this so I decided to add a couple of screenshots.
Step 0: git log
See where you are withgit log
. Most important, find the commit hash of the first commit youdon't want to squash. So only the :
Step 1: git rebase
Executegit rebase -i [your hash]
, in my case:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
Step 2: pick / squash what you want
In my case, I want to squash everything on the commit that was first in time. The ordering is from first to last, so exactly the other way as ingit log
. In my case, I want:
Step 3: Adjust message(s)
If you have picked only one commit and squashed the rest, you can adjust one commit message:
That's it. Once you save this (:wq
), you're done. Have a look at it withgit log
.
2 Comments
git log
I think the easiest way to do this is by making a new branch based on master and doing amerge --squash
of the feature branch.
git checkout mastergit checkout -b feature_branch_squashedgit merge --squash feature_branch
Then you have all of the changes ready to commit.
6 Comments
git merge
To squash the last 10 commits into 1 single commit:
git reset --soft HEAD~10 && git commit -m "squashed commit"
If you also want to update the remote branch with the squashed commit:
git push -f
2 Comments
Procedure 1
1) Identify the commit short hash
# git log --pretty=oneline --abbrev-commitabcd1234 Update to Fix for issue Bcdababcd Fix issue Bdeab3412 Fix issue A....
Here evengit log --oneline
also can be used to get short hash.
2) If you want to squash (merge) last two commit
# git rebase -i deab3412
3) This opens up anano
editor for merging. And it looks like below
....pick cdababcd Fix issue Bpick abcd1234 Update to Fix for issue B....
4) Rename the wordpick
tosquash
which is present beforeabcd1234
. After rename it should be like below.
....pick cdababcd Fix issue Bsquash abcd1234 Update to Fix for issue B....
5) Now save and close thenano
editor. Pressctrl + o
and pressEnter
to save. And then pressctrl + x
to exit the editor.
6) Thennano
editor again opens for updating comments, if necessary update it.
7) Now its squashed successfully, you can verify it by checking logs.
# git log --pretty=oneline --abbrev-commit1122abcd Fix issue Bdeab3412 Fix issue A....
8) Now push to repo. Note to add+
sign before the branch name. This means forced push.
# git push origin +master
Note : This is based on using git onubuntu
shell. If you are using different os (Windows
orMac
) then above commands are same except editor. You might get different editor.
Procedure 2
- First add the required files for commit
git add <files>
- Then commit using
--fixup
option and theOLDCOMMIT
should be on which we need to merge(squash) this commit.
git commit --fixup=OLDCOMMIT
Now this creates a new commit on top of HEAD withfixup1 <OLDCOMMIT_MSG>
.
- Then execute below command to merge(squash) the new commit to the
OLDCOMMIT
.
git rebase --interactive --autosquash OLDCOMMIT^
Here^
means the previous commit toOLDCOMMIT
. Thisrebase
command opens interactive window on a editor (vim or nano) on thatwe no need to do anything just save and exiting is sufficient. Because the option passed to this will automatically move the latestcommit to next to old commit and change the operation tofixup
(equivalent to squash). Then rebase continues and finishes.
Procedure 3
- If need to add new changes to the last commit means
--amend
can be used withgit-commit
.
# git log --pretty=oneline --abbrev-commit cdababcd Fix issue B deab3412 Fix issue A .... # git add <files> # New changes # git commit --amend # git log --pretty=oneline --abbrev-commit 1d4ab2e1 Fix issue B deab3412 Fix issue A ....
Here--amend
merges the new changes to last commitcdababcd
and generates new commit ID1d4ab2e1
Conclusion
- Advantage of 1st procedure is to squash multiple commits and to reorder. But this procedure will be difficult if we need to merge a fix to very old commit.
- So the 2nd procedure helps to merge the commit to very old commit easily.
- And the 3rd procedure is useful in a case to squash a new changes to last commit.
2 Comments
If you are on a remote branch(calledfeature-branch
) cloned from a Golden Repository(golden_repo_name
), then here's the technique to squash your commits into one:
Checkout the golden repo
git checkout golden_repo_name
Create a new branch from it(golden repo) as follows
git checkout -b dev-branch
Squash merge with your local branch that you have already
git merge --squash feature-branch
Commit your changes (this will be the only commit that goes in dev-branch)
git commit -m "My feature complete"
Push the branch to your local repository
git push origin dev-branch
5 Comments
If for example, you want to squash the last 3 commits to a single commit in a branch (remote repository) in for example:https://bitbucket.org
What I did is
git reset --soft HEAD~3
git commit
git push origin <branch_name> --force
6 Comments
git reset --soft
answer was already given in 2011. What does your answer add to it?What can be really convenient:
Find the commit hash you want to squash on top of, sayd43e15
.
Now use
git reset d43e15git commit -am 'new commit name'
4 Comments
Did anyone mention how easy it is to do onIntelliJ IDEA UI:
- Go to
git
window - Manually select all the commits you want to merge into one.
- Right-click >
Squash Commits
> Edit the squashed commit message - Click on branch name on left side > Right-click > Push >
Force Push
2 Comments
method 1 if you have many commits
git rebase -i master
then press keyboard 'i' to edit
you will see like this:
pick etc1pick etc2pick etc2
replace the word pick with'f'
and pressesc y :wq
pick etc1 //this commit will the one commitf etc2f etc2
and press this command
git push origin +head
method 2 if you have few commitsyou can do this to delete a commit, you need to do same for delete your second commit and so on
git reset --soft HEAD^1 // or git reset --soft head~1git commit --amend //then press `:wq` git push -f
method 3 if you already have one commit and you dont want submit another commit more
git add files...git commit --amend //then press `:wq`git push origin +head
1 Comment
vi
set as your default editor in git.simple solution:
git reset --soft HEAD~5
git commit -m "commit message"
git push origin branch --force-with-lease
2 Comments
If you want to squishevery commit into a single commit (e.g. when releasing a project publicly for the first time), try:
git checkout --orphan <new-branch>git commit
Comments
This is super-duper kludgy, but in a kind of cool way, so I'll just toss it into the ring:
GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo
Translation: provide a new "editor" for git which, if the filename to be edited isgit-rebase-todo
(the interactive rebase prompt) changes all but the first "pick" to "squash", and otherwise spawns vim - so that when you're prompted to edit the squashed commit message, you get vim. (And obviously I was squashing the last five commits on branch foo, but you could change that however you like.)
I'd probably do whatMark Longair suggested, though.
3 Comments
gawk
.git -c core.editor="gawk -i inplace '{if(NR>1 && \$1==\"pick\"){\$1=\"squash\"} print \$0}'" rebase -i --autosquash HEAD~5
.GIT_SEQUENCE_EDITOR
, for I needautostash
Easiest way to do this is using GitHub Desktop. Just select all the commits in your History, right-click and select "Squash x commits":
4 Comments
Simple one-liner that always works, given that you are currently on the branch you want to squash, master is the branch it originated from, and the latest commit contains the commit message and author you wish to use:
git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
1 Comment
⚠️ WARNING: "My last X commits" might be ambiguous.
(MASTER) Fleetwood Mac Fritz ║ ║ Add Danny Lindsey Stevie Kirwan Buckingham Nicks ║ ╚═══╦══════╝ Add Christine ║ Perfect Buckingham ║ Nicks LA1974══════════╝ ║ ║ Bill <══════ YOU ARE EDITING HERE Clinton (CHECKED OUT, CURRENT WORKING DIRECTORY)
In this very abbreviated history of thehttps://github.com/fleetwood-mac/band-history repository you have opened a pull request to merge in the the Bill Clinton commit into the original (MASTER
) Fleetwood Mac commit.
You opened a pull request and on GitHub you see this:
Four commits:
- Add Danny Kirwan
- Add Christine Perfect
- LA1974
- Bill Clinton
Thinking that nobody would ever care to read the full repository history. (There actually is a repository, click the link above!) You decide to squash these commits. So you go and rungit reset --soft HEAD~4 && git commit
. Then yougit push --force
it onto GitHub to clean up your PR.
And what happens? You just made single commit that get from Fritz to Bill Clinton. Because you forgot that yesterday you were working on the Buckingham Nicks version of this project. Andgit log
doesn't match what you see on GitHub.
🐻 MORAL OF THE STORY
- Find the exact files you want to getto, and
git checkout
them - Find the exact prior commit you want to keep in history, and
git reset --soft
that - Make a
git commit
that warps directly from thefrom to theto
2 Comments
If you don't care about the commit messages of the in-between commits, you can use
git reset --soft <commit-hash-into-which-you-want-to-squash>git commit -a --amend
Comments
We were really happy with this answer, it means you don't need to count through commits.
If you have loads of commits on master branch and want to combine them all into one:
- Ensure you're on the master branch and dogit status to ensure it's all clean
- Copy the commit SHA for the commit BEFORE you started your work
- git reset ‒soft commitIdSHA
[ You'll notice that all your changes at this point will be shown in the source control section of your IDE - good to review them to check it's all there]
- git commit -am "the message you want that will replace all the other commit messages"
- git push (will complain as you are behind on the commits) so dogit push --force
That's it!
Comments
Explore related questions
See similar questions with these tags.