[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