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] |