Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Gclient-specific SCM-specific operations.""" | 5 """Gclient-specific SCM-specific operations.""" |
| 6 | 6 |
| 7 # TODO(borenet): Uncomment this once SCMWrapper._DeleteOrMove is enabled. | |
| 8 #import errno | |
|
iannucci
2014/04/04 00:49:54
Probably should just leave these comments out and
borenet
2014/04/07 14:24:02
Done.
| |
| 7 import logging | 9 import logging |
| 8 import os | 10 import os |
| 9 import posixpath | 11 import posixpath |
| 10 import re | 12 import re |
| 11 import shlex | 13 import shlex |
| 12 import sys | 14 import sys |
| 13 import tempfile | 15 import tempfile |
| 14 import traceback | 16 import traceback |
| 15 import urlparse | 17 import urlparse |
| 16 | 18 |
| 17 import download_from_google_storage | 19 import download_from_google_storage |
| 18 import gclient_utils | 20 import gclient_utils |
| 19 import scm | 21 import scm |
| 22 # TODO(borenet): Uncomment this once SCMWrapper._DeleteOrMove is enabled. | |
| 23 #import shutil | |
| 20 import subprocess2 | 24 import subprocess2 |
| 21 | 25 |
| 22 | 26 |
| 23 THIS_FILE_PATH = os.path.abspath(__file__) | 27 THIS_FILE_PATH = os.path.abspath(__file__) |
| 24 | 28 |
| 25 GSUTIL_DEFAULT_PATH = os.path.join( | 29 GSUTIL_DEFAULT_PATH = os.path.join( |
| 26 os.path.dirname(os.path.abspath(__file__)), | 30 os.path.dirname(os.path.abspath(__file__)), |
| 27 'third_party', 'gsutil', 'gsutil') | 31 'third_party', 'gsutil', 'gsutil') |
| 28 | 32 |
| 29 | 33 |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 | 162 |
| 159 actual_remote_url = self.GetActualRemoteURL() | 163 actual_remote_url = self.GetActualRemoteURL() |
| 160 if actual_remote_url: | 164 if actual_remote_url: |
| 161 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') | 165 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') |
| 162 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) | 166 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) |
| 163 else: | 167 else: |
| 164 # This may occur if the self.checkout_path exists but does not contain a | 168 # This may occur if the self.checkout_path exists but does not contain a |
| 165 # valid git or svn checkout. | 169 # valid git or svn checkout. |
| 166 return False | 170 return False |
| 167 | 171 |
| 172 # TODO(borenet): Remove this once SCMWrapper._DeleteOrMove is enabled. | |
| 173 # pylint: disable=R0201 | |
| 174 def _DeleteOrMove(self, force): | |
| 175 """Delete the checkout directory or move it out of the way. | |
| 176 | |
| 177 Args: | |
| 178 force: bool; if True, delete the directory. Otherwise, just move it. | |
| 179 """ | |
| 180 # TODO(borenet): Uncomment the below implementation once we're sure nobody | |
| 181 # will trigger it accidentally. | |
| 182 gclient_utils.AddWarning('WARNING: Upcoming changes would cause %s to be ' | |
|
iannucci
2014/04/04 00:49:54
Prep the cl to turn this feature on, and mention i
borenet
2014/04/07 14:24:02
Done.
| |
| 183 'deleted or moved to the side. This is intended ' | |
| 184 'to ease changes to DEPS in the future. If you ' | |
| 185 'are seeing this warning and haven\'t changed the ' | |
| 186 'DEPS file, please contact borenet@ immediately.' | |
| 187 % self.checkout_path) | |
| 188 # if force: | |
| 189 # print('_____ Conflicting directory found in %s. Removing.' | |
| 190 # % self.checkout_path) | |
| 191 # gclient_utils.rmtree(self.checkout_path) | |
| 192 # else: | |
| 193 # bad_scm_dir = os.path.join(self._root_dir, '_bad_scm', | |
| 194 # os.path.dirname(self.relpath)) | |
| 195 # | |
| 196 # try: | |
| 197 # os.makedirs(bad_scm_dir) | |
| 198 # except OSError as e: | |
| 199 # if e.errno != errno.EEXIST: | |
| 200 # raise | |
| 201 # | |
| 202 # dest_path = tempfile.mkdtemp( | |
| 203 # prefix=os.path.basename(self.relpath), | |
| 204 # dir=bad_scm_dir) | |
| 205 # print('_____ Conflicting directory found in %s. Moving to %s.' | |
| 206 # % (self.checkout_path, dest_path)) | |
| 207 # gclient_utils.AddWarning('Conflicting directory %s moved to %s.' | |
| 208 # % (self.checkout_path, dest_path)) | |
| 209 # shutil.move(self.checkout_path, dest_path) | |
| 210 | |
| 168 | 211 |
| 169 class GitWrapper(SCMWrapper): | 212 class GitWrapper(SCMWrapper): |
| 170 """Wrapper for Git""" | 213 """Wrapper for Git""" |
| 171 name = 'git' | 214 name = 'git' |
| 172 remote = 'origin' | 215 remote = 'origin' |
| 173 | 216 |
| 174 cache_dir = None | 217 cache_dir = None |
| 175 | 218 |
| 176 def __init__(self, url=None, root_dir=None, relpath=None): | 219 def __init__(self, url=None, root_dir=None, relpath=None): |
| 177 """Removes 'git+' fake prefix from git URL.""" | 220 """Removes 'git+' fake prefix from git URL.""" |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 319 # For compatibility with old naming, translate 'origin' to 'refs/heads' | 362 # For compatibility with old naming, translate 'origin' to 'refs/heads' |
| 320 revision = revision.replace(self.remote + '/', 'refs/heads/') | 363 revision = revision.replace(self.remote + '/', 'refs/heads/') |
| 321 rev_type = "branch" | 364 rev_type = "branch" |
| 322 else: | 365 else: |
| 323 # hash is also a tag, only make a distinction at checkout | 366 # hash is also a tag, only make a distinction at checkout |
| 324 rev_type = "hash" | 367 rev_type = "hash" |
| 325 | 368 |
| 326 if (not os.path.exists(self.checkout_path) or | 369 if (not os.path.exists(self.checkout_path) or |
| 327 (os.path.isdir(self.checkout_path) and | 370 (os.path.isdir(self.checkout_path) and |
| 328 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 371 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
| 372 if (os.path.isdir(self.checkout_path) and | |
| 373 not os.path.exists(os.path.join(self.checkout_path, '.git'))): | |
| 374 self._DeleteOrMove(options.force) | |
| 329 self._Clone(revision, url, options) | 375 self._Clone(revision, url, options) |
| 330 self.UpdateSubmoduleConfig() | 376 self.UpdateSubmoduleConfig() |
| 331 if file_list is not None: | 377 if file_list is not None: |
| 332 files = self._Capture(['ls-files']).splitlines() | 378 files = self._Capture(['ls-files']).splitlines() |
| 333 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 379 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 334 if not verbose: | 380 if not verbose: |
| 335 # Make the output a little prettier. It's nice to have some whitespace | 381 # Make the output a little prettier. It's nice to have some whitespace |
| 336 # between projects when cloning. | 382 # between projects when cloning. |
| 337 print('') | 383 print('') |
| 338 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 384 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 339 | 385 |
| 340 if not managed: | 386 if not managed: |
| 341 self._UpdateBranchHeads(options, fetch=False) | 387 self._UpdateBranchHeads(options, fetch=False) |
| 342 self.UpdateSubmoduleConfig() | 388 self.UpdateSubmoduleConfig() |
| 343 print ('________ unmanaged solution; skipping %s' % self.relpath) | 389 print ('________ unmanaged solution; skipping %s' % self.relpath) |
| 344 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 390 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 345 | 391 |
| 346 if not os.path.exists(os.path.join(self.checkout_path, '.git')): | |
| 347 raise gclient_utils.Error('\n____ %s%s\n' | |
| 348 '\tPath is not a git repo. No .git dir.\n' | |
| 349 '\tTo resolve:\n' | |
| 350 '\t\trm -rf %s\n' | |
| 351 '\tAnd run gclient sync again\n' | |
| 352 % (self.relpath, rev_str, self.relpath)) | |
| 353 | |
| 354 # See if the url has changed (the unittests use git://foo for the url, let | 392 # See if the url has changed (the unittests use git://foo for the url, let |
| 355 # that through). | 393 # that through). |
| 356 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) | 394 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) |
| 357 return_early = False | 395 return_early = False |
| 358 # TODO(maruel): Delete url != 'git://foo' since it's just to make the | 396 # TODO(maruel): Delete url != 'git://foo' since it's just to make the |
| 359 # unit test pass. (and update the comment above) | 397 # unit test pass. (and update the comment above) |
| 360 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. | 398 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. |
| 361 # This allows devs to use experimental repos which have a different url | 399 # This allows devs to use experimental repos which have a different url |
| 362 # but whose branch(s) are the same as official repos. | 400 # but whose branch(s) are the same as official repos. |
| 363 if (current_url != url and | 401 if (current_url.rstrip('/') != url.rstrip('/') and |
| 364 url != 'git://foo' and | 402 url != 'git://foo' and |
| 365 subprocess2.capture( | 403 subprocess2.capture( |
| 366 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 404 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
| 367 cwd=self.checkout_path).strip() != 'False'): | 405 cwd=self.checkout_path).strip() != 'False'): |
| 368 print('_____ switching %s to a new upstream' % self.relpath) | 406 print('_____ switching %s to a new upstream' % self.relpath) |
| 369 # Make sure it's clean | 407 # Make sure it's clean |
| 370 self._CheckClean(rev_str) | 408 self._CheckClean(rev_str) |
| 371 # Switch over to the new upstream | 409 # Switch over to the new upstream |
| 372 self._Run(['remote', 'set-url', self.remote, url], options) | 410 self._Run(['remote', 'set-url', self.remote, url], options) |
| 373 self._FetchAndReset(revision, file_list, options) | 411 self._FetchAndReset(revision, file_list, options) |
| (...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1017 filter_fn=SvnDiffFilterer(self.relpath).Filter) | 1055 filter_fn=SvnDiffFilterer(self.relpath).Filter) |
| 1018 | 1056 |
| 1019 def update(self, options, args, file_list): | 1057 def update(self, options, args, file_list): |
| 1020 """Runs svn to update or transparently checkout the working copy. | 1058 """Runs svn to update or transparently checkout the working copy. |
| 1021 | 1059 |
| 1022 All updated files will be appended to file_list. | 1060 All updated files will be appended to file_list. |
| 1023 | 1061 |
| 1024 Raises: | 1062 Raises: |
| 1025 Error: if can't get URL for relative path. | 1063 Error: if can't get URL for relative path. |
| 1026 """ | 1064 """ |
| 1027 # Only update if git or hg is not controlling the directory. | 1065 # Only update if hg is not controlling the directory. |
| 1028 git_path = os.path.join(self.checkout_path, '.git') | |
| 1029 if os.path.exists(git_path): | |
| 1030 print('________ found .git directory; skipping %s' % self.relpath) | |
| 1031 return | |
| 1032 | |
| 1033 hg_path = os.path.join(self.checkout_path, '.hg') | 1066 hg_path = os.path.join(self.checkout_path, '.hg') |
| 1034 if os.path.exists(hg_path): | 1067 if os.path.exists(hg_path): |
| 1035 print('________ found .hg directory; skipping %s' % self.relpath) | 1068 print('________ found .hg directory; skipping %s' % self.relpath) |
| 1036 return | 1069 return |
| 1037 | 1070 |
| 1038 if args: | 1071 if args: |
| 1039 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 1072 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
| 1040 | 1073 |
| 1041 # revision is the revision to match. It is None if no revision is specified, | 1074 # revision is the revision to match. It is None if no revision is specified, |
| 1042 # i.e. the 'deps ain't pinned'. | 1075 # i.e. the 'deps ain't pinned'. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 1053 # Reconstruct the url. | 1086 # Reconstruct the url. |
| 1054 url = '%s@%s' % (url, revision) | 1087 url = '%s@%s' % (url, revision) |
| 1055 rev_str = ' at %s' % revision | 1088 rev_str = ' at %s' % revision |
| 1056 else: | 1089 else: |
| 1057 managed = False | 1090 managed = False |
| 1058 revision = None | 1091 revision = None |
| 1059 else: | 1092 else: |
| 1060 forced_revision = False | 1093 forced_revision = False |
| 1061 rev_str = '' | 1094 rev_str = '' |
| 1062 | 1095 |
| 1096 exists = os.path.exists(self.checkout_path) | |
| 1097 if exists and managed: | |
| 1098 # Git is only okay if it's a git-svn checkout of the right repo. | |
| 1099 if scm.GIT.IsGitSvn(self.checkout_path): | |
| 1100 remote_url = scm.GIT.Capture(['config', '--local', '--get', | |
| 1101 'svn-remote.svn.url'], | |
| 1102 cwd=self.checkout_path).rstrip() | |
| 1103 if remote_url.rstrip('/') == base_url.rstrip('/'): | |
| 1104 print('\n_____ %s looks like a git-svn checkout. Skipping.' | |
| 1105 % self.relpath) | |
| 1106 return # TODO(borenet): Get the svn revision number? | |
| 1107 | |
| 1063 # Get the existing scm url and the revision number of the current checkout. | 1108 # Get the existing scm url and the revision number of the current checkout. |
| 1064 exists = os.path.exists(self.checkout_path) | |
| 1065 if exists and managed: | 1109 if exists and managed: |
| 1066 try: | 1110 try: |
| 1067 from_info = scm.SVN.CaptureLocalInfo( | 1111 from_info = scm.SVN.CaptureLocalInfo( |
| 1068 [], os.path.join(self.checkout_path, '.')) | 1112 [], os.path.join(self.checkout_path, '.')) |
| 1069 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1113 except (gclient_utils.Error, subprocess2.CalledProcessError): |
| 1070 if options.reset and options.delete_unversioned_trees: | 1114 self._DeleteOrMove(options.force) |
| 1071 print 'Removing troublesome path %s' % self.checkout_path | 1115 exists = False |
| 1072 gclient_utils.rmtree(self.checkout_path) | |
| 1073 exists = False | |
| 1074 else: | |
| 1075 msg = ('Can\'t update/checkout %s if an unversioned directory is ' | |
| 1076 'present. Delete the directory and try again.') | |
| 1077 raise gclient_utils.Error(msg % self.checkout_path) | |
| 1078 | 1116 |
| 1079 BASE_URLS = { | 1117 BASE_URLS = { |
| 1080 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', | 1118 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', |
| 1081 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', | 1119 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', |
| 1082 } | 1120 } |
| 1083 WHITELISTED_ROOTS = [ | 1121 WHITELISTED_ROOTS = [ |
| 1084 'svn://svn.chromium.org', | 1122 'svn://svn.chromium.org', |
| 1085 'svn://svn-mirror.golo.chromium.org', | 1123 'svn://svn-mirror.golo.chromium.org', |
| 1086 ] | 1124 ] |
| 1087 if not exists: | 1125 if not exists: |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1147 | 1185 |
| 1148 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) | 1186 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) |
| 1149 # We need to checkout. | 1187 # We need to checkout. |
| 1150 command = ['checkout', url, self.checkout_path] | 1188 command = ['checkout', url, self.checkout_path] |
| 1151 command = self._AddAdditionalUpdateFlags(command, options, revision) | 1189 command = self._AddAdditionalUpdateFlags(command, options, revision) |
| 1152 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 1190 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
| 1153 return self.Svnversion() | 1191 return self.Svnversion() |
| 1154 | 1192 |
| 1155 if not managed: | 1193 if not managed: |
| 1156 print ('________ unmanaged solution; skipping %s' % self.relpath) | 1194 print ('________ unmanaged solution; skipping %s' % self.relpath) |
| 1157 return self.Svnversion() | 1195 if os.path.exists(os.path.join(self.checkout_path, '.svn')): |
| 1196 return self.Svnversion() | |
| 1197 return | |
| 1158 | 1198 |
| 1159 if 'URL' not in from_info: | 1199 if 'URL' not in from_info: |
| 1160 raise gclient_utils.Error( | 1200 raise gclient_utils.Error( |
| 1161 ('gclient is confused. Couldn\'t get the url for %s.\n' | 1201 ('gclient is confused. Couldn\'t get the url for %s.\n' |
| 1162 'Try using @unmanaged.\n%s') % ( | 1202 'Try using @unmanaged.\n%s') % ( |
| 1163 self.checkout_path, from_info)) | 1203 self.checkout_path, from_info)) |
| 1164 | 1204 |
| 1165 # Look for locked directories. | 1205 # Look for locked directories. |
| 1166 dir_info = scm.SVN.CaptureStatus( | 1206 dir_info = scm.SVN.CaptureStatus( |
| 1167 None, os.path.join(self.checkout_path, '.')) | 1207 None, os.path.join(self.checkout_path, '.')) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 1190 if d[0][0] == '!': | 1230 if d[0][0] == '!': |
| 1191 print 'You can pass --force to enable automatic removal.' | 1231 print 'You can pass --force to enable automatic removal.' |
| 1192 raise e | 1232 raise e |
| 1193 | 1233 |
| 1194 # Retrieve the current HEAD version because svn is slow at null updates. | 1234 # Retrieve the current HEAD version because svn is slow at null updates. |
| 1195 if options.manually_grab_svn_rev and not revision: | 1235 if options.manually_grab_svn_rev and not revision: |
| 1196 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) | 1236 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) |
| 1197 revision = str(from_info_live['Revision']) | 1237 revision = str(from_info_live['Revision']) |
| 1198 rev_str = ' at %s' % revision | 1238 rev_str = ' at %s' % revision |
| 1199 | 1239 |
| 1200 if from_info['URL'] != base_url: | 1240 if from_info['URL'].rstrip('/') != base_url.rstrip('/'): |
| 1201 # The repository url changed, need to switch. | 1241 # The repository url changed, need to switch. |
| 1202 try: | 1242 try: |
| 1203 to_info = scm.SVN.CaptureRemoteInfo(url) | 1243 to_info = scm.SVN.CaptureRemoteInfo(url) |
| 1204 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1244 except (gclient_utils.Error, subprocess2.CalledProcessError): |
| 1205 # The url is invalid or the server is not accessible, it's safer to bail | 1245 # The url is invalid or the server is not accessible, it's safer to bail |
| 1206 # out right now. | 1246 # out right now. |
| 1207 raise gclient_utils.Error('This url is unreachable: %s' % url) | 1247 raise gclient_utils.Error('This url is unreachable: %s' % url) |
| 1208 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 1248 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
| 1209 and (from_info['UUID'] == to_info['UUID'])) | 1249 and (from_info['UUID'] == to_info['UUID'])) |
| 1210 if can_switch: | 1250 if can_switch: |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1426 new_command.append('--force') | 1466 new_command.append('--force') |
| 1427 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1467 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1428 new_command.extend(('--accept', 'theirs-conflict')) | 1468 new_command.extend(('--accept', 'theirs-conflict')) |
| 1429 elif options.manually_grab_svn_rev: | 1469 elif options.manually_grab_svn_rev: |
| 1430 new_command.append('--force') | 1470 new_command.append('--force') |
| 1431 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1471 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1432 new_command.extend(('--accept', 'postpone')) | 1472 new_command.extend(('--accept', 'postpone')) |
| 1433 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1473 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1434 new_command.extend(('--accept', 'postpone')) | 1474 new_command.extend(('--accept', 'postpone')) |
| 1435 return new_command | 1475 return new_command |
| OLD | NEW |