| 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 from __future__ import print_function | 7 from __future__ import print_function |
| 8 | 8 |
| 9 import collections | |
| 10 import errno | 9 import errno |
| 11 import logging | 10 import logging |
| 12 import os | 11 import os |
| 13 import posixpath | 12 import posixpath |
| 14 import re | 13 import re |
| 15 import shlex | 14 import shlex |
| 16 import sys | 15 import sys |
| 17 import tempfile | 16 import tempfile |
| 18 import traceback | 17 import traceback |
| 19 import urlparse | 18 import urlparse |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 | 152 |
| 154 if not command in commands: | 153 if not command in commands: |
| 155 raise gclient_utils.Error('Unknown command %s' % command) | 154 raise gclient_utils.Error('Unknown command %s' % command) |
| 156 | 155 |
| 157 if not command in dir(self): | 156 if not command in dir(self): |
| 158 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( | 157 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( |
| 159 command, self.__class__.__name__)) | 158 command, self.__class__.__name__)) |
| 160 | 159 |
| 161 return getattr(self, command)(options, args, file_list) | 160 return getattr(self, command)(options, args, file_list) |
| 162 | 161 |
| 163 def GetActualRemoteURL(self): | 162 def GetActualRemoteURL(self, options): |
| 164 """Attempt to determine the remote URL for this SCMWrapper.""" | 163 """Attempt to determine the remote URL for this SCMWrapper.""" |
| 164 # Git |
| 165 if os.path.exists(os.path.join(self.checkout_path, '.git')): | 165 if os.path.exists(os.path.join(self.checkout_path, '.git')): |
| 166 actual_remote_url = shlex.split(scm.GIT.Capture( | 166 actual_remote_url = shlex.split(scm.GIT.Capture( |
| 167 ['config', '--local', '--get-regexp', r'remote.*.url'], | 167 ['config', '--local', '--get-regexp', r'remote.*.url'], |
| 168 cwd=self.checkout_path))[1] | 168 cwd=self.checkout_path))[1] |
| 169 | 169 |
| 170 # If a cache_dir is used, obtain the actual remote URL from the cache. | 170 # If a cache_dir is used, obtain the actual remote URL from the cache. |
| 171 if getattr(self, 'cache_dir', None): | 171 if getattr(self, 'cache_dir', None): |
| 172 url, _ = gclient_utils.SplitUrlRevision(self.url) | 172 url, _ = gclient_utils.SplitUrlRevision(self.url) |
| 173 mirror = git_cache.Mirror(url) | 173 mirror = git_cache.Mirror(url) |
| 174 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') == | 174 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') == |
| 175 actual_remote_url.replace('\\', '/')): | 175 actual_remote_url.replace('\\', '/')): |
| 176 actual_remote_url = shlex.split(scm.GIT.Capture( | 176 actual_remote_url = shlex.split(scm.GIT.Capture( |
| 177 ['config', '--local', '--get-regexp', r'remote.*.url'], | 177 ['config', '--local', '--get-regexp', r'remote.*.url'], |
| 178 cwd=mirror.mirror_path))[1] | 178 cwd=mirror.mirror_path))[1] |
| 179 return actual_remote_url | 179 return actual_remote_url |
| 180 | 180 |
| 181 # Svn | 181 # Svn |
| 182 if os.path.exists(os.path.join(self.checkout_path, '.svn')): | 182 if os.path.exists(os.path.join(self.checkout_path, '.svn')): |
| 183 return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL'] | 183 return scm.SVN.CaptureLocalInfo([], self.checkout_path)['URL'] |
| 184 return None | 184 return None |
| 185 | 185 |
| 186 def DoesRemoteURLMatch(self, options): | 186 def DoesRemoteURLMatch(self, options): |
| 187 """Determine whether the remote URL of this checkout is the expected URL.""" | 187 """Determine whether the remote URL of this checkout is the expected URL.""" |
| 188 if not os.path.exists(self.checkout_path): | 188 if not os.path.exists(self.checkout_path): |
| 189 # A checkout which doesn't exist can't be broken. | 189 # A checkout which doesn't exist can't be broken. |
| 190 return True | 190 return True |
| 191 | 191 |
| 192 actual_remote_url = self.GetActualRemoteURL() | 192 actual_remote_url = self.GetActualRemoteURL(options) |
| 193 if actual_remote_url: | 193 if actual_remote_url: |
| 194 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') | 194 return (gclient_utils.SplitUrlRevision(actual_remote_url)[0].rstrip('/') |
| 195 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) | 195 == gclient_utils.SplitUrlRevision(self.url)[0].rstrip('/')) |
| 196 else: | 196 else: |
| 197 # This may occur if the self.checkout_path exists but does not contain a | 197 # This may occur if the self.checkout_path exists but does not contain a |
| 198 # valid git or svn checkout. | 198 # valid git or svn checkout. |
| 199 return False | 199 return False |
| 200 | 200 |
| 201 def _DeleteOrMove(self, force): | 201 def _DeleteOrMove(self, force): |
| 202 """Delete the checkout directory or move it out of the way. | 202 """Delete the checkout directory or move it out of the way. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 223 dest_path = tempfile.mkdtemp( | 223 dest_path = tempfile.mkdtemp( |
| 224 prefix=os.path.basename(self.relpath), | 224 prefix=os.path.basename(self.relpath), |
| 225 dir=bad_scm_dir) | 225 dir=bad_scm_dir) |
| 226 self.Print('_____ Conflicting directory found in %s. Moving to %s.' | 226 self.Print('_____ Conflicting directory found in %s. Moving to %s.' |
| 227 % (self.checkout_path, dest_path)) | 227 % (self.checkout_path, dest_path)) |
| 228 gclient_utils.AddWarning('Conflicting directory %s moved to %s.' | 228 gclient_utils.AddWarning('Conflicting directory %s moved to %s.' |
| 229 % (self.checkout_path, dest_path)) | 229 % (self.checkout_path, dest_path)) |
| 230 shutil.move(self.checkout_path, dest_path) | 230 shutil.move(self.checkout_path, dest_path) |
| 231 | 231 |
| 232 | 232 |
| 233 _GitRefishBase = collections.namedtuple('GitRef', ( | |
| 234 # The initial string that the parsed Refish came from | |
| 235 'source', | |
| 236 # Is this ref a branch? | |
| 237 'is_branch', | |
| 238 # The name of this ref when accessed through the local repository | |
| 239 'local_ref', | |
| 240 # The name of the remote that this refish resides on | |
| 241 'remote', | |
| 242 # The path of this refish on the remote (e.g., 'master') | |
| 243 'remote_ref', | |
| 244 # The remote-qualified path to this refish (e.g., 'origin/master') | |
| 245 'remote_refspec', | |
| 246 # If a branch, the expected name of the upstream branch that it should | |
| 247 # track; otherwise, 'None' | |
| 248 'upstream_branch', | |
| 249 )) | |
| 250 | |
| 251 | |
| 252 class GitRefish(_GitRefishBase): | |
| 253 # Disable 'no __init__' warning | pylint: disable=W0232 | |
| 254 """Implements refish parsing and properties. | |
| 255 | |
| 256 This class is designed to concentrate and codify the assumptions made by | |
| 257 'gclient' code about refs and revisions. | |
| 258 """ | |
| 259 | |
| 260 _DEFAULT_REMOTE = 'origin' | |
| 261 | |
| 262 _GIT_SHA1_RE = re.compile('[0-9a-f]{40}', re.IGNORECASE) | |
| 263 _GIT_SHA1_SHORT_RE = re.compile('[0-9a-f]{4,40}', re.IGNORECASE) | |
| 264 | |
| 265 def __str__(self): | |
| 266 return self.source | |
| 267 | |
| 268 @classmethod | |
| 269 def Parse(cls, ref, remote=None, other_remotes=None): | |
| 270 """Parses a ref into a 'GitRefish' instance. | |
| 271 | |
| 272 Args: | |
| 273 ref: (str) The refish (branch, tag, hash, etc.) to parse | |
| 274 remote: (None/str) If supplied, the name of the primary remote. In | |
| 275 addtion to being recognized as a remote string, the primary remote | |
| 276 will also be used (as needed) when generating the remote refspec. If | |
| 277 omitted, the default remote name ('origin') will be used. | |
| 278 other_remotes: (None/iterable) If supplied, a list of strings to | |
| 279 recognize as remotes in addition to 'remote'. | |
| 280 """ | |
| 281 assert ref == ref.strip() | |
| 282 | |
| 283 # Use default set of remotes | |
| 284 remote = remote or cls._DEFAULT_REMOTE | |
| 285 remotes = {remote} | |
| 286 if other_remotes: | |
| 287 remotes.update(other_remotes) | |
| 288 | |
| 289 # Treat 'ref' as a '/'-delimited set of items; analyze their contents | |
| 290 ref_split = local_ref = remote_ref = tuple(ref.split('/')) | |
| 291 is_branch = True | |
| 292 if len(ref_split) > 1: | |
| 293 if ref_split[0] == 'refs': | |
| 294 if len(ref_split) >= 3 and ref_split[1] == 'heads': | |
| 295 # refs/heads/foo/bar | |
| 296 # | |
| 297 # Local Ref: foo/bar | |
| 298 # Remote Ref: foo/bar | |
| 299 local_ref = remote_ref = ref_split[2:] | |
| 300 elif len(ref_split) >= 4 and ref_split[1] == 'remotes': | |
| 301 # refs/remotes/<REMOTE>/foo/bar | |
| 302 # This is a bad assumption, and this logic can be improved, but it | |
| 303 # is consistent with traditional 'gclient' logic. | |
| 304 # | |
| 305 # Local Ref: refs/remotes/<REMOTE>/foo/bar | |
| 306 # Remote: <REMOTE> | |
| 307 # Remote Ref: foo/bar | |
| 308 remote = ref_split[2] | |
| 309 remote_ref = ref_split[3:] | |
| 310 elif len(ref_split) >= 2 and ref_split[0] in remotes: | |
| 311 # origin/master | |
| 312 # | |
| 313 # Local Ref: refs/remotes/origin/master | |
| 314 # Remote Ref: master | |
| 315 remote = ref_split[0] | |
| 316 remote_ref = ref_split[1:] | |
| 317 local_ref = ('refs', 'remotes') + ref_split | |
| 318 | |
| 319 # (Default) The refish has multiple paths. Assume at least that it's a | |
| 320 # branch name. | |
| 321 # | |
| 322 # foo/bar | |
| 323 # refs/foo/bar | |
| 324 # | |
| 325 # Local Ref: foo/bar | |
| 326 # Remote ref: foo/bar | |
| 327 else: | |
| 328 # It could be a hash, a short-hash, or a tag | |
| 329 is_branch = False | |
| 330 | |
| 331 # Determine how the ref should be referenced remotely | |
| 332 if is_branch: | |
| 333 # foo/bar/baz => origin/foo/bar/baz | |
| 334 remote_refspec = (remote,) + remote_ref | |
| 335 else: | |
| 336 # Refer to the hash/tag directly. This is actually not allowed in | |
| 337 # 'fetch', although it oftentimes works regardless. | |
| 338 # | |
| 339 # <HASH/TAG> => <HASH/TAG> | |
| 340 remote_refspec = (ref,) | |
| 341 | |
| 342 # Calculate the upstream branch | |
| 343 if is_branch: | |
| 344 # Convert '/refs/heads/...' to 'refs/remotes/REMOTE/...' | |
| 345 if ref_split[:2] == ('refs', 'heads'): | |
| 346 upstream_branch = ('refs', 'remotes', remote) + ref_split[2:] | |
| 347 else: | |
| 348 upstream_branch = ref_split | |
| 349 else: | |
| 350 upstream_branch = None | |
| 351 | |
| 352 def compose(ref_tuple): | |
| 353 return '/'.join(ref_tuple) if ref_tuple else ref_tuple | |
| 354 | |
| 355 return cls( | |
| 356 source=ref, | |
| 357 is_branch=is_branch, | |
| 358 local_ref=compose(local_ref), | |
| 359 remote=remote, | |
| 360 remote_ref=compose(remote_ref), | |
| 361 remote_refspec=compose(remote_refspec), | |
| 362 upstream_branch=compose(upstream_branch), | |
| 363 ) | |
| 364 | |
| 365 | |
| 366 class GitWrapper(SCMWrapper): | 233 class GitWrapper(SCMWrapper): |
| 367 """Wrapper for Git""" | 234 """Wrapper for Git""" |
| 368 name = 'git' | 235 name = 'git' |
| 369 remote = 'origin' | 236 remote = 'origin' |
| 370 | 237 |
| 371 cache_dir = None | 238 cache_dir = None |
| 372 | 239 |
| 373 def __init__(self, url=None, *args): | 240 def __init__(self, url=None, *args): |
| 374 """Removes 'git+' fake prefix from git URL.""" | 241 """Removes 'git+' fake prefix from git URL.""" |
| 375 if url.startswith('git+http://') or url.startswith('git+https://'): | 242 if url.startswith('git+http://') or url.startswith('git+https://'): |
| 376 url = url[4:] | 243 url = url[4:] |
| 377 SCMWrapper.__init__(self, url, *args) | 244 SCMWrapper.__init__(self, url, *args) |
| 378 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } | 245 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } |
| 379 if self.out_cb: | 246 if self.out_cb: |
| 380 filter_kwargs['predicate'] = self.out_cb | 247 filter_kwargs['predicate'] = self.out_cb |
| 381 self.filter = gclient_utils.GitFilter(**filter_kwargs) | 248 self.filter = gclient_utils.GitFilter(**filter_kwargs) |
| 382 | 249 |
| 383 @staticmethod | 250 @staticmethod |
| 384 def BinaryExists(): | 251 def BinaryExists(): |
| 385 """Returns true if the command exists.""" | 252 """Returns true if the command exists.""" |
| 386 try: | 253 try: |
| 387 # We assume git is newer than 1.7. See: crbug.com/114483 | 254 # We assume git is newer than 1.7. See: crbug.com/114483 |
| 388 result, version = scm.GIT.AssertVersion('1.7') | 255 result, version = scm.GIT.AssertVersion('1.7') |
| 389 if not result: | 256 if not result: |
| 390 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) | 257 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) |
| 391 return result | 258 return result |
| 392 except OSError: | 259 except OSError: |
| 393 return False | 260 return False |
| 394 | 261 |
| 395 def ParseRefish(self, value, **kwargs): | |
| 396 kwargs.setdefault('remote', self.remote) | |
| 397 return GitRefish.Parse(value, **kwargs) | |
| 398 | |
| 399 def GetCheckoutRoot(self): | 262 def GetCheckoutRoot(self): |
| 400 return scm.GIT.GetCheckoutRoot(self.checkout_path) | 263 return scm.GIT.GetCheckoutRoot(self.checkout_path) |
| 401 | 264 |
| 402 def GetRevisionDate(self, _revision): | 265 def GetRevisionDate(self, _revision): |
| 403 """Returns the given revision's date in ISO-8601 format (which contains the | 266 """Returns the given revision's date in ISO-8601 format (which contains the |
| 404 time zone).""" | 267 time zone).""" |
| 405 # TODO(floitsch): get the time-stamp of the given revision and not just the | 268 # TODO(floitsch): get the time-stamp of the given revision and not just the |
| 406 # time-stamp of the currently checked out revision. | 269 # time-stamp of the currently checked out revision. |
| 407 return self._Capture(['log', '-n', '1', '--format=%ai']) | 270 return self._Capture(['log', '-n', '1', '--format=%ai']) |
| 408 | 271 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 423 | 286 |
| 424 The patch file is generated from a diff of the merge base of HEAD and | 287 The patch file is generated from a diff of the merge base of HEAD and |
| 425 its upstream branch. | 288 its upstream branch. |
| 426 """ | 289 """ |
| 427 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) | 290 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) |
| 428 gclient_utils.CheckCallAndFilter( | 291 gclient_utils.CheckCallAndFilter( |
| 429 ['git', 'diff', merge_base], | 292 ['git', 'diff', merge_base], |
| 430 cwd=self.checkout_path, | 293 cwd=self.checkout_path, |
| 431 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) | 294 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) |
| 432 | 295 |
| 433 def _FetchAndReset(self, refish, file_list, options): | 296 def _FetchAndReset(self, revision, file_list, options): |
| 434 """Equivalent to git fetch; git reset.""" | 297 """Equivalent to git fetch; git reset.""" |
| 435 quiet = [] | 298 quiet = [] |
| 436 if not options.verbose: | 299 if not options.verbose: |
| 437 quiet = ['--quiet'] | 300 quiet = ['--quiet'] |
| 438 self._UpdateBranchHeads(options, fetch=False) | 301 self._UpdateBranchHeads(options, fetch=False) |
| 439 | 302 |
| 440 self._Fetch(options, prune=True, quiet=options.verbose) | 303 self._Fetch(options, prune=True, quiet=options.verbose) |
| 441 self._Run(['reset', '--hard', refish.local_ref] + quiet, options) | 304 self._Run(['reset', '--hard', revision] + quiet, options) |
| 442 if file_list is not None: | 305 if file_list is not None: |
| 443 files = self._Capture(['ls-files']).splitlines() | 306 files = self._Capture(['ls-files']).splitlines() |
| 444 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 307 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 445 | 308 |
| 446 def _DisableHooks(self): | 309 def _DisableHooks(self): |
| 447 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') | 310 hook_dir = os.path.join(self.checkout_path, '.git', 'hooks') |
| 448 if not os.path.isdir(hook_dir): | 311 if not os.path.isdir(hook_dir): |
| 449 return | 312 return |
| 450 for f in os.listdir(hook_dir): | 313 for f in os.listdir(hook_dir): |
| 451 if not f.endswith('.sample') and not f.endswith('.disabled'): | 314 if not f.endswith('.sample') and not f.endswith('.disabled'): |
| 452 os.rename(os.path.join(hook_dir, f), | 315 os.rename(os.path.join(hook_dir, f), |
| 453 os.path.join(hook_dir, f + '.disabled')) | 316 os.path.join(hook_dir, f + '.disabled')) |
| 454 | 317 |
| 455 def update(self, options, args, file_list): | 318 def update(self, options, args, file_list): |
| 456 """Runs git to update or transparently checkout the working copy. | 319 """Runs git to update or transparently checkout the working copy. |
| 457 | 320 |
| 458 All updated files will be appended to file_list. | 321 All updated files will be appended to file_list. |
| 459 | 322 |
| 460 Raises: | 323 Raises: |
| 461 Error: if can't get URL for relative path. | 324 Error: if can't get URL for relative path. |
| 462 """ | 325 """ |
| 463 if args: | 326 if args: |
| 464 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) | 327 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) |
| 465 | 328 |
| 466 self._CheckMinVersion("1.6.6") | 329 self._CheckMinVersion("1.6.6") |
| 467 | 330 |
| 468 # If a dependency is not pinned, track the default remote branch. | 331 # If a dependency is not pinned, track the default remote branch. |
| 469 default_rev = 'refs/remotes/%s/master' % (self.remote,) | 332 default_rev = 'refs/remotes/%s/master' % self.remote |
| 470 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 333 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
| 471 rev_str = "" | 334 rev_str = "" |
| 472 revision = deps_revision | 335 revision = deps_revision |
| 473 managed = True | 336 managed = True |
| 474 if options.revision: | 337 if options.revision: |
| 475 # Override the revision number. | 338 # Override the revision number. |
| 476 revision = str(options.revision) | 339 revision = str(options.revision) |
| 477 if revision == 'unmanaged': | 340 if revision == 'unmanaged': |
| 478 # Check again for a revision in case an initial ref was specified | 341 # Check again for a revision in case an initial ref was specified |
| 479 # in the url, for example bla.git@refs/heads/custombranch | 342 # in the url, for example bla.git@refs/heads/custombranch |
| (...skipping 16 matching lines...) Expand all Loading... |
| 496 rev_str = ' at %s' % revision | 359 rev_str = ' at %s' % revision |
| 497 files = [] if file_list is not None else None | 360 files = [] if file_list is not None else None |
| 498 | 361 |
| 499 printed_path = False | 362 printed_path = False |
| 500 verbose = [] | 363 verbose = [] |
| 501 if options.verbose: | 364 if options.verbose: |
| 502 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 365 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 503 verbose = ['--verbose'] | 366 verbose = ['--verbose'] |
| 504 printed_path = True | 367 printed_path = True |
| 505 | 368 |
| 506 refish = self.ParseRefish(revision) | 369 if revision.startswith('refs/'): |
| 370 rev_type = "branch" |
| 371 elif revision.startswith(self.remote + '/'): |
| 372 # Rewrite remote refs to their local equivalents. |
| 373 revision = 'refs/remotes/' + revision |
| 374 rev_type = "branch" |
| 375 else: |
| 376 # hash is also a tag, only make a distinction at checkout |
| 377 rev_type = "hash" |
| 378 |
| 507 mirror = self._GetMirror(url, options) | 379 mirror = self._GetMirror(url, options) |
| 508 if mirror: | 380 if mirror: |
| 509 url = mirror.mirror_path | 381 url = mirror.mirror_path |
| 510 | 382 |
| 511 if (not os.path.exists(self.checkout_path) or | 383 if (not os.path.exists(self.checkout_path) or |
| 512 (os.path.isdir(self.checkout_path) and | 384 (os.path.isdir(self.checkout_path) and |
| 513 not os.path.exists(os.path.join(self.checkout_path, '.git')))): | 385 not os.path.exists(os.path.join(self.checkout_path, '.git')))): |
| 514 if mirror: | 386 if mirror: |
| 515 self._UpdateMirror(mirror, options) | 387 self._UpdateMirror(mirror, options) |
| 516 try: | 388 try: |
| 517 self._Clone(refish.local_ref, url, options) | 389 self._Clone(revision, url, options) |
| 518 except subprocess2.CalledProcessError: | 390 except subprocess2.CalledProcessError: |
| 519 self._DeleteOrMove(options.force) | 391 self._DeleteOrMove(options.force) |
| 520 self._Clone(refish.local_ref, url, options) | 392 self._Clone(revision, url, options) |
| 521 if deps_revision and deps_revision.startswith('branch-heads/'): | 393 if deps_revision and deps_revision.startswith('branch-heads/'): |
| 522 deps_branch = deps_revision.replace('branch-heads/', '') | 394 deps_branch = deps_revision.replace('branch-heads/', '') |
| 523 self._Capture(['branch', deps_branch, deps_revision]) | 395 self._Capture(['branch', deps_branch, deps_revision]) |
| 524 self._Checkout(options, deps_branch, quiet=True) | 396 self._Checkout(options, deps_branch, quiet=True) |
| 525 if file_list is not None: | 397 if file_list is not None: |
| 526 files = self._Capture(['ls-files']).splitlines() | 398 files = self._Capture(['ls-files']).splitlines() |
| 527 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 399 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 528 if not verbose: | 400 if not verbose: |
| 529 # Make the output a little prettier. It's nice to have some whitespace | 401 # Make the output a little prettier. It's nice to have some whitespace |
| 530 # between projects when cloning. | 402 # between projects when cloning. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 551 if (current_url.rstrip('/') != url.rstrip('/') and | 423 if (current_url.rstrip('/') != url.rstrip('/') and |
| 552 url != 'git://foo' and | 424 url != 'git://foo' and |
| 553 subprocess2.capture( | 425 subprocess2.capture( |
| 554 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], | 426 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], |
| 555 cwd=self.checkout_path).strip() != 'False'): | 427 cwd=self.checkout_path).strip() != 'False'): |
| 556 self.Print('_____ switching %s to a new upstream' % self.relpath) | 428 self.Print('_____ switching %s to a new upstream' % self.relpath) |
| 557 # Make sure it's clean | 429 # Make sure it's clean |
| 558 self._CheckClean(rev_str) | 430 self._CheckClean(rev_str) |
| 559 # Switch over to the new upstream | 431 # Switch over to the new upstream |
| 560 self._Run(['remote', 'set-url', self.remote, url], options) | 432 self._Run(['remote', 'set-url', self.remote, url], options) |
| 561 self._FetchAndReset(refish, file_list, options) | 433 self._FetchAndReset(revision, file_list, options) |
| 562 return_early = True | 434 return_early = True |
| 563 | 435 |
| 564 if return_early: | 436 if return_early: |
| 565 return self._Capture(['rev-parse', '--verify', 'HEAD']) | 437 return self._Capture(['rev-parse', '--verify', 'HEAD']) |
| 566 | 438 |
| 567 cur_branch = self._GetCurrentBranch() | 439 cur_branch = self._GetCurrentBranch() |
| 568 | 440 |
| 569 # Cases: | 441 # Cases: |
| 570 # 0) HEAD is detached. Probably from our initial clone. | 442 # 0) HEAD is detached. Probably from our initial clone. |
| 571 # - make sure HEAD is contained by a named ref, then update. | 443 # - make sure HEAD is contained by a named ref, then update. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 594 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 466 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
| 595 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): | 467 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): |
| 596 current_type = "hash" | 468 current_type = "hash" |
| 597 logging.debug("Current branch is not tracking an upstream (remote)" | 469 logging.debug("Current branch is not tracking an upstream (remote)" |
| 598 " branch.") | 470 " branch.") |
| 599 elif upstream_branch.startswith('refs/remotes'): | 471 elif upstream_branch.startswith('refs/remotes'): |
| 600 current_type = "branch" | 472 current_type = "branch" |
| 601 else: | 473 else: |
| 602 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) | 474 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) |
| 603 | 475 |
| 604 if not scm.GIT.IsValidRevision(self.checkout_path, refish.local_ref, | 476 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): |
| 605 sha_only=True): | |
| 606 # Update the remotes first so we have all the refs. | 477 # Update the remotes first so we have all the refs. |
| 607 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], | 478 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], |
| 608 cwd=self.checkout_path) | 479 cwd=self.checkout_path) |
| 609 if verbose: | 480 if verbose: |
| 610 self.Print(remote_output) | 481 self.Print(remote_output) |
| 611 | 482 |
| 612 self._UpdateBranchHeads(options, fetch=True) | 483 self._UpdateBranchHeads(options, fetch=True) |
| 613 | 484 |
| 614 # This is a big hammer, debatable if it should even be here... | 485 # This is a big hammer, debatable if it should even be here... |
| 615 if options.force or options.reset: | 486 if options.force or options.reset: |
| 616 target = 'HEAD' | 487 target = 'HEAD' |
| 617 if options.upstream and upstream_branch: | 488 if options.upstream and upstream_branch: |
| 618 target = upstream_branch | 489 target = upstream_branch |
| 619 self._Run(['reset', '--hard', target], options) | 490 self._Run(['reset', '--hard', target], options) |
| 620 | 491 |
| 621 if current_type == 'detached': | 492 if current_type == 'detached': |
| 622 # case 0 | 493 # case 0 |
| 623 self._CheckClean(rev_str) | 494 self._CheckClean(rev_str) |
| 624 self._CheckDetachedHead(rev_str, options) | 495 self._CheckDetachedHead(rev_str, options) |
| 625 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == refish.local_ref: | 496 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == revision: |
| 626 self.Print('Up-to-date; skipping checkout.') | 497 self.Print('Up-to-date; skipping checkout.') |
| 627 else: | 498 else: |
| 628 # 'git checkout' may need to overwrite existing untracked files. Allow | 499 # 'git checkout' may need to overwrite existing untracked files. Allow |
| 629 # it only when nuclear options are enabled. | 500 # it only when nuclear options are enabled. |
| 630 self._Checkout( | 501 self._Checkout( |
| 631 options, | 502 options, |
| 632 revision, | 503 revision, |
| 633 force=(options.force and options.delete_unversioned_trees), | 504 force=(options.force and options.delete_unversioned_trees), |
| 634 quiet=True, | 505 quiet=True, |
| 635 ) | 506 ) |
| 636 if not printed_path: | 507 if not printed_path: |
| 637 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 508 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 638 elif current_type == 'hash': | 509 elif current_type == 'hash': |
| 639 # case 1 | 510 # case 1 |
| 640 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: | 511 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: |
| 641 # Our git-svn branch (upstream_branch) is our upstream | 512 # Our git-svn branch (upstream_branch) is our upstream |
| 642 self._AttemptRebase(upstream_branch, files, options, | 513 self._AttemptRebase(upstream_branch, files, options, |
| 643 newbase=refish.local_ref, | 514 newbase=revision, printed_path=printed_path, |
| 644 printed_path=printed_path, merge=options.merge) | 515 merge=options.merge) |
| 645 printed_path = True | 516 printed_path = True |
| 646 else: | 517 else: |
| 647 # Can't find a merge-base since we don't know our upstream. That makes | 518 # Can't find a merge-base since we don't know our upstream. That makes |
| 648 # this command VERY likely to produce a rebase failure. For now we | 519 # this command VERY likely to produce a rebase failure. For now we |
| 649 # assume origin is our upstream since that's what the old behavior was. | 520 # assume origin is our upstream since that's what the old behavior was. |
| 650 upstream_branch = self.remote | 521 upstream_branch = self.remote |
| 651 if options.revision or deps_revision: | 522 if options.revision or deps_revision: |
| 652 upstream_branch = refish.local_ref | 523 upstream_branch = revision |
| 653 self._AttemptRebase(upstream_branch, files, options, | 524 self._AttemptRebase(upstream_branch, files, options, |
| 654 printed_path=printed_path, merge=options.merge) | 525 printed_path=printed_path, merge=options.merge) |
| 655 printed_path = True | 526 printed_path = True |
| 656 elif not refish.is_branch: | 527 elif rev_type == 'hash': |
| 657 # case 2 | 528 # case 2 |
| 658 self._AttemptRebase(upstream_branch, files, options, | 529 self._AttemptRebase(upstream_branch, files, options, |
| 659 newbase=refish.local_ref, printed_path=printed_path, | 530 newbase=revision, printed_path=printed_path, |
| 660 merge=options.merge) | 531 merge=options.merge) |
| 661 printed_path = True | 532 printed_path = True |
| 662 elif refish.upstream_branch != upstream_branch: | 533 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: |
| 663 # case 4 | 534 # case 4 |
| 664 new_base = refish.upstream_branch | 535 new_base = revision.replace('heads', 'remotes/' + self.remote) |
| 665 if not printed_path: | 536 if not printed_path: |
| 666 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) | 537 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) |
| 667 switch_error = ("Switching upstream branch from %s to %s\n" | 538 switch_error = ("Switching upstream branch from %s to %s\n" |
| 668 % (upstream_branch, new_base) + | 539 % (upstream_branch, new_base) + |
| 669 "Please merge or rebase manually:\n" + | 540 "Please merge or rebase manually:\n" + |
| 670 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + | 541 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + |
| 671 "OR git checkout -b <some new branch> %s" % new_base) | 542 "OR git checkout -b <some new branch> %s" % new_base) |
| 672 raise gclient_utils.Error(switch_error) | 543 raise gclient_utils.Error(switch_error) |
| 673 else: | 544 else: |
| 674 # case 3 - the default case | 545 # case 3 - the default case |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 785 return self.update(options, [], file_list) | 656 return self.update(options, [], file_list) |
| 786 | 657 |
| 787 default_rev = "refs/heads/master" | 658 default_rev = "refs/heads/master" |
| 788 if options.upstream: | 659 if options.upstream: |
| 789 if self._GetCurrentBranch(): | 660 if self._GetCurrentBranch(): |
| 790 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) | 661 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) |
| 791 default_rev = upstream_branch or default_rev | 662 default_rev = upstream_branch or default_rev |
| 792 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) | 663 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) |
| 793 if not deps_revision: | 664 if not deps_revision: |
| 794 deps_revision = default_rev | 665 deps_revision = default_rev |
| 795 refish = self.ParseRefish(deps_revision) | 666 if deps_revision.startswith('refs/heads/'): |
| 796 deps_revision = self.GetUsableRev(refish.remote_refspec, options) | 667 deps_revision = deps_revision.replace('refs/heads/', self.remote + '/') |
| 668 deps_revision = self.GetUsableRev(deps_revision, options) |
| 797 | 669 |
| 798 if file_list is not None: | 670 if file_list is not None: |
| 799 files = self._Capture(['diff', deps_revision, '--name-only']).split() | 671 files = self._Capture(['diff', deps_revision, '--name-only']).split() |
| 800 | 672 |
| 801 self._Run(['reset', '--hard', deps_revision], options) | 673 self._Run(['reset', '--hard', deps_revision], options) |
| 802 self._Run(['clean', '-f', '-d'], options) | 674 self._Run(['clean', '-f', '-d'], options) |
| 803 | 675 |
| 804 if file_list is not None: | 676 if file_list is not None: |
| 805 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) | 677 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) |
| 806 | 678 |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), | 835 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), |
| 964 os.path.join(self.checkout_path, '.git')) | 836 os.path.join(self.checkout_path, '.git')) |
| 965 except: | 837 except: |
| 966 traceback.print_exc(file=self.out_fh) | 838 traceback.print_exc(file=self.out_fh) |
| 967 raise | 839 raise |
| 968 finally: | 840 finally: |
| 969 if os.listdir(tmp_dir): | 841 if os.listdir(tmp_dir): |
| 970 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) | 842 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) |
| 971 gclient_utils.rmtree(tmp_dir) | 843 gclient_utils.rmtree(tmp_dir) |
| 972 self._UpdateBranchHeads(options, fetch=True) | 844 self._UpdateBranchHeads(options, fetch=True) |
| 973 | 845 self._Checkout(options, revision.replace('refs/heads/', ''), quiet=True) |
| 974 refish = self.ParseRefish(revision) | |
| 975 self._Checkout(options, refish.local_ref, quiet=True) | |
| 976 if self._GetCurrentBranch() is None: | 846 if self._GetCurrentBranch() is None: |
| 977 # Squelch git's very verbose detached HEAD warning and use our own | 847 # Squelch git's very verbose detached HEAD warning and use our own |
| 978 self.Print( | 848 self.Print( |
| 979 "Checked out %s to a detached HEAD. Before making any commits\n" | 849 ('Checked out %s to a detached HEAD. Before making any commits\n' |
| 980 "in this repo, you should use 'git checkout <branch>' to switch to\n" | 850 'in this repo, you should use \'git checkout <branch>\' to switch to\n' |
| 981 "an existing branch or use 'git checkout %s -b <branch>' to\n" | 851 'an existing branch or use \'git checkout %s -b <branch>\' to\n' |
| 982 "create a new branch for your work." % (revision, self.remote)) | 852 'create a new branch for your work.') % (revision, self.remote)) |
| 983 | 853 |
| 984 def _AskForData(self, prompt, options): | 854 def _AskForData(self, prompt, options): |
| 985 if options.jobs > 1: | 855 if options.jobs > 1: |
| 986 self.Print(prompt) | 856 self.Print(prompt) |
| 987 raise gclient_utils.Error("Background task requires input. Rerun " | 857 raise gclient_utils.Error("Background task requires input. Rerun " |
| 988 "gclient with --jobs=1 so that\n" | 858 "gclient with --jobs=1 so that\n" |
| 989 "interaction is possible.") | 859 "interaction is possible.") |
| 990 try: | 860 try: |
| 991 return raw_input(prompt) | 861 return raw_input(prompt) |
| 992 except KeyboardInterrupt: | 862 except KeyboardInterrupt: |
| (...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1669 new_command.append('--force') | 1539 new_command.append('--force') |
| 1670 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1540 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1671 new_command.extend(('--accept', 'theirs-conflict')) | 1541 new_command.extend(('--accept', 'theirs-conflict')) |
| 1672 elif options.manually_grab_svn_rev: | 1542 elif options.manually_grab_svn_rev: |
| 1673 new_command.append('--force') | 1543 new_command.append('--force') |
| 1674 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1544 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1675 new_command.extend(('--accept', 'postpone')) | 1545 new_command.extend(('--accept', 'postpone')) |
| 1676 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: | 1546 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: |
| 1677 new_command.extend(('--accept', 'postpone')) | 1547 new_command.extend(('--accept', 'postpone')) |
| 1678 return new_command | 1548 return new_command |
| OLD | NEW |