3 # ====================================================================
5 # incremental-update.py
7 # This script performs updates of a single working copy tree piece by
8 # piece, starting with deep subdirectores, and working its way up
9 # toward the root of the working copy. Why? Because for working
10 # copies that have significantly mixed revisions, the size and
11 # complexity of the report that Subversion has to transmit to the
12 # server can be prohibitive, even triggering server-configured limits
13 # for such things. But doing an incremental update, you lessen the
14 # chance of hitting such a limit.
16 # ====================================================================
17 # Copyright (c) 2007 CollabNet. All rights reserved.
19 # This software is licensed as described in the file COPYING, which
20 # you should have received as part of this distribution. The terms
21 # are also available at http://subversion.tigris.org/license-1.html.
22 # If newer versions of this license are posted there, you may use a
23 # newer version instead, at your option.
25 # This software consists of voluntary contributions made by many
26 # individuals. For exact contribution history, see the revision
27 # history and logs, available at http://subversion.tigris.org/.
28 # ====================================================================
30 # --------------------------------------------------------------------
31 # Configuration (oooh... so complex...)
37 # --------------------------------------------------------------------
45 sys
.stderr
.write("ERROR: %s\n\n" % (err
))
47 def usage_and_exit(err
=None):
53 stream
.write("""Usage: %s [OPTIONS] WC-DIR
55 Update WC-DIR in an incremental fashion, starting will smaller
56 subtrees of it, and working up toward WC-DIR itself. SVN_UP_ARGS are
57 command-line parameters passed straight through to the Subversion
58 command-line client (svn) as parameters to its update command.
60 WARNING: Speed of operation is explicitly *NOT* of interest to this
61 script. Use it only when a typical 'svn update' isn't working for you
62 due to the complexity of your working copy's mixed-revision state.
66 --username USER Specify the username used to connect to the repository
67 --password PASS Specify the PASSWORD used to connect to the repository
69 """ % (os
.path
.basename(sys
.argv
[0])))
70 sys
.exit(err
and 1 or 0)
73 def get_head_revision(path
, args
):
74 """Return the current HEAD revision for the repository associated
75 with PATH. ARGS are extra arguments to provide to the svn
78 lines
= os
.popen('%s status --show-updates --non-recursive %s %s'
79 % (SVN_BINARY
, args
, path
)).readlines()
80 if lines
and lines
[-1].startswith('Status against revision:'):
81 return int(lines
[-1][24:].strip())
82 raise Exception, "Unable to fetch HEAD revision number."
85 def compare_paths(path1
, path2
):
86 """This is a sort() helper function for two paths."""
88 path1_len
= len (path1
);
89 path2_len
= len (path2
);
90 min_len
= min(path1_len
, path2_len
)
93 # Are the paths exactly the same?
97 # Skip past common prefix
98 while (i
< min_len
) and (path1
[i
] == path2
[i
]):
101 # Children of paths are greater than their parents, but less than
102 # greater siblings of their parents
110 if (char1
== '/') and (i
== path2_len
):
112 if (char2
== '/') and (i
== path1_len
):
114 if (i
< path1_len
) and (char1
== '/'):
116 if (i
< path2_len
) and (char2
== '/'):
119 # Common prefix was skipped above, next character is compared to
121 return cmp(char1
, char2
)
124 def harvest_dirs(path
):
125 """Return a list of versioned directories under working copy
126 directory PATH, inclusive."""
128 # 'svn status' output line matcher, taken from the Subversion test suite
129 rm
= re
.compile('^([!MACDRUG_ ][MACDRUG_ ])([L ])([+ ])([S ])([KOBT ]) ' \
130 '([* ]) [^0-9-]*(\d+|-|\?) +(\d|-|\?)+ +(\S+) +(.+)')
132 fp
= os
.popen('%s status --verbose %s' % (SVN_BINARY
, path
))
138 if line
.startswith('Performing'):
140 match
= rm
.search(line
)
142 stpath
= match
.group(10)
144 if os
.path
.isdir(stpath
):
154 usage_and_exit("No working copy directory specified")
155 if '--help' in sys
.argv
:
158 args
= ' '.join(sys
.argv
[1:-1] + ['--non-interactive'])
159 print "Fetch HEAD revision...",
160 head_revision
= get_head_revision(path
, args
)
162 print "Updating to revision %d" % (head_revision
)
163 print "Harvesting the list of subdirectories...",
164 dirs
= harvest_dirs(path
)
166 dirs
.sort(compare_paths
)
168 print "Update the tree, one subdirectory at a time. This could take " \
171 width
= len(str(num_dirs
))
172 format_string
= '[%%%dd/%%%dd] Updating %%s' % (width
, width
)
175 current
= current
+ 1
176 print format_string
% (current
, num_dirs
, dir)
177 os
.system('%s update --quiet --revision %d %s %s'
178 % (SVN_BINARY
, head_revision
, args
, dir))
181 if __name__
== "__main__":