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 and Gerrit.""" | 8 """A git-command for integrating reviews on Rietveld and Gerrit.""" |
9 | 9 |
10 from distutils.version import LooseVersion | 10 from distutils.version import LooseVersion |
(...skipping 1408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1419 directory: switch to directory before applying the patch. Rietveld only. | 1419 directory: switch to directory before applying the patch. Rietveld only. |
1420 """ | 1420 """ |
1421 raise NotImplementedError() | 1421 raise NotImplementedError() |
1422 | 1422 |
1423 @staticmethod | 1423 @staticmethod |
1424 def ParseIssueURL(parsed_url): | 1424 def ParseIssueURL(parsed_url): |
1425 """Parses url and returns instance of _ParsedIssueNumberArgument or None if | 1425 """Parses url and returns instance of _ParsedIssueNumberArgument or None if |
1426 failed.""" | 1426 failed.""" |
1427 raise NotImplementedError() | 1427 raise NotImplementedError() |
1428 | 1428 |
| 1429 def CMDUploadChange(self, options, args, change): |
| 1430 """Uploads a change to codereview.""" |
| 1431 raise NotImplementedError() |
| 1432 |
1429 | 1433 |
1430 class _RietveldChangelistImpl(_ChangelistCodereviewBase): | 1434 class _RietveldChangelistImpl(_ChangelistCodereviewBase): |
1431 def __init__(self, changelist, auth_config=None, rietveld_server=None): | 1435 def __init__(self, changelist, auth_config=None, rietveld_server=None): |
1432 super(_RietveldChangelistImpl, self).__init__(changelist) | 1436 super(_RietveldChangelistImpl, self).__init__(changelist) |
1433 assert settings, 'must be initialized in _ChangelistCodereviewBase' | 1437 assert settings, 'must be initialized in _ChangelistCodereviewBase' |
1434 settings.GetDefaultServerUrl() | 1438 settings.GetDefaultServerUrl() |
1435 | 1439 |
1436 self._rietveld_server = rietveld_server | 1440 self._rietveld_server = rietveld_server |
1437 self._auth_config = auth_config | 1441 self._auth_config = auth_config |
1438 self._props = None | 1442 self._props = None |
1439 self._rpc_server = None | 1443 self._rpc_server = None |
1440 | 1444 |
1441 def GetAuthConfig(self): | |
1442 return self._auth_config | |
1443 | |
1444 def GetCodereviewServer(self): | 1445 def GetCodereviewServer(self): |
1445 if not self._rietveld_server: | 1446 if not self._rietveld_server: |
1446 # If we're on a branch then get the server potentially associated | 1447 # If we're on a branch then get the server potentially associated |
1447 # with that branch. | 1448 # with that branch. |
1448 if self.GetIssue(): | 1449 if self.GetIssue(): |
1449 rietveld_server_setting = self.GetCodereviewServerSetting() | 1450 rietveld_server_setting = self.GetCodereviewServerSetting() |
1450 if rietveld_server_setting: | 1451 if rietveld_server_setting: |
1451 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit( | 1452 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit( |
1452 ['config', rietveld_server_setting], error_ok=True).strip()) | 1453 ['config', rietveld_server_setting], error_ok=True).strip()) |
1453 if not self._rietveld_server: | 1454 if not self._rietveld_server: |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1680 # Rietveld patch: https://domain/download/issue<number>_<patchset>.diff | 1681 # Rietveld patch: https://domain/download/issue<number>_<patchset>.diff |
1681 match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path) | 1682 match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path) |
1682 if match: | 1683 if match: |
1683 return _RietveldParsedIssueNumberArgument( | 1684 return _RietveldParsedIssueNumberArgument( |
1684 issue=int(match.group(1)), | 1685 issue=int(match.group(1)), |
1685 patchset=int(match.group(2)), | 1686 patchset=int(match.group(2)), |
1686 hostname=parsed_url.netloc, | 1687 hostname=parsed_url.netloc, |
1687 patch_url=gclient_utils.UpgradeToHttps(parsed_url.geturl())) | 1688 patch_url=gclient_utils.UpgradeToHttps(parsed_url.geturl())) |
1688 return None | 1689 return None |
1689 | 1690 |
| 1691 def CMDUploadChange(self, options, args, change): |
| 1692 """Upload the patch to Rietveld.""" |
| 1693 upload_args = ['--assume_yes'] # Don't ask about untracked files. |
| 1694 upload_args.extend(['--server', self.GetCodereviewServer()]) |
| 1695 # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl. |
| 1696 upload_args.extend(auth.auth_config_to_command_options(self._auth_config)) |
| 1697 if options.emulate_svn_auto_props: |
| 1698 upload_args.append('--emulate_svn_auto_props') |
| 1699 |
| 1700 change_desc = None |
| 1701 |
| 1702 if options.email is not None: |
| 1703 upload_args.extend(['--email', options.email]) |
| 1704 |
| 1705 if self.GetIssue(): |
| 1706 if options.title: |
| 1707 upload_args.extend(['--title', options.title]) |
| 1708 if options.message: |
| 1709 upload_args.extend(['--message', options.message]) |
| 1710 upload_args.extend(['--issue', str(self.GetIssue())]) |
| 1711 print ('This branch is associated with issue %s. ' |
| 1712 'Adding patch to that issue.' % self.GetIssue()) |
| 1713 else: |
| 1714 if options.title: |
| 1715 upload_args.extend(['--title', options.title]) |
| 1716 message = (options.title or options.message or |
| 1717 CreateDescriptionFromLog(args)) |
| 1718 change_desc = ChangeDescription(message) |
| 1719 if options.reviewers or options.tbr_owners: |
| 1720 change_desc.update_reviewers(options.reviewers, |
| 1721 options.tbr_owners, |
| 1722 change) |
| 1723 if not options.force: |
| 1724 change_desc.prompt() |
| 1725 |
| 1726 if not change_desc.description: |
| 1727 print "Description is empty; aborting." |
| 1728 return 1 |
| 1729 |
| 1730 upload_args.extend(['--message', change_desc.description]) |
| 1731 if change_desc.get_reviewers(): |
| 1732 upload_args.append('--reviewers=%s' % ','.join( |
| 1733 change_desc.get_reviewers())) |
| 1734 if options.send_mail: |
| 1735 if not change_desc.get_reviewers(): |
| 1736 DieWithError("Must specify reviewers to send email.") |
| 1737 upload_args.append('--send_mail') |
| 1738 |
| 1739 # We check this before applying rietveld.private assuming that in |
| 1740 # rietveld.cc only addresses which we can send private CLs to are listed |
| 1741 # if rietveld.private is set, and so we should ignore rietveld.cc only |
| 1742 # when --private is specified explicitly on the command line. |
| 1743 if options.private: |
| 1744 logging.warn('rietveld.cc is ignored since private flag is specified. ' |
| 1745 'You need to review and add them manually if necessary.') |
| 1746 cc = self.GetCCListWithoutDefault() |
| 1747 else: |
| 1748 cc = self.GetCCList() |
| 1749 cc = ','.join(filter(None, (cc, ','.join(options.cc)))) |
| 1750 if cc: |
| 1751 upload_args.extend(['--cc', cc]) |
| 1752 |
| 1753 if options.private or settings.GetDefaultPrivateFlag() == "True": |
| 1754 upload_args.append('--private') |
| 1755 |
| 1756 upload_args.extend(['--git_similarity', str(options.similarity)]) |
| 1757 if not options.find_copies: |
| 1758 upload_args.extend(['--git_no_find_copies']) |
| 1759 |
| 1760 # Include the upstream repo's URL in the change -- this is useful for |
| 1761 # projects that have their source spread across multiple repos. |
| 1762 remote_url = self.GetGitBaseUrlFromConfig() |
| 1763 if not remote_url: |
| 1764 if settings.GetIsGitSvn(): |
| 1765 remote_url = self.GetGitSvnRemoteUrl() |
| 1766 else: |
| 1767 if self.GetRemoteUrl() and '/' in self.GetUpstreamBranch(): |
| 1768 remote_url = '%s@%s' % (self.GetRemoteUrl(), |
| 1769 self.GetUpstreamBranch().split('/')[-1]) |
| 1770 if remote_url: |
| 1771 upload_args.extend(['--base_url', remote_url]) |
| 1772 remote, remote_branch = self.GetRemoteBranch() |
| 1773 target_ref = GetTargetRef(remote, remote_branch, options.target_branch, |
| 1774 settings.GetPendingRefPrefix()) |
| 1775 if target_ref: |
| 1776 upload_args.extend(['--target_ref', target_ref]) |
| 1777 |
| 1778 # Look for dependent patchsets. See crbug.com/480453 for more details. |
| 1779 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) |
| 1780 upstream_branch = ShortBranchName(upstream_branch) |
| 1781 if remote is '.': |
| 1782 # A local branch is being tracked. |
| 1783 local_branch = ShortBranchName(upstream_branch) |
| 1784 if settings.GetIsSkipDependencyUpload(local_branch): |
| 1785 print |
| 1786 print ('Skipping dependency patchset upload because git config ' |
| 1787 'branch.%s.skip-deps-uploads is set to True.' % local_branch) |
| 1788 print |
| 1789 else: |
| 1790 auth_config = auth.extract_auth_config_from_options(options) |
| 1791 branch_cl = Changelist(branchref=local_branch, |
| 1792 auth_config=auth_config) |
| 1793 branch_cl_issue_url = branch_cl.GetIssueURL() |
| 1794 branch_cl_issue = branch_cl.GetIssue() |
| 1795 branch_cl_patchset = branch_cl.GetPatchset() |
| 1796 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset: |
| 1797 upload_args.extend( |
| 1798 ['--depends_on_patchset', '%s:%s' % ( |
| 1799 branch_cl_issue, branch_cl_patchset)]) |
| 1800 print ( |
| 1801 '\n' |
| 1802 'The current branch (%s) is tracking a local branch (%s) with ' |
| 1803 'an associated CL.\n' |
| 1804 'Adding %s/#ps%s as a dependency patchset.\n' |
| 1805 '\n' % (self.GetBranch(), local_branch, branch_cl_issue_url, |
| 1806 branch_cl_patchset)) |
| 1807 |
| 1808 project = settings.GetProject() |
| 1809 if project: |
| 1810 upload_args.extend(['--project', project]) |
| 1811 |
| 1812 if options.cq_dry_run: |
| 1813 upload_args.extend(['--cq_dry_run']) |
| 1814 |
| 1815 try: |
| 1816 upload_args = ['upload'] + upload_args + args |
| 1817 logging.info('upload.RealMain(%s)', upload_args) |
| 1818 issue, patchset = upload.RealMain(upload_args) |
| 1819 issue = int(issue) |
| 1820 patchset = int(patchset) |
| 1821 except KeyboardInterrupt: |
| 1822 sys.exit(1) |
| 1823 except: |
| 1824 # If we got an exception after the user typed a description for their |
| 1825 # change, back up the description before re-raising. |
| 1826 if change_desc: |
| 1827 backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE) |
| 1828 print('\nGot exception while uploading -- saving description to %s\n' % |
| 1829 backup_path) |
| 1830 backup_file = open(backup_path, 'w') |
| 1831 backup_file.write(change_desc.description) |
| 1832 backup_file.close() |
| 1833 raise |
| 1834 |
| 1835 if not self.GetIssue(): |
| 1836 self.SetIssue(issue) |
| 1837 self.SetPatchset(patchset) |
| 1838 |
| 1839 if options.use_commit_queue: |
| 1840 self.SetFlag('commit', '1') |
| 1841 return 0 |
| 1842 |
1690 | 1843 |
1691 class _GerritChangelistImpl(_ChangelistCodereviewBase): | 1844 class _GerritChangelistImpl(_ChangelistCodereviewBase): |
1692 def __init__(self, changelist, auth_config=None): | 1845 def __init__(self, changelist, auth_config=None): |
1693 # auth_config is Rietveld thing, kept here to preserve interface only. | 1846 # auth_config is Rietveld thing, kept here to preserve interface only. |
1694 super(_GerritChangelistImpl, self).__init__(changelist) | 1847 super(_GerritChangelistImpl, self).__init__(changelist) |
1695 self._change_id = None | 1848 self._change_id = None |
1696 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com | 1849 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com |
1697 self._gerrit_host = None # e.g. chromium-review.googlesource.com | 1850 self._gerrit_host = None # e.g. chromium-review.googlesource.com |
1698 | 1851 |
1699 def _GetGerritHost(self): | 1852 def _GetGerritHost(self): |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1819 # TODO(tandrii) | 1972 # TODO(tandrii) |
1820 raise NotImplementedError() | 1973 raise NotImplementedError() |
1821 | 1974 |
1822 def CloseIssue(self): | 1975 def CloseIssue(self): |
1823 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') | 1976 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') |
1824 | 1977 |
1825 def SubmitIssue(self, wait_for_merge=True): | 1978 def SubmitIssue(self, wait_for_merge=True): |
1826 gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(), | 1979 gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(), |
1827 wait_for_merge=wait_for_merge) | 1980 wait_for_merge=wait_for_merge) |
1828 | 1981 |
1829 def _GetChangeDetail(self, options): | 1982 def _GetChangeDetail(self, options=None, issue=None): |
1830 return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), | 1983 options = options or [] |
1831 options) | 1984 issue = issue or self.GetIssue() |
| 1985 assert issue, 'issue required to query Gerrit' |
| 1986 return gerrit_util.GetChangeDetail(self._GetGerritHost(), options, issue) |
1832 | 1987 |
1833 def CMDLand(self, force, bypass_hooks, verbose): | 1988 def CMDLand(self, force, bypass_hooks, verbose): |
1834 if git_common.is_dirty_git_tree('land'): | 1989 if git_common.is_dirty_git_tree('land'): |
1835 return 1 | 1990 return 1 |
1836 differs = True | 1991 differs = True |
1837 last_upload = RunGit(['config', | 1992 last_upload = RunGit(['config', |
1838 'branch.%s.gerritsquashhash' % self.GetBranch()], | 1993 'branch.%s.gerritsquashhash' % self.GetBranch()], |
1839 error_ok=True).strip() | 1994 error_ok=True).strip() |
1840 # Note: git diff outputs nothing if there is no diff. | 1995 # Note: git diff outputs nothing if there is no diff. |
1841 if not last_upload or RunGit(['diff', last_upload]).strip(): | 1996 if not last_upload or RunGit(['diff', last_upload]).strip(): |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1915 else: | 2070 else: |
1916 part = parsed_url.path | 2071 part = parsed_url.path |
1917 match = re.match('(/c)?/(\d+)(/(\d+)?/?)?$', part) | 2072 match = re.match('(/c)?/(\d+)(/(\d+)?/?)?$', part) |
1918 if match: | 2073 if match: |
1919 return _ParsedIssueNumberArgument( | 2074 return _ParsedIssueNumberArgument( |
1920 issue=int(match.group(2)), | 2075 issue=int(match.group(2)), |
1921 patchset=int(match.group(4)) if match.group(4) else None, | 2076 patchset=int(match.group(4)) if match.group(4) else None, |
1922 hostname=parsed_url.netloc) | 2077 hostname=parsed_url.netloc) |
1923 return None | 2078 return None |
1924 | 2079 |
| 2080 def CMDUploadChange(self, options, args, change): |
| 2081 """Upload the current branch to Gerrit.""" |
| 2082 # We assume the remote called "origin" is the one we want. |
| 2083 # It is probably not worthwhile to support different workflows. |
| 2084 gerrit_remote = 'origin' |
| 2085 |
| 2086 remote, remote_branch = self.GetRemoteBranch() |
| 2087 branch = GetTargetRef(remote, remote_branch, options.target_branch, |
| 2088 pending_prefix='') |
| 2089 |
| 2090 if options.title: |
| 2091 # TODO(tandrii): it's now supported by Gerrit, implement! |
| 2092 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..." |
| 2093 return 1 |
| 2094 |
| 2095 if options.squash: |
| 2096 if not self.GetIssue(): |
| 2097 # TODO(tandrii): deperecate this after 2016Q2. Backwards compatibility |
| 2098 # with shadow branch, which used to contain change-id for a given |
| 2099 # branch, using which we can fetch actual issue number and set it as the |
| 2100 # property of the branch, which is the new way. |
| 2101 message = RunGitSilent([ |
| 2102 'show', '--format=%B', '-s', |
| 2103 'refs/heads/git_cl_uploads/%s' % self.GetBranch()]) |
| 2104 if message: |
| 2105 change_ids = git_footers.get_footer_change_id(message.strip()) |
| 2106 if change_ids and len(change_ids) == 1: |
| 2107 details = self._GetChangeDetail(issue=change_ids[0]) |
| 2108 if details: |
| 2109 print('WARNING: found old upload in branch git_cl_uploads/%s ' |
| 2110 'corresponding to issue %s' % |
| 2111 (self.GetBranch(), details['_number'])) |
| 2112 self.SetIssue(details['_number']) |
| 2113 if not self.GetIssue(): |
| 2114 DieWithError( |
| 2115 '\n' # For readability of the blob below. |
| 2116 'Found old upload in branch git_cl_uploads/%s, ' |
| 2117 'but failed to find corresponding Gerrit issue.\n' |
| 2118 'If you know the issue number, set it manually first:\n' |
| 2119 ' git cl issue 123456\n' |
| 2120 'If you intended to upload this CL as new issue, ' |
| 2121 'just delete or rename the old upload branch:\n' |
| 2122 ' git rename-branch git_cl_uploads/%s old_upload-%s\n' |
| 2123 'After that, please run git cl upload again.' % |
| 2124 tuple([self.GetBranch()] * 3)) |
| 2125 # End of backwards compatability. |
| 2126 |
| 2127 if self.GetIssue(): |
| 2128 # Try to get the message from a previous upload. |
| 2129 message = self.GetDescription() |
| 2130 if not message: |
| 2131 DieWithError( |
| 2132 'failed to fetch description from current Gerrit issue %d\n' |
| 2133 '%s' % (self.GetIssue(), self.GetIssueURL())) |
| 2134 change_id = self._GetChangeDetail()['change_id'] |
| 2135 while True: |
| 2136 footer_change_ids = git_footers.get_footer_change_id(message) |
| 2137 if footer_change_ids == [change_id]: |
| 2138 break |
| 2139 if not footer_change_ids: |
| 2140 message = git_footers.add_footer_change_id(message, change_id) |
| 2141 print('WARNING: appended missing Change-Id to issue description') |
| 2142 continue |
| 2143 # There is already a valid footer but with different or several ids. |
| 2144 # Doing this automatically is non-trivial as we don't want to lose |
| 2145 # existing other footers, yet we want to append just 1 desired |
| 2146 # Change-Id. Thus, just create a new footer, but let user verify the |
| 2147 # new description. |
| 2148 message = '%s\n\nChange-Id: %s' % (message, change_id) |
| 2149 print( |
| 2150 'WARNING: issue %s has Change-Id footer(s):\n' |
| 2151 ' %s\n' |
| 2152 'but issue has Change-Id %s, according to Gerrit.\n' |
| 2153 'Please, check the proposed correction to the description, ' |
| 2154 'and edit it if necessary but keep the "Change-Id: %s" footer\n' |
| 2155 % (self.GetIssue(), '\n '.join(footer_change_ids), change_id, |
| 2156 change_id)) |
| 2157 ask_for_data('Press enter to edit now, Ctrl+C to abort') |
| 2158 if not options.force: |
| 2159 change_desc = ChangeDescription(message) |
| 2160 change_desc.prompt() |
| 2161 message = change_desc.description |
| 2162 if not message: |
| 2163 DieWithError("Description is empty. Aborting...") |
| 2164 # Continue the while loop. |
| 2165 # Sanity check of this code - we should end up with proper message |
| 2166 # footer. |
| 2167 assert [change_id] == git_footers.get_footer_change_id(message) |
| 2168 change_desc = ChangeDescription(message) |
| 2169 else: |
| 2170 change_desc = ChangeDescription( |
| 2171 options.message or CreateDescriptionFromLog(args)) |
| 2172 if not options.force: |
| 2173 change_desc.prompt() |
| 2174 if not change_desc.description: |
| 2175 DieWithError("Description is empty. Aborting...") |
| 2176 message = change_desc.description |
| 2177 change_ids = git_footers.get_footer_change_id(message) |
| 2178 if len(change_ids) > 1: |
| 2179 DieWithError('too many Change-Id footers, at most 1 allowed.') |
| 2180 if not change_ids: |
| 2181 # Generate the Change-Id automatically. |
| 2182 message = git_footers.add_footer_change_id( |
| 2183 message, GenerateGerritChangeId(message)) |
| 2184 change_desc.set_description(message) |
| 2185 change_ids = git_footers.get_footer_change_id(message) |
| 2186 assert len(change_ids) == 1 |
| 2187 change_id = change_ids[0] |
| 2188 |
| 2189 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) |
| 2190 if remote is '.': |
| 2191 # If our upstream branch is local, we base our squashed commit on its |
| 2192 # squashed version. |
| 2193 upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch) |
| 2194 # Check the squashed hash of the parent. |
| 2195 parent = RunGit(['config', |
| 2196 'branch.%s.gerritsquashhash' % upstream_branch_name], |
| 2197 error_ok=True).strip() |
| 2198 # Verify that the upstream branch has been uploaded too, otherwise |
| 2199 # Gerrit will create additional CLs when uploading. |
| 2200 if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) != |
| 2201 RunGitSilent(['rev-parse', parent + ':'])): |
| 2202 # TODO(tandrii): remove "old depot_tools" part on April 12, 2016. |
| 2203 DieWithError( |
| 2204 'Upload upstream branch %s first.\n' |
| 2205 'Note: maybe you\'ve uploaded it with --no-squash or with an old ' |
| 2206 'version of depot_tools. If so, then re-upload it with:\n' |
| 2207 ' git cl upload --squash\n' % upstream_branch_name) |
| 2208 else: |
| 2209 parent = self.GetCommonAncestorWithUpstream() |
| 2210 |
| 2211 tree = RunGit(['rev-parse', 'HEAD:']).strip() |
| 2212 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, |
| 2213 '-m', message]).strip() |
| 2214 else: |
| 2215 change_desc = ChangeDescription( |
| 2216 options.message or CreateDescriptionFromLog(args)) |
| 2217 if not change_desc.description: |
| 2218 DieWithError("Description is empty. Aborting...") |
| 2219 |
| 2220 if not git_footers.get_footer_change_id(change_desc.description): |
| 2221 DownloadGerritHook(False) |
| 2222 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) |
| 2223 ref_to_push = 'HEAD' |
| 2224 parent = '%s/%s' % (gerrit_remote, branch) |
| 2225 change_id = git_footers.get_footer_change_id(change_desc.description)[0] |
| 2226 |
| 2227 assert change_desc |
| 2228 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, |
| 2229 ref_to_push)]).splitlines() |
| 2230 if len(commits) > 1: |
| 2231 print('WARNING: This will upload %d commits. Run the following command ' |
| 2232 'to see which commits will be uploaded: ' % len(commits)) |
| 2233 print('git log %s..%s' % (parent, ref_to_push)) |
| 2234 print('You can also use `git squash-branch` to squash these into a ' |
| 2235 'single commit.') |
| 2236 ask_for_data('About to upload; enter to confirm.') |
| 2237 |
| 2238 if options.reviewers or options.tbr_owners: |
| 2239 change_desc.update_reviewers(options.reviewers, options.tbr_owners, |
| 2240 change) |
| 2241 |
| 2242 receive_options = [] |
| 2243 cc = self.GetCCList().split(',') |
| 2244 if options.cc: |
| 2245 cc.extend(options.cc) |
| 2246 cc = filter(None, cc) |
| 2247 if cc: |
| 2248 receive_options += ['--cc=' + email for email in cc] |
| 2249 if change_desc.get_reviewers(): |
| 2250 receive_options.extend( |
| 2251 '--reviewer=' + email for email in change_desc.get_reviewers()) |
| 2252 |
| 2253 git_command = ['push'] |
| 2254 if receive_options: |
| 2255 git_command.append('--receive-pack=git receive-pack %s' % |
| 2256 ' '.join(receive_options)) |
| 2257 git_command += [gerrit_remote, ref_to_push + ':refs/for/' + branch] |
| 2258 push_stdout = gclient_utils.CheckCallAndFilter( |
| 2259 ['git'] + git_command, |
| 2260 print_stdout=True, |
| 2261 # Flush after every line: useful for seeing progress when running as |
| 2262 # recipe. |
| 2263 filter_fn=lambda _: sys.stdout.flush()) |
| 2264 |
| 2265 if options.squash: |
| 2266 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') |
| 2267 change_numbers = [m.group(1) |
| 2268 for m in map(regex.match, push_stdout.splitlines()) |
| 2269 if m] |
| 2270 if len(change_numbers) != 1: |
| 2271 DieWithError( |
| 2272 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' |
| 2273 'Change-Id: %s') % (len(change_numbers), change_id)) |
| 2274 self.SetIssue(change_numbers[0]) |
| 2275 RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(), |
| 2276 ref_to_push]) |
| 2277 return 0 |
| 2278 |
| 2279 |
1925 | 2280 |
1926 _CODEREVIEW_IMPLEMENTATIONS = { | 2281 _CODEREVIEW_IMPLEMENTATIONS = { |
1927 'rietveld': _RietveldChangelistImpl, | 2282 'rietveld': _RietveldChangelistImpl, |
1928 'gerrit': _GerritChangelistImpl, | 2283 'gerrit': _GerritChangelistImpl, |
1929 } | 2284 } |
1930 | 2285 |
1931 | 2286 |
1932 class ChangeDescription(object): | 2287 class ChangeDescription(object): |
1933 """Contains a parsed form of the change description.""" | 2288 """Contains a parsed form of the change description.""" |
1934 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' | 2289 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
(...skipping 852 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2787 lines.append('') | 3142 lines.append('') |
2788 # Note: Gerrit's commit-hook actually cleans message of some lines and | 3143 # Note: Gerrit's commit-hook actually cleans message of some lines and |
2789 # whitespace. This code is not doing this, but it clearly won't decrease | 3144 # whitespace. This code is not doing this, but it clearly won't decrease |
2790 # entropy. | 3145 # entropy. |
2791 lines.append(message) | 3146 lines.append(message) |
2792 change_hash = RunCommand(['git', 'hash-object', '-t', 'commit', '--stdin'], | 3147 change_hash = RunCommand(['git', 'hash-object', '-t', 'commit', '--stdin'], |
2793 stdin='\n'.join(lines)) | 3148 stdin='\n'.join(lines)) |
2794 return 'I%s' % change_hash.strip() | 3149 return 'I%s' % change_hash.strip() |
2795 | 3150 |
2796 | 3151 |
2797 def GerritUpload(options, args, cl, change): | |
2798 """upload the current branch to gerrit.""" | |
2799 # TODO(tandrii): refactor this to be a method of _GerritChangelistImpl, | |
2800 # to avoid private members accessors below. | |
2801 | |
2802 # We assume the remote called "origin" is the one we want. | |
2803 # It is probably not worthwhile to support different workflows. | |
2804 gerrit_remote = 'origin' | |
2805 | |
2806 remote, remote_branch = cl.GetRemoteBranch() | |
2807 branch = GetTargetRef(remote, remote_branch, options.target_branch, | |
2808 pending_prefix='') | |
2809 | |
2810 if options.title: | |
2811 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..." | |
2812 return 1 | |
2813 | |
2814 if options.squash: | |
2815 if not cl.GetIssue(): | |
2816 # TODO(tandrii): deperecate this after 2016Q2. | |
2817 # Backwards compatibility with shadow branch, which used to contain | |
2818 # change-id for a given branch, using which we can fetch actual issue | |
2819 # number and set it as the property of the branch, which is the new way. | |
2820 message = RunGitSilent(['show', '--format=%B', '-s', | |
2821 'refs/heads/git_cl_uploads/%s' % cl.GetBranch()]) | |
2822 if message: | |
2823 change_ids = git_footers.get_footer_change_id(message.strip()) | |
2824 if change_ids and len(change_ids) == 1: | |
2825 details = gerrit_util.GetChangeDetail( | |
2826 cl._codereview_impl._GetGerritHost(), change_ids[0]) | |
2827 if details: | |
2828 print('WARNING: found old upload in branch git_cl_uploads/%s ' | |
2829 'corresponding to issue %s' % | |
2830 (cl.GetBranch(), details['_number'])) | |
2831 cl.SetIssue(details['_number']) | |
2832 if not cl.GetIssue(): | |
2833 DieWithError( | |
2834 '\n' # For readability of the blob below. | |
2835 'Found old upload in branch git_cl_uploads/%s, ' | |
2836 'but failed to find corresponding Gerrit issue.\n' | |
2837 'If you know the issue number, set it manually first:\n' | |
2838 ' git cl issue 123456\n' | |
2839 'If you intended to upload this CL as new issue, ' | |
2840 'just delete or rename the old upload branch:\n' | |
2841 ' git rename-branch git_cl_uploads/%s old_upload-%s\n' | |
2842 'After that, please run git cl upload again.' % | |
2843 tuple([cl.GetBranch()] * 3)) | |
2844 # End of backwards compatability. | |
2845 | |
2846 if cl.GetIssue(): | |
2847 # Try to get the message from a previous upload. | |
2848 message = cl.GetDescription() | |
2849 if not message: | |
2850 DieWithError( | |
2851 'failed to fetch description from current Gerrit issue %d\n' | |
2852 '%s' % (cl.GetIssue(), cl.GetIssueURL())) | |
2853 change_id = cl._codereview_impl._GetChangeDetail([])['change_id'] | |
2854 while True: | |
2855 footer_change_ids = git_footers.get_footer_change_id(message) | |
2856 if footer_change_ids == [change_id]: | |
2857 break | |
2858 if not footer_change_ids: | |
2859 message = git_footers.add_footer_change_id(message, change_id) | |
2860 print('WARNING: appended missing Change-Id to issue description') | |
2861 continue | |
2862 # There is already a valid footer but with different or several ids. | |
2863 # Doing this automatically is non-trivial as we don't want to lose | |
2864 # existing other footers, yet we want to append just 1 desired | |
2865 # Change-Id. Thus, just create a new footer, but let user verify the new | |
2866 # description. | |
2867 message = '%s\n\nChange-Id: %s' % (message, change_id) | |
2868 print( | |
2869 'WARNING: issue %s has Change-Id footer(s):\n' | |
2870 ' %s\n' | |
2871 'but issue has Change-Id %s, according to Gerrit.\n' | |
2872 'Please, check the proposed correction to the description, ' | |
2873 'and edit it if necessary but keep the "Change-Id: %s" footer\n' | |
2874 % (cl.GetIssue(), '\n '.join(footer_change_ids), change_id, | |
2875 change_id)) | |
2876 ask_for_data('Press enter to edit now, Ctrl+C to abort') | |
2877 if not options.force: | |
2878 change_desc = ChangeDescription(message) | |
2879 change_desc.prompt() | |
2880 message = change_desc.description | |
2881 if not message: | |
2882 DieWithError("Description is empty. Aborting...") | |
2883 # Continue the while loop. | |
2884 # Sanity check of this code - we should end up with proper message footer. | |
2885 assert [change_id] == git_footers.get_footer_change_id(message) | |
2886 change_desc = ChangeDescription(message) | |
2887 else: | |
2888 change_desc = ChangeDescription( | |
2889 options.message or CreateDescriptionFromLog(args)) | |
2890 if not options.force: | |
2891 change_desc.prompt() | |
2892 if not change_desc.description: | |
2893 DieWithError("Description is empty. Aborting...") | |
2894 message = change_desc.description | |
2895 change_ids = git_footers.get_footer_change_id(message) | |
2896 if len(change_ids) > 1: | |
2897 DieWithError('too many Change-Id footers, at most 1 allowed.') | |
2898 if not change_ids: | |
2899 # Generate the Change-Id automatically. | |
2900 message = git_footers.add_footer_change_id( | |
2901 message, GenerateGerritChangeId(message)) | |
2902 change_desc.set_description(message) | |
2903 change_ids = git_footers.get_footer_change_id(message) | |
2904 assert len(change_ids) == 1 | |
2905 change_id = change_ids[0] | |
2906 | |
2907 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) | |
2908 if remote is '.': | |
2909 # If our upstream branch is local, we base our squashed commit on its | |
2910 # squashed version. | |
2911 upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch) | |
2912 # Check the squashed hash of the parent. | |
2913 parent = RunGit(['config', | |
2914 'branch.%s.gerritsquashhash' % upstream_branch_name], | |
2915 error_ok=True).strip() | |
2916 # Verify that the upstream branch has been uploaded too, otherwise | |
2917 # Gerrit will create additional CLs when uploading. | |
2918 if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) != | |
2919 RunGitSilent(['rev-parse', parent + ':'])): | |
2920 # TODO(tandrii): remove "old depot_tools" part on April 12, 2016. | |
2921 DieWithError( | |
2922 'Upload upstream branch %s first.\n' | |
2923 'Note: maybe you\'ve uploaded it with --no-squash or with an old\n' | |
2924 ' version of depot_tools. If so, then re-upload it with:\n' | |
2925 ' git cl upload --squash\n' % upstream_branch_name) | |
2926 else: | |
2927 parent = cl.GetCommonAncestorWithUpstream() | |
2928 | |
2929 tree = RunGit(['rev-parse', 'HEAD:']).strip() | |
2930 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, | |
2931 '-m', message]).strip() | |
2932 else: | |
2933 change_desc = ChangeDescription( | |
2934 options.message or CreateDescriptionFromLog(args)) | |
2935 if not change_desc.description: | |
2936 DieWithError("Description is empty. Aborting...") | |
2937 | |
2938 if not git_footers.get_footer_change_id(change_desc.description): | |
2939 DownloadGerritHook(False) | |
2940 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) | |
2941 ref_to_push = 'HEAD' | |
2942 parent = '%s/%s' % (gerrit_remote, branch) | |
2943 change_id = git_footers.get_footer_change_id(change_desc.description)[0] | |
2944 | |
2945 assert change_desc | |
2946 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, | |
2947 ref_to_push)]).splitlines() | |
2948 if len(commits) > 1: | |
2949 print('WARNING: This will upload %d commits. Run the following command ' | |
2950 'to see which commits will be uploaded: ' % len(commits)) | |
2951 print('git log %s..%s' % (parent, ref_to_push)) | |
2952 print('You can also use `git squash-branch` to squash these into a single ' | |
2953 'commit.') | |
2954 ask_for_data('About to upload; enter to confirm.') | |
2955 | |
2956 if options.reviewers or options.tbr_owners: | |
2957 change_desc.update_reviewers(options.reviewers, options.tbr_owners, change) | |
2958 | |
2959 receive_options = [] | |
2960 cc = cl.GetCCList().split(',') | |
2961 if options.cc: | |
2962 cc.extend(options.cc) | |
2963 cc = filter(None, cc) | |
2964 if cc: | |
2965 receive_options += ['--cc=' + email for email in cc] | |
2966 if change_desc.get_reviewers(): | |
2967 receive_options.extend( | |
2968 '--reviewer=' + email for email in change_desc.get_reviewers()) | |
2969 | |
2970 git_command = ['push'] | |
2971 if receive_options: | |
2972 git_command.append('--receive-pack=git receive-pack %s' % | |
2973 ' '.join(receive_options)) | |
2974 git_command += [gerrit_remote, ref_to_push + ':refs/for/' + branch] | |
2975 push_stdout = gclient_utils.CheckCallAndFilter( | |
2976 ['git'] + git_command, | |
2977 print_stdout=True, | |
2978 # Flush after every line: useful for seeing progress when running as | |
2979 # recipe. | |
2980 filter_fn=lambda _: sys.stdout.flush()) | |
2981 | |
2982 if options.squash: | |
2983 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') | |
2984 change_numbers = [m.group(1) | |
2985 for m in map(regex.match, push_stdout.splitlines()) | |
2986 if m] | |
2987 if len(change_numbers) != 1: | |
2988 DieWithError( | |
2989 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' | |
2990 'Change-Id: %s') % (len(change_numbers), change_id)) | |
2991 cl.SetIssue(change_numbers[0]) | |
2992 RunGit(['config', 'branch.%s.gerritsquashhash' % cl.GetBranch(), | |
2993 ref_to_push]) | |
2994 return 0 | |
2995 | |
2996 | |
2997 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix): | 3152 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix): |
2998 """Computes the remote branch ref to use for the CL. | 3153 """Computes the remote branch ref to use for the CL. |
2999 | 3154 |
3000 Args: | 3155 Args: |
3001 remote (str): The git remote for the CL. | 3156 remote (str): The git remote for the CL. |
3002 remote_branch (str): The git remote branch for the CL. | 3157 remote_branch (str): The git remote branch for the CL. |
3003 target_branch (str): The target branch specified by the user. | 3158 target_branch (str): The target branch specified by the user. |
3004 pending_prefix (str): The pending prefix from the settings. | 3159 pending_prefix (str): The pending prefix from the settings. |
3005 """ | 3160 """ |
3006 if not (remote and remote_branch): | 3161 if not (remote and remote_branch): |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3042 remote_branch = remote_branch.replace('refs/remotes/%s/' % remote, | 3197 remote_branch = remote_branch.replace('refs/remotes/%s/' % remote, |
3043 'refs/heads/') | 3198 'refs/heads/') |
3044 elif remote_branch.startswith('refs/remotes/branch-heads'): | 3199 elif remote_branch.startswith('refs/remotes/branch-heads'): |
3045 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') | 3200 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') |
3046 # If a pending prefix exists then replace refs/ with it. | 3201 # If a pending prefix exists then replace refs/ with it. |
3047 if pending_prefix: | 3202 if pending_prefix: |
3048 remote_branch = remote_branch.replace('refs/', pending_prefix) | 3203 remote_branch = remote_branch.replace('refs/', pending_prefix) |
3049 return remote_branch | 3204 return remote_branch |
3050 | 3205 |
3051 | 3206 |
3052 def RietveldUpload(options, args, cl, change): | |
3053 """upload the patch to rietveld.""" | |
3054 upload_args = ['--assume_yes'] # Don't ask about untracked files. | |
3055 upload_args.extend(['--server', cl.GetCodereviewServer()]) | |
3056 # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl. | |
3057 upload_args.extend(auth.auth_config_to_command_options( | |
3058 cl._codereview_impl.GetAuthConfig())) | |
3059 if options.emulate_svn_auto_props: | |
3060 upload_args.append('--emulate_svn_auto_props') | |
3061 | |
3062 change_desc = None | |
3063 | |
3064 if options.email is not None: | |
3065 upload_args.extend(['--email', options.email]) | |
3066 | |
3067 if cl.GetIssue(): | |
3068 if options.title: | |
3069 upload_args.extend(['--title', options.title]) | |
3070 if options.message: | |
3071 upload_args.extend(['--message', options.message]) | |
3072 upload_args.extend(['--issue', str(cl.GetIssue())]) | |
3073 print ("This branch is associated with issue %s. " | |
3074 "Adding patch to that issue." % cl.GetIssue()) | |
3075 else: | |
3076 if options.title: | |
3077 upload_args.extend(['--title', options.title]) | |
3078 message = options.title or options.message or CreateDescriptionFromLog(args) | |
3079 change_desc = ChangeDescription(message) | |
3080 if options.reviewers or options.tbr_owners: | |
3081 change_desc.update_reviewers(options.reviewers, | |
3082 options.tbr_owners, | |
3083 change) | |
3084 if not options.force: | |
3085 change_desc.prompt() | |
3086 | |
3087 if not change_desc.description: | |
3088 print "Description is empty; aborting." | |
3089 return 1 | |
3090 | |
3091 upload_args.extend(['--message', change_desc.description]) | |
3092 if change_desc.get_reviewers(): | |
3093 upload_args.append('--reviewers=' + ','.join(change_desc.get_reviewers())) | |
3094 if options.send_mail: | |
3095 if not change_desc.get_reviewers(): | |
3096 DieWithError("Must specify reviewers to send email.") | |
3097 upload_args.append('--send_mail') | |
3098 | |
3099 # We check this before applying rietveld.private assuming that in | |
3100 # rietveld.cc only addresses which we can send private CLs to are listed | |
3101 # if rietveld.private is set, and so we should ignore rietveld.cc only when | |
3102 # --private is specified explicitly on the command line. | |
3103 if options.private: | |
3104 logging.warn('rietveld.cc is ignored since private flag is specified. ' | |
3105 'You need to review and add them manually if necessary.') | |
3106 cc = cl.GetCCListWithoutDefault() | |
3107 else: | |
3108 cc = cl.GetCCList() | |
3109 cc = ','.join(filter(None, (cc, ','.join(options.cc)))) | |
3110 if cc: | |
3111 upload_args.extend(['--cc', cc]) | |
3112 | |
3113 if options.private or settings.GetDefaultPrivateFlag() == "True": | |
3114 upload_args.append('--private') | |
3115 | |
3116 upload_args.extend(['--git_similarity', str(options.similarity)]) | |
3117 if not options.find_copies: | |
3118 upload_args.extend(['--git_no_find_copies']) | |
3119 | |
3120 # Include the upstream repo's URL in the change -- this is useful for | |
3121 # projects that have their source spread across multiple repos. | |
3122 remote_url = cl.GetGitBaseUrlFromConfig() | |
3123 if not remote_url: | |
3124 if settings.GetIsGitSvn(): | |
3125 remote_url = cl.GetGitSvnRemoteUrl() | |
3126 else: | |
3127 if cl.GetRemoteUrl() and '/' in cl.GetUpstreamBranch(): | |
3128 remote_url = (cl.GetRemoteUrl() + '@' | |
3129 + cl.GetUpstreamBranch().split('/')[-1]) | |
3130 if remote_url: | |
3131 upload_args.extend(['--base_url', remote_url]) | |
3132 remote, remote_branch = cl.GetRemoteBranch() | |
3133 target_ref = GetTargetRef(remote, remote_branch, options.target_branch, | |
3134 settings.GetPendingRefPrefix()) | |
3135 if target_ref: | |
3136 upload_args.extend(['--target_ref', target_ref]) | |
3137 | |
3138 # Look for dependent patchsets. See crbug.com/480453 for more details. | |
3139 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) | |
3140 upstream_branch = ShortBranchName(upstream_branch) | |
3141 if remote is '.': | |
3142 # A local branch is being tracked. | |
3143 local_branch = ShortBranchName(upstream_branch) | |
3144 if settings.GetIsSkipDependencyUpload(local_branch): | |
3145 print | |
3146 print ('Skipping dependency patchset upload because git config ' | |
3147 'branch.%s.skip-deps-uploads is set to True.' % local_branch) | |
3148 print | |
3149 else: | |
3150 auth_config = auth.extract_auth_config_from_options(options) | |
3151 branch_cl = Changelist(branchref=local_branch, auth_config=auth_config) | |
3152 branch_cl_issue_url = branch_cl.GetIssueURL() | |
3153 branch_cl_issue = branch_cl.GetIssue() | |
3154 branch_cl_patchset = branch_cl.GetPatchset() | |
3155 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset: | |
3156 upload_args.extend( | |
3157 ['--depends_on_patchset', '%s:%s' % ( | |
3158 branch_cl_issue, branch_cl_patchset)]) | |
3159 print | |
3160 print ('The current branch (%s) is tracking a local branch (%s) with ' | |
3161 'an associated CL.') % (cl.GetBranch(), local_branch) | |
3162 print 'Adding %s/#ps%s as a dependency patchset.' % ( | |
3163 branch_cl_issue_url, branch_cl_patchset) | |
3164 print | |
3165 | |
3166 project = settings.GetProject() | |
3167 if project: | |
3168 upload_args.extend(['--project', project]) | |
3169 | |
3170 if options.cq_dry_run: | |
3171 upload_args.extend(['--cq_dry_run']) | |
3172 | |
3173 try: | |
3174 upload_args = ['upload'] + upload_args + args | |
3175 logging.info('upload.RealMain(%s)', upload_args) | |
3176 issue, patchset = upload.RealMain(upload_args) | |
3177 issue = int(issue) | |
3178 patchset = int(patchset) | |
3179 except KeyboardInterrupt: | |
3180 sys.exit(1) | |
3181 except: | |
3182 # If we got an exception after the user typed a description for their | |
3183 # change, back up the description before re-raising. | |
3184 if change_desc: | |
3185 backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE) | |
3186 print '\nGot exception while uploading -- saving description to %s\n' \ | |
3187 % backup_path | |
3188 backup_file = open(backup_path, 'w') | |
3189 backup_file.write(change_desc.description) | |
3190 backup_file.close() | |
3191 raise | |
3192 | |
3193 if not cl.GetIssue(): | |
3194 cl.SetIssue(issue) | |
3195 cl.SetPatchset(patchset) | |
3196 | |
3197 if options.use_commit_queue: | |
3198 cl.SetFlag('commit', '1') | |
3199 return 0 | |
3200 | |
3201 | |
3202 def cleanup_list(l): | 3207 def cleanup_list(l): |
3203 """Fixes a list so that comma separated items are put as individual items. | 3208 """Fixes a list so that comma separated items are put as individual items. |
3204 | 3209 |
3205 So that "--reviewers joe@c,john@c --reviewers joa@c" results in | 3210 So that "--reviewers joe@c,john@c --reviewers joa@c" results in |
3206 options.reviewers == sorted(['joe@c', 'john@c', 'joa@c']). | 3211 options.reviewers == sorted(['joe@c', 'john@c', 'joa@c']). |
3207 """ | 3212 """ |
3208 items = sum((i.split(',') for i in l), []) | 3213 items = sum((i.split(',') for i in l), []) |
3209 stripped_items = (i.strip() for i in items) | 3214 stripped_items = (i.strip() for i in items) |
3210 return sorted(filter(None, stripped_items)) | 3215 return sorted(filter(None, stripped_items)) |
3211 | 3216 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3342 ask_for_data('About to upload; enter to confirm.') | 3347 ask_for_data('About to upload; enter to confirm.') |
3343 | 3348 |
3344 print_stats(options.similarity, options.find_copies, args) | 3349 print_stats(options.similarity, options.find_copies, args) |
3345 if cl.IsGerrit(): | 3350 if cl.IsGerrit(): |
3346 if options.squash and options.no_squash: | 3351 if options.squash and options.no_squash: |
3347 DieWithError('Can only use one of --squash or --no-squash') | 3352 DieWithError('Can only use one of --squash or --no-squash') |
3348 | 3353 |
3349 options.squash = ((settings.GetSquashGerritUploads() or options.squash) and | 3354 options.squash = ((settings.GetSquashGerritUploads() or options.squash) and |
3350 not options.no_squash) | 3355 not options.no_squash) |
3351 | 3356 |
3352 ret = GerritUpload(options, args, cl, change) | 3357 ret = cl.CMDUploadChange(options, args, change) |
3353 else: | |
3354 ret = RietveldUpload(options, args, cl, change) | |
3355 if not ret: | 3358 if not ret: |
3356 git_set_branch_value('last-upload-hash', | 3359 git_set_branch_value('last-upload-hash', |
3357 RunGit(['rev-parse', 'HEAD']).strip()) | 3360 RunGit(['rev-parse', 'HEAD']).strip()) |
3358 # Run post upload hooks, if specified. | 3361 # Run post upload hooks, if specified. |
3359 if settings.GetRunPostUploadHook(): | 3362 if settings.GetRunPostUploadHook(): |
3360 presubmit_support.DoPostUploadExecuter( | 3363 presubmit_support.DoPostUploadExecuter( |
3361 change, | 3364 change, |
3362 cl, | 3365 cl, |
3363 settings.GetRoot(), | 3366 settings.GetRoot(), |
3364 options.verbose, | 3367 options.verbose, |
(...skipping 1269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4634 if __name__ == '__main__': | 4637 if __name__ == '__main__': |
4635 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4638 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4636 # unit testing. | 4639 # unit testing. |
4637 fix_encoding.fix_encoding() | 4640 fix_encoding.fix_encoding() |
4638 setup_color.init() | 4641 setup_color.init() |
4639 try: | 4642 try: |
4640 sys.exit(main(sys.argv[1:])) | 4643 sys.exit(main(sys.argv[1:])) |
4641 except KeyboardInterrupt: | 4644 except KeyboardInterrupt: |
4642 sys.stderr.write('interrupted\n') | 4645 sys.stderr.write('interrupted\n') |
4643 sys.exit(1) | 4646 sys.exit(1) |
OLD | NEW |