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

Side by Side Diff: git_cl.py

Issue 496073002: Add support for landing to pending ref. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/git_cl_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 #!/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.""" 8 """A git-command for integrating reviews on Rietveld."""
9 9
10 from distutils.version import LooseVersion 10 from distutils.version import LooseVersion
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 print >> sys.stderr, message 67 print >> sys.stderr, message
68 sys.exit(1) 68 sys.exit(1)
69 69
70 70
71 def GetNoGitPagerEnv(): 71 def GetNoGitPagerEnv():
72 env = os.environ.copy() 72 env = os.environ.copy()
73 # 'cat' is a magical git string that disables pagers on all platforms. 73 # 'cat' is a magical git string that disables pagers on all platforms.
74 env['GIT_PAGER'] = 'cat' 74 env['GIT_PAGER'] = 'cat'
75 return env 75 return env
76 76
77
77 def RunCommand(args, error_ok=False, error_message=None, **kwargs): 78 def RunCommand(args, error_ok=False, error_message=None, **kwargs):
78 try: 79 try:
79 return subprocess2.check_output(args, shell=False, **kwargs) 80 return subprocess2.check_output(args, shell=False, **kwargs)
80 except subprocess2.CalledProcessError as e: 81 except subprocess2.CalledProcessError as e:
81 logging.debug('Failed running %s', args) 82 logging.debug('Failed running %s', args)
82 if not error_ok: 83 if not error_ok:
83 DieWithError( 84 DieWithError(
84 'Command "%s" failed.\n%s' % ( 85 'Command "%s" failed.\n%s' % (
85 ' '.join(args), error_message or e.stdout or '')) 86 ' '.join(args), error_message or e.stdout or ''))
86 return e.stdout 87 return e.stdout
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 self.cc = None 274 self.cc = None
274 self.root = None 275 self.root = None
275 self.is_git_svn = None 276 self.is_git_svn = None
276 self.svn_branch = None 277 self.svn_branch = None
277 self.tree_status_url = None 278 self.tree_status_url = None
278 self.viewvc_url = None 279 self.viewvc_url = None
279 self.updated = False 280 self.updated = False
280 self.is_gerrit = None 281 self.is_gerrit = None
281 self.git_editor = None 282 self.git_editor = None
282 self.project = None 283 self.project = None
284 self.pending_ref_prefix = None
283 285
284 def LazyUpdateIfNeeded(self): 286 def LazyUpdateIfNeeded(self):
285 """Updates the settings from a codereview.settings file, if available.""" 287 """Updates the settings from a codereview.settings file, if available."""
286 if not self.updated: 288 if not self.updated:
287 # The only value that actually changes the behavior is 289 # The only value that actually changes the behavior is
288 # autoupdate = "false". Everything else means "true". 290 # autoupdate = "false". Everything else means "true".
289 autoupdate = RunGit(['config', 'rietveld.autoupdate'], 291 autoupdate = RunGit(['config', 'rietveld.autoupdate'],
290 error_ok=True 292 error_ok=True
291 ).strip().lower() 293 ).strip().lower()
292 294
(...skipping 25 matching lines...) Expand all
318 return RunGit(['rev-parse', '--show-cdup']).strip() 320 return RunGit(['rev-parse', '--show-cdup']).strip()
319 321
320 def GetRoot(self): 322 def GetRoot(self):
321 if self.root is None: 323 if self.root is None:
322 self.root = os.path.abspath(self.GetRelativeRoot()) 324 self.root = os.path.abspath(self.GetRelativeRoot())
323 return self.root 325 return self.root
324 326
325 def GetIsGitSvn(self): 327 def GetIsGitSvn(self):
326 """Return true if this repo looks like it's using git-svn.""" 328 """Return true if this repo looks like it's using git-svn."""
327 if self.is_git_svn is None: 329 if self.is_git_svn is None:
328 # If you have any "svn-remote.*" config keys, we think you're using svn. 330 if self.GetPendingRefPrefix():
329 self.is_git_svn = RunGitWithCode( 331 # If PENDING_REF_PREFIX is set then it's a pure git repo no matter what.
330 ['config', '--local', '--get-regexp', r'^svn-remote\.'])[0] == 0 332 self.is_git_svn = False
333 else:
334 # If you have any "svn-remote.*" config keys, we think you're using svn.
335 self.is_git_svn = RunGitWithCode(
336 ['config', '--local', '--get-regexp', r'^svn-remote\.'])[0] == 0
331 return self.is_git_svn 337 return self.is_git_svn
332 338
333 def GetSVNBranch(self): 339 def GetSVNBranch(self):
334 if self.svn_branch is None: 340 if self.svn_branch is None:
335 if not self.GetIsGitSvn(): 341 if not self.GetIsGitSvn():
336 DieWithError('Repo doesn\'t appear to be a git-svn repo.') 342 DieWithError('Repo doesn\'t appear to be a git-svn repo.')
337 343
338 # Try to figure out which remote branch we're based on. 344 # Try to figure out which remote branch we're based on.
339 # Strategy: 345 # Strategy:
340 # 1) iterate through our branch history and find the svn URL. 346 # 1) iterate through our branch history and find the svn URL.
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 445
440 def GetLintIgnoreRegex(self): 446 def GetLintIgnoreRegex(self):
441 return (self._GetRietveldConfig('cpplint-ignore-regex', error_ok=True) or 447 return (self._GetRietveldConfig('cpplint-ignore-regex', error_ok=True) or
442 DEFAULT_LINT_IGNORE_REGEX) 448 DEFAULT_LINT_IGNORE_REGEX)
443 449
444 def GetProject(self): 450 def GetProject(self):
445 if not self.project: 451 if not self.project:
446 self.project = self._GetRietveldConfig('project', error_ok=True) 452 self.project = self._GetRietveldConfig('project', error_ok=True)
447 return self.project 453 return self.project
448 454
455 def GetPendingRefPrefix(self):
456 if not self.pending_ref_prefix:
457 self.pending_ref_prefix = self._GetRietveldConfig(
458 'pending-ref-prefix', error_ok=True)
459 return self.pending_ref_prefix
460
449 def _GetRietveldConfig(self, param, **kwargs): 461 def _GetRietveldConfig(self, param, **kwargs):
450 return self._GetConfig('rietveld.' + param, **kwargs) 462 return self._GetConfig('rietveld.' + param, **kwargs)
451 463
452 def _GetConfig(self, param, **kwargs): 464 def _GetConfig(self, param, **kwargs):
453 self.LazyUpdateIfNeeded() 465 self.LazyUpdateIfNeeded()
454 return RunGit(['config', param], **kwargs).strip() 466 return RunGit(['config', param], **kwargs).strip()
455 467
456 468
457 def ShortBranchName(branch): 469 def ShortBranchName(branch):
458 """Convert a name like 'refs/heads/foo' to just 'foo'.""" 470 """Convert a name like 'refs/heads/foo' to just 'foo'."""
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after
1078 # Only server setting is required. Other settings can be absent. 1090 # Only server setting is required. Other settings can be absent.
1079 # In that case, we ignore errors raised during option deletion attempt. 1091 # In that case, we ignore errors raised during option deletion attempt.
1080 SetProperty('cc', 'CC_LIST', unset_error_ok=True) 1092 SetProperty('cc', 'CC_LIST', unset_error_ok=True)
1081 SetProperty('private', 'PRIVATE', unset_error_ok=True) 1093 SetProperty('private', 'PRIVATE', unset_error_ok=True)
1082 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) 1094 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True)
1083 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) 1095 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True)
1084 SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True) 1096 SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True)
1085 SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True) 1097 SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True)
1086 SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True) 1098 SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True)
1087 SetProperty('project', 'PROJECT', unset_error_ok=True) 1099 SetProperty('project', 'PROJECT', unset_error_ok=True)
1100 SetProperty('pending-ref-prefix', 'PENDING_REF_PREFIX', unset_error_ok=True)
1088 1101
1089 if 'GERRIT_HOST' in keyvals: 1102 if 'GERRIT_HOST' in keyvals:
1090 RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']]) 1103 RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']])
1091 1104
1092 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: 1105 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals:
1093 #should be of the form 1106 #should be of the form
1094 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof 1107 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof
1095 #ORIGIN_URL_CONFIG: http://src.chromium.org/git 1108 #ORIGIN_URL_CONFIG: http://src.chromium.org/git
1096 RunGit(['config', keyvals['PUSH_URL_CONFIG'], 1109 RunGit(['config', keyvals['PUSH_URL_CONFIG'],
1097 keyvals['ORIGIN_URL_CONFIG']]) 1110 keyvals['ORIGIN_URL_CONFIG']])
(...skipping 742 matching lines...) Expand 10 before | Expand all | Expand 10 after
1840 print 1853 print
1841 print 'Either reparent this branch on top of origin/master:' 1854 print 'Either reparent this branch on top of origin/master:'
1842 print ' git reparent-branch --root' 1855 print ' git reparent-branch --root'
1843 print 1856 print
1844 print 'OR run `git rebase-update` if you think the parent branch is already' 1857 print 'OR run `git rebase-update` if you think the parent branch is already'
1845 print 'committed.' 1858 print 'committed.'
1846 print 1859 print
1847 print ' Current parent: %r' % upstream_branch 1860 print ' Current parent: %r' % upstream_branch
1848 return 1 1861 return 1
1849 1862
1850 if not args or cmd == 'push': 1863 if not args or cmd == 'land':
1851 # Default to merging against our best guess of the upstream branch. 1864 # Default to merging against our best guess of the upstream branch.
1852 args = [cl.GetUpstreamBranch()] 1865 args = [cl.GetUpstreamBranch()]
1853 1866
1854 if options.contributor: 1867 if options.contributor:
1855 if not re.match('^.*\s<\S+@\S+>$', options.contributor): 1868 if not re.match('^.*\s<\S+@\S+>$', options.contributor):
1856 print "Please provide contibutor as 'First Last <email@example.com>'" 1869 print "Please provide contibutor as 'First Last <email@example.com>'"
1857 return 1 1870 return 1
1858 1871
1859 base_branch = args[0] 1872 base_branch = args[0]
1860 base_has_submodules = IsSubmoduleMergeCommit(base_branch) 1873 base_has_submodules = IsSubmoduleMergeCommit(base_branch)
1861 1874
1862 if is_dirty_git_tree(cmd): 1875 if is_dirty_git_tree(cmd):
1863 return 1 1876 return 1
1864 1877
1865 # This rev-list syntax means "show all commits not in my branch that 1878 # This rev-list syntax means "show all commits not in my branch that
1866 # are in base_branch". 1879 # are in base_branch".
1867 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), 1880 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(),
1868 base_branch]).splitlines() 1881 base_branch]).splitlines()
1869 if upstream_commits: 1882 if upstream_commits:
1870 print ('Base branch "%s" has %d commits ' 1883 print ('Base branch "%s" has %d commits '
1871 'not in this branch.' % (base_branch, len(upstream_commits))) 1884 'not in this branch.' % (base_branch, len(upstream_commits)))
1872 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd) 1885 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd)
1873 return 1 1886 return 1
1874 1887
1875 # This is the revision `svn dcommit` will commit on top of. 1888 # This is the revision `svn dcommit` will commit on top of.
1876 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', 1889 svn_head = None
1877 '--pretty=format:%H']) 1890 if cmd == 'dcommit' or base_has_submodules:
1891 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1',
1892 '--pretty=format:%H'])
1878 1893
1879 if cmd == 'dcommit': 1894 if cmd == 'dcommit':
1880 # If the base_head is a submodule merge commit, the first parent of the 1895 # If the base_head is a submodule merge commit, the first parent of the
1881 # base_head should be a git-svn commit, which is what we're interested in. 1896 # base_head should be a git-svn commit, which is what we're interested in.
1882 base_svn_head = base_branch 1897 base_svn_head = base_branch
1883 if base_has_submodules: 1898 if base_has_submodules:
1884 base_svn_head += '^1' 1899 base_svn_head += '^1'
1885 1900
1886 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head]) 1901 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head])
1887 if extra_commits: 1902 if extra_commits:
1888 print ('This branch has %d additional commits not upstreamed yet.' 1903 print ('This branch has %d additional commits not upstreamed yet.'
1889 % len(extra_commits.splitlines())) 1904 % len(extra_commits.splitlines()))
1890 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' 1905 print ('Upstream "%s" or rebase this branch on top of the upstream trunk '
1891 'before attempting to %s.' % (base_branch, cmd)) 1906 'before attempting to %s.' % (base_branch, cmd))
1892 return 1 1907 return 1
1893 1908
1894 base_branch = RunGit(['merge-base', base_branch, 'HEAD']).strip() 1909 base_branch = RunGit(['merge-base', base_branch, 'HEAD']).strip()
1895 if not options.bypass_hooks: 1910 if not options.bypass_hooks:
1896 author = None 1911 author = None
1897 if options.contributor: 1912 if options.contributor:
1898 author = re.search(r'\<(.*)\>', options.contributor).group(1) 1913 author = re.search(r'\<(.*)\>', options.contributor).group(1)
1899 hook_results = cl.RunHook( 1914 hook_results = cl.RunHook(
1900 committing=True, 1915 committing=True,
1901 may_prompt=not options.force, 1916 may_prompt=not options.force,
1902 verbose=options.verbose, 1917 verbose=options.verbose,
1903 change=cl.GetChange(base_branch, author)) 1918 change=cl.GetChange(base_branch, author))
1904 if not hook_results.should_continue(): 1919 if not hook_results.should_continue():
1905 return 1 1920 return 1
1906 1921
1907 if cmd == 'dcommit': 1922 # Check the tree status if the tree status URL is set.
1908 # Check the tree status if the tree status URL is set. 1923 status = GetTreeStatus()
1909 status = GetTreeStatus() 1924 if 'closed' == status:
1910 if 'closed' == status: 1925 print('The tree is closed. Please wait for it to reopen. Use '
1911 print('The tree is closed. Please wait for it to reopen. Use ' 1926 '"git cl %s --bypass-hooks" to commit on a closed tree.' % cmd)
1912 '"git cl dcommit --bypass-hooks" to commit on a closed tree.') 1927 return 1
1913 return 1 1928 elif 'unknown' == status:
1914 elif 'unknown' == status: 1929 print('Unable to determine tree status. Please verify manually and '
1915 print('Unable to determine tree status. Please verify manually and ' 1930 'use "git cl %s --bypass-hooks" to commit on a closed tree.' % cmd)
1916 'use "git cl dcommit --bypass-hooks" to commit on a closed tree.') 1931 return 1
1917 else: 1932 else:
1918 breakpad.SendStack( 1933 breakpad.SendStack(
1919 'GitClHooksBypassedCommit', 1934 'GitClHooksBypassedCommit',
1920 'Issue %s/%s bypassed hook when committing (tree status was "%s")' % 1935 'Issue %s/%s bypassed hook when committing (tree status was "%s")' %
1921 (cl.GetRietveldServer(), cl.GetIssue(), GetTreeStatus()), 1936 (cl.GetRietveldServer(), cl.GetIssue(), GetTreeStatus()),
1922 verbose=False) 1937 verbose=False)
1923 1938
1924 change_desc = ChangeDescription(options.message) 1939 change_desc = ChangeDescription(options.message)
1925 if not change_desc.description and cl.GetIssue(): 1940 if not change_desc.description and cl.GetIssue():
1926 change_desc = ChangeDescription(cl.GetDescription()) 1941 change_desc = ChangeDescription(cl.GetDescription())
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1971 # trunk. Move up to the top of the tree so that git commands that expect a 1986 # trunk. Move up to the top of the tree so that git commands that expect a
1972 # valid CWD won't fail after we check out the merge branch. 1987 # valid CWD won't fail after we check out the merge branch.
1973 rel_base_path = settings.GetRelativeRoot() 1988 rel_base_path = settings.GetRelativeRoot()
1974 if rel_base_path: 1989 if rel_base_path:
1975 os.chdir(rel_base_path) 1990 os.chdir(rel_base_path)
1976 1991
1977 # Stuff our change into the merge branch. 1992 # Stuff our change into the merge branch.
1978 # We wrap in a try...finally block so if anything goes wrong, 1993 # We wrap in a try...finally block so if anything goes wrong,
1979 # we clean up the branches. 1994 # we clean up the branches.
1980 retcode = -1 1995 retcode = -1
1996 used_pending = False
1997 pending_ref = None
1981 try: 1998 try:
1982 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) 1999 RunGit(['checkout', '-q', '-b', MERGE_BRANCH])
1983 RunGit(['reset', '--soft', base_branch]) 2000 RunGit(['reset', '--soft', base_branch])
1984 if options.contributor: 2001 if options.contributor:
1985 RunGit( 2002 RunGit(
1986 [ 2003 [
1987 'commit', '--author', options.contributor, 2004 'commit', '--author', options.contributor,
1988 '-m', commit_desc.description, 2005 '-m', commit_desc.description,
1989 ]) 2006 ])
1990 else: 2007 else:
1991 RunGit(['commit', '-m', commit_desc.description]) 2008 RunGit(['commit', '-m', commit_desc.description])
1992 if base_has_submodules: 2009 if base_has_submodules:
1993 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() 2010 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip()
1994 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) 2011 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head])
1995 RunGit(['checkout', CHERRY_PICK_BRANCH]) 2012 RunGit(['checkout', CHERRY_PICK_BRANCH])
1996 RunGit(['cherry-pick', cherry_pick_commit]) 2013 RunGit(['cherry-pick', cherry_pick_commit])
1997 if cmd == 'push': 2014 if cmd == 'land':
1998 # push the merge branch.
1999 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) 2015 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch())
2000 retcode, output = RunGitWithCode( 2016 pending_prefix = settings.GetPendingRefPrefix()
2001 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) 2017 if not pending_prefix or branch.startswith(pending_prefix):
2018 # If not using refs/pending/heads/* at all, or target ref is already set
2019 # to pending, then push to the target ref directly.
2020 retcode, output = RunGitWithCode(
2021 ['push', '--porcelain', remote, 'HEAD:%s' % branch])
2022 used_pending = pending_prefix and branch.startswith(pending_prefix)
2023 else:
2024 # Cherry-pick the change on top of pending ref and then push it.
2025 assert branch.startswith('refs/'), branch
2026 assert pending_prefix[-1] == '/', pending_prefix
2027 pending_ref = pending_prefix + branch[len('refs/'):]
2028 retcode, output = PushToGitPending(remote, pending_ref, branch)
2029 used_pending = (retcode == 0)
2002 logging.debug(output) 2030 logging.debug(output)
2003 else: 2031 else:
2004 # dcommit the merge branch. 2032 # dcommit the merge branch.
2005 retcode, output = RunGitWithCode(['svn', 'dcommit', 2033 retcode, output = RunGitWithCode(['svn', 'dcommit',
2006 '-C%s' % options.similarity, 2034 '-C%s' % options.similarity,
2007 '--no-rebase', '--rmdir']) 2035 '--no-rebase', '--rmdir'])
2008 finally: 2036 finally:
2009 # And then swap back to the original branch and clean up. 2037 # And then swap back to the original branch and clean up.
2010 RunGit(['checkout', '-q', cl.GetBranch()]) 2038 RunGit(['checkout', '-q', cl.GetBranch()])
2011 RunGit(['branch', '-D', MERGE_BRANCH]) 2039 RunGit(['branch', '-D', MERGE_BRANCH])
2012 if base_has_submodules: 2040 if base_has_submodules:
2013 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) 2041 RunGit(['branch', '-D', CHERRY_PICK_BRANCH])
2014 2042
2015 if cl.GetIssue(): 2043 if cl.GetIssue():
2016 if cmd == 'dcommit' and 'Committed r' in output: 2044 if cmd == 'dcommit' and 'Committed r' in output:
2017 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) 2045 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1)
2018 elif cmd == 'push' and retcode == 0: 2046 elif cmd == 'land' and retcode == 0:
2019 match = (re.match(r'.*?([a-f0-9]{7,})\.\.([a-f0-9]{7,})$', l) 2047 match = (re.match(r'.*?([a-f0-9]{7,})\.\.([a-f0-9]{7,})$', l)
2020 for l in output.splitlines(False)) 2048 for l in output.splitlines(False))
2021 match = filter(None, match) 2049 match = filter(None, match)
2022 if len(match) != 1: 2050 if len(match) != 1:
2023 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % 2051 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" %
2024 output) 2052 output)
2025 revision = match[0].group(2) 2053 revision = match[0].group(2)
2026 else: 2054 else:
2027 return 1 2055 return 1
2056 to_pending = ' to pending queue' if used_pending else ''
2028 viewvc_url = settings.GetViewVCUrl() 2057 viewvc_url = settings.GetViewVCUrl()
2029 if viewvc_url and revision: 2058 if viewvc_url and revision:
2030 change_desc.append_footer('Committed: ' + viewvc_url + revision) 2059 change_desc.append_footer(
2060 'Committed%s: %s%s' % (to_pending, viewvc_url, revision))
2031 elif revision: 2061 elif revision:
2032 change_desc.append_footer('Committed: ' + revision) 2062 change_desc.append_footer('Committed%s: %s' % (to_pending, revision))
2033 print ('Closing issue ' 2063 print ('Closing issue '
2034 '(you may be prompted for your codereview password)...') 2064 '(you may be prompted for your codereview password)...')
2035 cl.UpdateDescription(change_desc.description) 2065 cl.UpdateDescription(change_desc.description)
2036 cl.CloseIssue() 2066 cl.CloseIssue()
2037 props = cl.GetIssueProperties() 2067 props = cl.GetIssueProperties()
2038 patch_num = len(props['patchsets']) 2068 patch_num = len(props['patchsets'])
2039 comment = "Committed patchset #%d manually as %s" % (patch_num, revision) 2069 comment = "Committed patchset #%d%s manually as %s" % (
2070 patch_num, to_pending, revision)
2040 if options.bypass_hooks: 2071 if options.bypass_hooks:
2041 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.' 2072 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.'
2042 else: 2073 else:
2043 comment += ' (presubmit successful).' 2074 comment += ' (presubmit successful).'
2044 cl.RpcServer().add_comment(cl.GetIssue(), comment) 2075 cl.RpcServer().add_comment(cl.GetIssue(), comment)
2045 cl.SetIssue(None) 2076 cl.SetIssue(None)
2046 2077
2078 if used_pending and retcode == 0:
2079 _, branch = cl.FetchUpstreamTuple(cl.GetBranch())
2080 print 'The commit is in the pending queue (%s).' % pending_ref
2081 print (
2082 'It will show up on %s in ~1 min, once it gets Cr-Commit-Position '
2083 'footer.' % branch)
2084
2047 if retcode == 0: 2085 if retcode == 0:
2048 hook = POSTUPSTREAM_HOOK_PATTERN % cmd 2086 hook = POSTUPSTREAM_HOOK_PATTERN % cmd
2049 if os.path.isfile(hook): 2087 if os.path.isfile(hook):
2050 RunCommand([hook, base_branch], error_ok=True) 2088 RunCommand([hook, base_branch], error_ok=True)
2051 2089
2052 return 0 2090 return 0
2053 2091
2054 2092
2093 def PushToGitPending(remote, pending_ref, upstream_ref):
2094 """Fetches pending_ref, cherry-picks current HEAD on top of it, pushes.
2095
2096 Returns:
2097 (retcode of last operation, output log of last operation).
2098 """
2099 assert pending_ref.startswith('refs/'), pending_ref
2100 local_pending_ref = 'refs/git-cl/' + pending_ref[len('refs/'):]
2101 cherry = RunGit(['rev-parse', 'HEAD']).strip()
2102 code = 0
2103 out = ''
2104 attempt = 0
2105 while attempt < 5:
2106 attempt += 1
2107
2108 # Fetch. Retry fetch errors.
2109 code, out = RunGitWithCode(
2110 ['retry', 'fetch', remote, '+%s:%s' % (pending_ref, local_pending_ref)],
2111 suppress_stderr=True)
2112 if code:
2113 continue
2114
2115 # Try to cherry pick. Abort on merge conflicts.
2116 RunGitWithCode(['checkout', local_pending_ref], suppress_stderr=True)
2117 code, out = RunGitWithCode(['cherry-pick', cherry], suppress_stderr=True)
2118 if code:
2119 print (
2120 'Your patch doesn\'t apply cleanly to upstream ref \'%s\', '
2121 'the following files have merge conflicts:' % upstream_ref)
2122 print RunGit(['diff', '--name-status', '--diff-filter=U']).strip()
2123 print 'Please rebase your patch and try again.'
2124 RunGitWithCode(['cherry-pick', '--abort'], suppress_stderr=True)
2125 return code, out
2126
2127 # Applied cleanly, try to push now. Retry on error (flake or non-ff push).
2128 code, out = RunGitWithCode(
2129 ['retry', 'push', '--porcelain', remote, 'HEAD:%s' % pending_ref])
2130 if code == 0:
2131 # Success.
2132 return code, out
2133
2134 return code, out
2135
2136
2055 @subcommand.usage('[upstream branch to apply against]') 2137 @subcommand.usage('[upstream branch to apply against]')
2056 def CMDdcommit(parser, args): 2138 def CMDdcommit(parser, args):
2057 """Commits the current changelist via git-svn.""" 2139 """Commits the current changelist via git-svn."""
2058 if not settings.GetIsGitSvn(): 2140 if not settings.GetIsGitSvn():
2059 message = """This doesn't appear to be an SVN repository. 2141 message = """This doesn't appear to be an SVN repository.
2060 If your project has a git mirror with an upstream SVN master, you probably need 2142 If your project has a git mirror with an upstream SVN master, you probably need
2061 to run 'git svn init', see your project's git mirror documentation. 2143 to run 'git svn init', see your project's git mirror documentation.
2062 If your project has a true writeable upstream repository, you probably want 2144 If your project has a true writeable upstream repository, you probably want
2063 to run 'git cl land' instead. 2145 to run 'git cl land' instead.
2064 Choose wisely, if you get this wrong, your commit might appear to succeed but 2146 Choose wisely, if you get this wrong, your commit might appear to succeed but
2065 will instead be silently ignored.""" 2147 will instead be silently ignored."""
2066 print(message) 2148 print(message)
2067 ask_for_data('[Press enter to dcommit or ctrl-C to quit]') 2149 ask_for_data('[Press enter to dcommit or ctrl-C to quit]')
2068 return SendUpstream(parser, args, 'dcommit') 2150 return SendUpstream(parser, args, 'dcommit')
2069 2151
2070 2152
2071 @subcommand.usage('[upstream branch to apply against]') 2153 @subcommand.usage('[upstream branch to apply against]')
2072 def CMDland(parser, args): 2154 def CMDland(parser, args):
2073 """Commits the current changelist via git.""" 2155 """Commits the current changelist via git."""
2074 if settings.GetIsGitSvn(): 2156 if settings.GetIsGitSvn():
2075 print('This appears to be an SVN repository.') 2157 print('This appears to be an SVN repository.')
2076 print('Are you sure you didn\'t mean \'git cl dcommit\'?') 2158 print('Are you sure you didn\'t mean \'git cl dcommit\'?')
2077 ask_for_data('[Press enter to push or ctrl-C to quit]') 2159 ask_for_data('[Press enter to push or ctrl-C to quit]')
2078 return SendUpstream(parser, args, 'push') 2160 return SendUpstream(parser, args, 'land')
2079 2161
2080 2162
2081 @subcommand.usage('<patch url or issue id>') 2163 @subcommand.usage('<patch url or issue id>')
2082 def CMDpatch(parser, args): 2164 def CMDpatch(parser, args):
2083 """Patches in a code review.""" 2165 """Patches in a code review."""
2084 parser.add_option('-b', dest='newbranch', 2166 parser.add_option('-b', dest='newbranch',
2085 help='create a new branch off trunk for the patch') 2167 help='create a new branch off trunk for the patch')
2086 parser.add_option('-f', '--force', action='store_true', 2168 parser.add_option('-f', '--force', action='store_true',
2087 help='with -b, clobber any existing branch') 2169 help='with -b, clobber any existing branch')
2088 parser.add_option('-d', '--directory', action='store', metavar='DIR', 2170 parser.add_option('-d', '--directory', action='store', metavar='DIR',
(...skipping 584 matching lines...) Expand 10 before | Expand all | Expand 10 after
2673 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' 2755 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith '
2674 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) 2756 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)))
2675 2757
2676 2758
2677 if __name__ == '__main__': 2759 if __name__ == '__main__':
2678 # These affect sys.stdout so do it outside of main() to simplify mocks in 2760 # These affect sys.stdout so do it outside of main() to simplify mocks in
2679 # unit testing. 2761 # unit testing.
2680 fix_encoding.fix_encoding() 2762 fix_encoding.fix_encoding()
2681 colorama.init() 2763 colorama.init()
2682 sys.exit(main(sys.argv[1:])) 2764 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698