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

Side by Side Diff: git_cl.py

Issue 2394033003: Remove SVN (and dcommit) support from git-cl (Closed)
Patch Set: Rebase Created 4 years 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
« no previous file with comments | « PRESUBMIT.py ('k') | tests/abandon.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld and Gerrit.""" 8 """A git-command for integrating reviews on Rietveld and Gerrit."""
9 9
10 from __future__ import print_function 10 from __future__ import print_function
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 import rietveld 59 import rietveld
60 import scm 60 import scm
61 import subcommand 61 import subcommand
62 import subprocess2 62 import subprocess2
63 import watchlists 63 import watchlists
64 64
65 __version__ = '2.0' 65 __version__ = '2.0'
66 66
67 COMMIT_BOT_EMAIL = 'commit-bot@chromium.org' 67 COMMIT_BOT_EMAIL = 'commit-bot@chromium.org'
68 DEFAULT_SERVER = 'https://codereview.chromium.org' 68 DEFAULT_SERVER = 'https://codereview.chromium.org'
69 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' 69 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-land'
70 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' 70 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup'
71 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit'
72 REFS_THAT_ALIAS_TO_OTHER_REFS = { 71 REFS_THAT_ALIAS_TO_OTHER_REFS = {
73 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', 72 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master',
74 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', 73 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
75 } 74 }
76 75
77 # Valid extensions for files we want to lint. 76 # Valid extensions for files we want to lint.
78 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" 77 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
79 DEFAULT_LINT_IGNORE_REGEX = r"$^" 78 DEFAULT_LINT_IGNORE_REGEX = r"$^"
80 79
81 # Buildbucket master name prefix. 80 # Buildbucket master name prefix.
(...skipping 612 matching lines...) Expand 10 before | Expand all | Expand 10 after
694 'failure_reason': build.get('failure_reason'), 693 'failure_reason': build.get('failure_reason'),
695 'url': build.get('url'), 694 'url': build.get('url'),
696 } 695 }
697 696
698 converted = [] 697 converted = []
699 for _, build in sorted(builds.items()): 698 for _, build in sorted(builds.items()):
700 converted.append(convert_build_dict(build)) 699 converted.append(convert_build_dict(build))
701 write_json(output_file, converted) 700 write_json(output_file, converted)
702 701
703 702
704 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
705 """Return the corresponding git ref if |base_url| together with |glob_spec|
706 matches the full |url|.
707
708 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below).
709 """
710 fetch_suburl, as_ref = glob_spec.split(':')
711 if allow_wildcards:
712 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl)
713 if glob_match:
714 # Parse specs like "branches/*/src:refs/remotes/svn/*" or
715 # "branches/{472,597,648}/src:refs/remotes/svn/*".
716 branch_re = re.escape(base_url)
717 if glob_match.group(1):
718 branch_re += '/' + re.escape(glob_match.group(1))
719 wildcard = glob_match.group(2)
720 if wildcard == '*':
721 branch_re += '([^/]*)'
722 else:
723 # Escape and replace surrounding braces with parentheses and commas
724 # with pipe symbols.
725 wildcard = re.escape(wildcard)
726 wildcard = re.sub('^\\\\{', '(', wildcard)
727 wildcard = re.sub('\\\\,', '|', wildcard)
728 wildcard = re.sub('\\\\}$', ')', wildcard)
729 branch_re += wildcard
730 if glob_match.group(3):
731 branch_re += re.escape(glob_match.group(3))
732 match = re.match(branch_re, url)
733 if match:
734 return re.sub('\*$', match.group(1), as_ref)
735
736 # Parse specs like "trunk/src:refs/remotes/origin/trunk".
737 if fetch_suburl:
738 full_url = base_url + '/' + fetch_suburl
739 else:
740 full_url = base_url
741 if full_url == url:
742 return as_ref
743 return None
744
745
746 def print_stats(similarity, find_copies, args): 703 def print_stats(similarity, find_copies, args):
747 """Prints statistics about the change to the user.""" 704 """Prints statistics about the change to the user."""
748 # --no-ext-diff is broken in some versions of Git, so try to work around 705 # --no-ext-diff is broken in some versions of Git, so try to work around
749 # this by overriding the environment (but there is still a problem if the 706 # this by overriding the environment (but there is still a problem if the
750 # git config key "diff.external" is used). 707 # git config key "diff.external" is used).
751 env = GetNoGitPagerEnv() 708 env = GetNoGitPagerEnv()
752 if 'GIT_EXTERNAL_DIFF' in env: 709 if 'GIT_EXTERNAL_DIFF' in env:
753 del env['GIT_EXTERNAL_DIFF'] 710 del env['GIT_EXTERNAL_DIFF']
754 711
755 if find_copies: 712 if find_copies:
(...skipping 13 matching lines...) Expand all
769 726
770 class BuildbucketResponseException(Exception): 727 class BuildbucketResponseException(Exception):
771 pass 728 pass
772 729
773 730
774 class Settings(object): 731 class Settings(object):
775 def __init__(self): 732 def __init__(self):
776 self.default_server = None 733 self.default_server = None
777 self.cc = None 734 self.cc = None
778 self.root = None 735 self.root = None
779 self.is_git_svn = None
780 self.svn_branch = None
781 self.tree_status_url = None 736 self.tree_status_url = None
782 self.viewvc_url = None 737 self.viewvc_url = None
783 self.updated = False 738 self.updated = False
784 self.is_gerrit = None 739 self.is_gerrit = None
785 self.squash_gerrit_uploads = None 740 self.squash_gerrit_uploads = None
786 self.gerrit_skip_ensure_authenticated = None 741 self.gerrit_skip_ensure_authenticated = None
787 self.git_editor = None 742 self.git_editor = None
788 self.project = None 743 self.project = None
789 self.force_https_commit_url = None 744 self.force_https_commit_url = None
790 self.pending_ref_prefix = None 745 self.pending_ref_prefix = None
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
832 if not os.path.isdir(local_url): 787 if not os.path.isdir(local_url):
833 return None 788 return None
834 git_cache.Mirror.SetCachePath(os.path.dirname(local_url)) 789 git_cache.Mirror.SetCachePath(os.path.dirname(local_url))
835 remote_url = git_cache.Mirror.CacheDirToUrl(local_url) 790 remote_url = git_cache.Mirror.CacheDirToUrl(local_url)
836 # Use the /dev/null print_func to avoid terminal spew in WaitForRealCommit. 791 # Use the /dev/null print_func to avoid terminal spew in WaitForRealCommit.
837 mirror = git_cache.Mirror(remote_url, print_func = lambda *args: None) 792 mirror = git_cache.Mirror(remote_url, print_func = lambda *args: None)
838 if mirror.exists(): 793 if mirror.exists():
839 return mirror 794 return mirror
840 return None 795 return None
841 796
842 def GetIsGitSvn(self):
843 """Return true if this repo looks like it's using git-svn."""
844 if self.is_git_svn is None:
845 if self.GetPendingRefPrefix():
846 # If PENDING_REF_PREFIX is set then it's a pure git repo no matter what.
847 self.is_git_svn = False
848 else:
849 # If you have any "svn-remote.*" config keys, we think you're using svn.
850 self.is_git_svn = RunGitWithCode(
851 ['config', '--local', '--get-regexp', r'^svn-remote\.'])[0] == 0
852 return self.is_git_svn
853
854 def GetSVNBranch(self):
855 if self.svn_branch is None:
856 if not self.GetIsGitSvn():
857 DieWithError('Repo doesn\'t appear to be a git-svn repo.')
858
859 # Try to figure out which remote branch we're based on.
860 # Strategy:
861 # 1) iterate through our branch history and find the svn URL.
862 # 2) find the svn-remote that fetches from the URL.
863
864 # regexp matching the git-svn line that contains the URL.
865 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE)
866
867 # We don't want to go through all of history, so read a line from the
868 # pipe at a time.
869 # The -100 is an arbitrary limit so we don't search forever.
870 cmd = ['git', 'log', '-100', '--pretty=medium']
871 proc = subprocess2.Popen(cmd, stdout=subprocess2.PIPE,
872 env=GetNoGitPagerEnv())
873 url = None
874 for line in proc.stdout:
875 match = git_svn_re.match(line)
876 if match:
877 url = match.group(1)
878 proc.stdout.close() # Cut pipe.
879 break
880
881 if url:
882 svn_remote_re = re.compile(r'^svn-remote\.([^.]+)\.url (.*)$')
883 remotes = RunGit(['config', '--get-regexp',
884 r'^svn-remote\..*\.url']).splitlines()
885 for remote in remotes:
886 match = svn_remote_re.match(remote)
887 if match:
888 remote = match.group(1)
889 base_url = match.group(2)
890 rewrite_root = RunGit(
891 ['config', 'svn-remote.%s.rewriteRoot' % remote],
892 error_ok=True).strip()
893 if rewrite_root:
894 base_url = rewrite_root
895 fetch_spec = RunGit(
896 ['config', 'svn-remote.%s.fetch' % remote],
897 error_ok=True).strip()
898 if fetch_spec:
899 self.svn_branch = MatchSvnGlob(url, base_url, fetch_spec, False)
900 if self.svn_branch:
901 break
902 branch_spec = RunGit(
903 ['config', 'svn-remote.%s.branches' % remote],
904 error_ok=True).strip()
905 if branch_spec:
906 self.svn_branch = MatchSvnGlob(url, base_url, branch_spec, True)
907 if self.svn_branch:
908 break
909 tag_spec = RunGit(
910 ['config', 'svn-remote.%s.tags' % remote],
911 error_ok=True).strip()
912 if tag_spec:
913 self.svn_branch = MatchSvnGlob(url, base_url, tag_spec, True)
914 if self.svn_branch:
915 break
916
917 if not self.svn_branch:
918 DieWithError('Can\'t guess svn branch -- try specifying it on the '
919 'command line')
920
921 return self.svn_branch
922
923 def GetTreeStatusUrl(self, error_ok=False): 797 def GetTreeStatusUrl(self, error_ok=False):
924 if not self.tree_status_url: 798 if not self.tree_status_url:
925 error_message = ('You must configure your tree status URL by running ' 799 error_message = ('You must configure your tree status URL by running '
926 '"git cl config".') 800 '"git cl config".')
927 self.tree_status_url = self._GetRietveldConfig( 801 self.tree_status_url = self._GetRietveldConfig(
928 'tree-status-url', error_ok=error_ok, error_message=error_message) 802 'tree-status-url', error_ok=error_ok, error_message=error_message)
929 return self.tree_status_url 803 return self.tree_status_url
930 804
931 def GetViewVCUrl(self): 805 def GetViewVCUrl(self):
932 if not self.viewvc_url: 806 if not self.viewvc_url:
(...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after
1390 upstream_branch = _git_get_branch_config_value('merge', branch=branch) 1264 upstream_branch = _git_get_branch_config_value('merge', branch=branch)
1391 1265
1392 if upstream_branch: 1266 if upstream_branch:
1393 remote = _git_get_branch_config_value('remote', branch=branch) 1267 remote = _git_get_branch_config_value('remote', branch=branch)
1394 else: 1268 else:
1395 upstream_branch = RunGit(['config', 'rietveld.upstream-branch'], 1269 upstream_branch = RunGit(['config', 'rietveld.upstream-branch'],
1396 error_ok=True).strip() 1270 error_ok=True).strip()
1397 if upstream_branch: 1271 if upstream_branch:
1398 remote = RunGit(['config', 'rietveld.upstream-remote']).strip() 1272 remote = RunGit(['config', 'rietveld.upstream-remote']).strip()
1399 else: 1273 else:
1400 # Fall back on trying a git-svn upstream branch. 1274 # Else, try to guess the origin remote.
1401 if settings.GetIsGitSvn(): 1275 remote_branches = RunGit(['branch', '-r']).split()
1402 upstream_branch = settings.GetSVNBranch() 1276 if 'origin/master' in remote_branches:
1277 # Fall back on origin/master if it exits.
1278 remote = 'origin'
1279 upstream_branch = 'refs/heads/master'
1403 else: 1280 else:
1404 # Else, try to guess the origin remote. 1281 DieWithError(
1405 remote_branches = RunGit(['branch', '-r']).split() 1282 'Unable to determine default branch to diff against.\n'
1406 if 'origin/master' in remote_branches: 1283 'Either pass complete "git diff"-style arguments, like\n'
1407 # Fall back on origin/master if it exits. 1284 ' git cl upload origin/master\n'
1408 remote = 'origin' 1285 'or verify this branch is set up to track another \n'
1409 upstream_branch = 'refs/heads/master' 1286 '(via the --track argument to "git checkout -b ...").')
1410 elif 'origin/trunk' in remote_branches:
1411 # Fall back on origin/trunk if it exists. Generally a shared
1412 # git-svn clone
1413 remote = 'origin'
1414 upstream_branch = 'refs/heads/trunk'
1415 else:
1416 DieWithError(
1417 'Unable to determine default branch to diff against.\n'
1418 'Either pass complete "git diff"-style arguments, like\n'
1419 ' git cl upload origin/master\n'
1420 'or verify this branch is set up to track another \n'
1421 '(via the --track argument to "git checkout -b ...").')
1422 1287
1423 return remote, upstream_branch 1288 return remote, upstream_branch
1424 1289
1425 def GetCommonAncestorWithUpstream(self): 1290 def GetCommonAncestorWithUpstream(self):
1426 upstream_branch = self.GetUpstreamBranch() 1291 upstream_branch = self.GetUpstreamBranch()
1427 if not BranchExists(upstream_branch): 1292 if not BranchExists(upstream_branch):
1428 DieWithError('The upstream for the current branch (%s) does not exist ' 1293 DieWithError('The upstream for the current branch (%s) does not exist '
1429 'anymore.\nPlease fix it and try again.' % self.GetBranch()) 1294 'anymore.\nPlease fix it and try again.' % self.GetBranch())
1430 return git_common.get_or_create_merge_base(self.GetBranch(), 1295 return git_common.get_or_create_merge_base(self.GetBranch(),
1431 upstream_branch) 1296 upstream_branch)
(...skipping 18 matching lines...) Expand all
1450 remote, branch = self.FetchUpstreamTuple(branch) 1315 remote, branch = self.FetchUpstreamTuple(branch)
1451 branch = ShortBranchName(branch) 1316 branch = ShortBranchName(branch)
1452 if remote != '.' or branch.startswith('refs/remotes'): 1317 if remote != '.' or branch.startswith('refs/remotes'):
1453 break 1318 break
1454 else: 1319 else:
1455 remotes = RunGit(['remote'], error_ok=True).split() 1320 remotes = RunGit(['remote'], error_ok=True).split()
1456 if len(remotes) == 1: 1321 if len(remotes) == 1:
1457 remote, = remotes 1322 remote, = remotes
1458 elif 'origin' in remotes: 1323 elif 'origin' in remotes:
1459 remote = 'origin' 1324 remote = 'origin'
1460 logging.warning('Could not determine which remote this change is ' 1325 logging.warn('Could not determine which remote this change is '
1461 'associated with, so defaulting to "%s". This may ' 1326 'associated with, so defaulting to "%s".' % self._remote)
1462 'not be what you want. You may prevent this message '
1463 'by running "git svn info" as documented here: %s',
1464 self._remote,
1465 GIT_INSTRUCTIONS_URL)
1466 else: 1327 else:
1467 logging.warn('Could not determine which remote this change is ' 1328 logging.warn('Could not determine which remote this change is '
1468 'associated with. You may prevent this message by ' 1329 'associated with.')
1469 'running "git svn info" as documented here: %s',
1470 GIT_INSTRUCTIONS_URL)
1471 branch = 'HEAD' 1330 branch = 'HEAD'
1472 if branch.startswith('refs/remotes'): 1331 if branch.startswith('refs/remotes'):
1473 self._remote = (remote, branch) 1332 self._remote = (remote, branch)
1474 elif branch.startswith('refs/branch-heads/'): 1333 elif branch.startswith('refs/branch-heads/'):
1475 self._remote = (remote, branch.replace('refs/', 'refs/remotes/')) 1334 self._remote = (remote, branch.replace('refs/', 'refs/remotes/'))
1476 else: 1335 else:
1477 self._remote = (remote, 'refs/remotes/%s/%s' % (remote, branch)) 1336 self._remote = (remote, 'refs/remotes/%s/%s' % (remote, branch))
1478 return self._remote 1337 return self._remote
1479 1338
1480 def GitSanityChecks(self, upstream_git_obj): 1339 def GitSanityChecks(self, upstream_git_obj):
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1519 return False 1378 return False
1520 return True 1379 return True
1521 1380
1522 def GetGitBaseUrlFromConfig(self): 1381 def GetGitBaseUrlFromConfig(self):
1523 """Return the configured base URL from branch.<branchname>.baseurl. 1382 """Return the configured base URL from branch.<branchname>.baseurl.
1524 1383
1525 Returns None if it is not set. 1384 Returns None if it is not set.
1526 """ 1385 """
1527 return self._GitGetBranchConfigValue('base-url') 1386 return self._GitGetBranchConfigValue('base-url')
1528 1387
1529 def GetGitSvnRemoteUrl(self):
1530 """Return the configured git-svn remote URL parsed from git svn info.
1531
1532 Returns None if it is not set.
1533 """
1534 # URL is dependent on the current directory.
1535 data = RunGit(['svn', 'info'], cwd=settings.GetRoot())
1536 if data:
1537 keys = dict(line.split(': ', 1) for line in data.splitlines()
1538 if ': ' in line)
1539 return keys.get('URL', None)
1540 return None
1541
1542 def GetRemoteUrl(self): 1388 def GetRemoteUrl(self):
1543 """Return the configured remote URL, e.g. 'git://example.org/foo.git/'. 1389 """Return the configured remote URL, e.g. 'git://example.org/foo.git/'.
1544 1390
1545 Returns None if there is no remote. 1391 Returns None if there is no remote.
1546 """ 1392 """
1547 remote, _ = self.GetRemoteBranch() 1393 remote, _ = self.GetRemoteBranch()
1548 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() 1394 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip()
1549 1395
1550 # If URL is pointing to a local directory, it is probably a git cache. 1396 # If URL is pointing to a local directory, it is probably a git cache.
1551 if os.path.isdir(url): 1397 if os.path.isdir(url):
(...skipping 734 matching lines...) Expand 10 before | Expand all | Expand 10 after
2286 if options.title is not None: 2132 if options.title is not None:
2287 upload_args.extend(['--title', options.title]) 2133 upload_args.extend(['--title', options.title])
2288 if options.message: 2134 if options.message:
2289 upload_args.extend(['--message', options.message]) 2135 upload_args.extend(['--message', options.message])
2290 upload_args.extend(['--issue', str(self.GetIssue())]) 2136 upload_args.extend(['--issue', str(self.GetIssue())])
2291 print('This branch is associated with issue %s. ' 2137 print('This branch is associated with issue %s. '
2292 'Adding patch to that issue.' % self.GetIssue()) 2138 'Adding patch to that issue.' % self.GetIssue())
2293 else: 2139 else:
2294 if options.title is not None: 2140 if options.title is not None:
2295 upload_args.extend(['--title', options.title]) 2141 upload_args.extend(['--title', options.title])
2296 message = (options.title or options.message or 2142 if options.message:
2297 CreateDescriptionFromLog(args)) 2143 message = options.message
2144 else:
2145 message = CreateDescriptionFromLog(args)
2146 if options.title:
2147 message = options.title + '\n\n' + message
2298 change_desc = ChangeDescription(message) 2148 change_desc = ChangeDescription(message)
2299 if options.reviewers or options.tbr_owners: 2149 if options.reviewers or options.tbr_owners:
2300 change_desc.update_reviewers(options.reviewers, 2150 change_desc.update_reviewers(options.reviewers,
2301 options.tbr_owners, 2151 options.tbr_owners,
2302 change) 2152 change)
2303 if not options.force: 2153 if not options.force:
2304 change_desc.prompt(bug=options.bug) 2154 change_desc.prompt(bug=options.bug)
2305 2155
2306 if not change_desc.description: 2156 if not change_desc.description:
2307 print('Description is empty; aborting.') 2157 print('Description is empty; aborting.')
(...skipping 28 matching lines...) Expand all
2336 upload_args.append('--private') 2186 upload_args.append('--private')
2337 2187
2338 upload_args.extend(['--git_similarity', str(options.similarity)]) 2188 upload_args.extend(['--git_similarity', str(options.similarity)])
2339 if not options.find_copies: 2189 if not options.find_copies:
2340 upload_args.extend(['--git_no_find_copies']) 2190 upload_args.extend(['--git_no_find_copies'])
2341 2191
2342 # Include the upstream repo's URL in the change -- this is useful for 2192 # Include the upstream repo's URL in the change -- this is useful for
2343 # projects that have their source spread across multiple repos. 2193 # projects that have their source spread across multiple repos.
2344 remote_url = self.GetGitBaseUrlFromConfig() 2194 remote_url = self.GetGitBaseUrlFromConfig()
2345 if not remote_url: 2195 if not remote_url:
2346 if settings.GetIsGitSvn(): 2196 if self.GetRemoteUrl() and '/' in self.GetUpstreamBranch():
2347 remote_url = self.GetGitSvnRemoteUrl() 2197 remote_url = '%s@%s' % (self.GetRemoteUrl(),
2348 else: 2198 self.GetUpstreamBranch().split('/')[-1])
2349 if self.GetRemoteUrl() and '/' in self.GetUpstreamBranch():
2350 remote_url = '%s@%s' % (self.GetRemoteUrl(),
2351 self.GetUpstreamBranch().split('/')[-1])
2352 if remote_url: 2199 if remote_url:
2353 remote, remote_branch = self.GetRemoteBranch() 2200 remote, remote_branch = self.GetRemoteBranch()
2354 target_ref = GetTargetRef(remote, remote_branch, options.target_branch, 2201 target_ref = GetTargetRef(remote, remote_branch, options.target_branch,
2355 pending_prefix_check=True, 2202 pending_prefix_check=True,
2356 remote_url=self.GetRemoteUrl()) 2203 remote_url=self.GetRemoteUrl())
2357 if target_ref: 2204 if target_ref:
2358 upload_args.extend(['--target_ref', target_ref]) 2205 upload_args.extend(['--target_ref', target_ref])
2359 2206
2360 # Look for dependent patchsets. See crbug.com/480453 for more details. 2207 # Look for dependent patchsets. See crbug.com/480453 for more details.
2361 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) 2208 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
(...skipping 1977 matching lines...) Expand 10 before | Expand all | Expand 10 after
4339 git config --unset branch.branch_name.skip-deps-uploads 4186 git config --unset branch.branch_name.skip-deps-uploads
4340 Can also set the above globally by using the --global flag. 4187 Can also set the above globally by using the --global flag.
4341 """ 4188 """
4342 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 4189 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
4343 help='bypass upload presubmit hook') 4190 help='bypass upload presubmit hook')
4344 parser.add_option('--bypass-watchlists', action='store_true', 4191 parser.add_option('--bypass-watchlists', action='store_true',
4345 dest='bypass_watchlists', 4192 dest='bypass_watchlists',
4346 help='bypass watchlists auto CC-ing reviewers') 4193 help='bypass watchlists auto CC-ing reviewers')
4347 parser.add_option('-f', action='store_true', dest='force', 4194 parser.add_option('-f', action='store_true', dest='force',
4348 help="force yes to questions (don't prompt)") 4195 help="force yes to questions (don't prompt)")
4349 parser.add_option('-m', dest='message', help='message for patchset') 4196 parser.add_option('--message', '-m', dest='message',
4197 help='message for patchset')
4350 parser.add_option('-b', '--bug', 4198 parser.add_option('-b', '--bug',
4351 help='pre-populate the bug number(s) for this issue. ' 4199 help='pre-populate the bug number(s) for this issue. '
4352 'If several, separate with commas') 4200 'If several, separate with commas')
4353 parser.add_option('--message-file', dest='message_file', 4201 parser.add_option('--message-file', dest='message_file',
4354 help='file which contains message for patchset') 4202 help='file which contains message for patchset')
4355 parser.add_option('-t', dest='title', 4203 parser.add_option('--title', '-t', dest='title',
4356 help='title for patchset (Rietveld only)') 4204 help='title for patchset (Rietveld only)')
4357 parser.add_option('-r', '--reviewers', 4205 parser.add_option('-r', '--reviewers',
4358 action='append', default=[], 4206 action='append', default=[],
4359 help='reviewer email addresses') 4207 help='reviewer email addresses')
4360 parser.add_option('--cc', 4208 parser.add_option('--cc',
4361 action='append', default=[], 4209 action='append', default=[],
4362 help='cc email addresses') 4210 help='cc email addresses')
4363 parser.add_option('-s', '--send-mail', action='store_true', 4211 parser.add_option('-s', '--send-mail', action='store_true',
4364 help='send email to reviewer immediately') 4212 help='send email to reviewer immediately')
4365 parser.add_option('--emulate_svn_auto_props', 4213 parser.add_option('--emulate_svn_auto_props',
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
4418 if options.cq_dry_run and options.use_commit_queue: 4266 if options.cq_dry_run and options.use_commit_queue:
4419 parser.error('only one of --use-commit-queue and --cq-dry-run allowed.') 4267 parser.error('only one of --use-commit-queue and --cq-dry-run allowed.')
4420 4268
4421 # For sanity of test expectations, do this otherwise lazy-loading *now*. 4269 # For sanity of test expectations, do this otherwise lazy-loading *now*.
4422 settings.GetIsGerrit() 4270 settings.GetIsGerrit()
4423 4271
4424 cl = Changelist(auth_config=auth_config, codereview=options.forced_codereview) 4272 cl = Changelist(auth_config=auth_config, codereview=options.forced_codereview)
4425 return cl.CMDUpload(options, args, orig_args) 4273 return cl.CMDUpload(options, args, orig_args)
4426 4274
4427 4275
4428 def IsSubmoduleMergeCommit(ref):
4429 # When submodules are added to the repo, we expect there to be a single
4430 # non-git-svn merge commit at remote HEAD with a signature comment.
4431 pattern = '^SVN changes up to revision [0-9]*$'
4432 cmd = ['rev-list', '--merges', '--grep=%s' % pattern, '%s^!' % ref]
4433 return RunGit(cmd) != ''
4434
4435
4436 def SendUpstream(parser, args, cmd): 4276 def SendUpstream(parser, args, cmd):
4437 """Common code for CMDland and CmdDCommit 4277 """Common code for CMDland and CmdDCommit
4438 4278
4439 In case of Gerrit, uses Gerrit REST api to "submit" the issue, which pushes 4279 In case of Gerrit, uses Gerrit REST api to "submit" the issue, which pushes
4440 upstream and closes the issue automatically and atomically. 4280 upstream and closes the issue automatically and atomically.
4441 4281
4442 Otherwise (in case of Rietveld): 4282 Otherwise (in case of Rietveld):
4443 Squashes branch into a single commit. 4283 Squashes branch into a single commit.
4444 Updates commit message with metadata (e.g. pointer to review). 4284 Updates commit message with metadata (e.g. pointer to review).
4445 Pushes the code upstream. 4285 Pushes the code upstream.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
4479 '"Forge-Author" permission.') 4319 '"Forge-Author" permission.')
4480 if not cl.GetIssue(): 4320 if not cl.GetIssue():
4481 DieWithError('You must upload the change first to Gerrit.\n' 4321 DieWithError('You must upload the change first to Gerrit.\n'
4482 ' If you would rather have `git cl land` upload ' 4322 ' If you would rather have `git cl land` upload '
4483 'automatically for you, see http://crbug.com/642759') 4323 'automatically for you, see http://crbug.com/642759')
4484 return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks, 4324 return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks,
4485 options.verbose) 4325 options.verbose)
4486 4326
4487 current = cl.GetBranch() 4327 current = cl.GetBranch()
4488 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) 4328 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch())
4489 if not settings.GetIsGitSvn() and remote == '.': 4329 if remote == '.':
4490 print() 4330 print()
4491 print('Attempting to push branch %r into another local branch!' % current) 4331 print('Attempting to push branch %r into another local branch!' % current)
4492 print() 4332 print()
4493 print('Either reparent this branch on top of origin/master:') 4333 print('Either reparent this branch on top of origin/master:')
4494 print(' git reparent-branch --root') 4334 print(' git reparent-branch --root')
4495 print() 4335 print()
4496 print('OR run `git rebase-update` if you think the parent branch is ') 4336 print('OR run `git rebase-update` if you think the parent branch is ')
4497 print('already committed.') 4337 print('already committed.')
4498 print() 4338 print()
4499 print(' Current parent: %r' % upstream_branch) 4339 print(' Current parent: %r' % upstream_branch)
4500 return 1 4340 return 1
4501 4341
4502 if not args or cmd == 'land': 4342 if not args:
4503 # Default to merging against our best guess of the upstream branch. 4343 # Default to merging against our best guess of the upstream branch.
4504 args = [cl.GetUpstreamBranch()] 4344 args = [cl.GetUpstreamBranch()]
4505 4345
4506 if options.contributor: 4346 if options.contributor:
4507 if not re.match('^.*\s<\S+@\S+>$', options.contributor): 4347 if not re.match('^.*\s<\S+@\S+>$', options.contributor):
4508 print("Please provide contibutor as 'First Last <email@example.com>'") 4348 print("Please provide contibutor as 'First Last <email@example.com>'")
4509 return 1 4349 return 1
4510 4350
4511 base_branch = args[0] 4351 base_branch = args[0]
4512 base_has_submodules = IsSubmoduleMergeCommit(base_branch)
4513 4352
4514 if git_common.is_dirty_git_tree(cmd): 4353 if git_common.is_dirty_git_tree('land'):
4515 return 1 4354 return 1
4516 4355
4517 # This rev-list syntax means "show all commits not in my branch that 4356 # This rev-list syntax means "show all commits not in my branch that
4518 # are in base_branch". 4357 # are in base_branch".
4519 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), 4358 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(),
4520 base_branch]).splitlines() 4359 base_branch]).splitlines()
4521 if upstream_commits: 4360 if upstream_commits:
4522 print('Base branch "%s" has %d commits ' 4361 print('Base branch "%s" has %d commits '
4523 'not in this branch.' % (base_branch, len(upstream_commits))) 4362 'not in this branch.' % (base_branch, len(upstream_commits)))
4524 print('Run "git merge %s" before attempting to %s.' % (base_branch, cmd)) 4363 print('Run "git merge %s" before attempting to land.' % base_branch)
4525 return 1 4364 return 1
4526 4365
4527 # This is the revision `svn dcommit` will commit on top of.
4528 svn_head = None
4529 if cmd == 'dcommit' or base_has_submodules:
4530 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1',
4531 '--pretty=format:%H'])
4532
4533 if cmd == 'dcommit':
4534 # If the base_head is a submodule merge commit, the first parent of the
4535 # base_head should be a git-svn commit, which is what we're interested in.
4536 base_svn_head = base_branch
4537 if base_has_submodules:
4538 base_svn_head += '^1'
4539
4540 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head])
4541 if extra_commits:
4542 print('This branch has %d additional commits not upstreamed yet.'
4543 % len(extra_commits.splitlines()))
4544 print('Upstream "%s" or rebase this branch on top of the upstream trunk '
4545 'before attempting to %s.' % (base_branch, cmd))
4546 return 1
4547
4548 merge_base = RunGit(['merge-base', base_branch, 'HEAD']).strip() 4366 merge_base = RunGit(['merge-base', base_branch, 'HEAD']).strip()
4549 if not options.bypass_hooks: 4367 if not options.bypass_hooks:
4550 author = None 4368 author = None
4551 if options.contributor: 4369 if options.contributor:
4552 author = re.search(r'\<(.*)\>', options.contributor).group(1) 4370 author = re.search(r'\<(.*)\>', options.contributor).group(1)
4553 hook_results = cl.RunHook( 4371 hook_results = cl.RunHook(
4554 committing=True, 4372 committing=True,
4555 may_prompt=not options.force, 4373 may_prompt=not options.force,
4556 verbose=options.verbose, 4374 verbose=options.verbose,
4557 change=cl.GetChange(merge_base, author)) 4375 change=cl.GetChange(merge_base, author))
4558 if not hook_results.should_continue(): 4376 if not hook_results.should_continue():
4559 return 1 4377 return 1
4560 4378
4561 # Check the tree status if the tree status URL is set. 4379 # Check the tree status if the tree status URL is set.
4562 status = GetTreeStatus() 4380 status = GetTreeStatus()
4563 if 'closed' == status: 4381 if 'closed' == status:
4564 print('The tree is closed. Please wait for it to reopen. Use ' 4382 print('The tree is closed. Please wait for it to reopen. Use '
4565 '"git cl %s --bypass-hooks" to commit on a closed tree.' % cmd) 4383 '"git cl land --bypass-hooks" to commit on a closed tree.')
4566 return 1 4384 return 1
4567 elif 'unknown' == status: 4385 elif 'unknown' == status:
4568 print('Unable to determine tree status. Please verify manually and ' 4386 print('Unable to determine tree status. Please verify manually and '
4569 'use "git cl %s --bypass-hooks" to commit on a closed tree.' % cmd) 4387 'use "git cl land --bypass-hooks" to commit on a closed tree.')
4570 return 1 4388 return 1
4571 4389
4572 change_desc = ChangeDescription(options.message) 4390 change_desc = ChangeDescription(options.message)
4573 if not change_desc.description and cl.GetIssue(): 4391 if not change_desc.description and cl.GetIssue():
4574 change_desc = ChangeDescription(cl.GetDescription()) 4392 change_desc = ChangeDescription(cl.GetDescription())
4575 4393
4576 if not change_desc.description: 4394 if not change_desc.description:
4577 if not cl.GetIssue() and options.bypass_hooks: 4395 if not cl.GetIssue() and options.bypass_hooks:
4578 change_desc = ChangeDescription(CreateDescriptionFromLog([merge_base])) 4396 change_desc = ChangeDescription(CreateDescriptionFromLog([merge_base]))
4579 else: 4397 else:
(...skipping 19 matching lines...) Expand all
4599 4417
4600 print('Description:') 4418 print('Description:')
4601 print(commit_desc.description) 4419 print(commit_desc.description)
4602 4420
4603 branches = [merge_base, cl.GetBranchRef()] 4421 branches = [merge_base, cl.GetBranchRef()]
4604 if not options.force: 4422 if not options.force:
4605 print_stats(options.similarity, options.find_copies, branches) 4423 print_stats(options.similarity, options.find_copies, branches)
4606 4424
4607 # We want to squash all this branch's commits into one commit with the proper 4425 # We want to squash all this branch's commits into one commit with the proper
4608 # description. We do this by doing a "reset --soft" to the base branch (which 4426 # description. We do this by doing a "reset --soft" to the base branch (which
4609 # keeps the working copy the same), then dcommitting that. If origin/master 4427 # keeps the working copy the same), then dcommitting that.
4610 # has a submodule merge commit, we'll also need to cherry-pick the squashed
4611 # commit onto a branch based on the git-svn head.
4612 MERGE_BRANCH = 'git-cl-commit' 4428 MERGE_BRANCH = 'git-cl-commit'
4613 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick' 4429 CHERRY_PICK_BRANCH = 'git-cl-cherry-pick'
4614 # Delete the branches if they exist. 4430 # Delete the branches if they exist.
4615 for branch in [MERGE_BRANCH, CHERRY_PICK_BRANCH]: 4431 for branch in [MERGE_BRANCH, CHERRY_PICK_BRANCH]:
4616 showref_cmd = ['show-ref', '--quiet', '--verify', 'refs/heads/%s' % branch] 4432 showref_cmd = ['show-ref', '--quiet', '--verify', 'refs/heads/%s' % branch]
4617 result = RunGitWithCode(showref_cmd) 4433 result = RunGitWithCode(showref_cmd)
4618 if result[0] == 0: 4434 if result[0] == 0:
4619 RunGit(['branch', '-D', branch]) 4435 RunGit(['branch', '-D', branch])
4620 4436
4621 # We might be in a directory that's present in this branch but not in the 4437 # We might be in a directory that's present in this branch but not in the
(...skipping 14 matching lines...) Expand all
4636 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) 4452 RunGit(['checkout', '-q', '-b', MERGE_BRANCH])
4637 RunGit(['reset', '--soft', merge_base]) 4453 RunGit(['reset', '--soft', merge_base])
4638 if options.contributor: 4454 if options.contributor:
4639 RunGit( 4455 RunGit(
4640 [ 4456 [
4641 'commit', '--author', options.contributor, 4457 'commit', '--author', options.contributor,
4642 '-m', commit_desc.description, 4458 '-m', commit_desc.description,
4643 ]) 4459 ])
4644 else: 4460 else:
4645 RunGit(['commit', '-m', commit_desc.description]) 4461 RunGit(['commit', '-m', commit_desc.description])
4646 if base_has_submodules: 4462
4647 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() 4463 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch())
4648 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) 4464 mirror = settings.GetGitMirror(remote)
4649 RunGit(['checkout', CHERRY_PICK_BRANCH]) 4465 if mirror:
4650 RunGit(['cherry-pick', cherry_pick_commit]) 4466 pushurl = mirror.url
4651 if cmd == 'land': 4467 git_numberer = _GitNumbererState.load(pushurl, branch)
4652 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) 4468 else:
4653 logging.debug('remote: %s, branch %s', remote, branch) 4469 pushurl = remote # Usually, this is 'origin'.
4654 mirror = settings.GetGitMirror(remote) 4470 git_numberer = _GitNumbererState.load(
4655 if mirror: 4471 RunGit(['config', 'remote.%s.url' % remote]).strip(), branch)
4656 pushurl = mirror.url
4657 git_numberer = _GitNumbererState.load(pushurl, branch)
4658 else:
4659 pushurl = remote # Usually, this is 'origin'.
4660 git_numberer = _GitNumbererState.load(
4661 RunGit(['config', 'remote.%s.url' % remote]).strip(), branch)
4662 4472
4663 pending_prefix = git_numberer.pending_prefix 4473 pending_prefix = git_numberer.pending_prefix
4664 4474
4665 if git_numberer.should_git_number: 4475 if git_numberer.should_git_number:
4666 # TODO(tandrii): run git fetch in a loop + autorebase when there there 4476 # TODO(tandrii): run git fetch in a loop + autorebase when there there
4667 # is no pending ref to push to? 4477 # is no pending ref to push to?
4668 logging.debug('Adding git number footers') 4478 logging.debug('Adding git number footers')
4669 parent_msg = RunGit(['show', '-s', '--format=%B', merge_base]).strip() 4479 parent_msg = RunGit(['show', '-s', '--format=%B', merge_base]).strip()
4670 commit_desc.update_with_git_number_footers(merge_base, parent_msg, 4480 commit_desc.update_with_git_number_footers(merge_base, parent_msg,
4671 branch) 4481 branch)
4672 # Ensure timestamps are monotonically increasing. 4482 # Ensure timestamps are monotonically increasing.
4673 timestamp = max(1 + _get_committer_timestamp(merge_base), 4483 timestamp = max(1 + _get_committer_timestamp(merge_base),
4674 _get_committer_timestamp('HEAD')) 4484 _get_committer_timestamp('HEAD'))
4675 _git_amend_head(commit_desc.description, timestamp) 4485 _git_amend_head(commit_desc.description, timestamp)
4676 change_desc = ChangeDescription(commit_desc.description) 4486 change_desc = ChangeDescription(commit_desc.description)
4677 # If gnumbd is sitll ON and we ultimately push to branch with 4487 # If gnumbd is sitll ON and we ultimately push to branch with
4678 # pending_prefix, gnumbd will modify footers we've just inserted with 4488 # pending_prefix, gnumbd will modify footers we've just inserted with
4679 # 'Original-', which is annoying but still technically correct. 4489 # 'Original-', which is annoying but still technically correct.
4680 4490
4681 if not pending_prefix or branch.startswith(pending_prefix): 4491 if not pending_prefix or branch.startswith(pending_prefix):
4682 # If not using refs/pending/heads/* at all, or target ref is already set 4492 # If not using refs/pending/heads/* at all, or target ref is already set
4683 # to pending, then push to the target ref directly. 4493 # to pending, then push to the target ref directly.
4684 # NB(tandrii): I think branch.startswith(pending_prefix) never happens 4494 # NB(tandrii): I think branch.startswith(pending_prefix) never happens
4685 # in practise. I really tried to create a new branch tracking 4495 # in practise. I really tried to create a new branch tracking
4686 # refs/pending/heads/master directly and git cl land failed long before 4496 # refs/pending/heads/master directly and git cl land failed long before
4687 # reaching this. Disagree? Comment on http://crbug.com/642493. 4497 # reaching this. Disagree? Comment on http://crbug.com/642493.
4688 if pending_prefix: 4498 if pending_prefix:
4689 print('\n\nYOU GOT A CHANCE TO WIN A FREE GIFT!\n\n' 4499 print('\n\nYOU GOT A CHANCE TO WIN A FREE GIFT!\n\n'
4690 'Grab your .git/config, add instructions how to reproduce ' 4500 'Grab your .git/config, add instructions how to reproduce '
4691 'this, and post it to http://crbug.com/642493.\n' 4501 'this, and post it to http://crbug.com/642493.\n'
4692 'The first reporter gets a free "Black Swan" book from ' 4502 'The first reporter gets a free "Black Swan" book from '
4693 'tandrii@\n\n') 4503 'tandrii@\n\n')
4694 retcode, output = RunGitWithCode( 4504 retcode, output = RunGitWithCode(
4695 ['push', '--porcelain', pushurl, 'HEAD:%s' % branch]) 4505 ['push', '--porcelain', pushurl, 'HEAD:%s' % branch])
4696 pushed_to_pending = pending_prefix and branch.startswith(pending_prefix) 4506 pushed_to_pending = pending_prefix and branch.startswith(pending_prefix)
4697 else:
4698 # Cherry-pick the change on top of pending ref and then push it.
4699 assert branch.startswith('refs/'), branch
4700 assert pending_prefix[-1] == '/', pending_prefix
4701 pending_ref = pending_prefix + branch[len('refs/'):]
4702 retcode, output = PushToGitPending(pushurl, pending_ref)
4703 pushed_to_pending = (retcode == 0)
4704 if retcode == 0:
4705 revision = RunGit(['rev-parse', 'HEAD']).strip()
4706 else: 4507 else:
4707 # dcommit the merge branch. 4508 # Cherry-pick the change on top of pending ref and then push it.
4708 cmd_args = [ 4509 assert branch.startswith('refs/'), branch
4709 'svn', 'dcommit', 4510 assert pending_prefix[-1] == '/', pending_prefix
4710 '-C%s' % options.similarity, 4511 pending_ref = pending_prefix + branch[len('refs/'):]
4711 '--no-rebase', '--rmdir', 4512 retcode, output = PushToGitPending(pushurl, pending_ref)
4712 ] 4513 pushed_to_pending = (retcode == 0)
4713 if settings.GetForceHttpsCommitUrl(): 4514 if retcode == 0:
4714 # Allow forcing https commit URLs for some projects that don't allow 4515 revision = RunGit(['rev-parse', 'HEAD']).strip()
4715 # committing to http URLs (like Google Code).
4716 remote_url = cl.GetGitSvnRemoteUrl()
4717 if urlparse.urlparse(remote_url).scheme == 'http':
4718 remote_url = remote_url.replace('http://', 'https://')
4719 cmd_args.append('--commit-url=%s' % remote_url)
4720 _, output = RunGitWithCode(cmd_args)
4721 if 'Committed r' in output:
4722 revision = re.match(
4723 '.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1)
4724 logging.debug(output) 4516 logging.debug(output)
4725 except: # pylint: disable=W0702 4517 except: # pylint: disable=W0702
4726 if _IS_BEING_TESTED: 4518 if _IS_BEING_TESTED:
4727 logging.exception('this is likely your ACTUAL cause of test failure.\n' 4519 logging.exception('this is likely your ACTUAL cause of test failure.\n'
4728 + '-' * 30 + '8<' + '-' * 30) 4520 + '-' * 30 + '8<' + '-' * 30)
4729 logging.error('\n' + '-' * 30 + '8<' + '-' * 30 + '\n\n\n') 4521 logging.error('\n' + '-' * 30 + '8<' + '-' * 30 + '\n\n\n')
4730 raise 4522 raise
4731 finally: 4523 finally:
4732 # And then swap back to the original branch and clean up. 4524 # And then swap back to the original branch and clean up.
4733 RunGit(['checkout', '-q', cl.GetBranch()]) 4525 RunGit(['checkout', '-q', cl.GetBranch()])
4734 RunGit(['branch', '-D', MERGE_BRANCH]) 4526 RunGit(['branch', '-D', MERGE_BRANCH])
4735 if base_has_submodules:
4736 RunGit(['branch', '-D', CHERRY_PICK_BRANCH])
4737 4527
4738 if not revision: 4528 if not revision:
4739 print('Failed to push. If this persists, please file a bug.') 4529 print('Failed to push. If this persists, please file a bug.')
4740 return 1 4530 return 1
4741 4531
4742 killed = False 4532 killed = False
4743 if pushed_to_pending: 4533 if pushed_to_pending:
4744 try: 4534 try:
4745 revision = WaitForRealCommit(remote, revision, base_branch, branch) 4535 revision = WaitForRealCommit(remote, revision, base_branch, branch)
4746 # We set pushed_to_pending to False, since it made it all the way to the 4536 # We set pushed_to_pending to False, since it made it all the way to the
(...skipping 24 matching lines...) Expand all
4771 else: 4561 else:
4772 comment += ' (presubmit successful).' 4562 comment += ' (presubmit successful).'
4773 cl.RpcServer().add_comment(cl.GetIssue(), comment) 4563 cl.RpcServer().add_comment(cl.GetIssue(), comment)
4774 4564
4775 if pushed_to_pending: 4565 if pushed_to_pending:
4776 _, branch = cl.FetchUpstreamTuple(cl.GetBranch()) 4566 _, branch = cl.FetchUpstreamTuple(cl.GetBranch())
4777 print('The commit is in the pending queue (%s).' % pending_ref) 4567 print('The commit is in the pending queue (%s).' % pending_ref)
4778 print('It will show up on %s in ~1 min, once it gets a Cr-Commit-Position ' 4568 print('It will show up on %s in ~1 min, once it gets a Cr-Commit-Position '
4779 'footer.' % branch) 4569 'footer.' % branch)
4780 4570
4781 hook = POSTUPSTREAM_HOOK_PATTERN % cmd 4571 hook = POSTUPSTREAM_HOOK_PATTERN
4782 if os.path.isfile(hook): 4572 if os.path.isfile(hook):
4783 RunCommand([hook, merge_base], error_ok=True) 4573 RunCommand([hook, merge_base], error_ok=True)
4784 4574
4785 return 1 if killed else 0 4575 return 1 if killed else 0
4786 4576
4787 4577
4788 def WaitForRealCommit(remote, pushed_commit, local_base_ref, real_ref): 4578 def WaitForRealCommit(remote, pushed_commit, local_base_ref, real_ref):
4789 print() 4579 print()
4790 print('Waiting for commit to be landed on %s...' % real_ref) 4580 print('Waiting for commit to be landed on %s...' % real_ref)
4791 print('(If you are impatient, you may Ctrl-C once without harm)') 4581 print('(If you are impatient, you may Ctrl-C once without harm)')
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
4871 4661
4872 print('All attempts to push to pending ref failed.') 4662 print('All attempts to push to pending ref failed.')
4873 return code, out 4663 return code, out
4874 4664
4875 4665
4876 def IsFatalPushFailure(push_stdout): 4666 def IsFatalPushFailure(push_stdout):
4877 """True if retrying push won't help.""" 4667 """True if retrying push won't help."""
4878 return '(prohibited by Gerrit)' in push_stdout 4668 return '(prohibited by Gerrit)' in push_stdout
4879 4669
4880 4670
4881 @subcommand.usage('[upstream branch to apply against]') 4671 @subcommand.usage('DEPRECATED')
4882 def CMDdcommit(parser, args): 4672 def CMDdcommit(parser, args):
4883 """Commits the current changelist via git-svn.""" 4673 """DEPRECATED: Used to commit the current changelist via git-svn."""
4884 if not settings.GetIsGitSvn(): 4674 message = ('git-cl no longer supports committing to SVN repositories via '
4885 if git_footers.get_footer_svn_id(): 4675 'git-svn. You probably want to use `git cl land` instead.')
4886 # If it looks like previous commits were mirrored with git-svn. 4676 print(message)
4887 message = """This repository appears to be a git-svn mirror, but we 4677 return 1
4888 don't support git-svn mirrors anymore."""
4889 else:
4890 message = """This doesn't appear to be an SVN repository.
4891 If your project has a true, writeable git repository, you probably want to run
4892 'git cl land' instead.
4893 If your project has a git mirror of an upstream SVN master, you probably need
4894 to run 'git svn init'.
4895
4896 Using the wrong command might cause your commit to appear to succeed, and the
4897 review to be closed, without actually landing upstream. If you choose to
4898 proceed, please verify that the commit lands upstream as expected."""
4899 print(message)
4900 ask_for_data('[Press enter to dcommit or ctrl-C to quit]')
4901 print('WARNING: chrome infrastructure is migrating SVN repos to Git.\n'
4902 'Please let us know of this project you are committing to:'
4903 ' http://crbug.com/600451')
4904 return SendUpstream(parser, args, 'dcommit')
4905 4678
4906 4679
4907 @subcommand.usage('[upstream branch to apply against]') 4680 @subcommand.usage('[upstream branch to apply against]')
4908 def CMDland(parser, args): 4681 def CMDland(parser, args):
4909 """Commits the current changelist via git.""" 4682 """Commits the current changelist via git."""
4910 if settings.GetIsGitSvn() or git_footers.get_footer_svn_id():
4911 print('This appears to be an SVN repository.')
4912 print('Are you sure you didn\'t mean \'git cl dcommit\'?')
4913 print('(Ignore if this is the first commit after migrating from svn->git)')
4914 ask_for_data('[Press enter to push or ctrl-C to quit]')
4915 return SendUpstream(parser, args, 'land') 4683 return SendUpstream(parser, args, 'land')
4916 4684
4917 4685
4918 @subcommand.usage('<patch url or issue id or issue url>') 4686 @subcommand.usage('<patch url or issue id or issue url>')
4919 def CMDpatch(parser, args): 4687 def CMDpatch(parser, args):
4920 """Patches in a code review.""" 4688 """Patches in a code review."""
4921 parser.add_option('-b', dest='newbranch', 4689 parser.add_option('-b', dest='newbranch',
4922 help='create a new branch off trunk for the patch') 4690 help='create a new branch off trunk for the patch')
4923 parser.add_option('-f', '--force', action='store_true', 4691 parser.add_option('-f', '--force', action='store_true',
4924 help='with -b, clobber any existing branch') 4692 help='with -b, clobber any existing branch')
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
4998 parser.error('--reject is not supported with Gerrit codereview.') 4766 parser.error('--reject is not supported with Gerrit codereview.')
4999 if options.nocommit: 4767 if options.nocommit:
5000 parser.error('--nocommit is not supported with Gerrit codereview.') 4768 parser.error('--nocommit is not supported with Gerrit codereview.')
5001 if options.directory: 4769 if options.directory:
5002 parser.error('--directory is not supported with Gerrit codereview.') 4770 parser.error('--directory is not supported with Gerrit codereview.')
5003 4771
5004 return cl.CMDPatchIssue(args[0], options.reject, options.nocommit, 4772 return cl.CMDPatchIssue(args[0], options.reject, options.nocommit,
5005 options.directory) 4773 options.directory)
5006 4774
5007 4775
5008 def CMDrebase(parser, args):
5009 """Rebases current branch on top of svn repo."""
5010 # Provide a wrapper for git svn rebase to help avoid accidental
5011 # git svn dcommit.
5012 # It's the only command that doesn't use parser at all since we just defer
5013 # execution to git-svn.
5014
5015 return RunGitWithCode(['svn', 'rebase'] + args)[1]
5016
5017
5018 def GetTreeStatus(url=None): 4776 def GetTreeStatus(url=None):
5019 """Fetches the tree status and returns either 'open', 'closed', 4777 """Fetches the tree status and returns either 'open', 'closed',
5020 'unknown' or 'unset'.""" 4778 'unknown' or 'unset'."""
5021 url = url or settings.GetTreeStatusUrl(error_ok=True) 4779 url = url or settings.GetTreeStatusUrl(error_ok=True)
5022 if url: 4780 if url:
5023 status = urllib2.urlopen(url).read().lower() 4781 status = urllib2.urlopen(url).read().lower()
5024 if status.find('closed') != -1 or status == '0': 4782 if status.find('closed') != -1 or status == '0':
5025 return 'closed' 4783 return 'closed'
5026 elif status.find('open') != -1 or status == '1': 4784 elif status.find('open') != -1 or status == '1':
5027 return 'open' 4785 return 'open'
(...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after
5652 if __name__ == '__main__': 5410 if __name__ == '__main__':
5653 # These affect sys.stdout so do it outside of main() to simplify mocks in 5411 # These affect sys.stdout so do it outside of main() to simplify mocks in
5654 # unit testing. 5412 # unit testing.
5655 fix_encoding.fix_encoding() 5413 fix_encoding.fix_encoding()
5656 setup_color.init() 5414 setup_color.init()
5657 try: 5415 try:
5658 sys.exit(main(sys.argv[1:])) 5416 sys.exit(main(sys.argv[1:]))
5659 except KeyboardInterrupt: 5417 except KeyboardInterrupt:
5660 sys.stderr.write('interrupted\n') 5418 sys.stderr.write('interrupted\n')
5661 sys.exit(1) 5419 sys.exit(1)
OLDNEW
« no previous file with comments | « PRESUBMIT.py ('k') | tests/abandon.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698