2 # NetHack 3.6 NHsubst $NHDT-Date: 1524689631 2018/04/25 20:53:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.4 $
3 # Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland
4 # NetHack may be freely redistributed. See license for details.
6 # git merge driver for substitutions (like RCS/CVS)
7 # driver line: .... %O %A %B %L
11 my $rawin = 0; # feed diff to stdin for testing (do NOT set $debug=1)
13 # We want TRACE open so we don't need to test $debug everywhere, but we skip
14 # this first block because it's expensive and dumpfile() hangs with $rawin.
15 my $sink = ($^O eq "MSWin32") ? "NUL" : "/dev/null";
16 my $dbgfile = ($^O eq "MSWin32") ? "$ENV{TEMP}.$$" : "/tmp/trace.$$";
17 open TRACE, ">>", $rawin?"/dev/tty":(($debug==0)? $sink : $dbgfile);
18 print TRACE "TEST TRACE\n";
20 print TRACE "START CLIENT ARGV:\n";
21 print TRACE "[0] $0\n";
23 for(my $x=0;$x<scalar @ARGV;$x++){
25 print TRACE "[$x1] $ARGV[$x]\n";
28 foreach my $k (sort keys %ENV){
29 next unless ($k =~ m/^GIT_/);
30 print TRACE " $k => $ENV{$k}\n";
32 print TRACE "CWD: " . `pwd`;
33 &dumpfile($ARGV[0], "[0O]");
34 &dumpfile($ARGV[1], "[1A]");
35 &dumpfile($ARGV[2], "[2B]");
36 print TRACE "L=$ARGV[3]\n";
40 my $mark_len = $ARGV[3];
41 $mark_len = 3 if($mark_len==0 && $rawin);
43 my $mark_start = '<' x $mark_len;
44 my $mark_middle = '=' x $mark_len;
45 my $mark_end = '>' x $mark_len;
48 # pick up the prefix for substitutions in this repo
52 $PREFIX = `git config --local --get nethack.substprefix`;
61 #system "git merge-file -p .... > temp
62 my $tags = "-L CURRENT -L ANCESTOR -L OTHER"; # XXX should "CURRENT" be "MINE"?
63 @out = `git merge-file -p $tags $ARGV[1] $ARGV[0] $ARGV[2]`;
64 #NB: we don't check the exit value because it's useless
65 print TRACE "MERGE-FILE START\n".join("",@out)."MERGE-FILE END\n";
68 ($cntout,@out) = &edit_merge(@out);
71 print "COUNT: $cntout\n";
74 # spit @out to $ARGV[1] (careful: what about EOL character?)
75 open OUT, ">$ARGV[1]" or die "Can't open $ARGV[1]";
79 print TRACE "WRITING START ($ARGV[1])\n".join("",@out)."WRITING END\n";
80 &dumpfile($ARGV[1], "READBACK");
82 print TRACE "COUNT: $cntout\n";
84 exit( ($cntout>0) ? 1 : 0);
86 #git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
87 # [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
88 # [--[no-]diff3] <current-file> <base-file> <other-file>
89 #The `merge.*.driver` variable's value is used to construct a command to run to merge ancestor's
90 # version (%O), current version (%A) and the other branches' version (%B). These three tokens are
91 # replaced with the names of temporary files that hold the contents of these versions when the
92 # command line is built. Additionally, %L will be replaced with the conflict marker size (see
95 # keep failing so we don't need to keep changing the setup while building this script
99 print TRACE "FILE $tag START\n";
100 print TRACE `hexdump -C $file`;
101 print TRACE "FILE END\n";
106 # $::count is a bit ugly XXX
107 local $::count = 0; # we need the number of conflicts for exit()
111 while($_ = shift @input){
112 if(m/^$mark_start /){
113 print TRACE "FOUND A CONFLICT\n";
116 while($_ = shift @input){
122 push(@out, &edit_conflict(@conflict));
127 print TRACE "RETURN count=$::count\n";
128 return($::count, @out);
134 print TRACE "EDIT START: " . scalar(@in)."\n";
136 foreach my $x (@in){ my $xx = $x; chomp($xx); print TRACE "-$xx-\n"; }
138 print TRACE "EDIT END INPUT\n";
140 # one-line change - use as base case to develop the code
141 # ours ARGV[1] top-of-diff
142 # theirs ARGV[2] bottom-of-diff
145 # [1] $$PREFIX-Date: 1 ...
147 # [3] $$PREFIX-Date: 3 ...
149 if(scalar(@in) == 5 && $in[2] =~ m/^$mark_middle/){
150 my $back = &merge_one_line_maybe($in[1],$in[3]); # (ours, theirs)
152 $::count++; # leave the conflict
160 # Start at the top of both sections and work downwards. As long as the lines can be merged,
161 # push them out and keep going. If there are lines left, we will still have a conflict but
162 # we can try to make it smaller. Push out the start-conflict marker. Start at the
163 # bottom of both section and work upwards. As long as the lines can be merged, reverse push out
164 # the merged line and keep going. (We know there will be lines left at some point.) Push out
165 # remaining (middle) lines from OURS. Push out mark_middle. Push out remaining middle lines
166 # from THEIRS. Push out end-conflict marker. $::count++; return (@a,$b,@c,$d,@e,$f,@g)
175 # not matched - return the unchanged conflict
180 # XXX This is expensive. Add a quick check for "anything that looks like a subst var" and just
181 # declare the lines unmergeable if it fails.
182 sub merge_one_line_maybe {
183 my($ours, $theirs) = @_;
193 my($ourstype, $theirtype);
194 my($oursvar, $theirvar);
195 my($oursval, $theirval);
198 ($ourstype, $theirtype) = (0,0);
199 ($oursvar, $theirvar) = (undef, undef);
200 ($oursvar, $theirvar) = (undef, undef);
202 if($ours =~ m/\G\$$PREFIX-([A-Z][a-z]+)\$/gc){
206 if($theirs =~ m/\G\$$PREFIX-([A-Z][a-z]+)\$/gc){
212 if($ours =~ m/\G\$$PREFIX-([A-Za-z]+):\s+(.*?)\s\$/gc){
219 if($theirs =~ m/\G\$$PREFIX-([A-Za-z]+):\s+(.*?)\s\$/gc){
227 if($ours =~ m/\G(\$?[^\x24]*)/gc){
233 if($theirs =~ m/\G(\$?[^\x24]*)/gc){
238 print TRACE "MID: $ourstype/$oursval $theirtype/$theirval\n";
240 if(pos($ours)==length $ours && pos($theirs) == length $theirs){
243 if($ourstype == 0 && $theirtype == 0){
244 die "NHsubst MERGE FAILED - aborted infinite loop\n";
247 # now see if ours and their match or can be resolved
249 if($ourstype == 3 && $theirtype == 3){
250 #mismatch is \s vs \s\s - where is this coming from?
251 # HACK - hopefully temporary
252 if($oursval =~ m/^\s+$/ && $theirval =~ m/^\s+$/){
256 if($oursval eq $theirval){
262 if($ourstype == 3 || $theirtype == 3){
265 # XXX we could do better: on failure of one field, return 2 lines with the fields we _can_ fix
266 # substituted into those lines, leaving only the fail-to-match bits for the user to
269 if($oursvar ne $theirvar){
272 my $m = merge_one_var_maybe($oursvar, $oursval, $theirval);
281 # return undef if we can't merge the values; $NAME: VALUE $ or $NAME$ (as appropriate) if we can.
282 sub merge_one_var_maybe {
283 my($varname, $oursval, $theirval) = @_;
284 print TRACE "MVM: -$varname-$oursval-$theirval-\n";
288 my $fn = "PREFIX::$varname";
290 $resolvedas = &$fn($PREFIX,$varname,$oursval, $theirval);
292 $resolvedas = undef; # can't resolve
296 if(!defined $resolvedas){
297 $::count++; # we have an externally visible conflict
306 # Resolve the conflict of a single var's 2 values. Return undef to leave the conflict.
308 my($PREFIX, $varname, $mine, $theirs) = @_;
309 my $m = ($mine =~ m/(\d+)/)[0];
310 my $t = ($theirs =~ m/(\d+)/)[0];
311 return undef unless ($m>0) && ($t>0);
313 return "\$$PREFIX-$varname: " . (($m>$t)?$mine:$theirs) .' $';
320 my($PREFIX, $varname, $mine, $theirs) = @_;
321 $mine =~ s/^\s+//; $mine =~ s/\s+$//;
322 $theirs =~ s/^\s+//; $theirs =~ s/\s+$//;
323 return "\$$PREFIX-$varname: $mine \$" if(length $mine);
324 return "\$$PREFIX-$varname: $theirs \$" if(length $theirs);
325 return "\$$PREFIX-$varname\$" if(length $theirs);
329 my($PREFIX, $varname, $mine, $theirs) = @_;
330 my($m) = ($mine =~ m/1.(\d+)/);
331 my($t) = ($theirs =~ m/1.(\d+)/);
332 if($m > 0 && $t > 0){
333 my $q = ($m > $t) ? $m : $t;
334 return "\$$PREFIX-$varname: 1.$q \$";
337 return "\$$PREFIX-$varname: 1.$m \$";
340 return "\$$PREFIX-$varname: 1.$t \$";
342 return "\$$PREFIX-$varname\$";
381 $TEST-Date: 3 $ yes$TEST-Date: 4 $
383 $TEST-Date: 1 $ yes$TEST-Date: 5 $
390 $TEST-Branch: theirs $
395 /* NetHack 3.6 objnam.c $TEST-Date$ $TEST-Branch$:$TEST-Revision$ */
397 /* NetHack 3.6 objnam.c $TEST-Date: 1426977394 2015/03/21 22:36:34 $ $TEST-Branch: master $:$TEST-Revision: 1.108 $ */