git-squash branch after - Squash my last X commits together using Git





15 Answers

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

push tutorial before

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




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.




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.




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'



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 bellow,

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

For details click on the Link

Good luck!!




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 :

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:

Step 3: Adjust message(s)

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

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




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



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'



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




git rebase -i HEAD^^

where the number of ^'s is X

(in this case, squash the two last commits)




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.




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.



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.




Another way to do this if you have a ton of commits is to do a squash AFTER a commit like git rebase -i <hashbeforeyouwanttosquash>

This will open your editor to pick/squash like normal.

See https://git-scm.com/docs/git-rebase#_interactive_mode




Switch to the master branch and make sure you are up to date:

sh git checkout master && git fetch && git pull

Merge your feature branch into the master branch locally:

sh git merge feature_branch

Reset the local master branch to origin's state:

sh git reset origin/master

Now all of your changes are considered as unstaged changed. You can stage and commit them into one or more commits.

sh git add . --all git commit




Related