Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 if self.pending_ref_prefix: | |
| 460 self.pending_ref_prefix = self.pending_ref_prefix.rstrip('/') + '/' | |
|
iannucci
2014/08/21 20:32:20
why not just take it as-is and then commit the rig
Vadim Sh.
2014/08/21 21:14:35
Okay... just defensive programming.
| |
| 461 return self.pending_ref_prefix | |
| 462 | |
| 449 def _GetRietveldConfig(self, param, **kwargs): | 463 def _GetRietveldConfig(self, param, **kwargs): |
| 450 return self._GetConfig('rietveld.' + param, **kwargs) | 464 return self._GetConfig('rietveld.' + param, **kwargs) |
| 451 | 465 |
| 452 def _GetConfig(self, param, **kwargs): | 466 def _GetConfig(self, param, **kwargs): |
| 453 self.LazyUpdateIfNeeded() | 467 self.LazyUpdateIfNeeded() |
| 454 return RunGit(['config', param], **kwargs).strip() | 468 return RunGit(['config', param], **kwargs).strip() |
| 455 | 469 |
| 456 | 470 |
| 457 def ShortBranchName(branch): | 471 def ShortBranchName(branch): |
| 458 """Convert a name like 'refs/heads/foo' to just 'foo'.""" | 472 """Convert a name like 'refs/heads/foo' to just 'foo'.""" |
| (...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1078 # Only server setting is required. Other settings can be absent. | 1092 # Only server setting is required. Other settings can be absent. |
| 1079 # In that case, we ignore errors raised during option deletion attempt. | 1093 # In that case, we ignore errors raised during option deletion attempt. |
| 1080 SetProperty('cc', 'CC_LIST', unset_error_ok=True) | 1094 SetProperty('cc', 'CC_LIST', unset_error_ok=True) |
| 1081 SetProperty('private', 'PRIVATE', unset_error_ok=True) | 1095 SetProperty('private', 'PRIVATE', unset_error_ok=True) |
| 1082 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) | 1096 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) |
| 1083 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) | 1097 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) |
| 1084 SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True) | 1098 SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True) |
| 1085 SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True) | 1099 SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True) |
| 1086 SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True) | 1100 SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True) |
| 1087 SetProperty('project', 'PROJECT', unset_error_ok=True) | 1101 SetProperty('project', 'PROJECT', unset_error_ok=True) |
| 1102 SetProperty('pending-ref-prefix', 'PENDING_REF_PREFIX', unset_error_ok=True) | |
| 1088 | 1103 |
| 1089 if 'GERRIT_HOST' in keyvals: | 1104 if 'GERRIT_HOST' in keyvals: |
| 1090 RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']]) | 1105 RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']]) |
| 1091 | 1106 |
| 1092 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: | 1107 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: |
| 1093 #should be of the form | 1108 #should be of the form |
| 1094 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof | 1109 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof |
| 1095 #ORIGIN_URL_CONFIG: http://src.chromium.org/git | 1110 #ORIGIN_URL_CONFIG: http://src.chromium.org/git |
| 1096 RunGit(['config', keyvals['PUSH_URL_CONFIG'], | 1111 RunGit(['config', keyvals['PUSH_URL_CONFIG'], |
| 1097 keyvals['ORIGIN_URL_CONFIG']]) | 1112 keyvals['ORIGIN_URL_CONFIG']]) |
| (...skipping 742 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1840 print | 1855 print |
| 1841 print 'Either reparent this branch on top of origin/master:' | 1856 print 'Either reparent this branch on top of origin/master:' |
| 1842 print ' git reparent-branch --root' | 1857 print ' git reparent-branch --root' |
| 1843 print | 1858 print |
| 1844 print 'OR run `git rebase-update` if you think the parent branch is already' | 1859 print 'OR run `git rebase-update` if you think the parent branch is already' |
| 1845 print 'committed.' | 1860 print 'committed.' |
| 1846 print | 1861 print |
| 1847 print ' Current parent: %r' % upstream_branch | 1862 print ' Current parent: %r' % upstream_branch |
| 1848 return 1 | 1863 return 1 |
| 1849 | 1864 |
| 1850 if not args or cmd == 'push': | 1865 if not args or cmd == 'land': |
|
iannucci
2014/08/21 20:32:20
good catch
| |
| 1851 # Default to merging against our best guess of the upstream branch. | 1866 # Default to merging against our best guess of the upstream branch. |
| 1852 args = [cl.GetUpstreamBranch()] | 1867 args = [cl.GetUpstreamBranch()] |
| 1853 | 1868 |
| 1854 if options.contributor: | 1869 if options.contributor: |
| 1855 if not re.match('^.*\s<\S+@\S+>$', options.contributor): | 1870 if not re.match('^.*\s<\S+@\S+>$', options.contributor): |
| 1856 print "Please provide contibutor as 'First Last <email@example.com>'" | 1871 print "Please provide contibutor as 'First Last <email@example.com>'" |
| 1857 return 1 | 1872 return 1 |
| 1858 | 1873 |
| 1859 base_branch = args[0] | 1874 base_branch = args[0] |
| 1860 base_has_submodules = IsSubmoduleMergeCommit(base_branch) | 1875 base_has_submodules = IsSubmoduleMergeCommit(base_branch) |
| 1861 | 1876 |
| 1862 if is_dirty_git_tree(cmd): | 1877 if is_dirty_git_tree(cmd): |
| 1863 return 1 | 1878 return 1 |
| 1864 | 1879 |
| 1865 # This rev-list syntax means "show all commits not in my branch that | 1880 # This rev-list syntax means "show all commits not in my branch that |
| 1866 # are in base_branch". | 1881 # are in base_branch". |
| 1867 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), | 1882 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), |
| 1868 base_branch]).splitlines() | 1883 base_branch]).splitlines() |
| 1869 if upstream_commits: | 1884 if upstream_commits: |
| 1870 print ('Base branch "%s" has %d commits ' | 1885 print ('Base branch "%s" has %d commits ' |
| 1871 'not in this branch.' % (base_branch, len(upstream_commits))) | 1886 'not in this branch.' % (base_branch, len(upstream_commits))) |
| 1872 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd) | 1887 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd) |
| 1873 return 1 | 1888 return 1 |
| 1874 | 1889 |
| 1875 # This is the revision `svn dcommit` will commit on top of. | 1890 # This is the revision `svn dcommit` will commit on top of. |
| 1876 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', | 1891 svn_head = None |
| 1877 '--pretty=format:%H']) | 1892 if cmd == 'dcommit' or base_has_submodules: |
|
iannucci
2014/08/21 20:32:20
dcommit?
| |
| 1893 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', | |
|
Vadim Sh.
2014/08/21 19:31:22
This stuff finds svn HEADs in pure git repos... Fo
| |
| 1894 '--pretty=format:%H']) | |
| 1878 | 1895 |
| 1879 if cmd == 'dcommit': | 1896 if cmd == 'dcommit': |
| 1880 # If the base_head is a submodule merge commit, the first parent of the | 1897 # 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. | 1898 # base_head should be a git-svn commit, which is what we're interested in. |
| 1882 base_svn_head = base_branch | 1899 base_svn_head = base_branch |
| 1883 if base_has_submodules: | 1900 if base_has_submodules: |
| 1884 base_svn_head += '^1' | 1901 base_svn_head += '^1' |
| 1885 | 1902 |
| 1886 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head]) | 1903 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head]) |
| 1887 if extra_commits: | 1904 if extra_commits: |
| 1888 print ('This branch has %d additional commits not upstreamed yet.' | 1905 print ('This branch has %d additional commits not upstreamed yet.' |
| 1889 % len(extra_commits.splitlines())) | 1906 % len(extra_commits.splitlines())) |
| 1890 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 1907 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' |
| 1891 'before attempting to %s.' % (base_branch, cmd)) | 1908 'before attempting to %s.' % (base_branch, cmd)) |
| 1892 return 1 | 1909 return 1 |
| 1893 | 1910 |
| 1894 base_branch = RunGit(['merge-base', base_branch, 'HEAD']).strip() | 1911 base_branch = RunGit(['merge-base', base_branch, 'HEAD']).strip() |
| 1895 if not options.bypass_hooks: | 1912 if not options.bypass_hooks: |
| 1896 author = None | 1913 author = None |
| 1897 if options.contributor: | 1914 if options.contributor: |
| 1898 author = re.search(r'\<(.*)\>', options.contributor).group(1) | 1915 author = re.search(r'\<(.*)\>', options.contributor).group(1) |
| 1899 hook_results = cl.RunHook( | 1916 hook_results = cl.RunHook( |
| 1900 committing=True, | 1917 committing=True, |
| 1901 may_prompt=not options.force, | 1918 may_prompt=not options.force, |
| 1902 verbose=options.verbose, | 1919 verbose=options.verbose, |
| 1903 change=cl.GetChange(base_branch, author)) | 1920 change=cl.GetChange(base_branch, author)) |
| 1904 if not hook_results.should_continue(): | 1921 if not hook_results.should_continue(): |
| 1905 return 1 | 1922 return 1 |
| 1906 | 1923 |
| 1907 if cmd == 'dcommit': | 1924 # Check the tree status if the tree status URL is set. |
|
Vadim Sh.
2014/08/21 19:31:22
Now checks with 'git cl land' too.
| |
| 1908 # Check the tree status if the tree status URL is set. | 1925 status = GetTreeStatus() |
| 1909 status = GetTreeStatus() | 1926 if 'closed' == status: |
| 1910 if 'closed' == status: | 1927 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 ' | 1928 '"git cl %s --bypass-hooks" to commit on a closed tree.' % cmd) |
| 1912 '"git cl dcommit --bypass-hooks" to commit on a closed tree.') | 1929 return 1 |
| 1913 return 1 | 1930 elif 'unknown' == status: |
| 1914 elif 'unknown' == status: | 1931 print('Unable to determine tree status. Please verify manually and ' |
| 1915 print('Unable to determine tree status. Please verify manually and ' | 1932 '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.') | 1933 return 1 |
|
Vadim Sh.
2014/08/21 19:31:22
Now aborts if tree status is unknown, as error mes
| |
| 1917 else: | 1934 else: |
| 1918 breakpad.SendStack( | 1935 breakpad.SendStack( |
| 1919 'GitClHooksBypassedCommit', | 1936 'GitClHooksBypassedCommit', |
| 1920 'Issue %s/%s bypassed hook when committing (tree status was "%s")' % | 1937 'Issue %s/%s bypassed hook when committing (tree status was "%s")' % |
| 1921 (cl.GetRietveldServer(), cl.GetIssue(), GetTreeStatus()), | 1938 (cl.GetRietveldServer(), cl.GetIssue(), GetTreeStatus()), |
| 1922 verbose=False) | 1939 verbose=False) |
| 1923 | 1940 |
| 1924 change_desc = ChangeDescription(options.message) | 1941 change_desc = ChangeDescription(options.message) |
| 1925 if not change_desc.description and cl.GetIssue(): | 1942 if not change_desc.description and cl.GetIssue(): |
| 1926 change_desc = ChangeDescription(cl.GetDescription()) | 1943 change_desc = ChangeDescription(cl.GetDescription()) |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1971 # trunk. Move up to the top of the tree so that git commands that expect a | 1988 # 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. | 1989 # valid CWD won't fail after we check out the merge branch. |
| 1973 rel_base_path = settings.GetRelativeRoot() | 1990 rel_base_path = settings.GetRelativeRoot() |
| 1974 if rel_base_path: | 1991 if rel_base_path: |
| 1975 os.chdir(rel_base_path) | 1992 os.chdir(rel_base_path) |
| 1976 | 1993 |
| 1977 # Stuff our change into the merge branch. | 1994 # Stuff our change into the merge branch. |
| 1978 # We wrap in a try...finally block so if anything goes wrong, | 1995 # We wrap in a try...finally block so if anything goes wrong, |
| 1979 # we clean up the branches. | 1996 # we clean up the branches. |
| 1980 retcode = -1 | 1997 retcode = -1 |
| 1998 used_pending = False | |
| 1981 try: | 1999 try: |
| 1982 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) | 2000 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) |
| 1983 RunGit(['reset', '--soft', base_branch]) | 2001 RunGit(['reset', '--soft', base_branch]) |
| 1984 if options.contributor: | 2002 if options.contributor: |
| 1985 RunGit( | 2003 RunGit( |
| 1986 [ | 2004 [ |
| 1987 'commit', '--author', options.contributor, | 2005 'commit', '--author', options.contributor, |
| 1988 '-m', commit_desc.description, | 2006 '-m', commit_desc.description, |
| 1989 ]) | 2007 ]) |
| 1990 else: | 2008 else: |
| 1991 RunGit(['commit', '-m', commit_desc.description]) | 2009 RunGit(['commit', '-m', commit_desc.description]) |
| 1992 if base_has_submodules: | 2010 if base_has_submodules: |
| 1993 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() | 2011 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() |
| 1994 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) | 2012 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) |
| 1995 RunGit(['checkout', CHERRY_PICK_BRANCH]) | 2013 RunGit(['checkout', CHERRY_PICK_BRANCH]) |
| 1996 RunGit(['cherry-pick', cherry_pick_commit]) | 2014 RunGit(['cherry-pick', cherry_pick_commit]) |
| 1997 if cmd == 'push': | 2015 if cmd == 'land': |
| 1998 # push the merge branch. | |
| 1999 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) | 2016 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
| 2000 retcode, output = RunGitWithCode( | 2017 pending_prefix = settings.GetPendingRefPrefix() |
| 2001 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) | 2018 if not pending_prefix or branch.startswith(pending_prefix): |
| 2019 # If not using refs/pending/heads/* at all, or target ref is already set | |
| 2020 # to pending, then push to the target ref directly. | |
| 2021 retcode, output = RunGitWithCode( | |
| 2022 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) | |
| 2023 used_pending = pending_prefix and branch.startswith(pending_prefix) | |
| 2024 else: | |
| 2025 # Cherry-pick the change on top of pending ref and then push it. | |
| 2026 assert branch.startswith('refs/'), branch | |
| 2027 assert pending_prefix[-1] == '/', pending_prefix | |
| 2028 pending_ref = pending_prefix + branch[len('refs/'):] | |
| 2029 retcode, output = PushToGitPending(remote, pending_ref) | |
| 2030 used_pending = True | |
| 2002 logging.debug(output) | 2031 logging.debug(output) |
| 2003 else: | 2032 else: |
| 2004 # dcommit the merge branch. | 2033 # dcommit the merge branch. |
| 2005 retcode, output = RunGitWithCode(['svn', 'dcommit', | 2034 retcode, output = RunGitWithCode(['svn', 'dcommit', |
| 2006 '-C%s' % options.similarity, | 2035 '-C%s' % options.similarity, |
| 2007 '--no-rebase', '--rmdir']) | 2036 '--no-rebase', '--rmdir']) |
| 2008 finally: | 2037 finally: |
| 2009 # And then swap back to the original branch and clean up. | 2038 # And then swap back to the original branch and clean up. |
| 2010 RunGit(['checkout', '-q', cl.GetBranch()]) | 2039 RunGit(['checkout', '-q', cl.GetBranch()]) |
| 2011 RunGit(['branch', '-D', MERGE_BRANCH]) | 2040 RunGit(['branch', '-D', MERGE_BRANCH]) |
| 2012 if base_has_submodules: | 2041 if base_has_submodules: |
| 2013 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) | 2042 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) |
| 2014 | 2043 |
| 2015 if cl.GetIssue(): | 2044 if cl.GetIssue(): |
| 2016 if cmd == 'dcommit' and 'Committed r' in output: | 2045 if cmd == 'dcommit' and 'Committed r' in output: |
| 2017 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) | 2046 revision = re.match('.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) |
| 2018 elif cmd == 'push' and retcode == 0: | 2047 elif cmd == 'land' and retcode == 0: |
| 2019 match = (re.match(r'.*?([a-f0-9]{7,})\.\.([a-f0-9]{7,})$', l) | 2048 match = (re.match(r'.*?([a-f0-9]{7,})\.\.([a-f0-9]{7,})$', l) |
| 2020 for l in output.splitlines(False)) | 2049 for l in output.splitlines(False)) |
| 2021 match = filter(None, match) | 2050 match = filter(None, match) |
| 2022 if len(match) != 1: | 2051 if len(match) != 1: |
| 2023 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % | 2052 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % |
| 2024 output) | 2053 output) |
| 2025 revision = match[0].group(2) | 2054 revision = match[0].group(2) |
| 2026 else: | 2055 else: |
| 2027 return 1 | 2056 return 1 |
| 2057 to_pending = ' to pending' if used_pending else '' | |
| 2028 viewvc_url = settings.GetViewVCUrl() | 2058 viewvc_url = settings.GetViewVCUrl() |
| 2029 if viewvc_url and revision: | 2059 if viewvc_url and revision: |
| 2030 change_desc.append_footer('Committed: ' + viewvc_url + revision) | 2060 change_desc.append_footer( |
| 2061 'Committed%s: %s%s' % (to_pending, viewvc_url, revision)) | |
| 2031 elif revision: | 2062 elif revision: |
| 2032 change_desc.append_footer('Committed: ' + revision) | 2063 change_desc.append_footer('Committed%s: %s' % (to_pending, revision)) |
| 2033 print ('Closing issue ' | 2064 print ('Closing issue ' |
| 2034 '(you may be prompted for your codereview password)...') | 2065 '(you may be prompted for your codereview password)...') |
| 2035 cl.UpdateDescription(change_desc.description) | 2066 cl.UpdateDescription(change_desc.description) |
| 2036 cl.CloseIssue() | 2067 cl.CloseIssue() |
| 2037 props = cl.GetIssueProperties() | 2068 props = cl.GetIssueProperties() |
| 2038 patch_num = len(props['patchsets']) | 2069 patch_num = len(props['patchsets']) |
| 2039 comment = "Committed patchset #%d manually as %s" % (patch_num, revision) | 2070 comment = "Committed patchset #%d%s manually as %s" % ( |
| 2071 patch_num, to_pending, revision) | |
| 2040 if options.bypass_hooks: | 2072 if options.bypass_hooks: |
| 2041 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.' | 2073 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.' |
| 2042 else: | 2074 else: |
| 2043 comment += ' (presubmit successful).' | 2075 comment += ' (presubmit successful).' |
| 2044 cl.RpcServer().add_comment(cl.GetIssue(), comment) | 2076 cl.RpcServer().add_comment(cl.GetIssue(), comment) |
| 2045 cl.SetIssue(None) | 2077 cl.SetIssue(None) |
| 2046 | 2078 |
| 2047 if retcode == 0: | 2079 if retcode == 0: |
| 2048 hook = POSTUPSTREAM_HOOK_PATTERN % cmd | 2080 hook = POSTUPSTREAM_HOOK_PATTERN % cmd |
| 2049 if os.path.isfile(hook): | 2081 if os.path.isfile(hook): |
| 2050 RunCommand([hook, base_branch], error_ok=True) | 2082 RunCommand([hook, base_branch], error_ok=True) |
| 2051 | 2083 |
| 2052 return 0 | 2084 return 0 |
| 2053 | 2085 |
| 2054 | 2086 |
| 2087 def PushToGitPending(remote, pending_ref): | |
| 2088 """Fetches pending_ref, cherry-picks current HEAD on top of it, pushes. | |
| 2089 | |
| 2090 Returns: | |
| 2091 (retcode of last operation, output log of last operation). | |
| 2092 """ | |
| 2093 cherry = RunGit(['rev-parse', 'HEAD']).strip() | |
| 2094 code = 0 | |
| 2095 out = '' | |
| 2096 attempt = 0 | |
| 2097 while attempt < 5: | |
| 2098 attempt += 1 | |
| 2099 | |
| 2100 # Fetch. Retry fetch errors. | |
| 2101 code, out = RunGitWithCode( | |
| 2102 ['fetch', remote, '+%s:refs/git-cl/pending' % pending_ref]) | |
| 2103 if code: | |
| 2104 continue | |
| 2105 | |
| 2106 # Try to cherry pick. Abort on merge conflicts. | |
| 2107 RunGitWithCode(['checkout', 'refs/git-cl/pending']) | |
|
iannucci
2014/08/21 20:37:03
let's make this
refs/git-cl/pending/heads/master
Vadim Sh.
2014/08/21 21:14:35
Done.
| |
| 2108 code, out = RunGitWithCode(['cherry-pick', cherry]) | |
|
Vadim Sh.
2014/08/21 19:31:22
cherry-pick supports different merge strategies. A
| |
| 2109 if code: | |
| 2110 RunGitWithCode(['cherry-pick', '--abort']) | |
|
Vadim Sh.
2014/08/21 19:31:22
Not sure it's needed...
iannucci
2014/08/21 20:32:20
yep
| |
| 2111 return code, out | |
| 2112 | |
| 2113 # Applied cleanly, try to push now. Retry on error (flake or non-ff push). | |
| 2114 code, out = RunGitWithCode(['push', remote, 'HEAD:%s' % pending_ref]) | |
|
Vadim Sh.
2014/08/21 19:31:22
This can fail in 3 cases:
1. Network\git flake.
2.
iannucci
2014/08/21 20:32:20
I think we should just put 'retry' before 'push'
Vadim Sh.
2014/08/21 21:14:35
Done.
| |
| 2115 if not code: | |
|
iannucci
2014/08/21 20:32:20
let's do `code == 0`
Vadim Sh.
2014/08/21 21:14:35
Done.
| |
| 2116 # Success. | |
| 2117 return code, out | |
| 2118 | |
| 2119 return code, out | |
| 2120 | |
| 2121 | |
| 2055 @subcommand.usage('[upstream branch to apply against]') | 2122 @subcommand.usage('[upstream branch to apply against]') |
| 2056 def CMDdcommit(parser, args): | 2123 def CMDdcommit(parser, args): |
| 2057 """Commits the current changelist via git-svn.""" | 2124 """Commits the current changelist via git-svn.""" |
| 2058 if not settings.GetIsGitSvn(): | 2125 if not settings.GetIsGitSvn(): |
| 2059 message = """This doesn't appear to be an SVN repository. | 2126 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 | 2127 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. | 2128 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 | 2129 If your project has a true writeable upstream repository, you probably want |
| 2063 to run 'git cl land' instead. | 2130 to run 'git cl land' instead. |
| 2064 Choose wisely, if you get this wrong, your commit might appear to succeed but | 2131 Choose wisely, if you get this wrong, your commit might appear to succeed but |
| 2065 will instead be silently ignored.""" | 2132 will instead be silently ignored.""" |
| 2066 print(message) | 2133 print(message) |
| 2067 ask_for_data('[Press enter to dcommit or ctrl-C to quit]') | 2134 ask_for_data('[Press enter to dcommit or ctrl-C to quit]') |
| 2068 return SendUpstream(parser, args, 'dcommit') | 2135 return SendUpstream(parser, args, 'dcommit') |
| 2069 | 2136 |
| 2070 | 2137 |
| 2071 @subcommand.usage('[upstream branch to apply against]') | 2138 @subcommand.usage('[upstream branch to apply against]') |
| 2072 def CMDland(parser, args): | 2139 def CMDland(parser, args): |
| 2073 """Commits the current changelist via git.""" | 2140 """Commits the current changelist via git.""" |
| 2074 if settings.GetIsGitSvn(): | 2141 if settings.GetIsGitSvn(): |
| 2075 print('This appears to be an SVN repository.') | 2142 print('This appears to be an SVN repository.') |
| 2076 print('Are you sure you didn\'t mean \'git cl dcommit\'?') | 2143 print('Are you sure you didn\'t mean \'git cl dcommit\'?') |
| 2077 ask_for_data('[Press enter to push or ctrl-C to quit]') | 2144 ask_for_data('[Press enter to push or ctrl-C to quit]') |
| 2078 return SendUpstream(parser, args, 'push') | 2145 return SendUpstream(parser, args, 'land') |
| 2079 | 2146 |
| 2080 | 2147 |
| 2081 @subcommand.usage('<patch url or issue id>') | 2148 @subcommand.usage('<patch url or issue id>') |
| 2082 def CMDpatch(parser, args): | 2149 def CMDpatch(parser, args): |
| 2083 """Patches in a code review.""" | 2150 """Patches in a code review.""" |
| 2084 parser.add_option('-b', dest='newbranch', | 2151 parser.add_option('-b', dest='newbranch', |
| 2085 help='create a new branch off trunk for the patch') | 2152 help='create a new branch off trunk for the patch') |
| 2086 parser.add_option('-f', '--force', action='store_true', | 2153 parser.add_option('-f', '--force', action='store_true', |
| 2087 help='with -b, clobber any existing branch') | 2154 help='with -b, clobber any existing branch') |
| 2088 parser.add_option('-d', '--directory', action='store', metavar='DIR', | 2155 parser.add_option('-d', '--directory', action='store', metavar='DIR', |
| (...skipping 584 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2673 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 2740 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
| 2674 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 2741 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
| 2675 | 2742 |
| 2676 | 2743 |
| 2677 if __name__ == '__main__': | 2744 if __name__ == '__main__': |
| 2678 # These affect sys.stdout so do it outside of main() to simplify mocks in | 2745 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 2679 # unit testing. | 2746 # unit testing. |
| 2680 fix_encoding.fix_encoding() | 2747 fix_encoding.fix_encoding() |
| 2681 colorama.init() | 2748 colorama.init() |
| 2682 sys.exit(main(sys.argv[1:])) | 2749 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |