Squash my last X commits together using Git

Created 04.03.2011 04:11
Viewed 2.11M times
4105 votes

How can I squash my last X commits together into one commit using Git?

5
Comments
Related: Git - combining multiple commits before pushing. by User, 06.06.2014 08:07
@matt TortoiseGit is your tool. It provides a single function "Combine to one commit" which will call all steps automatically in the background. Unfortunately only available for Windows. See my answer below. by Matthias M, 06.11.2015 12:57
For squashing upto THE first commit see this - stackoverflow.com/questions/1657017/… by goelakash, 06.03.2016 13:26
post squash one need to do force push stackoverflow.com/questions/10298291/… by vikramvi, 14.07.2016 13:04
In addition to the posted answers, GUI clients can do this easily. I can squash dozens of commits in GitKraken with only four clicks. by Aaron Franke, 14.01.2019 12:56
Answers 38
33
4353

You can do this fairly easily without git rebase or git merge --squash. In this example, we'll squash the last 3 commits.

If you want to write the new commit message from scratch, this suffices:

git reset --soft HEAD~3 &&
git 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/…/squash git rebase -i instruction list would start you with), then you need to extract those messages and pass them to git 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”).

05.03.2011 04:19
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. Possibly git rebase --squash-recent, or even git commit --amend-many. by Adrian Ratnapala, 08.11.2013 17:50
When doing the soft reset, what's the best way to auto-determine the target commit id, i.e. of the last pushed commit? I don't want to have to lookup and count 3, for example. by Acumenus, 22.04.2014 18:45
@A-B-B: If your branch has an “upstream” set, then you may be able to use branch@{upstream} (or just @{upstream} for the current branch; in both cases, the last part can be abbreviated to @{u}; see gitrevisions). This may differ from your “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. by Chris Johnsen, 23.04.2014 06:13
I like this solution in general but like providing my own commit message. Here is a solution that sets up an alias so that you can do git squash 3 'my commit message' by n8tr, 03.06.2014 15:02
This kinda-sorta required me to push -f but otherwise it was lovely, thanks. by 2rs2ts, 29.10.2014 23:32
Here's an alias in case you'd like to fixup instead of squash: fixup = "!f() { base=${1:-2}; git reset --soft HEAD~$base && git commit -C ORIG_HEAD~$(expr $base - 1); }; f". by sschuberth, 12.11.2014 14:24
git log --one-line or something like that will display a list of commit IDs and messages, one per line. Very useful with this solution. by Mark K Cowan, 07.01.2015 17:12
@2rs2ts git push -f sound dangerous. Take care to only squash local commits. Never touch pushed commits! by Matthias M, 24.09.2015 14:49
after doing this while trying to push getting error error: failed to push some refs to 'git@github.com:....' hint: Updates were rejected because the tip of your current branch is behind. I found how to solve this here: stackoverflow.com/questions/5667884/… by vikramvi, 14.07.2016 12:53
I also need to use git push --force afterwards so that it takes the commit by Zach Saucier, 21.07.2016 00:21
Make sure your working copy has no other changes (git stash them or something). Otherwise, you have the chance to commit them into the squashed commit too, which might be not something you intend to do. by Victor Sergienko, 27.07.2016 09:33
I want to upvote this, but I can't bring myself to be the one who changes the upvoted count from 1337... by stevenhaddox, 21.10.2016 17:30
This helped me when git rebase --interactive was giving me error: could not apply. Possibly because one of the commits I was trying to squash was a merge with a resolved conflict. by GreensterRox, 23.03.2017 11:51
the git reset --soft HEAD~3 definitely is better in terms of number of conflicts to solve. our test case was HEAD~6, but one of the commit was a merge commit, so we had 45 commits and the rebase -i HEAD~6 gave us so many conflicts which end up failing the build. And when we used reset --soft it gave us 0 conflicts to solve. by soMuchToLearn, 14.06.2017 12:31
git push -f doesn't appear to alter the commit log of the upsteam repository. by Tom Russell, 13.07.2017 08:13
I put this into ~/bin/git-funge as a script because we have to have single commit messages on pull requests. Now I just write git funge HEAD~3 and edit my messages to suite a single PR message and save and it is all sorted. :) Thanks for a great answer. (funge is my way of meaning fungible but reads better as a verb :P) by Tyrone Wilson, 16.11.2017 06:49
I think better than those saying to force push is to just make a new feature branch. So something like git checkout -b feature-name-squashed, follow these directions, then push the new squashed version of the branch. If you do this, you will have access to the full commit logs of the feature (on feature-name) if you ever need them. by rsmith54, 29.03.2018 15:34
Having to git push -f is always a requirement after rewriting history. It CAN be bad to rewrite history, certainly if you are doing it to a shared repository. But that's a separate subject not related to this answer (which is great). by moodboom, 17.04.2018 13:07
If you have already pushed the commits that you are squashing and you want to avoid having to do a force push, create a new branch at the commit you are at and push it: git checkout -b new-branch-name && git push -u by mageos, 21.06.2018 16:05
I thik this is currently the best method. But watch our for its caveat: If you have unstaged or uncommitted changes, Git won't stop you from performing these operations, and your uncommitted changes will get merged into the squashed commit. This probably is or is not what you want. by Cyker, 15.01.2019 13:09
A noteworthy difference between this approach and "real" rebase is that the authors of the original commits are lost here. by Raphael, 05.02.2019 21:38
Not sure what version this was added in but you can use this one liner to reliably reuse the latest commit message and author: git reset --soft {base-commit} && git commit --reuse=HEAD@{1} by ColinM, 14.02.2019 19:13
Try to get used to git push --force-with-lease instead of git push -f ! by rockdaboot, 02.03.2019 10:29
Don't forget + if your other commits were already pushed to remote - i.e. git push origin +name-of-branch by William Hampshire, 04.03.2019 15:10
@AdrianRatnapala I've not tried it yet, but EthanB added an answer below, based on this one, to do something like that using .gitconfig. by Chiramisu, 25.03.2019 22:18
I accidentally did git reset --soft HEAD~3 twice. Be aware that they stack. by Zach Saucier, 17.04.2019 23:34
For those curious about HEAD..HEAD@{1}, r1..r2 specifies a range of commits that are reachable from r2 excluding those that are reachable from r1, and <refname>@{<n>} specifies the n-th prior value of that ref. So here, HEAD..HEAD@{1} means all the commits between the current HEAD and where HEAD was right before the reset. by cowlinator, 19.06.2019 01:57
If you want to target a commit specifically you can do so by providing the hash, but it wasn't as simple as replacing the index with the hash. I got this by using Source Tree: git -c diff.mnemonicprefix=false -c core.quotepath=false --no-optional-locks reset -q --soft <Your SHA here> git commit The hash that should be provided though should be the commit just before your target. by dyslexicanaboko, 12.07.2019 21:53
If my current branch has branched off 'otherbranch', and I want to squash everything I did on the current branch before I merge it into 'otherbranch', I can do git reset --soft otherbranch rather than having to work out how many commits ago that was by joel, 09.10.2019 10:50
This worked for me where the git rebase -i did not. I was trying to squash some work that had a couple of merge commits thrown in the middle and I kept running in to conflicts (I didn't think that was possible, but I'm guessing it was because of the merge commits that were removed). Tried this and voila! No complaints, worked first time. Highly recommended. :) by RTHarston, 23.10.2019 00:05
Put this in your bashrc for an alias to squash the last two commits together, using the message of the first commit --> alias gsquash="git reset --soft HEAD~2 && git commit --edit -m\"$(git log --format=%B HEAD@{0}..HEAD@{2})\"" # Squash last two commits together, using message of first commit. by Varun Singh, 11.11.2019 21:34
Leaving this here in case it helps someone. Using bash, I just define a function squash() { git reset --soft HEAD~$1; git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; } and then I can just say squash 2 or squash 3. by Brian Pursley, 18.02.2020 14:28
@MatthiasM re "git push -f sound dangerous. Take care to only squash local commits. Never touch pushed commits!" Depends on your workflow and context. Using GitHub it is normal (actually required) to have to git push -f after a rebase of a branch. This may be no different. If you're working on your own branch for a PR, there's no problem. Also, you cannot force-push to a protected branch (like main on GitHub). So there's no danger if your project is set up right. by Jake, 17.04.2021 23:53
Show remaining 28 comments
19
2449

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.

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 is git rebase -i HEAD~5.

04.03.2011 04:18
Comments
This, I think, answers this question a bit better stackoverflow.com/a/5201642/295797 by Roy Truelove, 01.05.2013 14:22
What is meant by <after-this-commit>? by 2540625, 04.11.2014 05:49
<after-this-commit> is commit X+1 i.e. parent of the oldest commit you want to squash. by joozek, 04.11.2014 12:04
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. by interfect, 08.12.2014 18:31
I personally found this article (git-scm.com/book/en/v2/Git-Tools-Rewriting-History) a lot easier to follow than the one linked (same site). by joedragons, 02.04.2015 19:36
The difference between this rebase -i approach and reset --soft is, rebase -iallows me to retain the commit author, while reset --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. by zionyx, 15.09.2015 09:31
I always end up screwing up the rebase for some reason, reset --soft seems more intuitive for most purposes. by yoyodunno, 14.01.2016 20:33
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? by Cheeso, 27.07.2016 03:18
is there a way to squash using git rebase without using interactive mode? by Alexander Mills, 19.02.2017 21:11
@Cheeso, this article helps explain the squash and fixup designations used during an interactive rebase. Specifically, the squash and fixup designations tell Git how to combine the commit messages. by Eric Schnipke, 29.09.2017 14:48
The explanation for <after-this-commit> is needlessly complicated. With a simpler explanation (the chosen words are almost self-explanatory, already, but the given explanation overcomplicates), this answer would be perfect. by polynomial_donut, 11.12.2017 19:45
It should be <after-and-including-this-commit> by CIRCLE, 05.01.2018 11:24
I feel like you really mean <before-this-commit>, but you describe such an argument as a commit in the past. by Spencer Williams, 11.03.2018 20:17
Here is a practical example: feeding.cloud.geek.nz/posts/combining-multiple-commits-into-‌​one by Geynen, 03.05.2018 18:34
What you supposed to do after the squashing to get it pushed? by jackrabbithanna, 23.10.2018 21:09
@Geynen Thanks, that link provide the only description that made full sense. Now I can even tell people that <after-this-commit>, means exactly that. It is non-inclusive and will let you pick and squash commits after that one, as long as none of them are an initial commit. If you want to include initial commit, you need to use the git reset method instead. by not2qubit, 30.11.2018 11:15
Note that you can re-order commits when rebasing interactively - e.g. if you made commits A, B, then C, you can squash commit C onto A by swapping the order of the lines of B and C when prompted (and changing C to "squash"). Useful if you have committed more work since the commit you are trying to squash. by csey, 19.02.2019 09:55
git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1} by ColinM, 09.06.2019 03:33
But shouldn't it be For example, if the user wishes to view 5 commits from the current HEAD in the past the command is git rebase -i HEAD~6. instead? by Iulian Onofrei, 11.10.2019 10:55
Show remaining 14 comments
16
813

You can use git merge --squash for this, which is slightly more elegant than git 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 that git status is clean (since git 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

The documentation for git merge describes the --squash option in more detail.


Update: the only real advantage of this method over the simpler git reset --soft HEAD~12 && git commit suggested by Chris Johnsen in his answer is that you get the commit message prepopulated with every commit message that you're squashing.

04.03.2011 06:10
Comments
You say this is more 'elegant' than git rebase -i, but you don't give a reason why. Tentatively -1ing this because it seems to me that in fact the opposite is true and this is a hack; aren't you performing more commands than necessary just in order to force git merge into doing one of the things that git rebase is specifically designed for? by Mark Amery, 08.07.2013 11:14
@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. Using git merge --squash is also easier to use in a script. Essentially, the reasoning was that you don't need the "interactivity" of git rebase -i at all for this. by Mark Longair, 08.07.2013 15:59
Even though I appreciate the advantage of having a verbose commit message for big changes such as this, there's also a real disadvantage of this method over Chris's: doing a hard reset (git reset --hard) touches a lot more files. If you're using Unity3D, for instance, you'll appreciate less files being touched. by cregox, 26.11.2013 12:35
Another advantage is that 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!) by Cheezmeister, 27.02.2014 22:21
I'm always very reluctant when it comes to hard resets - I'd use a temporal tag instead of HEAD@{1} just to be on the safe side e.g. when your workflow is interrupted for an hour by a power outage etc. by Tobias Kienzler, 11.08.2014 12:18
@PaulDraper well let's say due to a power outage or other interruption you cannot finish this, and next time you're in that machine out of habit you do a git pull, obtaining a few commits but not the ones you wanted to squash. I don't know if there also is a HEAD@{2} etc but with a temporary tag you don't have to remember... of course so long as you don't git gc --prune you can still retrieve your HEAD, but it's just more tedious by Tobias Kienzler, 21.03.2015 06:29
@TobiasKienzler, you may be interested in git reflog. Git already "tags" recent commits for this purpose. git gc --prune shouldn't change that, unless you ask to expire the reflog entries. It may all be a matter of preference. by Paul Draper, 21.03.2015 14:37
@PaulDraper Thanks for the info, yeah it's probably preference - and Python's "Explicit is better than implicit" by Tobias Kienzler, 23.03.2015 10:21
Wonderful.. this destroyed my commit. -1 by B T, 22.05.2016 01:08
@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)". by Mark Longair, 22.05.2016 09:55
maybe is outdated? I did the reset --hard and when doing the merge --squash I get this error "fatal: You cannot combine --squash with --no-ff." by jpganz18, 23.10.2018 07:25
FYI: If you do git merge --squash HEAD@{1} and get a error: unknown switch 'c back you are probably running in powershell console. Probably easiest way to continue is to temporarily move to ordinary command shell through cmd then do your git merge --squash HEAD@{1} and then go back to powershell through leaving the command shell by exit. (I haven't bothered to figure out how to run the git merge --squash HEAD@{1} through powershell.) by LosManos, 06.05.2019 05:35
I would also argue that "elegant" is not the right word to use here. I would say it's slightly quicker rather than slightly more elegant since you don't spawn an editor. It's a great advantage but a hard reset also seems dangerous since you could easily lose your data. by Flov, 13.10.2020 11:55
@LosManos I just found that we can use the command in powershell. git merge --squash 'HEAD@{1}' just put HEAD@{1} inside ' ' to make it work. by MING WU, 20.10.2020 05:36
I get "Your branch is behind 'origin/master' by .. commits, and can be fast-forwarded". I then do commit or pull or merge (--ff-only) and get the same commit history as before.. by Timo, 07.11.2020 13:30
How can I create a zsh function out of it with this line git merge --squash HEAD@{1} which results in a parse error. by Timo, 20.11.2020 13:01
Show remaining 11 comments
14
253

I recommend avoiding git reset when possible -- especially for Git-novices. Unless you really need to automate a process based on a number 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.

14.03.2014 23:24
Comments
This is the safest method : no reset soft / hard (!!), or reflog used ! by TeChn4K, 30.11.2016 14:08
It would be great if you expanded on (1). by Adam, 11.04.2017 14:16
@Adam: Basically, this means use the GUI interface of 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. by Brent Bradburn, 11.04.2017 21:48
Note that this method doesn't mark the working branch as being fully merged, so removing it requires forcing deletion. :( by Kyrstellaine, 13.04.2017 20:17
For (1), I've found 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. by eis, 30.05.2017 17:01
@Kyrstellaine: Yes, the merge does not affect the working branch -- so you will have to remove it manually, if you want it removed (an optional step 5). by Brent Bradburn, 16.08.2017 17:14
@Adam: There is an assumption in this answer that you already have a working branch that has diverged from your target branch, and that you want to squash all the commits on the working branch from the divergence point from target, and apply them to the target branch in a single squash (non-merge) commit. If your changes are already committed on the target branch, this answer doesn't help, since if you follow it, it will just add a new commit to your target on top of the ones you want to squash (in that case, you'd need to rebase to rewrite history on the target branch to remove commits). by Bogatyr, 11.11.2019 07:42
Futher elaboration regarding (1): If you are adept with gitk (a GUI tool), then you can (with care) move and rename branches. For example, you can place a new branch called tobesquashed on the same commit with master, then delete master and recreate it at the base of the to-be-squashed commits. ... Then proceed with step 2. by Brent Bradburn, 02.06.2020 06:26
Easy. This is the best answer. by V Cezar, 14.08.2020 22:35
@BrentBradburn, would you provide an real-world example, I did not understand your answer. by Menai Ala Eddine - Aladdin, 15.09.2020 21:16
@MenaiAlaEddine-Aladdin, gitk is a GUI for viewing your Git repository. I highly recommend using gitk or something similar. Exploring your repository with a graphical tool should help bring this into focus. Using gitk, you can attach the head of branches (such as 'master' and 'my_new_branch') to specific commits in the history. Once this is done, the steps 2, 3, and 4 are trivial. See my previous "Further elaboration" comment for more... by Brent Bradburn, 15.09.2020 22:15
I agree that this is the safest way, and also as a note to Git-novices, if it's your first time using this command you can also create duplicate branches of the target and working branch to get a feel for the command first before actually applying it to the real branches. by lasec0203, 25.09.2020 01:42
Important to note that method assumes the working branch being squash will be deleted afterwards, because if you add more changes to it and then do a regular git merge it will bring merge all of the previous commits that were squashed by lasec0203, 25.09.2020 04:21
The idea is to create a helper branch that is at head of master(latest commit) and goes down until the end of the squashed commit(1).Checkout master(2). Git merge --squash (3) How does git know to merge the helper branch and master? git commit(4) git branch -D helper (5) by Timo, 07.11.2020 13:47
Show remaining 9 comments
8
168

Thanks to this 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:

The interactive rebase editor shows the last three commits. This constraint was determined by HEAD~3 when running the command git rebase -i HEAD~3.

The most recent commit, HEAD, is displayed first on line 1. The lines starting with a # are comments/documentation.

The documentation displayed is pretty clear. On any given line you can change the command from pick to a command of your choice.

I prefer to use the command fixup as this "squashes" the commit's changes into the commit on the line above and discards the commit's message.

As the commit on line 1 is HEAD, in most cases you would leave this as pick. You cannot use squash or fixup as there is no other commit to squash the commit into.

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 comment squash this into my new feature!.

I then run git log or gitk and get the commit SHA of the new feature (in this case 1ff9460).

Next, I bring up the interactive rebase editor with git 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 change pick to fixup.

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

17.05.2016 06:19
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 by Kolob Canyon, 07.09.2018 17:35
Yes, leave pick in line 1. If you choose squash or fixup 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'.". by br3nt, 10.09.2018 03:27
I am constantly using this command. I recommend to add an alias for that called gr3: alias gr3='git rebase -i HEAD~3' by Flov, 13.10.2020 11:57
More on the git rebase -interactive here by Timo, 07.11.2020 14:05
Git rebase -i .. turns the git commits around: the newest is the last in the list and the oldest the first.Am I looking wrong? by Timo, 08.11.2020 15:03
@Timo, correct. Oldest at the top, newest at the bottom. That's why you need to pick the first line. And when you choose squash or fixup on a line, it will put the changes into the commit on the line above. by br3nt, 08.11.2020 23:05
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 . by Staghouse, 25.01.2021 20:07
@Staghouse yeah, it gives you a lot of control and it’s super flexible. You might want to squash a commit which fixes a type in a variable name but also reword a commit message at the same time by br3nt, 25.01.2021 21:05
Show remaining 3 comments
13
141

Based on Chris 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 last N 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 always git commit --amend to modify it manually. (Or, edit the alias to match your tastes.)

19.02.2014 19:21
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 specify git squash -m "New summary." and have N determined automatically as the number of unpushed commits. by Acumenus, 22.04.2014 18:50
@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.) by EthanB, 23.04.2014 22:29
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. by funroll, 10.07.2014 01:40
@funroll Agreed. Just dropping the last commit msg is a super common need for me. We should be able to devise that... by Steve Clay, 26.09.2014 15:52
@SteveClay Somewhere around here: git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\" :) by EthanB, 26.09.2014 15:55
you might like to count to the branch or commit with something like git rev-list @{1}..head --count by Andy Hayden, 05.02.2015 23:36
I was hoping the following would work, it doesn't quite: f(){c=$(git rev-list ${1}..head --count); git reset --soft HEAD~$c && git commit --edit -m\"$(git log --format=%B --reverse ${1}..head)\"; } by Andy Hayden, 06.02.2015 00:04
is there a way to squash all the commits for that branch instead of last X commits? by Mohamed El Mahallawy, 27.07.2015 23:07
@MohamedElMahallawy What would that even mean? All commits to branch X, since it diverged from branch Y? You'd still need to enter the name of branch Y. For me, it's simpler to just count the number of commits. by EthanB, 28.07.2015 19:15
@EthanB yes, merging with the branch ancestor (at the time of initial branching) is quite useful. The problem is we cannot rely on the ancestor's name because it could be changed. Right now the best way is to look at git-log, count and use the marvelous squash alias of yours. by dashesy, 14.07.2016 15:57
@A-B-B you can use 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. by dashesy, 14.07.2016 15:59
@dashesy Thanks for the (unintentional) reminder that folks might not be familiar with --amend. I've updated the answer to provide the hint. by EthanB, 16.09.2016 16:11
As a git n00b, this absolutely ticked the box. by Eight-Bit Guru, 03.12.2018 14:23
Show remaining 8 comments
6
111

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

12.04.2020 12:14
Comments
This solution shouldn't be encouraged. Using the-f (force) option in push is a dangerous practice, particularly if you're pushing to a shared repo (i.e public history) that'll make life dfficult for contributors by FXQuantTrader, 17.11.2020 05:09
works good for the personal feature branches, should not be used on the branches where multiple people work, but I'd say latter may be in some cases just bad organization. by ivan.ukr, 31.12.2020 02:26
if the goal is to add this new commit to master as part of an ongoing pr, you could use git reset --soft $(git merge-base feature master) and then git commit. by Karl Pokus, 15.02.2021 08:42
@FXQuantTrader when you git rebase -i you have to force-push too, don't you? Unless you're not using --force-with-lease I don't think there's any harm in that solution. by blisher, 11.03.2021 16:32
@FXQuantTrader: If everybody force-pushes, the opposite the case. Almost always there is need to push with --force. Doing this aligns you well with the rest of the team and different remote locations as you know what is going on. Also you know which part of the history has stabilized already. No need to hide that. by hakre, 30.03.2021 23:05
I would argue that pushing with force should be actually encouraged 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. by Matheus Felipe, 13.04.2021 23:16
Show remaining 1 comments
1
68

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 below pick one commit and squash the others into the most recent,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

For details click on the Link

06.04.2017 05:26
Comments
More details on SO on the git rebase -interactive here by Timo, 08.11.2020 14:56
4
61

If you use TortoiseGit, you can the function Combine to one commit:

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

Combine commits

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

06.11.2015 12:51
Comments
As far as I am aware, this will not work for merge commits. by Thorkil Holm-Jacobsen, 26.04.2017 09:50
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. by Charles Roberto Canato, 27.04.2018 22:03
This is awesome! Thank you by gsscoder, 09.07.2020 15:23
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. by sanpat, 18.03.2021 19:39
7
54

Based on this 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/dev
git add --all
git commit -m 'my commit message'
22.05.2014 00:41
Comments
Just what I needed. Squash down commits from my feature branch, and then I git cherry pick that commit into my master. by David Victor, 10.07.2015 12:34
This does not squash previous commits! by IgorGanapolsky, 22.10.2015 16:26
could you elaborate that a bit further @igorGanapolsky ? by trudolf, 03.12.2015 05:54
@trudolf This isn't really squashing (picking individual commits to squash). This is more of committing all of your changes at once. by IgorGanapolsky, 03.12.2015 06:26
yes, hence it squashes all of your commits into one. congratulations! by trudolf, 04.12.2015 10:50
For some reason this overwrites existing changes on master that have been pushed to it after I branched off master to create feature branch. Not sure why though. I will try doing it the other way, as mentioned in the linked article. by Jared, 12.11.2018 14:57
As continuation to my comment. When I create a merge branch, then merge feature branch into it, squashing works fine. It requires a little more work, but still less than cherry-picking everything by hand. by Jared, 12.11.2018 15:32
Show remaining 2 comments
2
46

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~1

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 rebase --continue

For more on this and other ways to rewrite your commit history see this helpful post

10.07.2018 22:28
Comments
Please also explain what the --continue and vim :x does. by not2qubit, 30.11.2018 12:20
The rebase will happen in blocks as it goes through the commits on your branch, after you git add the correct configuration in your files you use git 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 see this by aabiro, 30.11.2018 16:51
2
37

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 with git log. Most important, find the commit hash of the first commit you don't want to squash. So only the :

enter image description here

Step 1: git rebase

Execute git 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 in git 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 with git log.

26.06.2018 18:03
Comments
would be nice to see the final result, e.g., git log by Timothy L.J. Stewart, 03.12.2018 19:44
@Axalix Did you remove all your lines? That's how you lose your commits. by a3y3, 22.06.2019 19:53
2
36

Procedure 1

1) Identify the commit short hash

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Here even git 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 a nano editor for merging. And it looks like below

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Rename the word pick to squash which is present before abcd1234. After rename it should be like below.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Now save and close the nano editor. Press ctrl + o and press Enter to save. And then press ctrl + x to exit the editor.

6) Then nano 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-commit
1122abcd Fix issue B
deab3412 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 on ubuntu shell. If you are using different os (Windows or Mac) 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 the OLDCOMMIT 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 with fixup1 <OLDCOMMIT_MSG>.

  1. Then execute below command to merge(squash) the new commit to the OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

Here ^ means the previous commit to OLDCOMMIT. This rebase command opens interactive window on a editor (vim or nano) on that we no need to do anything just save and exiting is sufficient. Because the option passed to this will automatically move the latest commit to next to old commit and change the operation to fixup (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 with git-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 commit cdababcd and generates new commit ID 1d4ab2e1

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.
10.01.2018 14:19
Comments
It only updates the last two commits even I reset to a commit Id to the 6th last commit, do not know why by Carlos Liu, 16.07.2018 07:06
Even you can rearrange the commit order. It works fine. by rashok, 21.08.2018 18:16
1
31

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
21.08.2018 19:25
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. by bharath, 21.02.2020 19:30
5
32

If you are on a remote branch(called feature-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
    
09.09.2015 15:58
Comments
Since I was just squashing ~100 commits (for syncing svn branches via git-svn), this is far faster than interactively rebasing! by sage, 03.11.2015 20:34
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... by sage, 03.11.2015 20:35
agree with you @sage, lets hope they might do it sometime in the future by Sandesh Kumar, 03.11.2015 20:51
This is the right way. Rebase approach is good, but should only be used for squash as a last resort solution. by Axalix, 11.03.2019 20:13
I'm getting everything up to date by Jake Sylvestre, 06.12.2020 16:33
3
31

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
16.05.2019 07:55
Comments
Just be careful, Since if you use force then there is no way to retrieve the previous commits since you removed it by Albert Ruelan, 03.07.2019 00:28
Nice and quick solution, as for me. by B. Bohdan, 03.07.2020 06:53
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. by Osama Shabrez, 31.08.2020 10:26
2
30

I think the easiest way to do this is by making a new branch based on master and doing a merge --squash of the feature branch.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Then you have all of the changes ready to commit.

20.03.2018 15:28
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 of git merge by Felipe Romero, 07.10.2020 02:11
I was trying to do other solutions, but for w/e reason they weren't working. This one did. by Brian Schermerhorn, 12.03.2021 15:11
1
22

What can be really convenient:
Find the commit hash you want to squash on top of, say d43e15.

Now use

git reset d43e15
git commit -am 'new commit name'
14.09.2017 10:33
Comments
This. Why don't more people use this? It's way faster than rebasing and squashing individual commits. by a3y3, 22.06.2019 19:51
2
16

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 is git-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 what Mark Longair suggested, though.

04.03.2011 16:12
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. by Mark Longair, 04.03.2011 21:04
You could simplify this using gawk. git -c core.editor="gawk -i inplace '{if(NR>1 && \$1==\"pick\"){\$1=\"squash\"} print \$0}'" rebase -i --autosquash HEAD~5. by typesanitizer, 30.08.2020 08:56
0
17

If you want to squish every commit into a single commit (e.g. when releasing a project publicly for the first time), try:

git checkout --orphan <new-branch>
git commit
20.08.2016 20:39
0
17

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/

19.03.2021 14:59
1
16

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}
14.02.2019 19:20
Comments
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. by Locane, 07.05.2019 20:41
2
15

⚠️ 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 the https://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 run git reset --soft HEAD~4 && git commit. Then you git 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. And git log doesn't match what you see on GitHub.

🐻 MORAL OF THE STORY

  1. Find the exact files you want to get to, and git checkout them
  2. Find the exact prior commit you want to keep in history, and git reset --soft that
  3. Make a git commit that warps directly from the from to the to
02.11.2018 04:34
Comments
This is 100% the easiest way to do this. If your current HEAD is the correct state you want, then you can skip #1. by Stan, 06.02.2019 22:26
This is the only way I know that allows to rewrite the first commit history. by Vadorequest, 19.11.2019 12:18
0
9

If you don't care about the commit messages of the in-between commits, you can use

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend
08.07.2019 13:16
1
8

If you're working with GitLab, you can just click the Squash option in the Merge Request as shown below. The commit message will be the title of the Merge Request.

enter image description here

12.03.2019 10:00
Comments
With GitLab Enterprise Edition 12.8.6-ee it just randomly took a commit message for the squashed commit... by Wolfson, 09.06.2020 12:23
7
6

I find a more generic solution is not to specify 'N' commits, but rather the branch/commit-id you want to squash on top of. This is less error-prone than counting the commits up to a specific commit—just specify the tag directly, or if you really want to count you can specify HEAD~N.

In my workflow, I start a branch, and my first commit on that branch summarizes the goal (i.e. it's usually what I will push as the 'final' message for the feature to the public repository.) So when I'm done, all I want to do is git squash master back to the first message and then I'm ready to push.

I use the alias:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

This will dump the history being squashed before it does so—this gives you a chance to recover by grabbing an old commit ID off the console if you want to revert. (Solaris users note it uses the GNU sed -i option, Mac and Linux users should be fine with this.)

04.11.2014 20:40
Comments
I tried the alias but I'm not sure if the sed replaces are having any effect. What should they do? by raine, 21.03.2015 18:04
The first sed just dumps the history to the console. The second sed replaces all the 'pick' with 'f' (fixup) and rewrites the editor file in-place (the -i option). So the second one does all the work. by Ethan, 22.04.2015 20:28
You are right, counting N-number of specific commits is very error prone. It has screwed me up several times and wasted hours trying to undo the rebase. by IgorGanapolsky, 02.10.2015 00:37
Hi Ethan, I would like to know if this workflow will hide possible conflicts on the merge. So please consider if you have two branches master and slave. If the slave has a conflict with the master and we use git squash master when we are checked out on the slave. what will it happen? will we hide the conflict? by Sergio Bilello, 24.02.2016 07:55
@Sergio This is a case of rewriting history, so you probably will have conflicts if you squash commits that have already been pushed, and then try to merge/rebase the squashed version back. (Some trivial cases might get away with it.) by Ethan, 28.06.2016 01:51
@Sergio squashing can hide some conflicts if you do rebases instead of merges. The merge only happens on the final commit, so it's equivalent to merging the squashed version. But a rebase has to process each commit, so you might have transitory conflicts that are unrolled by a later commit and you'll get a conflict on each as the rebase is processed. Rebasing the squashed version hides this intermediate history. by Ethan, 28.06.2016 01:58
yes I solved with git push --force-with-lease when the branch is mine. I don't have to take care about the history :) by Sergio Bilello, 21.10.2016 05:26
Show remaining 2 comments
0
6

How can I squash my last X commits together into one commit using Git?

git rebase -i HEAD~X

The following content will be shown:

pick 1bffc15c My earlier commit
pick 474bf0c2 My recent commit

# ...

For the commits that you want to squash, replace pick with fixup, so it becomes:

pick 1bffc15c My earlier commit
fixup 474bf0c2 My recent commit

# ...

If it's open in vim (default interface within terminal), then press Esc on your keyboard, type :wq and Enter to save the file.

Verify: Check git log

20.05.2020 14:48
0
6

In addition to other excellent answers, I'd like to add how git rebase -i always confuses me with the commit order - older to newer one or vice versa? So this is my workflow:

  1. git rebase -i HEAD~[N] , where N is the number of commits I want to join, starting from the most recent one. So git rebase -i HEAD~5 would mean "squash the last 5 commits into a new one";
  2. the editor pops up, showing the list of commits I want to merge. Now they are displayed in reverse order: the older commit is on top. Mark as "squash" or "s" all the commits in there except the first/older one: it will be used as a starting point. Save and close the editor;
  3. the editor pops up again with a default message for the new commit: change it to your needs, save and close. Squash completed!

Sources & additional reads: #1, #2.

09.03.2018 09:51
1
6

What about an answer for the question related to a workflow like this?

  1. many local commits, mixed with multiple merges FROM master,
  2. finally a push to remote,
  3. PR and merge TO master by reviewer. (Yes, it would be easier for the developer to merge --squash after the PR, but the team thought that would slow down the process.)

I haven't seen a workflow like that on this page. (That may be my eyes.) If I understand rebase correctly, multiple merges would require multiple conflict resolutions. I do NOT want even to think about that!

So, this seems to work for us.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. edit and commit a lot locally, merge master regularly
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // puts all changes in stage
  7. git commit 'one message to rule them all'
  8. git push
  9. Reviewer does PR and merges to master.
20.06.2018 19:28
Comments
From many opinions I like your approach. It's very convenient and fast by Artem Vadimovich Solovev, 05.09.2018 09:57
0
6
git rebase -i HEAD^^

where the number of ^'s is X

(in this case, squash the two last commits)

19.02.2018 12:32
0
5

In question it could be ambiguous what is meant by "last".

for example git log --graph outputs the following (simplified):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

Then last commits by time are H0, merge, B0. To squash them you will have to rebase your merged branch on commit H1.

The problem is that H0 contains H1 and H2 (and generally more commits before merge and after branching) while B0 don't. So you have to manage changes from H0, merge, H1, H2, B0 at least.

It's possible to use rebase but in different manner then in others mentioned answers:

rebase -i HEAD~2

This will show you choice options (as mentioned in other answers):

pick B1
pick B0
pick H0

Put squash instead of pick to H0:

pick B1
pick B0
s H0

After save and exit rebase will apply commits in turn after H1. That means that it will ask you to resolve conflicts again (where HEAD will be H1 at first and then accumulating commits as they are applied).

After rebase will finish you can choose message for squashed H0 and B0:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

P.S. If you just do some reset to BO: (for example, using reset --mixed that is explained in more detail here https://stackoverflow.com/a/18690845/2405850):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

then you squash into B0 changes of H0, H1, H2 (losing completely commits for changes after branching and before merge.

25.05.2017 01:26
0
1

Just add this bash function to your bash of .zshrc file.

# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
    if [ -z "${1}" -o -z "${2}" ]; then
        echo "Usage: \`squash X COMMIT_MSG\`"
        echo "X= Number of last commits."
        echo "COMMIT_MSG= New commit msg."
        return 1
    fi

    git reset --soft HEAD~"$1"
    git add . && git ci -m "$2" # With 100 emoji
    git push --force
}

Then just run

squash X 'New Commit Message'

And you're done.

23.05.2017 12:33
0
2

First I find out the number of commits between my feature branch and current master branch by

git checkout master
git rev-list master.. --count

Then, I create another branch based out my-feature branch, keep my-feature branch untouched.

Lastly, I run

git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge

Hope it helps, thanks.

29.08.2018 06:37
0
2

Many answers are based on git rebase command, but in my experience it is somewhat complex and advanced for git-beginners.

Let's say you want to squish last 3 commits. Then following are the steps:

  • First check git log: Use git log -1 --oneline to note down 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 by git reset --soft <ORIGINAL_COMMIT>

23.02.2021 11:37
0
1

If you're using GitUp, select the commit you want to merge with its parent and press S. You have to do it once for each commit, but it's much more straightforward than coming up with the correct command line incantation. Especially if it's something you only do once in a while.

19.02.2016 00:52
1
1

simple solution:

git reset --soft HEAD~5

git commit -m "commit message"

git push origin branch --force-with-lease

19.04.2021 13:28
Comments
nice try, but there is a better way mentioned here with rebase by Radek, 21.04.2021 09:36
0
1

To avoid having to resolve any merge conflicts when rebasing onto a commit in the same branch, you can use the following command

git rebase -i <last commit id before your changes start> -s recursive -X ours

To squash all commits into one, when you are prompted to edit commits to be merged (-i flag), update all but first action from pick to squash as recommended in other answers too.

Here, we use the merge strategy (-s flag) recursive and strategy option (-X) ours to make sure that the later commits in the history win any merge conflicts.

NOTE: Do not confuse this with git rebase -s ours which does something else.

Reference: git rebase recursive merge strategy

10.01.2020 17:31
0
1

Tried all approaches mention here. But finally my issue resolved by following this link. https://gist.github.com/longtimeago/f7055aa4c3bba8a62197

$ git fetch upstream
$ git checkout omgpull 
$ git rebase -i upstream/master

 < choose squash for all of your commits, except the first one >
 < Edit the commit message to make sense, and describe all your changes >

$ git push origin omgpull -f
30.06.2020 08:37