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

Side by Side Diff: gclient_scm.py

Issue 370393002: Revert of Consolidated 'git' refish parsing into a class (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 years, 5 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 | « gclient.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »
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
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
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « gclient.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698