#!/usr/bin/perl -w use strict; use POSIX; use IPC::Open2; $!=0; $?=0; my $refs=`git-for-each-ref`; die "$? $!" if $?; chomp $refs; 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) 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)'` if git-symbolic-ref HEAD >/dev/null 2>&1; then refs+=' HEAD' fi 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 git filter-branch --original dgit-badcommit --commit-filter ' echo >&2 "FOO $*" cat >&2 echo >&2 ==== false sed -e '\''1,/^$/ s/^commiter /committer /'\'' ' $args