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 860 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
871 return tmp | 871 return tmp |
872 return fail_result | 872 return fail_result |
873 | 873 |
874 | 874 |
875 class Changelist(object): | 875 class Changelist(object): |
876 """Changelist works with one changelist in local branch. | 876 """Changelist works with one changelist in local branch. |
877 | 877 |
878 Supports two codereview backends: Rietveld or Gerrit, selected at object | 878 Supports two codereview backends: Rietveld or Gerrit, selected at object |
879 creation. | 879 creation. |
880 | 880 |
881 Not safe for concurrent multi-{thread,process} use. | 881 Notes: |
| 882 * Not safe for concurrent multi-{thread,process} use. |
| 883 * Caches values from current branch. Therefore, re-use after branch change |
| 884 with care. |
882 """ | 885 """ |
883 | 886 |
884 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): | 887 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): |
885 """Create a new ChangeList instance. | 888 """Create a new ChangeList instance. |
886 | 889 |
887 If issue is given, the codereview must be given too. | 890 If issue is given, the codereview must be given too. |
888 | 891 |
889 If `codereview` is given, it must be 'rietveld' or 'gerrit'. | 892 If `codereview` is given, it must be 'rietveld' or 'gerrit'. |
890 Otherwise, it's decided based on current configuration of the local branch, | 893 Otherwise, it's decided based on current configuration of the local branch, |
891 with default being 'rietveld' for backwards compatibility. | 894 with default being 'rietveld' for backwards compatibility. |
(...skipping 1463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2355 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, | 2358 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, |
2356 '-m', message]).strip() | 2359 '-m', message]).strip() |
2357 else: | 2360 else: |
2358 change_desc = ChangeDescription( | 2361 change_desc = ChangeDescription( |
2359 options.message or CreateDescriptionFromLog(args)) | 2362 options.message or CreateDescriptionFromLog(args)) |
2360 if not change_desc.description: | 2363 if not change_desc.description: |
2361 DieWithError("Description is empty. Aborting...") | 2364 DieWithError("Description is empty. Aborting...") |
2362 | 2365 |
2363 if not git_footers.get_footer_change_id(change_desc.description): | 2366 if not git_footers.get_footer_change_id(change_desc.description): |
2364 DownloadGerritHook(False) | 2367 DownloadGerritHook(False) |
2365 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) | 2368 change_desc.set_description(self._AddChangeIdToCommitMessage(options, |
| 2369 args)) |
2366 ref_to_push = 'HEAD' | 2370 ref_to_push = 'HEAD' |
2367 parent = '%s/%s' % (gerrit_remote, branch) | 2371 parent = '%s/%s' % (gerrit_remote, branch) |
2368 change_id = git_footers.get_footer_change_id(change_desc.description)[0] | 2372 change_id = git_footers.get_footer_change_id(change_desc.description)[0] |
2369 | 2373 |
2370 assert change_desc | 2374 assert change_desc |
2371 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, | 2375 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, |
2372 ref_to_push)]).splitlines() | 2376 ref_to_push)]).splitlines() |
2373 if len(commits) > 1: | 2377 if len(commits) > 1: |
2374 print('WARNING: This will upload %d commits. Run the following command ' | 2378 print('WARNING: This will upload %d commits. Run the following command ' |
2375 'to see which commits will be uploaded: ' % len(commits)) | 2379 'to see which commits will be uploaded: ' % len(commits)) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2425 if m] | 2429 if m] |
2426 if len(change_numbers) != 1: | 2430 if len(change_numbers) != 1: |
2427 DieWithError( | 2431 DieWithError( |
2428 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' | 2432 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' |
2429 'Change-Id: %s') % (len(change_numbers), change_id)) | 2433 'Change-Id: %s') % (len(change_numbers), change_id)) |
2430 self.SetIssue(change_numbers[0]) | 2434 self.SetIssue(change_numbers[0]) |
2431 RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(), | 2435 RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(), |
2432 ref_to_push]) | 2436 ref_to_push]) |
2433 return 0 | 2437 return 0 |
2434 | 2438 |
| 2439 def _AddChangeIdToCommitMessage(self, options, args): |
| 2440 """Re-commits using the current message, assumes the commit hook is in |
| 2441 place. |
| 2442 """ |
| 2443 log_desc = options.message or CreateDescriptionFromLog(args) |
| 2444 git_command = ['commit', '--amend', '-m', log_desc] |
| 2445 RunGit(git_command) |
| 2446 new_log_desc = CreateDescriptionFromLog(args) |
| 2447 if git_footers.get_footer_change_id(new_log_desc): |
| 2448 print 'git-cl: Added Change-Id to commit message.' |
| 2449 return new_log_desc |
| 2450 else: |
| 2451 print >> sys.stderr, 'ERROR: Gerrit commit-msg hook not available.' |
2435 | 2452 |
2436 | 2453 |
2437 _CODEREVIEW_IMPLEMENTATIONS = { | 2454 _CODEREVIEW_IMPLEMENTATIONS = { |
2438 'rietveld': _RietveldChangelistImpl, | 2455 'rietveld': _RietveldChangelistImpl, |
2439 'gerrit': _GerritChangelistImpl, | 2456 'gerrit': _GerritChangelistImpl, |
2440 } | 2457 } |
2441 | 2458 |
2442 | 2459 |
2443 class ChangeDescription(object): | 2460 class ChangeDescription(object): |
2444 """Contains a parsed form of the change description.""" | 2461 """Contains a parsed form of the change description.""" |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2723 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url', True) | 2740 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url', True) |
2724 SetProperty(settings.GetBugPrefix(), 'Bug Prefix', 'bug-prefix', False) | 2741 SetProperty(settings.GetBugPrefix(), 'Bug Prefix', 'bug-prefix', False) |
2725 SetProperty(settings.GetRunPostUploadHook(), 'Run Post Upload Hook', | 2742 SetProperty(settings.GetRunPostUploadHook(), 'Run Post Upload Hook', |
2726 'run-post-upload-hook', False) | 2743 'run-post-upload-hook', False) |
2727 | 2744 |
2728 @subcommand.usage('[repo root containing codereview.settings]') | 2745 @subcommand.usage('[repo root containing codereview.settings]') |
2729 def CMDconfig(parser, args): | 2746 def CMDconfig(parser, args): |
2730 """Edits configuration for this tree.""" | 2747 """Edits configuration for this tree.""" |
2731 | 2748 |
2732 print('WARNING: git cl config works for Rietveld only.\n' | 2749 print('WARNING: git cl config works for Rietveld only.\n' |
2733 'For Gerrit, see http://crbug.com/579160.') | 2750 'For Gerrit, see http://crbug.com/603116.') |
2734 # TODO(tandrii): add Gerrit support as part of http://crbug.com/579160. | 2751 # TODO(tandrii): add Gerrit support as part of http://crbug.com/603116. |
2735 parser.add_option('--activate-update', action='store_true', | 2752 parser.add_option('--activate-update', action='store_true', |
2736 help='activate auto-updating [rietveld] section in ' | 2753 help='activate auto-updating [rietveld] section in ' |
2737 '.git/config') | 2754 '.git/config') |
2738 parser.add_option('--deactivate-update', action='store_true', | 2755 parser.add_option('--deactivate-update', action='store_true', |
2739 help='deactivate auto-updating [rietveld] section in ' | 2756 help='deactivate auto-updating [rietveld] section in ' |
2740 '.git/config') | 2757 '.git/config') |
2741 options, args = parser.parse_args(args) | 2758 options, args = parser.parse_args(args) |
2742 | 2759 |
2743 if options.deactivate_update: | 2760 if options.deactivate_update: |
2744 RunGit(['config', 'rietveld.autoupdate', 'false']) | 2761 RunGit(['config', 'rietveld.autoupdate', 'false']) |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3073 continue | 3090 continue |
3074 print 'Branch for issue number %s: %s' % ( | 3091 print 'Branch for issue number %s: %s' % ( |
3075 issue, ', '.join(issue_branch_map.get(int(issue)) or ('None',))) | 3092 issue, ', '.join(issue_branch_map.get(int(issue)) or ('None',))) |
3076 else: | 3093 else: |
3077 cl = Changelist() | 3094 cl = Changelist() |
3078 if len(args) > 0: | 3095 if len(args) > 0: |
3079 try: | 3096 try: |
3080 issue = int(args[0]) | 3097 issue = int(args[0]) |
3081 except ValueError: | 3098 except ValueError: |
3082 DieWithError('Pass a number to set the issue or none to list it.\n' | 3099 DieWithError('Pass a number to set the issue or none to list it.\n' |
3083 'Maybe you want to run git cl status?') | 3100 'Maybe you want to run git cl status?') |
3084 cl.SetIssue(issue) | 3101 cl.SetIssue(issue) |
3085 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 3102 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
3086 return 0 | 3103 return 0 |
3087 | 3104 |
3088 | 3105 |
3089 def CMDcomments(parser, args): | 3106 def CMDcomments(parser, args): |
3090 """Shows or posts review comments for any changelist.""" | 3107 """Shows or posts review comments for any changelist.""" |
3091 parser.add_option('-a', '--add-comment', dest='comment', | 3108 parser.add_option('-a', '--add-comment', dest='comment', |
3092 help='comment to add to an issue') | 3109 help='comment to add to an issue') |
3093 parser.add_option('-i', dest='issue', | 3110 parser.add_option('-i', dest='issue', |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3253 base_branch = cl.GetCommonAncestorWithUpstream() | 3270 base_branch = cl.GetCommonAncestorWithUpstream() |
3254 | 3271 |
3255 cl.RunHook( | 3272 cl.RunHook( |
3256 committing=not options.upload, | 3273 committing=not options.upload, |
3257 may_prompt=False, | 3274 may_prompt=False, |
3258 verbose=options.verbose, | 3275 verbose=options.verbose, |
3259 change=cl.GetChange(base_branch, None)) | 3276 change=cl.GetChange(base_branch, None)) |
3260 return 0 | 3277 return 0 |
3261 | 3278 |
3262 | 3279 |
3263 def AddChangeIdToCommitMessage(options, args): | |
3264 """Re-commits using the current message, assumes the commit hook is in | |
3265 place. | |
3266 """ | |
3267 log_desc = options.message or CreateDescriptionFromLog(args) | |
3268 git_command = ['commit', '--amend', '-m', log_desc] | |
3269 RunGit(git_command) | |
3270 new_log_desc = CreateDescriptionFromLog(args) | |
3271 if git_footers.get_footer_change_id(new_log_desc): | |
3272 print 'git-cl: Added Change-Id to commit message.' | |
3273 return new_log_desc | |
3274 else: | |
3275 print >> sys.stderr, 'ERROR: Gerrit commit-msg hook not available.' | |
3276 | |
3277 | |
3278 def GenerateGerritChangeId(message): | 3280 def GenerateGerritChangeId(message): |
3279 """Returns Ixxxxxx...xxx change id. | 3281 """Returns Ixxxxxx...xxx change id. |
3280 | 3282 |
3281 Works the same way as | 3283 Works the same way as |
3282 https://gerrit-review.googlesource.com/tools/hooks/commit-msg | 3284 https://gerrit-review.googlesource.com/tools/hooks/commit-msg |
3283 but can be called on demand on all platforms. | 3285 but can be called on demand on all platforms. |
3284 | 3286 |
3285 The basic idea is to generate git hash of a state of the tree, original commit | 3287 The basic idea is to generate git hash of a state of the tree, original commit |
3286 message, author/committer info and timestamps. | 3288 message, author/committer info and timestamps. |
3287 """ | 3289 """ |
(...skipping 1418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4706 if __name__ == '__main__': | 4708 if __name__ == '__main__': |
4707 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4709 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4708 # unit testing. | 4710 # unit testing. |
4709 fix_encoding.fix_encoding() | 4711 fix_encoding.fix_encoding() |
4710 setup_color.init() | 4712 setup_color.init() |
4711 try: | 4713 try: |
4712 sys.exit(main(sys.argv[1:])) | 4714 sys.exit(main(sys.argv[1:])) |
4713 except KeyboardInterrupt: | 4715 except KeyboardInterrupt: |
4714 sys.stderr.write('interrupted\n') | 4716 sys.stderr.write('interrupted\n') |
4715 sys.exit(1) | 4717 sys.exit(1) |
OLD | NEW |