Code contribution and review extension for the GitMsg protocol (name: review, version: 0.1.0).
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
pull-request: Propose code changesfeedback: Code review activity (inline comment, verdict, suggestion, or combination)For general comments on a pull request, see Section 1.6.
Fields (in header order):
state: MUST be open, merged, or closeddraft: MAY be true to indicate the pull request is not ready for review (OPTIONAL)base: Target branch reference (<repo-url>#branch:<name> or #branch:<name>)base-tip: Base branch commit hash at time of creation or update, 12 characters (OPTIONAL)head: Source branch reference (<repo-url>#branch:<name> or #branch:<name>)head-tip: Head branch commit hash at time of creation or update, 12 characters (OPTIONAL)depends-on: MAY contain comma-separated pull request references that this PR depends on (OPTIONAL)closes: MAY contain comma-separated issue references to close on mergemerge-base: Common ancestor commit hash, 12 characters (REQUIRED on state="merged" edits, MUST NOT appear otherwise)merge-head: Head branch commit hash at merge time, 12 characters (REQUIRED on state="merged" edits, MUST NOT appear otherwise)reviewers: MAY contain comma-separated reviewer email addresseslabels: MAY contain comma-separated scoped values (e.g. labels="kind/bug,priority/high") (OPTIONAL, core field)Field order: state, draft, base, base-tip, head, head-tip, depends-on, closes, merge-base, merge-head, reviewers, labels.
The head and base fields support full repository URLs, enabling cross-forge contributions (e.g., GitLab to GitHub).
Fields (in header order):
pull-request: MUST reference the pull request commitcommit: Specific commit hash (12 characters, OPTIONAL)file: File path relative to repo root (OPTIONAL)new-line: Line number in the new file version, 1-indexed (OPTIONAL)new-line-end: End line in the new file version (OPTIONAL)old-line: Line number in the old file version, 1-indexed (OPTIONAL)old-line-end: End line in the old file version (OPTIONAL)review-state: approved or changes-requested (OPTIONAL)suggestion: true if the message body contains a suggested replacement (OPTIONAL)Field order: pull-request, commit, file, new-line, new-line-end, old-line, old-line-end, review-state, suggestion.
A feedback message MUST include at least one of: code-location fields (file, commit, and at least one of old-line or new-line) OR review-state.
Inline feedback (with code-location fields) MUST include file, commit, and at least one of old-line or new-line.
closes SHOULD auto-close referenced issues when merged via core versioningdepends-on SHOULD NOT be merged until all referenced PRs are merged. Implementations SHOULD enforce bottom-up merge ordering within a stackbase matches the merged PR's head to point to the merged PR's base instead suggestion ... )Pull requests MAY be edited or retracted using core versioning (GITMSG.md Section 1.5). State transitions (open -> merged, open -> closed) are edits to the original pull-request commit. Draft transitions (adding or removing draft="true") are also edits. Implementations MUST NOT merge a pull request while draft="true". Implementations SHOULD suppress review request notifications for draft pull requests.
Implementations SHOULD include base-tip and head-tip when creating or editing a pull request. These fields capture the commit hashes (12 characters) that the base and head branches point to at the time of the commit. When a pull request's code is updated (e.g., after rebase or new commits), implementations SHOULD create an edit with updated base-tip and head-tip values. The edits chain serves as a version history of the pull request's code state, enabling implementations to compare any two versions via range-diff.
When transitioning to state="merged", implementations MUST include merge-base with the common ancestor commit hash (12 characters) and merge-head with the head branch tip commit hash (12 characters), both computed before the merge. These two fields are the only durable record of the merged commit range — head-tip and base-tip describe the live branches at edit time, but the head branch may be deleted afterward, leaving merge-base..merge-head as the sole reconstruction path for the diff. Implementations MUST refuse to record a state="merged" edit when either field cannot be computed (e.g., the head branch is missing or the merge-base is unreachable).
When resolving a cross-repository pull request (fork PR) by merge or close, implementations SHOULD first copy it to the upstream review branch, carrying adopts="<fork-pr-ref>" (naming the fork PR) and a GitMsg-Ref: trailer preserving the original author's identity. The resolving edit then references the local copy as canonical, ensuring the upstream has a self-contained record that survives fork deletion.
Feedback messages MAY be edited or retracted using core versioning. Implementations SHOULD display an edit indicator on modified messages.
Implementations MUST use GitSocial for pull request comments. The original field references the pull request commit:
Looks great, just one nit on the theme toggle.
GitMsg: ext="social"; type="comment"; original="#commit:abc123456789@gitmsg/review"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-06T10:00:00Z"; ref="#commit:abc123456789@gitmsg/review"; v="0.1.0"
> Add dark mode support
Nested comment threads use GitSocial's reply-to field.
Inline reviews are anchored to specific file locations at a specific commit. When the PR branch is rebased or updated, implementations SHOULD attempt to map comment locations to the new commit:
Anchoring is an implementation concern. Implementations MAY use different strategies for mapping comments across revisions.
Implementations SHOULD derive pull request review state from feedback commits that include review-state:
review-state="changes-requested", the pull request SHOULD be considered blockedreview-state="approved", the pull request SHOULD be considered approvedImplementations MAY enforce additional merge requirements via configuration.
Configuration MUST be stored at refs/gitmsg/review/config:
{
"version": "0.1.0",
"branch": "gitmsg/review",
"require-review": true
}
Configuration MUST include: version.
Configuration SHOULD include: branch. Default: gitmsg/review.
Configuration MAY include: require-review (boolean, default false).
{
"name": "review",
"version": "0.1.0",
"display": "GitReview",
"description": "Code contribution and review extension for GitMsg",
"types": ["pull-request", "feedback"],
"fields": ["base", "base-tip", "closes", "commit", "depends-on", "draft", "file", "head", "head-tip", "merge-base", "merge-head", "new-line", "new-line-end", "old-line", "old-line-end", "pull-request", "review-state", "reviewers", "state", "suggestion"]
}
| Field | Pattern | ||
|---|---|---|---|
state (pull-request) | `open\ | merged\ | closed` |
draft | true | ||
review-state | `approved\ | changes-requested` | |
file | relative path, no leading slash | ||
old-line | positive integer, 1-indexed | ||
old-line-end | positive integer, 1-indexed | ||
new-line | positive integer, 1-indexed | ||
new-line-end | positive integer, 1-indexed | ||
commit | 12-character hash | ||
reviewers | comma-delimited email addresses | ||
depends-on | comma-separated pull request references | ||
closes | comma-separated issue references | ||
base-tip | 12-character hash | ||
head-tip | 12-character hash | ||
merge-base | 12-character hash; required when state="merged", prohibited otherwise | ||
merge-head | 12-character hash; required when state="merged", prohibited otherwise | ||
suggestion | true |
Add dark mode support
GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; closes="#commit:abc123456789@gitmsg/pm"; reviewers="[email protected]"; v="0.1.0"
WIP: Add dark mode support
GitMsg: ext="review"; type="pull-request"; state="open"; draft="true"; base="#branch:main"; head="#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"
Add dark mode support
GitMsg: ext="review"; type="pull-request"; edits="#commit:abc123456789@gitmsg/review"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; reviewers="[email protected]"; v="0.1.0"
LGTM!
GitMsg: ext="review"; type="feedback"; pull-request="#commit:abc123456789@gitmsg/review"; review-state="approved"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:abc123456789@gitmsg/review"; v="0.1.0"
> Add dark mode support
Consider caching this value
GitMsg: ext="review"; type="feedback"; pull-request="#commit:abc123456789@gitmsg/review"; commit="def456789abc"; file="src/theme.js"; new-line="42"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:abc123456789@gitmsg/review"; v="0.1.0"
> Add dark mode support
~~~ Use a CSS custom property for the transition
transition: background-color var(--theme-transition, 200ms) ease;
GitMsg: ext="review"; type="feedback"; pull-request="#commit:abc123456789@gitmsg/review"; commit="def456789abc"; file="src/theme.css"; new-line="18"; suggestion="true"; v="0.1.0" GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="#commit:abc123456789@gitmsg/review"; v="0.1.0"
Add dark mode support
~~~
Add dark mode support
GitMsg: ext="review"; type="pull-request"; edits="#commit:abc123456789@gitmsg/review"; state="merged"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="#branch:dark-mode"; head-tip="d4e5f6a1b2c3"; closes="#commit:abc123456789@gitmsg/pm"; merge-base="f1e2d3c4b5a6"; merge-head="d4e5f6a1b2c3"; reviewers="[email protected]"; v="0.1.0"
GitMsg: ext="review"; edits="#commit:abc123456789@gitmsg/review"; retracted="true"; v="0.1.0"
Add dark mode support
GitMsg: ext="review"; type="pull-request"; state="open"; base="https://github.com/bob/repo#branch:main"; base-tip="f1e2d3c4b5a6"; head="https://gitlab.com/alice/repo#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"
Step 1 - Copy fork PR to upstream (preserves original author via GitMsg-Ref):
Add dark mode support
GitMsg: ext="review"; type="pull-request"; state="open"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="https://gitlab.com/alice/repo#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; v="0.1.0"
GitMsg-Ref: ext="review"; type="pull-request"; author="Alice"; email="[email protected]"; time="2025-01-20T10:00:00Z"; ref="https://gitlab.com/alice/repo#commit:abc123456789@gitmsg/review"; v="0.1.0"
> Add dark mode support
Step 2 - Merge edit references the local copy:
Add dark mode support
GitMsg: ext="review"; type="pull-request"; edits="#commit:def456789abc@gitmsg/review"; state="merged"; base="#branch:main"; base-tip="f1e2d3c4b5a6"; head="https://gitlab.com/alice/repo#branch:dark-mode"; head-tip="a1b2c3d4e5f6"; merge-base="f1e2d3c4b5a6"; merge-head="a1b2c3d4e5f6"; v="0.1.0"