Une fusion apporte plusieurs commits d'un coup, et rien ne guidait les deux écrits qu'elle demande : l'entrée de changelog et le message de merge. La commande déployée impose la source — CHANGELOG.base.md, les fichiers générés étant perdus au prochain doc_markdown — et le corps bilingue avec son trailer, que le garde-fou ne verra jamais, commit-msg ignorant tout message ouvrant sur « Merge ». Elle prépare, elle ne fusionne pas : le message part dans tasks/, non versionné. Vérifié : 45 tests du menu, et un déploiement dans un HOME jetable que l'écran de contexte relit « à jour » face à son gabarit. --- EN --- A merge lands several commits at once, and nothing guided the two pieces of writing it needs: the changelog entry and the merge message. The deployed command imposes the source — CHANGELOG.base.md, the generated files being lost at the next doc_markdown — and the bilingual body with its trailer, which the guard rail never sees, commit-msg skipping any message opening on « Merge ». It prepares, it does not merge: the message goes to tasks/, which is not versioned. Checked: 45 menu tests, and a deployment into a throwaway HOME that the context screen reads back as up to date against its template. Assisted-by: Claude Opus 5
170 lines
6.4 KiB
Markdown
170 lines
6.4 KiB
Markdown
---
|
|
name: git_prepare_merge
|
|
description: "ERPLibre merge preparation: changelog entry, then the merge message for the current branch."
|
|
disable-model-invocation: true
|
|
allowed-tools:
|
|
- Bash(git status:*)
|
|
- Bash(git branch:*)
|
|
- Bash(git log:*)
|
|
- Bash(git diff:*)
|
|
- Bash(git merge-base:*)
|
|
- Bash(sed:*)
|
|
- Bash(make doc_markdown:*)
|
|
- Bash(python3:*)
|
|
- Read
|
|
- Edit
|
|
- Write
|
|
---
|
|
|
|
## Context
|
|
|
|
- Current branch: !`git branch --show-current`
|
|
- Branch commits: !`git log --oneline $(git merge-base HEAD master)..HEAD`
|
|
- Files touched: !`git diff --stat $(git merge-base HEAD master)..HEAD`
|
|
- Working tree: !`git status --porcelain`
|
|
- Changelog head: !`sed -n '24,45p' CHANGELOG.base.md`
|
|
|
|
## Task
|
|
|
|
Prepare the merge of the CURRENT branch into its integration branch. Two
|
|
deliverables, in this order: the changelog entry, then the merge message.
|
|
Nothing is merged here — `/git_prepare_merge` prepares, the human merges.
|
|
|
|
### 0. Read the branch
|
|
|
|
`master` is production, `develop` is where the work lands. Take the target
|
|
from where the branch forked: `git merge-base HEAD develop` and
|
|
`git merge-base HEAD master`, the closer of the two names the target.
|
|
|
|
Read the WHOLE branch before writing a word — `git log -p <base>..HEAD` for
|
|
the commits, `git diff <base>..HEAD` for the net result. A merge message
|
|
summarises what the branch delivers, which is rarely the concatenation of its
|
|
subjects: commits that undo each other cancel, and a fix to a feature added on
|
|
the same branch is part of the feature, not a separate line.
|
|
|
|
Stop and say so, rather than inventing, when the branch is empty, when it is
|
|
already merged, or when the working tree carries changes not yet committed —
|
|
uncommitted work is not part of the merge and must not be described as if it
|
|
were.
|
|
|
|
### 1. The changelog entry
|
|
|
|
`CHANGELOG.base.md` at the repository root is the SOURCE. `CHANGELOG.md` and
|
|
`CHANGELOG.fr.md` are generated by mmg and every direct edit to them is lost
|
|
at the next `make doc_markdown` — never open them to write.
|
|
|
|
The entry goes under `## [Unreleased]`, in the section that fits: Added /
|
|
Ajouté, Changed / Modifié, Fixed / Corrigé, Removed / Retiré, Security /
|
|
Sécurité. Create the pair of headings if the section does not exist yet, in
|
|
the file's own order.
|
|
|
|
The file alternates language blocks with markers. Within one section the
|
|
English bullets sit under `<!-- [en] -->` and the French translation under
|
|
`<!-- [fr] -->`, in the SAME order: the two lists are read side by side, and a
|
|
bullet added to one language only leaves the other half wrong. Nothing goes
|
|
under `<!-- [common] -->` but the version headings.
|
|
|
|
What a bullet says: what the software now DOES or REFUSES, in the present, for
|
|
someone who was not on this branch. It is longer than a commit subject and
|
|
shorter than the commit body — the reader is choosing whether to upgrade, not
|
|
reviewing the diff. Keep the failure mode removed, the figure that bounds it,
|
|
the flag or the file a user has to know. Drop the internals nobody outside
|
|
calls.
|
|
|
|
The two rules of `.claude/rules/04-code-conventions.md` hold here as
|
|
everywhere: nothing identifying — no customer, no real database, no host, no
|
|
address, no account path — and the code as the subject, never the session
|
|
that produced it.
|
|
|
|
Regenerate afterwards, and stage the three files together, the generated pair
|
|
being what most readers actually open:
|
|
|
|
```bash
|
|
make doc_markdown
|
|
git status --porcelain CHANGELOG.base.md CHANGELOG.md CHANGELOG.fr.md
|
|
```
|
|
|
|
### 2. The merge message
|
|
|
|
Resolve `{MODEL}` exactly as `/commit` does — the trailer is required here
|
|
too, a merge message being as AI-assisted as any other. Run:
|
|
|
|
```bash
|
|
python3 -c "
|
|
import glob, json, os, sys
|
|
sid = os.environ.get('CLAUDE_CODE_SESSION_ID', '')
|
|
hits = glob.glob(os.path.expanduser('~/.claude/projects/*/%s.jsonl' % sid)) if sid else []
|
|
mid = ''
|
|
for path in hits[:1]:
|
|
with open(path) as fh:
|
|
for line in fh:
|
|
try:
|
|
m = json.loads(line).get('message', {}).get('model', '')
|
|
except Exception:
|
|
continue
|
|
if m and not m.startswith('<'):
|
|
mid = m
|
|
if not mid:
|
|
sys.exit('UNKNOWN')
|
|
mid = mid.removeprefix('claude-')
|
|
parts = [p for p in mid.split('-') if not (len(p) == 8 and p.isdigit())]
|
|
print('Claude %s %s' % (parts[0].capitalize(), '.'.join(parts[1:])))
|
|
"
|
|
```
|
|
|
|
The shape, as this repository writes it:
|
|
|
|
```
|
|
Merge branch '<branch>'
|
|
|
|
[TAG] scope: what the branch delivers, imperative, 72 characters maximum
|
|
|
|
<N> commits. Why the branch existed: the failure mode it removes, the
|
|
figure that bounds it, what was verified and how. Wrap at 80 characters.
|
|
|
|
--- EN ---
|
|
|
|
The same body, translated.
|
|
|
|
Assisted-by: {MODEL}
|
|
```
|
|
|
|
The first line stays `Merge branch '<branch>'` — git writes it, tools read it,
|
|
and a merge whose first line says something else no longer looks like a merge
|
|
in `git log --oneline`. The tagged line beneath it is what a reader gets from
|
|
`--oneline` on the second row and from a release note, so it carries the same
|
|
duty as a commit subject: name the part of the system, then what is now
|
|
different about it. The evidence — the symptom, the quoted error, the
|
|
metaphor — belongs in the body.
|
|
|
|
The body: the same budget as a commit, per language, and here a merge covers
|
|
several commits, so it is a SUMMARY and not a list. Open by stating how many
|
|
commits the branch carries, then say what they add up to. No bullet list, no
|
|
per-commit rundown: `git log <base>..HEAD` already gives that, and a body
|
|
repeating it teaches nothing.
|
|
|
|
The bilingual rule holds — body, then the marker naming the language of what
|
|
FOLLOWS, then the translation — as does the ban on naming an AI in
|
|
`Co-authored-by:`.
|
|
|
|
**The hook does not check this one.** `script/git/hooks/commit-msg` skips any
|
|
message beginning with `Merge `, along with `Revert `, `fixup!` and `squash!`.
|
|
Length, addresses and account paths pass unchallenged here, so the discipline
|
|
is entirely yours.
|
|
|
|
### 3. Hand it over
|
|
|
|
Write the message to `tasks/merge_message.txt` — `tasks/` is not versioned,
|
|
which is why the repository sends working material there — and print the two
|
|
commands the human runs, with `--no-ff` so the branch keeps a merge commit and
|
|
its history stays readable:
|
|
|
|
```bash
|
|
git switch <target>
|
|
git merge --no-ff <branch> -F tasks/merge_message.txt
|
|
```
|
|
|
|
Do not run them. Do not switch branch, do not merge, do not push: the merge is
|
|
the human's decision and the last chance to read the message before it is
|
|
permanent. Report, in a sentence each, the changelog section written to and
|
|
the number of commits summarised.
|