Usage
Review flow
Create a PR for the current worktree branch, review its live basis, and use the exact reported pair for a verdict:
gitpr create --title "Improve validation"
gitpr list
gitpr review <pr-id>
gitpr comment <pr-id> --file internal/example.go --line-start 20 \
--text "Handle the empty value here."
gitpr approve <pr-id> --basis <source-head>:<base-head>
gitpr merge <pr-id>
For a compact state check, use:
gitpr review <pr-id> --summary
This keeps the live heads, containment, latest verdict, and projected thread state. It omits patch and comment bodies. With a prior verdict, it reports fixed interdiff file counts from blob IDs and modes. Each compared diff represents renames as deletions and additions. Use the default review command to inspect code changes.
Base selection
gitpr create --base <branch> selects the base explicitly. Without --base,
create uses the saved repository default, then refs/remotes/origin/HEAD, then
local main or master. Remote branch names retain their full path, such as
epic/current. Create does not infer the branch from which a worktree branched.
A detected base must be a local branch. If the saved default was deleted, or
origin’s default has no corresponding local branch, create fails with the
detected name and --base guidance before writing configuration or a PR record.
It does not silently select another base. Retry with an existing base:
gitpr create --base main --title "Improve validation"
A successful create saves the selected base as the repository default. For epic
child branches, pass the intended epic branch with --base.
Descriptions
--description passes its value through the invoking shell, so backticks,
$(), and mixed quoting in a multiline body can be consumed or mangled by the
shell before gitpr sees them. Use --description-file <path> (or - for
standard input) instead; it is read verbatim, byte-for-byte, with no shell
interpretation or trimming, and is mutually exclusive with --description:
gitpr create --title "Improve validation" --description-file description.txt
To correct title or description on an open PR without creating a second
record — for example after a shell-mangled description — use gitpr edit.
It refuses closed or merged PRs without writing, and never changes anything
but the fields given:
gitpr edit <pr-id> --description-file description.txt
Approve records an accepted event and does not merge. Merge reads the latest event and refuses rejected verdicts, head drift, deleted source branches, divergent histories, and equal source/base heads. Reject records a rejected event for the reviewed pair:
gitpr reject <pr-id> --basis <source-head>:<base-head>
Comments can be anchored, PR-level, or replies:
gitpr comment <pr-id> --pr-level --text "Ready for another review."
gitpr comment <pr-id> --thread <thread-id> --text "Addressed."
gitpr resolve <pr-id> <thread-id>
gitpr reopen <pr-id> <thread-id>
Comments are allowed while a PR is open, merged, or closed. A post-closure comment is marked in the record.
End work without merging through gitpr:
gitpr close <pr-id> --reason abandoned
gitpr close <pr-id> --reason superseded --superseded-by <replacement-id>
gitpr close <pr-id> --reason integrated --destination main \
--commit <landed-commit-sha>
Records remain retained after merge or closure. gitpr delete <id> previews the
destructive operation; --force performs it and may make pinned commits
collectable.
Use --text-file <path> for multiline comments or text with backticks, $(),
or mixed quotes. Use --text-file - to read standard input. Both preserve the
body byte-for-byte, including leading and trailing whitespace. The path must
be nonblank; the body must contain non-whitespace text. --text-file and
--text are mutually exclusive. These inputs work for anchored comments,
PR-level comments, and replies:
gitpr comment <pr-id> --pr-level --text-file comment.txt
gitpr comment <pr-id> --thread <thread-id> --text-file - <<'COMMENT'
Keep `value` and $(expression) literal.
Preserve this indentation.
COMMENT
The CLI accepts a full ULID or a unique prefix. show and comments open a
picker without an ID. The TUI lists records and provides a read-only detail
view. Press d in detail to read the live review report: per-file hunks with
old/new line numbers, source/base heads, merge base, and existing threads with
anchors, comments, resolution status, and outdated state. Use j/k or arrow
keys to scroll, PgUp/PgDn to page, g/G for start/end, and R to refresh.
Esc returns to detail, then to the list. Actions use the CLI review and basis
flow; the diff does not write verdicts, merges, or comments.
If a branch is missing, the diff reports it instead of showing an empty change.
Live hunks and remapped threads are unavailable in that state; detail still
shows the stored thread counts. A load or refresh error appears in the current
screen; a failed refresh retains the last successfully loaded report and its
basis. Only the latest requested report can update the view. Returning with
Esc discards pending responses, so a late refresh does not reopen the diff.
The terminal UI and PR picker adapt to the terminal background and honour
NO_COLOR and the terminal colour capability. Terminals without background
query support can supply COLORFGBG (for example, 15;0 for dark or 0;15
for light). Quit or cancel restores the terminal screen.
Legacy records
A record without a schema field is a legacy snapshot from the prior model.
gitpr does not read, list, mutate, merge, export, or delete one: every command
that takes its ID refuses and carries the commands below, and gitpr list skips
it with a count.
There is no migration. Recreate in-flight work with gitpr create and remove
the old record by hand. A legacy record is self-describing YAML behind plain
Git refs, so no tool is needed to retrieve it.
List the legacy records in a repository:
for ref in $(git for-each-ref --format='%(refname)' 'refs/gitpr/pr/*/meta'); do
git show "$ref:pr.yaml" | grep -q '^schema:' || echo "$ref"
done
Read one, read its history, and export it before removal:
git show refs/gitpr/pr/<id>/meta:pr.yaml
git log --patch refs/gitpr/pr/<id>/meta
git show refs/gitpr/pr/<id>/meta:pr.yaml > <id>.pr.yaml
Remove every ref belonging to one record. The first pattern is a prefix, not a glob, so it also reaches nested event and anchor pins:
git for-each-ref --format='%(refname)' \
refs/gitpr/pr/<id> 'refs/gitpr/index/*/<id>' |
while read -r ref; do git update-ref -d "$ref"; done
Removal is irreversible, and the commits the record pinned become collectable by Git.
Review against an immutable tag
gitpr create --title "Release review" --base refs/tags/1.2.3
gitpr review <id>
gitpr approve <id> --basis <source-head>:<base-head>
gitpr close <id> --reason integrated --destination <branch> --commit <sha>
Lightweight and annotated tags are supported when they peel to a commit.
Use refs/tags/<name> to choose a tag when a branch has the same name;
unqualified names prefer branches. A tag review does not set the default branch.
The review output includes base_tag.ref, base_tag.object_sha, and
base_tag.commit_sha; basis.base_head_sha is the peeled commit.
Approve and reject require the original tag ref and object to remain unchanged. Replacing an annotation counts as drift even if the commit stays the same. Deletion or drift refuses the verdict without changing review refs. Create a new PR to review the replacement tag. Original objects remain pinned until record deletion. Explicit-basis comments, metadata edits, and closure remain available after tag loss; live-basis review and comments report the missing or changed tag.
gitpr merge refuses a tag base, including with --cleanup. Tags are never
integration destinations. Close as integrated to record external integration
or verification, with a destination branch and resulting commits or patch IDs.
Closure records evidence; it does not perform or prove external integration,
move refs, or remove worktrees. An accepted event records approval separately.
You can also close as abandoned or superseded.