feat(rust): port stack push end-to-end + delete Python push.py#1551
Conversation
The Rust binary now serves ``mergify stack drop <COMMIT>...
[--dry-run]`` natively. The Python implementation
(``mergify_cli/stack/drop.py``, its click registration, and
``mergify_cli/tests/stack/test_drop.py``) is removed in the same
PR.
Fourth ``stack`` subcommand to land natively. Builds on the
rebase-todo machinery introduced with ``stack edit``: this slice
adds one ``Action::Drop { shas }`` variant to
``mergify_stack::rebase_todo`` (which deletes the targeted
``pick`` lines from the todo) and one new arm in the
``_internal rebase-todo-rewrite`` subcommand. No new bridge
surface — the existing
``_internal rebase-todo-rewrite --action drop --shas <SHA,…>``
fits the pre-port pattern.
``mergify stack drop <COMMIT>... [--dry-run]``:
1. Resolves the trunk and walks the stack (shared with
``stack edit``).
2. Matches each ``<COMMIT>`` argument against the walker by SHA
prefix or Change-Id prefix. Duplicate prefixes (two args
resolving to the same commit) are rejected with
``InvalidState``.
3. ``--dry-run`` short-circuits with a ``Drop plan:`` table and
exits 0 without touching git.
4. Otherwise spawns ``git rebase -i <base>`` with
``GIT_SEQUENCE_EDITOR`` pointing at the binary's
``_internal rebase-todo-rewrite --action drop --shas
<comma-separated>``; the todo rewriter removes the targeted
``pick`` lines and git replays the rebase.
The rewriter requires every targeted SHA to match exactly one
``pick`` line — a partial match aborts the rebase with
``InvalidState`` listing the missing SHAs, so the user never
ends up with a half-applied drop.
End-to-end coverage in
``crates/mergify-cli/tests/stack_drop.rs`` (6 cases mirroring
the deleted Python ``test_drop.py``): drop middle, drop
multiple, drop by Change-Id, ``--dry-run`` is a no-op, unknown
prefix exits non-zero, duplicate prefix rejected. The pure
transformer is covered by 5 new ``rebase_todo`` unit tests
(happy path, multi-target, no-match, partial match,
empty-target).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I22a924187bf504fd13515f0c8e5512e57b6dff40
The Rust binary now serves ``mergify stack fixup <COMMIT>...
[--dry-run]`` natively. The Python implementation
(``stack_fixup`` in ``mergify_cli/stack/squash.py``, its click
registration, and ``TestStackFixup`` in
``mergify_cli/tests/stack/test_squash.py``) is removed in the
same PR; ``stack_squash`` stays put.
Builds on the rebase-todo machinery: this slice adds
``Action::Fixup { shas }`` to ``mergify_stack::rebase_todo``
(rewrites the targeted ``pick`` lines to ``fixup``, preserving
the diff so it folds into the previous commit) and an ``Fixup``
variant on the ``_internal rebase-todo-rewrite --action`` enum.
Validation mirrors Python: each ``<COMMIT>`` argument resolves
against the stack walker by SHA-prefix or Change-Id prefix;
duplicates are rejected; the first commit of the stack is
rejected because it has no parent inside the stack to fold
into.
End-to-end coverage in
``crates/mergify-cli/tests/stack_fixup.rs`` (4 cases): folds B
into A leaving [A, C], dry-run is a no-op, first-commit fixup
is rejected, unknown prefix exits non-zero. The pure
transformer is covered by 3 new ``rebase_todo`` unit tests.
The pick→fixup transform shares its implementation with a new
generalised ``rewrite_replace_verb`` helper in ``rebase_todo``
— ``reword`` will slot into the same shape when that port
follows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I7a139112fd95ef2b1e5bd2763cb62b0b9b2f5879
The Rust binary now serves ``mergify stack reword <COMMIT>
[-m <msg>] [--dry-run]`` natively. The Python implementation
(``mergify_cli/stack/reword.py``, its click registration, and
``mergify_cli/tests/stack/test_reword.py``) is removed in the
same PR.
Two new ``rebase_todo`` actions back the two flavors:
- ``Action::Reword { sha }`` rewrites the target ``pick`` line as
``reword``. Git pauses on the commit and runs
``git commit --amend``, opening ``$GIT_EDITOR``. Used when no
``-m`` argument is given.
- ``Action::ExecAfter { sha, command }`` injects an ``exec
<command>`` line directly after the target ``pick`` line. Used
with ``-m``: the message is written to a tempfile and the
exec runs ``git commit --amend -F <file>`` while HEAD still
points at the target commit, so any ``prepare-commit-msg``
hook re-attaches the Change-Id. Tempfile is intentionally
leaked so ``git rebase --continue`` after a conflict can still
find it. The ``ExecAfter`` shape lands here because it is also
the squash-with-custom-message machinery, so the next slice
reuses it for free.
``mergify stack reword <COMMIT> [-m <msg>] [--dry-run]``:
1. Resolves the trunk and walks the stack (shared with the
other rebase-family commands).
2. Matches the ``<COMMIT>`` argument by SHA-prefix or Change-Id
prefix.
3. ``--dry-run`` short-circuits with a ``Reword plan:`` line
showing ``reword`` (no ``-m``) or ``amend`` (``-m`` given) so
the dry-run text matches Python.
4. Otherwise spawns ``git rebase -i <base>`` with
``GIT_SEQUENCE_EDITOR`` pointing at the binary's
``_internal rebase-todo-rewrite`` subcommand.
End-to-end coverage in
``crates/mergify-cli/tests/stack_reword.rs`` (3 cases): ``-m``
replaces subject + body, ``--dry-run`` is a no-op, unknown
prefix exits non-zero. The pure transformer is covered by 4
new ``rebase_todo`` unit tests (reword happy path / no match;
exec-after happy path / no match).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I39f31d0f8b31b890a3a390e928f7342ca04cd7b7
The Rust binary now serves ``mergify stack reorder <COMMIT>...
[--dry-run]`` and ``mergify stack move <COMMIT> <POSITION>
[<TARGET>] [--dry-run]`` natively. The Python implementations
(``mergify_cli/stack/reorder.py::stack_reorder``,
``mergify_cli/stack/move.py``, their click registrations, and
``mergify_cli/tests/stack/test_{reorder,move}.py``) are removed.
Both commands reduce to the same rebase-todo transformation —
they only differ in how they compute the desired pick order —
so this slice ships both behind a single new
``Action::Reorder { ordered_shas }`` variant on
``mergify_stack::rebase_todo``. The transformer rebuilds the
todo with picks in the requested order and appends every
non-pick line (comments, blanks, ``exec`` annotations) at the
end, mirroring Python's ``run_action_rebase`` bucketing.
``mergify stack reorder``: the ``<COMMIT>...`` arguments list
every stack commit in the desired new order. Validation rejects
duplicate prefixes, prefixes that don't resolve, and length
mismatches. When the requested order already matches the
current order, we short-circuit with
``Stack is already in the requested order`` and don't spawn a
rebase.
``mergify stack move``: a thin wrapper that computes the new
order from ``<COMMIT> <POSITION> [<TARGET>]`` and then delegates
to the same ``Action::Reorder`` path. ``POSITION`` is one of
``first``, ``last``, ``before``, ``after``; ``before`` and
``after`` require a ``<TARGET>``, ``first`` and ``last`` reject
one.
The Python ``stack_reorder``/``stack_move`` functions are gone,
but ``mergify_cli/stack/reorder.py`` itself is kept as a
header file: it still hosts the ``get_stack_commits``,
``match_commit``, ``run_scripted_rebase``,
``run_action_rebase`` and ``display_*`` helpers that the
remaining Python ``stack_squash`` (the next slice) still depends
on. When ``stack squash`` follows, the whole file goes.
Also fixes a parallel-test flake in
``crates/mergify-cli/tests/stack_*.rs``: each integration test
file now spawns ``git`` through a local ``isolated_git()``
helper that sets ``GIT_CONFIG_GLOBAL=/dev/null`` and
``GIT_CONFIG_NOSYSTEM=1``, matching the lib-test fixture
behavior. Symptom was sporadic ``git`` failures when many test
binaries ran their per-test fixtures concurrently.
End-to-end coverage in
``crates/mergify-cli/tests/stack_reorder.rs`` (7 cases): reorder
to explicit sequence, already-in-order is a no-op, count
mismatch errors, move to ``first``, move ``before`` target, move
``before`` without target errors, move ``first`` with target
errors. The pure transformer adds 3 new ``rebase_todo`` unit
tests (happy path, count mismatch, unknown SHA).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I8b603412b742c1a863f5c807b05c954635dd0e94
The Rust binary now serves ``mergify stack squash <SRC>... into <TARGET> [-m <msg>] [--dry-run]`` natively. With this slice, the entire ``stack`` rebase family is native: ``edit``, ``drop``, ``fixup``, ``reword``, ``reorder``, ``move``, ``squash``. Final cleanup: ``mergify_cli/stack/squash.py``, ``mergify_cli/stack/reorder.py`` (the helpers ``squash.py`` was still using), and their tests (``test_squash.py``, ``test_squash_cli.py``) are removed. ``mergify_cli/stack/cli.py`` loses its ``squash`` click registration and its ``_parse_squash_tokens`` helper. The rebase-todo machinery grows one new variant — ``Action::Squash`` — that combines what ``Reorder`` and ``Fixup`` did separately plus an optional ``ExecAfter``: - ``ordered_shas`` — the full new pick order (target's neighbours rearranged so all sources sit directly after it). - ``fixup_shas`` — the subset whose verb flips from ``pick`` to ``fixup``. - ``exec_after_sha`` + ``exec_command`` — when ``-m`` is given, an ``exec git commit --amend -F <file>`` is injected right after the last fixed-up source. The amend runs while HEAD still points at the combined target commit so ``prepare-commit-msg`` re-attaches the Change-Id. ``mergify stack squash``: 1. Resolves the trunk and walks the stack. 2. Parses ``SRC... into TARGET`` — the ``into`` keyword splits the positional list. Mirrors the Python parser: exactly one ``into``, at least one source before, exactly one target after. 3. Validates: target is not among the sources; no source SHA appears twice. 4. Builds the new order: keep non-source commits in their original positions, insert all sources directly after target in the order they were listed. 5. ``--dry-run`` short-circuits with the plan; otherwise spawns ``git rebase -i <base>`` with the squash sequence editor. 6. With ``-m``: writes the message to a leaked tempfile and passes it through ``--sha``/``--command`` on the ``rebase-todo-rewrite`` self-invocation. End-to-end coverage in ``crates/mergify-cli/tests/stack_squash.rs`` (6 cases): single source into target keeps target's message, custom ``-m`` replaces subject + body, dry-run is a no-op, source-equals- target errors, missing ``into`` errors, multi-source folds all into target. The pure transformer adds 3 new ``rebase_todo`` unit tests (happy path, exec-after injection, count mismatch). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Change-Id: I380f216ca57ebf78c8fb6c62e3a5af48a9dfdd55
The Rust binary now serves ``mergify stack checkout <NAME>`` natively. ``mergify_cli/stack/checkout.py``, ``mergify_cli/tests/stack/test_checkout.py``, and the click registration are removed in the same PR. First GitHub-API-backed stack subcommand to land natively. The slice introduces a shared ``mergify_stack::stack_context`` module that resolves the per-command preamble — repository owner/repo, GitHub API server URL, default branch prefix — that ``sync``, ``list``, ``open``, and ``push`` will all reuse when their ports follow. ``mergify_stack::stack_context``: - ``parse_slug`` — owner/repo extraction handling both ``https://github.com/o/r.git`` and ``git@github.com:o/r.git`` shapes, port of ``utils.get_slug``. - ``resolve_repo`` — explicit ``--repository OWNER/REPO`` takes precedence; otherwise we parse ``git config remote.<remote>.url``. - ``resolve_github_server`` — reads ``mergify-cli.github-server`` git config, falls back to ``https://api.github.com/``, mirrors Python's https-coercion + ``/api/v3`` suffix for GitHub Enterprise. - ``resolve_default_branch_prefix`` — reads ``mergify-cli.stack-branch-prefix``, falls back to ``stack/<author>``. ``mergify_stack::commands::checkout``: 1. Normalises the ``NAME`` argument — strip any trailing ``/Ixxxx…`` Change-Id suffix (via the new ``change_id::strip_branch_suffix`` helper) and any leading ``<branch_prefix>/`` so users can paste a leaf branch ref verbatim. 2. Searches GitHub for the stack's PRs via the existing ``mergify_stack::remote_changes::get_remote_changes``. 3. Builds the open-PR chain from root to leaf — root is the PR whose ``base.ref`` doesn't start with the stack branch prefix; we walk up via ``head.ref`` → ``base.ref`` linking. Two roots surface as ``InvalidState``, matching Python. 4. ``--dry-run`` short-circuits with the printed chain. Otherwise ``git fetch <remote> <leaf-head>``, ``git checkout -b <local> <remote>/<leaf-head>``, ``git branch --set-upstream-to=<remote>/<root-base>``. Binary handler resolves the trunk via ``--trunk REMOTE/BRANCH`` or the existing ``mergify_stack::trunk::get_trunk`` fallback, the token via ``mergify_core::auth::resolve_token``, and the author via either ``--author`` or ``GET /user`` against the resolved client. Coverage in ``crates/mergify-stack/src/commands/checkout.rs`` (7 tests): the pure chain builder (root-to-leaf, closed-PR filter, two-root detection, empty case) plus three end-to-end ``run`` tests against wiremock — no-stacked-prs, dry-run returns chain without touching git, name normalisation strips both the Change-Id suffix and the branch prefix. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Change-Id: I71e74cae43846d9708bef9c57d32ba81b8499edc
The Rust binary now serves ``mergify stack sync [--dry-run]``
natively. The Python ``stack_sync`` entry point and
``test_sync.py`` are gone; the click registration is gone too.
The rest of ``mergify_cli/stack/sync.py`` (``SyncStatus``,
``get_sync_status``, ``smart_rebase``, ``_write_drop_script``)
is kept for now because the still-Python ``stack push`` calls
``smart_rebase`` directly. The whole file is queued for deletion
with the ``stack push`` port.
New infrastructure that the remaining slices (``list``,
``push``, ``open``) will reuse:
- ``mergify_stack::sync_status`` — sync-specific classifier that
walks the local stack commits, matches each to a remote PR by
``Change-Id`` (with the cross-prefix tolerance Python's
``pop_remote_change`` had), and buckets them into
``MergedCommit`` (closed-and-merged AND head SHA matches the
local commit) and ``RemainingCommit`` (everything else). The
full create/update/skip-up-to-date classifier needed by
``stack push``/``stack list`` lands with those slices.
- ``mergify_stack::stack_context::check_local_branch`` — rejects
local branches whose shape matches the auto-generated
``<prefix>/.../<slug>--<hex8>`` or legacy ``/I<40hex>`` forms.
- ``MERGIFY_GITHUB_SERVER`` env var on
``stack_context::resolve_github_server`` — bypasses git config
and the unconditional ``https://`` coercion, so integration
tests can point at a wiremock server speaking plain ``http``.
``mergify stack sync``:
1. Validates the local branch isn't an auto-generated leaf branch
and that it isn't the trunk itself.
2. Resolves ``user/repo``, the trunk, and the
``git merge-base --fork-point`` (falling back to plain
``merge-base`` for sandboxes with no reflog).
3. Searches GitHub for the stack PRs and classifies each local
commit via ``sync_status::classify``.
4. ``--dry-run`` prints a plan and exits 0. Otherwise:
- When every commit is merged or none are, ``git pull --rebase``.
- Mixed case: ``git rebase -i <trunk>`` with
``GIT_SEQUENCE_EDITOR`` pointing at this binary's
``_internal rebase-todo-rewrite --action drop --shas <merged>``
— single rebase pass, no separate drop step.
End-to-end coverage in
``crates/mergify-cli/tests/stack_sync.rs`` (3 cases against a
real binary + wiremock + real git): up-to-date when no PRs are
merged, dry-run lists merged commits, validation rejects
auto-generated branch names. The classifier adds 7 unit tests
(merged / amended-after-merge / open / no-pr / cross-prefix
match / ``all_merged`` / ``up_to_date`` predicates).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I7f75be875289707f492ecbf59f6bcb6530554f24
The Rust binary now serves ``mergify stack list [--json]
[--verbose]`` and ``mergify stack open [<commit>]`` natively.
The Python implementations
(``mergify_cli/stack/list.py``,
``mergify_cli/stack/list_schema.py``,
``mergify_cli/stack/open.py``, the click registrations, and
``test_{list,open}.py``) are removed.
New shared infrastructure that the remaining slices
(``stack push``) will reuse:
- ``mergify_stack::changes::classify`` — full per-commit
classifier that buckets each local commit as
``Create`` / ``Update`` / ``SkipMerged`` / ``SkipUpToDate``,
the same shape Python's ``get_changes`` returns. Also
surfaces orphan PRs (open remote changes that don't match
any local commit). The merged-vs-remaining ``sync_status``
module stays for ``stack sync`` since sync only needs the
bucket; the full classifier here is what ``list``, ``open``,
and ``push`` need.
- ``StackContext`` + ``resolve_stack_context`` in the binary
— bundles token / GitHub server / repo slug / author /
branch prefix / trunk resolution that every API-backed stack
subcommand runs. ``checkout`` and ``sync`` will pick this up
in a follow-up cleanup; for now they keep their inline
copies.
``mergify stack list``:
1. Runs the shared stack-context preamble.
2. Walks the local stack via ``local_commits::read``, fetches
the matching remote PRs via ``remote_changes::get_remote_changes``,
classifies via ``changes::classify``.
3. For each entry, computes the display status
(``open`` / ``draft`` / ``merged`` / ``no_pr``) from the
classifier's action plus the PR's ``merged_at`` / ``draft``
fields.
4. Optionally fans out per-PR ``/check-runs`` and ``/reviews``
fetches and folds them into the entry's ``ci_*`` / ``review_*``
fields. Sequential (not concurrent) — GitHub's secondary
rate limit on the same endpoint pool starts firing around
~80 concurrent calls, and a typical stack is well under
that. ``stack open`` passes ``include_status=false`` to
skip this.
5. Renders either JSON (``serde_json::to_string_pretty`` of
``StackListOutput``, byte-compatible with the pre-port
``--json`` output) or a plain-text table.
``mergify stack open``:
1. Runs ``stack::list`` with ``include_status=false``.
2. ``None`` commit → leaf entry (the Python interactive picker
defaulted there too; we leave the picker out of the port for
now).
3. Explicit commit → ``git rev-parse --verify`` then look up
the matching ``StackListEntry``.
4. Hand the ``html_url`` to the OS opener: ``open`` (macOS),
``xdg-open`` (other Unix), ``cmd /C start ""`` (Windows).
5. PR-less commits surface ``StackNotFound`` with a hint to
run ``stack push`` first.
Coverage: 7 ``changes::tests`` for the classifier (create /
skip-merged / amended-after-merge / skip-up-to-date / update /
orphan / closed-unmatched), 6 ``commands::list::tests`` for
the CI / review status reducers (passing / failing-over-pending
/ pending / approved / changes-requested / pending-from-comments).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I0a12ccb23061dceba00b94cbddf93df388452657
The Rust binary now serves both ``mergify stack hooks`` (status
+ ``--setup``) and ``mergify stack setup`` (alias for
``hooks --setup``, plus ``--check``). The Python implementation
(``mergify_cli/stack/setup.py``, the click registrations, the
``test_setup.py`` suite, and the hook resource tree under
``mergify_cli/stack/hooks/``) is removed. The hook scripts and
wrappers are renamed to ``crates/mergify-stack/hooks/{scripts,
wrappers}/`` and embedded into the binary via ``include_str!``.
``mergify_stack::commands::setup``:
- Mirrors Python's on-disk layout exactly so a checkout
upgraded from a Python install is recognised without
re-installation: ``.git/hooks/<hook>`` is the thin wrapper
(user-editable below the marker comment),
``.git/hooks/mergify-hooks/<hook>.sh`` is the managed script
(always upgradable).
- ``WrapperStatus`` detection mirrors ``_get_wrapper_status``:
``mergify-hooks`` + ``<hook>.sh`` substring pair = ours;
legacy heuristics (``Change-Id: I${random}`` on
``commit-msg``, ``is_amend_with_m_flag`` on
``prepare-commit-msg``) match the pre-sourcing-architecture
installs so ``--force`` knows to migrate them.
- ``ensure_notes_display_ref`` adds the
``notes.displayRef = refs/notes/mergify/*`` git config line
so ``git log`` surfaces the amend notes attached by
``mergify stack note``.
- ``HookAction`` log values let the binary handler render the
same per-hook lines Python printed
(``Installing hook wrapper: …`` /
``Updating managed hook script: mergify-hooks/<hook>.sh`` /
``Migrating legacy hook to new format: …`` /
``Found legacy hook: …``).
The (still-Python) ``stack push`` pre-flight used to call
``stack_setup_mod.stack_setup()`` and
``stack_setup_mod.ensure_hooks_updated()`` directly. With
``setup.py`` gone, those two calls in ``mergify_cli/stack/cli.py``
shell out to ``mergify stack setup`` instead — same behaviour,
goes through the native installer. The whole pre-flight block
goes away when ``stack push`` itself lands native.
Coverage: 6 lib tests over a tempdir-backed git repo —
``status`` reports all-missing on a fresh repo; ``install``
writes wrappers + scripts and is idempotent on a second run;
legacy wrappers report ``WrapperLegacyNeedsForce`` without
``--force`` and ``WrapperMigrated`` with it; user-written
wrappers (no ``mergify-hooks`` sentinel) are left untouched;
the ``notes.displayRef`` config line is added on first install.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I12f4b4af3bf3dcae8002687f03eb55876c61170b
End-to-end native port of `mergify stack push`: wires every
leaf module, the orchestrator, and the binary dispatch in one
self-consistent change so `mergify stack push` works on the
Rust binary and the Python implementation gets deleted in the
same commit.
New `mergify-stack` modules (each is a faithful port from
`mergify_cli/stack/`):
- `change_type` — patch-id-based rebase-vs-content
classification + `refs/pull/<n>/head` fetch helper.
- `stack_comment` — the "this PR is part of a stack" sticky
comment renderer + header recogniser. JSON marker stays
byte-for-byte compatible with historic comments.
- `replay` — `git merge-tree` + `git diff-tree` + the HTTP
upload (`POST /git/trees` + `POST /git/commits`) that
materialise the rebase-aware revision-history compare URL.
- `revision_history` — the "Revision history" sticky comment
with parse + append + render round-tripping byte-for-byte
against the Python implementation.
- `approvals` — the rebase/no-rebase decision: skip the
rebase when PRs are already approved (so reviews aren't
dismissed) unless the bottom of the stack has a real merge
conflict with trunk.
- `notes_push` — `git fetch`/`git push` plumbing for
`refs/notes/mergify/stack` + the per-PR refspecs that
`stack push` lands atomically with `--force-with-lease`.
- `rebase_log` — pure formatters for the three rebase
narration log lines.
- `push_helpers` — `format_pull_description` (strip
Change-Id + stale Depends-On, append fresh Depends-On) +
`build_change_tasks` (the per-PR dependency graph).
- `pr_upsert` — per-PR Create (POST /pulls) and Update
(PATCH /pulls/<n>) + orphan branch teardown.
- `comment_upsert` — per-PR sticky-comment upserters: stack
comment (skip-when-single-PR) + revision history (parse +
append + recover-from-corrupt).
- `plan` — the layer above `classify` that applies the
`--next-only` / `--only-update-existing-pulls` overrides
and resolves `dest_branch` / `base_branch`.
- `changes::Action` extended with `SkipCreate` /
`SkipNextOnly` so the planner can surface
would-be-created PRs without opening any.
- `commands::push` — the end-to-end orchestrator wiring
every leaf above. Runs per-PR upserts sequentially
(typical 2-5 PR stacks make the latency difference
negligible vs. the GitHub round-trip cost, and the
simpler code avoids a `tokio::sync::Notify` graph).
Binary wiring (`crates/mergify-cli/src/main.rs`):
- `NativeCommand::StackPush(StackPushOpts)` variant +
`StackPushCli` clap struct mirroring the Python click
option surface 1:1 (`--skip-rebase`, `--force-rebase`,
`--next-only`, `--dry-run`, `--draft`, `--keep-pull-
request-title-and-body`, `--author`, `--trunk`,
`--branch-prefix`, `--only-update-existing-pulls`,
`--no-revision-history`, `--no-verify`).
- `dispatch_stack` "push" arm routing through to
`commands::push::run`.
- `("stack", "push")` added to `NATIVE_COMMANDS`.
Python deletion:
- `mergify_cli/stack/push.py` (the orchestrator, 1394 LOC).
- `mergify_cli/stack/{sync,approvals,replay,changes}.py` —
helpers only imported by push.py and its tests.
- `mergify_cli/tests/stack/test_{push,replay,approvals}.py`.
- `mergify_cli/stack/cli.py` — the `push` command + the
`stack_push_mod` import. The click group itself stays
so `python -m mergify_cli stack` keeps printing help
until the package is removed wholesale in the follow-up.
Adds `chrono` (with `clock` feature) and `tokio` (with
`time` feature) + `url` to `mergify-stack`'s prod deps to
support the revision-history timestamps, the
mergeable-retry sleep, and the wiremock test base-URL
parse.
End-to-end verified against the Python implementation on
real fixtures (stack-comment + revision-history bodies
match byte-for-byte; planner action overrides match the
Python `get_changes` semantics including the
`--next-only` × `--only-update-existing-pulls` interaction
at idx 0).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I1b2c30509153a19071cf495155a03e9a2e616e87
Member
Author
|
This pull request is part of a Mergify stack:
|
Contributor
Merge ProtectionsYour pull request matches the following merge protections and will not be merged until they are valid. 🔴 👀 Review RequirementsWaiting for
This rule is failing.
🔴 🔎 ReviewsWaiting for
This rule is failing.
🟢 🤖 Continuous IntegrationWonderful, this rule succeeded.
🟢 Enforce conventional commitWonderful, this rule succeeded.Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 📕 PR descriptionWonderful, this rule succeeded.
|
56cb890 to
e5c33cc
Compare
Contributor
|
@jd this pull request is now in conflict 😩 |
Member
Author
|
Superseded by #1553 — restructured into a single-branch stack via mergify stack. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
End-to-end native port of
mergify stack push: wires everyleaf module, the orchestrator, and the binary dispatch in one
self-consistent change so
mergify stack pushworks on theRust binary and the Python implementation gets deleted in the
same commit.
New
mergify-stackmodules (each is a faithful port frommergify_cli/stack/):change_type— patch-id-based rebase-vs-contentclassification +
refs/pull/<n>/headfetch helper.stack_comment— the "this PR is part of a stack" stickycomment renderer + header recogniser. JSON marker stays
byte-for-byte compatible with historic comments.
replay—git merge-tree+git diff-tree+ the HTTPupload (
POST /git/trees+POST /git/commits) thatmaterialise the rebase-aware revision-history compare URL.
revision_history— the "Revision history" sticky commentwith parse + append + render round-tripping byte-for-byte
against the Python implementation.
approvals— the rebase/no-rebase decision: skip therebase when PRs are already approved (so reviews aren't
dismissed) unless the bottom of the stack has a real merge
conflict with trunk.
notes_push—git fetch/git pushplumbing forrefs/notes/mergify/stack+ the per-PR refspecs thatstack pushlands atomically with--force-with-lease.rebase_log— pure formatters for the three rebasenarration log lines.
push_helpers—format_pull_description(stripChange-Id + stale Depends-On, append fresh Depends-On) +
build_change_tasks(the per-PR dependency graph).pr_upsert— per-PR Create (POST /pulls) and Update(PATCH /pulls/) + orphan branch teardown.
comment_upsert— per-PR sticky-comment upserters: stackcomment (skip-when-single-PR) + revision history (parse +
append + recover-from-corrupt).
plan— the layer aboveclassifythat applies the--next-only/--only-update-existing-pullsoverridesand resolves
dest_branch/base_branch.changes::Actionextended withSkipCreate/SkipNextOnlyso the planner can surfacewould-be-created PRs without opening any.
commands::push— the end-to-end orchestrator wiringevery leaf above. Runs per-PR upserts sequentially
(typical 2-5 PR stacks make the latency difference
negligible vs. the GitHub round-trip cost, and the
simpler code avoids a
tokio::sync::Notifygraph).Binary wiring (
crates/mergify-cli/src/main.rs):NativeCommand::StackPush(StackPushOpts)variant +StackPushCliclap struct mirroring the Python clickoption surface 1:1 (
--skip-rebase,--force-rebase,--next-only,--dry-run,--draft,--keep-pull- request-title-and-body,--author,--trunk,--branch-prefix,--only-update-existing-pulls,--no-revision-history,--no-verify).dispatch_stack"push" arm routing through tocommands::push::run.("stack", "push")added toNATIVE_COMMANDS.Python deletion:
mergify_cli/stack/push.py(the orchestrator, 1394 LOC).mergify_cli/stack/{sync,approvals,replay,changes}.py—helpers only imported by push.py and its tests.
mergify_cli/tests/stack/test_{push,replay,approvals}.py.mergify_cli/stack/cli.py— thepushcommand + thestack_push_modimport. The click group itself staysso
python -m mergify_cli stackkeeps printing helpuntil the package is removed wholesale in the follow-up.
Adds
chrono(withclockfeature) andtokio(withtimefeature) +urltomergify-stack's prod deps tosupport the revision-history timestamps, the
mergeable-retry sleep, and the wiremock test base-URL
parse.
End-to-end verified against the Python implementation on
real fixtures (stack-comment + revision-history bodies
match byte-for-byte; planner action overrides match the
Python
get_changessemantics including the--next-only×--only-update-existing-pullsinteractionat idx 0).
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com