Last active
May 2, 2024 21:55
-
-
Save geraldlai/8aeb3240e0e7f9f11d8cdb5b7a933125 to your computer and use it in GitHub Desktop.
git commit-fix: fixup an old commit quickly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# git commit-fix: fixup an old commit quickly | |
# $ git commit-fix 1efa604 | |
# ( fixup commit 1efa604 to have currently staged changes ) | |
# ( unstaged changes will be safely stashed away ) | |
# $ git commit-fix -s 1efa604 | |
# ( same as above, but allow new commit message to be squashed in ) | |
# $ git commit-fix -e 1efa604 | |
# ( same as fixup, but stop branch on 1efa604 first for any pre-edit ) | |
# ( can be combined with --squash/-s option ) | |
# Author: Gerald Lai | |
# License: 0BSD | |
use warnings; | |
use strict; | |
use File::Basename qw(basename); | |
use File::Temp qw(tempfile); | |
sub run { system(@_) == 0 or exit($? >> 8) } | |
if ($ENV{GIT_COMMIT_FIX_TARGET}) { | |
my ($rebase_file) = @ARGV; | |
if (basename($rebase_file) ne "git-rebase-todo") { | |
exec(($ENV{EDITOR} || "vim"), @ARGV); | |
} | |
if (!$ENV{GIT_COMMIT_FIX_EDIT}) { | |
exec "touch", @ARGV; | |
} | |
open(my $REBASE, "<", $rebase_file) | |
or die "Cannot read $rebase_file : $!\n"; | |
my ($OUT, $out_file) = tempfile(); | |
while (<$REBASE>) { | |
if (/^pick ([a-f0-9]+) (.*)/) { | |
$_ = "edit $1 $2\n" if index($ENV{GIT_COMMIT_FIX_TARGET}, $1) == 0; | |
} | |
print $OUT $_; | |
} | |
close $OUT; | |
close $REBASE; | |
run( "mv", $out_file, $rebase_file ); | |
exit 0; | |
} | |
my $is_edit = 0; | |
my $fixup_opt = "fixup"; | |
my $target; | |
if (@ARGV) { | |
local $_ = $ARGV[0]; | |
if (/^(--edit|-e)$/) { | |
$is_edit = 1; | |
shift @ARGV; | |
} | |
if (/^(--squash|-s)$/) { | |
$fixup_opt = "squash"; | |
shift @ARGV; | |
} | |
} | |
if (@ARGV) { | |
$target = $ARGV[0]; | |
$target =~ s{'}{'"'"'}g; # sh escape | |
shift @ARGV; | |
} | |
die "Usage: git commit-fix [ --edit | --squash ] <commit>\n" unless defined $target; | |
my $is_commit = do { | |
chomp(my $type = qx{ git cat-file -t '$target' 2> /dev/null }); | |
( $type eq "commit" ); | |
}; | |
die "Not a valid commit: $target\n" unless $is_commit; | |
chomp($target = qx{ git rev-list -n 1 '$target' }); | |
if (system( qw(git diff --cached --quiet) ) != 0) { | |
run( qw(git commit), "--$fixup_opt", $target, @ARGV ); | |
} | |
run( qw(git stash push -u) ); | |
$ENV{GIT_COMMIT_FIX_TARGET} = $target; | |
$ENV{GIT_COMMIT_FIX_EDIT} = $is_edit; | |
$ENV{GIT_EDITOR} = $0; # usurp rebase editing | |
exec qw(git rebase -i --autosquash), "$target~1"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment