Add --partial_ok to compare_branches.py. This change lets compare_branches.py succeed with a partial set of cherry-picks. In our case, we sometimes get baacked up with several commits needing to be cherry-picked. The first few cherry-pick fine, and there's a problematic commit sometime down the line. By accepting the first few that cherry-pick fine, someone resolving the conflict can start at where that conflict begins, rather than having to wrangle more commits unnecessarily.
I tested this with and without the flag, and confirmed that if the first commit is problematic for cherry-picks, the command does fail. Change-Id: I2a8b34577f9cb74565adf90a2b7d5328bc555f85 Reviewed-on: http://gerrit.cloudera.org:8080/10025 Reviewed-by: Joe McDonnell <joemcdonn...@cloudera.com> Tested-by: Philip Zeyliger <phi...@cloudera.com> Project: http://git-wip-us.apache.org/repos/asf/impala/repo Commit: http://git-wip-us.apache.org/repos/asf/impala/commit/242e822a Tree: http://git-wip-us.apache.org/repos/asf/impala/tree/242e822a Diff: http://git-wip-us.apache.org/repos/asf/impala/diff/242e822a Branch: refs/heads/master Commit: 242e822ae6819c5029270f58f771ee61c52b676e Parents: 257ae0e Author: Philip Zeyliger <phi...@cloudera.com> Authored: Wed Apr 11 15:36:14 2018 -0700 Committer: Philip Zeyliger <phi...@cloudera.com> Committed: Wed Apr 11 22:54:51 2018 +0000 ---------------------------------------------------------------------- bin/compare_branches.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/impala/blob/242e822a/bin/compare_branches.py ---------------------------------------------------------------------- diff --git a/bin/compare_branches.py b/bin/compare_branches.py index 0928751..8b54636 100755 --- a/bin/compare_branches.py +++ b/bin/compare_branches.py @@ -90,6 +90,8 @@ def create_parser(): parser.add_argument('--cherry_pick', action='store_true', default=False, help='Cherry-pick mismatched commits to current branch. This ' + 'must match (in the hash sense) the target branch.') + parser.add_argument('--partial_ok', action='store_true', default=False, + help='Exit with success if at least one cherrypick succeeded.') parser.add_argument('--source_branch', default='master') parser.add_argument('--target_branch', default='2.x') parser.add_argument('--source_remote_name', default='asf-gerrit', @@ -159,7 +161,7 @@ def build_commit_map(branch, merge_base): logging.debug("Commit map for branch %s has size %d.", branch, len(result)) return result -def cherrypick(cherry_pick_hashes, full_target_branch_name): +def cherrypick(cherry_pick_hashes, full_target_branch_name, partial_ok): """Cherrypicks the given commits. Also, asserts that full_target_branch_name matches the current HEAD. @@ -167,6 +169,9 @@ def cherrypick(cherry_pick_hashes, full_target_branch_name): cherry_pick_hashes is a list of git hashes, in the order to be cherry-picked. + If partial_ok is true, return gracefully if at least one cherrypick + has succeeded. + Note that this function does not push to the remote. """ print "Cherrypicking %d changes." % (len(cherry_pick_hashes),) @@ -184,10 +189,16 @@ def cherrypick(cherry_pick_hashes, full_target_branch_name): sys.exit(1) cherry_pick_hashes.reverse() - for cherry_pick_hash in cherry_pick_hashes: - subprocess.check_call( + for i, cherry_pick_hash in enumerate(cherry_pick_hashes): + ret = subprocess.call( ['git', 'cherry-pick', '--keep-redundant-commits', cherry_pick_hash]) - + if ret != 0: + if partial_ok and i > 0: + subprocess.check_call(['git', 'cherry-pick', '--abort']) + print "Failed to cherry-pick %s; stopping picks." % (cherry_pick_hash,) + return + else: + raise Exception("Failed to cherry-pick: %s" % (cherry_pick_hash,)) def main(): parser = create_parser() @@ -270,7 +281,7 @@ def main(): .format(pformat(commits_ignored))) if options.cherry_pick: - cherrypick(cherry_pick_hashes, full_target_branch_name) + cherrypick(cherry_pick_hashes, full_target_branch_name, options.partial_ok) if __name__ == '__main__': main()