X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=badcommit-fixup;h=ca2e0dfbdd28a8f02db50d38cd45244c06464d1b;hp=58ad12d7d6e5e36d97f557c0d1d6436634e8be60;hb=270c257f81665d1bcfab1ca4a3d831b5bcabc034;hpb=e9c288efff0795ad647b2293ece57166a2395c98 diff --git a/badcommit-fixup b/badcommit-fixup index 58ad12d7..ca2e0dfb 100755 --- a/badcommit-fixup +++ b/badcommit-fixup @@ -1,71 +1,94 @@ #!/usr/bin/perl -w +# usage: +# .../badcommit-fixup [-- --test +# .../badcommit-fixup --real + use strict; use POSIX; use IPC::Open2; +use Data::Dumper; -$!=0; $?=0; -my $refs=`git-for-each-ref`; -die "$? $!" if $?; +my $real; -chomp $refs; +foreach my $a (@ARGV) { + if ($a eq '--test') { + $real = 0; + } elsif ($a eq '--real') { + $real = 1; + } else { + die "$a ?"; + } +} + +die unless defined $real; my $gcfpid = open2 \*GCFO, \*GCFI, 'git cat-file --batch' or die $!; +our %count; + +no warnings qw(recursion); + 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); + my ($gtype, $gsize) = $x =~ m/^\w+ (\w+) (\d+)\n/ or die "$obj ?"; $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 $!"; + (read GCFO, $gdata, $gsize) == $gsize or die "$obj $!"; +#print STDERR ">$obj|$x|$gdata|$gsize<\n"; + $x = ; + $x eq "\n" or die "$obj ($_) $!"; + $count{inspected}++; return $gdata; } +sub hashobj ($$) { + my ($data,$type) = @_; + my $gwopid = open2 \*GWO, \*GWI, + "git hash-object -w -t $type --stdin" + or die $!; + print GWI $data or die $!; + close GWI or die $!; + $_ = ; + close GWO or die $!; + waitpid $gwopid,0 == $gwopid or die $!; + die $? if $?; + m/^(\w+)\n/ or die "$_ ?"; + $count{"rewritten $type"}++; + return $1; +} + our %memo; -our %count; +sub rewrite_commit ($); 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; + $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}++; + $count{'fix import'}++; } - $newdata = $_.$msg; + my $newdata = $_.$msg; my $newobj; if ($newdata eq $olddata) { - $newobj = $oldobj; + $newobj = $obj; + $count{unchanged}++; } 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}++; + $newobj = hashobj $newdata, 'commit'; } $$m= $newobj; return $newobj; @@ -77,7 +100,7 @@ sub rewrite_tag ($) { m/^type (\w+)\n/m or die "$obj ?"; if ($1 ne 'commit') { $count{"oddtags $1"}++; - return; + return $obj; } m/^object (\w+)\n/m or die "$obj ?"; my $oldref = $1; @@ -85,79 +108,72 @@ sub rewrite_tag ($) { if ($oldref eq $newref) { return $obj; } - s/^(?<=object )\w+(?=\n)/$newref/m or die "$obj ?"; - + s/^(object )\w+$/ $1.$newref /me or die "$obj ($_) ?"; + s/^-----BEGIN PGP SIGNATURE-----\n.*^-----END PGP SIGNATURE-----\n$//sm; + return hashobj $_, 'tag'; } +$!=0; $?=0; +my $refs=`git for-each-ref`; +die "$? $!" if $?; + +chomp $refs; + +our @updates; + foreach my $rline (split /\n/, $refs) { - die "$_ ?" unless m/^(\w+)\s+(\w+)\s+(\S.*)/; - my ($obj, $type, $refname) = @_; + my ($obj, $type, $refname) = + $rline =~ m/^(\w+)\s+(\w+)\s+(\S.*)/ + or die "$_ ?"; my $rewrite; if ($type eq 'commit') { $rewrite = rewrite_commit($obj); } elsif ($type eq 'tag') { - my $rewrite = rewrite_tag($obj); + $rewrite = rewrite_tag($obj); } else { warn "ref $refname refers to $type\n"; next; } - next if $refname eq $rewrite; - push @updates, [ $refname, $rewrite ]; + next if $rewrite eq $obj; + push @updates, [ $refname, $obj, $rewrite ]; } +open U, "|git update-ref -m 'dgit bad commit fixup' --stdin" or die $!; +if ($real) { + $!=0; $?=0; + my $bare = `git rev-parse --is-bare-repository`; + die "$? $!" if $?; + if ($bare eq 'false') { + print "detaching your HEAD\n" or die $!; + system 'git checkout --detach' and die "$! $?"; + } +} -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) - echo >&2 'nothing bad found - is git-fsck doing as we expect?' ; - exit 8 ;; - 1) - read <$tmp/bad bads - nots="^$bads^0 ^$bads^1" - ;; - *) - bads="cat $tmp/bad" - ;; -esac - -args="$nots" - -refs=`git-for-each-ref --format='%(refname)'` +for my $up (@updates) { + my ($ref, $old, $new) = @$up; + my $otherref = $ref; + $otherref =~ s{^refs/}{}; + if ($real) { + print U </dev/null 2>&1; then - refs+=' HEAD' -fi +$?=0; $!=0; +close U or die "$? $!"; +die $? if $?; -for head in $refs; do - exec <$tmp/bad - needed=false - for bad in $bads; do - if git merge-base --is-ancestor $bad $head; then - needed=true - break - fi - done - if ! $needed; then continue; fi - args+=" $head" -done +print Dumper(\%count); -git filter-branch --original dgit-badcommit --commit-filter ' -echo >&2 "FOO $*" -cat >&2 -echo >&2 ==== -false - sed -e '\''1,/^$/ s/^commiter /committer /'\'' -' $args +if ($real) { + print "old values saved in refs/dgit-badcommit/\n" or die $!; +} else { + print "testing output saved in refs/dgit-badfixuptest/\n" or die $!; +}