Workflow
Run git diff --stat and git status --short to see what changed, plus
git log -1 --oneline (and git log @{u}.. --oneline if a remote-tracking
branch exists) to see the last local commit and whether it's still unpushed.
If the change is a direct continuation or fix of that unpushed HEAD commit,
skip drafting a new message and fold it in instead of creating a second commit
for the same logical change:
git add <file>
git commit --amend --no-edit # or drop --no-edit to also revise the message
Otherwise, draft a new commit message.
Stage relevant files by name - never git add -A or git add ..
Write the commit message to a temporary file, then commit with git commit -F:
cat > /tmp/odoo-commit-message.txt <<'EOF'
[TAG] module: short description
Optional body line.
EOF
git commit -F /tmp/odoo-commit-message.txt
Any equivalent temp-file flow is fine (for example, using PowerShell's
Set-Content or another editor) as long as the final commit is created with
git commit -F <file>.
Before opening a pull request, squash your own back-and-forth commits into one
clean commit per logical change (per OCA guidelines):
the rest of the world doesn't need your intermediate "fix bug 1", "fix bug 2"
history - only the final state and a clear summary. For a single trailing fix,
git commit --amend (step 2) is usually enough; for folding several commits
into one, use an interactive rebase instead:
git rebase -i HEAD~N # N = number of commits to fold
Keep the first line as pick with the real [TAG] module: description message,
mark the rest fixup (drop their messages) or squash (merge messages):
pick 1949129 [IMP] module: Introduce feature A
fixup d2cf643 Fix bug 1 of feature A
fixup 42bd9e8 Fix bug 2 of feature A
fixup 7f767d5 Fix bug 3 of feature A
Either way, only rewrite commits that are still local/unpushed, or on a branch
you alone own - never amend or rebase shared history without confirming with
the user first.
Report the resulting commit hash and subject.
Do not bypass pre-commit hooks. If a hook fails, fix the issue,
re-stage the changes, and create the commit again.
Format
[TAG] module: short description
Optional body explaining WHY, not what. What is visible in the diff.
Focus on motivation, constraints, and decisions made.
task-XXXX, opw-XXXXXX
Subject Line Rules
[TAG] module: description - tag in brackets, then module name, colon, space, description
- Target the whole header (
[TAG] module: description) at about 50 characters for
readability; 72 is a hard ceiling, not something to aim for
- Self-test: the header must read as a valid sentence after "if applied, this commit
will
<header>" - e.g. [IMP] base: prevent to archive users linked to active partners -> "if applied, this commit will prevent to archive users linked to
active partners"
- Never use single, vague words like "bugfix" or "improvements" as the description -
it must be self-explanatory and state the reason for the change
- Imperative mood: "add", "fix", "remove" - not "added", "fixes"
- No trailing period
- Module = technical module name (e.g.
hhc, plc, nhso_stddataset, imc)
- Avoid touching multiple modules in one commit - split per module so each can be
reverted independently. If truly unavoidable, list the modules or use
various
Tags
[FIX] - bug fix; used in stable versions, also valid for recent dev bugs
[REF] - refactoring: feature heavily rewritten
[ADD] - adding new modules
[REM] - removing resources: dead code, views, modules
[REV] - reverting commits
[MOV] - moving files (no content change; use git mv)
[REL] - release commits: major/minor stable versions
[IMP] - improvements: most incremental dev changes
[MERGE] - merge commits / forward port of bug fixes
[CLA] - signing Odoo Individual Contributor License
[I18N] - translation file changes
[PERF] - performance patches
[CLN] - code cleanup
[LINT] - linting passes
Body Rules
- Skip body when subject is self-explanatory
- Include body for: non-obvious WHY, breaking changes, migration notes, task references
- Explain WHY, not WHAT - the diff already shows what changed. WHAT is only worth
spelling out when a technical choice or trade-off was involved, and then explain
WHY that choice was made
- Don't force brevity for its own sake: official Odoo guidance explicitly says not to
hesitate being verbose when the reasoning deserves it. Every line should still earn
its place - no restating the diff, no filler
- Wrap at 72 characters per line
- Reference task IDs at the end:
task-XXXX, opw-XXXXXX, Fixes #123, Closes #123
What Never Goes In
- "This commit does X" - the diff says what
- "I", "we", "now", "currently"
- AI attribution
- Restating the file name or module when the subject already says it
Examples
Examples from the official Odoo git guidelines:
[REF] models: use `parent_path` to implement parent_store
This replaces the former modified preorder tree traversal (MPTT) with the
fields `parent_left`/`parent_right`[...]
[FIX] account: remove frenglish
[...]
Closes #22793
Fixes #22769
[FIX] website: remove unused alert div, fixes look of input-group-btn
Bootstrap's CSS depends on the input-group-btn element being the first/last
child of its parent. This was not the case because of the invisible and
useless alert.
Header-only, illustrating the "valid sentence" self-test:
[IMP] base: prevent to archive users linked to active partners
1---2name: odoo-commit3description: Guides Odoo-style commit creation following official Odoo git guidelines. Drafts `[TAG] module: short description` messages, checks whether to amend or create a new commit, stages files explicitly, commits via `git commit -F`, and keeps local history clean before PRs. Auto-invokes for commit-related requests in Odoo repositories and takes priority over generic commit skills.4---5
6## Workflow
7
81. Run `git diff --stat` and `git status --short` to see what changed, plus
9 `git log -1 --oneline` (and `git log @{u}.. --oneline` if a remote-tracking
10 branch exists) to see the last local commit and whether it's still unpushed.
112. If the change is a direct continuation or fix of that unpushed `HEAD` commit,
12 skip drafting a new message and fold it in instead of creating a second commit
13 for the same logical change:
14
15 ```bash
16 git add <file>
17 git commit --amend --no-edit # or drop --no-edit to also revise the message
18 ```
19
20 Otherwise, draft a new commit message.
213. Stage relevant files by name - never `git add -A` or `git add .`.
224. Write the commit message to a temporary file, then commit with `git commit -F`:
23
24```bash
25cat > /tmp/odoo-commit-message.txt <<'EOF'
26[TAG] module: short description
27
28Optional body line.
29EOF
30git commit -F /tmp/odoo-commit-message.txt
31```
32
33 Any equivalent temp-file flow is fine (for example, using PowerShell's
34 `Set-Content` or another editor) as long as the final commit is created with
35 `git commit -F <file>`.
36
375. Before opening a pull request, squash your own back-and-forth commits into one
38 clean commit per logical change (per [OCA guidelines](https://github.com/OCA/maintainer-tools/wiki/Merge-commits-in-pull-requests#mergesquash-your-own-commits)):
39 the rest of the world doesn't need your intermediate "fix bug 1", "fix bug 2"
40 history - only the final state and a clear summary. For a single trailing fix,
41 `git commit --amend` (step 2) is usually enough; for folding several commits
42 into one, use an interactive rebase instead:
43
44 ```bash
45 git rebase -i HEAD~N # N = number of commits to fold
46 ```
47
48 Keep the first line as `pick` with the real `[TAG] module: description` message,
49 mark the rest `fixup` (drop their messages) or `squash` (merge messages):
50
51 ```
52 pick 1949129 [IMP] module: Introduce feature A
53 fixup d2cf643 Fix bug 1 of feature A
54 fixup 42bd9e8 Fix bug 2 of feature A
55 fixup 7f767d5 Fix bug 3 of feature A
56 ```
57
58 Either way, only rewrite commits that are still local/unpushed, or on a branch
59 you alone own - never amend or rebase shared history without confirming with
60 the user first.
61
626. Report the resulting commit hash and subject.
63
64Do not bypass pre-commit hooks. If a hook fails, fix the issue,
65re-stage the changes, and create the commit again.
66
67## Format
68
69```
70[TAG] module: short description
71
72Optional body explaining WHY, not what. What is visible in the diff.
73Focus on motivation, constraints, and decisions made.
74
75task-XXXX, opw-XXXXXX
76```
77
78## Subject Line Rules
79
80- `[TAG] module: description` - tag in brackets, then module name, colon, space, description
81- Target the **whole header** (`[TAG] module: description`) at about 50 characters for
82 readability; 72 is a hard ceiling, not something to aim for
83- Self-test: the header must read as a valid sentence after "if applied, this commit
84 will `<header>`" - e.g. `[IMP] base: prevent to archive users linked to active
85 partners` -> *"if applied, this commit will prevent to archive users linked to
86 active partners"*
87- Never use single, vague words like "bugfix" or "improvements" as the description -
88 it must be self-explanatory and state the reason for the change
89- Imperative mood: "add", "fix", "remove" - not "added", "fixes"
90- No trailing period
91- Module = technical module name (e.g. `hhc`, `plc`, `nhso_stddataset`, `imc`)
92- Avoid touching multiple modules in one commit - split per module so each can be
93 reverted independently. If truly unavoidable, list the modules or use `various`
94
95## Tags
96
97- `[FIX]` - bug fix; used in stable versions, also valid for recent dev bugs
98- `[REF]` - refactoring: feature heavily rewritten
99- `[ADD]` - adding new modules
100- `[REM]` - removing resources: dead code, views, modules
101- `[REV]` - reverting commits
102- `[MOV]` - moving files (no content change; use git mv)
103- `[REL]` - release commits: major/minor stable versions
104- `[IMP]` - improvements: most incremental dev changes
105- `[MERGE]` - merge commits / forward port of bug fixes
106- `[CLA]` - signing Odoo Individual Contributor License
107- `[I18N]` - translation file changes
108- `[PERF]` - performance patches
109- `[CLN]` - code cleanup
110- `[LINT]` - linting passes
111
112## Body Rules
113
114- Skip body when subject is self-explanatory
115- Include body for: non-obvious WHY, breaking changes, migration notes, task references
116- **Explain WHY, not WHAT** - the diff already shows what changed. WHAT is only worth
117 spelling out when a technical choice or trade-off was involved, and then explain
118 WHY that choice was made
119- Don't force brevity for its own sake: official Odoo guidance explicitly says not to
120 hesitate being verbose when the reasoning deserves it. Every line should still earn
121 its place - no restating the diff, no filler
122- Wrap at 72 characters per line
123- Reference task IDs at the end: `task-XXXX`, `opw-XXXXXX`, `Fixes #123`, `Closes #123`
124
125## What Never Goes In
126
127- "This commit does X" - the diff says what
128- "I", "we", "now", "currently"
129- AI attribution
130- Restating the file name or module when the subject already says it
131
132## Examples
133
134Examples from the [official Odoo git guidelines](https://www.odoo.com/documentation/19.0/contributing/development/git_guidelines.html):
135
136```
137[REF] models: use `parent_path` to implement parent_store
138
139This replaces the former modified preorder tree traversal (MPTT) with the
140fields `parent_left`/`parent_right`[...]
141```
142
143```
144[FIX] account: remove frenglish
145
146[...]
147
148Closes #22793
149Fixes #22769
150```
151
152```
153[FIX] website: remove unused alert div, fixes look of input-group-btn
154
155Bootstrap's CSS depends on the input-group-btn element being the first/last
156child of its parent. This was not the case because of the invisible and
157useless alert.
158```
159
160Header-only, illustrating the "valid sentence" self-test:
161```
162[IMP] base: prevent to archive users linked to active partners
163```