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 2506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2517 def GerritUpload(options, args, cl, change): | 2517 def GerritUpload(options, args, cl, change): |
2518 """upload the current branch to gerrit.""" | 2518 """upload the current branch to gerrit.""" |
2519 # We assume the remote called "origin" is the one we want. | 2519 # We assume the remote called "origin" is the one we want. |
2520 # It is probably not worthwhile to support different workflows. | 2520 # It is probably not worthwhile to support different workflows. |
2521 gerrit_remote = 'origin' | 2521 gerrit_remote = 'origin' |
2522 | 2522 |
2523 remote, remote_branch = cl.GetRemoteBranch() | 2523 remote, remote_branch = cl.GetRemoteBranch() |
2524 branch = GetTargetRef(remote, remote_branch, options.target_branch, | 2524 branch = GetTargetRef(remote, remote_branch, options.target_branch, |
2525 pending_prefix='') | 2525 pending_prefix='') |
2526 | 2526 |
2527 change_desc = ChangeDescription( | |
2528 options.message or CreateDescriptionFromLog(args)) | |
2529 if not change_desc.description: | |
2530 print "\nDescription is empty. Aborting..." | |
2531 return 1 | |
2532 | |
2533 if options.title: | 2527 if options.title: |
2534 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..." | 2528 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..." |
2535 return 1 | 2529 return 1 |
2536 | 2530 |
2537 if options.squash: | 2531 if options.squash: |
2538 # Try to get the message from a previous upload. | 2532 if cl.GetIssue(): |
2539 shadow_branch = 'refs/heads/git_cl_uploads/' + cl.GetBranch() | 2533 # Try to get the message from a previous upload. |
2540 message = RunGitSilent(['show', '--format=%B', '-s', shadow_branch]) | 2534 message = cl.GetDescription() |
2541 if not message: | 2535 if not message: |
2536 DieWithError( | |
2537 'failed to fetch description from current Gerrit issue %d\n' | |
2538 '%s' % (cl.GetIssue(), cl.GetIssueURL())) | |
2539 # Issues already on Gerrit really must have exactly 1 Change-Id. | |
Bernhard Bauer
2016/03/29 08:22:49
In practice this could fail (e.g. an old CL that w
tandrii(chromium)
2016/03/29 08:37:41
Didn't know it was possible, but let's assume so.
Bernhard Bauer
2016/03/29 09:03:21
If a commit is pushed that doesn't have a Change-I
tandrii(chromium)
2016/03/29 13:06:24
Ah, i see.
| |
2540 change_ids = git_footers.get_footer_change_id(message) | |
2541 assert len(change_ids) == 1 | |
2542 change_desc = ChangeDescription(message) | |
2543 else: | |
2544 change_desc = ChangeDescription( | |
2545 options.message or CreateDescriptionFromLog(args)) | |
2542 if not options.force: | 2546 if not options.force: |
2543 change_desc.prompt() | 2547 change_desc.prompt() |
2544 if not change_desc.description: | 2548 if not change_desc.description: |
2545 print "Description is empty; aborting." | 2549 print "Description is empty; aborting." |
2546 return 1 | 2550 return 1 |
2547 message = change_desc.description | 2551 message = change_desc.description |
2548 change_ids = git_footers.get_footer_change_id(message) | 2552 change_ids = git_footers.get_footer_change_id(message) |
2549 if len(change_ids) > 1: | 2553 if len(change_ids) > 1: |
2550 DieWithError('too many Change-Id footers in %s branch' % shadow_branch) | 2554 DieWithError('too many Change-Id footers, at most 1 allowed.') |
2551 if not change_ids: | 2555 if not change_ids: |
2556 # Generate the Change-Id automatically. | |
2552 message = git_footers.add_footer_change_id( | 2557 message = git_footers.add_footer_change_id( |
2553 message, GenerateGerritChangeId(message)) | 2558 message, GenerateGerritChangeId(message)) |
2554 change_desc.set_description(message) | 2559 change_desc.set_description(message) |
2555 change_ids = git_footers.get_footer_change_id(message) | 2560 change_ids = git_footers.get_footer_change_id(message) |
2556 assert len(change_ids) == 1 | 2561 assert len(change_ids) == 1 |
2557 | 2562 change_id = change_ids[0] |
2558 change_id = change_ids[0] | |
2559 | 2563 |
2560 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) | 2564 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
2561 if remote is '.': | 2565 if remote is '.': |
2562 # If our upstream branch is local, we base our squashed commit on its | 2566 # If our upstream branch is local, we base our squashed commit on its |
2563 # squashed version. | 2567 # squashed version. |
2564 parent = ('refs/heads/git_cl_uploads/' + | 2568 upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch) |
2565 scm.GIT.ShortBranchName(upstream_branch)) | 2569 # Check the squashed hash of the parent. |
2566 | 2570 parent = RunGit(['config', |
2567 # Verify that the upstream branch has been uploaded too, otherwise Gerrit | 2571 'branch.%s.gerritsquashhash' % upstream_branch_name], |
2568 # will create additional CLs when uploading. | 2572 error_ok=True).strip() |
2569 if (RunGitSilent(['rev-parse', upstream_branch + ':']) != | 2573 # Verify that the upstream branch has been uploaded too, otherwise |
2570 RunGitSilent(['rev-parse', parent + ':'])): | 2574 # Gerrit will create additional CLs when uploading. |
2571 print 'Upload upstream branch ' + upstream_branch + ' first.' | 2575 if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) != |
2572 return 1 | 2576 RunGitSilent(['rev-parse', parent + ':'])): |
2577 # TODO(tandrii): remove "old depot_tools" part on April 12, 2016. | |
2578 DieWithError( | |
2579 'Upload upstream branch %s first.\n' | |
2580 'Note: maybe you\'ve uploaded it with --no-squash or with old\n' | |
Bernhard Bauer
2016/03/29 08:22:49
Nit: "[...] with an old version [...]".
tandrii(chromium)
2016/03/29 13:06:24
Done.
| |
2581 ' version of depot_tools. If so, then re-upload it with:\n' | |
2582 ' git cl upload --squash\n' % upstream_branch_name) | |
2573 else: | 2583 else: |
2574 parent = cl.GetCommonAncestorWithUpstream() | 2584 parent = cl.GetCommonAncestorWithUpstream() |
2575 | 2585 |
2576 tree = RunGit(['rev-parse', 'HEAD:']).strip() | 2586 tree = RunGit(['rev-parse', 'HEAD:']).strip() |
2577 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, | 2587 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, |
2578 '-m', message]).strip() | 2588 '-m', message]).strip() |
2579 else: | 2589 else: |
2590 change_desc = ChangeDescription( | |
2591 options.message or CreateDescriptionFromLog(args)) | |
2592 if not change_desc.description: | |
2593 print "\nDescription is empty. Aborting..." | |
ukai
2016/03/29 02:16:16
nit: DieWithError?
what is difference between Die
tandrii(chromium)
2016/03/29 08:37:41
Same thing, afaiu. This was copy-paste from above,
tandrii(chromium)
2016/03/29 13:06:24
Done.
| |
2594 return 1 | |
2595 | |
2580 if not git_footers.get_footer_change_id(change_desc.description): | 2596 if not git_footers.get_footer_change_id(change_desc.description): |
2581 DownloadGerritHook(False) | 2597 DownloadGerritHook(False) |
2582 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) | 2598 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) |
2583 ref_to_push = 'HEAD' | 2599 ref_to_push = 'HEAD' |
2584 parent = '%s/%s' % (gerrit_remote, branch) | 2600 parent = '%s/%s' % (gerrit_remote, branch) |
2585 change_id = git_footers.get_footer_change_id(change_desc.description)[0] | 2601 change_id = git_footers.get_footer_change_id(change_desc.description)[0] |
2586 | 2602 |
2603 assert change_desc | |
2587 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, | 2604 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, |
2588 ref_to_push)]).splitlines() | 2605 ref_to_push)]).splitlines() |
2589 if len(commits) > 1: | 2606 if len(commits) > 1: |
2590 print('WARNING: This will upload %d commits. Run the following command ' | 2607 print('WARNING: This will upload %d commits. Run the following command ' |
2591 'to see which commits will be uploaded: ' % len(commits)) | 2608 'to see which commits will be uploaded: ' % len(commits)) |
2592 print('git log %s..%s' % (parent, ref_to_push)) | 2609 print('git log %s..%s' % (parent, ref_to_push)) |
2593 print('You can also use `git squash-branch` to squash these into a single ' | 2610 print('You can also use `git squash-branch` to squash these into a single ' |
2594 'commit.') | 2611 'commit.') |
2595 ask_for_data('About to upload; enter to confirm.') | 2612 ask_for_data('About to upload; enter to confirm.') |
2596 | 2613 |
(...skipping 26 matching lines...) Expand all Loading... | |
2623 if options.squash: | 2640 if options.squash: |
2624 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') | 2641 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') |
2625 change_numbers = [m.group(1) | 2642 change_numbers = [m.group(1) |
2626 for m in map(regex.match, push_stdout.splitlines()) | 2643 for m in map(regex.match, push_stdout.splitlines()) |
2627 if m] | 2644 if m] |
2628 if len(change_numbers) != 1: | 2645 if len(change_numbers) != 1: |
2629 DieWithError( | 2646 DieWithError( |
2630 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' | 2647 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' |
2631 'Change-Id: %s') % (len(change_numbers), change_id)) | 2648 'Change-Id: %s') % (len(change_numbers), change_id)) |
2632 cl.SetIssue(change_numbers[0]) | 2649 cl.SetIssue(change_numbers[0]) |
2633 head = RunGit(['rev-parse', 'HEAD']).strip() | 2650 RunGit(['config', 'branch.%s.gerritsquashhash' % cl.GetBranch(), |
2634 RunGit(['update-ref', '-m', 'Uploaded ' + head, shadow_branch, ref_to_push]) | 2651 ref_to_push]) |
2635 return 0 | 2652 return 0 |
2636 | 2653 |
2637 | 2654 |
2638 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix): | 2655 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix): |
2639 """Computes the remote branch ref to use for the CL. | 2656 """Computes the remote branch ref to use for the CL. |
2640 | 2657 |
2641 Args: | 2658 Args: |
2642 remote (str): The git remote for the CL. | 2659 remote (str): The git remote for the CL. |
2643 remote_branch (str): The git remote branch for the CL. | 2660 remote_branch (str): The git remote branch for the CL. |
2644 target_branch (str): The target branch specified by the user. | 2661 target_branch (str): The target branch specified by the user. |
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2915 auth.add_auth_options(parser) | 2932 auth.add_auth_options(parser) |
2916 (options, args) = parser.parse_args(args) | 2933 (options, args) = parser.parse_args(args) |
2917 auth_config = auth.extract_auth_config_from_options(options) | 2934 auth_config = auth.extract_auth_config_from_options(options) |
2918 | 2935 |
2919 if git_common.is_dirty_git_tree('upload'): | 2936 if git_common.is_dirty_git_tree('upload'): |
2920 return 1 | 2937 return 1 |
2921 | 2938 |
2922 options.reviewers = cleanup_list(options.reviewers) | 2939 options.reviewers = cleanup_list(options.reviewers) |
2923 options.cc = cleanup_list(options.cc) | 2940 options.cc = cleanup_list(options.cc) |
2924 | 2941 |
2942 # For sanity of test expectations, do this otherwise lazy-loading *now*. | |
2943 settings.GetIsGerrit() | |
2944 | |
2925 cl = Changelist(auth_config=auth_config) | 2945 cl = Changelist(auth_config=auth_config) |
2926 if args: | 2946 if args: |
2927 # TODO(ukai): is it ok for gerrit case? | 2947 # TODO(ukai): is it ok for gerrit case? |
2928 base_branch = args[0] | 2948 base_branch = args[0] |
2929 else: | 2949 else: |
2930 if cl.GetBranch() is None: | 2950 if cl.GetBranch() is None: |
2931 DieWithError('Can\'t upload from detached HEAD state. Get on a branch!') | 2951 DieWithError('Can\'t upload from detached HEAD state. Get on a branch!') |
2932 | 2952 |
2933 # Default to diffing against common ancestor of upstream branch | 2953 # Default to diffing against common ancestor of upstream branch |
2934 base_branch = cl.GetCommonAncestorWithUpstream() | 2954 base_branch = cl.GetCommonAncestorWithUpstream() |
(...skipping 1389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4324 if __name__ == '__main__': | 4344 if __name__ == '__main__': |
4325 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4345 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4326 # unit testing. | 4346 # unit testing. |
4327 fix_encoding.fix_encoding() | 4347 fix_encoding.fix_encoding() |
4328 colorama.init() | 4348 colorama.init() |
4329 try: | 4349 try: |
4330 sys.exit(main(sys.argv[1:])) | 4350 sys.exit(main(sys.argv[1:])) |
4331 except KeyboardInterrupt: | 4351 except KeyboardInterrupt: |
4332 sys.stderr.write('interrupted\n') | 4352 sys.stderr.write('interrupted\n') |
4333 sys.exit(1) | 4353 sys.exit(1) |
OLD | NEW |