I'm trying to squash a range of commits - HEAD to HEAD~3. Is there a quick way to do this, or do I need to use rebase --interactive?
问题:
回答1:
Make sure your working tree is clean, then
git reset --soft HEAD~3
git commit -m'new commit message'
回答2:
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 '----------------'
回答3:
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" :)
回答4:
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.
回答5:
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
回答6:
Here is a one liner to squash the last 2 commits. In this example, the message of second last commit will be retained. You may change the message as you wish.
git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"
This command will be very useful if you create an alias for this command and use the alias instead.
回答7:
You can get pretty close with
git rebase --onto HEAD~4 HEAD~ master
This assumes you're on master with a linear history. It's not quite a squash because it discards the intermediate commits. You'd need to amend the new HEAD to modify the commit message.
回答8:
To squash everything since the branch was forked from master:
git reset --soft $(git merge-base --fork-point master) \
&& git commit --verbose --reedit-message=HEAD --reset-author