Chromium Code Reviews

Side by Side Diff: git_cl.py

Issue 9264065: Add minimal Gerrit support to 'git cl config' and 'git cl upload' (Closed) Base URL: svn://chrome-svn/chrome/trunk/tools/depot_tools/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld.""" 8 """A git-command for integrating reviews on Rietveld."""
9 9
10 import logging 10 import logging
(...skipping 129 matching lines...)
140 def __init__(self): 140 def __init__(self):
141 self.default_server = None 141 self.default_server = None
142 self.cc = None 142 self.cc = None
143 self.root = None 143 self.root = None
144 self.is_git_svn = None 144 self.is_git_svn = None
145 self.svn_branch = None 145 self.svn_branch = None
146 self.tree_status_url = None 146 self.tree_status_url = None
147 self.viewvc_url = None 147 self.viewvc_url = None
148 self.updated = False 148 self.updated = False
149 self.did_migrate_check = False 149 self.did_migrate_check = False
150 self.is_gerrit = None
150 151
151 def LazyUpdateIfNeeded(self): 152 def LazyUpdateIfNeeded(self):
152 """Updates the settings from a codereview.settings file, if available.""" 153 """Updates the settings from a codereview.settings file, if available."""
153 if not self.updated: 154 if not self.updated:
154 cr_settings_file = FindCodereviewSettingsFile() 155 cr_settings_file = FindCodereviewSettingsFile()
155 if cr_settings_file: 156 if cr_settings_file:
156 LoadCodereviewSettingsFromFile(cr_settings_file) 157 LoadCodereviewSettingsFromFile(cr_settings_file)
157 self.updated = True 158 self.updated = True
158 159
159 def GetDefaultServerUrl(self, error_ok=False): 160 def GetDefaultServerUrl(self, error_ok=False):
(...skipping 97 matching lines...)
257 258
258 def GetViewVCUrl(self): 259 def GetViewVCUrl(self):
259 if not self.viewvc_url: 260 if not self.viewvc_url:
260 self.viewvc_url = gclient_utils.UpgradeToHttps( 261 self.viewvc_url = gclient_utils.UpgradeToHttps(
261 self._GetConfig('rietveld.viewvc-url', error_ok=True)) 262 self._GetConfig('rietveld.viewvc-url', error_ok=True))
262 return self.viewvc_url 263 return self.viewvc_url
263 264
264 def GetDefaultCCList(self): 265 def GetDefaultCCList(self):
265 return self._GetConfig('rietveld.cc', error_ok=True) 266 return self._GetConfig('rietveld.cc', error_ok=True)
266 267
268 def GetIsGerrit(self):
269 """Return true if this repo is assosiated with gerrit code review system."""
270 if self.is_gerrit is None:
271 self.is_gerrit = self._GetConfig('gerrit.host', error_ok=True)
272 return self.is_gerrit
273
267 def _GetConfig(self, param, **kwargs): 274 def _GetConfig(self, param, **kwargs):
268 self.LazyUpdateIfNeeded() 275 self.LazyUpdateIfNeeded()
269 return RunGit(['config', param], **kwargs).strip() 276 return RunGit(['config', param], **kwargs).strip()
270 277
271 278
272 def CheckForMigration(): 279 def CheckForMigration():
273 """Migrate from the old issue format, if found. 280 """Migrate from the old issue format, if found.
274 281
275 We used to store the branch<->issue mapping in a file in .git, but it's 282 We used to store the branch<->issue mapping in a file in .git, but it's
276 better to store it in the .git/config, since deleting a branch deletes that 283 better to store it in the .git/config, since deleting a branch deletes that
(...skipping 320 matching lines...)
597 """Return the git setting that stores this change's most recent patchset.""" 604 """Return the git setting that stores this change's most recent patchset."""
598 return 'branch.%s.rietveldpatchset' % self.GetBranch() 605 return 'branch.%s.rietveldpatchset' % self.GetBranch()
599 606
600 def _RietveldServer(self): 607 def _RietveldServer(self):
601 """Returns the git setting that stores this change's rietveld server.""" 608 """Returns the git setting that stores this change's rietveld server."""
602 return 'branch.%s.rietveldserver' % self.GetBranch() 609 return 'branch.%s.rietveldserver' % self.GetBranch()
603 610
604 611
605 def GetCodereviewSettingsInteractively(): 612 def GetCodereviewSettingsInteractively():
606 """Prompt the user for settings.""" 613 """Prompt the user for settings."""
614 # TODO(ukai): ask code review system is rietveld or gerrit?
607 server = settings.GetDefaultServerUrl(error_ok=True) 615 server = settings.GetDefaultServerUrl(error_ok=True)
608 prompt = 'Rietveld server (host[:port])' 616 prompt = 'Rietveld server (host[:port])'
609 prompt += ' [%s]' % (server or DEFAULT_SERVER) 617 prompt += ' [%s]' % (server or DEFAULT_SERVER)
610 newserver = ask_for_data(prompt + ':') 618 newserver = ask_for_data(prompt + ':')
611 if not server and not newserver: 619 if not server and not newserver:
612 newserver = DEFAULT_SERVER 620 newserver = DEFAULT_SERVER
613 if newserver: 621 if newserver:
614 newserver = gclient_utils.UpgradeToHttps(newserver) 622 newserver = gclient_utils.UpgradeToHttps(newserver)
615 if newserver != server: 623 if newserver != server:
616 RunGit(['config', 'rietveld.server', newserver]) 624 RunGit(['config', 'rietveld.server', newserver])
(...skipping 99 matching lines...)
716 else: 724 else:
717 RunGit(['config', '--unset-all', fullname], error_ok=unset_error_ok) 725 RunGit(['config', '--unset-all', fullname], error_ok=unset_error_ok)
718 726
719 SetProperty('server', 'CODE_REVIEW_SERVER') 727 SetProperty('server', 'CODE_REVIEW_SERVER')
720 # Only server setting is required. Other settings can be absent. 728 # Only server setting is required. Other settings can be absent.
721 # In that case, we ignore errors raised during option deletion attempt. 729 # In that case, we ignore errors raised during option deletion attempt.
722 SetProperty('cc', 'CC_LIST', unset_error_ok=True) 730 SetProperty('cc', 'CC_LIST', unset_error_ok=True)
723 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True) 731 SetProperty('tree-status-url', 'STATUS', unset_error_ok=True)
724 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True) 732 SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True)
725 733
734 if 'GERRIT_HOST' in keyvals and 'GERRIT_PORT' in keyvals:
735 RunGit(['config', 'gerrit.host', keyvals['GERRIT_HOST']])
736 RunGit(['config', 'gerrit.port', keyvals['GERRIT_PORT']])
737 # Install the standard commit-msg hook.
738 RunCommand(['scp', '-p', '-P', keyvals['GERRIT_PORT'],
739 '%s:hooks/commit-msg' % keyvals['GERRIT_HOST'],
740 os.path.join(settings.GetRoot(),
741 '.git', 'hooks', 'commit-msg')])
742
726 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals: 743 if 'PUSH_URL_CONFIG' in keyvals and 'ORIGIN_URL_CONFIG' in keyvals:
727 #should be of the form 744 #should be of the form
728 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof 745 #PUSH_URL_CONFIG: url.ssh://gitrw.chromium.org.pushinsteadof
729 #ORIGIN_URL_CONFIG: http://src.chromium.org/git 746 #ORIGIN_URL_CONFIG: http://src.chromium.org/git
730 RunGit(['config', keyvals['PUSH_URL_CONFIG'], 747 RunGit(['config', keyvals['PUSH_URL_CONFIG'],
731 keyvals['ORIGIN_URL_CONFIG']]) 748 keyvals['ORIGIN_URL_CONFIG']])
732 749
733 750
734 @usage('[repo root containing codereview.settings]') 751 @usage('[repo root containing codereview.settings]')
735 def CMDconfig(parser, args): 752 def CMDconfig(parser, args):
(...skipping 120 matching lines...)
856 else: 873 else:
857 # Default to diffing against the "upstream" branch. 874 # Default to diffing against the "upstream" branch.
858 base_branch = cl.GetUpstreamBranch() 875 base_branch = cl.GetUpstreamBranch()
859 876
860 cl.RunHook(committing=not options.upload, upstream_branch=base_branch, 877 cl.RunHook(committing=not options.upload, upstream_branch=base_branch,
861 may_prompt=False, verbose=options.verbose, 878 may_prompt=False, verbose=options.verbose,
862 author=None) 879 author=None)
863 return 0 880 return 0
864 881
865 882
883 def GerritUpload(options, args, cl):
884 """upload the current branch to gerrit."""
885 # We assume the remote called "origin" is the one we want.
886 # It is probably not worthwhile to support different workflows.
M-A Ruel 2012/02/01 15:50:35 What about CL pipelining? Why not just get the bra
ukai 2012/02/02 04:21:59 I'm new to gerrit and don't know how to do it yet.
887 remote = 'origin'
888 branch = 'master'
889 if args:
890 branch = args[0]
891
892 description = RunCommand(['git', 'log', '--pretty=format:%s%n%n%b',
893 '%s...' % (branch)]).strip()
894
895 receive_options = []
896 cc = []
897 if options.cc:
898 cc = options.cc.split(',')
899 cc = filter(None, (cl.GetCCList(), cc))
900 if cc:
901 receive_options += ['--cc=' + email for email in cc]
902 # Retrieves all reviewer lines
903 reviewer_regexp = re.compile(r'^\s*(TBR|R)=(.+)$', re.MULTILINE)
M-A Ruel 2012/02/01 15:50:35 Why can't you reuse the parsing code? It'd be nice
ukai 2012/02/02 04:21:59 Done.
904 reviewers = filter(None, ','.join(
905 i.group(2).strip()
906 for i in reviewer_regexp.finditer(description)).split(','))
907 if options.reviewers:
908 reviewers += filter(None, options.reviewers.split(','))
909 if reviewers:
910 receive_options += ['--reviewer=' + email for email in reviewers]
911
912 git_command = ['push']
913 if receive_options:
914 git_command.append('--receive-pack=git receive-pack ' +
915 ' '.join(receive_options))
916 git_command += [remote, 'HEAD:refs/for/' + branch]
917 RunGit(git_command)
918 return 0
919
920
866 @usage('[args to "git diff"]') 921 @usage('[args to "git diff"]')
867 def CMDupload(parser, args): 922 def CMDupload(parser, args):
868 """upload the current changelist to codereview""" 923 """upload the current changelist to codereview"""
869 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 924 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
870 help='bypass upload presubmit hook') 925 help='bypass upload presubmit hook')
871 parser.add_option('-f', action='store_true', dest='force', 926 parser.add_option('-f', action='store_true', dest='force',
872 help="force yes to questions (don't prompt)") 927 help="force yes to questions (don't prompt)")
873 parser.add_option('-m', dest='message', help='message for patch') 928 parser.add_option('-m', dest='message', help='message for patch')
874 parser.add_option('-r', '--reviewers', 929 parser.add_option('-r', '--reviewers',
875 help='reviewer email addresses') 930 help='reviewer email addresses')
(...skipping 13 matching lines...)
889 (options, args) = parser.parse_args(args) 944 (options, args) = parser.parse_args(args)
890 945
891 # Make sure index is up-to-date before running diff-index. 946 # Make sure index is up-to-date before running diff-index.
892 RunGit(['update-index', '--refresh', '-q'], error_ok=True) 947 RunGit(['update-index', '--refresh', '-q'], error_ok=True)
893 if RunGit(['diff-index', 'HEAD']): 948 if RunGit(['diff-index', 'HEAD']):
894 print 'Cannot upload with a dirty tree. You must commit locally first.' 949 print 'Cannot upload with a dirty tree. You must commit locally first.'
895 return 1 950 return 1
896 951
897 cl = Changelist() 952 cl = Changelist()
898 if args: 953 if args:
954 # TODO(ukai): is it ok for gerrit case?
899 base_branch = args[0] 955 base_branch = args[0]
900 else: 956 else:
901 # Default to diffing against the "upstream" branch. 957 # Default to diffing against the "upstream" branch.
902 base_branch = cl.GetUpstreamBranch() 958 base_branch = cl.GetUpstreamBranch()
903 args = [base_branch + "..."] 959 if settings.GetIsGerrit():
960 args = ['master']
M-A Ruel 2012/02/01 15:50:35 Why?
ukai 2012/02/02 04:21:59 I initially thought args[0] to be used for target
961 else:
962 args = [base_branch + "..."]
904 963
905 if not options.bypass_hooks: 964 if not options.bypass_hooks:
906 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch, 965 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch,
907 may_prompt=not options.force, 966 may_prompt=not options.force,
908 verbose=options.verbose, 967 verbose=options.verbose,
909 author=None) 968 author=None)
910 if not hook_results.should_continue(): 969 if not hook_results.should_continue():
911 return 1 970 return 1
912 if not options.reviewers and hook_results.reviewers: 971 if not options.reviewers and hook_results.reviewers:
913 options.reviewers = hook_results.reviewers 972 options.reviewers = hook_results.reviewers
914 973
915 # --no-ext-diff is broken in some versions of Git, so try to work around 974 # --no-ext-diff is broken in some versions of Git, so try to work around
916 # this by overriding the environment (but there is still a problem if the 975 # this by overriding the environment (but there is still a problem if the
917 # git config key "diff.external" is used). 976 # git config key "diff.external" is used).
918 env = os.environ.copy() 977 env = os.environ.copy()
919 if 'GIT_EXTERNAL_DIFF' in env: 978 if 'GIT_EXTERNAL_DIFF' in env:
920 del env['GIT_EXTERNAL_DIFF'] 979 del env['GIT_EXTERNAL_DIFF']
921 subprocess2.call( 980 subprocess2.call(
922 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env) 981 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
923 982
983 if settings.GetIsGerrit():
984 GerritUpload(options, args, cl)
985 # TODO(ukai): parse Change-Id: and set issue number?
986 return 0
987
924 upload_args = ['--assume_yes'] # Don't ask about untracked files. 988 upload_args = ['--assume_yes'] # Don't ask about untracked files.
M-A Ruel 2012/02/01 15:50:35 I'd rather move the rest into RietveldUpload to ma
ukai 2012/02/02 04:21:59 Done.
925 upload_args.extend(['--server', cl.GetRietveldServer()]) 989 upload_args.extend(['--server', cl.GetRietveldServer()])
926 if options.emulate_svn_auto_props: 990 if options.emulate_svn_auto_props:
927 upload_args.append('--emulate_svn_auto_props') 991 upload_args.append('--emulate_svn_auto_props')
928 if options.from_logs and not options.message: 992 if options.from_logs and not options.message:
929 print 'Must set message for subject line if using desc_from_logs' 993 print 'Must set message for subject line if using desc_from_logs'
930 return 1 994 return 1
931 995
932 change_desc = None 996 change_desc = None
933 997
934 if cl.GetIssue(): 998 if cl.GetIssue():
(...skipping 287 matching lines...)
1222 help='allow failed patches and spew .rej files') 1286 help='allow failed patches and spew .rej files')
1223 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', 1287 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit',
1224 help="don't commit after patch applies") 1288 help="don't commit after patch applies")
1225 (options, args) = parser.parse_args(args) 1289 (options, args) = parser.parse_args(args)
1226 if len(args) != 1: 1290 if len(args) != 1:
1227 parser.print_help() 1291 parser.print_help()
1228 return 1 1292 return 1
1229 issue_arg = args[0] 1293 issue_arg = args[0]
1230 1294
1231 # TODO(maruel): Use apply_issue.py 1295 # TODO(maruel): Use apply_issue.py
1296 # TODO(ukai): use gerrit-cherry-pick for gerrit repository?
1232 1297
1233 if re.match(r'\d+', issue_arg): 1298 if re.match(r'\d+', issue_arg):
1234 # Input is an issue id. Figure out the URL. 1299 # Input is an issue id. Figure out the URL.
1235 issue = issue_arg 1300 issue = issue_arg
1236 patch_data = Changelist().GetPatchSetDiff(issue) 1301 patch_data = Changelist().GetPatchSetDiff(issue)
1237 else: 1302 else:
1238 # Assume it's a URL to the patch. Default to https. 1303 # Assume it's a URL to the patch. Default to https.
1239 issue_url = gclient_utils.UpgradeToHttps(issue_arg) 1304 issue_url = gclient_utils.UpgradeToHttps(issue_arg)
1240 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url) 1305 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url)
1241 if not match: 1306 if not match:
(...skipping 186 matching lines...)
1428 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) 1493 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)))
1429 1494
1430 # Not a known command. Default to help. 1495 # Not a known command. Default to help.
1431 GenUsage(parser, 'help') 1496 GenUsage(parser, 'help')
1432 return CMDhelp(parser, argv) 1497 return CMDhelp(parser, argv)
1433 1498
1434 1499
1435 if __name__ == '__main__': 1500 if __name__ == '__main__':
1436 fix_encoding.fix_encoding() 1501 fix_encoding.fix_encoding()
1437 sys.exit(main(sys.argv[1:])) 1502 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine