[PATCH 1 of 1] svn subrepos: try to work around checkout obstructions (issue2752)

Augie Fackler durin42 at gmail.com
Fri Jun 17 10:28:20 CDT 2011


# HG changeset patch
# User Augie Fackler <durin42 at gmail.com>
# Date 1308324448 18000
# Node ID 1329c2aa495166ebe97d061d5a4bfc90720ff3d2
# Parent  ba3c36cea66e7a9cc0bb274b7f7b73ad65212df7
svn subrepos: try to work around checkout obstructions (issue2752)

I'm a little confused that the last test case I added passes as it's
written now. I would have expected the unclean working copy to block
the update, but it seems that the 'hg update tip' call passes
overwrite=True into get(), so we run 'svn revert --recursive' right
away. Is that correct behavior?

diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -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):
diff --git a/tests/test-subrepo-svn.t b/tests/test-subrepo-svn.t
--- a/tests/test-subrepo-svn.t
+++ b/tests/test-subrepo-svn.t
@@ -489,3 +489,40 @@
   $ if "$TESTDIR/hghave" -q svn15; then
   > hg up 2 >/dev/null 2>&1 || echo update failed
   > fi
+
+Modify one of the externals to point to a different path so we can
+test having obstructions when switching branches on checkout:
+  $ hg checkout tip
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo "obstruct =        [svn]       $SVNREPO/externals" >> .hgsub
+  $ svn co -r5 --quiet "$SVNREPO"/externals obstruct
+  $ hg commit -m 'Start making obstructed wc'
+  committing subrepository obstruct
+  $ hg book other
+  $ hg co -r 'p1(tip)'
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo "obstruct =        [svn]       $SVNREPO/src" >> .hgsub
+  $ svn co -r5 --quiet "$SVNREPO"/src obstruct
+  $ hg commit -m 'Other branch which will be obstructed'
+  committing subrepository obstruct
+  created new head
+
+Switching back to the head where we have another path mapped to the
+same subrepo should work if the subrepo is clean.
+  $ hg co other
+  A    $TESTTMP/rebaserepo/obstruct/other
+  Checked out revision 1.
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+This is surprising, but is also correct based on the current code:
+  $ echo "updating should (maybe) fail" > obstruct/other
+  $ hg co tip
+  A    $TESTTMP/rebaserepo/obstruct/alpha
+   U   $TESTTMP/rebaserepo/obstruct
+  
+  Fetching external item into '$TESTTMP/rebaserepo/obstruct/externals'
+  A    $TESTTMP/rebaserepo/obstruct/externals/other
+  Checked out external at revision 1.
+  
+  Checked out revision 5.
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved


More information about the Mercurial-devel mailing list