3
# REuse REcorded REsolve. This tool records a conflicted automerge
4
# result and its hand resolution, and helps to resolve future
5
# automerge that results in the same conflict.
7
# To enable this feature, create a directory 'rr-cache' under your
14
my $git_dir = $::ENV{GIT_DIR} || ".git";
15
my $rr_dir = "$git_dir/rr-cache";
16
my $merge_rr = "$git_dir/rr-cache/MERGE_RR";
27
open $in, "<$merge_rr" or die "$!: $merge_rr";
30
my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s;
31
$merge_rr{$path} = $name;
38
open $out, ">$merge_rr" or die "$!: $merge_rr";
39
for my $path (sort keys %merge_rr) {
40
my $name = $merge_rr{$path};
41
print $out "$name\t$path\0";
46
sub compute_conflict_name {
50
open $in, "<$path" or die "$!: $path";
52
my $sha1 = Digest->new("SHA-1");
62
elsif (/^>>>>>>> .*/) {
64
$one = join('', @{$side[0]});
65
$two = join('', @{$side[1]});
67
($one, $two) = ($two, $one);
78
elsif (defined $side[1]) {
86
return ($sha1->hexdigest, $hunk);
90
my ($path, $name) = @_;
93
open $in, "<$path" or die "$!: $path";
94
open $out, ">$name" or die "$!: $name";
100
elsif (/^=======$/) {
103
elsif (/^>>>>>>> .*/) {
105
$one = join('', @{$side[0]});
106
$two = join('', @{$side[1]});
108
($one, $two) = ($two, $one);
110
print $out "<<<<<<<\n";
112
print $out "=======\n";
114
print $out ">>>>>>>\n";
120
elsif (defined $side[1]) {
121
push @{$side[1]}, $_;
124
push @{$side[0]}, $_;
134
my $pid = open($in, '-|');
135
die "$!" unless defined $pid;
137
exec(qw(git ls-files -z -u)) or die "$!: ls-files";
143
my ($mode, $sha1, $stage, $path) =
144
/^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s;
145
$path{$path} |= (1 << $stage);
148
while (my ($path, $status) = each %path) {
149
if ($status == 14) { push @path, $path; }
155
my ($name, $path) = @_;
156
record_preimage($path, "$rr_dir/$name/thisimage");
157
unless (system('merge', map { "$rr_dir/$name/${_}image" }
158
qw(this pre post))) {
160
open $in, "<$rr_dir/$name/thisimage" or
161
die "$!: $name/thisimage";
163
open $out, ">$path" or die "$!: $path";
164
while (<$in>) { print $out $_; }
172
-d "$rr_dir" || exit(0);
175
my %conflict = map { $_ => 1 } find_conflict();
177
# MERGE_RR records paths with conflicts immediately after merge
178
# failed. Some of the conflicted paths might have been hand resolved
179
# in the working tree since then, but the initial run would catch all
180
# and register their preimages.
182
for my $path (keys %conflict) {
183
# This path has conflict. If it is not recorded yet,
184
# record the pre-image.
185
if (!exists $merge_rr{$path}) {
186
my ($name, $hunk) = compute_conflict_name($path);
188
$merge_rr{$path} = $name;
189
if (! -d "$rr_dir/$name") {
190
mkpath("$rr_dir/$name", 0, 0777);
191
print STDERR "Recorded preimage for '$path'\n";
192
record_preimage($path, "$rr_dir/$name/preimage");
197
# Now some of the paths that had conflicts earlier might have been
198
# hand resolved. Others may be similar to a conflict already that
199
# was resolved before.
201
for my $path (keys %merge_rr) {
202
my $name = $merge_rr{$path};
204
# We could resolve this automatically if we have images.
205
if (-f "$rr_dir/$name/preimage" &&
206
-f "$rr_dir/$name/postimage") {
207
if (merge($name, $path)) {
208
print STDERR "Resolved '$path' using previous resolution.\n";
209
# Then we do not have to worry about this path
211
delete $merge_rr{$path};
216
# Let's see if we have resolved it.
217
(undef, my $hunk) = compute_conflict_name($path);
220
print STDERR "Recorded resolution for '$path'.\n";
221
copy($path, "$rr_dir/$name/postimage");
222
# And we do not have to worry about this path anymore.
223
delete $merge_rr{$path};
226
# Write out the rest.