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]