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

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. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld.""" 8 """A git-command for integrating reviews on Rietveld."""
9 9
10 import logging 10 import logging
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
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...) Expand 10 before | Expand all | Expand 10 after
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...) Expand 10 before | Expand all | Expand 10 after
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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
659 initial_text += '\nBUG=' 667 initial_text += '\nBUG='
660 if '\nTEST=' not in self.description: 668 if '\nTEST=' not in self.description:
661 initial_text += '\nTEST=' 669 initial_text += '\nTEST='
662 initial_text = initial_text.rstrip('\n') + '\n' 670 initial_text = initial_text.rstrip('\n') + '\n'
663 content = gclient_utils.RunEditor(initial_text, True) 671 content = gclient_utils.RunEditor(initial_text, True)
664 if not content: 672 if not content:
665 DieWithError('Running editor failed') 673 DieWithError('Running editor failed')
666 content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip() 674 content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip()
667 if not content: 675 if not content:
668 DieWithError('No CL description, aborting') 676 DieWithError('No CL description, aborting')
669 self._ParseDescription(content) 677 self.ParseDescription(content)
670 678
671 def _ParseDescription(self, description): 679 def ParseDescription(self, description):
672 """Updates the list of reviewers and subject from the description.""" 680 """Updates the list of reviewers and subject from the description."""
673 if not description: 681 if not description:
674 self.description = description 682 self.description = description
675 return 683 return
676 684
677 self.description = description.strip('\n') + '\n' 685 self.description = description.strip('\n') + '\n'
678 self.subject = description.split('\n', 1)[0] 686 self.subject = description.split('\n', 1)[0]
679 # Retrieves all reviewer lines 687 # Retrieves all reviewer lines
680 regexp = re.compile(r'^\s*(TBR|R)=(.+)$', re.MULTILINE) 688 regexp = re.compile(r'^\s*(TBR|R)=(.+)$', re.MULTILINE)
681 self.reviewers = ','.join( 689 self.reviewers = ','.join(
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
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...) Expand 10 before | Expand all | Expand 10 after
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
866 @usage('[args to "git diff"]') 883 def GerritUpload(options, args, cl):
867 def CMDupload(parser, args): 884 """upload the current branch to gerrit."""
868 """upload the current changelist to codereview""" 885 # We assume the remote called "origin" is the one we want.
869 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 886 # It is probably not worthwhile to support different workflows.
870 help='bypass upload presubmit hook') 887 remote = 'origin'
871 parser.add_option('-f', action='store_true', dest='force', 888 branch = 'master'
872 help="force yes to questions (don't prompt)") 889 if options.target_branch:
873 parser.add_option('-m', dest='message', help='message for patch') 890 branch = options.target_branch
874 parser.add_option('-r', '--reviewers',
875 help='reviewer email addresses')
876 parser.add_option('--cc',
877 help='cc email addresses')
878 parser.add_option('--send-mail', action='store_true',
879 help='send email to reviewer immediately')
880 parser.add_option("--emulate_svn_auto_props", action="store_true",
881 dest="emulate_svn_auto_props",
882 help="Emulate Subversion's auto properties feature.")
883 parser.add_option("--desc_from_logs", action="store_true",
884 dest="from_logs",
885 help="""Squashes git commit logs into change description and
886 uses message as subject""")
887 parser.add_option('-c', '--use-commit-queue', action='store_true',
888 help='tell the commit queue to commit this patchset')
889 (options, args) = parser.parse_args(args)
890 891
891 # Make sure index is up-to-date before running diff-index. 892 log_desc = CreateDescriptionFromLog(args)
892 RunGit(['update-index', '--refresh', '-q'], error_ok=True) 893 if options.reviewers:
893 if RunGit(['diff-index', 'HEAD']): 894 log_desc += '\nR=' + options.reviewers
894 print 'Cannot upload with a dirty tree. You must commit locally first.' 895 change_desc = ChangeDescription(options.message, log_desc,
896 options.reviewers)
897 change_desc.ParseDescription(log_desc)
898 if change_desc.IsEmpty():
899 print "Description is empty; aborting."
895 return 1 900 return 1
896 901
897 cl = Changelist() 902 receive_options = []
898 if args: 903 cc = cl.GetCCList().split(',')
899 base_branch = args[0] 904 if options.cc:
900 else: 905 cc += options.cc.split(',')
901 # Default to diffing against the "upstream" branch. 906 cc = filter(None, cc)
902 base_branch = cl.GetUpstreamBranch() 907 if cc:
903 args = [base_branch + "..."] 908 receive_options += ['--cc=' + email for email in cc]
909 if change_desc.reviewers:
910 reviewers = filter(None, change_desc.reviewers.split(','))
911 if reviewers:
912 receive_options += ['--reviewer=' + email for email in reviewers]
904 913
905 if not options.bypass_hooks: 914 git_command = ['push']
906 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch, 915 if receive_options:
907 may_prompt=not options.force, 916 git_command.append('--receive-pack="git receive-pack %s"' %
908 verbose=options.verbose, 917 ' '.join(receive_options))
909 author=None) 918 git_command += [remote, 'HEAD:refs/for/' + branch]
910 if not hook_results.should_continue(): 919 RunGit(git_command)
911 return 1 920 # TODO(ukai): parse Change-Id: and set issue number?
912 if not options.reviewers and hook_results.reviewers: 921 return 0
913 options.reviewers = hook_results.reviewers
914 922
915 # --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
917 # git config key "diff.external" is used).
918 env = os.environ.copy()
919 if 'GIT_EXTERNAL_DIFF' in env:
920 del env['GIT_EXTERNAL_DIFF']
921 subprocess2.call(
922 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
923 923
924 def RietveldUpload(options, args, cl):
925 """upload the patch to rietveld."""
924 upload_args = ['--assume_yes'] # Don't ask about untracked files. 926 upload_args = ['--assume_yes'] # Don't ask about untracked files.
925 upload_args.extend(['--server', cl.GetRietveldServer()]) 927 upload_args.extend(['--server', cl.GetRietveldServer()])
926 if options.emulate_svn_auto_props: 928 if options.emulate_svn_auto_props:
927 upload_args.append('--emulate_svn_auto_props') 929 upload_args.append('--emulate_svn_auto_props')
928 if options.from_logs and not options.message: 930 if options.from_logs and not options.message:
929 print 'Must set message for subject line if using desc_from_logs' 931 print 'Must set message for subject line if using desc_from_logs'
930 return 1 932 return 1
931 933
932 change_desc = None 934 change_desc = None
933 935
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
995 997
996 if not cl.GetIssue(): 998 if not cl.GetIssue():
997 cl.SetIssue(issue) 999 cl.SetIssue(issue)
998 cl.SetPatchset(patchset) 1000 cl.SetPatchset(patchset)
999 1001
1000 if options.use_commit_queue: 1002 if options.use_commit_queue:
1001 cl.SetFlag('commit', '1') 1003 cl.SetFlag('commit', '1')
1002 return 0 1004 return 0
1003 1005
1004 1006
1007 @usage('[args to "git diff"]')
1008 def CMDupload(parser, args):
1009 """upload the current changelist to codereview"""
1010 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
1011 help='bypass upload presubmit hook')
1012 parser.add_option('-f', action='store_true', dest='force',
1013 help="force yes to questions (don't prompt)")
1014 parser.add_option('-m', dest='message', help='message for patch')
1015 parser.add_option('-r', '--reviewers',
1016 help='reviewer email addresses')
1017 parser.add_option('--cc',
1018 help='cc email addresses')
1019 parser.add_option('--send-mail', action='store_true',
1020 help='send email to reviewer immediately')
1021 parser.add_option("--emulate_svn_auto_props", action="store_true",
1022 dest="emulate_svn_auto_props",
1023 help="Emulate Subversion's auto properties feature.")
1024 parser.add_option("--desc_from_logs", action="store_true",
1025 dest="from_logs",
1026 help="""Squashes git commit logs into change description and
1027 uses message as subject""")
1028 parser.add_option('-c', '--use-commit-queue', action='store_true',
1029 help='tell the commit queue to commit this patchset')
1030 if settings.GetIsGerrit():
1031 parser.add_option('--target_branch', dest='target_branch', default='master',
1032 help='target branch to upload')
1033 (options, args) = parser.parse_args(args)
1034
1035 # Make sure index is up-to-date before running diff-index.
1036 RunGit(['update-index', '--refresh', '-q'], error_ok=True)
1037 if RunGit(['diff-index', 'HEAD']):
1038 print 'Cannot upload with a dirty tree. You must commit locally first.'
1039 return 1
1040
1041 cl = Changelist()
1042 if args:
1043 # TODO(ukai): is it ok for gerrit case?
1044 base_branch = args[0]
1045 else:
1046 # Default to diffing against the "upstream" branch.
1047 base_branch = cl.GetUpstreamBranch()
1048 args = [base_branch + "..."]
1049
1050 if not options.bypass_hooks:
1051 hook_results = cl.RunHook(committing=False, upstream_branch=base_branch,
1052 may_prompt=not options.force,
1053 verbose=options.verbose,
1054 author=None)
1055 if not hook_results.should_continue():
1056 return 1
1057 if not options.reviewers and hook_results.reviewers:
1058 options.reviewers = hook_results.reviewers
1059
1060 # --no-ext-diff is broken in some versions of Git, so try to work around
1061 # this by overriding the environment (but there is still a problem if the
1062 # git config key "diff.external" is used).
1063 env = os.environ.copy()
1064 if 'GIT_EXTERNAL_DIFF' in env:
1065 del env['GIT_EXTERNAL_DIFF']
1066 subprocess2.call(
1067 ['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, env=env)
1068
1069 if settings.GetIsGerrit():
1070 return GerritUpload(options, args, cl)
1071 return RietveldUpload(options, args, cl)
1072
1073
1005 def SendUpstream(parser, args, cmd): 1074 def SendUpstream(parser, args, cmd):
1006 """Common code for CmdPush and CmdDCommit 1075 """Common code for CmdPush and CmdDCommit
1007 1076
1008 Squashed commit into a single. 1077 Squashed commit into a single.
1009 Updates changelog with metadata (e.g. pointer to review). 1078 Updates changelog with metadata (e.g. pointer to review).
1010 Pushes/dcommits the code upstream. 1079 Pushes/dcommits the code upstream.
1011 Updates review and closes. 1080 Updates review and closes.
1012 """ 1081 """
1013 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', 1082 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
1014 help='bypass upload presubmit hook') 1083 help='bypass upload presubmit hook')
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
1222 help='allow failed patches and spew .rej files') 1291 help='allow failed patches and spew .rej files')
1223 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', 1292 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit',
1224 help="don't commit after patch applies") 1293 help="don't commit after patch applies")
1225 (options, args) = parser.parse_args(args) 1294 (options, args) = parser.parse_args(args)
1226 if len(args) != 1: 1295 if len(args) != 1:
1227 parser.print_help() 1296 parser.print_help()
1228 return 1 1297 return 1
1229 issue_arg = args[0] 1298 issue_arg = args[0]
1230 1299
1231 # TODO(maruel): Use apply_issue.py 1300 # TODO(maruel): Use apply_issue.py
1301 # TODO(ukai): use gerrit-cherry-pick for gerrit repository?
1232 1302
1233 if re.match(r'\d+', issue_arg): 1303 if re.match(r'\d+', issue_arg):
1234 # Input is an issue id. Figure out the URL. 1304 # Input is an issue id. Figure out the URL.
1235 issue = issue_arg 1305 issue = issue_arg
1236 patch_data = Changelist().GetPatchSetDiff(issue) 1306 patch_data = Changelist().GetPatchSetDiff(issue)
1237 else: 1307 else:
1238 # Assume it's a URL to the patch. Default to https. 1308 # Assume it's a URL to the patch. Default to https.
1239 issue_url = gclient_utils.UpgradeToHttps(issue_arg) 1309 issue_url = gclient_utils.UpgradeToHttps(issue_arg)
1240 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url) 1310 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url)
1241 if not match: 1311 if not match:
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
1428 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) 1498 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)))
1429 1499
1430 # Not a known command. Default to help. 1500 # Not a known command. Default to help.
1431 GenUsage(parser, 'help') 1501 GenUsage(parser, 'help')
1432 return CMDhelp(parser, argv) 1502 return CMDhelp(parser, argv)
1433 1503
1434 1504
1435 if __name__ == '__main__': 1505 if __name__ == '__main__':
1436 fix_encoding.fix_encoding() 1506 fix_encoding.fix_encoding()
1437 sys.exit(main(sys.argv[1:])) 1507 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698