Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: gclient_scm.py

Issue 328843005: Consolidated 'git' refish parsing into a class (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | tests/gclient_scm_test.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
9 import errno 10 import errno
10 import logging 11 import logging
11 import os 12 import os
12 import posixpath 13 import posixpath
13 import re 14 import re
14 import shlex 15 import shlex
15 import sys 16 import sys
16 import tempfile 17 import tempfile
17 import traceback 18 import traceback
18 import urlparse 19 import urlparse
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 if not command in commands: 154 if not command in commands:
154 raise gclient_utils.Error('Unknown command %s' % command) 155 raise gclient_utils.Error('Unknown command %s' % command)
155 156
156 if not command in dir(self): 157 if not command in dir(self):
157 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % ( 158 raise gclient_utils.Error('Command %s not implemented in %s wrapper' % (
158 command, self.__class__.__name__)) 159 command, self.__class__.__name__))
159 160
160 return getattr(self, command)(options, args, file_list) 161 return getattr(self, command)(options, args, file_list)
161 162
162 def GetActualRemoteURL(self, options): 163 def GetActualRemoteURL(self, options):
164 # Disable 'unused argument: options' warning | pylint: disable=W0613
M-A Ruel 2014/06/24 21:18:11 Can you remove it instead?
dnj 2014/06/25 01:55:05 Done.
163 """Attempt to determine the remote URL for this SCMWrapper.""" 165 """Attempt to determine the remote URL for this SCMWrapper."""
164 # Git
165 if os.path.exists(os.path.join(self.checkout_path, '.git')): 166 if os.path.exists(os.path.join(self.checkout_path, '.git')):
166 actual_remote_url = shlex.split(scm.GIT.Capture( 167 actual_remote_url = shlex.split(scm.GIT.Capture(
167 ['config', '--local', '--get-regexp', r'remote.*.url'], 168 ['config', '--local', '--get-regexp', r'remote.*.url'],
168 cwd=self.checkout_path))[1] 169 cwd=self.checkout_path))[1]
169 170
170 # If a cache_dir is used, obtain the actual remote URL from the cache. 171 # If a cache_dir is used, obtain the actual remote URL from the cache.
171 if getattr(self, 'cache_dir', None): 172 if getattr(self, 'cache_dir', None):
172 url, _ = gclient_utils.SplitUrlRevision(self.url) 173 url, _ = gclient_utils.SplitUrlRevision(self.url)
173 mirror = git_cache.Mirror(url) 174 mirror = git_cache.Mirror(url)
174 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') == 175 if (mirror.exists() and mirror.mirror_path.replace('\\', '/') ==
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 dest_path = tempfile.mkdtemp( 224 dest_path = tempfile.mkdtemp(
224 prefix=os.path.basename(self.relpath), 225 prefix=os.path.basename(self.relpath),
225 dir=bad_scm_dir) 226 dir=bad_scm_dir)
226 self.Print('_____ Conflicting directory found in %s. Moving to %s.' 227 self.Print('_____ Conflicting directory found in %s. Moving to %s.'
227 % (self.checkout_path, dest_path)) 228 % (self.checkout_path, dest_path))
228 gclient_utils.AddWarning('Conflicting directory %s moved to %s.' 229 gclient_utils.AddWarning('Conflicting directory %s moved to %s.'
229 % (self.checkout_path, dest_path)) 230 % (self.checkout_path, dest_path))
230 shutil.move(self.checkout_path, dest_path) 231 shutil.move(self.checkout_path, dest_path)
231 232
232 233
234 _GitRefishBase = collections.namedtuple('GitRef', (
235 # The initial string that the parsed Refish came from
M-A Ruel 2014/06/24 21:18:11 +4
dnj 2014/06/24 22:10:50 Done.
236 'source',
237 # Is this ref a branch?
238 'is_branch',
239 # The name of this ref when accessed through the local repository
240 'local_ref',
241 # The name of the remote that this refish resides on
242 'remote',
243 # The path of this refish on the remote (e.g., 'master')
244 'remote_ref',
245 # The remote-qualified path to this refish (e.g., 'origin/master')
246 'remote_refspec',
247 # If a branch, the expected name of the upstream branch that it should
248 # track; otherwise, 'None'
249 'upstream_branch',
250 ))
251
M-A Ruel 2014/06/24 21:18:11 2 lines
dnj 2014/06/24 22:10:50 Done.
252 class GitRefish(_GitRefishBase):
253 # Disable 'no __init__' warning | pylint: disable=W0232
254 """Class to implement refish parsing and properties.
M-A Ruel 2014/06/24 21:18:11 No need to state a class is a class, so use someth
dnj 2014/06/24 22:10:50 Done.
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.
M-A Ruel 2014/06/24 21:18:11 """Parses a ref into a 'GitRefish' instance."""
dnj 2014/06/24 22:10:50 Done.
271 """
272 # Use default set of remotes
273 if remote is None:
274 remote = cls._DEFAULT_REMOTE
M-A Ruel 2014/06/24 21:18:11 shorthand is: remote = remote or cls._DEFAULT_REMO
dnj 2014/06/24 22:10:50 Done.
275 remotes = {remote}
276 if other_remotes is not None:
M-A Ruel 2014/06/24 21:18:11 if other_remotes:
dnj 2014/06/24 22:10:50 Done.
277 remotes.update(other_remotes)
278
279 ref = ref.strip()
280 # Treat 'ref' as a '/'-delimited set of items; analyze their contents
281 ref_split = local_ref = remote_ref = tuple(ref.split('/'))
282 is_branch = True
283 if (
284 len(ref_split) > 1 and
285 ref_split[:1] == ('refs',)):
286 if (len(ref_split) >= 3) and (ref_split[1] == 'heads'):
287 # refs/heads/foo/bar
288 #
289 # Local Ref: foo/bar
290 # Remote Ref: foo/bar
291 local_ref = remote_ref = ref_split[2:]
292 elif (len(ref_split) >= 4) and (ref_split[1] == 'remotes'):
293 # refs/remotes/<REMOTE>/foo/bar
294 # This is a bad assumption, and this logic can be improved, but it
295 # is consistent with traditional 'gclient' logic.
296 #
297 # Local Ref: refs/remotes/<REMOTE>/foo/bar
298 # Remote: <REMOTE>
299 # Remote Ref: foo/bar
300 remote = ref_split[2]
301 remote_ref = ref_split[3:]
302 else:
303 # If it's not part of the standard branch set, assume it's a remote
304 # branch.
305 #
306 # refs/foo/bar
307 #
308 # Local Ref: refs/foo/bar
309 # Remote ref: refs/foo/bar
310 pass
311 elif (len(ref_split) >= 2) and (ref_split[0] in remotes):
312 # origin/master
313 #
314 # Local Ref: refs/remotes/origin/master
315 # Remote Ref: master
316 remote = ref_split[0]
317 remote_ref = ref_split[1:]
318 local_ref = ('refs', 'remotes') + ref_split
319 else:
320 # It could be a hash, a short-hash, or a tag
321 is_branch = False
322
323 # Determine how the ref should be referenced remotely
324 if is_branch:
325 # foo/bar/baz => origin/foo/bar/baz
326 remote_refspec = (remote,) + remote_ref
327 else:
328 # Refer to the hash/tag directly. This is actually not allowed in
329 # 'fetch', although it oftentimes works regardless.
330 #
331 # <HASH/TAG> => <HASH/TAG>
332 remote_refspec = (ref,)
333
334 # Calculate the upstream branch
335 if is_branch:
336 # Convert '/refs/heads/...' to 'refs/remotes/REMOTE/...'
337 if ref_split[:2] == ('refs', 'heads'):
338 upstream_branch = ('refs', 'remotes', remote) + ref_split[2:]
339 else:
340 upstream_branch = ref_split
341 else:
342 upstream_branch = None
343
344 def compose(ref_tuple):
345 if ref_tuple is not None:
346 ref_tuple = '/'.join(ref_tuple)
347 return ref_tuple
348
349 # Instantiate
350 return cls(
351 source=ref,
352 is_branch=is_branch,
353 local_ref=compose(local_ref),
354 remote=remote,
355 remote_ref=compose(remote_ref),
356 remote_refspec=compose(remote_refspec),
357 upstream_branch=compose(upstream_branch),
358 )
359
360
233 class GitWrapper(SCMWrapper): 361 class GitWrapper(SCMWrapper):
234 """Wrapper for Git""" 362 """Wrapper for Git"""
235 name = 'git' 363 name = 'git'
236 remote = 'origin' 364 remote = 'origin'
237 365
238 cache_dir = None 366 cache_dir = None
239 367
240 def __init__(self, url=None, *args): 368 def __init__(self, url=None, *args):
241 """Removes 'git+' fake prefix from git URL.""" 369 """Removes 'git+' fake prefix from git URL."""
242 if url.startswith('git+http://') or url.startswith('git+https://'): 370 if url.startswith('git+http://') or url.startswith('git+https://'):
243 url = url[4:] 371 url = url[4:]
244 SCMWrapper.__init__(self, url, *args) 372 SCMWrapper.__init__(self, url, *args)
245 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh } 373 filter_kwargs = { 'time_throttle': 1, 'out_fh': self.out_fh }
246 if self.out_cb: 374 if self.out_cb:
247 filter_kwargs['predicate'] = self.out_cb 375 filter_kwargs['predicate'] = self.out_cb
248 self.filter = gclient_utils.GitFilter(**filter_kwargs) 376 self.filter = gclient_utils.GitFilter(**filter_kwargs)
249 377
250 @staticmethod 378 @staticmethod
251 def BinaryExists(): 379 def BinaryExists():
252 """Returns true if the command exists.""" 380 """Returns true if the command exists."""
253 try: 381 try:
254 # We assume git is newer than 1.7. See: crbug.com/114483 382 # We assume git is newer than 1.7. See: crbug.com/114483
255 result, version = scm.GIT.AssertVersion('1.7') 383 result, version = scm.GIT.AssertVersion('1.7')
256 if not result: 384 if not result:
257 raise gclient_utils.Error('Git version is older than 1.7: %s' % version) 385 raise gclient_utils.Error('Git version is older than 1.7: %s' % version)
258 return result 386 return result
259 except OSError: 387 except OSError:
260 return False 388 return False
261 389
390 def ParseRefish(self, value, **kwargs):
391 kwargs.setdefault('remote', self.remote)
392 return GitRefish.Parse(value, **kwargs)
393
262 def GetCheckoutRoot(self): 394 def GetCheckoutRoot(self):
263 return scm.GIT.GetCheckoutRoot(self.checkout_path) 395 return scm.GIT.GetCheckoutRoot(self.checkout_path)
264 396
265 def GetRevisionDate(self, _revision): 397 def GetRevisionDate(self, _revision):
266 """Returns the given revision's date in ISO-8601 format (which contains the 398 """Returns the given revision's date in ISO-8601 format (which contains the
267 time zone).""" 399 time zone)."""
268 # TODO(floitsch): get the time-stamp of the given revision and not just the 400 # TODO(floitsch): get the time-stamp of the given revision and not just the
269 # time-stamp of the currently checked out revision. 401 # time-stamp of the currently checked out revision.
270 return self._Capture(['log', '-n', '1', '--format=%ai']) 402 return self._Capture(['log', '-n', '1', '--format=%ai'])
271 403
(...skipping 14 matching lines...) Expand all
286 418
287 The patch file is generated from a diff of the merge base of HEAD and 419 The patch file is generated from a diff of the merge base of HEAD and
288 its upstream branch. 420 its upstream branch.
289 """ 421 """
290 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) 422 merge_base = self._Capture(['merge-base', 'HEAD', self.remote])
291 gclient_utils.CheckCallAndFilter( 423 gclient_utils.CheckCallAndFilter(
292 ['git', 'diff', merge_base], 424 ['git', 'diff', merge_base],
293 cwd=self.checkout_path, 425 cwd=self.checkout_path,
294 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) 426 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print)
295 427
296 def _FetchAndReset(self, revision, file_list, options): 428 def _FetchAndReset(self, refish, file_list, options):
297 """Equivalent to git fetch; git reset.""" 429 """Equivalent to git fetch; git reset."""
298 quiet = [] 430 quiet = []
299 if not options.verbose: 431 if not options.verbose:
300 quiet = ['--quiet'] 432 quiet = ['--quiet']
301 self._UpdateBranchHeads(options, fetch=False) 433 self._UpdateBranchHeads(options, fetch=False)
302 434
303 self._Fetch(options, prune=True, quiet=options.verbose) 435 self._Fetch(options, prune=True, quiet=options.verbose)
304 self._Run(['reset', '--hard', revision] + quiet, options) 436 self._Run(['reset', '--hard', refish.local_ref] + quiet, options)
305 if file_list is not None: 437 if file_list is not None:
306 files = self._Capture(['ls-files']).splitlines() 438 files = self._Capture(['ls-files']).splitlines()
307 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) 439 file_list.extend([os.path.join(self.checkout_path, f) for f in files])
308 440
309 def update(self, options, args, file_list): 441 def update(self, options, args, file_list):
310 """Runs git to update or transparently checkout the working copy. 442 """Runs git to update or transparently checkout the working copy.
311 443
312 All updated files will be appended to file_list. 444 All updated files will be appended to file_list.
313 445
314 Raises: 446 Raises:
315 Error: if can't get URL for relative path. 447 Error: if can't get URL for relative path.
316 """ 448 """
317 if args: 449 if args:
318 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args)) 450 raise gclient_utils.Error("Unsupported argument(s): %s" % ",".join(args))
319 451
320 self._CheckMinVersion("1.6.6") 452 self._CheckMinVersion("1.6.6")
321 453
322 # If a dependency is not pinned, track the default remote branch. 454 # If a dependency is not pinned, track the default remote branch.
323 default_rev = 'refs/remotes/%s/master' % self.remote 455 default_rev = 'refs/remotes/%s/master' % (self.remote,)
M-A Ruel 2014/06/24 21:18:11 Not needed.
dnj 2014/06/24 22:10:50 I think it looks cleaner to be explicit in formatt
M-A Ruel 2014/06/25 01:24:58 Please ask around to others if anyone else ever us
dnj 2014/06/25 01:55:06 I actually picked up this habit from iannucci@, wh
324 url, deps_revision = gclient_utils.SplitUrlRevision(self.url) 456 url, deps_revision = gclient_utils.SplitUrlRevision(self.url)
325 rev_str = "" 457 rev_str = ""
326 revision = deps_revision 458 revision = deps_revision
327 managed = True 459 managed = True
328 if options.revision: 460 if options.revision:
329 # Override the revision number. 461 # Override the revision number.
330 revision = str(options.revision) 462 revision = str(options.revision)
331 if revision == 'unmanaged': 463 if revision == 'unmanaged':
332 # Check again for a revision in case an initial ref was specified 464 # Check again for a revision in case an initial ref was specified
333 # in the url, for example bla.git@refs/heads/custombranch 465 # in the url, for example bla.git@refs/heads/custombranch
(...skipping 13 matching lines...) Expand all
347 rev_str = ' at %s' % revision 479 rev_str = ' at %s' % revision
348 files = [] if file_list is not None else None 480 files = [] if file_list is not None else None
349 481
350 printed_path = False 482 printed_path = False
351 verbose = [] 483 verbose = []
352 if options.verbose: 484 if options.verbose:
353 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) 485 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
354 verbose = ['--verbose'] 486 verbose = ['--verbose']
355 printed_path = True 487 printed_path = True
356 488
489 refish = self.ParseRefish(revision)
357 url = self._CreateOrUpdateCache(url, options) 490 url = self._CreateOrUpdateCache(url, options)
358 491
359 if revision.startswith('refs/'):
360 rev_type = "branch"
361 elif revision.startswith(self.remote + '/'):
362 # Rewrite remote refs to their local equivalents.
363 revision = 'refs/remotes/' + revision
364 rev_type = "branch"
365 else:
366 # hash is also a tag, only make a distinction at checkout
367 rev_type = "hash"
368
369 if (not os.path.exists(self.checkout_path) or 492 if (not os.path.exists(self.checkout_path) or
370 (os.path.isdir(self.checkout_path) and 493 (os.path.isdir(self.checkout_path) and
371 not os.path.exists(os.path.join(self.checkout_path, '.git')))): 494 not os.path.exists(os.path.join(self.checkout_path, '.git')))):
372 try: 495 try:
373 self._Clone(revision, url, options) 496 self._Clone(refish.local_ref, url, options)
374 except subprocess2.CalledProcessError: 497 except subprocess2.CalledProcessError:
375 self._DeleteOrMove(options.force) 498 self._DeleteOrMove(options.force)
376 self._Clone(revision, url, options) 499 self._Clone(refish.local_ref, url, options)
377 if deps_revision and deps_revision.startswith('branch-heads/'): 500 if deps_revision and deps_revision.startswith('branch-heads/'):
378 deps_branch = deps_revision.replace('branch-heads/', '') 501 deps_branch = deps_revision.replace('branch-heads/', '')
379 self._Capture(['branch', deps_branch, deps_revision]) 502 self._Capture(['branch', deps_branch, deps_revision])
380 self._Checkout(options, deps_branch, quiet=True) 503 self._Checkout(options, deps_branch, quiet=True)
381 if file_list is not None: 504 if file_list is not None:
382 files = self._Capture(['ls-files']).splitlines() 505 files = self._Capture(['ls-files']).splitlines()
383 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) 506 file_list.extend([os.path.join(self.checkout_path, f) for f in files])
384 if not verbose: 507 if not verbose:
385 # Make the output a little prettier. It's nice to have some whitespace 508 # Make the output a little prettier. It's nice to have some whitespace
386 # between projects when cloning. 509 # between projects when cloning.
(...skipping 17 matching lines...) Expand all
404 if (current_url.rstrip('/') != url.rstrip('/') and 527 if (current_url.rstrip('/') != url.rstrip('/') and
405 url != 'git://foo' and 528 url != 'git://foo' and
406 subprocess2.capture( 529 subprocess2.capture(
407 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote], 530 ['git', 'config', 'remote.%s.gclient-auto-fix-url' % self.remote],
408 cwd=self.checkout_path).strip() != 'False'): 531 cwd=self.checkout_path).strip() != 'False'):
409 self.Print('_____ switching %s to a new upstream' % self.relpath) 532 self.Print('_____ switching %s to a new upstream' % self.relpath)
410 # Make sure it's clean 533 # Make sure it's clean
411 self._CheckClean(rev_str) 534 self._CheckClean(rev_str)
412 # Switch over to the new upstream 535 # Switch over to the new upstream
413 self._Run(['remote', 'set-url', self.remote, url], options) 536 self._Run(['remote', 'set-url', self.remote, url], options)
414 self._FetchAndReset(revision, file_list, options) 537 self._FetchAndReset(refish, file_list, options)
415 return_early = True 538 return_early = True
416 539
417 if return_early: 540 if return_early:
418 return self._Capture(['rev-parse', '--verify', 'HEAD']) 541 return self._Capture(['rev-parse', '--verify', 'HEAD'])
419 542
420 cur_branch = self._GetCurrentBranch() 543 cur_branch = self._GetCurrentBranch()
421 544
422 # Cases: 545 # Cases:
423 # 0) HEAD is detached. Probably from our initial clone. 546 # 0) HEAD is detached. Probably from our initial clone.
424 # - make sure HEAD is contained by a named ref, then update. 547 # - make sure HEAD is contained by a named ref, then update.
(...skipping 22 matching lines...) Expand all
447 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) 570 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path)
448 if not upstream_branch or not upstream_branch.startswith('refs/remotes'): 571 if not upstream_branch or not upstream_branch.startswith('refs/remotes'):
449 current_type = "hash" 572 current_type = "hash"
450 logging.debug("Current branch is not tracking an upstream (remote)" 573 logging.debug("Current branch is not tracking an upstream (remote)"
451 " branch.") 574 " branch.")
452 elif upstream_branch.startswith('refs/remotes'): 575 elif upstream_branch.startswith('refs/remotes'):
453 current_type = "branch" 576 current_type = "branch"
454 else: 577 else:
455 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch) 578 raise gclient_utils.Error('Invalid Upstream: %s' % upstream_branch)
456 579
457 if not scm.GIT.IsValidRevision(self.checkout_path, revision, sha_only=True): 580 if not scm.GIT.IsValidRevision(self.checkout_path, refish.local_ref,
581 sha_only=True):
458 # Update the remotes first so we have all the refs. 582 # Update the remotes first so we have all the refs.
459 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'], 583 remote_output = scm.GIT.Capture(['remote'] + verbose + ['update'],
460 cwd=self.checkout_path) 584 cwd=self.checkout_path)
461 if verbose: 585 if verbose:
462 self.Print(remote_output) 586 self.Print(remote_output)
463 587
464 self._UpdateBranchHeads(options, fetch=True) 588 self._UpdateBranchHeads(options, fetch=True)
465 589
466 # This is a big hammer, debatable if it should even be here... 590 # This is a big hammer, debatable if it should even be here...
467 if options.force or options.reset: 591 if options.force or options.reset:
468 target = 'HEAD' 592 target = 'HEAD'
469 if options.upstream and upstream_branch: 593 if options.upstream and upstream_branch:
470 target = upstream_branch 594 target = upstream_branch
471 self._Run(['reset', '--hard', target], options) 595 self._Run(['reset', '--hard', target], options)
472 596
473 if current_type == 'detached': 597 if current_type == 'detached':
474 # case 0 598 # case 0
475 self._CheckClean(rev_str) 599 self._CheckClean(rev_str)
476 self._CheckDetachedHead(rev_str, options) 600 self._CheckDetachedHead(rev_str, options)
477 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == revision: 601 if self._Capture(['rev-list', '-n', '1', 'HEAD']) == refish.local_ref:
478 self.Print('Up-to-date; skipping checkout.') 602 self.Print('Up-to-date; skipping checkout.')
479 else: 603 else:
480 self._Checkout(options, revision, quiet=True) 604 self._Checkout(options, refish.local_ref, quiet=True)
481 if not printed_path: 605 if not printed_path:
482 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) 606 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
483 elif current_type == 'hash': 607 elif current_type == 'hash':
484 # case 1 608 # case 1
485 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None: 609 if scm.GIT.IsGitSvn(self.checkout_path) and upstream_branch is not None:
486 # Our git-svn branch (upstream_branch) is our upstream 610 # Our git-svn branch (upstream_branch) is our upstream
487 self._AttemptRebase(upstream_branch, files, options, 611 self._AttemptRebase(upstream_branch, files, options,
488 newbase=revision, printed_path=printed_path, 612 newbase=refish.local_ref,
489 merge=options.merge) 613 printed_path=printed_path, merge=options.merge)
490 printed_path = True 614 printed_path = True
491 else: 615 else:
492 # Can't find a merge-base since we don't know our upstream. That makes 616 # Can't find a merge-base since we don't know our upstream. That makes
493 # this command VERY likely to produce a rebase failure. For now we 617 # this command VERY likely to produce a rebase failure. For now we
494 # assume origin is our upstream since that's what the old behavior was. 618 # assume origin is our upstream since that's what the old behavior was.
495 upstream_branch = self.remote 619 upstream_branch = self.remote
496 if options.revision or deps_revision: 620 if options.revision or deps_revision:
497 upstream_branch = revision 621 upstream_branch = refish.local_ref
498 self._AttemptRebase(upstream_branch, files, options, 622 self._AttemptRebase(upstream_branch, files, options,
499 printed_path=printed_path, merge=options.merge) 623 printed_path=printed_path, merge=options.merge)
500 printed_path = True 624 printed_path = True
501 elif rev_type == 'hash': 625 elif not refish.is_branch:
502 # case 2 626 # case 2
503 self._AttemptRebase(upstream_branch, files, options, 627 self._AttemptRebase(upstream_branch, files, options,
504 newbase=revision, printed_path=printed_path, 628 newbase=refish.local_ref, printed_path=printed_path,
505 merge=options.merge) 629 merge=options.merge)
506 printed_path = True 630 printed_path = True
507 elif revision.replace('heads', 'remotes/' + self.remote) != upstream_branch: 631 elif refish.upstream_branch != upstream_branch:
508 # case 4 632 # case 4
509 new_base = revision.replace('heads', 'remotes/' + self.remote) 633 new_base = refish.upstream_branch
510 if not printed_path: 634 if not printed_path:
511 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) 635 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
512 switch_error = ("Switching upstream branch from %s to %s\n" 636 switch_error = ("Switching upstream branch from %s to %s\n"
513 % (upstream_branch, new_base) + 637 % (upstream_branch, new_base) +
514 "Please merge or rebase manually:\n" + 638 "Please merge or rebase manually:\n" +
515 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) + 639 "cd %s; git rebase %s\n" % (self.checkout_path, new_base) +
516 "OR git checkout -b <some new branch> %s" % new_base) 640 "OR git checkout -b <some new branch> %s" % new_base)
517 raise gclient_utils.Error(switch_error) 641 raise gclient_utils.Error(switch_error)
518 else: 642 else:
519 # case 3 - the default case 643 # case 3 - the default case
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
630 return self.update(options, [], file_list) 754 return self.update(options, [], file_list)
631 755
632 default_rev = "refs/heads/master" 756 default_rev = "refs/heads/master"
633 if options.upstream: 757 if options.upstream:
634 if self._GetCurrentBranch(): 758 if self._GetCurrentBranch():
635 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path) 759 upstream_branch = scm.GIT.GetUpstreamBranch(self.checkout_path)
636 default_rev = upstream_branch or default_rev 760 default_rev = upstream_branch or default_rev
637 _, deps_revision = gclient_utils.SplitUrlRevision(self.url) 761 _, deps_revision = gclient_utils.SplitUrlRevision(self.url)
638 if not deps_revision: 762 if not deps_revision:
639 deps_revision = default_rev 763 deps_revision = default_rev
640 if deps_revision.startswith('refs/heads/'): 764 refish = self.ParseRefish(deps_revision)
641 deps_revision = deps_revision.replace('refs/heads/', self.remote + '/') 765 deps_revision = self.GetUsableRev(refish.remote_refspec, options)
642 deps_revision = self.GetUsableRev(deps_revision, options)
643 766
644 if file_list is not None: 767 if file_list is not None:
645 files = self._Capture(['diff', deps_revision, '--name-only']).split() 768 files = self._Capture(['diff', deps_revision, '--name-only']).split()
646 769
647 self._Run(['reset', '--hard', deps_revision], options) 770 self._Run(['reset', '--hard', deps_revision], options)
648 self._Run(['clean', '-f', '-d'], options) 771 self._Run(['clean', '-f', '-d'], options)
649 772
650 if file_list is not None: 773 if file_list is not None:
651 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) 774 file_list.extend([os.path.join(self.checkout_path, f) for f in files])
652 775
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
812 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), 935 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'),
813 os.path.join(self.checkout_path, '.git')) 936 os.path.join(self.checkout_path, '.git'))
814 except: 937 except:
815 traceback.print_exc(file=self.out_fh) 938 traceback.print_exc(file=self.out_fh)
816 raise 939 raise
817 finally: 940 finally:
818 if os.listdir(tmp_dir): 941 if os.listdir(tmp_dir):
819 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) 942 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir)
820 gclient_utils.rmtree(tmp_dir) 943 gclient_utils.rmtree(tmp_dir)
821 self._UpdateBranchHeads(options, fetch=True) 944 self._UpdateBranchHeads(options, fetch=True)
822 self._Checkout(options, revision.replace('refs/heads/', ''), quiet=True) 945
946 refish = self.ParseRefish(revision)
947 self._Checkout(options, refish.local_ref, quiet=True)
823 if self._GetCurrentBranch() is None: 948 if self._GetCurrentBranch() is None:
824 # Squelch git's very verbose detached HEAD warning and use our own 949 # Squelch git's very verbose detached HEAD warning and use our own
825 self.Print( 950 self.Print("""\
826 ('Checked out %s to a detached HEAD. Before making any commits\n' 951 Checked out %s to a detached HEAD. Before making any commits
M-A Ruel 2014/06/24 21:18:11 It really breaks vim collapsing when using indent
dnj 2014/06/24 22:10:50 Done.
827 'in this repo, you should use \'git checkout <branch>\' to switch to\n' 952 in this repo, you should use 'git checkout <branch>' to switch to
828 'an existing branch or use \'git checkout %s -b <branch>\' to\n' 953 an existing branch or use 'git checkout %s -b <branch>' to
829 'create a new branch for your work.') % (revision, self.remote)) 954 create a new branch for your work.""" % (revision, self.remote))
830 955
831 def _AskForData(self, prompt, options): 956 def _AskForData(self, prompt, options):
832 if options.jobs > 1: 957 if options.jobs > 1:
833 self.Print(prompt) 958 self.Print(prompt)
834 raise gclient_utils.Error("Background task requires input. Rerun " 959 raise gclient_utils.Error("Background task requires input. Rerun "
835 "gclient with --jobs=1 so that\n" 960 "gclient with --jobs=1 so that\n"
836 "interaction is possible.") 961 "interaction is possible.")
837 try: 962 try:
838 return raw_input(prompt) 963 return raw_input(prompt)
839 except KeyboardInterrupt: 964 except KeyboardInterrupt:
(...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after
1513 new_command.append('--force') 1638 new_command.append('--force')
1514 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1639 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1515 new_command.extend(('--accept', 'theirs-conflict')) 1640 new_command.extend(('--accept', 'theirs-conflict'))
1516 elif options.manually_grab_svn_rev: 1641 elif options.manually_grab_svn_rev:
1517 new_command.append('--force') 1642 new_command.append('--force')
1518 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1643 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1519 new_command.extend(('--accept', 'postpone')) 1644 new_command.extend(('--accept', 'postpone'))
1520 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1645 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1521 new_command.extend(('--accept', 'postpone')) 1646 new_command.extend(('--accept', 'postpone'))
1522 return new_command 1647 return new_command
OLDNEW
« no previous file with comments | « no previous file | tests/gclient_scm_test.py » ('j') | tests/gclient_scm_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698