rebase - Squash my last X commits together using Git

ID : 78

viewed : 226

Tags : gitrebasesquashgit-squashgit

Top 5 Answer for rebase - Squash my last X commits together using Git

vote vote

97

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

vote vote

83

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.

vote vote

74

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.

vote vote

63

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.

vote vote

56

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

Top 3 video Explaining rebase - Squash my last X commits together using Git

Related QUESTION?