1 | #!/usr/bin/env python |
---|
2 | |
---|
3 | """cmt_svn_checkout.py: Checkout CMT package(s) from Subversion repository. |
---|
4 | |
---|
5 | Usage: cmt_svn_checkout.py [OPTION]... PATH... |
---|
6 | Checkout PATH(s) relative to or being Subversion repository URL(s). |
---|
7 | |
---|
8 | Mandatory arguments to long options are mandatory for short options too. |
---|
9 | -r, --version-tag=REV checkout version tag REV of PATH |
---|
10 | --version-dir=DIR use DIR as version directory instead of version tag |
---|
11 | -d, --directory=DIR checkout into DIR instead of (basename of) PATH |
---|
12 | -o, --offset=OFFSET checkout PATH at OFFSET relative to repository URL |
---|
13 | --no_config disable config step upon checkout |
---|
14 | --without_version_directory do not create version directory upon PATH checkout |
---|
15 | --with_version_directory create version directory upon PATH checkout (default) |
---|
16 | --url=URL checkout PATH from repository URL |
---|
17 | --debug print lots of debugging information |
---|
18 | -h, --help display this help and exit |
---|
19 | --version output version information and exit |
---|
20 | |
---|
21 | The SVNROOT, SVNTRUNK, SVNTAGS, and SVNBRANCHES (also SVNDEVBRANCHES) environment variables specify repository URL of PATH, location of PATH trunk, tags, and branches (also devbranches) (relatively to PATH) respectively. |
---|
22 | |
---|
23 | Report bugs to <CMT-L@IN2P3.FR>. |
---|
24 | """ |
---|
25 | |
---|
26 | __version__ = '0.10.0' |
---|
27 | __date__ = 'Fri May 27 2016' |
---|
28 | __author__ = 'Grigory Rybkin' |
---|
29 | |
---|
30 | import sys |
---|
31 | import getopt |
---|
32 | import os |
---|
33 | import posixpath |
---|
34 | import os.path |
---|
35 | import urlparse |
---|
36 | # for Python 2.3 and older |
---|
37 | if sys.version_info[0] == 2 and sys.version_info[1] < 4: |
---|
38 | for p in ('svn', 'svn+ssh'): |
---|
39 | if p not in urlparse.uses_netloc: |
---|
40 | urlparse.uses_netloc.append(p) |
---|
41 | import tempfile |
---|
42 | import re |
---|
43 | |
---|
44 | import logging |
---|
45 | |
---|
46 | self = 'cmt_svn_checkout.py' |
---|
47 | # try: |
---|
48 | # from svn import client, core |
---|
49 | # except ImportError, e: |
---|
50 | # print >>sys.stderr, '%s: cannot import Subversion Python bindings: %s' \ |
---|
51 | # % (self, str(e)) |
---|
52 | # sys.exit(1) |
---|
53 | os.environ['LC_ALL'] = 'C' |
---|
54 | |
---|
55 | class Utils(object): |
---|
56 | def getstatusoutput(cmd): |
---|
57 | """Return (status, stdout + stderr) of executing cmd in a shell. |
---|
58 | |
---|
59 | A trailing line separator is removed from the output string. The exit status of the command is encoded in the format specified for wait(), when the exit status is zero (termination without errors), 0 is returned. |
---|
60 | """ |
---|
61 | p = os.popen('( %s ) 2>&1' % cmd, 'r') |
---|
62 | out = p.read() |
---|
63 | sts = p.close() |
---|
64 | if sts is None: sts = 0 |
---|
65 | if out.endswith(os.linesep): |
---|
66 | out = out[:out.rindex(os.linesep)] |
---|
67 | elif out[-1:] == '\n': out = out[:-1] |
---|
68 | return sts, out |
---|
69 | getstatusoutput = staticmethod(getstatusoutput) |
---|
70 | |
---|
71 | def getstatuserror(cmd): |
---|
72 | """Return (status, stderr) of executing cmd in a shell. |
---|
73 | |
---|
74 | On Unix, the return value is the exit status of the command is encoded in the format specified for wait(). On Windows, on command.com systems (Windows 95, 98 and ME) this is always 0; on cmd.exe systems (Windows NT, 2000 and XP) this is the exit status of the command run. |
---|
75 | """ |
---|
76 | fd, p = tempfile.mkstemp() |
---|
77 | os.close(fd) |
---|
78 | # print >> sys.stderr, 'Created file %s with fd %i' % (p, fd) |
---|
79 | # p = os.tempnam() |
---|
80 | # print >> sys.stderr, 'Created file name %s' % (p) |
---|
81 | sc = os.system('( %s ) 2>%s' % (cmd, p)) |
---|
82 | f = open(p) |
---|
83 | e = f.read() |
---|
84 | f.close() |
---|
85 | os.unlink(p) |
---|
86 | return sc, e |
---|
87 | getstatuserror = staticmethod(getstatuserror) |
---|
88 | |
---|
89 | class ClientContext(object): |
---|
90 | |
---|
91 | schemes = ('http', 'https', 'svn', 'svn+ssh', 'file') |
---|
92 | |
---|
93 | def svn_path_canonicalize(self, path): |
---|
94 | """Return a new path (or URL) like path, but transformed such that some types of path specification redundancies are removed. |
---|
95 | |
---|
96 | This involves collapsing redundant "/./" elements, removing multiple adjacent separator characters, removing trailing separator characters, and possibly other semantically inoperative transformations. Convert the scheme and hostname to lowercase.""" |
---|
97 | scheme, netloc, path, query, fragment = urlparse.urlsplit(path) |
---|
98 | scheme = scheme.lower() |
---|
99 | netloc = netloc.lower() |
---|
100 | if path.startswith('/'): b = '/' |
---|
101 | else: b = '' |
---|
102 | path = b + '/'.join([s for s in path.split('/') if s and s != '.']) |
---|
103 | return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) |
---|
104 | |
---|
105 | def urljoin(self, *args): |
---|
106 | urls = [urlparse.urlsplit(arg) for arg in args] |
---|
107 | if not urls: return '' |
---|
108 | |
---|
109 | schemes = [url[0] for url in urls] |
---|
110 | schemes.reverse() |
---|
111 | for i, s in enumerate(schemes): |
---|
112 | if s and s in self.schemes: |
---|
113 | scheme = s |
---|
114 | index = i |
---|
115 | break |
---|
116 | else: |
---|
117 | scheme = '' |
---|
118 | index = len(urls) - 1 |
---|
119 | |
---|
120 | netlocs = [url[1] for url in urls] |
---|
121 | netlocs.reverse() |
---|
122 | for i, s in enumerate(netlocs[:index + 1]): |
---|
123 | if s: |
---|
124 | netloc = s |
---|
125 | index = i |
---|
126 | break |
---|
127 | else: |
---|
128 | netloc = '' |
---|
129 | |
---|
130 | path = posixpath.join(*[url[2] for url in urls][len(urls) - 1 - index:]) |
---|
131 | |
---|
132 | query = fragment = '' |
---|
133 | return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) |
---|
134 | |
---|
135 | # scheme = self.last_nonempty([url[0] for url in urls if url[0] in self.schemes]) |
---|
136 | # netloc = self.last_nonempty([url[1] for url in urls]) |
---|
137 | # path = posixpath.join(*[url[2] for url in urls]) |
---|
138 | # query = self.last_nonempty([url[3] for url in urls]) |
---|
139 | # fragment = self.last_nonempty([url[4] for url in urls]) |
---|
140 | |
---|
141 | # return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) |
---|
142 | |
---|
143 | def last_nonempty(self, list, default = ''): |
---|
144 | list.reverse() |
---|
145 | for i in list: |
---|
146 | if i: return i |
---|
147 | else: |
---|
148 | return default |
---|
149 | |
---|
150 | # def __init__(self): |
---|
151 | # core.svn_config_ensure(None) |
---|
152 | # self.ctx = client.create_context() |
---|
153 | # self.providers = [ |
---|
154 | # client.get_simple_provider(), |
---|
155 | # client.get_username_provider(), |
---|
156 | # client.get_ssl_server_trust_file_provider(), |
---|
157 | # client.get_ssl_client_cert_file_provider(), |
---|
158 | # client.get_ssl_client_cert_pw_file_provider() |
---|
159 | # ] |
---|
160 | # self.ctx.auth_baton = core.svn_auth_open(self.providers) |
---|
161 | # self.ctx.config = core.svn_config_get_config(None) |
---|
162 | |
---|
163 | # def __call__(self): |
---|
164 | # return self.ctx |
---|
165 | |
---|
166 | def cwd(): |
---|
167 | _gcwd = os.getcwd() |
---|
168 | try: |
---|
169 | _cwd = os.environ['PWD'] |
---|
170 | if _cwd and os.path.samefile(_cwd, _gcwd): |
---|
171 | return _cwd |
---|
172 | else: |
---|
173 | return _gcwd |
---|
174 | except (KeyError, AttributeError): |
---|
175 | return _gcwd |
---|
176 | |
---|
177 | def cd(path): |
---|
178 | new = os.path.normpath(os.path.join(cwd(), path)) |
---|
179 | os.chdir(path) |
---|
180 | _gcwd = os.getcwd() |
---|
181 | try: |
---|
182 | if os.path.samefile(new, _gcwd): |
---|
183 | os.environ['PWD'] = new |
---|
184 | else: |
---|
185 | os.environ['PWD'] = _gcwd |
---|
186 | except AttributeError: |
---|
187 | pass |
---|
188 | |
---|
189 | def error(instance, location='', file = sys.stderr): |
---|
190 | try: |
---|
191 | message = ': '.join([str(arg) for arg in instance.args]) |
---|
192 | except AttributeError: |
---|
193 | message = str(instance).rstrip() |
---|
194 | if location: location += ': ' |
---|
195 | print >> file, "%s%s" % (location, message) |
---|
196 | |
---|
197 | class CmtContext(object): |
---|
198 | def __init__(self, |
---|
199 | config = True, |
---|
200 | with_version_directory = True, |
---|
201 | cleanup = False, |
---|
202 | head_version = None): |
---|
203 | self.config = config |
---|
204 | self.with_version_directory = with_version_directory |
---|
205 | self.cleanup = cleanup |
---|
206 | if head_version: |
---|
207 | self.set_head_version(head_version) |
---|
208 | else: |
---|
209 | self.set_head_version(os.getenv('CMTHEADVERSION', '')) |
---|
210 | |
---|
211 | def set_head_version(self, version): |
---|
212 | if not version: |
---|
213 | self.head_version = '' |
---|
214 | self.head_version_tag = False |
---|
215 | elif version.startswith('!'): |
---|
216 | # strip the leading '!' |
---|
217 | # require synchronization with C++ code |
---|
218 | self.head_version = version[1:] |
---|
219 | self.head_version_tag = False |
---|
220 | else: |
---|
221 | self.head_version = version |
---|
222 | self.head_version_tag = True |
---|
223 | |
---|
224 | def eval_head_version(self, m): |
---|
225 | return str(self.head_version).replace('<package>', '%(package)s').replace('<PACKAGE>', '%(PACKAGE)s').replace('<revision>', '%(revision)i') % \ |
---|
226 | {'package' : m.package, |
---|
227 | 'PACKAGE' : m.package.upper(), |
---|
228 | 'revision' : m.info.last_changed_rev} \ |
---|
229 | or 'HEAD' |
---|
230 | |
---|
231 | def write(self, p, version): |
---|
232 | #print >> sys.stderr, 'write:', p, version |
---|
233 | try: |
---|
234 | t = version + '\n' |
---|
235 | if os.path.exists(p): |
---|
236 | f = open(p, 'r+') |
---|
237 | b = f.tell() |
---|
238 | v = f.read() |
---|
239 | if v != t: |
---|
240 | f.seek(b) |
---|
241 | f.write(t) |
---|
242 | f.truncate() |
---|
243 | else: |
---|
244 | f = open(p, 'w') |
---|
245 | f.write(t) |
---|
246 | f.close() |
---|
247 | except IOError, e: |
---|
248 | print >> sys.stderr, e |
---|
249 | return 1 |
---|
250 | return 0 |
---|
251 | |
---|
252 | def generate(self, p): |
---|
253 | #print >> sys.stderr, 'generate:', p |
---|
254 | curdir = cwd() |
---|
255 | # cmd = 'cmt -disable_warnings' |
---|
256 | cmd = 'cmt -q' |
---|
257 | if self.with_version_directory: |
---|
258 | cmd += ' -with_version_directory' |
---|
259 | else: |
---|
260 | cmd += ' -without_version_directory' |
---|
261 | if self.cleanup: |
---|
262 | cmd += ' -cleanup' |
---|
263 | else: |
---|
264 | cmd += ' -no_cleanup' |
---|
265 | cmd += ' config' |
---|
266 | cd(p) |
---|
267 | sc = os.system(cmd) |
---|
268 | if sc != 0: sc = 1 |
---|
269 | cd(curdir) |
---|
270 | # return sc |
---|
271 | return 0 |
---|
272 | |
---|
273 | def configure(self, path, version): |
---|
274 | sc = 0 |
---|
275 | for d in ('cmt', 'mgr'): |
---|
276 | p = os.path.join(path, d) |
---|
277 | if os.path.isdir(p): |
---|
278 | if not self.with_version_directory: |
---|
279 | sc += self.write(os.path.join(p,'version.cmt'), version) |
---|
280 | elif os.path.exists(os.path.join(p,'version.cmt')): |
---|
281 | print >>sys.stderr, 'Warning: file %s normally should not be under version control. Please, consider removing' % os.path.join(d,'version.cmt') |
---|
282 | try: |
---|
283 | os.rename(os.path.join(p,'version.cmt'), |
---|
284 | os.path.join(p,'version.cmt.orig')) |
---|
285 | print >>sys.stderr, 'renamed %s -> %s' % (`os.path.join(p,'version.cmt')`, `os.path.join(p,'version.cmt.orig')`) |
---|
286 | except (IOError, os.error), e: |
---|
287 | error(e) |
---|
288 | |
---|
289 | if self.config: |
---|
290 | sc += self.generate(p) |
---|
291 | return sc |
---|
292 | return sc |
---|
293 | #print >> sys.stderr, 'Cannot configure %s ' % (path, version) |
---|
294 | |
---|
295 | class Module(object): |
---|
296 | def __init__(self, module): |
---|
297 | self.module = module |
---|
298 | self.init = False |
---|
299 | class info(object): |
---|
300 | last_changed_rev = 0 |
---|
301 | self.info = info |
---|
302 | # print >>sys.stderr, 'init module %s: last_changed_rev %i' % \ |
---|
303 | # (self.module, self.info.last_changed_rev) |
---|
304 | |
---|
305 | class Checkout(object): |
---|
306 | def __init__(self, |
---|
307 | url = None, |
---|
308 | trunk = None, |
---|
309 | tags = None, |
---|
310 | branches = None, |
---|
311 | devbranches = None, |
---|
312 | version = None, |
---|
313 | version_dir = None, |
---|
314 | directory = None, |
---|
315 | offset = '', |
---|
316 | modules = []): |
---|
317 | self.url = url |
---|
318 | self.trunk = trunk |
---|
319 | self.tags = tags |
---|
320 | self.branches = branches |
---|
321 | self.devbranches = devbranches |
---|
322 | self.version = version |
---|
323 | self.version_dir = version_dir |
---|
324 | self.directory = directory |
---|
325 | self.offset = offset |
---|
326 | self.modules = modules |
---|
327 | self.reposLayout() |
---|
328 | |
---|
329 | def reposLayout(self): |
---|
330 | if self.url is None: |
---|
331 | self.url = os.getenv('SVNROOT', '') |
---|
332 | # try: |
---|
333 | # self.url = os.environ['SVNROOT'] |
---|
334 | # except KeyError: |
---|
335 | # pass |
---|
336 | if self.trunk is None: |
---|
337 | self.trunk = os.getenv('SVNTRUNK', 'trunk') |
---|
338 | if self.tags is None: |
---|
339 | self.tags = os.getenv('SVNTAGS', 'tags') |
---|
340 | if self.branches is None: |
---|
341 | self.branches = os.getenv('SVNBRANCHES', 'branches') |
---|
342 | if self.devbranches is None: |
---|
343 | self.devbranches = os.getenv('SVNDEVBRANCHES', 'devbranches') |
---|
344 | |
---|
345 | def cmtRepos(self): |
---|
346 | self.url = 'https://svn.lal.in2p3.fr/projects/CMT' |
---|
347 | self.trunk = 'HEAD' |
---|
348 | self.tags= '.' |
---|
349 | self.branches = '.' |
---|
350 | self.devbranches = '.' |
---|
351 | |
---|
352 | def add(self, module): |
---|
353 | self.modules.append(module) |
---|
354 | |
---|
355 | def info_receiver(self, path, info, pool): |
---|
356 | self.path = path |
---|
357 | self.info = info |
---|
358 | pool.info = info |
---|
359 | |
---|
360 | def cmp(self, path1, path2, client_context): |
---|
361 | cmd = 'svn diff %s %s' % (path1, path2) |
---|
362 | # cmd = 'svn diff --summarize %s %s' % (path1, path2) |
---|
363 | sc, out = Utils.getstatusoutput(cmd) |
---|
364 | if sc != 0: |
---|
365 | return 2 |
---|
366 | if out: return 1 |
---|
367 | else: return 0 |
---|
368 | |
---|
369 | # outfile = tempfile.TemporaryFile('wb') |
---|
370 | # errfile = tempfile.TemporaryFile('wb') |
---|
371 | # outb = outfile.tell() |
---|
372 | # errb = errfile.tell() |
---|
373 | # client.diff([] |
---|
374 | # , path1 |
---|
375 | # , self.head_revision |
---|
376 | # , path2 |
---|
377 | # , self.head_revision |
---|
378 | # , True, True, False |
---|
379 | # , outfile |
---|
380 | # , errfile |
---|
381 | # , client_context()) |
---|
382 | |
---|
383 | # # position at the end of the file |
---|
384 | # outfile.seek(0, 2) |
---|
385 | # errfile.seek(0, 2) |
---|
386 | # oute = outfile.tell() |
---|
387 | # erre = errfile.tell() |
---|
388 | |
---|
389 | # if erre > errb: return 2 |
---|
390 | # elif oute > outb: return 1 |
---|
391 | # else: return 0 |
---|
392 | |
---|
393 | def trunk_tag(self, module, client_context): |
---|
394 | """Attempt to determine the tag of the module's trunk. |
---|
395 | |
---|
396 | Return the tag if its files are copied from the trunk and |
---|
397 | their last_changed_rev numbers are the same as those of the trunk files. |
---|
398 | """ |
---|
399 | trunk = posixpath.join(module.url, self.trunk) |
---|
400 | # trunk = posixpath.normpath(posixpath.join(module.url, self.trunk)) |
---|
401 | cmd = 'svn ls -vR %s' % trunk |
---|
402 | sc, out = Utils.getstatusoutput(cmd) |
---|
403 | logger.debug('%s\n%s' % (cmd, out)) |
---|
404 | if sc != 0: |
---|
405 | return None |
---|
406 | trunk_dirent = [line.split() for line in out.splitlines()] |
---|
407 | trunk_revs = dict([(line[-1], int(line[0])) for line in trunk_dirent if not line[-1].endswith(posixpath.sep)]) |
---|
408 | logger.debug('%s' % trunk_revs) |
---|
409 | |
---|
410 | curdir = posixpath.curdir + posixpath.sep |
---|
411 | for line in trunk_dirent: |
---|
412 | if line[-1] == curdir: |
---|
413 | class info(object): pass |
---|
414 | info.last_changed_rev = int(line[0]) |
---|
415 | self.info_receiver(trunk, info, module) |
---|
416 | logger.debug('last_changed_rev: %d' % info.last_changed_rev) |
---|
417 | break |
---|
418 | |
---|
419 | # cmd = 'svn info %s' % trunk |
---|
420 | # p = r'last\s+changed\s+rev:\s+(?P<rev>\d+)' |
---|
421 | # m = re.search(p, out, re.I) |
---|
422 | # if m: |
---|
423 | # class info(object): pass |
---|
424 | # info.last_changed_rev = int(m.group('rev')) |
---|
425 | # self.info_receiver(trunk, info, module) |
---|
426 | # # self.info_receiver(trunk, info, None) |
---|
427 | # # print >>sys.stderr, '%s: last_changed_rev %i' % \ |
---|
428 | # # (trunk, self.info.last_changed_rev) |
---|
429 | # # last_changed_rev = int(m.group('rev')) |
---|
430 | # else: |
---|
431 | # return None |
---|
432 | |
---|
433 | tags = posixpath.join(module.url, self.tags) |
---|
434 | # tags = posixpath.normpath(posixpath.join(module.url, self.tags)) |
---|
435 | cmd = 'svn ls -vR %s' % tags |
---|
436 | # cmd = 'svn ls -v %s' % tags |
---|
437 | sc, out = Utils.getstatusoutput(cmd) |
---|
438 | logger.debug('%s\n%s' % (cmd, out)) |
---|
439 | if sc != 0: |
---|
440 | return None |
---|
441 | tags_dirent = [line.split() for line in out.splitlines()] |
---|
442 | tags_revs = dict() |
---|
443 | for ent in tags_dirent: |
---|
444 | try: |
---|
445 | tag, path = ent[-1].split(posixpath.sep, 1) |
---|
446 | except ValueError: |
---|
447 | continue |
---|
448 | if tag not in tags_revs: |
---|
449 | tags_revs[tag] = dict() |
---|
450 | if path and not path.endswith(posixpath.sep): |
---|
451 | # assume there are no empty directories in the tag |
---|
452 | tags_revs[tag][path] = int(ent[0]) |
---|
453 | logger.debug('%s' % tags_revs) |
---|
454 | |
---|
455 | cmd = 'svn info %s' % trunk |
---|
456 | sc, out = Utils.getstatusoutput(cmd) |
---|
457 | logger.debug('%s\n%s' % (cmd, out)) |
---|
458 | if sc != 0: |
---|
459 | return None |
---|
460 | p = r'repository\s+root:\s+(?P<root>%s://\S+)' % \ |
---|
461 | ('(?:' + '|'.join(map(re.escape, client_context.schemes)) + ')') |
---|
462 | m = re.search(p, out, re.I) |
---|
463 | logger.debug('pattern: %r' % (p)) |
---|
464 | if m: |
---|
465 | root = m.group('root') |
---|
466 | else: |
---|
467 | return None |
---|
468 | logger.debug('root: %s' % root) |
---|
469 | trunk_path = trunk[len(root):] |
---|
470 | tags_path = tags[len(root):] |
---|
471 | logger.debug('trunk_path: %s' % trunk_path) |
---|
472 | logger.debug('tags_path: %s' % tags_path) |
---|
473 | offset = len(trunk) - len(root) + len(posixpath.sep) |
---|
474 | |
---|
475 | # Usually, a tag is created as a server-side copy. |
---|
476 | # Sometimes, a tag is created as a copy of WC (working copy) |
---|
477 | # after commit. |
---|
478 | # Below, we try to take into account the latter case. |
---|
479 | cmd = 'svn log -v -q %s' % tags |
---|
480 | sc, out = Utils.getstatusoutput(cmd) |
---|
481 | logger.debug('%s\n%s' % (cmd, out)) |
---|
482 | if sc != 0: |
---|
483 | return None |
---|
484 | p = re.compile( |
---|
485 | r'^-{5,}$\s^r\d+.+$\s^Changed paths:$\s^\s+A\s+%s%s(?P<tag>[^%s]+)\s+\(from %s:\d+\)$(?P<replaced>(?:\s^\s+(?:R|A|D)\s+%s%s(?P=tag)%s(?P<path>.+)(?:\s+\(from %s%s(?P=path):\d+\))?$)*)' % (re.escape(tags_path), posixpath.sep, posixpath.sep, re.escape(trunk_path), re.escape(tags_path), posixpath.sep, posixpath.sep, re.escape(trunk_path), posixpath.sep) |
---|
486 | , re.M |
---|
487 | ) |
---|
488 | tags_copied = list() |
---|
489 | tags_replaced_revs = dict() |
---|
490 | for m in p.finditer(out): |
---|
491 | logger.debug('tag: %s replaced: %r' % (m.group('tag'), m.group('replaced'))) |
---|
492 | tags_copied.append(m.group('tag')) |
---|
493 | for line in m.group('replaced').strip().splitlines(): |
---|
494 | l = line.split() |
---|
495 | if len(l) == 2: continue # action code D - deleted paths |
---|
496 | repl = l[3].rstrip(')') |
---|
497 | i = repl.rindex(':') |
---|
498 | path = repl[offset:i] |
---|
499 | rev = int(repl[i + 1:]) |
---|
500 | logger.debug('path: %s rev: %d' % (path, rev)) |
---|
501 | |
---|
502 | if m.group('tag') not in tags_replaced_revs: |
---|
503 | tags_replaced_revs[m.group('tag')] = dict() |
---|
504 | if path and not path.endswith(posixpath.sep): |
---|
505 | # assume there are no empty directories in the tag |
---|
506 | tags_replaced_revs[m.group('tag')][path] = rev |
---|
507 | |
---|
508 | logger.debug('copied: %s' % tags_copied) |
---|
509 | logger.debug('replaced: %s' % tags_replaced_revs) |
---|
510 | |
---|
511 | for t in tags_revs.keys(): |
---|
512 | if t not in tags_copied: |
---|
513 | del tags_revs[t] |
---|
514 | logger.debug('%s: Not a trunk copy' % t) |
---|
515 | |
---|
516 | for t in tags_replaced_revs: |
---|
517 | if t in tags_revs: |
---|
518 | tags_revs[t].update(tags_replaced_revs[t]) |
---|
519 | |
---|
520 | for tag in tags_revs: |
---|
521 | logger.debug('Compare: %s -> %s' % (tag, tags_revs[tag])) |
---|
522 | if trunk_revs == tags_revs[tag]: |
---|
523 | return tag |
---|
524 | |
---|
525 | return None |
---|
526 | |
---|
527 | # tags_dirent = [line.split() for line in out.splitlines()] |
---|
528 | # rev_tag = dict([(int(line[0]), line[-1].rstrip(posixpath.sep)) for line in tags_dirent if line[-1].endswith(posixpath.sep)]) |
---|
529 | # revs = rev_tag.keys() |
---|
530 | # revs.sort() |
---|
531 | # revs.reverse() |
---|
532 | |
---|
533 | # for rev in revs: |
---|
534 | # logger.debug('rev: %s' % rev) |
---|
535 | # if rev < self.info.last_changed_rev: break |
---|
536 | # # if rev < last_changed_rev: break |
---|
537 | # tag = posixpath.join(tags, rev_tag[rev]) |
---|
538 | # # tag = posixpath.normpath(posixpath.join(tags, rev_tag[rev])) |
---|
539 | # logger.debug('comparing: %s %s(%d)' % (trunk, tag, rev)) |
---|
540 | # if 0 == self.cmp(trunk, tag, client_context): |
---|
541 | # return rev_tag[rev] |
---|
542 | |
---|
543 | # return None |
---|
544 | |
---|
545 | # try: |
---|
546 | # trunk = core.svn_path_canonicalize(posixpath.join(module.url, self.trunk)) |
---|
547 | # client.info(trunk, |
---|
548 | # self.head_revision, |
---|
549 | # self.head_revision, |
---|
550 | # self.info_receiver, |
---|
551 | # False, |
---|
552 | # client_context()) |
---|
553 | |
---|
554 | # tags = core.svn_path_canonicalize(posixpath.join(module.url, self.tags)) |
---|
555 | # tags_dirent = client.ls(tags, |
---|
556 | # self.head_revision, |
---|
557 | # False, |
---|
558 | # client_context()) |
---|
559 | |
---|
560 | # rev_tag = dict([(tags_dirent[p].created_rev, p) for p in tags_dirent if tags_dirent[p].kind == core.svn_node_dir]) |
---|
561 | # revs = rev_tag.keys() |
---|
562 | # revs.sort() |
---|
563 | # revs.reverse() |
---|
564 | |
---|
565 | # for rev in revs: |
---|
566 | # if rev < self.info.last_changed_rev: break |
---|
567 | # tag = core.svn_path_canonicalize(posixpath.join(tags, rev_tag[rev])) |
---|
568 | # if 0 == self.cmp(trunk, tag, client_context): |
---|
569 | # return rev_tag[rev] |
---|
570 | |
---|
571 | # return None |
---|
572 | # except core.SubversionException, e: |
---|
573 | # return None |
---|
574 | |
---|
575 | def initialize(self, cmt_context, client_context): |
---|
576 | sc = 0 |
---|
577 | # self.head_revision = core.svn_opt_revision_t() |
---|
578 | # self.head_revision.kind = core.svn_opt_revision_head |
---|
579 | |
---|
580 | # canonicalize: |
---|
581 | self.url = client_context.svn_path_canonicalize(self.url) |
---|
582 | self.offset = client_context.svn_path_canonicalize(self.offset) |
---|
583 | |
---|
584 | for m in self.modules: |
---|
585 | m.module = client_context.svn_path_canonicalize(m.module) |
---|
586 | m.url = client_context.urljoin(self.url, self.offset, m.module) |
---|
587 | |
---|
588 | if urlparse.urlparse(m.url)[0] not in client_context.schemes: |
---|
589 | error('%s: Not a valid Subversion URL' % m.url) |
---|
590 | sc += 1; continue |
---|
591 | # print >>sys.stderr, '%s: URL constructed from %s %s %s' % \ |
---|
592 | # (m.url, `self.url`, `self.offset`, `m.module`) |
---|
593 | |
---|
594 | m.package = posixpath.basename(m.url) |
---|
595 | |
---|
596 | if self.directory is not None: |
---|
597 | m.path = os.path.normpath(self.directory) |
---|
598 | else: |
---|
599 | scheme, netloc, path, query, fragment = urlparse.urlsplit(m.module) |
---|
600 | if not scheme and not netloc: |
---|
601 | m.path = os.path.normpath(os.path.join(*path.split(posixpath.sep))) |
---|
602 | else: |
---|
603 | m.path = posixpath.basename(m.url) |
---|
604 | |
---|
605 | if self.version is None: |
---|
606 | m.head = True |
---|
607 | if cmt_context.head_version_tag: |
---|
608 | m.version = self.trunk_tag(m, client_context) or \ |
---|
609 | cmt_context.eval_head_version(m) |
---|
610 | else: |
---|
611 | trunk = posixpath.join(m.url, self.trunk) |
---|
612 | cmd = 'svn info %s' % trunk |
---|
613 | scc, out = Utils.getstatusoutput(cmd) |
---|
614 | logger.debug('%s\n%s' % (cmd, out)) |
---|
615 | if scc != 0: |
---|
616 | logger.error('%s\n%s' % (cmd, out)) |
---|
617 | sc += 1; continue |
---|
618 | p = r'last\s+changed\s+rev:\s+(?P<rev>\d+)' |
---|
619 | M = re.search(p, out, re.I) |
---|
620 | if M: |
---|
621 | class info(object): pass |
---|
622 | info.last_changed_rev = int(M.group('rev')) |
---|
623 | self.info_receiver(trunk, info, m) |
---|
624 | logger.debug('last_changed_rev: %d' % info.last_changed_rev) |
---|
625 | else: |
---|
626 | logger.warning('%s: last_changed_rev: Not found' % trunk) |
---|
627 | m.version = cmt_context.eval_head_version(m) |
---|
628 | logger.debug('set version: %s' % m.version) |
---|
629 | else: |
---|
630 | m.head = False |
---|
631 | m.version = self.version |
---|
632 | |
---|
633 | if m.head: |
---|
634 | m.URL = [posixpath.join(m.url, self.trunk)] |
---|
635 | else: |
---|
636 | m.URL = [posixpath.join(m.url, p, m.version) |
---|
637 | for p in (self.tags, self.branches, self.devbranches)] |
---|
638 | m.URL = [client_context.svn_path_canonicalize(url) for url in m.URL] |
---|
639 | # m.URL = [core.svn_path_canonicalize(url) for url in m.URL] |
---|
640 | |
---|
641 | if cmt_context.with_version_directory: |
---|
642 | if self.version_dir is None: |
---|
643 | m.path = os.path.join(m.path, m.version) |
---|
644 | else: |
---|
645 | m.path = os.path.join(m.path, self.version_dir) |
---|
646 | |
---|
647 | m.init = True |
---|
648 | # print m.URL, m.path, m.init |
---|
649 | |
---|
650 | # for m in self.modules: |
---|
651 | # print m.url, m.path, m.init |
---|
652 | return sc |
---|
653 | |
---|
654 | def execute(self, cmt_context, client_context): |
---|
655 | sce = 0 |
---|
656 | |
---|
657 | for m in self.modules: |
---|
658 | if not m.init: continue |
---|
659 | done = False |
---|
660 | err = [] |
---|
661 | for url in m.URL: |
---|
662 | cmd = 'svn checkout %s %s' % (url, m.path) |
---|
663 | # cmd = 'svn checkout -q %s %s' % (url, m.path) |
---|
664 | sc, e = Utils.getstatuserror(cmd) |
---|
665 | # cmd = 'svn checkout -q %s %s' % (url, m.path) |
---|
666 | # sc, e = Utils.getstatusoutput(cmd) |
---|
667 | if 0 == sc: |
---|
668 | # try: |
---|
669 | # #print 'client.checkout2:', url, m.path |
---|
670 | # result_rev = client.checkout2(url, |
---|
671 | # m.path, |
---|
672 | # self.head_revision, |
---|
673 | # self.head_revision, |
---|
674 | # True, |
---|
675 | # True, |
---|
676 | # client_context()) |
---|
677 | # except core.SubversionException, e: |
---|
678 | # err.append(e) |
---|
679 | # continue |
---|
680 | done = True |
---|
681 | break |
---|
682 | else: |
---|
683 | err.append(e) |
---|
684 | continue |
---|
685 | |
---|
686 | if not done: |
---|
687 | for e in err: |
---|
688 | error(e) |
---|
689 | # #print >> sys.stderr, e |
---|
690 | # sc += 1 |
---|
691 | # print >> sys.stderr, 'Failed to checkout %s into %s.' % \ |
---|
692 | # (' or '.join(m.URL), m.path) |
---|
693 | sce += 1 |
---|
694 | |
---|
695 | # print 'Checked out revision %i.' % result_rev |
---|
696 | scc = cmt_context.configure(m.path, m.version) |
---|
697 | if scc != 0: |
---|
698 | print >> sys.stderr, \ |
---|
699 | '%s %s: configure returned %i.' % (m.path, m.version, scc) |
---|
700 | sce += scc |
---|
701 | |
---|
702 | return sce |
---|
703 | |
---|
704 | def main(argv=[__name__]): |
---|
705 | self = os.path.basename(argv[0]) |
---|
706 | try: |
---|
707 | opts, args = getopt.getopt(argv[1:], |
---|
708 | "hr:d:o:", |
---|
709 | ["help", "version", "version-tag=", |
---|
710 | "version-dir=", "directory=", |
---|
711 | "offset=", "no_config", |
---|
712 | "with_version_directory", |
---|
713 | "without_version_directory", "url=", "debug"]) |
---|
714 | except getopt.error, e: |
---|
715 | print >>sys.stderr, '%s: %s' % (self, str(e)) |
---|
716 | print >>sys.stderr, "Try '%s --help' for more information." % self |
---|
717 | return 1 |
---|
718 | |
---|
719 | global logger |
---|
720 | logging.basicConfig() |
---|
721 | logger = logging.getLogger(self) |
---|
722 | logger.setLevel(logging.INFO) |
---|
723 | if os.getenv('SVNDEBUG'): |
---|
724 | logger.setLevel(logging.DEBUG) |
---|
725 | |
---|
726 | cmt_context = CmtContext() |
---|
727 | checkout = Checkout() |
---|
728 | |
---|
729 | for o, v in opts: |
---|
730 | if o in ("-h", "--help"): |
---|
731 | print sys.modules[__name__].__doc__ |
---|
732 | return 0 |
---|
733 | elif o in ("--version",): |
---|
734 | print '%s %s (%s)' % (self, __version__, __date__) |
---|
735 | print '%sWritten by %s.' % (os.linesep, __author__) |
---|
736 | return 0 |
---|
737 | elif o in ("-r", "--version-tag"): |
---|
738 | checkout.version = v |
---|
739 | elif o in ("--version-dir",): |
---|
740 | checkout.version_dir = v |
---|
741 | elif o in ("-d", "--directory"): |
---|
742 | checkout.directory = v |
---|
743 | elif o in ("-o", "--offset"): |
---|
744 | checkout.offset = v |
---|
745 | elif o in ("--no_config",): |
---|
746 | cmt_context.config = False |
---|
747 | elif o in ("--without_version_directory",): |
---|
748 | cmt_context.with_version_directory = False |
---|
749 | elif o in ("--with_version_directory",): |
---|
750 | cmt_context.with_version_directory = True |
---|
751 | elif o in ("--url",): |
---|
752 | checkout.url = v |
---|
753 | elif o in ("--debug",): |
---|
754 | logger.setLevel(logging.DEBUG) |
---|
755 | |
---|
756 | if not args: |
---|
757 | print >>sys.stderr, '%s: missing path argument' % self |
---|
758 | print >>sys.stderr, "Try '%s --help' for more information." % self |
---|
759 | return 1 |
---|
760 | |
---|
761 | for arg in args: |
---|
762 | checkout.add(Module(arg)) |
---|
763 | |
---|
764 | client_context = ClientContext() |
---|
765 | sci = checkout.initialize(cmt_context, client_context) |
---|
766 | sce = checkout.execute(cmt_context, client_context) |
---|
767 | |
---|
768 | if sci != 0 or sce !=0: |
---|
769 | return 1 |
---|
770 | else: |
---|
771 | return 0 |
---|
772 | |
---|
773 | if __name__ == '__main__': |
---|
774 | sys.exit(main(sys.argv)) |
---|