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 import logging | 7 import logging |
8 import os | 8 import os |
9 import posixpath | 9 import posixpath |
10 import re | 10 import re |
11 import sys | 11 import sys |
12 import tempfile | 12 import tempfile |
13 import traceback | 13 import traceback |
14 import urlparse | 14 import urlparse |
15 | 15 |
16 import download_from_google_storage | 16 import download_from_google_storage |
17 import gclient_utils | 17 import gclient_utils |
18 import random | |
18 import scm | 19 import scm |
20 import shutil | |
19 import subprocess2 | 21 import subprocess2 |
20 | 22 |
21 | 23 |
22 THIS_FILE_PATH = os.path.abspath(__file__) | 24 THIS_FILE_PATH = os.path.abspath(__file__) |
23 | 25 |
24 GSUTIL_DEFAULT_PATH = os.path.join( | 26 GSUTIL_DEFAULT_PATH = os.path.join( |
25 os.path.dirname(os.path.abspath(__file__)), | 27 os.path.dirname(os.path.abspath(__file__)), |
26 'third_party', 'gsutil', 'gsutil') | 28 'third_party', 'gsutil', 'gsutil') |
27 | 29 |
28 | 30 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 | 134 |
133 if not command in commands: | 135 if not command in commands: |
134 raise gclient_utils.Error('Unknown command %s' % command) | 136 raise gclient_utils.Error('Unknown command %s' % command) |
135 | 137 |
136 if not command in dir(self): | 138 if not command in dir(self): |
137 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 139 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
138 command, self.__class__.__name__)) | 140 command, self.__class__.__name__)) |
139 | 141 |
140 return getattr(self, command)(options, args, file_list) | 142 return getattr(self, command)(options, args, file_list) |
141 | 143 |
144 def _DeleteOrMove(self, force): | |
145 """Delete the checkout directory or move out of the way. | |
146 | |
147 Args: | |
148 force: bool; if True, delete the directory. Otherwise, just move it. | |
149 """ | |
150 if force: | |
151 print('_____ Conflicting directory found in %s. Removing.' | |
152 % self.checkout_path) | |
153 gclient_utils.rmtree(self.checkout_path) | |
154 else: | |
155 dest_path = os.path.join(self._root_dir, '_bad_scm', self.relpath) | |
156 dest_path += '.%d' % random.randint(1000, 9999) | |
157 print('_____ Conflicting directory found in %s. Moving to %s.' | |
158 % (self.checkout_path, dest_path)) | |
iannucci
2014/03/12 19:55:25
I would probably use http://docs.python.org/2/libr
borenet
2014/03/13 13:00:28
Done.
| |
159 shutil.move(self.checkout_path, dest_path) | |
160 | |
142 | 161 |
143 class GitWrapper(SCMWrapper): | 162 class GitWrapper(SCMWrapper): |
144 """Wrapper for Git""" | 163 """Wrapper for Git""" |
145 name = 'git' | 164 name = 'git' |
146 remote = 'origin' | 165 remote = 'origin' |
147 | 166 |
148 cache_dir = None | 167 cache_dir = None |
149 | 168 |
150 def __init__(self, url=None, root_dir=None, relpath=None): | 169 def __init__(self, url=None, root_dir=None, relpath=None): |
151 """Removes 'git+' fake prefix from git URL.""" | 170 """Removes 'git+' fake prefix from git URL.""" |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
293 # For compatibility with old naming, translate 'origin' to 'refs/heads' | 312 # For compatibility with old naming, translate 'origin' to 'refs/heads' |
294 revision = revision.replace(self.remote + '/', 'refs/heads/') | 313 revision = revision.replace(self.remote + '/', 'refs/heads/') |
295 rev_type = "branch" | 314 rev_type = "branch" |
296 else: | 315 else: |
297 # hash is also a tag, only make a distinction at checkout | 316 # hash is also a tag, only make a distinction at checkout |
298 rev_type = "hash" | 317 rev_type = "hash" |
299 | 318 |
300 if (not os.path.exists(self.checkout_path) or | 319 if (not os.path.exists(self.checkout_path) or |
301 (os.path.isdir(self.checkout_path) and | 320 (os.path.isdir(self.checkout_path) and |
302 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 321 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
322 if (os.path.isdir(self.checkout_path) and | |
323 not os.path.exists(os.path.join(self.checkout_path, '.git'))): | |
324 self._DeleteOrMove(options.force) | |
303 self._Clone(revision, url, options) | 325 self._Clone(revision, url, options) |
304 self.UpdateSubmoduleConfig() | 326 self.UpdateSubmoduleConfig() |
305 if file_list is not None: | 327 if file_list is not None: |
306 files = self._Capture(['ls-files']).splitlines() | 328 files = self._Capture(['ls-files']).splitlines() |
307 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 329 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
308 if not verbose: | 330 if not verbose: |
309 # Make the output a little prettier. It's nice to have some whitespace | 331 # Make the output a little prettier. It's nice to have some whitespace |
310 # between projects when cloning. | 332 # between projects when cloning. |
311 print('') | 333 print('') |
312 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 334 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
313 | 335 |
314 if not managed: | 336 if not managed: |
315 self._UpdateBranchHeads(options, fetch=False) | 337 self._UpdateBranchHeads(options, fetch=False) |
316 self.UpdateSubmoduleConfig() | 338 self.UpdateSubmoduleConfig() |
317 print ('________ unmanaged solution; skipping %s' % self.relpath) | 339 print ('________ unmanaged solution; skipping %s' % self.relpath) |
318 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 340 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
319 | 341 |
320 if not os.path.exists(os.path.join(self.checkout_path, '.git')): | |
321 raise gclient_utils.Error('\n____ %s%s\n' | |
322 '\tPath is not a git repo. No .git dir.\n' | |
323 '\tTo resolve:\n' | |
324 '\t\trm -rf %s\n' | |
325 '\tAnd run gclient sync again\n' | |
326 % (self.relpath, rev_str, self.relpath)) | |
327 | |
328 # See if the url has changed (the unittests use git://foo for the url, let | 342 # See if the url has changed (the unittests use git://foo for the url, let |
329 # that through). | 343 # that through). |
330 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) | 344 current_url = self._Capture(['config', 'remote.%s.url' % self.remote]) |
331 return_early = False | 345 return_early = False |
332 # TODO(maruel): Delete url != 'git://foo' since it's just to make the | 346 # TODO(maruel): Delete url != 'git://foo' since it's just to make the |
333 # unit test pass. (and update the comment above) | 347 # unit test pass. (and update the comment above) |
334 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. | 348 # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. |
335 # This allows devs to use experimental repos which have a different url | 349 # This allows devs to use experimental repos which have a different url |
336 # but whose branch(s) are the same as official repos. | 350 # but whose branch(s) are the same as official repos. |
337 if (current_url != url and | 351 if (current_url.rstrip('/') != url.rstrip('/') and |
338 url != 'git://foo' and | 352 url != 'git://foo' and |
339 subprocess2.capture( | 353 subprocess2.capture( |
340 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 354 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
341 cwd=self.checkout_path).strip() != 'False'): | 355 cwd=self.checkout_path).strip() != 'False'): |
342 print('_____ switching %s to a new upstream' % self.relpath) | 356 print('_____ switching %s to a new upstream' % self.relpath) |
343 # Make sure it's clean | 357 # Make sure it's clean |
344 self._CheckClean(rev_str) | 358 self._CheckClean(rev_str) |
345 # Switch over to the new upstream | 359 # Switch over to the new upstream |
346 self._Run(['remote', 'set-url', self.remote, url], options) | 360 self._Run(['remote', 'set-url', self.remote, url], options) |
347 self._FetchAndReset(revision, file_list, options) | 361 self._FetchAndReset(revision, file_list, options) |
(...skipping 644 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
992 filter_fn=SvnDiffFilterer(self.relpath).Filter) | 1006 filter_fn=SvnDiffFilterer(self.relpath).Filter) |
993 | 1007 |
994 def update(self, options, args, file_list): | 1008 def update(self, options, args, file_list): |
995 """Runs svn to update or transparently checkout the working copy. | 1009 """Runs svn to update or transparently checkout the working copy. |
996 | 1010 |
997 All updated files will be appended to file_list. | 1011 All updated files will be appended to file_list. |
998 | 1012 |
999 Raises: | 1013 Raises: |
1000 Error: if can't get URL for relative path. | 1014 Error: if can't get URL for relative path. |
1001 """ | 1015 """ |
1002 # Only update if git or hg is not controlling the directory. | 1016 # Only update if hg is not controlling the directory. |
1003 git_path = os.path.join(self.checkout_path, '.git') | |
1004 if os.path.exists(git_path): | |
1005 print('________ found .git directory; skipping %s' % self.relpath) | |
1006 return | |
1007 | |
1008 hg_path = os.path.join(self.checkout_path, '.hg') | 1017 hg_path = os.path.join(self.checkout_path, '.hg') |
1009 if os.path.exists(hg_path): | 1018 if os.path.exists(hg_path): |
1010 print('________ found .hg directory; skipping %s' % self.relpath) | 1019 print('________ found .hg directory; skipping %s' % self.relpath) |
1011 return | 1020 return |
1012 | 1021 |
1013 if args: | 1022 if args: |
1014 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 1023 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
1015 | 1024 |
1016 # revision is the revision to match. It is None if no revision is specified, | 1025 # revision is the revision to match. It is None if no revision is specified, |
1017 # i.e. the 'deps ain't pinned'. | 1026 # i.e. the 'deps ain't pinned'. |
(...skipping 10 matching lines...) Expand all Loading... | |
1028 # Reconstruct the url. | 1037 # Reconstruct the url. |
1029 url = '%s@%s' % (url, revision) | 1038 url = '%s@%s' % (url, revision) |
1030 rev_str = ' at %s' % revision | 1039 rev_str = ' at %s' % revision |
1031 else: | 1040 else: |
1032 managed = False | 1041 managed = False |
1033 revision = None | 1042 revision = None |
1034 else: | 1043 else: |
1035 forced_revision = False | 1044 forced_revision = False |
1036 rev_str = '' | 1045 rev_str = '' |
1037 | 1046 |
1047 exists = os.path.exists(self.checkout_path) | |
1048 if exists and managed: | |
1049 # Git is only okay if it's a git-svn checkout of the right repo. | |
1050 if scm.GIT.IsGitSvn(self.checkout_path): | |
1051 remote_url = scm.GIT.Capture(['config', '--local', '--get', | |
1052 'svn-remote.svn.url'], | |
1053 cwd=self.checkout_path).rstrip() | |
1054 if remote_url.rstrip('/') == base_url.rstrip('/'): | |
1055 print('\n_____ %s looks like a git-svn checkout. Skipping.' | |
1056 % self.relpath) | |
1057 return # TODO(borenet): Get the svn revision number? | |
1058 | |
1038 # Get the existing scm url and the revision number of the current checkout. | 1059 # Get the existing scm url and the revision number of the current checkout. |
1039 exists = os.path.exists(self.checkout_path) | |
1040 if exists and managed: | 1060 if exists and managed: |
1041 try: | 1061 try: |
1042 from_info = scm.SVN.CaptureLocalInfo( | 1062 from_info = scm.SVN.CaptureLocalInfo( |
1043 [], os.path.join(self.checkout_path, '.')) | 1063 [], os.path.join(self.checkout_path, '.')) |
1044 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1064 except (gclient_utils.Error, subprocess2.CalledProcessError): |
1045 if options.reset and options.delete_unversioned_trees: | 1065 self._DeleteOrMove(options.force) |
1046 print 'Removing troublesome path %s' % self.checkout_path | 1066 exists = False |
1047 gclient_utils.rmtree(self.checkout_path) | |
1048 exists = False | |
1049 else: | |
1050 msg = ('Can\'t update/checkout %s if an unversioned directory is ' | |
1051 'present. Delete the directory and try again.') | |
1052 raise gclient_utils.Error(msg % self.checkout_path) | |
1053 | 1067 |
1054 BASE_URLS = { | 1068 BASE_URLS = { |
1055 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', | 1069 '/chrome/trunk/src': 'gs://chromium-svn-checkout/chrome/', |
1056 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', | 1070 '/blink/trunk': 'gs://chromium-svn-checkout/blink/', |
1057 } | 1071 } |
1058 WHITELISTED_ROOTS = [ | 1072 WHITELISTED_ROOTS = [ |
1059 'svn://svn.chromium.org', | 1073 'svn://svn.chromium.org', |
1060 'svn://svn-mirror.golo.chromium.org', | 1074 'svn://svn-mirror.golo.chromium.org', |
1061 ] | 1075 ] |
1062 if not exists: | 1076 if not exists: |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1122 | 1136 |
1123 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) | 1137 gclient_utils.safe_makedirs(os.path.dirname(self.checkout_path)) |
1124 # We need to checkout. | 1138 # We need to checkout. |
1125 command = ['checkout', url, self.checkout_path] | 1139 command = ['checkout', url, self.checkout_path] |
1126 command = self._AddAdditionalUpdateFlags(command, options, revision) | 1140 command = self._AddAdditionalUpdateFlags(command, options, revision) |
1127 self._RunAndGetFileList(command, options, file_list, self._root_dir) | 1141 self._RunAndGetFileList(command, options, file_list, self._root_dir) |
1128 return self.Svnversion() | 1142 return self.Svnversion() |
1129 | 1143 |
1130 if not managed: | 1144 if not managed: |
1131 print ('________ unmanaged solution; skipping %s' % self.relpath) | 1145 print ('________ unmanaged solution; skipping %s' % self.relpath) |
1132 return self.Svnversion() | 1146 if os.path.exists(os.path.join(self.checkout_path, '.svn')): |
1147 return self.Svnversion() | |
1148 return | |
1133 | 1149 |
1134 if 'URL' not in from_info: | 1150 if 'URL' not in from_info: |
1135 raise gclient_utils.Error( | 1151 raise gclient_utils.Error( |
1136 ('gclient is confused. Couldn\'t get the url for %s.\n' | 1152 ('gclient is confused. Couldn\'t get the url for %s.\n' |
1137 'Try using @unmanaged.\n%s') % ( | 1153 'Try using @unmanaged.\n%s') % ( |
1138 self.checkout_path, from_info)) | 1154 self.checkout_path, from_info)) |
1139 | 1155 |
1140 # Look for locked directories. | 1156 # Look for locked directories. |
1141 dir_info = scm.SVN.CaptureStatus( | 1157 dir_info = scm.SVN.CaptureStatus( |
1142 None, os.path.join(self.checkout_path, '.')) | 1158 None, os.path.join(self.checkout_path, '.')) |
(...skipping 22 matching lines...) Expand all Loading... | |
1165 if d[0][0] == '!': | 1181 if d[0][0] == '!': |
1166 print 'You can pass --force to enable automatic removal.' | 1182 print 'You can pass --force to enable automatic removal.' |
1167 raise e | 1183 raise e |
1168 | 1184 |
1169 # Retrieve the current HEAD version because svn is slow at null updates. | 1185 # Retrieve the current HEAD version because svn is slow at null updates. |
1170 if options.manually_grab_svn_rev and not revision: | 1186 if options.manually_grab_svn_rev and not revision: |
1171 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) | 1187 from_info_live = scm.SVN.CaptureRemoteInfo(from_info['URL']) |
1172 revision = str(from_info_live['Revision']) | 1188 revision = str(from_info_live['Revision']) |
1173 rev_str = ' at %s' % revision | 1189 rev_str = ' at %s' % revision |
1174 | 1190 |
1175 if from_info['URL'] != base_url: | 1191 if from_info['URL'].rstrip('/') != base_url.rstrip('/'): |
1176 # The repository url changed, need to switch. | 1192 # The repository url changed, need to switch. |
1177 try: | 1193 try: |
1178 to_info = scm.SVN.CaptureRemoteInfo(url) | 1194 to_info = scm.SVN.CaptureRemoteInfo(url) |
1179 except (gclient_utils.Error, subprocess2.CalledProcessError): | 1195 except (gclient_utils.Error, subprocess2.CalledProcessError): |
1180 # The url is invalid or the server is not accessible, it's safer to bail | 1196 # The url is invalid or the server is not accessible, it's safer to bail |
1181 # out right now. | 1197 # out right now. |
1182 raise gclient_utils.Error('This url is unreachable: %s' % url) | 1198 raise gclient_utils.Error('This url is unreachable: %s' % url) |
1183 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) | 1199 can_switch = ((from_info['Repository Root'] != to_info['Repository Root']) |
1184 and (from_info['UUID'] == to_info['UUID'])) | 1200 and (from_info['UUID'] == to_info['UUID'])) |
1185 if can_switch: | 1201 if can_switch: |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1401 new_command.append('--force') | 1417 new_command.append('--force') |
1402 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1418 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1403 new_command.extend(('--accept', 'theirs-conflict')) | 1419 new_command.extend(('--accept', 'theirs-conflict')) |
1404 elif options.manually_grab_svn_rev: | 1420 elif options.manually_grab_svn_rev: |
1405 new_command.append('--force') | 1421 new_command.append('--force') |
1406 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1422 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1407 new_command.extend(('--accept', 'postpone')) | 1423 new_command.extend(('--accept', 'postpone')) |
1408 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1424 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
1409 new_command.extend(('--accept', 'postpone')) | 1425 new_command.extend(('--accept', 'postpone')) |
1410 return new_command | 1426 return new_command |
OLD | NEW |