upmerge
/upmerge
Przenosi poprawkę php-src z najniższej gałęzi do wszystkich wyższych utrzymywanych gałęzi, z osobnym commitem scalającym na każdy krok.
- Autor
- pronskiy ↗
- Repozytorium
- php-src-developer ↗
- Licencja
- MIT
- Ostatnia zmiana repozytorium
- 26.05.2026
Ocena i bezpieczeństwo
- Pliki w paczce
- 1
- Skrypty
- 0
- Komentarze HTML
- 0
- Adresy URL w SKILL.md
- 0
Brak sygnałów. Repozytorium zmienione 26.05.2026, bez odwołań do wycofanych modeli i API.
MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.
Co robi i kiedy
Przenosi poprawkę php-src z najniższej gałęzi do wszystkich wyższych utrzymywanych gałęzi, z osobnym commitem scalającym na każdy krok.
Wyzwalacze z opisu
Oryginalny opis (EN)
Propagate a php-src bug fix from its lowest fix branch up through every higher maintained branch, applying the per-branch NEWS edit and producing one merge commit per hop. Use when the user says "upmerge", "merge up", "propagate fix to higher branches", "forward-port the fix", "/upmerge", or otherwise wants to mirror a fix that landed on a lower branch onto all newer maintained branches per CONTRIBUTING.md. Defaults to dry-run; `--execute` performs the merges locally. Never pushes, never fetches, never touches the remote.
Jak działa
- Inputs
- What it produces
- Workflow
- UPMERGE.md schema
- Boundaries
- Edge cases
- Quality checklist
- Out of scope
Podgląd SKILL.md
---
name: upmerge
description: >-
Propagate a php-src bug fix from its lowest fix branch up through every
higher maintained branch, applying the per-branch NEWS edit and producing
one merge commit per hop. Use when the user says "upmerge", "merge up",
"propagate fix to higher branches", "forward-port the fix",
"/upmerge", or otherwise wants to mirror a fix that landed on a lower
branch onto all newer maintained branches per CONTRIBUTING.md. Defaults to
dry-run; `--execute` performs the merges locally. Never pushes, never
fetches, never touches the remote.
---
# upmerge
Final, optional step of the bug-fix flow. Owns no SPEC.md phase; produces its own one-shot living doc `UPMERGE.md`. Reads the fix commit and `target_branch` from SPEC.md (or accepts explicit args), computes the merge chain dynamically, plans NEWS edits per branch, and — only on `--execute` — applies the merges locally.
## Inputs
1. **SPEC.md** in the task directory resolved per `_shared/task-dir.md`. From it:
- `type: bug`, `status: done` (Verification phase done — guardrail).
- `target_branch: PHP-x.y` (the source branch — the "from" of the chain).
- `linked:` → GH issue number for the NEWS entry.
- Fix commit SHA recovered from the Progress Log's `bug-fix` entry, or via `git -C $PHP_SRC_DIR log --grep "Closes GH-<N>" -1 --pretty=%H` on `target_branch`.
2. **Or** explicit flags when SPEC.md is absent: `--from PHP-x.y --commit <sha> --issue <N>`.
3. **`$PHP_SRC_DIR`** resolved per `_shared/php-src-detection.md`.
4. **Mode flag**: default dry-run; `--execute` to perform merges.
## What it produces
- `UPMERGE.md` in the same task dir as SPEC.md. Schema below.
- On `--execute`: one merge commit per branch hop in `$PHP_SRC_DIR`, with NEWS spliced on each upper branch. Tree left checked out on the topmost branch in the chain.
No `git push`. Ever.
## Workflow
### 1. Pre-guardrails
Refuse on any of:
- Working tree + index not clean:
```bash
git -C "$PHP_SRC_DIR" diff --quiet && git -C "$PHP_SRC_DIR" diff --cached --quiet
```
- `--from` branch missing locally: `git -C "$PHP_SRC_DIR" rev-parse --verify <from>`.
- `--from == master`: nothing above master. Refuse with: `master is the top of the chain; nothing to upmerge.`
- Fix commit not reachable from `--from`: `git merge-base --is-ancestor <sha> <from>` must succeed.
- Fix commit ALREADY in the next-higher branch (already upmerged): the negative form `! git merge-base --is-ancestor <sha> <next>` must succeed. Otherwise refuse with: `Fix already present in <next>; nothing to do.`
- Fix commit didn't touch NEWS on `--from`:
```bash
git -C "$PHP_SRC_DIR" show --stat <sha> | grep -q '^ NEWS '
```
If empty: refuse with: `Fix commit <sha> did not touch NEWS on <from>. Add the NEWS entry on <from> first, then re-run.`
- SPEC.md (if present) Verification phase not `done`. Refuse with: `Run /bug-verify before /upmerge.`
### 2. Resolve inputs
- Resolve `$PHP_SRC_DIR` per `_shared/php-src-detection.md`.
- Resolve task dir per `_shared/task-dir.md`. If SPEC.md exists, read frontmatter → `target_branch`, `linked`, `status`.
- Recover fix commit SHA (Progress Log scan, fallback to `git log --grep`).
- Without SPEC.md, require `--from`, `--commit`, `--issue` all three. Refuse if any missing.
### 3. Compute the merge chain dynamically
Read the maintained-branch list per `_shared/branch-detection.md` algorithm § 1. Intersect with locally-present branches:
```bash
git -C "$PHP_SRC_DIR" for-each-ref --format='%(refname:short)' refs/heads/
```
Take every entry strictly above `<from>` ordered low → high. Example with `<from>=PHP-8.3`:
```
PHP-8.3 (source) → PHP-8.4 → PHP-8.5 → master
```
If any branch in the list is missing locally: stop with `Branch <X> from matrix.php is not checked out locally. Either: git checkout -b <X> origin/<X>, or pass --skip-missing.` Don't auto-fetch (forbidden by the agent's git etiquette).
### 4. Extract the NEWS entry to propagate
From the fix commit on `<from>`, isolate the lines it added to NEWS:
```bash
git -C "$PHP_SRC_DIR" show "<sha>" -- NEWS \
| awk '/^@@/{in_hunk=1; next} in_hunk && /^\+[^+]/ { sub(/^\+/, ""); print }'
```
Identify the section header those `+` lines live under by scanning the same patch hunk upward for the nearest `- <Component>:` heading. Capture both the section header and the entry body (typically two-three lines starting with ` . Fixed bug GH-NNNNN ...`).
### 5. Plan — write UPMERGE.md (always)
Write `UPMERGE.md` to the task dir (schema below). Records:
- Frontmatter: `type: upmerge`, `status: planned`, `source_branch`, `source_commit`, `issue`, `created`.
- Chain (low → high).
- NEWS entry section + body extracted in step 4.
- One hop block per `(from, to)` pair, all `status: todo`, with the proposed merge subject `Merge branch '<from>'`.
- Build sanity check entry for the chain head (`master-build`), `status: todo`.
If `UPMERGE.md` already exists for this same `source_commit`: re-read it and resume at the first non-`done` hop (same resume algorithm as SPEC.md). If `source_commit` differs (re-planning a different fix): back up to `UPMERGE.md.bak` and rewrite.
**Stop here in dry-run mode.** Report the planned chain and the NEWS entry. Tell the user to re-run with `--execute` to apply.
### 6. Execute (only when `--execute`)
For each `(from, to)` pair in the chain, low → high:
```bash
git -C "$PHP_SRC_DIR" checkout "<to>"
git -C "$PHP_SRC_DIR" merge --no-ff --no-commit -m "Merge branch '<from>'" "<from>"
```
`--no-ff` matches the php-src convention visible in `git log --merges --first-parent` on master. `--no-commit` lets us land the NEWS resolution before the merge commit closes.
#### NEWS conflict resolution (expected)
```bash
# Take the upper branch's NEWS as authoritative
git -C "$PHP_SRC_DIR" checkout --ours -- NEWS
```
Then splice the propagated entry under the matching `- <Component>:` section inside the upper branch's unreleased header (the first line shaped like `?? ??? ????, PHP X.Y.Z`). Use `awk` (not `sed` — multi-line range matching is fragile):
```bash
awk -v section='- Core:' -v entry="$ENTRY_TEXT" '
BEGIN { spliced = 0 }
$0 == section && !spliced { print; in_sec=1; next }
in_sec && /^- / { print entry; print ""; print; in_sec=0; spliced=1; next }
in_sec && /^$/ && !spliced { print entry; print; in_sec=0; spliced=1; next }
{ print }
END {
if (!spliced) {
# section header not present in upper branch — append a new one
print ""; print section; print entry
}
}
' "$PHP_SRC_DIR/NEWS" > /tmp/news.new && mv /tmp/news.new "$PHP_SRC_DIR/NEWS"
git -C "$PHP_SRC_DIR" add NEWS
```
#### Non-NEWS conflicts
Stop. Mark the current hop `blocked` in UPMERGE.md with `git status` output and exact recovery commands:
```
Conflict outside NEWS — not auto-resolvable. To abort:
git -C "$PHP_SRC_DIR" merge --abort
Resolve manually, then re-run /upmerge --execute to continue.
```
Do not attempt content-aware resolution outside NEWS.
#### Finalise the hop
```bash
git -C "$PHP_SRC_DIR" commit --no-edit
git -C "$PHP_SRC_DIR" rev-parse HEAD
```
Record the new merge-commit SHA under the hop in UPMERGE.md. Mark the hop `done`. Append one Progress Log line:
```
YYYY-MM-DD HH:MM — upmerge — hop <from> → <to> done (SHA <short>)
```
Loop to the next hop with `(to)` becoming the new `(from)`.
### 7. Post-guardrail — build at chain head
After every hop is `done`, run one build at the topmost branch (the tree is already checked out there):
```bash
cd "$PHP_SRC_DIR" && make -j$(nproc)
```
Per-hop `make test` is **not** run — too slow (≥15 min × N hops) and `/bug-verify` already established correctness on the source branch. A build pass at the top catches header / macro drift between branches that would break compile.
If build fails: mark `master-build` step `blocked`, leave the tree where it is, do not auto-revert. Print the build error tail.
### 8. Report
```
Upmerge complete (dry-run | --execute).
Chain: <from> → <hop1> → <hop2> → ... → master
Hops: <N done>/<N total>
Build: <skipped | passed | failed>
NEWS: spliced on <list of branches>
UPMERGE.md: <task-dir>/UPMERGE.md
To publish (you decide when):
git -C "$PHP_SRC_DIR" push origin <from> <hop1> ... master
```
Never run the push. Never print the push without the explicit "you decide when" wrapper.
## UPMERGE.md schema
```markdown
---
type: upmerge
status: planned | in-progress | done | blocked
source_branch: PHP-8.3
source_commit: a1b2c3d
issue: GH-12345
created: YYYY-MM-DD
---
# Upmerge: GH-12345
## Chain
PHP-8.3 (source) → PHP-8.4 → PHP-8.5 → master
## NEWS entry to propagate
Section: `- Core:`
Body:
```
. Fixed bug GH-12345 (one-line description).
(contributor-handle)
```
## Hops
- [ ] **PHP-8.3 → PHP-8.4** — status: todo
merge subject: `Merge branch 'PHP-8.3'`
merge-commit SHA: (pending)
- [ ] **PHP-8.4 → PHP-8.5** — status: todo
- [ ] **PHP-8.5 → master** — status: todo
## Build check
- [ ] **master-build** — `make -j$(nproc)` at chain head
## Progress Log
- YYYY-MM-DD HH:MM — upmerge — planned chain from .github/matrix.php
```
Status values mirror SPEC.md (`todo` / `in-progress` / `done` / `skipped` / `blocked`). Resume algorithm is identical: re-running `/upmerge --execute` reads UPMERGE.md, finds the first non-`done` hop, picks up there.
## Boundaries
- **No `git push`, `git pull`, `git fetch`, `git remote *`.** The chain is computed from local refs only. If any branch in the chain is stale, that is the user's call to fix.
- **No branch creation or deletion.**
- **No modification of SPEC.md.** Bug-flow state is owned by other skills.
- **No `make test` per hop.** Single build at the chain head only.
- **No GitHub interaction.** No `gh pr`, no `gh issue comment`, no posting of upmerge status to the issue.
- **No conflict resolution outside NEWS.** Code conflicts always block.
- **No backporting.** This skill only goes up the chain. To put a fix on a branch *below* the one it landed on, the user must redo the fix on that branch — the policy is forward-port, not back-port.
- **No proceeding without an explicit `--from` when SPEC.md is absent.** Don't guess.
- **No reversal.** If the user wants to undo: `git reset --hard <pre-upmerge-sha>` per branch is on them. Document this in the final report when relevant.
## Edge cases
| Situation | Handling |
|---|---|
| `--from master` | Refuse (see § 1). |
| Fix commit landed on master only | Refuse: `/upmerge` is for forward-port from a lower branch. |
| Upper branch already contains the GH-NNNNN line in its NEWS | The `awk` splice is effectively idempotent — detect by grepping the upper NEWS for the exact entry first; if present, skip the splice, still produce the merge commit. Note in UPMERGE.md hop body. |
| `.github/matrix.php` missing | Fall back per `_shared/branch-detection.md` § 1 fallback. Note the fallback in the planning step. |
| User aborts mid-chain (Ctrl-C / `git merge --abort`) | Re-running `/upmerge --execute` reads UPMERGE.md, resumes at first non-`done` hop. |
| Upper-branch NEWS has no matching `- <Component>:` section | The awk's END branch appends a new section. Note in the hop body. |
| Fix commit is itself a merge commit (multi-parent) | Refuse: upmerge expects a single linear fix commit. The user must squash/cherry-pick first. |
| The chain has only one hop (`<from>` is just one below master) | Run normally — one hop, one build at the top. |
| User asks for partial upmerge (`--up-to PHP-8.5`) | Honour the flag; truncate the chain at `--up-to`. UPMERGE.md records the truncation. |
## Quality checklist
- [ ] Pre-guardrails all green before any merge happens
- [ ] Merge chain computed from `.github/matrix.php` (or documented fallback), never hardcoded
- [ ] NEWS entry section + body extracted from the source commit, not regenerated
- [ ] Each hop uses `--no-ff --no-commit`, spliced NEWS via `awk`, then `commit --no-edit`
- [ ] Non-NEWS conflicts cause the skill to stop, not silently resolve
- [ ] One build at the chain head; no per-hop test runs
- [ ] UPMERGE.md tracks every hop's SHA; Progress Log appended once per hop
- [ ] No `git push`, `git fetch`, `git pull`, `git remote *` executed
- [ ] Final report prints the push command but does not run it
## Out of scope
- Posting upmerge status to GitHub — never auto. The user pushes and announces themselves.
- Squashing or rewriting the fix commit on the source branch.
- Resolving non-NEWS conflicts.
- Detecting that a fix is security-only and shouldn't be upmerged — that judgement is the user's. If they ran `/upmerge`, the fix is upmergeable.
Wersje
W bazie nie ma innych skilli o nazwie upmerge.
Migawka bazy z 28.10.2026 · wersja paczki 363014ddbf7d (skrót SHA-256 całej paczki)
Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.
Podobne skille
- 100bug-fix
Znajduje przyczynę błędu w kodzie C php-src i wdraża poprawkę tak, by padający test .phpt przechodził, a test i poprawkę zapisuje jednym commitem na bieżącej gałęzi. - 100bug-context
Rozpoczyna naprawę błędu w php-src: pobiera zgłoszenie z GitHuba z powiązanymi PR przez gh CLI, zapisuje fazę Context w SPEC.md i przygotowuje szkielet pozostałych faz. - 100split-branch
Dzieli gałąź z N commitami na N gałęzi z jednym commitem każda, gdy użytkownik o to poprosi. - 100feature-branch
Wymusza przepływ pracy feature-branch z Atlassian dla GitHub i GitLab: gałąź od domyślnej, praca, PR lub MR i usunięcie gałęzi lokalnej oraz zdalnej po scaleniu. - 100cleanup-branches
Czyści lokalne gałęzie git, których praca została już scalona do gałęzi bazowej. - 100worktree-stale-base-merge
Pomaga ocenić, czy zielone CI nadal coś znaczy przy merge pull requesta z równoległej gałęzi agenta lub gałęzi odciętej od starszego main, po wylądowaniu gałęzi siostrzanych.