3 # ====================================================================
7 # Put this in your path somewhere and make sure it has exec perms.
8 # Do your thing w/subversion but when you would use 'svn update'
9 # call this script instead.
11 # ====================================================================
12 # Copyright (c) 2000-2004 CollabNet. All rights reserved.
14 # This software is licensed as described in the file COPYING, which
15 # you should have received as part of this distribution. The terms
16 # are also available at http://subversion.tigris.org/license-1.html.
17 # If newer versions of this license are posted there, you may use a
18 # newer version instead, at your option.
20 # This software consists of voluntary contributions made by many
21 # individuals. For exact contribution history, see the revision
22 # history and logs, available at http://subversion.tigris.org/.
23 # ====================================================================
26 # WHY THE NEED FOR THIS SCRIPT?
28 # Currently, the subversion server will attempt to stream all file
29 # data to the client at once for _each_ merge candidate. For cases
30 # that have >1 file and/or the complexity of the merge for any
31 # given file(s) that would require >n minutes, where n is the
32 # server's magic timeout (5 min.??), the server will timeout. This
33 # leaves the client/user in an unswell state. See issue #2048 for
34 # details http://subversion.tigris.org/issues/show_bug.cgi?id=2048.
36 # One solution is to wrap the 'svn update' command in a script that
37 # will perform the update one file at a time. The problem with
38 # this solution is that it defeats the beneficial atomic nature of
39 # subversion for this type of action. If commits are still coming
40 # in to the repository, the value of "HEAD" might change between each
41 # of these update operations.
43 # Another solution, the one that this script utilizes, passes the
44 # --diff3-cmd directive to 'svn update' using a command which forces
45 # a failed contextual merge ('/bin/false', for example). These faux
46 # merge failures cause subversion to leave all of the accounting files
47 # involved in a merge behind and puts them into the 'conflict'
48 # state. Since all the data required for all the merges took place
49 # at that exact moment atomicity is preserved and life is swell.
51 #######################################################################
53 # This is required for copy() command. I believe it's a standard
54 # module. If there's a doubt run this from a shell:
55 # perl -e 'use File::Copy;'
56 # If you don't get any complaint from perl you're good. Otherwise
57 # comment this line out and change the $backup_mine_files to 0.
60 # This forces backing up of the .mine files for reference even
61 # after the resolved command. The backups will be stored as
65 # Choose your favorite graphical diff app. If it's not here, just add
66 # the full path to it and the style of the options to the
67 # %DIFF3CMD_hash below.
68 $DIFF3CMD="xcleardiff";
70 # Override the diff3-cmd in the config file here.
71 # If this is an empty string it'll use the config file's
74 $d3cmdoverride="/bin/false";
76 # Add more diff programs here.
77 # For the internal, discovered, file parameters:
81 # +D+ ==> Destination (The output of your merged code would go here.
82 # This would, generally, be whatever
83 # $(basename <+A+> .mine) would evaluate to.
84 # But you can feel free to do something like these:
85 # "+B+ +C+ +A+ -out +D+.bob"
86 # "+B+ +A+ +C+ -out /tmp/bob"
87 # Just note that the '+' (plus) are to limit the false positives in the
88 # search and replace sub.
90 # HAVING THE CORRECT PATH, AND ARGS FOR YOUR DIFF PROGRAM, IS CRITICAL!!
91 %DIFF3CMD_hash=(# Ahh...hallowed be thy name.
92 "/opt/atria/bin/xcleardiff" => "+A+ +B+ +C+ -out +D+",
94 "/usr/bin/kdiff3" => "+A+ +B+ +C+ -o +D+",
95 # This one's slow and it sucks!(BUGGY)
96 "/usr/bin/xxdiff" => "-M +D+ +A+ +B+ +C+",
97 # This one's even worse (no output filename).
98 "/usr/bin/meld" => "+A+ +B+ +C+",
108 open (FH
,$CMD) || die("Can't '$CMD': $!\n");
124 my $A=$args[0]; # mine
125 my $B=$args[1]; # older
126 my $C=$args[2]; # latest
127 my $D=$args[3]; # output of merge (Destination)
130 # What's is the diff of choice?
131 if( $CHOSENDIFF eq "" )
137 # $CHOSENDIFF has data. We deal with the args.
138 ($diffcmd,$diff_format)=(split /:/,$CHOSENDIFF);
141 $diff_format=~s/\+A\+/$A/g;
142 $diff_format=~s/\+B\+/$B/g;
143 $diff_format=~s/\+C\+/$C/g;
144 $diff_format=~s/\+D\+/$D/g;
146 @rdat=@
{exec_cmd
("$diffcmd $diff_format 2>/dev/null |")};
154 foreach $diff_app (sort(keys(%DIFF3CMD_hash)))
156 if( ${diff_app
}=~m/${DIFF3CMD}/o )
158 $CHOSENDIFF="$diff_app:$DIFF3CMD_hash{$diff_app}";
161 if( $CHOSENDIFF eq "" )
163 # Big problem. Some kind of disconnect w/the choice and the hash.
164 # Most likely a typo.
165 print "It would seem that the '${DIFF3CMD}' was not found in\n";
166 print "the hash of diff applications I know about. Please\n";
167 print "investigate and correct.\n";
179 # Check to see if the d3cmdoverride is set so
180 # we don't fail here if it wasn't.
181 if( ${d3cmdoverride
} eq "" )
183 $d3cmdoverride_final="";
187 $d3cmdoverride_final="--diff3-cmd=${d3cmdoverride}";
190 @data=@
{exec_cmd
("svn update ${d3cmdoverride_final} | grep ^C | awk '{print \$2}' |")};
191 for($j=0;$j<(scalar(@data));$j++)
193 push( @file_array, exec_cmd
("svn info $data[$j] |") );
209 my $sbox_repo_changed;
210 my $sbox_repo_latest;
217 foreach $file_ref (@file_array)
220 for($i=0; $i < (scalar(@file)-1);$i++)
222 if( $file[$i]=~m/Name:/o )
224 # Key off of "Name:" and then back up one to get "Path:".
225 # This way the calculations will be correct when chopping off
226 # the name portion on the path bit.
227 $fname=(split /Name: /,$file[$i])[1];
228 $fpath_tmp=(split /Path: /,$file[$i-1])[1];
229 $fpath=(substr($fpath_tmp,0,(length($fpath_tmp)-(length($fname)+1))));
231 elsif( $file[$i]=~m/Conflict Previous Base File:/o )
233 $sbox_repo_base=(split /Conflict Previous Base File: /,$file[$i])[1];
235 elsif( $file[$i]=~m/Conflict Previous Working File:/o )
237 $sbox_repo_changed=(split /Conflict Previous Working File: /,
240 elsif( $file[$i]=~m/Conflict Current Base File:/o )
242 $sbox_repo_latest=(split /Conflict Current Base File: /,$file[$i])[1];
246 # print "\n----------------------------------------------\n";
247 # print " \$fpath: '$fpath'\n";
248 # print " \$fname: '$fname'\n";
249 # print "[A](mine) \$sbox_repo_changed: '$sbox_repo_changed'\n";
250 # print "[B](older) \$sbox_repo_base: '$sbox_repo_base'\n";
251 # print "[C](latest)\$sbox_repo_latest: '$sbox_repo_latest'\n";
252 # print "\n----------------------------------------------\n";
254 # Send them in standard, ABCD, order.
255 @rdat=diff_it
("${fpath}/${sbox_repo_changed}",
256 "${fpath}/${sbox_repo_base}",
257 "${fpath}/${sbox_repo_latest}",
258 "${fpath}/${fname}");
260 # Print out any return data. Shouldn't be much of anything due to
261 # the redirection to /dev/null in the invocation of whatever diff
263 print "\nOutput from diff3 command.\n";
264 print "-----------------------\n";
265 foreach $retline (@rdat)
269 print "---------END-----------\n";
271 # Add the files we may wish to remove to an array. Keep *.mine
272 # files in case something bad happened.
274 "${fpath}/${sbox_repo_base}",
275 "${fpath}/${sbox_repo_latest}",
276 "${fpath}/${fname}.orig");
278 # Make copies of *.mine for ctya purposes.
279 if ( ${backup_mine_files
} > 0 )
281 copy
("${fpath}/${sbox_repo_changed}",
282 "${fpath}/${fname}.${ENV{USERNAME}}");
285 # Return an array that contains all the files we might wish to
287 push(@commit_array,"${fpath}/${fname}");
290 return \
@cleanup_array, \
@commit_array;
299 foreach $file (@
{$args[0]})
304 # Need to tell subversion we have resolved the conflicts.
305 @rdat=@
{exec_cmd
("svn -R resolved . |")};
306 print "\nOutput from subversion 'resolved' command.\n";
307 print "-----------------------\n";
308 foreach $line (@rdat)
312 print "---------END-----------\n";
323 ($cleanup_aref,$commit_aref)=parse_it
(svn_update_info
());
324 clean_up
($cleanup_aref);
326 print "\nDon't forget to commit these files:\n";
327 print "-----------------------\n";
328 foreach $file (@
{$commit_aref})
332 print "---------END-----------\n";
339 # Get the ball rolling.