Skip to content

Instantly share code, notes, and snippets.

@hlecuanda
Last active April 7, 2024 02:46
Show Gist options
  • Save hlecuanda/c9a5091938cd6a7b585d to your computer and use it in GitHub Desktop.
Save hlecuanda/c9a5091938cd6a7b585d to your computer and use it in GitHub Desktop.
Git Squash alias, from amazingly clean explanation on stackoverflow

From This other answer on the same thread

Based on Chris Johnsen's answer:

I added this line to the [alias] section of my git config file (~/.gitconfig):

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.




My previous solution was this `[alias]`:
squash = "!f(){ git rebase -i HEAD~${1}; }; f"

... which has the same usage, but requires you to edit the "git-rebase-todo" file (and change pick to squash).

From This answer

You can do this fairly easily without git rebase or git merge --squash.

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

@Jont828
Copy link

Jont828 commented Apr 11, 2022

I reworked this a bit so it automatically figures out how many commits it's ahead of main (or whatever branch you specify) and takes the first commit message in the PR.

alias gsquash='!f(){ git reset --soft HEAD~$(gl --oneline $(git symbolic-ref --short HEAD) ^${1:-main} | wc -l) && git commit --edit -m "$(git log --format=%B --reverse HEAD..HEAD@{1} | head -n 1)"; };f'

@jordanst3wart
Copy link

@Jont828 $(git merge-base ${1:-main} $(git branch --show-current)) is probably nicer than $(gl --oneline $(git symbolic-ref --short HEAD) ^${1:-main} | wc -l)

@jordanst3wart
Copy link

I like:

git config --global alias.squash '!f(){ git reset --soft $(git merge-base master $(git branch --show-current)) && git commit --am "${1}";};f'

used like:

git squash "commits from my feature branch squashed to master reference"
git push -f

@CHE1RON
Copy link

CHE1RON commented Nov 14, 2023

@jordanst3wart please note that --am might throw error: pathspec 'first-word-of-commit-msg' did not match any file(s) known to git (well, at least it does for me 🤷) so -am is the way to go 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment