From 24f011f59d9efa76f4af945d67dc388a6d8eeac0 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 5 Jan 2017 01:15:43 +0000 Subject: [PATCH] badcommit-fixup: wip g-f-b --- badcommit-fixup | 124 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file 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 = ; + $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 = ; + $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 $!; + $_ = ; + 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) -- 2.30.2