diff 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
line wrap: on
line diff
--- a/mercurial/subrepo.py	Fri Jun 17 15:21:02 2011 -0500
+++ b/mercurial/subrepo.py	Fri Jun 17 15:22:50 2011 -0500
@@ -526,7 +526,7 @@
         self._ctx = ctx
         self._ui = ctx._repo.ui
 
-    def _svncommand(self, commands, filename=''):
+    def _svncommand(self, commands, filename='', failok=False):
         cmd = ['svn']
         extrakw = {}
         if not self._ui.interactive():
@@ -551,15 +551,16 @@
                               universal_newlines=True, env=env, **extrakw)
         stdout, stderr = p.communicate()
         stderr = stderr.strip()
-        if p.returncode:
-            raise util.Abort(stderr or 'exited with code %d' % p.returncode)
-        if stderr:
-            self._ui.warn(stderr + '\n')
-        return stdout
+        if not failok:
+            if p.returncode:
+                raise util.Abort(stderr or 'exited with code %d' % p.returncode)
+            if stderr:
+                self._ui.warn(stderr + '\n')
+        return stdout, stderr
 
     @propertycache
     def _svnversion(self):
-        output = self._svncommand(['--version'], filename=None)
+        output, err = self._svncommand(['--version'], filename=None)
         m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
         if not m:
             raise util.Abort(_('cannot retrieve svn tool version'))
@@ -569,7 +570,7 @@
         # Get the working directory revision as well as the last
         # commit revision so we can compare the subrepo state with
         # both. We used to store the working directory one.
-        output = self._svncommand(['info', '--xml'])
+        output, err = self._svncommand(['info', '--xml'])
         doc = xml.dom.minidom.parseString(output)
         entries = doc.getElementsByTagName('entry')
         lastrev, rev = '0', '0'
@@ -588,7 +589,7 @@
         if the working directory was changed, and extchanges is
         True if any of these changes concern an external entry.
         """
-        output = self._svncommand(['status', '--xml'])
+        output, err = self._svncommand(['status', '--xml'])
         externals, changes = [], []
         doc = xml.dom.minidom.parseString(output)
         for e in doc.getElementsByTagName('entry'):
@@ -623,13 +624,13 @@
         if extchanged:
             # Do not try to commit externals
             raise util.Abort(_('cannot commit svn externals'))
-        commitinfo = self._svncommand(['commit', '-m', text])
+        commitinfo, err = self._svncommand(['commit', '-m', text])
         self._ui.status(commitinfo)
         newrev = re.search('Committed revision ([0-9]+).', commitinfo)
         if not newrev:
             raise util.Abort(commitinfo.splitlines()[-1])
         newrev = newrev.groups()[0]
-        self._ui.status(self._svncommand(['update', '-r', newrev]))
+        self._ui.status(self._svncommand(['update', '-r', newrev])[0])
         return newrev
 
     def remove(self):
@@ -663,9 +664,15 @@
         if self._svnversion >= (1, 5):
             args.append('--force')
         args.extend([state[0], '--revision', state[1]])
-        status = self._svncommand(args)
+        status, err = self._svncommand(args, failok=True)
         if not re.search('Checked out revision [0-9]+.', status):
-            raise util.Abort(status.splitlines()[-1])
+            if ('is already a working copy for a different URL' in err
+                and (self._wcchanged() == (False, False))):
+                # obstructed but clean working copy, so just blow it away.
+                self.remove()
+                self.get(state, overwrite=False)
+                return
+            raise util.Abort((status or err).splitlines()[-1])
         self._ui.status(status)
 
     def merge(self, state):