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
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. Add --defaults to skip the confirmation.
More than one commit
Give it a title and description explicitly:
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
git push -u origin pr/release-fix -o target-branch=release/2.xUpdating
Push again. --force is fine. 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 — 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.
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:
# 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:
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.
Reading
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 # — ngit pr view #deadbeef. Ambiguous prefixes fail and list the matches.
Reviewing
ngit pr checkout <ID> # branch it locally
ngit pr comment <ID> --body "Looks good"
ngit pr comment <ID> --body "Fixed" --reply-to <comment-ID>To reference another PR, issue or comment from inside a comment body, use a nostr: URI rather than a raw hex ID:
ngit pr comment <ID> --body "Supersedes nostr:nevent1abc…"Merging
ngit merge <ID>
git push origin mainngit merge creates a no-ff merge commit — Merge #<8-hex>: <PR title> — on the PR's declared target branch, or the repository default if it has none. It resolves that target against the latest nostr state, so a stale local tracking ref can't route the merge onto old history.
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.
You can also merge the branch you're on:
ngit pr checkout <ID>
ngit merge # infers the PR from the pr/ branch
ngit merge --exclude-description <ID> # leave the cover note out of the commitTo require green CI first, see CI:
ngit merge <ID> --require-ci-trust maintainer-directedCheck for nested merges first
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
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:
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