Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/subrepo.py @ 14664:0ae98cd2a83f
svn subrepos: work around checkout obstructions (issue2752)
We do this by ensuring the working copy is clean and then blowing away
the working copy and replacing it with one from the desired path. We
could probably use 'svn switch' to do this more efficiently, but
there's some subtle logic required to get that right and this is
more likely to work reliably.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Fri, 17 Jun 2011 15:22:50 -0500 |
parents | 517e1d88bf7e |
children | 4f56b7530eab |
comparison
equal
deleted
inserted
replaced
14663:88cb01c4575e | 14664:0ae98cd2a83f |
---|---|
524 self._path = path | 524 self._path = path |
525 self._state = state | 525 self._state = state |
526 self._ctx = ctx | 526 self._ctx = ctx |
527 self._ui = ctx._repo.ui | 527 self._ui = ctx._repo.ui |
528 | 528 |
529 def _svncommand(self, commands, filename=''): | 529 def _svncommand(self, commands, filename='', failok=False): |
530 cmd = ['svn'] | 530 cmd = ['svn'] |
531 extrakw = {} | 531 extrakw = {} |
532 if not self._ui.interactive(): | 532 if not self._ui.interactive(): |
533 # Making stdin be a pipe should prevent svn from behaving | 533 # Making stdin be a pipe should prevent svn from behaving |
534 # interactively even if we can't pass --non-interactive. | 534 # interactively even if we can't pass --non-interactive. |
549 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds, | 549 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds, |
550 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 550 stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
551 universal_newlines=True, env=env, **extrakw) | 551 universal_newlines=True, env=env, **extrakw) |
552 stdout, stderr = p.communicate() | 552 stdout, stderr = p.communicate() |
553 stderr = stderr.strip() | 553 stderr = stderr.strip() |
554 if p.returncode: | 554 if not failok: |
555 raise util.Abort(stderr or 'exited with code %d' % p.returncode) | 555 if p.returncode: |
556 if stderr: | 556 raise util.Abort(stderr or 'exited with code %d' % p.returncode) |
557 self._ui.warn(stderr + '\n') | 557 if stderr: |
558 return stdout | 558 self._ui.warn(stderr + '\n') |
559 return stdout, stderr | |
559 | 560 |
560 @propertycache | 561 @propertycache |
561 def _svnversion(self): | 562 def _svnversion(self): |
562 output = self._svncommand(['--version'], filename=None) | 563 output, err = self._svncommand(['--version'], filename=None) |
563 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output) | 564 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output) |
564 if not m: | 565 if not m: |
565 raise util.Abort(_('cannot retrieve svn tool version')) | 566 raise util.Abort(_('cannot retrieve svn tool version')) |
566 return (int(m.group(1)), int(m.group(2))) | 567 return (int(m.group(1)), int(m.group(2))) |
567 | 568 |
568 def _wcrevs(self): | 569 def _wcrevs(self): |
569 # Get the working directory revision as well as the last | 570 # Get the working directory revision as well as the last |
570 # commit revision so we can compare the subrepo state with | 571 # commit revision so we can compare the subrepo state with |
571 # both. We used to store the working directory one. | 572 # both. We used to store the working directory one. |
572 output = self._svncommand(['info', '--xml']) | 573 output, err = self._svncommand(['info', '--xml']) |
573 doc = xml.dom.minidom.parseString(output) | 574 doc = xml.dom.minidom.parseString(output) |
574 entries = doc.getElementsByTagName('entry') | 575 entries = doc.getElementsByTagName('entry') |
575 lastrev, rev = '0', '0' | 576 lastrev, rev = '0', '0' |
576 if entries: | 577 if entries: |
577 rev = str(entries[0].getAttribute('revision')) or '0' | 578 rev = str(entries[0].getAttribute('revision')) or '0' |
586 def _wcchanged(self): | 587 def _wcchanged(self): |
587 """Return (changes, extchanges) where changes is True | 588 """Return (changes, extchanges) where changes is True |
588 if the working directory was changed, and extchanges is | 589 if the working directory was changed, and extchanges is |
589 True if any of these changes concern an external entry. | 590 True if any of these changes concern an external entry. |
590 """ | 591 """ |
591 output = self._svncommand(['status', '--xml']) | 592 output, err = self._svncommand(['status', '--xml']) |
592 externals, changes = [], [] | 593 externals, changes = [], [] |
593 doc = xml.dom.minidom.parseString(output) | 594 doc = xml.dom.minidom.parseString(output) |
594 for e in doc.getElementsByTagName('entry'): | 595 for e in doc.getElementsByTagName('entry'): |
595 s = e.getElementsByTagName('wc-status') | 596 s = e.getElementsByTagName('wc-status') |
596 if not s: | 597 if not s: |
621 if not changed: | 622 if not changed: |
622 return self._wcrev() | 623 return self._wcrev() |
623 if extchanged: | 624 if extchanged: |
624 # Do not try to commit externals | 625 # Do not try to commit externals |
625 raise util.Abort(_('cannot commit svn externals')) | 626 raise util.Abort(_('cannot commit svn externals')) |
626 commitinfo = self._svncommand(['commit', '-m', text]) | 627 commitinfo, err = self._svncommand(['commit', '-m', text]) |
627 self._ui.status(commitinfo) | 628 self._ui.status(commitinfo) |
628 newrev = re.search('Committed revision ([0-9]+).', commitinfo) | 629 newrev = re.search('Committed revision ([0-9]+).', commitinfo) |
629 if not newrev: | 630 if not newrev: |
630 raise util.Abort(commitinfo.splitlines()[-1]) | 631 raise util.Abort(commitinfo.splitlines()[-1]) |
631 newrev = newrev.groups()[0] | 632 newrev = newrev.groups()[0] |
632 self._ui.status(self._svncommand(['update', '-r', newrev])) | 633 self._ui.status(self._svncommand(['update', '-r', newrev])[0]) |
633 return newrev | 634 return newrev |
634 | 635 |
635 def remove(self): | 636 def remove(self): |
636 if self.dirty(): | 637 if self.dirty(): |
637 self._ui.warn(_('not removing repo %s because ' | 638 self._ui.warn(_('not removing repo %s because ' |
661 self._svncommand(['revert', '--recursive']) | 662 self._svncommand(['revert', '--recursive']) |
662 args = ['checkout'] | 663 args = ['checkout'] |
663 if self._svnversion >= (1, 5): | 664 if self._svnversion >= (1, 5): |
664 args.append('--force') | 665 args.append('--force') |
665 args.extend([state[0], '--revision', state[1]]) | 666 args.extend([state[0], '--revision', state[1]]) |
666 status = self._svncommand(args) | 667 status, err = self._svncommand(args, failok=True) |
667 if not re.search('Checked out revision [0-9]+.', status): | 668 if not re.search('Checked out revision [0-9]+.', status): |
668 raise util.Abort(status.splitlines()[-1]) | 669 if ('is already a working copy for a different URL' in err |
670 and (self._wcchanged() == (False, False))): | |
671 # obstructed but clean working copy, so just blow it away. | |
672 self.remove() | |
673 self.get(state, overwrite=False) | |
674 return | |
675 raise util.Abort((status or err).splitlines()[-1]) | |
669 self._ui.status(status) | 676 self._ui.status(status) |
670 | 677 |
671 def merge(self, state): | 678 def merge(self, state): |
672 old = self._state[1] | 679 old = self._state[1] |
673 new = state[1] | 680 new = state[1] |