Create a Safe, Reviewable Pull Request with gh pr create
You will finish with a pull request created from the current Git branch, with its title, description, target branch and review settings made explicit. The examples use GitHub CLI 2.87.3, reported by the installed gh binary. The Debian package database reports gh 2.45.0-1ubuntu0.3+esm3, so the binary and package metadata do not agree on this machine; check gh --version on your own host before relying on a version-specific option.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Check the repository and authentication
- 2. Confirm the commits and target branch
- 3. Prepare a complete, explicit command
- 4. Preview before creating when a push is possible
- 5. Choose how the body is supplied
- 6. Add reviewers, labels and issue linkage carefully
- 7. Verify the created pull request
- Common failure points
Allow about fifteen minutes for a normal pull request, longer if the branch has not been pushed before. You need a GitHub repository, an authenticated gh session, a local branch containing the intended commits, and permission to push its head branch. These steps create remote state. Read the final review carefully before running the creation command.
1. Check the repository and authentication
Start with ordinary, read-only checks. Run them from the worktree that contains the changes:
$ git status --short --branch
## feature/fix-login
$ gh auth status
github.com
Logged in to github.com account YOUR_LOGIN
Git operations protocol: ssh
The branch shown by git status is the default head branch for the command. A clean status is not required, but do not accidentally include uncommitted work: gh pr create builds the pull request from commits, not from a loose working-tree diff. If the authentication check fails, sign in through your normal approved process before continuing. Do not paste a token into a shell command or into the pull request body.
Checkpoint: identify the repository and branch you intend to publish:
$ gh repo view --json nameWithOwner,defaultBranchRef
{"defaultBranchRef":{"name":"main"},"nameWithOwner":"OWNER/REPOSITORY"}
$ git branch --show-current
feature/fix-login
2. Confirm the commits and target branch
Review the commits that will be proposed before you write the pull request text:
$ git log --oneline --decorate -5
abc1234 (HEAD -> feature/fix-login) Handle expired login sessions
def5678 Add regression coverage for session expiry
$ git diff --stat main...HEAD
src/session.go | 18 +++++++++++++-----
src/session_test.go | 24 ++++++++++++++++++++++++
2 files changed, 35 insertions(+), 7 deletions(-)
Replace main with the real base branch when necessary. --base is optional: without it, gh uses the current branch's branch.<current>.gh-merge-base Git setting when present, otherwise the repository's default branch. That fallback is convenient but easy to miss, so pass --base when the target matters.
Do not use sudo for these checks. Elevated privileges do not repair a wrong branch, a missing remote or a GitHub permission problem.
3. Prepare a complete, explicit command
For a predictable non-interactive creation, provide the title and body yourself. Quote both values so shell punctuation stays in the argument:
$ gh pr create \
--base main \
--head feature/fix-login \
--title "Handle expired login sessions" \
--body "Users now receive a fresh session after expiry.\n+\n+Tests: go test ./..." \
--reviewer REVIEWER_LOGIN \
--draft
https://github.com/OWNER/REPOSITORY/pull/123
Replace every uppercase placeholder. The printed URL is the expected success result. --draft creates a draft, which is useful when the branch is ready for review but not ready to merge. Remove it for a normal open pull request.
The command may push the current branch if it is not fully available on a remote. It can also ask where to push and offer to fork the base repository. That is a meaningful remote change, not a harmless prompt. Stop if the proposed repository or branch is not the one you expect.
4. Preview before creating when a push is possible
On gh 2.87.3, --dry-run prints the details instead of creating the pull request. It may still push Git changes, so treat it as a preview of the PR operation, not a no-network test:
$ gh pr create \
--base main \
--head feature/fix-login \
--title "Handle expired login sessions" \
--body-file /path/to/review-body.txt \
--dry-run
Would create a pull request in OWNER/REPOSITORY
...
Review the displayed base, head, title and body. If the branch must never be pushed or forked by this command, use an explicit head in the form OWNER:BRANCH and confirm that the branch already exists remotely. The manual also supports --head to skip the automatic forking or pushing behaviour. An organisation is not supported as the user in the USER:BRANCH head syntax in this installed command.
5. Choose how the body is supplied
Use --body-file when the description is more than a short paragraph or contains Markdown that deserves a separate review:
$ gh pr create \
--base main \
--head feature/fix-login \
--title "Handle expired login sessions" \
--body-file /path/to/review-body.txt
The special file name - reads the body from standard input. --template starts from a template file, while --editor opens an editor where the first line is the title and later lines are the body. Keep the body focused on the change, testing and known limitations.
--fill can take the title and body from Git commits; --fill-first uses the first commit, and --fill-verbose includes commit messages and bodies. Explicit --title or --body values override the corresponding autofilled content. Do not assume a commit message is a suitable review description until you have read the resulting text.
6. Add reviewers, labels and issue linkage carefully
Repeat options when you need several values:
$ gh pr create \
--base main \
--head feature/fix-login \
--title "Handle expired login sessions" \
--body-file /path/to/review-body.txt \
--reviewer REVIEWER_LOGIN \
--reviewer TEAM_NAME \
--label bug \
--assignee @me
Use the exact GitHub login, team handle or label name accepted by the repository. If the body says Fixes #123 or Closes #123, GitHub will close that issue when the pull request is merged. Only use that wording when closing the issue is genuinely intended.
Adding a project requires the project authorisation scope. If the command reports that scope is missing, the documented refresh command is:
$ gh auth refresh -s project
This changes local authentication state and may open a browser. Treat it as a deliberate security-sensitive step, and follow your organisation's approval rules.
7. Verify the created pull request
After creation, use the URL printed by gh and query the pull request directly:
$ gh pr view 123 --json number,state,isDraft,baseRefName,headRefName,title,url
{"baseRefName":"main","headRefName":"feature/fix-login","isDraft":true,"number":123,"state":"OPEN","title":"Handle expired login sessions","url":"https://github.com/OWNER/REPOSITORY/pull/123"}
Compare the base and head with your intended branches. Check that the title, draft state, reviewers and issue references are correct in the web interface as well. A successful command proves that GitHub accepted the request; it does not prove that the diff is safe to merge.
If you created the wrong pull request, do not try to undo it by rewriting the branch history. Close the pull request in GitHub, or use gh pr close 123 after checking the number. Closing it does not delete commits or undo a branch push. Removing a remote branch is a separate, potentially disruptive operation and is outside this workflow.
Common failure points
- Authentication required: check
gh auth status; exit status 4 means authentication is required. - Wrong target: pass
--baseexplicitly and inspectgit diff BASE...HEADbefore creating the request. - Unexpected push or fork prompt: stop, inspect the proposed destination, and use an explicit remote head that already exists when automatic publishing is not acceptable.
- Cancelled prompt: the command uses exit status 2 for cancellation. Nothing should be treated as created until a URL is printed and
gh pr viewconfirms it. - Failed run with recoverable input: the command provides
--recover STRING. Use the recovery value only when it came from the failedgh pr createrun; do not guess it.
Done means
- The intended commits and base branch were reviewed before creation.
- The title, body, head and base were explicit, or the interactive choices were checked carefully.
- Any possible push, fork, project authorisation or issue-closing action was deliberate.
- The printed URL and
gh pr viewconfirm the expected open or draft pull request. - You know how to close a mistaken pull request without confusing closure with deleting its commits.