These rules keep the code and its history readable. scripts/check_text_hygiene.py checks the ones listed under Checks; the rest rely on review.
Commands below use python3. On Windows, run python instead.
[Type] (Scope): content
- Detail 1
- Detail 2
- Subject only by default. Add
-detail lines only when a detail really needs to be in the commit message. Leave one blank line after the subject. A detail that wraps continues on at most two lines indented by two spaces. No paragraphs and no trailers. - Content after
):is at most 72 characters of plain English in the imperative mood ("flush once per frame", not "flushes" or "flushed"). - Type is one or more of these, comma-separated (
[Fix, Test]):Feat: new behavior.Fix: corrects behavior.Perf: faster or lighter, same behavior.Refactor: restructures code, same behavior.Test: tests only.Docs: documentation and comments only.Build: CMake, Gradle, toolchains.CI: workflows and CI scripts.Chore: housekeeping such as headers, file moves and dead files.Tools: developer tools undertools/andscripts/.
- Scope is each changed path without a leading
MobileGL/, cut to its first two parts:MG_Backend/DirectGLES,MG_Test/Wire,scripts/ci,docs/Disaggregated.- A shorter path is used whole:
MG_Backendfor a file directly in a subsystem,scriptsfor a file directly inscripts/, and the file name for a file at the repository root (CMakeLists.txt). - A file directly in
MobileGL/usesMobileGL, and a submodule uses its path (3rdparty/glslang). - List every scope the commit touches, comma-separated:
(MG_Backend/DirectGLES, MG_Test/Wire). A commit that touches more than three scopes skips this check: summarize the touched areas yourself, for example(...). python3 scripts/check_text_hygiene.py scopeprints the scope for the staged changes.
- A shorter path is used whole:
- No planning ids (phase or decision codes such as
P13orID-54), local paths, external project names or non-English text anywhere in the message. - Merges use
[Merge] (area): what the merge takes. The scope is free text there. Write the subject yourself, since git's default merge message fails the check. The commits a merge brings in are not checked again. - Reverts use
[Fix] (Scope): revert <the reverted subject>.
Example:
[Perf] (MG_Backend/DirectGLES): flush once per frame before the window pass
- Code, comments, log strings and commit messages are in English, and documentation may be in English. American spelling is preferred in all of them: documentation, comments, strings and identifiers (color, behavior, center, cancel, initialize, normalize, organize, analyze, license, defense, artifact, modeling, labeled, gray, honor, neighbor, catalog).
- A comment states what the code does now and why, in a few lines. Rationale longer than about 15 lines belongs in a design document under
docs/, with a one-line pointer in the code. - In code and comments, cite a symbol, file or section, never a line number:
ARCHITECTURE.md §1.3,VulkanRenderer::BeginFrame(). Documents may keepfile:linecitations; the documentation citation check resolves them. - State the rule, not its history. No "previously" or "after review", and no planning ids in comments, strings, identifiers or file names.
- Plain wording, without ALL-CAPS emphasis.
- In code and comments (C, C++, GLSL, CMake, shell, Python, YAML), do not name external projects such as Mesa, Zink or gallium; describe the technique instead. Naming the driver or hardware where a behavior was observed (llvmpipe, ANGLE, Adreno, Mali) is fine, and so are file, package and extension names that contain these words (
50_mesa.json,GL_MESA_*).
- C and C++ sources are formatted with
.clang-format, using clang-format 23. Install it withpip install 'clang-format==23.1.3'.git clang-format <base>from the same package formats the lines changed since<base>, andscripts/format_code.shformats every file the check covers. The tree is already formatted, so neither touches lines you did not change. - The commit that reformatted the whole tree is listed in
.git-blame-ignore-revs. Rungit config blame.ignoreRevsFile .git-blame-ignore-revsonce so thatgit blamelooks past it. .incfiles are fragments included in the middle of a.cppfile, inside a namespace, or are generated tables. clang-format cannot format them on their own, so the check skips them. Match the indentation of the file that includes them. The check also skips generated files and the GL export table,MG_Impl/GLImpl/Exporting/Definitions.cpp.- Text files are UTF-8.
- These source files under
MobileGL/,scripts/andtools/start with the license header:- C, C++,
.inc, GLSL, Java and JavaScript modules, with//comments; - Python, shell, CMake modules and
CMakeLists.txt, with#comments. - Generated files and files under another license are exempt.
python3 scripts/update-source-file-header.pyadds the header.
- C, C++,
- Keep files to about 3,000 lines and functions to about 300 lines.
- Modules are layered. A file includes files of its own layer and of the layers below it, never above:
- the root headers (
Includes.h,Config.h),MG_UtilandMG_State; MG_Pipe;MG_Backend,MG_RemoteandMG_Impl;- the root
.cppfiles andMG_Gbm; MG_Test,MG_IntegrationTestandMG_Benchmark.
- the root headers (
- Each backend is isolated. Files under
MG_Backend/DirectGLES/orMG_Backend/DirectVulkan/are included only by the same backend, by the registry files directly inMG_Backend/, by the root.cppfiles and by tests. - Platform-specific APIs stay behind an abstraction, never raw in logic code.
- A fix that restores intended behavior ships unconditionally. Add a switch only for a real trade-off.
- A cache keyed on object identity uses a monotonic lifetime id, never a GL name or a pointer, because both are recycled.
- Source, tests, build files and maintained documentation.
- Generated code, only together with a CI step that regenerates it and fails on any difference.
- Binary test fixtures.
- Not raw logs, and not working papers such as agent briefs, review rounds and drafts. Keep those outside the repository and commit the conclusion: a design-document change, a
DEBTS.mdentry, a measurement underdocs/Performance.
python3 scripts/check_text_hygiene.py ci runs what CI runs. It checks the commits on the branch's own line of history since the check was added. The base branch it compares with is feat/disaggregated, which you can change with TEXT_HYGIENE_BASE_BRANCH.
These fail on any violation:
- commit messages, as described above;
- a new file under
docs/Disaggregated/notes/or a new.logfile (moving a file is not adding it); - a missing or wrong license header;
- a text file that is not UTF-8;
- an external project name in a comment;
- a
file:linecitation in code that points past the end of the file; - changed lines that clang-format would change (a line moved unchanged, the same commit removing its exact text elsewhere, is not a changed line).
These are ratchets: each total may only go down.
- planning ids in comments;
- comment lines beyond 15 in a block;
- ALL-CAPS comment lines;
- non-English comment lines;
- line-number citations in code.
python3 scripts/check_text_hygiene.py tree --update-baseline lowers scripts/data/text_hygiene_baseline.json after a cleanup. It never raises it, and CI rejects a raised baseline.
python3 scripts/check_structure.py ci checks three more ratchets over the C and C++ sources under MobileGL/, against scripts/data/structure_baseline.json:
- includes that break the layering or the backend isolation above;
- lines beyond 3,000 in a file;
- lines beyond 300 in a function body.
python3 scripts/check_structure.py report lists what each total counts, and tree --update-baseline lowers the baseline the same way.
Not checked by CI: the platform, switch and lifetime-id rules, planning ids outside comments, English outside comments, and the regeneration step for generated code.