You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary
### Why?
The git merger landed with REBASE only. `SQUASH_REBASE` and `MERGE` are part of the wire contract SubmitQueue already publishes against, and until they apply here a request naming either is rejected as an invalid request. This adds the two transforming strategies on top of the shared apply machinery.
### What?
**SQUASH_REBASE** applies the step exactly like REBASE — cherry-picking each URI's head commit onto the tip — then collapses the commits the step produced into a single commit. The squash unit is the step, not the request: a multi-step request still lands one commit per step.
Two degenerate cases produce no output rather than an empty commit. If every change in the step was already present the picks moved HEAD nowhere, so there is nothing to squash. If the picks did create commits but their net tree matches the base, the squashed result would be empty, so the intermediates are dropped and the step reports no output. Both keep redelivery idempotent.
**MERGE** creates a `--no-ff` merge commit per URI, which keeps the original commit hashes reachable through second-parent history — the difference that matters versus REBASE, where cherry-picking rewrites them. A SHA already contained in HEAD is skipped rather than merged again. A merge that conflicts aborts the in-progress merge before returning `ErrConflict`, leaving the checkout usable for the next request.
Both strategies join the existing dispatch and inherit the reset/apply/push cycle, contention retry, and dry-run discard unchanged. `isConcreteStrategy` now admits them; PROMOTE remains rejected until the next change in the stack.
Restores `isAncestor` (containment checks for MERGE) and adds `squashMessage`, which synthesizes the squash commit message from the step id and the PRs named by its URIs — only SHAs are on the wire, so there is no upstream message to carry over.
## Test Plan
✅ `bazel test //runway/extension/merger/...` — 2/2 pass (git suite 24.7s)
New cases: SQUASH_REBASE collapsing two stacked URIs into one output, SQUASH_REBASE over an already-landed change producing none, MERGE creating a merge commit for a fresh change, MERGE skipping a SHA already an ancestor of the tip, and the MERGE dry-run path.
Copy file name to clipboardExpand all lines: runway/extension/merger/git/README.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,9 +13,11 @@ The URI carries only the head commit SHA — no base and no commit range. A chan
13
13
| Strategy | What it does | Outputs |
14
14
|---|---|---|
15
15
|`REBASE`| Cherry-picks each URI's head commit onto the tip, in order. A pick already present on the target is rebased out (reported, no output); an empty pick is rolled back. | one revision per newly-created commit |
16
+
|`SQUASH_REBASE`| Applies the step like `REBASE`, then collapses the resulting commits into a single commit (squash unit = the step). | one revision, or none when the step is entirely already-present |
17
+
|`MERGE`| Creates a `--no-ff` merge commit per URI, preserving the original commit hashes in second-parent history. A commit already contained in the tip is skipped. | the merge-commit revision(s) |
16
18
|`DEFAULT`| Resolved to the instance's configured default strategy before any step runs. | per the resolved strategy |
17
19
18
-
`REBASE` is the only strategy implemented so far. `SQUASH_REBASE`, `MERGE`, and `PROMOTE` are defined by the wire contract but not yet applied here — a step naming one is rejected as an invalid request.
20
+
`PROMOTE` is defined by the wire contract but not yet applied here — a step naming it is rejected as an invalid request.
19
21
20
22
## Committing, dry-run, atomicity, contention
21
23
@@ -29,6 +31,6 @@ A merge conflict surfaces as `merger.ErrConflict`; an unusable request (unsuppor
29
31
30
32
## Runtime and identity
31
33
32
-
Every git invocation uses the pinned runtime (explicit executable, exec-path, and template dir) and a scrubbed environment: no ambient configuration, no system or global git config, no interactive prompts. Because that leaves no ambient identity, the committer name and email are injected per-invocation, which the commit-creating `REBASE` strategy requires.
34
+
Every git invocation uses the pinned runtime (explicit executable, exec-path, and template dir) and a scrubbed environment: no ambient configuration, no system or global git config, no interactive prompts. Because that leaves no ambient identity, the committer name and email are injected per-invocation, which the commit-creating strategies (`REBASE`, `SQUASH_REBASE`, `MERGE`) require.
33
35
34
36
Object availability relies on `git fetch <remote>` making the referenced SHAs reachable through the remote's refs. A deployment whose head commits live only under non-fetched refs (for example GitHub `refs/pull/*`) must arrange for those objects to be fetchable.
0 commit comments