chiark / gitweb /
badcommit-fixup: wip g-f-b
[dgit-junk.git] / badcommit-fixup
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 use POSIX;
6 use IPC::Open2;
7
8 $!=0; $?=0;
9 my $refs=`git-for-each-ref`;
10 die "$? $!" if $?;
11
12 chomp $refs;
13
14 my $gcfpid = open2 \*GCFO, \*GCFI, 'git cat-file --batch' or die $!;
15
16 sub getobj ($$) {
17     my ($obj, $type) = @_;
18     print GCFI $obj, "\n" or die $!;
19     my $x = <GCFO>;
20     $x =~ m/^\w+ (\w+) (\d+)\n/ or die "$obj ?";
21     my ($gtype, $gsize) = ($1,$2,$3);
22     $gtype eq $type or die "$obj $gtype != $type ?";
23     my $gdata;
24     read GFCO, $gdata, $gsize == $gsize or die "$obj $!";
25     $x = <GFCO>;
26     $x eq "\n" or die "$obj $!";
27     return $gdata;
28 }
29
30 our %memo;
31 our %count;
32
33 sub rewrite_commit ($) {
34     my ($obj) = @_;
35     my $m = \ $memo{$obj};
36     return $$m if defined $$m;
37     my $olddata = getobj $obj, 'commit';
38     die "$obj ?" unless $old;
39     $olddata =~ m/(?<=\n)(?=\n)/ or die "$obj ?";
40     my $msg = $';
41     $_ = $`;
42     s{^(parent )(\w+)$}{ $1 . rewrite_commit($2) }gme;
43     $count{fix_overwrite} += s{^commiter }{committer }gm;
44     if (!m{^author }m && !m{^committer }m) {
45         m{^parent (\w+)}m or die "$obj ?";
46         my $parent = getobj $1, 'commit';
47         $parent =~ m/^(?:.+\n)+(author .*\ncommitter .*\n)/;
48         m/\n$/ or die "$obj ?";
49         $_ .= $1;
50         $count{fix_import}++;
51     }
52     $newdata = $_.$msg;
53     my $newobj;
54     if ($newdata eq $olddata) {
55         $newobj = $oldobj;
56     } else {
57         my $gwopid = open2 \*GWO, \*GWI,
58             'git hash-object -t comit --stdin'
59             or die $!;
60         print GWI $newdata or die $!;
61         close GWI or die $!;
62         $_ = <GWO>;
63         close GWO or die $!;
64         waitpid $gwopid,0 == $gwopid or die $!;
65         die $? if $?;
66         m/^(\w+)\n/ or die "$_ ?";
67         $newobj = $1;
68         $count{commits}++;
69     }
70     $$m= $newobj;
71     return $newobj;
72 }
73
74 sub rewrite_tag ($) {
75     my ($obj) = @_;
76     $_ = getobj $obj, 'tag';
77     m/^type (\w+)\n/m or die "$obj ?";
78     if ($1 ne 'commit') {
79         $count{"oddtags $1"}++;
80         return;
81     }
82     m/^object (\w+)\n/m or die "$obj ?";
83     my $oldref = $1;
84     my $newref = rewrite_commit $oldref;
85     if ($oldref eq $newref) {
86         return $obj;
87     }
88     s/^(?<=object )\w+(?=\n)/$newref/m or die "$obj ?";
89     
90 }
91
92 foreach my $rline (split /\n/, $refs) {
93     die "$_ ?" unless m/^(\w+)\s+(\w+)\s+(\S.*)/;
94     my ($obj, $type, $refname) = @_;
95     my $rewrite;
96     if ($type eq 'commit') {
97         $rewrite = rewrite_commit($obj);
98     } elsif ($type eq 'tag') {
99         my $rewrite = rewrite_tag($obj);
100     } else {
101         warn "ref $refname refers to $type\n";
102         next;
103     }
104     next if $refname eq $rewrite;
105     push @updates, [ $refname, $rewrite ];
106 }
107
108
109
110 if git-symbolic-ref HEAD >/dev/null 2>&1; then
111     refs+=' HEAD'
112
113
114
115 my $gfo = `LC_MESSAGES=C git fsck --no-dangling 2>&1`;
116 $? == 256 or die "$? $!"
117
118
119
120
121  m/^error in commit (\w+):.*invalid format - expected 'committer/;
122
123 case `wc -l <$tmp/bad` in
124     0)
125         echo >&2 'nothing bad found - is git-fsck doing as we expect?' ;
126         exit 8 ;;
127     1)
128         read <$tmp/bad bads
129         nots="^$bads^0 ^$bads^1"
130         ;;
131     *)
132         bads="cat $tmp/bad"
133         ;;
134 esac
135
136 args="$nots"
137
138 refs=`git-for-each-ref --format='%(refname)'`
139
140 if git-symbolic-ref HEAD >/dev/null 2>&1; then
141     refs+=' HEAD'
142 fi
143
144 for head in $refs; do
145     exec <$tmp/bad
146     needed=false
147     for bad in $bads; do
148         if git merge-base --is-ancestor $bad $head; then
149             needed=true
150             break
151         fi
152     done 
153     if ! $needed; then continue; fi
154     args+=" $head"
155 done
156
157 git filter-branch --original dgit-badcommit --commit-filter '
158 echo >&2 "FOO $*"
159 cat >&2
160 echo >&2 ====
161 false
162     sed -e '\''1,/^$/ s/^commiter /committer /'\''
163 ' $args