From d572a13021b0430eddf83e9caedc9d5add693c62 Mon Sep 17 00:00:00 2001 From: Victor Boivie Date: Thu, 11 Nov 2010 20:36:39 +0100 Subject: [PATCH] Added repo cherry-pick command It is undesired to have the same Change-Id:-line for two separate commits, and when cherry-picking, the user must manually change it. If this is not done, bad things may happen (such as when the user is uploading the cherry-picked commit to Gerrit, it will instead see it as a new patch-set for the original change, or worse). repo cherry-pick works the same was as git cherry-pick, except that it replaces the Change-Id with a new one and adds a reference back to the commit from where it was picked. On failures (when git can not successfully apply the cherry-picked commit), instructions will be written to the user. Change-Id: I5a38b89839f91848fad43386d43cae2f6cdabf83 --- subcmds/cherry_pick.py | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 subcmds/cherry_pick.py diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py new file mode 100644 index 00000000..8da3a750 --- /dev/null +++ b/subcmds/cherry_pick.py @@ -0,0 +1,114 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys, re, string, random, os +from command import Command +from git_command import GitCommand + +CHANGE_ID_RE = re.compile(r'^\s*Change-Id: I([0-9a-f]{40})\s*$') + +class CherryPick(Command): + common = True + helpSummary = "Cherry-pick a change." + helpUsage = """ +%prog +""" + helpDescription = """ +'%prog' cherry-picks a change from one branch to another. +The change id will be updated, and a reference to the old +change id will be added. +""" + + def _Options(self, p): + pass + + def Execute(self, opt, args): + if len(args) != 1: + self.Usage() + + reference = args[0] + + p = GitCommand(None, + ['rev-parse', '--verify', reference], + capture_stdout = True, + capture_stderr = True) + if p.Wait() != 0: + print >>sys.stderr, p.stderr + sys.exit(1) + sha1 = p.stdout.strip() + + p = GitCommand(None, ['cat-file', 'commit', sha1], capture_stdout=True) + if p.Wait() != 0: + print >>sys.stderr, "error: Failed to retrieve old commit message" + sys.exit(1) + old_msg = self._StripHeader(p.stdout) + + p = GitCommand(None, + ['cherry-pick', sha1], + capture_stdout = True, + capture_stderr = True) + status = p.Wait() + + print >>sys.stdout, p.stdout + print >>sys.stderr, p.stderr + + if status == 0: + # The cherry-pick was applied correctly. We just need to edit the + # commit message. + new_msg = self._Reformat(old_msg, sha1) + + p = GitCommand(None, ['commit', '--amend', '-F', '-'], + provide_stdin = True, + capture_stdout = True, + capture_stderr = True) + p.stdin.write(new_msg) + if p.Wait() != 0: + print >>sys.stderr, "error: Failed to update commit message" + sys.exit(1) + + else: + print >>sys.stderr, """\ +NOTE: When committing (please see above) and editing the commit message, +please remove the old Change-Id-line and add: +""" + print >>sys.stderr, self._GetReference(sha1) + print >>sys.stderr + + def _IsChangeId(self, line): + return CHANGE_ID_RE.match(line) + + def _GetReference(self, sha1): + return "(cherry picked from commit %s)" % sha1 + + def _StripHeader(self, commit_msg): + lines = commit_msg.splitlines() + return "\n".join(lines[lines.index("")+1:]) + + def _Reformat(self, old_msg, sha1): + new_msg = [] + + for line in old_msg.splitlines(): + if not self._IsChangeId(line): + new_msg.append(line) + + # Add a blank line between the message and the change id/reference + try: + if new_msg[-1].strip() != "": + new_msg.append("") + except IndexError: + pass + + new_msg.append(self._GetReference(sha1)) + return "\n".join(new_msg)