Pull requests
The pr/ prefix is mandatory
A branch becomes a pull request only if its name starts with pr/.
pr/my-feature opens a PR. my-feature and feature/foo are ordinary pushes and will never create one, whatever push options you pass.
Opening one
bash
git checkout -b pr/my-feature
# ... commits ...
git push -u origin pr/my-featureWith one commit that's the whole flow: the commit subject becomes the title and the body becomes the description.
More than one commit
Give it a title and description explicitly:
bash
git push -u origin pr/my-feature \
-o 'title=Add retry logic' \
-o 'description=Retries transient relay failures.\n\nCloses #deadbeef'Literal \n, not real newlines
Git cannot carry real newlines through a push option. Write the two characters \n and ngit's parser converts them.
Do not use $'...\n\n...' quoting, and do not pipe a Markdown file into -o description=. Escaping it through the shell and git is a losing game. For anything longer than a couple of lines, use ngit send, which takes ordinary multiline arguments.
Targeting another branch
bash
git push -u origin pr/release-fix -o target-branch=release/2.xUpdating
Push again. Use --force-with-lease for a guarded rewrite; --force is also supported. The branch just has to keep its pr/ prefix.
Stacked PRs
If your branch contains the unique latest tip of another of your open or draft PRs, ngit infers the stack, so you don't have to declare it. After the parent advances, rebase the child onto its new tip before updating.
Override the inference when it can't be right: an ambiguous stack, a parent from another author, or a deliberately historical base.
bash
git push -u origin pr/second-part -o base=<commit|branch|nevent>ngit refuses stale children and ambiguous candidates rather than guessing. If you pin an explicit historical base, repeat it on each later update, or the lineage will advance on its own.
Advanced: ngit send
ngit send is the route for descriptions that don't fit in a push option. Its --description is a normal shell argument, so real newlines work:
bash
# ANSI-C quoting for inline multiline text
ngit send HEAD~2 \
--subject "My feature" \
--description $'First paragraph.\n\nSecond paragraph.'
# or straight from a file
ngit send HEAD~2 \
--subject "My feature" \
--description "$(cat description.md)"WARNING
"...\n\n..." in plain double quotes does not produce newlines. You'll get literal backslash-n in the published event. Use $'...' or a file.
Other useful forms:
bash
ngit send --defaults # non-interactive
ngit send HEAD~2 --in-reply-to <PR-ID> # new version of a PR
ngit send --defaults --target-branch release/2.x
ngit send --defaults --base <commit|branch|nevent>If you open a proposal with ngit send, don't also push a pr/ branch for the same work. You'll publish it twice. See the generated ngit send reference for every input.
Reading
bash
ngit pr list # open and draft
ngit pr list --status open,draft,closed,applied
ngit pr list --label bug
ngit pr view <ID>
ngit pr view <ID> --comments<ID> accepts an nevent1... string, a full 64-character hex ID, or a unique hex prefix with an optional #, such as ngit pr view '#deadbeef'. Quote the # form so your shell doesn't treat it as a comment. Ambiguous prefixes fail and list the matches.
Reviewing
bash
ngit pr checkout <ID> # branch it locally
ngit pr comment <ID> --body "Looks good"
ngit pr comment <ID> --body "Fixed" --reply-to <comment-ID>ngit pr checkout <ID> creates a local pr/* branch linked to the proposal's upstream. Later git fetch and git pull receive the author's updates, and git push publishes your updates from that branch. This explicit checkout is how you opt into a PR now that routine fetches do not download every open PR branch.
To reference another PR, issue or comment from inside a comment body, use a nostr: URI rather than a raw hex ID:
bash
ngit pr comment <ID> --body "Supersedes nostr:nevent1abc…"Merging
ngit pr merge acts on the current branch. Switch to the intended target branch first:
bash
git switch <target-branch>
ngit pr merge <ID>
git push origin <target-branch>It deliberately does not push. The merge isn't published, and the PR isn't marked applied, until you do.
Conflicts stop it mid-way: resolve them and run git commit. The commit message is already prepared.
To require green CI first, see CI:
bash
ngit pr merge <ID> --require-ci-trust maintainer-directedCheck for nested merges first
bash
git log --merges --oneline origin/<target>..HEADIf that already shows a Merge #..., stop. Merging again nests PR merge history. Unless that's deliberate, rebase or cherry-pick the PR commits onto the current target branch and update the PR instead.
Lifecycle
bash
ngit pr close <ID> --reason "blocked by upstream"
ngit pr reopen <ID> --reason "fix was incomplete"
ngit pr ready <ID> --reason "addressed review feedback"
ngit pr draft <ID> --reason "needs more work"
ngit pr label <ID> --label bug --label enhancement
ngit pr set-subject <ID> --subject "New title"
ngit pr set-cover-note <ID> --body "$(cat updated-description.md)"A cover note replaces the description shown for the PR. Authors and maintainers can change these; other people can't.
Signing as someone else
--signer applies to ngit commands. For a git push, use git's own -c:
bash
ngit --signer alice pr comment <ID> --body "..."
git -c nostr.signer=alice push origin pr/topicNeither changes your configured login. See Accounts & keys.
Next
- Issues
- CI: gate merges on trusted results
- Troubleshooting
- Generated PR reference