111 # svn.client.url_from_path() fails with local repositories |
111 # svn.client.url_from_path() fails with local repositories |
112 pass |
112 pass |
113 if os.path.isdir(path): |
113 if os.path.isdir(path): |
114 path = os.path.normpath(os.path.abspath(path)) |
114 path = os.path.normpath(os.path.abspath(path)) |
115 if pycompat.iswindows: |
115 if pycompat.iswindows: |
116 path = '/' + util.normpath(path) |
116 path = b'/' + util.normpath(path) |
117 # Module URL is later compared with the repository URL returned |
117 # Module URL is later compared with the repository URL returned |
118 # by svn API, which is UTF-8. |
118 # by svn API, which is UTF-8. |
119 path = encoding.tolocal(path) |
119 path = encoding.tolocal(path) |
120 path = 'file://%s' % quote(path) |
120 path = b'file://%s' % quote(path) |
121 return svn.core.svn_path_canonicalize(path) |
121 return svn.core.svn_path_canonicalize(path) |
122 |
122 |
123 |
123 |
124 def optrev(number): |
124 def optrev(number): |
125 optrev = svn.core.svn_opt_revision_t() |
125 optrev = svn.core.svn_opt_revision_t() |
280 # this by requesting a version-controlled URL we know can't exist and looking |
280 # this by requesting a version-controlled URL we know can't exist and looking |
281 # for the svn-specific "not found" XML. |
281 # for the svn-specific "not found" XML. |
282 def httpcheck(ui, path, proto): |
282 def httpcheck(ui, path, proto): |
283 try: |
283 try: |
284 opener = urlreq.buildopener() |
284 opener = urlreq.buildopener() |
285 rsp = opener.open('%s://%s/!svn/ver/0/.svn' % (proto, path), 'rb') |
285 rsp = opener.open(b'%s://%s/!svn/ver/0/.svn' % (proto, path), b'rb') |
286 data = rsp.read() |
286 data = rsp.read() |
287 except urlerr.httperror as inst: |
287 except urlerr.httperror as inst: |
288 if inst.code != 404: |
288 if inst.code != 404: |
289 # Except for 404 we cannot know for sure this is not an svn repo |
289 # Except for 404 we cannot know for sure this is not an svn repo |
290 ui.warn( |
290 ui.warn( |
291 _( |
291 _( |
292 'svn: cannot probe remote repository, assume it could ' |
292 b'svn: cannot probe remote repository, assume it could ' |
293 'be a subversion repository. Use --source-type if you ' |
293 b'be a subversion repository. Use --source-type if you ' |
294 'know better.\n' |
294 b'know better.\n' |
295 ) |
295 ) |
296 ) |
296 ) |
297 return True |
297 return True |
298 data = inst.fp.read() |
298 data = inst.fp.read() |
299 except Exception: |
299 except Exception: |
300 # Could be urlerr.urlerror if the URL is invalid or anything else. |
300 # Could be urlerr.urlerror if the URL is invalid or anything else. |
301 return False |
301 return False |
302 return '<m:human-readable errcode="160013">' in data |
302 return b'<m:human-readable errcode="160013">' in data |
303 |
303 |
304 |
304 |
305 protomap = { |
305 protomap = { |
306 'http': httpcheck, |
306 b'http': httpcheck, |
307 'https': httpcheck, |
307 b'https': httpcheck, |
308 'file': filecheck, |
308 b'file': filecheck, |
309 } |
309 } |
310 |
310 |
311 |
311 |
312 def issvnurl(ui, url): |
312 def issvnurl(ui, url): |
313 try: |
313 try: |
314 proto, path = url.split('://', 1) |
314 proto, path = url.split(b'://', 1) |
315 if proto == 'file': |
315 if proto == b'file': |
316 if ( |
316 if ( |
317 pycompat.iswindows |
317 pycompat.iswindows |
318 and path[:1] == '/' |
318 and path[:1] == b'/' |
319 and path[1:2].isalpha() |
319 and path[1:2].isalpha() |
320 and path[2:6].lower() == '%3a/' |
320 and path[2:6].lower() == b'%3a/' |
321 ): |
321 ): |
322 path = path[:2] + ':/' + path[6:] |
322 path = path[:2] + b':/' + path[6:] |
323 path = urlreq.url2pathname(path) |
323 path = urlreq.url2pathname(path) |
324 except ValueError: |
324 except ValueError: |
325 proto = 'file' |
325 proto = b'file' |
326 path = os.path.abspath(url) |
326 path = os.path.abspath(url) |
327 if proto == 'file': |
327 if proto == b'file': |
328 path = util.pconvert(path) |
328 path = util.pconvert(path) |
329 check = protomap.get(proto, lambda *args: False) |
329 check = protomap.get(proto, lambda *args: False) |
330 while '/' in path: |
330 while b'/' in path: |
331 if check(ui, path, proto): |
331 if check(ui, path, proto): |
332 return True |
332 return True |
333 path = path.rsplit('/', 1)[0] |
333 path = path.rsplit(b'/', 1)[0] |
334 return False |
334 return False |
335 |
335 |
336 |
336 |
337 # SVN conversion code stolen from bzr-svn and tailor |
337 # SVN conversion code stolen from bzr-svn and tailor |
338 # |
338 # |
351 class svn_source(converter_source): |
351 class svn_source(converter_source): |
352 def __init__(self, ui, repotype, url, revs=None): |
352 def __init__(self, ui, repotype, url, revs=None): |
353 super(svn_source, self).__init__(ui, repotype, url, revs=revs) |
353 super(svn_source, self).__init__(ui, repotype, url, revs=revs) |
354 |
354 |
355 if not ( |
355 if not ( |
356 url.startswith('svn://') |
356 url.startswith(b'svn://') |
357 or url.startswith('svn+ssh://') |
357 or url.startswith(b'svn+ssh://') |
358 or ( |
358 or ( |
359 os.path.exists(url) |
359 os.path.exists(url) |
360 and os.path.exists(os.path.join(url, '.svn')) |
360 and os.path.exists(os.path.join(url, b'.svn')) |
361 ) |
361 ) |
362 or issvnurl(ui, url) |
362 or issvnurl(ui, url) |
363 ): |
363 ): |
364 raise NoRepo( |
364 raise NoRepo( |
365 _("%s does not look like a Subversion repository") % url |
365 _(b"%s does not look like a Subversion repository") % url |
366 ) |
366 ) |
367 if svn is None: |
367 if svn is None: |
368 raise MissingTool(_('could not load Subversion python bindings')) |
368 raise MissingTool(_(b'could not load Subversion python bindings')) |
369 |
369 |
370 try: |
370 try: |
371 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR |
371 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR |
372 if version < (1, 4): |
372 if version < (1, 4): |
373 raise MissingTool( |
373 raise MissingTool( |
374 _( |
374 _( |
375 'Subversion python bindings %d.%d found, ' |
375 b'Subversion python bindings %d.%d found, ' |
376 '1.4 or later required' |
376 b'1.4 or later required' |
377 ) |
377 ) |
378 % version |
378 % version |
379 ) |
379 ) |
380 except AttributeError: |
380 except AttributeError: |
381 raise MissingTool( |
381 raise MissingTool( |
382 _( |
382 _( |
383 'Subversion python bindings are too old, 1.4 ' |
383 b'Subversion python bindings are too old, 1.4 ' |
384 'or later required' |
384 b'or later required' |
385 ) |
385 ) |
386 ) |
386 ) |
387 |
387 |
388 self.lastrevs = {} |
388 self.lastrevs = {} |
389 |
389 |
390 latest = None |
390 latest = None |
391 try: |
391 try: |
392 # Support file://path@rev syntax. Useful e.g. to convert |
392 # Support file://path@rev syntax. Useful e.g. to convert |
393 # deleted branches. |
393 # deleted branches. |
394 at = url.rfind('@') |
394 at = url.rfind(b'@') |
395 if at >= 0: |
395 if at >= 0: |
396 latest = int(url[at + 1 :]) |
396 latest = int(url[at + 1 :]) |
397 url = url[:at] |
397 url = url[:at] |
398 except ValueError: |
398 except ValueError: |
399 pass |
399 pass |
400 self.url = geturl(url) |
400 self.url = geturl(url) |
401 self.encoding = 'UTF-8' # Subversion is always nominal UTF-8 |
401 self.encoding = b'UTF-8' # Subversion is always nominal UTF-8 |
402 try: |
402 try: |
403 self.transport = transport.SvnRaTransport(url=self.url) |
403 self.transport = transport.SvnRaTransport(url=self.url) |
404 self.ra = self.transport.ra |
404 self.ra = self.transport.ra |
405 self.ctx = self.transport.client |
405 self.ctx = self.transport.client |
406 self.baseurl = svn.ra.get_repos_root(self.ra) |
406 self.baseurl = svn.ra.get_repos_root(self.ra) |
412 self.commits = {} |
412 self.commits = {} |
413 self.paths = {} |
413 self.paths = {} |
414 self.uuid = svn.ra.get_uuid(self.ra) |
414 self.uuid = svn.ra.get_uuid(self.ra) |
415 except svn.core.SubversionException: |
415 except svn.core.SubversionException: |
416 ui.traceback() |
416 ui.traceback() |
417 svnversion = '%d.%d.%d' % ( |
417 svnversion = b'%d.%d.%d' % ( |
418 svn.core.SVN_VER_MAJOR, |
418 svn.core.SVN_VER_MAJOR, |
419 svn.core.SVN_VER_MINOR, |
419 svn.core.SVN_VER_MINOR, |
420 svn.core.SVN_VER_MICRO, |
420 svn.core.SVN_VER_MICRO, |
421 ) |
421 ) |
422 raise NoRepo( |
422 raise NoRepo( |
423 _( |
423 _( |
424 "%s does not look like a Subversion repository " |
424 b"%s does not look like a Subversion repository " |
425 "to libsvn version %s" |
425 b"to libsvn version %s" |
426 ) |
426 ) |
427 % (self.url, svnversion) |
427 % (self.url, svnversion) |
428 ) |
428 ) |
429 |
429 |
430 if revs: |
430 if revs: |
431 if len(revs) > 1: |
431 if len(revs) > 1: |
432 raise error.Abort( |
432 raise error.Abort( |
433 _( |
433 _( |
434 'subversion source does not support ' |
434 b'subversion source does not support ' |
435 'specifying multiple revisions' |
435 b'specifying multiple revisions' |
436 ) |
436 ) |
437 ) |
437 ) |
438 try: |
438 try: |
439 latest = int(revs[0]) |
439 latest = int(revs[0]) |
440 except ValueError: |
440 except ValueError: |
441 raise error.Abort( |
441 raise error.Abort( |
442 _('svn: revision %s is not an integer') % revs[0] |
442 _(b'svn: revision %s is not an integer') % revs[0] |
443 ) |
443 ) |
444 |
444 |
445 trunkcfg = self.ui.config('convert', 'svn.trunk') |
445 trunkcfg = self.ui.config(b'convert', b'svn.trunk') |
446 if trunkcfg is None: |
446 if trunkcfg is None: |
447 trunkcfg = 'trunk' |
447 trunkcfg = b'trunk' |
448 self.trunkname = trunkcfg.strip('/') |
448 self.trunkname = trunkcfg.strip(b'/') |
449 self.startrev = self.ui.config('convert', 'svn.startrev') |
449 self.startrev = self.ui.config(b'convert', b'svn.startrev') |
450 try: |
450 try: |
451 self.startrev = int(self.startrev) |
451 self.startrev = int(self.startrev) |
452 if self.startrev < 0: |
452 if self.startrev < 0: |
453 self.startrev = 0 |
453 self.startrev = 0 |
454 except ValueError: |
454 except ValueError: |
455 raise error.Abort( |
455 raise error.Abort( |
456 _('svn: start revision %s is not an integer') % self.startrev |
456 _(b'svn: start revision %s is not an integer') % self.startrev |
457 ) |
457 ) |
458 |
458 |
459 try: |
459 try: |
460 self.head = self.latest(self.module, latest) |
460 self.head = self.latest(self.module, latest) |
461 except SvnPathNotFound: |
461 except SvnPathNotFound: |
462 self.head = None |
462 self.head = None |
463 if not self.head: |
463 if not self.head: |
464 raise error.Abort(_('no revision found in module %s') % self.module) |
464 raise error.Abort( |
|
465 _(b'no revision found in module %s') % self.module |
|
466 ) |
465 self.last_changed = self.revnum(self.head) |
467 self.last_changed = self.revnum(self.head) |
466 |
468 |
467 self._changescache = (None, None) |
469 self._changescache = (None, None) |
468 |
470 |
469 if os.path.exists(os.path.join(url, '.svn/entries')): |
471 if os.path.exists(os.path.join(url, b'.svn/entries')): |
470 self.wc = url |
472 self.wc = url |
471 else: |
473 else: |
472 self.wc = None |
474 self.wc = None |
473 self.convertfp = None |
475 self.convertfp = None |
474 |
476 |
497 def isdir(path, revnum): |
499 def isdir(path, revnum): |
498 kind = self._checkpath(path, revnum) |
500 kind = self._checkpath(path, revnum) |
499 return kind == svn.core.svn_node_dir |
501 return kind == svn.core.svn_node_dir |
500 |
502 |
501 def getcfgpath(name, rev): |
503 def getcfgpath(name, rev): |
502 cfgpath = self.ui.config('convert', 'svn.' + name) |
504 cfgpath = self.ui.config(b'convert', b'svn.' + name) |
503 if cfgpath is not None and cfgpath.strip() == '': |
505 if cfgpath is not None and cfgpath.strip() == b'': |
504 return None |
506 return None |
505 path = (cfgpath or name).strip('/') |
507 path = (cfgpath or name).strip(b'/') |
506 if not self.exists(path, rev): |
508 if not self.exists(path, rev): |
507 if self.module.endswith(path) and name == 'trunk': |
509 if self.module.endswith(path) and name == b'trunk': |
508 # we are converting from inside this directory |
510 # we are converting from inside this directory |
509 return None |
511 return None |
510 if cfgpath: |
512 if cfgpath: |
511 raise error.Abort( |
513 raise error.Abort( |
512 _('expected %s to be at %r, but not found') |
514 _(b'expected %s to be at %r, but not found') |
513 % (name, path) |
515 % (name, path) |
514 ) |
516 ) |
515 return None |
517 return None |
516 self.ui.note(_('found %s at %r\n') % (name, path)) |
518 self.ui.note(_(b'found %s at %r\n') % (name, path)) |
517 return path |
519 return path |
518 |
520 |
519 rev = optrev(self.last_changed) |
521 rev = optrev(self.last_changed) |
520 oldmodule = '' |
522 oldmodule = b'' |
521 trunk = getcfgpath('trunk', rev) |
523 trunk = getcfgpath(b'trunk', rev) |
522 self.tags = getcfgpath('tags', rev) |
524 self.tags = getcfgpath(b'tags', rev) |
523 branches = getcfgpath('branches', rev) |
525 branches = getcfgpath(b'branches', rev) |
524 |
526 |
525 # If the project has a trunk or branches, we will extract heads |
527 # If the project has a trunk or branches, we will extract heads |
526 # from them. We keep the project root otherwise. |
528 # from them. We keep the project root otherwise. |
527 if trunk: |
529 if trunk: |
528 oldmodule = self.module or '' |
530 oldmodule = self.module or b'' |
529 self.module += '/' + trunk |
531 self.module += b'/' + trunk |
530 self.head = self.latest(self.module, self.last_changed) |
532 self.head = self.latest(self.module, self.last_changed) |
531 if not self.head: |
533 if not self.head: |
532 raise error.Abort( |
534 raise error.Abort( |
533 _('no revision found in module %s') % self.module |
535 _(b'no revision found in module %s') % self.module |
534 ) |
536 ) |
535 |
537 |
536 # First head in the list is the module's head |
538 # First head in the list is the module's head |
537 self.heads = [self.head] |
539 self.heads = [self.head] |
538 if self.tags is not None: |
540 if self.tags is not None: |
539 self.tags = '%s/%s' % (oldmodule, (self.tags or 'tags')) |
541 self.tags = b'%s/%s' % (oldmodule, (self.tags or b'tags')) |
540 |
542 |
541 # Check if branches bring a few more heads to the list |
543 # Check if branches bring a few more heads to the list |
542 if branches: |
544 if branches: |
543 rpath = self.url.strip('/') |
545 rpath = self.url.strip(b'/') |
544 branchnames = svn.client.ls( |
546 branchnames = svn.client.ls( |
545 rpath + '/' + quote(branches), rev, False, self.ctx |
547 rpath + b'/' + quote(branches), rev, False, self.ctx |
546 ) |
548 ) |
547 for branch in sorted(branchnames): |
549 for branch in sorted(branchnames): |
548 module = '%s/%s/%s' % (oldmodule, branches, branch) |
550 module = b'%s/%s/%s' % (oldmodule, branches, branch) |
549 if not isdir(module, self.last_changed): |
551 if not isdir(module, self.last_changed): |
550 continue |
552 continue |
551 brevid = self.latest(module, self.last_changed) |
553 brevid = self.latest(module, self.last_changed) |
552 if not brevid: |
554 if not brevid: |
553 self.ui.note(_('ignoring empty branch %s\n') % branch) |
555 self.ui.note(_(b'ignoring empty branch %s\n') % branch) |
554 continue |
556 continue |
555 self.ui.note( |
557 self.ui.note( |
556 _('found branch %s at %d\n') % (branch, self.revnum(brevid)) |
558 _(b'found branch %s at %d\n') |
|
559 % (branch, self.revnum(brevid)) |
557 ) |
560 ) |
558 self.heads.append(brevid) |
561 self.heads.append(brevid) |
559 |
562 |
560 if self.startrev and self.heads: |
563 if self.startrev and self.heads: |
561 if len(self.heads) > 1: |
564 if len(self.heads) > 1: |
562 raise error.Abort( |
565 raise error.Abort( |
563 _( |
566 _( |
564 'svn: start revision is not supported ' |
567 b'svn: start revision is not supported ' |
565 'with more than one branch' |
568 b'with more than one branch' |
566 ) |
569 ) |
567 ) |
570 ) |
568 revnum = self.revnum(self.heads[0]) |
571 revnum = self.revnum(self.heads[0]) |
569 if revnum < self.startrev: |
572 if revnum < self.startrev: |
570 raise error.Abort( |
573 raise error.Abort( |
571 _('svn: no revision found after start revision %d') |
574 _(b'svn: no revision found after start revision %d') |
572 % self.startrev |
575 % self.startrev |
573 ) |
576 ) |
574 |
577 |
575 return self.heads |
578 return self.heads |
576 |
579 |
626 stop = self.lastrevs.get(module, 0) |
629 stop = self.lastrevs.get(module, 0) |
627 if revnum < stop: |
630 if revnum < stop: |
628 stop = revnum + 1 |
631 stop = revnum + 1 |
629 self._fetch_revisions(revnum, stop) |
632 self._fetch_revisions(revnum, stop) |
630 if rev not in self.commits: |
633 if rev not in self.commits: |
631 raise error.Abort(_('svn: revision %s not found') % revnum) |
634 raise error.Abort(_(b'svn: revision %s not found') % revnum) |
632 revcommit = self.commits[rev] |
635 revcommit = self.commits[rev] |
633 # caller caches the result, so free it here to release memory |
636 # caller caches the result, so free it here to release memory |
634 del self.commits[rev] |
637 del self.commits[rev] |
635 return revcommit |
638 return revcommit |
636 |
639 |
637 def checkrevformat(self, revstr, mapname='splicemap'): |
640 def checkrevformat(self, revstr, mapname=b'splicemap'): |
638 """ fails if revision format does not match the correct format""" |
641 """ fails if revision format does not match the correct format""" |
639 if not re.match( |
642 if not re.match( |
640 r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-' |
643 r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-' |
641 r'[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]' |
644 r'[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]' |
642 r'{12,12}(.*)\@[0-9]+$', |
645 r'{12,12}(.*)\@[0-9]+$', |
643 revstr, |
646 revstr, |
644 ): |
647 ): |
645 raise error.Abort( |
648 raise error.Abort( |
646 _('%s entry %s is not a valid revision' ' identifier') |
649 _(b'%s entry %s is not a valid revision' b' identifier') |
647 % (mapname, revstr) |
650 % (mapname, revstr) |
648 ) |
651 ) |
649 |
652 |
650 def numcommits(self): |
653 def numcommits(self): |
651 return int(self.head.rsplit('@', 1)[1]) - self.startrev |
654 return int(self.head.rsplit(b'@', 1)[1]) - self.startrev |
652 |
655 |
653 def gettags(self): |
656 def gettags(self): |
654 tags = {} |
657 tags = {} |
655 if self.tags is None: |
658 if self.tags is None: |
656 return tags |
659 return tags |
707 # It happens with tools like cvs2svn. Such tags cannot |
710 # It happens with tools like cvs2svn. Such tags cannot |
708 # be represented in mercurial. |
711 # be represented in mercurial. |
709 addeds = dict( |
712 addeds = dict( |
710 (p, e.copyfrom_path) |
713 (p, e.copyfrom_path) |
711 for p, e in origpaths.iteritems() |
714 for p, e in origpaths.iteritems() |
712 if e.action == 'A' and e.copyfrom_path |
715 if e.action == b'A' and e.copyfrom_path |
713 ) |
716 ) |
714 badroots = set() |
717 badroots = set() |
715 for destroot in addeds: |
718 for destroot in addeds: |
716 for source, sourcerev, dest in pendings: |
719 for source, sourcerev, dest in pendings: |
717 if not dest.startswith( |
720 if not dest.startswith( |
718 destroot + '/' |
721 destroot + b'/' |
719 ) or source.startswith(addeds[destroot] + '/'): |
722 ) or source.startswith(addeds[destroot] + b'/'): |
720 continue |
723 continue |
721 badroots.add(destroot) |
724 badroots.add(destroot) |
722 break |
725 break |
723 |
726 |
724 for badroot in badroots: |
727 for badroot in badroots: |
725 pendings = [ |
728 pendings = [ |
726 p |
729 p |
727 for p in pendings |
730 for p in pendings |
728 if p[2] != badroot |
731 if p[2] != badroot |
729 and not p[2].startswith(badroot + '/') |
732 and not p[2].startswith(badroot + b'/') |
730 ] |
733 ] |
731 |
734 |
732 # Tell tag renamings from tag creations |
735 # Tell tag renamings from tag creations |
733 renamings = [] |
736 renamings = [] |
734 for source, sourcerev, dest in pendings: |
737 for source, sourcerev, dest in pendings: |
735 tagname = dest.split('/')[-1] |
738 tagname = dest.split(b'/')[-1] |
736 if source.startswith(srctagspath): |
739 if source.startswith(srctagspath): |
737 renamings.append([source, sourcerev, tagname]) |
740 renamings.append([source, sourcerev, tagname]) |
738 continue |
741 continue |
739 if tagname in tags: |
742 if tagname in tags: |
740 # Keep the latest tag value |
743 # Keep the latest tag value |
759 def converted(self, rev, destrev): |
762 def converted(self, rev, destrev): |
760 if not self.wc: |
763 if not self.wc: |
761 return |
764 return |
762 if self.convertfp is None: |
765 if self.convertfp is None: |
763 self.convertfp = open( |
766 self.convertfp = open( |
764 os.path.join(self.wc, '.svn', 'hg-shamap'), 'ab' |
767 os.path.join(self.wc, b'.svn', b'hg-shamap'), b'ab' |
765 ) |
768 ) |
766 self.convertfp.write( |
769 self.convertfp.write( |
767 util.tonativeeol('%s %d\n' % (destrev, self.revnum(rev))) |
770 util.tonativeeol(b'%s %d\n' % (destrev, self.revnum(rev))) |
768 ) |
771 ) |
769 self.convertfp.flush() |
772 self.convertfp.flush() |
770 |
773 |
771 def revid(self, revnum, module=None): |
774 def revid(self, revnum, module=None): |
772 return 'svn:%s%s@%s' % (self.uuid, module or self.module, revnum) |
775 return b'svn:%s%s@%s' % (self.uuid, module or self.module, revnum) |
773 |
776 |
774 def revnum(self, rev): |
777 def revnum(self, rev): |
775 return int(rev.split('@')[-1]) |
778 return int(rev.split(b'@')[-1]) |
776 |
779 |
777 def latest(self, path, stop=None): |
780 def latest(self, path, stop=None): |
778 """Find the latest revid affecting path, up to stop revision |
781 """Find the latest revid affecting path, up to stop revision |
779 number. If stop is None, default to repository latest |
782 number. If stop is None, default to repository latest |
780 revision. It may return a revision in a different module, |
783 revision. It may return a revision in a different module, |
811 finally: |
814 finally: |
812 stream.close() |
815 stream.close() |
813 |
816 |
814 if not path.startswith(self.rootmodule): |
817 if not path.startswith(self.rootmodule): |
815 # Requests on foreign branches may be forbidden at server level |
818 # Requests on foreign branches may be forbidden at server level |
816 self.ui.debug('ignoring foreign branch %r\n' % path) |
819 self.ui.debug(b'ignoring foreign branch %r\n' % path) |
817 return None |
820 return None |
818 |
821 |
819 if stop is None: |
822 if stop is None: |
820 stop = svn.ra.get_latest_revnum(self.ra) |
823 stop = svn.ra.get_latest_revnum(self.ra) |
821 try: |
824 try: |
822 prevmodule = self.reparent('') |
825 prevmodule = self.reparent(b'') |
823 dirent = svn.ra.stat(self.ra, path.strip('/'), stop) |
826 dirent = svn.ra.stat(self.ra, path.strip(b'/'), stop) |
824 self.reparent(prevmodule) |
827 self.reparent(prevmodule) |
825 except svn.core.SubversionException: |
828 except svn.core.SubversionException: |
826 dirent = None |
829 dirent = None |
827 if not dirent: |
830 if not dirent: |
828 raise SvnPathNotFound( |
831 raise SvnPathNotFound( |
829 _('%s not found up to revision %d') % (path, stop) |
832 _(b'%s not found up to revision %d') % (path, stop) |
830 ) |
833 ) |
831 |
834 |
832 # stat() gives us the previous revision on this line of |
835 # stat() gives us the previous revision on this line of |
833 # development, but it might be in *another module*. Fetch the |
836 # development, but it might be in *another module*. Fetch the |
834 # log and detect renames down to the latest revision. |
837 # log and detect renames down to the latest revision. |
841 # returned by ra.stat(), at least when stating the root |
844 # returned by ra.stat(), at least when stating the root |
842 # module. In that case, do not trust created_rev and scan |
845 # module. In that case, do not trust created_rev and scan |
843 # the whole history. |
846 # the whole history. |
844 revnum, realpath = findchanges(path, stop) |
847 revnum, realpath = findchanges(path, stop) |
845 if revnum is None: |
848 if revnum is None: |
846 self.ui.debug('ignoring empty branch %r\n' % realpath) |
849 self.ui.debug(b'ignoring empty branch %r\n' % realpath) |
847 return None |
850 return None |
848 |
851 |
849 if not realpath.startswith(self.rootmodule): |
852 if not realpath.startswith(self.rootmodule): |
850 self.ui.debug('ignoring foreign branch %r\n' % realpath) |
853 self.ui.debug(b'ignoring foreign branch %r\n' % realpath) |
851 return None |
854 return None |
852 return self.revid(revnum, realpath) |
855 return self.revid(revnum, realpath) |
853 |
856 |
854 def reparent(self, module): |
857 def reparent(self, module): |
855 """Reparent the svn transport and return the previous parent.""" |
858 """Reparent the svn transport and return the previous parent.""" |
856 if self.prevmodule == module: |
859 if self.prevmodule == module: |
857 return module |
860 return module |
858 svnurl = self.baseurl + quote(module) |
861 svnurl = self.baseurl + quote(module) |
859 prevmodule = self.prevmodule |
862 prevmodule = self.prevmodule |
860 if prevmodule is None: |
863 if prevmodule is None: |
861 prevmodule = '' |
864 prevmodule = b'' |
862 self.ui.debug("reparent to %s\n" % svnurl) |
865 self.ui.debug(b"reparent to %s\n" % svnurl) |
863 svn.ra.reparent(self.ra, svnurl) |
866 svn.ra.reparent(self.ra, svnurl) |
864 self.prevmodule = module |
867 self.prevmodule = module |
865 return prevmodule |
868 return prevmodule |
866 |
869 |
867 def expandpaths(self, rev, paths, parents): |
870 def expandpaths(self, rev, paths, parents): |
892 continue |
895 continue |
893 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule) |
896 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule) |
894 if not copyfrom_path: |
897 if not copyfrom_path: |
895 continue |
898 continue |
896 self.ui.debug( |
899 self.ui.debug( |
897 "copied to %s from %s@%s\n" |
900 b"copied to %s from %s@%s\n" |
898 % (entrypath, copyfrom_path, ent.copyfrom_rev) |
901 % (entrypath, copyfrom_path, ent.copyfrom_rev) |
899 ) |
902 ) |
900 copies[self.recode(entrypath)] = self.recode(copyfrom_path) |
903 copies[self.recode(entrypath)] = self.recode(copyfrom_path) |
901 elif kind == 0: # gone, but had better be a deleted *file* |
904 elif kind == 0: # gone, but had better be a deleted *file* |
902 self.ui.debug("gone from %s\n" % ent.copyfrom_rev) |
905 self.ui.debug(b"gone from %s\n" % ent.copyfrom_rev) |
903 pmodule, prevnum = revsplit(parents[0])[1:] |
906 pmodule, prevnum = revsplit(parents[0])[1:] |
904 parentpath = pmodule + "/" + entrypath |
907 parentpath = pmodule + b"/" + entrypath |
905 fromkind = self._checkpath(entrypath, prevnum, pmodule) |
908 fromkind = self._checkpath(entrypath, prevnum, pmodule) |
906 |
909 |
907 if fromkind == svn.core.svn_node_file: |
910 if fromkind == svn.core.svn_node_file: |
908 removed.add(self.recode(entrypath)) |
911 removed.add(self.recode(entrypath)) |
909 elif fromkind == svn.core.svn_node_dir: |
912 elif fromkind == svn.core.svn_node_dir: |
910 oroot = parentpath.strip('/') |
913 oroot = parentpath.strip(b'/') |
911 nroot = path.strip('/') |
914 nroot = path.strip(b'/') |
912 children = self._iterfiles(oroot, prevnum) |
915 children = self._iterfiles(oroot, prevnum) |
913 for childpath in children: |
916 for childpath in children: |
914 childpath = childpath.replace(oroot, nroot) |
917 childpath = childpath.replace(oroot, nroot) |
915 childpath = self.getrelpath("/" + childpath, pmodule) |
918 childpath = self.getrelpath(b"/" + childpath, pmodule) |
916 if childpath: |
919 if childpath: |
917 removed.add(self.recode(childpath)) |
920 removed.add(self.recode(childpath)) |
918 else: |
921 else: |
919 self.ui.debug( |
922 self.ui.debug( |
920 'unknown path in revision %d: %s\n' % (revnum, path) |
923 b'unknown path in revision %d: %s\n' % (revnum, path) |
921 ) |
924 ) |
922 elif kind == svn.core.svn_node_dir: |
925 elif kind == svn.core.svn_node_dir: |
923 if ent.action == 'M': |
926 if ent.action == b'M': |
924 # If the directory just had a prop change, |
927 # If the directory just had a prop change, |
925 # then we shouldn't need to look for its children. |
928 # then we shouldn't need to look for its children. |
926 continue |
929 continue |
927 if ent.action == 'R' and parents: |
930 if ent.action == b'R' and parents: |
928 # If a directory is replacing a file, mark the previous |
931 # If a directory is replacing a file, mark the previous |
929 # file as deleted |
932 # file as deleted |
930 pmodule, prevnum = revsplit(parents[0])[1:] |
933 pmodule, prevnum = revsplit(parents[0])[1:] |
931 pkind = self._checkpath(entrypath, prevnum, pmodule) |
934 pkind = self._checkpath(entrypath, prevnum, pmodule) |
932 if pkind == svn.core.svn_node_file: |
935 if pkind == svn.core.svn_node_file: |
933 removed.add(self.recode(entrypath)) |
936 removed.add(self.recode(entrypath)) |
934 elif pkind == svn.core.svn_node_dir: |
937 elif pkind == svn.core.svn_node_dir: |
935 # We do not know what files were kept or removed, |
938 # We do not know what files were kept or removed, |
936 # mark them all as changed. |
939 # mark them all as changed. |
937 for childpath in self._iterfiles(pmodule, prevnum): |
940 for childpath in self._iterfiles(pmodule, prevnum): |
938 childpath = self.getrelpath("/" + childpath) |
941 childpath = self.getrelpath(b"/" + childpath) |
939 if childpath: |
942 if childpath: |
940 changed.add(self.recode(childpath)) |
943 changed.add(self.recode(childpath)) |
941 |
944 |
942 for childpath in self._iterfiles(path, revnum): |
945 for childpath in self._iterfiles(path, revnum): |
943 childpath = self.getrelpath("/" + childpath) |
946 childpath = self.getrelpath(b"/" + childpath) |
944 if childpath: |
947 if childpath: |
945 changed.add(self.recode(childpath)) |
948 changed.add(self.recode(childpath)) |
946 |
949 |
947 # Handle directory copies |
950 # Handle directory copies |
948 if not ent.copyfrom_path or not parents: |
951 if not ent.copyfrom_path or not parents: |
954 continue |
957 continue |
955 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule) |
958 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule) |
956 if not copyfrompath: |
959 if not copyfrompath: |
957 continue |
960 continue |
958 self.ui.debug( |
961 self.ui.debug( |
959 "mark %s came from %s:%d\n" |
962 b"mark %s came from %s:%d\n" |
960 % (path, copyfrompath, ent.copyfrom_rev) |
963 % (path, copyfrompath, ent.copyfrom_rev) |
961 ) |
964 ) |
962 children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev) |
965 children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev) |
963 for childpath in children: |
966 for childpath in children: |
964 childpath = self.getrelpath("/" + childpath, pmodule) |
967 childpath = self.getrelpath(b"/" + childpath, pmodule) |
965 if not childpath: |
968 if not childpath: |
966 continue |
969 continue |
967 copytopath = path + childpath[len(copyfrompath) :] |
970 copytopath = path + childpath[len(copyfrompath) :] |
968 copytopath = self.getrelpath(copytopath) |
971 copytopath = self.getrelpath(copytopath) |
969 copies[self.recode(copytopath)] = self.recode(childpath) |
972 copies[self.recode(copytopath)] = self.recode(childpath) |
1026 paths.append((path, ent)) |
1030 paths.append((path, ent)) |
1027 |
1031 |
1028 # Example SVN datetime. Includes microseconds. |
1032 # Example SVN datetime. Includes microseconds. |
1029 # ISO-8601 conformant |
1033 # ISO-8601 conformant |
1030 # '2007-01-04T17:35:00.902377Z' |
1034 # '2007-01-04T17:35:00.902377Z' |
1031 date = dateutil.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"]) |
1035 date = dateutil.parsedate( |
1032 if self.ui.configbool('convert', 'localtimezone'): |
1036 date[:19] + b" UTC", [b"%Y-%m-%dT%H:%M:%S"] |
|
1037 ) |
|
1038 if self.ui.configbool(b'convert', b'localtimezone'): |
1033 date = makedatetimestamp(date[0]) |
1039 date = makedatetimestamp(date[0]) |
1034 |
1040 |
1035 if message: |
1041 if message: |
1036 log = self.recode(message) |
1042 log = self.recode(message) |
1037 else: |
1043 else: |
1038 log = '' |
1044 log = b'' |
1039 |
1045 |
1040 if author: |
1046 if author: |
1041 author = self.recode(author) |
1047 author = self.recode(author) |
1042 else: |
1048 else: |
1043 author = '' |
1049 author = b'' |
1044 |
1050 |
1045 try: |
1051 try: |
1046 branch = self.module.split("/")[-1] |
1052 branch = self.module.split(b"/")[-1] |
1047 if branch == self.trunkname: |
1053 if branch == self.trunkname: |
1048 branch = None |
1054 branch = None |
1049 except IndexError: |
1055 except IndexError: |
1050 branch = None |
1056 branch = None |
1051 |
1057 |
1052 cset = commit( |
1058 cset = commit( |
1053 author=author, |
1059 author=author, |
1054 date=dateutil.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), |
1060 date=dateutil.datestr(date, b'%Y-%m-%d %H:%M:%S %1%2'), |
1055 desc=log, |
1061 desc=log, |
1056 parents=parents, |
1062 parents=parents, |
1057 branch=branch, |
1063 branch=branch, |
1058 rev=rev, |
1064 rev=rev, |
1059 ) |
1065 ) |
1133 # ra.get_file() seems to keep a reference on the input buffer |
1139 # ra.get_file() seems to keep a reference on the input buffer |
1134 # preventing collection. Release it explicitly. |
1140 # preventing collection. Release it explicitly. |
1135 io.close() |
1141 io.close() |
1136 if isinstance(info, list): |
1142 if isinstance(info, list): |
1137 info = info[-1] |
1143 info = info[-1] |
1138 mode = ("svn:executable" in info) and 'x' or '' |
1144 mode = (b"svn:executable" in info) and b'x' or b'' |
1139 mode = ("svn:special" in info) and 'l' or mode |
1145 mode = (b"svn:special" in info) and b'l' or mode |
1140 except svn.core.SubversionException as e: |
1146 except svn.core.SubversionException as e: |
1141 notfound = ( |
1147 notfound = ( |
1142 svn.core.SVN_ERR_FS_NOT_FOUND, |
1148 svn.core.SVN_ERR_FS_NOT_FOUND, |
1143 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND, |
1149 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND, |
1144 ) |
1150 ) |
1145 if e.apr_err in notfound: # File not found |
1151 if e.apr_err in notfound: # File not found |
1146 return None, None |
1152 return None, None |
1147 raise |
1153 raise |
1148 if mode == 'l': |
1154 if mode == b'l': |
1149 link_prefix = "link " |
1155 link_prefix = b"link " |
1150 if data.startswith(link_prefix): |
1156 if data.startswith(link_prefix): |
1151 data = data[len(link_prefix) :] |
1157 data = data[len(link_prefix) :] |
1152 return data, mode |
1158 return data, mode |
1153 |
1159 |
1154 def _iterfiles(self, path, revnum): |
1160 def _iterfiles(self, path, revnum): |
1155 """Enumerate all files in path at revnum, recursively.""" |
1161 """Enumerate all files in path at revnum, recursively.""" |
1156 path = path.strip('/') |
1162 path = path.strip(b'/') |
1157 pool = svn.core.Pool() |
1163 pool = svn.core.Pool() |
1158 rpath = '/'.join([self.baseurl, quote(path)]).strip('/') |
1164 rpath = b'/'.join([self.baseurl, quote(path)]).strip(b'/') |
1159 entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool) |
1165 entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool) |
1160 if path: |
1166 if path: |
1161 path += '/' |
1167 path += b'/' |
1162 return ( |
1168 return ( |
1163 (path + p) |
1169 (path + p) |
1164 for p, e in entries.iteritems() |
1170 for p, e in entries.iteritems() |
1165 if e.kind == svn.core.svn_node_file |
1171 if e.kind == svn.core.svn_node_file |
1166 ) |
1172 ) |
1173 # extract the "entry" portion (a relative path) from what |
1179 # extract the "entry" portion (a relative path) from what |
1174 # svn log --xml says, i.e. |
1180 # svn log --xml says, i.e. |
1175 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" |
1181 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" |
1176 # that is to say "tests/PloneTestCase.py" |
1182 # that is to say "tests/PloneTestCase.py" |
1177 if path.startswith(module): |
1183 if path.startswith(module): |
1178 relative = path.rstrip('/')[len(module) :] |
1184 relative = path.rstrip(b'/')[len(module) :] |
1179 if relative.startswith('/'): |
1185 if relative.startswith(b'/'): |
1180 return relative[1:] |
1186 return relative[1:] |
1181 elif relative == '': |
1187 elif relative == b'': |
1182 return relative |
1188 return relative |
1183 |
1189 |
1184 # The path is outside our tracked tree... |
1190 # The path is outside our tracked tree... |
1185 self.ui.debug('%r is not under %r, ignoring\n' % (path, module)) |
1191 self.ui.debug(b'%r is not under %r, ignoring\n' % (path, module)) |
1186 return None |
1192 return None |
1187 |
1193 |
1188 def _checkpath(self, path, revnum, module=None): |
1194 def _checkpath(self, path, revnum, module=None): |
1189 if module is not None: |
1195 if module is not None: |
1190 prevmodule = self.reparent('') |
1196 prevmodule = self.reparent(b'') |
1191 path = module + '/' + path |
1197 path = module + b'/' + path |
1192 try: |
1198 try: |
1193 # ra.check_path does not like leading slashes very much, it leads |
1199 # ra.check_path does not like leading slashes very much, it leads |
1194 # to PROPFIND subversion errors |
1200 # to PROPFIND subversion errors |
1195 return svn.ra.check_path(self.ra, path.strip('/'), revnum) |
1201 return svn.ra.check_path(self.ra, path.strip(b'/'), revnum) |
1196 finally: |
1202 finally: |
1197 if module is not None: |
1203 if module is not None: |
1198 self.reparent(prevmodule) |
1204 self.reparent(prevmodule) |
1199 |
1205 |
1200 def _getlog( |
1206 def _getlog( |
1208 ): |
1214 ): |
1209 # Normalize path names, svn >= 1.5 only wants paths relative to |
1215 # Normalize path names, svn >= 1.5 only wants paths relative to |
1210 # supplied URL |
1216 # supplied URL |
1211 relpaths = [] |
1217 relpaths = [] |
1212 for p in paths: |
1218 for p in paths: |
1213 if not p.startswith('/'): |
1219 if not p.startswith(b'/'): |
1214 p = self.module + '/' + p |
1220 p = self.module + b'/' + p |
1215 relpaths.append(p.strip('/')) |
1221 relpaths.append(p.strip(b'/')) |
1216 args = [ |
1222 args = [ |
1217 self.baseurl, |
1223 self.baseurl, |
1218 relpaths, |
1224 relpaths, |
1219 start, |
1225 start, |
1220 end, |
1226 end, |
1221 limit, |
1227 limit, |
1222 discover_changed_paths, |
1228 discover_changed_paths, |
1223 strict_node_history, |
1229 strict_node_history, |
1224 ] |
1230 ] |
1225 # developer config: convert.svn.debugsvnlog |
1231 # developer config: convert.svn.debugsvnlog |
1226 if not self.ui.configbool('convert', 'svn.debugsvnlog'): |
1232 if not self.ui.configbool(b'convert', b'svn.debugsvnlog'): |
1227 return directlogstream(*args) |
1233 return directlogstream(*args) |
1228 arg = encodeargs(args) |
1234 arg = encodeargs(args) |
1229 hgexe = procutil.hgexecutable() |
1235 hgexe = procutil.hgexecutable() |
1230 cmd = '%s debugsvnlog' % procutil.shellquote(hgexe) |
1236 cmd = b'%s debugsvnlog' % procutil.shellquote(hgexe) |
1231 stdin, stdout = procutil.popen2(procutil.quotecommand(cmd)) |
1237 stdin, stdout = procutil.popen2(procutil.quotecommand(cmd)) |
1232 stdin.write(arg) |
1238 stdin.write(arg) |
1233 try: |
1239 try: |
1234 stdin.close() |
1240 stdin.close() |
1235 except IOError: |
1241 except IOError: |
1236 raise error.Abort( |
1242 raise error.Abort( |
1237 _( |
1243 _( |
1238 'Mercurial failed to run itself, check' |
1244 b'Mercurial failed to run itself, check' |
1239 ' hg executable is in PATH' |
1245 b' hg executable is in PATH' |
1240 ) |
1246 ) |
1241 ) |
1247 ) |
1242 return logstream(stdout) |
1248 return logstream(stdout) |
1243 |
1249 |
1244 |
1250 |
1270 def postrun(self): |
1276 def postrun(self): |
1271 if self.wc: |
1277 if self.wc: |
1272 os.chdir(self.cwd) |
1278 os.chdir(self.cwd) |
1273 |
1279 |
1274 def join(self, name): |
1280 def join(self, name): |
1275 return os.path.join(self.wc, '.svn', name) |
1281 return os.path.join(self.wc, b'.svn', name) |
1276 |
1282 |
1277 def revmapfile(self): |
1283 def revmapfile(self): |
1278 return self.join('hg-shamap') |
1284 return self.join(b'hg-shamap') |
1279 |
1285 |
1280 def authorfile(self): |
1286 def authorfile(self): |
1281 return self.join('hg-authormap') |
1287 return self.join(b'hg-authormap') |
1282 |
1288 |
1283 def __init__(self, ui, repotype, path): |
1289 def __init__(self, ui, repotype, path): |
1284 |
1290 |
1285 converter_sink.__init__(self, ui, repotype, path) |
1291 converter_sink.__init__(self, ui, repotype, path) |
1286 commandline.__init__(self, ui, 'svn') |
1292 commandline.__init__(self, ui, b'svn') |
1287 self.delete = [] |
1293 self.delete = [] |
1288 self.setexec = [] |
1294 self.setexec = [] |
1289 self.delexec = [] |
1295 self.delexec = [] |
1290 self.copies = [] |
1296 self.copies = [] |
1291 self.wc = None |
1297 self.wc = None |
1292 self.cwd = encoding.getcwd() |
1298 self.cwd = encoding.getcwd() |
1293 |
1299 |
1294 created = False |
1300 created = False |
1295 if os.path.isfile(os.path.join(path, '.svn', 'entries')): |
1301 if os.path.isfile(os.path.join(path, b'.svn', b'entries')): |
1296 self.wc = os.path.realpath(path) |
1302 self.wc = os.path.realpath(path) |
1297 self.run0('update') |
1303 self.run0(b'update') |
1298 else: |
1304 else: |
1299 if not re.search(br'^(file|http|https|svn|svn\+ssh)\://', path): |
1305 if not re.search(br'^(file|http|https|svn|svn\+ssh)\://', path): |
1300 path = os.path.realpath(path) |
1306 path = os.path.realpath(path) |
1301 if os.path.isdir(os.path.dirname(path)): |
1307 if os.path.isdir(os.path.dirname(path)): |
1302 if not os.path.exists(os.path.join(path, 'db', 'fs-type')): |
1308 if not os.path.exists( |
|
1309 os.path.join(path, b'db', b'fs-type') |
|
1310 ): |
1303 ui.status( |
1311 ui.status( |
1304 _("initializing svn repository '%s'\n") |
1312 _(b"initializing svn repository '%s'\n") |
1305 % os.path.basename(path) |
1313 % os.path.basename(path) |
1306 ) |
1314 ) |
1307 commandline(ui, 'svnadmin').run0('create', path) |
1315 commandline(ui, b'svnadmin').run0(b'create', path) |
1308 created = path |
1316 created = path |
1309 path = util.normpath(path) |
1317 path = util.normpath(path) |
1310 if not path.startswith('/'): |
1318 if not path.startswith(b'/'): |
1311 path = '/' + path |
1319 path = b'/' + path |
1312 path = 'file://' + path |
1320 path = b'file://' + path |
1313 |
1321 |
1314 wcpath = os.path.join( |
1322 wcpath = os.path.join( |
1315 encoding.getcwd(), os.path.basename(path) + '-wc' |
1323 encoding.getcwd(), os.path.basename(path) + b'-wc' |
1316 ) |
1324 ) |
1317 ui.status( |
1325 ui.status( |
1318 _("initializing svn working copy '%s'\n") |
1326 _(b"initializing svn working copy '%s'\n") |
1319 % os.path.basename(wcpath) |
1327 % os.path.basename(wcpath) |
1320 ) |
1328 ) |
1321 self.run0('checkout', path, wcpath) |
1329 self.run0(b'checkout', path, wcpath) |
1322 |
1330 |
1323 self.wc = wcpath |
1331 self.wc = wcpath |
1324 self.opener = vfsmod.vfs(self.wc) |
1332 self.opener = vfsmod.vfs(self.wc) |
1325 self.wopener = vfsmod.vfs(self.wc) |
1333 self.wopener = vfsmod.vfs(self.wc) |
1326 self.childmap = mapfile(ui, self.join('hg-childmap')) |
1334 self.childmap = mapfile(ui, self.join(b'hg-childmap')) |
1327 if util.checkexec(self.wc): |
1335 if util.checkexec(self.wc): |
1328 self.is_exec = util.isexec |
1336 self.is_exec = util.isexec |
1329 else: |
1337 else: |
1330 self.is_exec = None |
1338 self.is_exec = None |
1331 |
1339 |
1332 if created: |
1340 if created: |
1333 hook = os.path.join(created, 'hooks', 'pre-revprop-change') |
1341 hook = os.path.join(created, b'hooks', b'pre-revprop-change') |
1334 fp = open(hook, 'wb') |
1342 fp = open(hook, b'wb') |
1335 fp.write(pre_revprop_change) |
1343 fp.write(pre_revprop_change) |
1336 fp.close() |
1344 fp.close() |
1337 util.setflags(hook, False, True) |
1345 util.setflags(hook, False, True) |
1338 |
1346 |
1339 output = self.run0('info') |
1347 output = self.run0(b'info') |
1340 self.uuid = self.uuid_re.search(output).group(1).strip() |
1348 self.uuid = self.uuid_re.search(output).group(1).strip() |
1341 |
1349 |
1342 def wjoin(self, *names): |
1350 def wjoin(self, *names): |
1343 return os.path.join(self.wc, *names) |
1351 return os.path.join(self.wc, *names) |
1344 |
1352 |
1385 |
1393 |
1386 self.wopener.write(filename, data) |
1394 self.wopener.write(filename, data) |
1387 |
1395 |
1388 if self.is_exec: |
1396 if self.is_exec: |
1389 if wasexec: |
1397 if wasexec: |
1390 if 'x' not in flags: |
1398 if b'x' not in flags: |
1391 self.delexec.append(filename) |
1399 self.delexec.append(filename) |
1392 else: |
1400 else: |
1393 if 'x' in flags: |
1401 if b'x' in flags: |
1394 self.setexec.append(filename) |
1402 self.setexec.append(filename) |
1395 util.setflags(self.wjoin(filename), False, 'x' in flags) |
1403 util.setflags(self.wjoin(filename), False, b'x' in flags) |
1396 |
1404 |
1397 def _copyfile(self, source, dest): |
1405 def _copyfile(self, source, dest): |
1398 # SVN's copy command pukes if the destination file exists, but |
1406 # SVN's copy command pukes if the destination file exists, but |
1399 # our copyfile method expects to record a copy that has |
1407 # our copyfile method expects to record a copy that has |
1400 # already occurred. Cross the semantic gap. |
1408 # already occurred. Cross the semantic gap. |
1401 wdest = self.wjoin(dest) |
1409 wdest = self.wjoin(dest) |
1402 exists = os.path.lexists(wdest) |
1410 exists = os.path.lexists(wdest) |
1403 if exists: |
1411 if exists: |
1404 fd, tempname = pycompat.mkstemp( |
1412 fd, tempname = pycompat.mkstemp( |
1405 prefix='hg-copy-', dir=os.path.dirname(wdest) |
1413 prefix=b'hg-copy-', dir=os.path.dirname(wdest) |
1406 ) |
1414 ) |
1407 os.close(fd) |
1415 os.close(fd) |
1408 os.unlink(tempname) |
1416 os.unlink(tempname) |
1409 os.rename(wdest, tempname) |
1417 os.rename(wdest, tempname) |
1410 try: |
1418 try: |
1411 self.run0('copy', source, dest) |
1419 self.run0(b'copy', source, dest) |
1412 finally: |
1420 finally: |
1413 self.manifest.add(dest) |
1421 self.manifest.add(dest) |
1414 if exists: |
1422 if exists: |
1415 try: |
1423 try: |
1416 os.unlink(wdest) |
1424 os.unlink(wdest) |
1422 dirs = set() |
1430 dirs = set() |
1423 for f in files: |
1431 for f in files: |
1424 if os.path.isdir(self.wjoin(f)): |
1432 if os.path.isdir(self.wjoin(f)): |
1425 dirs.add(f) |
1433 dirs.add(f) |
1426 i = len(f) |
1434 i = len(f) |
1427 for i in iter(lambda: f.rfind('/', 0, i), -1): |
1435 for i in iter(lambda: f.rfind(b'/', 0, i), -1): |
1428 dirs.add(f[:i]) |
1436 dirs.add(f[:i]) |
1429 return dirs |
1437 return dirs |
1430 |
1438 |
1431 def add_dirs(self, files): |
1439 def add_dirs(self, files): |
1432 add_dirs = [ |
1440 add_dirs = [ |
1433 d for d in sorted(self.dirs_of(files)) if d not in self.manifest |
1441 d for d in sorted(self.dirs_of(files)) if d not in self.manifest |
1434 ] |
1442 ] |
1435 if add_dirs: |
1443 if add_dirs: |
1436 self.manifest.update(add_dirs) |
1444 self.manifest.update(add_dirs) |
1437 self.xargs(add_dirs, 'add', non_recursive=True, quiet=True) |
1445 self.xargs(add_dirs, b'add', non_recursive=True, quiet=True) |
1438 return add_dirs |
1446 return add_dirs |
1439 |
1447 |
1440 def add_files(self, files): |
1448 def add_files(self, files): |
1441 files = [f for f in files if f not in self.manifest] |
1449 files = [f for f in files if f not in self.manifest] |
1442 if files: |
1450 if files: |
1443 self.manifest.update(files) |
1451 self.manifest.update(files) |
1444 self.xargs(files, 'add', quiet=True) |
1452 self.xargs(files, b'add', quiet=True) |
1445 return files |
1453 return files |
1446 |
1454 |
1447 def addchild(self, parent, child): |
1455 def addchild(self, parent, child): |
1448 self.childmap[parent] = child |
1456 self.childmap[parent] = child |
1449 |
1457 |
1450 def revid(self, rev): |
1458 def revid(self, rev): |
1451 return "svn:%s@%s" % (self.uuid, rev) |
1459 return b"svn:%s@%s" % (self.uuid, rev) |
1452 |
1460 |
1453 def putcommit( |
1461 def putcommit( |
1454 self, files, copies, parents, commit, source, revmap, full, cleanp2 |
1462 self, files, copies, parents, commit, source, revmap, full, cleanp2 |
1455 ): |
1463 ): |
1456 for parent in parents: |
1464 for parent in parents: |
1478 if self.copies: |
1486 if self.copies: |
1479 for s, d in self.copies: |
1487 for s, d in self.copies: |
1480 self._copyfile(s, d) |
1488 self._copyfile(s, d) |
1481 self.copies = [] |
1489 self.copies = [] |
1482 if self.delete: |
1490 if self.delete: |
1483 self.xargs(self.delete, 'delete') |
1491 self.xargs(self.delete, b'delete') |
1484 for f in self.delete: |
1492 for f in self.delete: |
1485 self.manifest.remove(f) |
1493 self.manifest.remove(f) |
1486 self.delete = [] |
1494 self.delete = [] |
1487 entries.update(self.add_files(files.difference(entries))) |
1495 entries.update(self.add_files(files.difference(entries))) |
1488 if self.delexec: |
1496 if self.delexec: |
1489 self.xargs(self.delexec, 'propdel', 'svn:executable') |
1497 self.xargs(self.delexec, b'propdel', b'svn:executable') |
1490 self.delexec = [] |
1498 self.delexec = [] |
1491 if self.setexec: |
1499 if self.setexec: |
1492 self.xargs(self.setexec, 'propset', 'svn:executable', '*') |
1500 self.xargs(self.setexec, b'propset', b'svn:executable', b'*') |
1493 self.setexec = [] |
1501 self.setexec = [] |
1494 |
1502 |
1495 fd, messagefile = pycompat.mkstemp(prefix='hg-convert-') |
1503 fd, messagefile = pycompat.mkstemp(prefix=b'hg-convert-') |
1496 fp = os.fdopen(fd, r'wb') |
1504 fp = os.fdopen(fd, r'wb') |
1497 fp.write(util.tonativeeol(commit.desc)) |
1505 fp.write(util.tonativeeol(commit.desc)) |
1498 fp.close() |
1506 fp.close() |
1499 try: |
1507 try: |
1500 output = self.run0( |
1508 output = self.run0( |
1501 'commit', |
1509 b'commit', |
1502 username=stringutil.shortuser(commit.author), |
1510 username=stringutil.shortuser(commit.author), |
1503 file=messagefile, |
1511 file=messagefile, |
1504 encoding='utf-8', |
1512 encoding=b'utf-8', |
1505 ) |
1513 ) |
1506 try: |
1514 try: |
1507 rev = self.commit_re.search(output).group(1) |
1515 rev = self.commit_re.search(output).group(1) |
1508 except AttributeError: |
1516 except AttributeError: |
1509 if not files: |
1517 if not files: |
1510 return parents[0] if parents else 'None' |
1518 return parents[0] if parents else b'None' |
1511 self.ui.warn(_('unexpected svn output:\n')) |
1519 self.ui.warn(_(b'unexpected svn output:\n')) |
1512 self.ui.warn(output) |
1520 self.ui.warn(output) |
1513 raise error.Abort(_('unable to cope with svn output')) |
1521 raise error.Abort(_(b'unable to cope with svn output')) |
1514 if commit.rev: |
1522 if commit.rev: |
1515 self.run( |
1523 self.run( |
1516 'propset', |
1524 b'propset', |
1517 'hg:convert-rev', |
1525 b'hg:convert-rev', |
1518 commit.rev, |
1526 commit.rev, |
1519 revprop=True, |
1527 revprop=True, |
1520 revision=rev, |
1528 revision=rev, |
1521 ) |
1529 ) |
1522 if commit.branch and commit.branch != 'default': |
1530 if commit.branch and commit.branch != b'default': |
1523 self.run( |
1531 self.run( |
1524 'propset', |
1532 b'propset', |
1525 'hg:convert-branch', |
1533 b'hg:convert-branch', |
1526 commit.branch, |
1534 commit.branch, |
1527 revprop=True, |
1535 revprop=True, |
1528 revision=rev, |
1536 revision=rev, |
1529 ) |
1537 ) |
1530 for parent in parents: |
1538 for parent in parents: |