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

Side by Side Diff: gclient_scm.py

Issue 250523004: Added remote 'git' branch awareness to 'gclient' (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 years, 8 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 | testing_support/fake_repos.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 logging 9 import logging
10 import os 10 import os
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 269
270 The patch file is generated from a diff of the merge base of HEAD and 270 The patch file is generated from a diff of the merge base of HEAD and
271 its upstream branch. 271 its upstream branch.
272 """ 272 """
273 merge_base = self._Capture(['merge-base', 'HEAD', self.remote]) 273 merge_base = self._Capture(['merge-base', 'HEAD', self.remote])
274 gclient_utils.CheckCallAndFilter( 274 gclient_utils.CheckCallAndFilter(
275 ['git', 'diff', merge_base], 275 ['git', 'diff', merge_base],
276 cwd=self.checkout_path, 276 cwd=self.checkout_path,
277 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) 277 filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print)
278 278
279 def _ParseRevision(self, revision):
280 """Analyzes a revision string and determines what the repository-local
281 refspec is for that revision.
282
283 This function handles:
284 - Standard local revision paths (refs/heads, refs/remotes)
285 (e.g., refs/heads/master)
286 - Remote-relative mirrored revision paths (e.g., origin/master)
287 - Non-remote revision paths (anything else begining with 'refs/') are
288 assumed to be refs on our remote.
289 - Everything else (branches, commits)
290
291 Params:
292 revision: The revision string, fitting into one of the categories above,
293 to resolve
294
295 Return (dict): Refspec analysis dictionary including:
296 - 'revision' (str): The refspec to use to reference the revision in the
297 local repository (after it's been fetched).
298 - 'type' (str): The type of revision, one of ('branch', 'hash')
299 - 'mapped' (bool): If 'True', the revision is part of the default mapped
300 revision set ('refs/heads/*'); if 'False', it needs to be
301 explicitly fetched before it can be referenced at 'revision'.
302 - 'fetch_refspec' (str): The parameter to use to fetch this revision from
303 its origin.
304 """
305 result = {
306 'revision': revision,
307 'type': 'branch',
308 'mapped': True,
309 'fetch_refspec': revision,
310 }
311 #
312 # If 'revision' begins with some prefixes, chop them off to reference
313 # local refs.
314 for prefix, replacement in (
315 ('refs/heads/', None),
316 ('refs/remotes/%s/' % (self.remote), None),
317 ('%s/' % (self.remote), 'refs/heads/'),
318 ):
319 if revision.startswith(prefix):
320 local_revision = revision[len(prefix):]
321 if replacement is not None:
322 revision = revision.replace(prefix, replacement)
323
324 # Trim 'prefix' to make 'revision' relative to our standard
325 # 'refs/heads/*' fetch mapping.
326 result['revision'] = revision
327 result['fetch_refspec'] = local_revision
328 return result
329
330 # If 'revision' begins with 'refs/', treat it as an unmapped remote ref
331 if revision.startswith('refs/'):
332 result['fetch_refspec'] = '%s:%s' % (revision, revision)
333 result['mapped'] = False
334 return result
335
336 # Assume 'revision' is either a local branch or a commit
337 result['type'] = 'hash'
338 return result
339
340 def _Fetch(self, options, revision=None):
341 cfg = gclient_utils.DefaultIndexPackConfig()
342 fetch_cmd = cfg + ['fetch', self.remote]
343
344 if revision is not None:
345 refdict = self._ParseRevision(revision)
346 fetch_cmd.append(refdict['fetch_refspec'])
347
348 if not options.verbose:
349 fetch_cmd.append('--quiet')
350 fetch_cmd.append('--prune')
351 self._Run(fetch_cmd, options, retry=True)
352
353 def _FetchIfRemote(self, options, revision):
354 refdict = self._ParseRevision(revision)
355 if not refdict['mapped']:
356 self._Fetch(options, revision)
357
279 def _FetchAndReset(self, revision, file_list, options): 358 def _FetchAndReset(self, revision, file_list, options):
280 """Equivalent to git fetch; git reset.""" 359 """Equivalent to git fetch; git reset."""
360 self._UpdateBranchHeads(options, fetch=False)
361
362 # Fetch default fetch targets ('master'...)
363 self._Fetch(options)
364 # Fetch our specific revision from the remote, if it's not in the default
365 # fetch set.
366 self._FetchIfRemote(options, revision)
367
281 quiet = [] 368 quiet = []
282 if not options.verbose: 369 if not options.verbose:
283 quiet = ['--quiet'] 370 quiet.append('--quiet')
284 self._UpdateBranchHeads(options, fetch=False)
285
286 cfg = gclient_utils.DefaultIndexPackConfig(self.url)
287 fetch_cmd = cfg + ['fetch', self.remote, '--prune']
288 self._Run(fetch_cmd + quiet, options, retry=True)
289 self._Run(['reset', '--hard', revision] + quiet, options) 371 self._Run(['reset', '--hard', revision] + quiet, options)
290 if file_list is not None: 372 if file_list is not None:
291 files = self._Capture(['ls-files']).splitlines() 373 files = self._Capture(['ls-files']).splitlines()
292 file_list.extend([os.path.join(self.checkout_path, f) for f in files]) 374 file_list.extend([os.path.join(self.checkout_path, f) for f in files])
293 375
294 def update(self, options, args, file_list): 376 def update(self, options, args, file_list):
295 """Runs git to update or transparently checkout the working copy. 377 """Runs git to update or transparently checkout the working copy.
296 378
297 All updated files will be appended to file_list. 379 All updated files will be appended to file_list.
298 380
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 414
333 printed_path = False 415 printed_path = False
334 verbose = [] 416 verbose = []
335 if options.verbose: 417 if options.verbose:
336 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False) 418 self.Print('_____ %s%s' % (self.relpath, rev_str), timestamp=False)
337 verbose = ['--verbose'] 419 verbose = ['--verbose']
338 printed_path = True 420 printed_path = True
339 421
340 url = self._CreateOrUpdateCache(url, options) 422 url = self._CreateOrUpdateCache(url, options)
341 423
342 if revision.startswith('refs/'): 424 refdict = self._ParseRevision(revision)
343 rev_type = "branch" 425 revision = refdict['revision']
344 elif revision.startswith(self.remote + '/'): 426 rev_type = refdict['type']
345 # For compatibility with old naming, translate 'origin' to 'refs/heads'
346 revision = revision.replace(self.remote + '/', 'refs/heads/')
347 rev_type = "branch"
348 else:
349 # hash is also a tag, only make a distinction at checkout
350 rev_type = "hash"
351 427
352 if (not os.path.exists(self.checkout_path) or 428 if (not os.path.exists(self.checkout_path) or
353 (os.path.isdir(self.checkout_path) and 429 (os.path.isdir(self.checkout_path) and
354 not os.path.exists(os.path.join(self.checkout_path, '.git')))): 430 not os.path.exists(os.path.join(self.checkout_path, '.git')))):
355 if (os.path.isdir(self.checkout_path) and 431 if (os.path.isdir(self.checkout_path) and
356 not os.path.exists(os.path.join(self.checkout_path, '.git')) and 432 not os.path.exists(os.path.join(self.checkout_path, '.git')) and
357 os.listdir(self.checkout_path)): 433 os.listdir(self.checkout_path)):
358 # This is a little hack to work around checkouts which are created 434 # This is a little hack to work around checkouts which are created
359 # using "gclient config --name ." 435 # using "gclient config --name ."
360 if not self.relpath == '.': 436 if not self.relpath == '.':
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after
779 try: 855 try:
780 clone_cmd.append(tmp_dir) 856 clone_cmd.append(tmp_dir)
781 self._Run(clone_cmd, options, cwd=self._root_dir, retry=True) 857 self._Run(clone_cmd, options, cwd=self._root_dir, retry=True)
782 gclient_utils.safe_makedirs(self.checkout_path) 858 gclient_utils.safe_makedirs(self.checkout_path)
783 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'), 859 gclient_utils.safe_rename(os.path.join(tmp_dir, '.git'),
784 os.path.join(self.checkout_path, '.git')) 860 os.path.join(self.checkout_path, '.git'))
785 except: 861 except:
786 traceback.print_exc(file=self.out_fh) 862 traceback.print_exc(file=self.out_fh)
787 raise 863 raise
788 finally: 864 finally:
789 if os.listdir(tmp_dir): 865 if os.path.exists(tmp_dir) and os.listdir(tmp_dir):
dnj (Google) 2014/04/24 19:30:44 A bit unrelated, but I did hit this case when 'lis
790 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) 866 self.Print('_____ removing non-empty tmp dir %s' % tmp_dir)
791 gclient_utils.rmtree(tmp_dir) 867 gclient_utils.rmtree(tmp_dir)
868
869 # Fetch our specific revision
870 self._FetchIfRemote(options, revision)
871
792 if revision.startswith('refs/heads/'): 872 if revision.startswith('refs/heads/'):
793 self._Run( 873 self._Run(
794 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) 874 ['checkout', '--quiet', revision.replace('refs/heads/', '')], options)
795 else: 875 else:
796 # Squelch git's very verbose detached HEAD warning and use our own 876 # Squelch git's very verbose detached HEAD warning and use our own
797 self._Run(['checkout', '--quiet', revision], options) 877 self._Run(['checkout', '--quiet', revision], options)
798 self.Print( 878 self.Print(
799 ('Checked out %s to a detached HEAD. Before making any commits\n' 879 ('Checked out %s to a detached HEAD. Before making any commits\n'
800 'in this repo, you should use \'git checkout <branch>\' to switch to\n' 880 'in this repo, you should use \'git checkout <branch>\' to switch to\n'
801 'an existing branch or use \'git checkout %s -b <branch>\' to\n' 881 'an existing branch or use \'git checkout %s -b <branch>\' to\n'
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
966 return subprocess2.check_output(['git'] + args, env=env, **kwargs).strip() 1046 return subprocess2.check_output(['git'] + args, env=env, **kwargs).strip()
967 1047
968 def _UpdateBranchHeads(self, options, fetch=False): 1048 def _UpdateBranchHeads(self, options, fetch=False):
969 """Adds, and optionally fetches, "branch-heads" refspecs if requested.""" 1049 """Adds, and optionally fetches, "branch-heads" refspecs if requested."""
970 if hasattr(options, 'with_branch_heads') and options.with_branch_heads: 1050 if hasattr(options, 'with_branch_heads') and options.with_branch_heads:
971 config_cmd = ['config', 'remote.%s.fetch' % self.remote, 1051 config_cmd = ['config', 'remote.%s.fetch' % self.remote,
972 '+refs/branch-heads/*:refs/remotes/branch-heads/*', 1052 '+refs/branch-heads/*:refs/remotes/branch-heads/*',
973 '^\\+refs/branch-heads/\\*:.*$'] 1053 '^\\+refs/branch-heads/\\*:.*$']
974 self._Run(config_cmd, options) 1054 self._Run(config_cmd, options)
975 if fetch: 1055 if fetch:
976 cfg = gclient_utils.DefaultIndexPackConfig(self.url) 1056 self._Fetch(options)
977 fetch_cmd = cfg + ['fetch', self.remote]
978 if options.verbose:
979 fetch_cmd.append('--verbose')
980 self._Run(fetch_cmd, options, retry=True)
981 1057
982 def _Run(self, args, options, **kwargs): 1058 def _Run(self, args, options, **kwargs):
983 cwd = kwargs.setdefault('cwd', self.checkout_path) 1059 cwd = kwargs.setdefault('cwd', self.checkout_path)
984 kwargs.setdefault('stdout', self.out_fh) 1060 kwargs.setdefault('stdout', self.out_fh)
985 kwargs['filter_fn'] = self.filter 1061 kwargs['filter_fn'] = self.filter
986 kwargs.setdefault('print_stdout', False) 1062 kwargs.setdefault('print_stdout', False)
987 env = scm.GIT.ApplyEnvVars(kwargs) 1063 env = scm.GIT.ApplyEnvVars(kwargs)
988 cmd = ['git'] + args 1064 cmd = ['git'] + args
989 header = "running '%s' in '%s'" % (' '.join(cmd), cwd) 1065 header = "running '%s' in '%s'" % (' '.join(cmd), cwd)
990 self.filter(header) 1066 self.filter(header)
(...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after
1453 new_command.append('--force') 1529 new_command.append('--force')
1454 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1530 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1455 new_command.extend(('--accept', 'theirs-conflict')) 1531 new_command.extend(('--accept', 'theirs-conflict'))
1456 elif options.manually_grab_svn_rev: 1532 elif options.manually_grab_svn_rev:
1457 new_command.append('--force') 1533 new_command.append('--force')
1458 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1534 if command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1459 new_command.extend(('--accept', 'postpone')) 1535 new_command.extend(('--accept', 'postpone'))
1460 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]: 1536 elif command[0] != 'checkout' and scm.SVN.AssertVersion('1.6')[0]:
1461 new_command.extend(('--accept', 'postpone')) 1537 new_command.extend(('--accept', 'postpone'))
1462 return new_command 1538 return new_command
OLDNEW
« no previous file with comments | « no previous file | testing_support/fake_repos.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698