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
33 changes: 33 additions & 0 deletions mergify_cli/stack/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from mergify_cli.stack import push as stack_push_mod
from mergify_cli.stack import reorder as stack_reorder_mod
from mergify_cli.stack import setup as stack_setup_mod
from mergify_cli.stack import sync as stack_sync_mod


def trunk_type(
Expand Down Expand Up @@ -440,6 +441,38 @@ async def github_action_auto_rebase(ctx: click.Context) -> None:
)


@stack.command(help="Sync the stack: fetch trunk, remove merged commits, rebase")
@click.pass_context
@click.option(
"--dry-run",
"-n",
is_flag=True,
default=False,
help="Show what would happen without making changes",
)
@click.option(
"--trunk",
"-t",
type=click.UNPROCESSED,
default=lambda: asyncio.run(utils.get_trunk()),
callback=trunk_type,
help="Change the target branch of the stack.",
)
@utils.run_with_asyncio
async def sync(
ctx: click.Context,
*,
dry_run: bool,
trunk: tuple[str, str],
) -> None:
await stack_sync_mod.stack_sync(
github_server=ctx.obj["github_server"],
token=ctx.obj["token"],
trunk=trunk,
dry_run=dry_run,
Comment thread
jd marked this conversation as resolved.
)


@stack.command(name="list", help="List the stack's commits and their associated PRs")
@click.pass_context
@click.option(
Expand Down
79 changes: 79 additions & 0 deletions mergify_cli/stack/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,82 @@ async def smart_rebase(
script_path.unlink(missing_ok=True)

return status


async def stack_sync(
github_server: str,
token: str,
*,
trunk: tuple[str, str],
dry_run: bool = False,
branch_prefix: str | None = None,
author: str | None = None,
) -> None:
"""Sync the current stack by removing merged commits and rebasing.

Args:
github_server: GitHub API server URL
token: GitHub personal access token
trunk: Tuple of (remote, branch) for the trunk
dry_run: If True, only report what would be done
branch_prefix: Optional branch prefix for stack branches
author: Optional author filter (defaults to token owner)
"""
remote, base_branch = trunk

# Dry-run: just check status and report
if dry_run:
with console.status("Checking sync status\u2026"):
status = await get_sync_status(
github_server,
token,
trunk=trunk,
branch_prefix=branch_prefix,
author=author,
)

if status.all_merged:
console.print(
f"All commits in the stack have been merged into {base_branch}.\n"
f"You can switch to {base_branch} with: git checkout {base_branch}",
)
elif status.up_to_date:
console.print("Stack is up to date.")
else:
console.print(
"[bold]Dry run:[/] the following merged commits would be removed:",
)
for m in status.merged:
console.print(f" - {m.title} (#{m.pull_number}, merged)")
console.print(
f"\n{len(status.remaining)} commit(s) would remain in the stack.",
)
Comment thread
jd marked this conversation as resolved.
return

# Fetch and sync
with console.status(f"Fetching {remote}/{base_branch}\u2026"):
await utils.git("fetch", remote, base_branch)

Comment thread
jd marked this conversation as resolved.
with console.status(f"Rebasing onto {remote}/{base_branch}\u2026"):
status = await smart_rebase(
github_server,
token,
trunk=trunk,
branch_prefix=branch_prefix,
author=author,
)

if status.all_merged:
console.print(
f"All commits in the stack have been merged into {base_branch}.\n"
f"You can switch to {base_branch} with: git checkout {base_branch}",
)
elif status.up_to_date:
console.print("Stack is up to date.")
else:
for m in status.merged:
console.print(f" ✓ Dropped: {m.title} (#{m.pull_number})")
console.print(
Comment thread
jd marked this conversation as resolved.
f"Dropped {len(status.merged)} merged commit(s). "
f"{len(status.remaining)} commit(s) remaining in the stack.",
)
Loading
Loading