Create a git worktree from a GitHub issue
worktree can derive branch names directly from GitHub issues, and assign the issue to you at the same time.
The issue is read from your repository, or from the one it was forked from; see
Forks.
Basic Usage
Section titled “Basic Usage”worktree branch --github 42worktree branch --github "#42"worktree branch --github https://github.com/acme/widgets/issues/42worktree branch --github 42 --assignThe generated branch name is shown as the default in the prompt, so you can accept it or edit it before creation. A non-interactive run takes it without asking.
A pasted URL must be an issue (or pull request) URL of the repository chosen as described under Forks,
compared ignoring case. A URL for another repository exits 2 with invalid_value, naming both and
github.repository, before anything is created; set github.repository to that repository, or pass the number.
The URL’s host must be github.host (github.com, or www.github.com, when it is unset), compared ignoring case;
a URL on any other host exits 2 with invalid_value before anything is created, and the URL’s host is never
contacted.
--github and --assign read and assign in one repository, chosen in this order:
- The
github.repositorykey, anowner/reposuch asacme/widgets. It is read from git config, then from.worktree.yml. - The
upstreamremote, when it exists and is ongithub.host(github.comunless you set one). - The
originremote.
With no upstream remote and no key, that is origin, as it has always been, and the output is unchanged. In a
fork, origin is your copy and the issues usually live on upstream, so --github 42 reads issue 42 of
the original with no setup. An upstream that is not on github.host is skipped, not an error.
Whenever the repository is not the one behind origin, the output says which one it used and why. The spinner
reads Fetching GitHub issue #42 from acme/widgets (upstream), the assignment line ends in acme/widgets (upstream), and a failure names the repository in the same form. The part in brackets is upstream or
github.repository. On a GitHub Enterprise host the repository is written <host>/<owner>/<repo>, for
example ghe.example.com/acme/widgets (upstream); on github.com the text is unchanged.
If your issues are on the fork, or on a repository neither remote points at, pin it:
worktree config github.repository <owner/repo>github.repository is a team key: commit it under github: in
.worktree.yml to say it once for everyone, and any teammate can override it
from git config. Only owner/repo is accepted; a URL, a path or a trailing .git is refused. worktree config never asks for it, since most people have no fork, so set it by name.
A token that can read your fork may not be allowed on upstream; a private upstream answers 404, and the
error names the repository it asked.
GitHub Enterprise
Section titled “GitHub Enterprise”--github and --assign work against GitHub Enterprise Server and GitHub Enterprise Cloud with data residency
(*.ghe.com), one host at a time. The host is configured, not guessed from a remote, and the clone has to agree
with it.
Set the host
Section titled “Set the host”worktree config github.host ghe.example.comThat sets it for this repository, in git config. To say it once for everyone, commit it under github: in
.worktree.yml, where a teammate can still override it from git config:
github: host: ghe.example.comThe value is a bare hostname, such as ghe.example.com: no https://, port, path or trailing dot. worktree config never asks for it, since most people use github.com, so set it by name. github.com is accepted and means
the default, so a repository can switch back from a team-wide Enterprise value. Any other host under github.com,
such as api.github.com, is refused.
Which remotes count
Section titled “Which remotes count”The host has to be named twice, by github.host and by a remote of this clone. Requests go to the configured host
only when the remote the repository was read from is on that host. When github.repository supplies the
repository, origin has to be on the host instead. A merged .worktree.yml change therefore cannot send your
token to a new host on its own.
- An
upstreamon another host is skipped, as in Forks. - An
originon another host is an error that names the remote, the host expected andgithub.host. Correctgithub.host, or unset it to use github.com. - On github.com, a non-GitHub
originkeeps its usual error and adds how to set an Enterprise host.
Every common remote form is read: https://, ssh:// (and its older spellings git+ssh:// and ssh+git://),
git:// and the scp-like git@host:owner/repo.git. The host is compared ignoring case, and a port is ignored,
since an SSH port is not the API’s port. ssh.github.com and www.github.com count as github.com.
Authenticate
Section titled “Authenticate”The token comes from the GitHub CLI, for that host only:
gh auth login --hostname ghe.example.comFor a run without a stored gh login, such as CI, set GH_ENTERPRISE_TOKEN (Server hosts); worktree does not
read it itself, gh auth token --hostname <host> returns it. See the
gh environment reference. The stored github.token is
never used for an Enterprise host. See Authentication Resolution.
Host names
Section titled “Host names”A host ending in .ghe.com uses https://api.<host>. Any other host uses https://<host>/api/v3. The scheme is
always https, whatever scheme your remote uses, so a token never travels in clear. A repository on an Enterprise
host is written <host>/<owner>/<repo> in the output.
A private certificate authority
Section titled “A private certificate authority”Enterprise Server often has a certificate from your company’s own authority, which Node does not trust. The run
then fails with GitHub: could not reach <host>. and the TLS reason. Point Node at the authority’s certificate
(a PEM file) before running worktree:
export NODE_EXTRA_CA_CERTS=/path/to/company-ca.pemor, on Node.js 22.19 or 24.6 and later, let Node trust the operating system’s certificate store:
export NODE_USE_SYSTEM_CA=1Both are read when Node starts, so set them in the shell before the command. See the Node.js command-line reference.
How Branch Names Are Built
Section titled “How Branch Names Are Built”- Read issue number and title from the GitHub API.
- Sanitize the title into a branch-safe slug: lowercase letters, digits and dashes only, so a title can never add a
/or..to the name. - Cut the slug to at most 48 characters at the last dash, so it ends on a whole word. A single word longer than 48 characters is cut at 48.
- Optionally prepend a configured prefix based on issue type, giving
<prefix><number>-<slug>by default; abranchNametemplate in.worktree.ymlchanges the shape (see Branch name template).
For example, an issue 42 titled “Add a dark mode toggle to the settings page so that users can switch themes at runtime” becomes 42-add-a-dark-mode-toggle-to-the-settings-page-so. The name is the same whether or not you are prompted.
Prefix mapping
Section titled “Prefix mapping”Supported issue-type prefix mapping:
Feature->branchPrefix.featureBug->branchPrefix.bugfixTask->branchPrefix.chore
If a prefix key is not configured, no prefix is applied.
An issue with no type, or with a type other than these three, gets no prefix, whatever its labels say. That
default stays as it is so that branch names people already get do not change. To choose the prefix yourself,
add a github.branchType list to .worktree.yml, or a shared top-level branchType list. It can match the issue’s Type and its Labels (by name or
by { id: ... }), and ending it with - otherwise: feature gives the issues no rule matches a prefix:
github: branchType: - when: { Type: Bug } type: bugfix - when: { Labels: [chore, maintenance] } type: chore - otherwise: featureTo write the rules once for every provider you use, put the same list at the top level instead (branchType: rather
than github: and branchType:). A github.branchType list still wins over it for GitHub issues, and either way
the built-in mapping above is not used. See
Branch-type rules for how rules match, and
Branch name template to change the shape of the name.
Authentication Resolution
Section titled “Authentication Resolution”On github.com, authentication is resolved in this order:
- Existing
github.tokenin local git config. gh auth token --hostname github.comfrom GitHub CLI. The token is read on each run and is never saved to git config, so it cannot go stale or sit in clear text. Because the host is named, neitherGH_HOSTnor aghlogin to another host supplies it.- Interactive prompt for a token, then save to local git config. A non-interactive run has no prompt: it exits
2namingworktree config github.token <token>orgh auth login, and never prints a token.
Only a token you type at the prompt or set with worktree config github.token <token> is stored. If GitHub
refuses a token with 401, the error says so. A stored token, including one you have just typed at the
prompt, wins over gh, so when it is the one refused, running gh auth login does not help: the error tells
you to replace it with worktree config github.token <token>, or to remove it so gh auth token is used
again:
git config --unset northguild.worktree.github.tokenA token saved by an earlier version stays stored until you run that command or replace it.
On github.com, a token that came from gh gets a shorter hint: gh auth login, or
worktree config github.token <token>.
On a GitHub Enterprise host (github.host) there is one source only: gh auth token --hostname <host>. The stored github.token is never read for that host and never sent to it, and nothing is prompted for, since a typed token would be stored with no host. With no token the run exits 2 with missing_value, naming gh auth login --hostname <host>; a 401 names the same command. See GitHub Enterprise.
Reading an issue and assigning one need different amounts of access. A public repository answers an
unauthenticated read, which is why --github has never required a token there. Assigning always does, and
the token has to be allowed to write: Assigning needs a token whose account has push access to the
repository; a fine-grained token also needs the Issues (or Pull requests) permission set to Read and
write. So the first time you assign on such a repository you may be asked for one, unless the resolution
order above already supplies it.
Assigning the Issue
Section titled “Assigning the Issue”Creating the branch is the moment you have demonstrably started work, so worktree can say so on the issue
for you.
The decision is made in this order:
--assignassigns it to the authenticated user;--no-assignskips it for that run.github.autoAssign:truealways,falsenever. An explicitfalseis respected in every mode.- A non-interactive run assigns. The default is not saved, so one unattended run does not settle the key.
- Otherwise you are asked once, and your answer is saved to
github.autoAssign.
To change your mind later:
worktree config github.autoAssign trueworktree confirms the assignment against the API response rather than assuming it worked, and reports a
warning if the issue comes back without you on it. A failed assignment never stops the branch — the
worktree is created regardless.
Suggested Setup
Section titled “Suggested Setup”worktree config github.token <token>worktree config github.autoAssign trueworktree config branchPrefix.feature feature/worktree config branchPrefix.bugfix fix/worktree config branchPrefix.chore chore/You can also configure only missing values:
worktree config --missing --names github.token,github.autoAssign,branchPrefix.feature,branchPrefix.bugfix,branchPrefix.chore