[ADD] script todo : déployer /git_prepare_merge depuis le menu Claude
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
This commit is contained in:
parent
8bdbf42a31
commit
c32e7d87c9
3 changed files with 188 additions and 1 deletions
170
conf/template_claude_commands_git_prepare_merge.md
Normal file
170
conf/template_claude_commands_git_prepare_merge.md
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
---
|
||||
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.
|
||||
|
|
@ -3050,6 +3050,11 @@ class TODO(
|
|||
print(f"🤖 {t('Deploy Claude Code commands!')}")
|
||||
choices = [
|
||||
{"prompt_description": t("Commit - OCA/Odoo commit command")},
|
||||
{
|
||||
"prompt_description": t(
|
||||
"Git prepare merge - Git merge preparation command"
|
||||
)
|
||||
},
|
||||
{
|
||||
"prompt_description": t(
|
||||
"Todo Add Command - Add a command to todo.py menu"
|
||||
|
|
@ -3071,11 +3076,16 @@ class TODO(
|
|||
personalize=True,
|
||||
)
|
||||
elif status == "2":
|
||||
self._setup_claude_command(
|
||||
"git_prepare_merge",
|
||||
"template_claude_commands_git_prepare_merge.md",
|
||||
)
|
||||
elif status == "3":
|
||||
self._setup_claude_command(
|
||||
"todo_add_command",
|
||||
"template_claude_commands_todo_add_command.md",
|
||||
)
|
||||
elif status == "3":
|
||||
elif status == "4":
|
||||
self._list_claude_commands()
|
||||
else:
|
||||
print(t("Command not found !"))
|
||||
|
|
@ -3203,6 +3213,9 @@ class TODO(
|
|||
print(f"{t('Deployed commands'):<22} ~/.claude/commands/")
|
||||
gabarits = {
|
||||
"commit": "template_claude_commands_commit.md",
|
||||
"git_prepare_merge": (
|
||||
"template_claude_commands_git_prepare_merge.md"
|
||||
),
|
||||
"todo_add_command": "template_claude_commands_todo_add_command.md",
|
||||
}
|
||||
for nom, gabarit in sorted(gabarits.items()):
|
||||
|
|
|
|||
|
|
@ -1176,6 +1176,10 @@ TRANSLATIONS = {
|
|||
"fr": "Commit - Commande de commit OCA/Odoo",
|
||||
"en": "Commit - OCA/Odoo commit command",
|
||||
},
|
||||
"Git prepare merge - Git merge preparation command": {
|
||||
"fr": "Git prepare merge - Commande de préparation merge git",
|
||||
"en": "Git prepare merge - Git merge preparation command",
|
||||
},
|
||||
"Todo Add Command - Add a command to todo.py menu": {
|
||||
"fr": "Todo Add Command - Ajouter une commande au menu todo.py",
|
||||
"en": "Todo Add Command - Add a command to todo.py menu",
|
||||
|
|
|
|||
Loading…
Reference in a new issue