robustcheckout: resolve symbolic revisions from remote (bug 1287506); r?Callek draft
authorGregory Szorc <gps@mozilla.com>
Mon, 18 Jul 2016 10:02:54 -0700
changeset 8931 75956c5c73b84852db20917558cebe6aa6ee03d2
parent 8930 c5522c2e9be41e36b8747ae1482c78a04c8168f7
push id1027
push userbmo:gps@mozilla.com
push dateMon, 18 Jul 2016 17:06:18 +0000
reviewersCallek
bugs1287506
robustcheckout: resolve symbolic revisions from remote (bug 1287506); r?Callek Previously, if we were checkout out a symbolic revision, we would always pull from the remote then resolve that name locally. This is not ideal because the local repo may have other references to that name not on the remote and the local references may be checked out. We change the code to resolve the name on the remote then use the resolved value locally. As part of this, we refactor the code to avoid pulling if the remotely resolved name is locally available. In theory, we may not pull things like phases and obsolescence markers that could have been updated on the remote. However, this is an existing deficiency in the robustcheckout extension. If we want to ensure all remote state is present locally, we should add an --always-pull flag or something. But that's for another bug. MozReview-Commit-ID: 4dSEZITAL2D
hgext/robustcheckout/__init__.py
hgext/robustcheckout/tests/test-revision-branch.t
--- a/hgext/robustcheckout/__init__.py
+++ b/hgext/robustcheckout/__init__.py
@@ -13,16 +13,17 @@ from __future__ import absolute_import
 
 import contextlib
 import errno
 import functools
 import os
 import re
 
 from mercurial.i18n import _
+from mercurial.node import hex
 from mercurial import (
     commands,
     error,
     exchange,
     extensions,
     cmdutil,
     hg,
     scmutil,
@@ -254,27 +255,37 @@ def _docheckout(ui, url, dest, upstream,
     if revision and revision in repo:
         ctx = repo[revision]
 
         if not ctx.hex().startswith(revision):
             raise error.Abort('--revision argument is ambiguous',
                               hint='must be the first 12+ characters of a '
                                    'SHA-1 fragment')
 
+        checkoutrevision = ctx.hex()
         havewantedrev = True
 
     if not havewantedrev:
         ui.write('(pulling to obtain %s)\n' % (revision or branch,))
 
         try:
             remote = hg.peer(repo, {}, url)
             pullrevs = [remote.lookup(revision or branch)]
-            pullop = exchange.pull(repo, remote, heads=pullrevs)
-            if not pullop.rheads:
-                raise error.Abort('unable to pull requested revision')
+            checkoutrevision = hex(pullrevs[0])
+            if branch:
+                ui.warn('(remote resolved %s to %s; '
+                        'result is not deterministic)\n' %
+                        (branch, checkoutrevision))
+
+            if checkoutrevision in repo:
+                ui.warn('(revision already present locally; not pulling)\n')
+            else:
+                pullop = exchange.pull(repo, remote, heads=pullrevs)
+                if not pullop.rheads:
+                    raise error.Abort('unable to pull requested revision')
         except error.Abort as e:
             if e.message == _('repository is unrelated'):
                 ui.warn('(repository is unrelated; deleting)\n')
                 destvfs.rmtree(forcibly=True)
                 return callself()
 
             raise
         except error.RepoError as e:
@@ -298,21 +309,20 @@ def _docheckout(ui, url, dest, upstream,
         if purgeext.purge(ui, repo, all=True, abort_on_err=True,
                           # The function expects all arguments to be
                           # defined.
                           **{'print': None, 'print0': None, 'dirs': None,
                              'files': None}):
             raise error.Abort('error purging')
 
     # Update the working directory.
-    if commands.update(ui, repo, rev=revision or branch, clean=True):
+    if commands.update(ui, repo, rev=checkoutrevision, clean=True):
         raise error.Abort('error updating')
 
-    ctx = repo[revision or branch]
-    ui.write('updated to %s\n' % ctx.hex())
+    ui.write('updated to %s\n' % checkoutrevision)
     return None
 
 
 def extsetup(ui):
     # Ensure required extensions are loaded.
     for ext in ('purge', 'share'):
         try:
             extensions.find(ext)
--- a/hgext/robustcheckout/tests/test-revision-branch.t
+++ b/hgext/robustcheckout/tests/test-revision-branch.t
@@ -38,37 +38,40 @@ Specifying branch argument will checkout
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 3 changesets with 3 changes to 1 files (+1 heads)
   searching for changes
   no changes found
   (pulling to obtain default)
-  no changes found
+  (remote resolved default to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce; result is not deterministic)
+  (revision already present locally; not pulling)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updated to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce
 
 Specifying branch argument will always attempt to pull because branch revisions can change
 
   $ hg robustcheckout http://localhost:$HGPORT/repo0 dest --branch default
   ensuring http://localhost:$HGPORT/repo0@default is available at dest
   (existing repository shared store: $TESTTMP/share/b8b78f0253d822e33ba652fd3d80a5c0837cfdf3/.hg)
   (pulling to obtain default)
-  no changes found
+  (remote resolved default to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce; result is not deterministic)
+  (revision already present locally; not pulling)
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updated to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce
 
 Updating to another branch works
 
   $ hg robustcheckout http://localhost:$HGPORT/repo0 dest --branch branch1
   ensuring http://localhost:$HGPORT/repo0@branch1 is available at dest
   (existing repository shared store: $TESTTMP/share/b8b78f0253d822e33ba652fd3d80a5c0837cfdf3/.hg)
   (pulling to obtain branch1)
-  no changes found
+  (remote resolved branch1 to aada1b3e573f7272bb2ef93b34acbf0f77c69d44; result is not deterministic)
+  (revision already present locally; not pulling)
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   updated to aada1b3e573f7272bb2ef93b34acbf0f77c69d44
 
 Specifying revision will switch away from branch
 
   $ hg robustcheckout http://localhost:$HGPORT/repo0 dest --revision 5d6cdc75a09b
   ensuring http://localhost:$HGPORT/repo0@5d6cdc75a09b is available at dest
   (existing repository shared store: $TESTTMP/share/b8b78f0253d822e33ba652fd3d80a5c0837cfdf3/.hg)
@@ -101,17 +104,16 @@ revision
   $ hg -q commit -A -m 'default head 2'
   $ cd ..
 
   $ hg -R server/repo0 log -r default -T '{node}\n'
   5d6cdc75a09bcccf76f9339a28e1d89360c59dce
   $ hg -R dest log -r default -T '{node}\n'
   6f89935a511842d2a7393cad33ef93bf793b1db2
 
-FIXME
-
   $ hg robustcheckout http://localhost:$HGPORT/repo0 dest --branch default
   ensuring http://localhost:$HGPORT/repo0@default is available at dest
   (existing repository shared store: $TESTTMP/share/b8b78f0253d822e33ba652fd3d80a5c0837cfdf3/.hg)
   (pulling to obtain default)
-  no changes found
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  updated to 6f89935a511842d2a7393cad33ef93bf793b1db2
+  (remote resolved default to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce; result is not deterministic)
+  (revision already present locally; not pulling)
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  updated to 5d6cdc75a09bcccf76f9339a28e1d89360c59dce