Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/stack_pr/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ class StackEntry:
_pr: str | None = None
_base: str | None = None
_head: str | None = None
need_update: bool = False
is_tmp_draft: bool = False

@property
def pr(self) -> str:
Expand Down Expand Up @@ -783,7 +783,7 @@ def toc_entry(se: StackEntry) -> str:
return f"Stacked PRs:\n{''.join(entries)}\n"


def get_current_pr_body(e: StackEntry) -> str:
def get_pr_body(e: StackEntry) -> str:
out = get_command_output(
["gh", "pr", "view", e.pr, "--json", "body"],
)
Expand Down Expand Up @@ -815,7 +815,7 @@ def add_cross_links(st: list[StackEntry], *, keep_body: bool, verbose: bool) ->

if keep_body:
# Keep current body of the PR after the cross links component
current_pr_body = get_current_pr_body(e)
current_pr_body = get_pr_body(e)
body_content = current_pr_body.split(CROSS_LINKS_DELIMETER, 1)[-1].lstrip()

pr_body = [*header, body_content]
Expand All @@ -831,6 +831,13 @@ def add_cross_links(st: list[StackEntry], *, keep_body: bool, verbose: bool) ->
raise RuntimeError


def is_draft_pr(e: StackEntry) -> bool:
out = get_command_output(
["gh", "pr", "view", e.pr, "--json", "isDraft"],
)
return json.loads(out)["isDraft"]


# Temporarily set base branches of existing PRs to the bottom of the stack.
# This needs to be done to avoid PRs getting closed when commits are
# rearranged.
Expand All @@ -852,14 +859,21 @@ def add_cross_links(st: list[StackEntry], *, keep_body: bool, verbose: bool) ->
# stack/2. If we push stack/1, then PR #2 gets automatically closed, since its
# head branch will contain all the commits from its base branch.
#
# To avoid this, we temporarily set all base branches to point to 'main' - once
# all the branches are pushed we can set the actual base branches.
# To avoid this, we temporarily set all base branches to point to 'main'. To ensure
# we don't accidentally notify reviewers in this transient state (where the PRs are
# pointing to 'main'), we mark the PRs as draft - once all the branches are pushed
# we can set the actual base branches and undraft the PRs.
def reset_remote_base_branches(
st: list[StackEntry], target: str, *, verbose: bool
) -> None:
log(h("Resetting remote base branches"), level=2)

for e in filter(lambda e: e.has_pr(), st):
# We need to check if the PR is already draft, otherwise we would
# unintentionally undo the draft status later.
if not is_draft_pr(e):
run_shell_command(["gh", "pr", "ready", e.pr, "--undo"], quiet=not verbose)
e.is_tmp_draft = True
run_shell_command(["gh", "pr", "edit", e.pr, "-B", target], quiet=not verbose)


Expand Down Expand Up @@ -1117,6 +1131,12 @@ def command_submit(
log(h("Adding cross-links to PRs"), level=1)
add_cross_links(st, keep_body=keep_body, verbose=args.verbose)

# Undraft the PRs if they were marked as temporary drafts.
for e in st:
if e.is_tmp_draft:
run_shell_command(["gh", "pr", "ready", e.pr], quiet=not args.verbose)
e.is_tmp_draft = False

if need_to_rebase_current:
log(h(f"Rebasing the original branch '{current_branch}'"), level=2)
run_shell_command(
Expand Down
Loading