chiark / gitweb /
badcommit-fixup: wip g-f-b
[dgit.git] / badcommit-fixup
index 15b1720cf4d265028b5d9bfa73a5001b1e6fdf2c..58ad12d7d6e5e36d97f557c0d1d6436634e8be60 100755 (executable)
 
 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)