Add external changes for stacked-changes dogfood.

Bug: b/265929888

Change-Id: Idc280549c7e41d6665173ef566637b48c2980e09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4300530
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 fd5c90c..bc58ef2 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -1005,7 +1005,7 @@
 # TODO(b/265929888): Change `parent` to `pushed_commit_base`.
 _NewUpload = collections.namedtuple('NewUpload', [
     'reviewers', 'ccs', 'commit_to_push', 'new_last_uploaded_commit', 'parent',
-    'change_desc'
+    'change_desc', 'prev_patchset'
 ])
 
 
@@ -1691,8 +1691,11 @@
           ['commit-tree', latest_tree, '-p', parent, '-F',
            desc_tempfile]).strip()
 
+    # Gerrit may or may not update fast enough to return the correct patchset
+    # number after we push. Get the pre-upload patchset and increment later.
+    prev_patchset = self.GetMostRecentPatchset(update=False) or 0
     return _NewUpload(reviewers, ccs, commit_to_push, end_commit, parent,
-                      change_desc)
+                      change_desc, prev_patchset)
 
   def PrepareCherryPickSquashedCommit(self, options, parent):
     # type: (optparse.Values, str) -> _NewUpload()
@@ -1724,8 +1727,11 @@
     commit_to_push = RunGit(['rev-parse', 'HEAD']).strip()
     RunGit(['checkout', '-q', self.branch])
 
+    # Gerrit may or may not update fast enough to return the correct patchset
+    # number after we push. Get the pre-upload patchset and increment later.
+    prev_patchset = self.GetMostRecentPatchset(update=False) or 0
     return _NewUpload(reviewers, ccs, commit_to_push, new_upload_hash,
-                      cherry_pick_base, change_desc)
+                      cherry_pick_base, change_desc, prev_patchset)
 
   def _PrepareChange(self, options, parent, end_commit):
     # type: (optparse.Values, str, str) ->
@@ -1757,9 +1763,6 @@
       change_id = change_detail['change_id']
       change_desc.ensure_change_id(change_id)
 
-      # TODO(b/265929888): Pull in external changes for the current branch
-      # only. No clear way to pull in external changes for upstream branches
-      # yet. Potentially offer a separate command to pull in external changes.
     else:  # No change issue. First time uploading
       if not options.force and not options.message_file:
         change_desc.prompt()
@@ -1807,6 +1810,8 @@
     if not self.GetIssue():
       self.SetIssue(change_number)
 
+    self.SetPatchset(new_upload.prev_patchset + 1)
+
     self._GitSetBranchConfigValue(GERRIT_SQUASH_HASH_CONFIG_KEY,
                                   new_upload.commit_to_push)
     self._GitSetBranchConfigValue(LAST_UPLOAD_HASH_CONFIG_KEY,
@@ -4837,30 +4842,35 @@
   else:
     ordered_cls = list(reversed(cls))
 
-    parent = None
-    origin = '.'
     cl = ordered_cls[0]
-    branch = cl.GetBranch()
-    while origin == '.':
-      # Search for cl's closest ancestor with a gerrit hash.
-      origin, upstream_branch_ref = Changelist.FetchUpstreamTuple(branch)
-      if origin == '.':
-        upstream_branch = scm.GIT.ShortBranchName(upstream_branch_ref)
+    # We can only support external changes when we're only uploading one
+    # branch.
+    parent = cl._UpdateWithExternalChanges() if len(ordered_cls) == 1 else None
+    if parent is None:
+      origin = '.'
+      branch = cl.GetBranch()
 
-        # Support the `git merge` and `git pull` workflow.
-        if upstream_branch in ['master', 'main']:
-          parent = cl.GetCommonAncestorWithUpstream()
-        else:
-          parent = scm.GIT.GetBranchConfig(settings.GetRoot(), upstream_branch,
-                                           GERRIT_SQUASH_HASH_CONFIG_KEY)
-        if parent:
-          break
-        branch = upstream_branch
-    else:
-      # Either the root of the tree is the cl's direct parent and the while
-      # loop above only found empty branches between cl and the root of the
-      # tree.
-      parent = cl.GetCommonAncestorWithUpstream()
+      while origin == '.':
+        # Search for cl's closest ancestor with a gerrit hash.
+        origin, upstream_branch_ref = Changelist.FetchUpstreamTuple(branch)
+        if origin == '.':
+          upstream_branch = scm.GIT.ShortBranchName(upstream_branch_ref)
+
+          # Support the `git merge` and `git pull` workflow.
+          if upstream_branch in ['master', 'main']:
+            parent = cl.GetCommonAncestorWithUpstream()
+          else:
+            parent = scm.GIT.GetBranchConfig(settings.GetRoot(),
+                                             upstream_branch,
+                                             GERRIT_SQUASH_HASH_CONFIG_KEY)
+          if parent:
+            break
+          branch = upstream_branch
+      else:
+        # Either the root of the tree is the cl's direct parent and the while
+        # loop above only found empty branches between cl and the root of the
+        # tree.
+        parent = cl.GetCommonAncestorWithUpstream()
 
     for i, cl in enumerate(ordered_cls):
       # If we're in the middle of the stack, set end_commit to downstream's