You can do this fairly easily without git rebase or git merge --squash . In this example, we'll squash the last 3 commits. 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.
There is no explicit Git squash command. Instead, to squash git commits, the interactive git rebase is used. To squash all commits on a single branch, the interactive git rebase command must be passed one of two arguments: the id of the commit from which the branch split from its parent.
Make sure your working tree is clean, then
git reset --soft HEAD~3
git commit -m 'new commit message'
I personally like wilhelmtell's solution:
git reset --soft HEAD~3
git commit -m 'new commit message'
However, I made an alias with some error checking so that you can do this:
git squash 3 'my commit message'
I recommend setting up aliases that actually run scripts so that it is easier to (a) code up your scripts and (b) do more complex work with error checking. Below is a script that does the work of squash and then below that is a script for setting up your git aliases.
Script for squashing (squash.sh)
#!/bin/bash
#
#get number of commits to squash
squashCount=$1
#get the commit message
shift
commitMsg=$@
#regular expression to verify that squash number is an integer
regex='^[0-9]+$'
echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"
echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
echo "Invalid commit message. Make sure string is not empty"
else
echo "...input looks good"
echo "...proceeding to squash"
git reset --soft HEAD~$squashCount
git commit -m "$commitMsg"
echo "...done"
fi
echo
exit 0
Then to hook up that squash.sh script to a git alias, make another script for setting up your git aliases like so (create_aliases.command or create_aliases.sh):
#!/bin/sh
echo '-----------------------'
echo 'adding git aliases....'
echo '-----------------------'
echo
git config --global alias.squash "!bash -c 'bash <path to scripts directory>/squash.sh \$1 \$2' -"
#add your other git aliases setup here
#and here
#etc.
echo '------------------------------------'
echo 'here is your global gitconfig file:'
echo '------------------------------------'
more ~/.gitconfig
echo
echo
echo '----------------'
echo 'end of script...'
echo '----------------'
To add to the answer by wilhelmtell I find it convenient to soft reset to HEAD~2
and then amending the commit of HEAD~3
:
git reset --soft HEAD~2
git commit --all --amend --no-edit
This will merge all commits to the HEAD~3
commit and use its commit message. Be sure to start from a clean working tree.
I used:
EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>
Worked quite fine. Just don't try to have a commit log with a line that starts with "pick" :)
Use the following command to squash the last 4 commits within the last commit:
git squash 4
With the alias:
squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf
From https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With