summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Jackson <ijackson@chiark.greenend.org.uk>2017-01-05 01:15:43 +0000
committerIan Jackson <ijackson@chiark.greenend.org.uk>2017-01-05 01:15:43 +0000
commite9c288efff0795ad647b2293ece57166a2395c98 (patch)
tree3596526877a8c013acca4a4394a990a240ff5d97
parent2a80749484dc1ee5ad89512454de3a55ad677584 (diff)
badcommit-fixup: wip g-f-b
-rwxr-xr-xbadcommit-fixup124
1 files changed, 114 insertions, 10 deletions
diff --git a/badcommit-fixup b/badcommit-fixup
index 15b1720..58ad12d 100755
--- a/badcommit-fixup
+++ b/badcommit-fixup
@@ -2,19 +2,123 @@
use strict;
-set -e
-set -o pipefail
+use POSIX;
+use IPC::Open2;
-tmp=.git/dgit-badcommit-fixup-tmp
-rm -rf $tmp
-mkdir $tmp
+$!=0; $?=0;
+my $refs=`git-for-each-ref`;
+die "$? $!" if $?;
-LC_MESSAGES=C git fsck --no-dangling >$tmp/gfo 2>&1 || test $? = 1
+chomp $refs;
-perl -ne '
- print $1, "\n" or die $! if
- m/^error in commit (\w+):.*invalid format - expected '\''committer/;
-' <$tmp/gfo >$tmp/bad
+my $gcfpid = open2 \*GCFO, \*GCFI, 'git cat-file --batch' or die $!;
+
+sub getobj ($$) {
+ my ($obj, $type) = @_;
+ print GCFI $obj, "\n" or die $!;
+ my $x = <GCFO>;
+ $x =~ m/^\w+ (\w+) (\d+)\n/ or die "$obj ?";
+ my ($gtype, $gsize) = ($1,$2,$3);
+ $gtype eq $type or die "$obj $gtype != $type ?";
+ my $gdata;
+ read GFCO, $gdata, $gsize == $gsize or die "$obj $!";
+ $x = <GFCO>;
+ $x eq "\n" or die "$obj $!";
+ return $gdata;
+}
+
+our %memo;
+our %count;
+
+sub rewrite_commit ($) {
+ my ($obj) = @_;
+ my $m = \ $memo{$obj};
+ return $$m if defined $$m;
+ my $olddata = getobj $obj, 'commit';
+ die "$obj ?" unless $old;
+ $olddata =~ m/(?<=\n)(?=\n)/ or die "$obj ?";
+ my $msg = $';
+ $_ = $`;
+ s{^(parent )(\w+)$}{ $1 . rewrite_commit($2) }gme;
+ $count{fix_overwrite} += s{^commiter }{committer }gm;
+ if (!m{^author }m && !m{^committer }m) {
+ m{^parent (\w+)}m or die "$obj ?";
+ my $parent = getobj $1, 'commit';
+ $parent =~ m/^(?:.+\n)+(author .*\ncommitter .*\n)/;
+ m/\n$/ or die "$obj ?";
+ $_ .= $1;
+ $count{fix_import}++;
+ }
+ $newdata = $_.$msg;
+ my $newobj;
+ if ($newdata eq $olddata) {
+ $newobj = $oldobj;
+ } else {
+ my $gwopid = open2 \*GWO, \*GWI,
+ 'git hash-object -t comit --stdin'
+ or die $!;
+ print GWI $newdata or die $!;
+ close GWI or die $!;
+ $_ = <GWO>;
+ close GWO or die $!;
+ waitpid $gwopid,0 == $gwopid or die $!;
+ die $? if $?;
+ m/^(\w+)\n/ or die "$_ ?";
+ $newobj = $1;
+ $count{commits}++;
+ }
+ $$m= $newobj;
+ return $newobj;
+}
+
+sub rewrite_tag ($) {
+ my ($obj) = @_;
+ $_ = getobj $obj, 'tag';
+ m/^type (\w+)\n/m or die "$obj ?";
+ if ($1 ne 'commit') {
+ $count{"oddtags $1"}++;
+ return;
+ }
+ m/^object (\w+)\n/m or die "$obj ?";
+ my $oldref = $1;
+ my $newref = rewrite_commit $oldref;
+ if ($oldref eq $newref) {
+ return $obj;
+ }
+ s/^(?<=object )\w+(?=\n)/$newref/m or die "$obj ?";
+
+}
+
+foreach my $rline (split /\n/, $refs) {
+ die "$_ ?" unless m/^(\w+)\s+(\w+)\s+(\S.*)/;
+ my ($obj, $type, $refname) = @_;
+ my $rewrite;
+ if ($type eq 'commit') {
+ $rewrite = rewrite_commit($obj);
+ } elsif ($type eq 'tag') {
+ my $rewrite = rewrite_tag($obj);
+ } else {
+ warn "ref $refname refers to $type\n";
+ next;
+ }
+ next if $refname eq $rewrite;
+ push @updates, [ $refname, $rewrite ];
+}
+
+
+
+if git-symbolic-ref HEAD >/dev/null 2>&1; then
+ refs+=' HEAD'
+
+
+
+my $gfo = `LC_MESSAGES=C git fsck --no-dangling 2>&1`;
+$? == 256 or die "$? $!"
+
+
+
+
+ m/^error in commit (\w+):.*invalid format - expected 'committer/;
case `wc -l <$tmp/bad` in
0)