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
4 changes: 3 additions & 1 deletion mergify_cli/stack/changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import httpx


CHANGEID_RE = re.compile(r"Change-Id: (I[0-9a-z]{40})")
_CHANGEID_PATTERN = r"I[0-9a-z]{40}"
CHANGEID_RE = re.compile(rf"Change-Id: ({_CHANGEID_PATTERN})")
CHANGEID_SUFFIX_RE = re.compile(rf"/{_CHANGEID_PATTERN}$")


def is_change_id_prefix(prefix: str) -> bool:
Expand Down
7 changes: 7 additions & 0 deletions mergify_cli/stack/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ async def stack_checkout(
if branch_prefix is None:
branch_prefix = await utils.get_default_branch_prefix(author)

# Strip change ID suffix if present (e.g. /Ibb431d523fb75f48f387a3964d2936ada933cffe)
branch = changes.CHANGEID_SUFFIX_RE.sub("", branch)

# Strip branch prefix from branch if already included
if branch_prefix and branch.startswith(f"{branch_prefix}/"):
branch = branch.removeprefix(f"{branch_prefix}/")

stack_branch = f"{branch_prefix}/{branch}" if branch_prefix else branch

async with utils.get_github_http_client(github_server, token) as client:
Expand Down
3 changes: 1 addition & 2 deletions mergify_cli/stack/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ class LocalBranchInvalidError(Exception):


def check_local_branch(branch_name: str, branch_prefix: str) -> None:
if branch_name.startswith(branch_prefix) and re.search(
r"I[0-9a-z]{40}$",
if branch_name.startswith(branch_prefix) and changes.CHANGEID_SUFFIX_RE.search(
branch_name,
):
msg = "Local branch is a branch generated by Mergify CLI"
Expand Down
71 changes: 71 additions & 0 deletions mergify_cli/tests/stack/test_checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,74 @@ async def test_stack_checkout_repository_explicit(
"--get",
"remote.origin.url",
)


@pytest.mark.respx(base_url="https://api.github.com/")
@pytest.mark.parametrize(
("branch_input", "expected_branch"),
[
# Full head ref with prefix and change ID suffix → stripped to plain branch
(
"devs/JulianMaurin/MRGFY-6797/Ibb431d523fb75f48f387a3964d2936ada933cffe",
"MRGFY-6797",
),
# Change ID suffix only → stripped
(
"MRGFY-6797/Ibb431d523fb75f48f387a3964d2936ada933cffe",
"MRGFY-6797",
),
# Prefix only → stripped
(
"devs/JulianMaurin/MRGFY-6797",
"MRGFY-6797",
),
# Already plain → unchanged
(
"MRGFY-6797",
"MRGFY-6797",
),
],
ids=[
"full-head-ref",
"with-suffix-no-prefix",
"with-prefix-no-suffix",
"plain-branch",
],
)
async def test_stack_checkout_branch_normalization(
git_mock: test_utils.GitMock,
respx_mock: respx.MockRouter,
branch_input: str,
expected_branch: str,
) -> None:
"""Test that checkout normalizes --branch by stripping prefix and change ID suffix."""
git_mock.mock(
"config",
"--get",
"mergify-cli.stack-branch-prefix",
output="devs/JulianMaurin",
)

search_mock = respx_mock.get("/search/issues").respond(
200,
json={"items": []},
)

with pytest.raises(SystemExit, match="0"):
await stack_checkout_mod.stack_checkout(
github_server="https://api.github.com/",
token="",
user="user",
repo="repo",
branch_prefix=None,
branch=branch_input,
author="author",
trunk=("origin", "main"),
dry_run=True,
)

# Verify the search query: stack_branch = prefix/normalized_branch
expected_stack_branch = f"devs/JulianMaurin/{expected_branch}"
assert len(search_mock.calls) == 1
query = str(search_mock.calls[0].request.url)
assert f"head%3A{expected_stack_branch.replace('/', '%2F')}%2F" in query
Loading