Skip to content
Merged
Show file tree
Hide file tree
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
17 changes: 12 additions & 5 deletions revup/topic_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,18 @@ async def mark_rebases(self, skip_rebase: bool) -> None:
if topic.relative_topic is None:
if not review.base_ref:
raise RuntimeError("Review doesn't have a base ref!")
# For non-relative reviews, the base is correct if the remote base commit is a
# first-parent ancestor of the local remote base.
is_on_correct_base = await self.git_ctx.is_ancestor(
review.remote_commits[0].parents[0], review.base_ref
)
remote_parent = review.remote_commits[0].parents[0]
if review.relative_branch:
# A relative branch is someone else's work that this review is stacked
# on, so the review has to sit exactly on it, same as a relative series.
# Anything else makes the forge show their commits as part of this review.
is_on_correct_base = remote_parent == review.base_ref
else:
# For non-relative reviews, the base is correct if the remote base commit
# is a first-parent ancestor of the local remote base.
is_on_correct_base = await self.git_ctx.is_ancestor(
remote_parent, review.base_ref
)
else:
# For a relative series of reviews, revup will only ever upload them directly
# on top of each other. If this relationship is ever broken, we always reupload
Expand Down
53 changes: 53 additions & 0 deletions tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,59 @@ async def test_base_derived_by_walking_back_from_head(self):
assert g_review.is_pure_rebase
assert g_review.remote_commits[0].parents[0] == b_review.remote_commits[-1].commit_id

@async_test
async def test_relative_branch_moving_forward_is_pushed(self):
"""A review stacked on a relative branch follows that branch's tip."""
async with GitTestEnvironment() as env:
await setup_repo(env)
root = await env.get_commit_hash()
await env.git_ctx.git("branch", "origin/staging", root)
await env.commit("feat\n\nTopic: alpha\nRelative-Branch: staging", {"a.txt": "a"})

first = await run_upload_pipeline(env)
first_review = first.topics["alpha"].reviews["origin/main"]
remote_head = first_review.new_commits[-1]
remote_num_commits = len(first_review.new_commits)

await env.git_ctx.git("checkout", root)
await env.commit("staging moves on", {"s.txt": "s"})
await env.git_ctx.git("branch", "origin/staging", "HEAD", "-f")
await env.git_ctx.git("checkout", "main")

topics = await run_upload_pipeline(env)
review = topics.topics["alpha"].reviews["origin/main"]
review.pr_info = PrInfo(
baseRef="staging",
headRef=review.remote_head,
headRefOid=remote_head,
numCommits=remote_num_commits,
body="",
title="",
state="OPEN",
)

await topics.mark_rebases(skip_rebase=True)

assert review.is_pure_rebase
assert review.remote_commits[0].parents[0] == root
assert review.push_status == PushStatus.PUSHED

@async_test
async def test_relative_branch_staying_put_is_nochange(self):
"""A review already on the relative branch's tip isn't pushed again."""
async with GitTestEnvironment() as env:
await setup_repo(env)
await env.git_ctx.git("branch", "origin/staging", "HEAD")
await env.commit("feat\n\nTopic: alpha\nRelative-Branch: staging", {"a.txt": "a"})

topics = await run_upload_pipeline(env)
review = topics.topics["alpha"].reviews["origin/main"]
review.pr_info = make_pr_info(review, base_branch="staging")

await topics.mark_rebases(skip_rebase=True)

assert review.push_status == PushStatus.NOCHANGE


class TestSkipEmptyFirstCommit:
@async_test
Expand Down
Loading