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

Side by Side Diff: git_cl.py

Issue 1835963003: Gerrit git cl: stop creating a shadow branch. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@H150
Patch Set: review Created 4 years, 8 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
« 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 distutils.version import LooseVersion 10 from distutils.version import LooseVersion
(...skipping 2500 matching lines...) Expand 10 before | Expand all | Expand 10 after
2511 # whitespace. This code is not doing this, but it clearly won't decrease 2511 # whitespace. This code is not doing this, but it clearly won't decrease
2512 # entropy. 2512 # entropy.
2513 lines.append(message) 2513 lines.append(message)
2514 change_hash = RunCommand(['git', 'hash-object', '-t', 'commit', '--stdin'], 2514 change_hash = RunCommand(['git', 'hash-object', '-t', 'commit', '--stdin'],
2515 stdin='\n'.join(lines)) 2515 stdin='\n'.join(lines))
2516 return 'I%s' % change_hash.strip() 2516 return 'I%s' % change_hash.strip()
2517 2517
2518 2518
2519 def GerritUpload(options, args, cl, change): 2519 def GerritUpload(options, args, cl, change):
2520 """upload the current branch to gerrit.""" 2520 """upload the current branch to gerrit."""
2521 # TODO(tandrii): refactor this to be a method of _GerritChangelistImpl,
2522 # to avoid private members accessors below.
2523
2521 # We assume the remote called "origin" is the one we want. 2524 # We assume the remote called "origin" is the one we want.
2522 # It is probably not worthwhile to support different workflows. 2525 # It is probably not worthwhile to support different workflows.
2523 gerrit_remote = 'origin' 2526 gerrit_remote = 'origin'
2524 2527
2525 remote, remote_branch = cl.GetRemoteBranch() 2528 remote, remote_branch = cl.GetRemoteBranch()
2526 branch = GetTargetRef(remote, remote_branch, options.target_branch, 2529 branch = GetTargetRef(remote, remote_branch, options.target_branch,
2527 pending_prefix='') 2530 pending_prefix='')
2528 2531
2529 change_desc = ChangeDescription(
2530 options.message or CreateDescriptionFromLog(args))
2531 if not change_desc.description:
2532 print "\nDescription is empty. Aborting..."
2533 return 1
2534
2535 if options.title: 2532 if options.title:
2536 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..." 2533 print "\nPatch titles (-t) are not supported in Gerrit. Aborting..."
2537 return 1 2534 return 1
2538 2535
2539 if options.squash: 2536 if options.squash:
2540 # Try to get the message from a previous upload. 2537 if not cl.GetIssue():
2541 shadow_branch = 'refs/heads/git_cl_uploads/' + cl.GetBranch() 2538 # TODO(tandrii): deperecate this after 2016Q2.
2542 message = RunGitSilent(['show', '--format=%B', '-s', shadow_branch]) 2539 # Backwards compatibility with shadow branch, which used to contain
2543 if not message: 2540 # change-id for a given branch, using which we can fetch actual issue
2541 # number and set it as the property of the branch, which is the new way.
2542 message = RunGitSilent(['show', '--format=%B', '-s',
2543 'refs/heads/git_cl_uploads/%s' % cl.GetBranch()])
2544 if message:
2545 change_ids = git_footers.get_footer_change_id(message.strip())
2546 if change_ids and len(change_ids) == 1:
2547 details = gerrit_util.GetChangeDetail(
2548 cl._codereview_impl._GetGerritHost(), change_ids[0])
2549 if details:
2550 print('WARNING: found old upload in branch git_cl_uploads/%s '
2551 'corresponding to issue %s' %
2552 (cl.GetBranch(), details['_number']))
2553 cl.SetIssue(details['_number'])
2554 if not cl.GetIssue():
2555 DieWithError(
2556 '\n' # For readability of the blob below.
2557 'Found old upload in branch git_cl_uploads/%s, '
2558 'but failed to find corresponding Gerrit issue.\n'
2559 'If you know the issue number, set it manually first:\n'
2560 ' git cl issue 123456\n'
2561 'If you intended to upload this CL as new issue, '
2562 'just delete or rename the old upload branch:\n'
2563 ' git rename-branch git_cl_uploads/%s old_upload-%s\n'
2564 'After that, please run git cl upload again.' %
2565 tuple([cl.GetBranch()] * 3))
2566 # End of backwards compatability.
2567
2568 if cl.GetIssue():
2569 # Try to get the message from a previous upload.
2570 message = cl.GetDescription()
2571 if not message:
2572 DieWithError(
2573 'failed to fetch description from current Gerrit issue %d\n'
2574 '%s' % (cl.GetIssue(), cl.GetIssueURL()))
2575 change_id = cl._codereview_impl._GetChangeDetail([])['change_id']
2576 while True:
2577 footer_change_ids = git_footers.get_footer_change_id(message)
2578 if footer_change_ids == [change_id]:
2579 break
2580 if not footer_change_ids:
2581 message = git_footers.add_footer_change_id(message, change_id)
2582 print('WARNING: appended missing Change-Id to issue description')
2583 continue
2584 # There is already a valid footer but with different or several ids.
2585 # Doing this automatically is non-trivial as we don't want to lose
2586 # existing other footers, yet we want to append just 1 desired
2587 # Change-Id. Thus, just create a new footer, but let user verify the new
2588 # description.
2589 message = '%s\n\nChange-Id: %s' % (message, change_id)
2590 print(
2591 'WARNING: issue %s has Change-Id footer(s):\n'
2592 ' %s\n'
2593 'but issue has Change-Id %s, according to Gerrit.\n'
2594 'Please, check the proposed correction to the description, '
2595 'and edit it if necessary but keep the "Change-Id: %s" footer\n'
2596 % (cl.GetIssue(), '\n '.join(footer_change_ids), change_id,
2597 change_id))
2598 ask_for_data('Press enter to edit now, Ctrl+C to abort')
2599 if not options.force:
2600 change_desc = ChangeDescription(message)
2601 change_desc.prompt()
2602 message = change_desc.description
2603 if not message:
2604 DieWithError("Description is empty. Aborting...")
2605 # Continue the while loop.
2606 # Sanity check of this code - we should end up with proper message footer.
2607 assert [change_id] == git_footers.get_footer_change_id(message)
2608 change_desc = ChangeDescription(message)
2609 else:
2610 change_desc = ChangeDescription(
2611 options.message or CreateDescriptionFromLog(args))
2544 if not options.force: 2612 if not options.force:
2545 change_desc.prompt() 2613 change_desc.prompt()
2546 if not change_desc.description: 2614 if not change_desc.description:
2547 print "Description is empty; aborting." 2615 DieWithError("Description is empty. Aborting...")
2548 return 1
2549 message = change_desc.description 2616 message = change_desc.description
2550 change_ids = git_footers.get_footer_change_id(message) 2617 change_ids = git_footers.get_footer_change_id(message)
2551 if len(change_ids) > 1: 2618 if len(change_ids) > 1:
2552 DieWithError('too many Change-Id footers in %s branch' % shadow_branch) 2619 DieWithError('too many Change-Id footers, at most 1 allowed.')
2553 if not change_ids: 2620 if not change_ids:
2621 # Generate the Change-Id automatically.
2554 message = git_footers.add_footer_change_id( 2622 message = git_footers.add_footer_change_id(
2555 message, GenerateGerritChangeId(message)) 2623 message, GenerateGerritChangeId(message))
2556 change_desc.set_description(message) 2624 change_desc.set_description(message)
2557 change_ids = git_footers.get_footer_change_id(message) 2625 change_ids = git_footers.get_footer_change_id(message)
2558 assert len(change_ids) == 1 2626 assert len(change_ids) == 1
2559
2560 change_id = change_ids[0] 2627 change_id = change_ids[0]
2561 2628
2562 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) 2629 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch())
2563 if remote is '.': 2630 if remote is '.':
2564 # If our upstream branch is local, we base our squashed commit on its 2631 # If our upstream branch is local, we base our squashed commit on its
2565 # squashed version. 2632 # squashed version.
2566 parent = ('refs/heads/git_cl_uploads/' + 2633 upstream_branch_name = scm.GIT.ShortBranchName(upstream_branch)
2567 scm.GIT.ShortBranchName(upstream_branch)) 2634 # Check the squashed hash of the parent.
2568 2635 parent = RunGit(['config',
2569 # Verify that the upstream branch has been uploaded too, otherwise Gerrit 2636 'branch.%s.gerritsquashhash' % upstream_branch_name],
2570 # will create additional CLs when uploading. 2637 error_ok=True).strip()
2571 if (RunGitSilent(['rev-parse', upstream_branch + ':']) != 2638 # Verify that the upstream branch has been uploaded too, otherwise
2572 RunGitSilent(['rev-parse', parent + ':'])): 2639 # Gerrit will create additional CLs when uploading.
2573 print 'Upload upstream branch ' + upstream_branch + ' first.' 2640 if not parent or (RunGitSilent(['rev-parse', upstream_branch + ':']) !=
2574 return 1 2641 RunGitSilent(['rev-parse', parent + ':'])):
2642 # TODO(tandrii): remove "old depot_tools" part on April 12, 2016.
2643 DieWithError(
2644 'Upload upstream branch %s first.\n'
2645 'Note: maybe you\'ve uploaded it with --no-squash or with an old\n'
2646 ' version of depot_tools. If so, then re-upload it with:\n'
2647 ' git cl upload --squash\n' % upstream_branch_name)
2575 else: 2648 else:
2576 parent = cl.GetCommonAncestorWithUpstream() 2649 parent = cl.GetCommonAncestorWithUpstream()
2577 2650
2578 tree = RunGit(['rev-parse', 'HEAD:']).strip() 2651 tree = RunGit(['rev-parse', 'HEAD:']).strip()
2579 ref_to_push = RunGit(['commit-tree', tree, '-p', parent, 2652 ref_to_push = RunGit(['commit-tree', tree, '-p', parent,
2580 '-m', message]).strip() 2653 '-m', message]).strip()
2581 else: 2654 else:
2655 change_desc = ChangeDescription(
2656 options.message or CreateDescriptionFromLog(args))
2657 if not change_desc.description:
2658 DieWithError("Description is empty. Aborting...")
2659
2582 if not git_footers.get_footer_change_id(change_desc.description): 2660 if not git_footers.get_footer_change_id(change_desc.description):
2583 DownloadGerritHook(False) 2661 DownloadGerritHook(False)
2584 change_desc.set_description(AddChangeIdToCommitMessage(options, args)) 2662 change_desc.set_description(AddChangeIdToCommitMessage(options, args))
2585 ref_to_push = 'HEAD' 2663 ref_to_push = 'HEAD'
2586 parent = '%s/%s' % (gerrit_remote, branch) 2664 parent = '%s/%s' % (gerrit_remote, branch)
2587 change_id = git_footers.get_footer_change_id(change_desc.description)[0] 2665 change_id = git_footers.get_footer_change_id(change_desc.description)[0]
2588 2666
2667 assert change_desc
2589 commits = RunGitSilent(['rev-list', '%s..%s' % (parent, 2668 commits = RunGitSilent(['rev-list', '%s..%s' % (parent,
2590 ref_to_push)]).splitlines() 2669 ref_to_push)]).splitlines()
2591 if len(commits) > 1: 2670 if len(commits) > 1:
2592 print('WARNING: This will upload %d commits. Run the following command ' 2671 print('WARNING: This will upload %d commits. Run the following command '
2593 'to see which commits will be uploaded: ' % len(commits)) 2672 'to see which commits will be uploaded: ' % len(commits))
2594 print('git log %s..%s' % (parent, ref_to_push)) 2673 print('git log %s..%s' % (parent, ref_to_push))
2595 print('You can also use `git squash-branch` to squash these into a single ' 2674 print('You can also use `git squash-branch` to squash these into a single '
2596 'commit.') 2675 'commit.')
2597 ask_for_data('About to upload; enter to confirm.') 2676 ask_for_data('About to upload; enter to confirm.')
2598 2677
(...skipping 26 matching lines...) Expand all
2625 if options.squash: 2704 if options.squash:
2626 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') 2705 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*')
2627 change_numbers = [m.group(1) 2706 change_numbers = [m.group(1)
2628 for m in map(regex.match, push_stdout.splitlines()) 2707 for m in map(regex.match, push_stdout.splitlines())
2629 if m] 2708 if m]
2630 if len(change_numbers) != 1: 2709 if len(change_numbers) != 1:
2631 DieWithError( 2710 DieWithError(
2632 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' 2711 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n'
2633 'Change-Id: %s') % (len(change_numbers), change_id)) 2712 'Change-Id: %s') % (len(change_numbers), change_id))
2634 cl.SetIssue(change_numbers[0]) 2713 cl.SetIssue(change_numbers[0])
2635 head = RunGit(['rev-parse', 'HEAD']).strip() 2714 RunGit(['config', 'branch.%s.gerritsquashhash' % cl.GetBranch(),
2636 RunGit(['update-ref', '-m', 'Uploaded ' + head, shadow_branch, ref_to_push]) 2715 ref_to_push])
2637 return 0 2716 return 0
2638 2717
2639 2718
2640 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix): 2719 def GetTargetRef(remote, remote_branch, target_branch, pending_prefix):
2641 """Computes the remote branch ref to use for the CL. 2720 """Computes the remote branch ref to use for the CL.
2642 2721
2643 Args: 2722 Args:
2644 remote (str): The git remote for the CL. 2723 remote (str): The git remote for the CL.
2645 remote_branch (str): The git remote branch for the CL. 2724 remote_branch (str): The git remote branch for the CL.
2646 target_branch (str): The target branch specified by the user. 2725 target_branch (str): The target branch specified by the user.
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
2917 auth.add_auth_options(parser) 2996 auth.add_auth_options(parser)
2918 (options, args) = parser.parse_args(args) 2997 (options, args) = parser.parse_args(args)
2919 auth_config = auth.extract_auth_config_from_options(options) 2998 auth_config = auth.extract_auth_config_from_options(options)
2920 2999
2921 if git_common.is_dirty_git_tree('upload'): 3000 if git_common.is_dirty_git_tree('upload'):
2922 return 1 3001 return 1
2923 3002
2924 options.reviewers = cleanup_list(options.reviewers) 3003 options.reviewers = cleanup_list(options.reviewers)
2925 options.cc = cleanup_list(options.cc) 3004 options.cc = cleanup_list(options.cc)
2926 3005
3006 # For sanity of test expectations, do this otherwise lazy-loading *now*.
3007 settings.GetIsGerrit()
3008
2927 cl = Changelist(auth_config=auth_config) 3009 cl = Changelist(auth_config=auth_config)
2928 if args: 3010 if args:
2929 # TODO(ukai): is it ok for gerrit case? 3011 # TODO(ukai): is it ok for gerrit case?
2930 base_branch = args[0] 3012 base_branch = args[0]
2931 else: 3013 else:
2932 if cl.GetBranch() is None: 3014 if cl.GetBranch() is None:
2933 DieWithError('Can\'t upload from detached HEAD state. Get on a branch!') 3015 DieWithError('Can\'t upload from detached HEAD state. Get on a branch!')
2934 3016
2935 # Default to diffing against common ancestor of upstream branch 3017 # Default to diffing against common ancestor of upstream branch
2936 base_branch = cl.GetCommonAncestorWithUpstream() 3018 base_branch = cl.GetCommonAncestorWithUpstream()
(...skipping 1389 matching lines...) Expand 10 before | Expand all | Expand 10 after
4326 if __name__ == '__main__': 4408 if __name__ == '__main__':
4327 # These affect sys.stdout so do it outside of main() to simplify mocks in 4409 # These affect sys.stdout so do it outside of main() to simplify mocks in
4328 # unit testing. 4410 # unit testing.
4329 fix_encoding.fix_encoding() 4411 fix_encoding.fix_encoding()
4330 colorama.init() 4412 colorama.init()
4331 try: 4413 try:
4332 sys.exit(main(sys.argv[1:])) 4414 sys.exit(main(sys.argv[1:]))
4333 except KeyboardInterrupt: 4415 except KeyboardInterrupt:
4334 sys.stderr.write('interrupted\n') 4416 sys.stderr.write('interrupted\n')
4335 sys.exit(1) 4417 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