gclient: in managed mode, warn if .gclient has a mismatched URL

BUG=

Review URL: https://codereview.chromium.org/195913002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@258617 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gclient.py b/gclient.py
index 86b742f..8dcb93b 100755
--- a/gclient.py
+++ b/gclient.py
@@ -1044,6 +1044,40 @@
     self._root_dir = root_dir
     self.config_content = None
 
+  def _CheckConfig(self):
+    """Verify that the config matches the state of the existing checked-out
+    solutions."""
+    for dep in self.dependencies:
+      if dep.managed and dep.url:
+        scm = gclient_scm.CreateSCM(dep.url, self.root_dir, dep.name)
+        actual_url = scm.GetActualRemoteURL()
+        if actual_url and not scm.DoesRemoteURLMatch():
+          print >> sys.stderr, ('''
+################################################################################
+################################### WARNING! ###################################
+################################################################################
+
+Your .gclient file seems to be broken. The requested URL is different from what
+is actually checked out in %(checkout_path)s. In the future this will be an
+error.
+
+Expected: %(expected_url)s (%(expected_scm)s)
+Actual:   %(actual_url)s (%(actual_scm)s)
+
+You should ensure that the URL listed in .gclient is correct and either change
+it or fix the checkout. If you're managing your own git checkout in
+%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
+want to set 'managed': False in .gclient.
+
+################################################################################
+################################################################################
+################################################################################
+'''  % {'checkout_path': os.path.join(self.root_dir, dep.name),
+        'expected_url': dep.url,
+        'expected_scm': gclient_scm.GetScmName(dep.url),
+        'actual_url': actual_url,
+        'actual_scm': gclient_scm.GetScmName(actual_url)})
+
   def SetConfig(self, content):
     assert not self.dependencies
     config_dict = {}
@@ -1294,6 +1328,7 @@
             gclient_utils.rmtree(e_dir)
       # record the current list of entries for next time
       self._SaveEntries()
+    self._CheckConfig()
     return 0
 
   def PrintRevInfo(self):
diff --git a/gclient_scm.py b/gclient_scm.py
index e8ab140..76b4d8d 100644
--- a/gclient_scm.py
+++ b/gclient_scm.py
@@ -8,6 +8,7 @@
 import os
 import posixpath
 import re
+import shlex
 import sys
 import tempfile
 import traceback
@@ -139,6 +140,34 @@
 
     return getattr(self, command)(options, args, file_list)
 
+  def GetActualRemoteURL(self):
+    """Attempt to determine the remote URL for this SCMWrapper."""
+    try:
+      return shlex.split(scm.GIT.Capture(
+          ['config', '--local', '--get-regexp', r'remote.*.url'],
+          self.checkout_path))[1]
+    except Exception:
+      pass
+    try:
+      return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL']
+    except Exception:
+      pass
+    return None
+
+  def DoesRemoteURLMatch(self):
+    """Determine whether the remote URL of this checkout is the expected URL."""
+    if not os.path.exists(self.checkout_path):
+      # A checkout which doesn't exist can't be broken.
+      return True
+
+    actual_remote_url = self.GetActualRemoteURL()
+    if actual_remote_url:
+      return actual_remote_url.rstrip('/') == self.url.rstrip('/')
+    else:
+      # This may occur if the self.checkout_path exists but does not contain a
+      # valid git or svn checkout.
+      return False
+
 
 class GitWrapper(SCMWrapper):
   """Wrapper for Git"""
diff --git a/tests/gclient_test.py b/tests/gclient_test.py
index b758718..18876cc 100755
--- a/tests/gclient_test.py
+++ b/tests/gclient_test.py
@@ -44,6 +44,13 @@
   def FullUrlForRelativeUrl(self, url):
     return self.url + url
 
+  # pylint: disable=R0201
+  def DoesRemoteURLMatch(self):
+    return True
+
+  def GetActualRemoteURL(self):
+    return self.url
+
 
 class GclientTest(trial_dir.TestCase):
   def setUp(self):
@@ -250,7 +257,7 @@
     topdir = self.root_dir
     gclient_fn = os.path.join(topdir, '.gclient')
     fh = open(gclient_fn, 'w')
-    print >> fh, 'solutions = [{"name":"top","url":"svn://svn.top.com/top"}]'
+    print >> fh, 'solutions = [{"name":"top","url":"svn://example.com/top"}]'
     fh.close()
     subdir_fn = os.path.join(topdir, 'top')
     os.mkdir(subdir_fn)
@@ -281,9 +288,9 @@
     gclient_fn = os.path.join(topdir, '.gclient')
     fh = open(gclient_fn, 'w')
     extra_hooks = [{'name': 'append', 'pattern':'.', 'action':['supercmd']}]
-    print >> fh, ('solutions = [{"name":"top","url":"svn://svn.top.com/top",'
+    print >> fh, ('solutions = [{"name":"top","url":"svn://example.com/top",'
         '"custom_hooks": %s},' ) % repr(extra_hooks + [{'name': 'skip'}])
-    print >> fh, '{"name":"bottom","url":"svn://svn.top.com/bottom"}]'
+    print >> fh, '{"name":"bottom","url":"svn://example.com/bottom"}]'
     fh.close()
     subdir_fn = os.path.join(topdir, 'top')
     os.mkdir(subdir_fn)