diff options
author | Ian Jackson <ijackson@chiark.greenend.org.uk> | 2017-01-06 16:11:52 +0000 |
---|---|---|
committer | Ian Jackson <ijackson@chiark.greenend.org.uk> | 2017-01-06 16:11:52 +0000 |
commit | 91e2737f7ffeb765d291b6d886626331725f31cd (patch) | |
tree | 9798903c9fc7240c72e0c18a378d527f28c1ff68 /dgit-badcommit-fixup | |
parent | f773b1ed2796196e0fa801886fce493544c38fe0 (diff) |
dgit-badcommit-fixup: Rename
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Diffstat (limited to 'dgit-badcommit-fixup')
-rwxr-xr-x | dgit-badcommit-fixup | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/dgit-badcommit-fixup b/dgit-badcommit-fixup new file mode 100755 index 0000000..4573121 --- /dev/null +++ b/dgit-badcommit-fixup @@ -0,0 +1,256 @@ +#!/usr/bin/perl -w + +# usage: +# .../badcommit-fixup --test +# .../badcommit-fixup --real + +use strict; + +use POSIX; +use IPC::Open2; +use Data::Dumper; + +my $real; + +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 runcmd { + system @_ and die "@_ $! $?"; +} + +$!=0; $?=0; +my $bare = `git rev-parse --is-bare-repository`; +die "$? $!" if $?; +chomp $bare or die; + +sub getobj ($$) { + my ($obj, $type) = @_; + print GCFI $obj, "\n" or die $!; + my $x = <GCFO>; + my ($gtype, $gsize) = $x =~ m/^\w+ (\w+) (\d+)\n/ or die "$obj ?"; + $gtype eq $type or die "$obj $gtype != $type ?"; + my $gdata; + (read GCFO, $gdata, $gsize) == $gsize or die "$obj $!"; + $x = <GCFO>; + $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 $!; + $_ = <GWO>; + close GWO or die $!; + waitpid $gwopid,0 == $gwopid or die $!; + die $? if $?; + m/^(\w+)\n/ or die "$_ ?"; + $count{"rewritten $type"}++; + return $1; +} + +our %memo; + +sub rewrite_commit ($); +sub rewrite_commit ($) { + my ($obj) = @_; + my $m = \ $memo{$obj}; + return $$m if defined $$m; + my $olddata = getobj $obj, 'commit'; + $olddata =~ m/(?<=\n)(?=\n)/ or die "$obj ?"; + my $msg = $'; + local $_ = $`; + 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'}++; + } + my $newdata = $_.$msg; + my $newobj; + if ($newdata eq $olddata) { + $newobj = $obj; + $count{unchanged}++; + } else { + $newobj = hashobj $newdata, 'commit'; + } + $$m= $newobj; + return $newobj; +} + +sub rewrite_commit_adddummy ($$$) { + my ($ref, $veryold, $old) = @_; + + die "$bare ?" unless $bare eq 'true'; + + my $td = 'dgit-broken-fixup.tmp'; + runcmd qw(rm -rf), $td; + mkdir $td, 0700 or die "$td $!"; + chdir $td or die $!; + runcmd qw(git init -q); + runcmd qw(git config gc.auto 0); + runcmd qw(rm -rf .git/objects); + symlink "../../objects", ".git/objects" or die $!; + runcmd qw(git checkout -q), $old; + + open C, "debian/changelog" or die $!; + my $clog = do { + local $/ = undef; + <C>; + }; + C->error and die $!; + close C or die $!; + defined $clog or die $!; + + $!=0; $?=0; + my $v = `dpkg-parsechangelog`; + die "$ref $veryold $old $? $!" if $?; + $v =~ m/^Source: (\S+)$/m or die "$ref $veryold $old ?"; + my $pkg = $1; + $v =~ m/^Version: (\S+)$/m or die "$ref $veryold $old ?"; + my $vsn = $1; + $vsn .= "+~dgitfix"; + + open C, ">", "debian/changelog" or die $!; + print C <<END; +$pkg ($vsn) UNRELEASED; urgency=low + + * Additional commit, with slightly incremented version number, + to override bad commits generated by dgit due to #849041. + * No changes to the package. + * Not uploaded anywhere. + + -- Ian Jackson <ijackson\@chiark.greenend.org.uk> Thu, 05 Jan 2017 17:58:21 +0000 + +END + print C $clog or die $!; + close C or die $!; + + runcmd qw(git commit -q), + '--author=Ian Jackson <ijackson@chiark.greenend.org.uk>', + qw(-m), 'Dummy changelog entry to work around #849041 fallout', + qw(debian/changelog); + + $!=0; $?=0; + my $new = `git rev-parse HEAD`; + die "$? $!" if $?; + chomp $new or die; + + chdir '..' or die $!; + runcmd qw(rm -rf), $td; + + $count{dummyadded}++; + + return $new; +} + +sub rewrite_tag ($) { + my ($obj) = @_; + $_ = getobj $obj, 'tag'; + m/^type (\w+)\n/m or die "$obj ?"; + if ($1 ne 'commit') { + $count{"oddtags $1"}++; + return $obj; + } + 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+$/ $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) { + my ($obj, $type, $refname) = + $rline =~ m/^(\w+)\s+(\w+)\s+(\S.*)/ + or die "$_ ?"; + my $rewrite; + if ($type eq 'commit') { + $rewrite = rewrite_commit($obj); + if ($refname =~ m{^refs/dgit/[^/]+$} && + $rewrite ne $obj) { + $rewrite = rewrite_commit_adddummy $refname, $obj, $rewrite; + } + } elsif ($type eq 'tag') { + $rewrite = rewrite_tag($obj); + } else { + warn "ref $refname refers to $type\n"; + next; + } + next if $rewrite eq $obj; + push @updates, [ $refname, $obj, $rewrite ]; +} + +our $worktree; + +#print Dumper(\@updates); + +open U, "|git update-ref -m 'dgit bad commit fixup' --stdin" or die $!; + +if ($real && $bare eq 'false') { + print "detaching your HEAD\n" or die $!; + runcmd 'git checkout --detach'; +} + +for my $up (@updates) { + my ($ref, $old, $new) = @$up; + my $otherref = $ref; + $otherref =~ s{^refs/}{}; + if ($real) { + print U <<END or die $!; +create refs/dgit-badcommit/$otherref $old +update $ref $new $old +END + } else { + print U <<END or die $!; +update refs/dgit-badfixuptest/$otherref $new +END + } +} + +$?=0; $!=0; +close U or die "$? $!"; +die $? if $?; + +print Dumper(\%count); + +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 $!; +} |