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

Side by Side Diff: git_cl.py

Issue 2445543002: Make git cl patch work with binary files. (Closed)
Patch Set: address review comments Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « 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 and Gerrit.""" 8 """A git-command for integrating reviews on Rietveld and Gerrit."""
9 9
10 from __future__ import print_function 10 from __future__ import print_function
(...skipping 22 matching lines...) Expand all
33 33
34 try: 34 try:
35 import readline # pylint: disable=F0401,W0611 35 import readline # pylint: disable=F0401,W0611
36 except ImportError: 36 except ImportError:
37 pass 37 pass
38 38
39 from third_party import colorama 39 from third_party import colorama
40 from third_party import httplib2 40 from third_party import httplib2
41 from third_party import upload 41 from third_party import upload
42 import auth 42 import auth
43 import checkout
43 import clang_format 44 import clang_format
44 import commit_queue 45 import commit_queue
45 import dart_format 46 import dart_format
46 import setup_color 47 import setup_color
47 import fix_encoding 48 import fix_encoding
48 import gclient_utils 49 import gclient_utils
49 import gerrit_util 50 import gerrit_util
50 import git_cache 51 import git_cache
51 import git_common 52 import git_common
52 import git_footers 53 import git_footers
(...skipping 930 matching lines...) Expand 10 before | Expand all | Expand 10 after
983 def __init__(self, issue=None, patchset=None, hostname=None): 984 def __init__(self, issue=None, patchset=None, hostname=None):
984 self.issue = issue 985 self.issue = issue
985 self.patchset = patchset 986 self.patchset = patchset
986 self.hostname = hostname 987 self.hostname = hostname
987 988
988 @property 989 @property
989 def valid(self): 990 def valid(self):
990 return self.issue is not None 991 return self.issue is not None
991 992
992 993
993 class _RietveldParsedIssueNumberArgument(_ParsedIssueNumberArgument):
994 def __init__(self, *args, **kwargs):
995 self.patch_url = kwargs.pop('patch_url', None)
996 super(_RietveldParsedIssueNumberArgument, self).__init__(*args, **kwargs)
997
998
999 def ParseIssueNumberArgument(arg): 994 def ParseIssueNumberArgument(arg):
1000 """Parses the issue argument and returns _ParsedIssueNumberArgument.""" 995 """Parses the issue argument and returns _ParsedIssueNumberArgument."""
1001 fail_result = _ParsedIssueNumberArgument() 996 fail_result = _ParsedIssueNumberArgument()
1002 997
1003 if arg.isdigit(): 998 if arg.isdigit():
1004 return _ParsedIssueNumberArgument(issue=int(arg)) 999 return _ParsedIssueNumberArgument(issue=int(arg))
1005 if not arg.startswith('http'): 1000 if not arg.startswith('http'):
1006 return fail_result 1001 return fail_result
1007 url = gclient_utils.UpgradeToHttps(arg) 1002 url = gclient_utils.UpgradeToHttps(arg)
1008 try: 1003 try:
(...skipping 796 matching lines...) Expand 10 before | Expand all | Expand 10 after
1805 DieWithError( 1800 DieWithError(
1806 '\nFailed to fetch issue description. HTTP error %d' % e.code) 1801 '\nFailed to fetch issue description. HTTP error %d' % e.code)
1807 except urllib2.URLError as e: 1802 except urllib2.URLError as e:
1808 print('Warning: Failed to retrieve CL description due to network ' 1803 print('Warning: Failed to retrieve CL description due to network '
1809 'failure.', file=sys.stderr) 1804 'failure.', file=sys.stderr)
1810 return '' 1805 return ''
1811 1806
1812 def GetMostRecentPatchset(self): 1807 def GetMostRecentPatchset(self):
1813 return self.GetIssueProperties()['patchsets'][-1] 1808 return self.GetIssueProperties()['patchsets'][-1]
1814 1809
1815 def GetPatchSetDiff(self, issue, patchset):
1816 return self.RpcServer().get(
1817 '/download/issue%s_%s.diff' % (issue, patchset))
1818
1819 def GetIssueProperties(self): 1810 def GetIssueProperties(self):
1820 if self._props is None: 1811 if self._props is None:
1821 issue = self.GetIssue() 1812 issue = self.GetIssue()
1822 if not issue: 1813 if not issue:
1823 self._props = {} 1814 self._props = {}
1824 else: 1815 else:
1825 self._props = self.RpcServer().get_issue_properties(issue, True) 1816 self._props = self.RpcServer().get_issue_properties(issue, True)
1826 return self._props 1817 return self._props
1827 1818
1828 def CannotTriggerTryJobReason(self): 1819 def CannotTriggerTryJobReason(self):
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
1965 self.SetFlags({'commit': '1', 'cq_dry_run': '0'}) 1956 self.SetFlags({'commit': '1', 'cq_dry_run': '0'})
1966 elif new_state == _CQState.NONE: 1957 elif new_state == _CQState.NONE:
1967 self.SetFlags({'commit': '0', 'cq_dry_run': '0'}) 1958 self.SetFlags({'commit': '0', 'cq_dry_run': '0'})
1968 else: 1959 else:
1969 assert new_state == _CQState.DRY_RUN 1960 assert new_state == _CQState.DRY_RUN
1970 self.SetFlags({'commit': '1', 'cq_dry_run': '1'}) 1961 self.SetFlags({'commit': '1', 'cq_dry_run': '1'})
1971 1962
1972 1963
1973 def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit, 1964 def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit,
1974 directory): 1965 directory):
1975 # TODO(maruel): Use apply_issue.py
1976
1977 # PatchIssue should never be called with a dirty tree. It is up to the 1966 # PatchIssue should never be called with a dirty tree. It is up to the
1978 # caller to check this, but just in case we assert here since the 1967 # caller to check this, but just in case we assert here since the
1979 # consequences of the caller not checking this could be dire. 1968 # consequences of the caller not checking this could be dire.
1980 assert(not git_common.is_dirty_git_tree('apply')) 1969 assert(not git_common.is_dirty_git_tree('apply'))
1981 assert(parsed_issue_arg.valid) 1970 assert(parsed_issue_arg.valid)
1982 self._changelist.issue = parsed_issue_arg.issue 1971 self._changelist.issue = parsed_issue_arg.issue
1983 if parsed_issue_arg.hostname: 1972 if parsed_issue_arg.hostname:
1984 self._rietveld_server = 'https://%s' % parsed_issue_arg.hostname 1973 self._rietveld_server = 'https://%s' % parsed_issue_arg.hostname
1985 1974
1986 if (isinstance(parsed_issue_arg, _RietveldParsedIssueNumberArgument) and 1975 patchset = parsed_issue_arg.patchset or self.GetMostRecentPatchset()
1987 parsed_issue_arg.patch_url): 1976 patchset_object = self.RpcServer().get_patch(self.GetIssue(), patchset)
1988 assert parsed_issue_arg.patchset 1977 scm_obj = checkout.GitCheckout(settings.GetRoot(), None, None, None, None)
1989 patchset = parsed_issue_arg.patchset
1990 patch_data = urllib2.urlopen(parsed_issue_arg.patch_url).read()
1991 else:
1992 patchset = parsed_issue_arg.patchset or self.GetMostRecentPatchset()
1993 patch_data = self.GetPatchSetDiff(self.GetIssue(), patchset)
1994
1995 # Switch up to the top-level directory, if necessary, in preparation for
1996 # applying the patch.
1997 top = settings.GetRelativeRoot()
1998 if top:
1999 os.chdir(top)
2000
2001 # Git patches have a/ at the beginning of source paths. We strip that out
2002 # with a sed script rather than the -p flag to patch so we can feed either
2003 # Git or svn-style patches into the same apply command.
2004 # re.sub() should be used but flags=re.MULTILINE is only in python 2.7.
2005 try: 1978 try:
2006 patch_data = subprocess2.check_output( 1979 scm_obj.apply_patch(patchset_object)
2007 ['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'], stdin=patch_data) 1980 except Exception as e:
2008 except subprocess2.CalledProcessError: 1981 print(str(e))
2009 DieWithError('Git patch mungling failed.')
2010 logging.info(patch_data)
2011
2012 # We use "git apply" to apply the patch instead of "patch" so that we can
2013 # pick up file adds.
2014 # The --index flag means: also insert into the index (so we catch adds).
2015 cmd = ['git', 'apply', '--index', '-p0']
2016 if directory:
2017 cmd.extend(('--directory', directory))
2018 if reject:
2019 cmd.append('--reject')
2020 elif IsGitVersionAtLeast('1.7.12'):
2021 cmd.append('--3way')
2022 try:
2023 subprocess2.check_call(cmd, env=GetNoGitPagerEnv(),
2024 stdin=patch_data, stdout=subprocess2.VOID)
2025 except subprocess2.CalledProcessError:
2026 print('Failed to apply the patch')
2027 return 1 1982 return 1
2028 1983
2029 # If we had an issue, commit the current state and register the issue. 1984 # If we had an issue, commit the current state and register the issue.
2030 if not nocommit: 1985 if not nocommit:
2031 RunGit(['commit', '-m', (self.GetDescription() + '\n\n' + 1986 RunGit(['commit', '-m', (self.GetDescription() + '\n\n' +
2032 'patch from issue %(i)s at patchset ' 1987 'patch from issue %(i)s at patchset '
2033 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' 1988 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
2034 % {'i': self.GetIssue(), 'p': patchset})]) 1989 % {'i': self.GetIssue(), 'p': patchset})])
2035 self.SetIssue(self.GetIssue()) 1990 self.SetIssue(self.GetIssue())
2036 self.SetPatchset(patchset) 1991 self.SetPatchset(patchset)
2037 print('Committed patch locally.') 1992 print('Committed patch locally.')
2038 else: 1993 else:
2039 print('Patch applied to index.') 1994 print('Patch applied to index.')
2040 return 0 1995 return 0
2041 1996
2042 @staticmethod 1997 @staticmethod
2043 def ParseIssueURL(parsed_url): 1998 def ParseIssueURL(parsed_url):
2044 if not parsed_url.scheme or not parsed_url.scheme.startswith('http'): 1999 if not parsed_url.scheme or not parsed_url.scheme.startswith('http'):
2045 return None 2000 return None
2046 # Rietveld patch: https://domain/<number>/#ps<patchset> 2001 # Rietveld patch: https://domain/<number>/#ps<patchset>
2047 match = re.match(r'/(\d+)/$', parsed_url.path) 2002 match = re.match(r'/(\d+)/$', parsed_url.path)
2048 match2 = re.match(r'ps(\d+)$', parsed_url.fragment) 2003 match2 = re.match(r'ps(\d+)$', parsed_url.fragment)
2049 if match and match2: 2004 if match and match2:
2050 return _RietveldParsedIssueNumberArgument( 2005 return _ParsedIssueNumberArgument(
2051 issue=int(match.group(1)), 2006 issue=int(match.group(1)),
2052 patchset=int(match2.group(1)), 2007 patchset=int(match2.group(1)),
2053 hostname=parsed_url.netloc) 2008 hostname=parsed_url.netloc)
2054 # Typical url: https://domain/<issue_number>[/[other]] 2009 # Typical url: https://domain/<issue_number>[/[other]]
2055 match = re.match('/(\d+)(/.*)?$', parsed_url.path) 2010 match = re.match('/(\d+)(/.*)?$', parsed_url.path)
2056 if match: 2011 if match:
2057 return _RietveldParsedIssueNumberArgument( 2012 return _ParsedIssueNumberArgument(
2058 issue=int(match.group(1)), 2013 issue=int(match.group(1)),
2059 hostname=parsed_url.netloc) 2014 hostname=parsed_url.netloc)
2060 # Rietveld patch: https://domain/download/issue<number>_<patchset>.diff 2015 # Rietveld patch: https://domain/download/issue<number>_<patchset>.diff
2061 match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path) 2016 match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path)
2062 if match: 2017 if match:
2063 return _RietveldParsedIssueNumberArgument( 2018 return _ParsedIssueNumberArgument(
2064 issue=int(match.group(1)), 2019 issue=int(match.group(1)),
2065 patchset=int(match.group(2)), 2020 patchset=int(match.group(2)),
2066 hostname=parsed_url.netloc, 2021 hostname=parsed_url.netloc)
2067 patch_url=gclient_utils.UpgradeToHttps(parsed_url.geturl()))
2068 return None 2022 return None
2069 2023
2070 def CMDUploadChange(self, options, args, change): 2024 def CMDUploadChange(self, options, args, change):
2071 """Upload the patch to Rietveld.""" 2025 """Upload the patch to Rietveld."""
2072 upload_args = ['--assume_yes'] # Don't ask about untracked files. 2026 upload_args = ['--assume_yes'] # Don't ask about untracked files.
2073 upload_args.extend(['--server', self.GetCodereviewServer()]) 2027 upload_args.extend(['--server', self.GetCodereviewServer()])
2074 upload_args.extend(auth.auth_config_to_command_options(self._auth_config)) 2028 upload_args.extend(auth.auth_config_to_command_options(self._auth_config))
2075 if options.emulate_svn_auto_props: 2029 if options.emulate_svn_auto_props:
2076 upload_args.append('--emulate_svn_auto_props') 2030 upload_args.append('--emulate_svn_auto_props')
2077 2031
(...skipping 3336 matching lines...) Expand 10 before | Expand all | Expand 10 after
5414 if __name__ == '__main__': 5368 if __name__ == '__main__':
5415 # These affect sys.stdout so do it outside of main() to simplify mocks in 5369 # These affect sys.stdout so do it outside of main() to simplify mocks in
5416 # unit testing. 5370 # unit testing.
5417 fix_encoding.fix_encoding() 5371 fix_encoding.fix_encoding()
5418 setup_color.init() 5372 setup_color.init()
5419 try: 5373 try:
5420 sys.exit(main(sys.argv[1:])) 5374 sys.exit(main(sys.argv[1:]))
5421 except KeyboardInterrupt: 5375 except KeyboardInterrupt:
5422 sys.stderr.write('interrupted\n') 5376 sys.stderr.write('interrupted\n')
5423 sys.exit(1) 5377 sys.exit(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