Generally I don't want to undo a bunch of commits, but rather edit an earlier commit to how I wish I had committed it in the first place.
I found myself fixing a past commit frequently enough that I wrote a script for it.
Here's the workflow:
git commit-edit <commit-hash>
This will drop you at the commit you want to edit.
The changes of the commit will be unstaged, ready to be staged as you wish it was the first time.
Fix and stage the commit as you wish it had been in the first place.
(You may want to use git stash save --keep-index
to squirrel away any files you're not committing)
Redo the commit with --amend
, eg:
git commit --amend
Complete the rebase:
git rebase --continue
Call this following git-commit-edit
and put it in your $PATH
:
#!/bin/bash
# Do an automatic git rebase --interactive, editing the specified commit
# Revert the index and working tree to the point before the commit was staged
# https://stackoverflow.com/a/52324605/5353461
set -euo pipefail
script_name=${0##*/}
warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }
[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"
# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
# Be able to show what commit we're editing to the user
if git config --get alias.print-commit-1 &>/dev/null; then
message=$(git print-commit-1 "$commit")
else
message=$(git log -1 --format='%h %s' "$commit")
fi
if [[ $OSTYPE =~ ^darwin ]]; then
sed_inplace=(sed -Ei "")
else
sed_inplace=(sed -Ei)
fi
export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)" # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty # Commit an empty commit so that that cache diffs are un-reversed
echo
echo "Editing commit: $message" >&2
echo