5775

How do I squash my last N commits together into one commit?

Mateen Ulhaq's user avatar
Mateen Ulhaq
27.7k21 gold badges120 silver badges155 bronze badges
askedMar 4, 2011 at 4:11
markdorison's user avatar
6

47 Answers47

6012

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>
Toby Speight's user avatar
Toby Speight
31.9k58 gold badges82 silver badges116 bronze badges
answeredMar 5, 2011 at 4:19
Chris Johnsen's user avatar
Sign up to request clarification or add additional context in comments.

40 Comments

Ha! I like this method. It is the one closes to the spirit of the problem. It's a pity that it requires so much voodoo. Something like this should be added to one of the basic commands. Possiblygit rebase --squash-recent, or evengit commit --amend-many.
@A-B-B: If your branch has an “upstream” set, then you may be able to usebranch@{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.
This kinda-sorta required me topush -f but otherwise it was lovely, thanks.
@2rs2ts git push -f sound dangerous. Take care to only squash local commits. Never touch pushed commits!
I also need to usegit push --force afterwards so that it takes the commit
|
3107

Usegit 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.

Yajo's user avatar
Yajo
6,5402 gold badges36 silver badges36 bronze badges
answeredMar 4, 2011 at 4:18
Anomie's user avatar

21 Comments

<after-this-commit> is commit X+1 i.e. parent of the oldest commit you want to squash.
If you've already pushed the commits, you will need to push -f to forcibly move the remote branch to your new commit. This will upset anyone who was working on top of the old commits, though.
The difference between thisrebase -i approach andreset --soft is,rebase -iallows 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.
I always end up screwing up the rebase for some reason, reset --soft seems more intuitive for most purposes.
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?
|
1061

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.

Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredMar 4, 2011 at 6:10
Mark Longair's user avatar

19 Comments

@Mark Amery: There are various reasons that I said that this is more elegant. For example, it doesn't involve unnecessarily spawning an editor and then searching and replacing for a string in the "to-do" file. Usinggit 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.
Another advantage is thatgit 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!)
I'm always very reluctant when it comes to hard resets - I'd use a temporal tag instead ofHEAD@{1} just to be on the safe side e.g. when your workflow is interrupted for an hour by a power outage etc.
@B T: Destroyed your commit? :( I'm not sure what you mean by that. Anything that you committed you'll easily be able to get back to from git's reflog. If you had uncommitted work, but the files were staged, you should still be able to get their contents back,although that will be more work. If your work wasn't even staged, however, I'm afraid there's little that can be done; that's why the answer says up-front: "First check that git status is clean (since git reset --hard will throw away staged and unstaged changes)".
FYI: If you dogit 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.)
|
430

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

Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredApr 12, 2020 at 12:14
Xys's user avatar

18 Comments

This solution shouldn't be encouraged. Using the-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 contributors
if the goal is to add this new commit to master as part of an ongoing pr, you could usegit reset --soft $(git merge-base feature master) and thengit commit.
I would argue that pushing withforce 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.
Agree with Matheus, force pushing to feature branches is fine and should be encouraged with rebasing. Pushing directly to main should always be restricted anyways.
--force is bad. Prefer--force-with-lease ;·)
|
362

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.

interactive rebase editor


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!

enter image description here

answeredMay 17, 2016 at 6:19
br3nt's user avatar

14 Comments

do you have to pick the top one and squash the rest? You should edit your answer to explain how to use the interactive rebase editor in more detail
Yes, leavepick 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'.".
@Timo, correct. Oldest at the top, newest at the bottom. That's why you need topick the first line. And when you choosesquash orfixup on a line, it will put the changes into the commit on the line above.
This feels like the best answer when you know that you want to squash a certain amount of commits or at least see the commits you can squash by entering some arbitrary number. Generally, I use this .
@Andy Thanks! I've updated the answer, and also included a link tothe relevant Git docs.
|
330

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...

  1. Put the to-be-squashed commits on a working branch (if they aren't already) -- use gitk for this
  2. Check out the target branch (e.g. 'master')
  3. git merge --squash (working branch name)
  4. git commit

The commit message will be prepopulated based on the squash.

answeredMar 14, 2014 at 23:24
Brent Bradburn's user avatar

16 Comments

This is the safest method : no reset soft / hard (!!), or reflog used !
It would be great if you expanded on (1).
@Adam: Basically, this means use the GUI interface ofgitk 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.
Note that this method doesn't mark the working branch as being fully merged, so removing it requires forcing deletion. :(
For (1), I've foundgit 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.
|
164

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.)

Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredFeb 19, 2014 at 19:21
EthanB's user avatar

12 Comments

Interesting, but I'd much rather type the squashed commit message myself, as a descriptive summary of my multiple commits, than have it auto-entered for me. So I'd rather specifygit squash -m "New summary." and haveN determined automatically as the number of unpushed commits.
@A-B-B, This sounds like a separate question. (I don't think it's exactly what the OP was asking; I've never felt a need for it in my git squash workflow.)
This is pretty sweet. Personally I'd like a version that uses the commit message from the first of the squashed-together commits. Would be good for things like whitespace tweaks.
@funroll Agreed. Just dropping the last commit msg is a super common need for me. We should be able to devise that...
@A-B-B you can usegit commit --amend to further change the message, but this alias lets you have a good start on what should be in the commit message.
|
96

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

answeredJul 10, 2018 at 22:28
aabiro's user avatar

3 Comments

Please also explain what the--continue and vim:x does.
The rebase will happen in blocks as it goes through the commits on your branch, after yougit 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 seethis
git rebase -i HEAD~2 but for some reason I see the full git history - any idea why?
91

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

Mike Pennington's user avatar
Mike Pennington
43.2k22 gold badges140 silver badges191 bronze badges
answeredApr 6, 2017 at 5:26
Jakir Hosen Khan's user avatar

1 Comment

More details on SO on the git rebase -interactivehere
84

Here is another visual example of what would follow after executing:git rebase -i HEAD~3

Here there is a visual example of what would follow after executing git rebase for the three last commits

Source:https://www.git-tower.com/learn/git/faq/git-squash/

answeredMar 19, 2021 at 14:59
ana's user avatar

Comments

70

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'
Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredMay 22, 2014 at 0:41
trudolf's user avatar

8 Comments

Just what I needed. Squash down commits from my feature branch, and then I git cherry pick that commit into my master.
This does not squash previous commits!
could you elaborate that a bit further @igorGanapolsky ?
@trudolf This isn't really squashing (picking individual commits to squash). This is more of committing all of your changes at once.
yes, hence it squashes all of your commits into one. congratulations!
|
67

If you use TortoiseGit, you can the functionCombine to one commit:

  1. Open TortoiseGit context menu
  2. SelectShow Log
  3. Mark the relevant commits in the log view
  4. SelectCombine to one commit from the context menu

Combine commits

This function automatically executes all necessary single git steps.Unfortunatly only available for Windows.

answeredNov 6, 2015 at 12:51
Matthias M's user avatar

6 Comments

As far as I am aware, this will not work for merge commits.
Although it's not commented by any others, this even works for commits which are not at the HEAD. For instance, my need was to squash some WIP commits I did with a more sane description before pushing. Worked beautifully. Of course, I still hope I can learn how to do it by commands.
This is superb! Everything will be done by just couple of mouse clicks and I could merge 200 commits of old repo before archiving! Thanks. Really useful to make branch tree clean and easily review code changes at once.
You can similarly select n commits and squash them via GUI of GitHub Desktopdesktop.github.com
WebStorm also has a "Squash commits" option if you select 2+ commits and right click.
|
67

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: Usegit 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: Usinggit 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 dogit 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>

Josep Alsina's user avatar
Josep Alsina
3,0931 gold badge20 silver badges19 bronze badges
answeredFeb 23, 2021 at 11:37
Pulkit Bansal's user avatar

3 Comments

It's IMPORTANT to use "--soft" in step #2, otherwise you will just revert to a state three commits ago. I think it should be emphasized.
Also crucial is that you will need --force when you want to push your squashed commit
if you're EXTRA nervous like me, you can always create a new branch right before you dogit 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 end
64

Anomies 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 :

enter image description here

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:

enter image description here

Step 3: Adjust message(s)

If you have picked only one commit and squashed the rest, you can adjust one commit message:

enter image description here

That's it. Once you save this (:wq), you're done. Have a look at it withgit log.

answeredJun 26, 2018 at 18:03
Martin Thoma's user avatar

2 Comments

would be nice to see the final result, e.g.,git log
@Axalix Did you remove all your lines? That's how you lose your commits.
48

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.

Wolfson's user avatar
Wolfson
1,4372 gold badges21 silver badges26 bronze badges
answeredMar 20, 2018 at 15:28
user1376350's user avatar

6 Comments

This is a nice alternative to achieve a similar end result. I came looking on how to do it using rebase, but I chose this way better. I keep forgetting about the existence ofgit merge
I was trying to do other solutions, but for w/e reason they weren't working. This one did.
Thank you man! It helped me. I'm already desperate to fix anything...
This works like charm. But I think there is one assumption, you need make sure the feature_branch has 0 behind master branch.
Nice technique to split the operations into a staging branch! One can then diff clean branches more easily.
|
48

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
Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredAug 21, 2018 at 19:25
Ayan's user avatar

2 Comments

--force is dangerous when multiple people are working on a shared branch as it blindly updates remote with your local copy. --force-with-lease could have been better as it makes sure that remote has no commits from others since you last fetched it.
Multiple people working on the same branch on itself is already dangerous. Whoever does it probably knows the risks of using force.
44

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

  1. First add the required files for commit
git add <files>
  1. 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>.

  1. Then execute below command to merge(squash) the new commit to theOLDCOMMIT.
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

  1. 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.
answeredJan 10, 2018 at 14:19
rashok's user avatar

2 Comments

It only updates the last two commits even I reset to a commit Id to the 6th last commit, do not know why
Even you can rearrange the commit order. It works fine.
39

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:

  1. Checkout the golden repo

    git checkout golden_repo_name
  2. Create a new branch from it(golden repo) as follows

    git checkout -b dev-branch
  3. Squash merge with your local branch that you have already

    git merge --squash feature-branch
  4. Commit your changes (this will be the only commit that goes in dev-branch)

    git commit -m "My feature complete"
  5. Push the branch to your local repository

    git push origin dev-branch
Machavity's user avatar
Machavity
31.8k27 gold badges97 silver badges108 bronze badges
answeredSep 9, 2015 at 15:58
Sandesh Kumar's user avatar

5 Comments

Since I was just squashing ~100 commits (for syncing svn branches via git-svn), this is far faster than interactively rebasing!
Reading down, I see @Chris's comment, which is what I used to do (rebase --soft...) - too bad that stackoverflow is no longer putting the answer with hundreds of upvotes at the top...
agree with you @sage, lets hope they might do it sometime in the future
This is the right way. Rebase approach is good, but should only be used for squash as a last resort solution.
I'm getting everything up to date
39

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

  1. git reset --soft HEAD~3
  2. git commit
  3. git push origin <branch_name> --force
Ivan Kaloyanov's user avatar
Ivan Kaloyanov
1,7466 gold badges20 silver badges24 bronze badges
answeredMay 16, 2019 at 7:55
Albert Ruelan's user avatar

6 Comments

Just be careful, Since if you use force then there is no way to retrieve the previous commits since you removed it
Nice and quick solution, as for me.
Force is destructive. This is not squashing commits rather removing the last three commits and adding them back as a fourth (now new) commit, essentially rewriting the history which can break the repo for other users until they also force pull. This will also remove any other commits your team has pushed meanwhile.
What's the point of linking to BitBucket?
Thegit reset --soft answer was already given in 2011. What does your answer add to it?
|
33

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'
Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredSep 14, 2017 at 10:33
Ariel Gabizon's user avatar

4 Comments

This. Why don't more people use this? It's way faster than rebasing and squashing individual commits.
@a3y3 maybe the working directory is not clean. one might stash changes first, in that case.
Because people need to put all logs of each commit into the new one... manually
That's excellent.
30

Did anyone mention how easy it is to do onIntelliJ IDEA UI:

  • Go togit 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

enter image description here

answeredJul 28, 2021 at 3:32
Saurav Sahu's user avatar

2 Comments

This doesn't work for merge commits
This works in Android Studio also.
28

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
answeredDec 10, 2021 at 15:18
rnewd_user's user avatar

1 Comment

It should be noted, that this answer assumes you havevi set as your default editor in git.
25

simple solution:

git reset --soft HEAD~5

git commit -m "commit message"

git push origin branch --force-with-lease

answeredApr 19, 2021 at 13:28
slisnychyi's user avatar

2 Comments

nice try, but there is a better way mentioned here with rebase
Thanks for your answer. I tried rebase a lot of times, but only your's worked.
21

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
answeredAug 20, 2016 at 20:39
William Denniss's user avatar

Comments

21

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.

Benjamin Loison's user avatar
Benjamin Loison
5,7314 gold badges19 silver badges37 bronze badges
answeredMar 4, 2011 at 16:12
Cascabel's user avatar

3 Comments

+1: that's fun and instructive, in that it's wasn't at all obvious to me that you can put anything more complex than the name of a program in the GIT_EDITOR environment variable.
You could simplify this usinggawk.git -c core.editor="gawk -i inplace '{if(NR>1 && \$1==\"pick\"){\$1=\"squash\"} print \$0}'" rebase -i --autosquash HEAD~5.
I prefer this command withGIT_SEQUENCE_EDITOR, for I needautostash
20

Easiest way to do this is using GitHub Desktop. Just select all the commits in your History, right-click and select "Squash x commits":

GitHub Desktop squash

answeredMay 14, 2022 at 17:10
Pranav Wadhwa's user avatar

4 Comments

yes, this solved the problem. however is not that-pro comparing to commandline answers
@Siwei doesn’t matter how “pro” it is if it works
I tried this because the other answers gave me a super long list of commits that were intimidating to deal with (at work we have a LOT of people touching code and feature branches abound). The message I got when attempting to squash was: "Unable to squash. Squashing replays all commits up to the last one required for the squash. A merge commit cannot exist among those commits."
This should have more votes so it can be more visible on the first page :)
19

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}
answeredFeb 14, 2019 at 19:20
ColinM's user avatar

1 Comment

I have been absolutely livid with frustration about squashing commits and how stupidly complicated it is - just effing use the last message and squash them all to one commit! Why is it that hard???? This one liner does that for me. Thank you from the bottom of my angry heart.
19

⚠️ 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

  1. Find the exact files you want to getto, andgit checkout them
  2. Find the exact prior commit you want to keep in history, andgit reset --soft that
  3. Make agit commit that warps directly from thefrom to theto
answeredNov 2, 2018 at 4:34
William Entriken's user avatar

2 Comments

This is 100% the easiest way to do this. If your current HEADis the correct state you want, then you can skip #1.
This is the only way I know that allows to rewrite the first commit history.
14

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
answeredJul 8, 2019 at 13:16
Zsolt Z.'s user avatar

Comments

12

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!

answeredAug 5, 2022 at 11:39
ironmongrrrl's user avatar

Comments

Protected question. To answer this question, you need to have at least 10 reputation on this site (not counting theassociation bonus). The reputation requirement helps protect this question from spam and non-answer activity.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.