mercurial/subrepo.py
changeset 14664 0ae98cd2a83f
parent 14556 517e1d88bf7e
child 14766 4f56b7530eab
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]