[stacked_changes] Create cherry-picked commit.

Bug:b/265929888
Change-Id: I4277474c1f09e4ac6ea6ebb5d9d340f22365f542
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4178924
Reviewed-by: Gavin Mak <gavinmak@google.com>
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
Commit-Queue: Joanna Wang <jojwang@chromium.org>
diff --git a/git_cl.py b/git_cl.py
index e4b3caa..77d4a10 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -999,6 +999,12 @@
                         'approval', 'disapproval'])
 
 
+_NewUpload = collections.namedtuple('NewUpload', [
+    'reviewers', 'ccs', 'commit_to_push', 'new_last_uploaded_commit',
+    'change_desc'
+])
+
+
 class Changelist(object):
   """Changelist works with one changelist in local branch.
 
@@ -1569,6 +1575,41 @@
       return title
     return user_title or title
 
+  def PrepareCherryPickSquashedCommit(self, options):
+    # type: (optparse.Values) -> _NewUpload()
+    """Create a commit cherry-picked on parent to push."""
+
+    parent = self.GetCommonAncestorWithUpstream()
+    reviewers, ccs, change_desc = self._PrepareChange(options, parent,
+                                                      self.branchref)
+
+    new_upload_hash = RunGit(['rev-parse', self.branchref]).strip()
+    latest_tree = RunGit(['rev-parse', self.branchref + ':']).strip()
+    with gclient_utils.temporary_file() as desc_tempfile:
+      gclient_utils.FileWrite(desc_tempfile, change_desc.description)
+      commit_to_cp = RunGit(
+          ['commit-tree', latest_tree, '-p', parent, '-F',
+           desc_tempfile]).strip()
+
+    _, upstream_branch_ref = self.FetchUpstreamTuple(self.GetBranch())
+
+    upstream_branch = scm.GIT.ShortBranchName(upstream_branch_ref)
+    upstream_squashed_upload = scm.GIT.GetBranchConfig(
+        settings.GetRoot(), upstream_branch, GERRIT_SQUASH_HASH_CONFIG_KEY)
+
+    RunGit(['checkout', '-q', upstream_squashed_upload])
+    ret, _out = RunGitWithCode(['cherry-pick', commit_to_cp])
+    if ret:
+      RunGit(['cherry-pick', '--abort'])
+      RunGit(['checkout', '-q', self.branch])
+      DieWithError('Could not cleanly cherry-pick')
+
+    commit_to_push = RunGit(['rev-parse', 'HEAD'])
+    RunGit(['checkout', '-q', self.branch])
+
+    return _NewUpload(reviewers, ccs, commit_to_push, new_upload_hash,
+                      change_desc)
+
   def _PrepareChange(self, options, parent, end_commit):
     # type: (optparse.Values, str, str) ->
     #     Tuple[Sequence[str], Sequence[str], ChangeDescription]