Appearance
Checking Out Pull Requests
daft can check out a GitHub pull request or GitLab merge request straight into a worktree, so reviewing a PR is one command and never disturbs your current work:
bash
daft go pr:123This resolves PR #123, creates a worktree on its source branch, and drops you into it. Fork PRs work the same way, with no need to add a remote.
How authentication works
daft never stores tokens or talks to a forge over HTTP. Every forge lookup shells out to the GitHub CLI (gh) or the GitLab CLI (glab), which supply the authentication. Log in once:
bash
gh auth login # GitHub
glab auth login # GitLabFrom then on daft inherits that auth — including SSO, Enterprise, and self-hosted instances, which the CLIs already handle. Run daft doctor to check whether gh/glab are installed and authenticated.
Accepted forms
| Form | Resolves |
|---|---|
daft go pr:123 | GitHub PR #123 |
daft go mr:45 | GitLab MR !45 |
daft go https://github.com/o/r/pull/123 | The pasted PR URL |
daft go https://gitlab.com/g/r/-/merge_requests/45 | The pasted MR URL |
pr: and mr: are interchangeable aliases — the platform is detected from the repository's remote, so pr:45 on a GitLab repo resolves the merge request.
What daft configures
- Same-repo PR/MR — the source branch is fetched from the base remote and checked out as an ordinary tracking branch.
- Fork PR/MR — the head is fetched from the base repo's
refs/pull/123/head(GitHub) orrefs/merge-requests/45/head(GitLab), and the new branch is configured sogit pullupdates it from the PR head.git pushback to a contributor's fork is not configured yet.
A closed or merged PR still checks out (with a note) — inspecting merged work is legitimate.
Seeing every open PR in daft list
In a repository with a GitHub or GitLab remote, daft list shows the repo's working state, not just your worktrees: the pr column is on by default, and every open PR gets a row. Concretely:
- A worktree whose branch has an open PR shows it in place —
#123(GitHub) or!45(GitLab) — no extra row. - A local branch with an open PR is listed even without
--branches. - An open PR with no local presence at all — a colleague's branch, any fork PR — appears as a dimmed row built from the forge data: the PR title where a commit subject would be, the PR's last activity as its age. Fork PRs render
owner:branch(GitHub's own notation), because per-fork branch names collide — two contributors'patch-1s, a fork'smainversus yours. - Merged and closed PRs decorate existing rows (the purple "this branch is done" signal) but never add one.
- On branch rows and foreign-PR rows, the Owner column shows the PR author's name — the forge profile's full name, falling back to the login for profiles without one — rather than an owner deduced from commit history. The forge's answer is canonical there, and for foreign rows there is no local history to deduce from. (Fork rows'
owner:branchprefix stays the login: that's the fork's namespace, not a person.) Worktree rows are the exception: they describe your local checkout, so they keep the deduced owner — real name, real email in structured output — and the PR decorates only theprcell.
The open-PR rows and the pr column are one unit, governed by the same silent rules, so the list never nags:
- Repositories with no forge remote (and no
daft.forge.platformoverride) never show either. - If the background refresh fails in a way only you can fix —
gh/glabnot installed, authentication expired, repository access lost — column and rows disappear from the nextdaft liston, and stay gone. Fix the underlying issue (say,gh auth login) and the next refresh detects it and restores them, again persistently.daft doctorexplains a hidden column under "Forge integration".
Transient trouble (network down, rate limits) never hides anything. Force the forge overlay regardless with --columns +pr, or remove it — rows and column together — with --columns -pr. To prefer just your local worktrees permanently in a repository (say, a large open-source project with hundreds of open PRs), persist the opt-out there:
bash
git config -- daft.list.columns -pr--branches remains a superset — it shows all local branches, PR-bearing or not — and structured output marks the foreign-PR rows with "kind": "pr".
The pr column is also on by default in the daft sync and daft prune tables, decorated from the same cache and governed by the same silent rules — seeing each worktree's PR fate right where you decide what to update or prune is the point. --columns -pr (or daft.sync.columns / daft.prune.columns) removes it there the same way. Unlike daft list, sync and prune render the column without the open-PR rows: their tables list the worktrees the operation acts on.
In a color terminal, the number's color is the PR's fate: green/red/yellow for CI passing/failing/running, purple for merged — the "this worktree is done, prune it" signal — and dim for closed-without-merge. When color is off (NO_COLOR, piped output), the same states trail the number as a glyph instead: #123 ✓/✗/● for CI, #123 ◆ merged, #123 ○ closed — the signal never exists as color alone. Where the table is printed as plain text, supporting terminals also make the number a clickable link to the PR.
The live table never presents a cached status as current: every run re-verifies against the forge. PR numbers and rows render immediately but fateless, and the fresh verdict lands as one repaint — colors arrive, rows for PRs that opened since the snapshot appear, rows for PRs that closed drop — typically a second or two; the table holds its final frame briefly for it when needed. On the very first run in a repository, before any snapshot exists, the PR cells show a loading skeleton (like the size column) until that first refresh concludes and its open PRs take their rows. The one next-run case: a PR freshly opened from a branch that already exists locally surfaces on the following list (its row needs local git data the live table doesn't gather mid-run).
The forge-PR cache
The PR numbers, titles, states, and CI come from a per-repository snapshot of the forge's open and recently-merged PR listings, stored locally. It refreshes in the background — never while you wait:
- checking out a
pr:/mr:reference records that PR immediately; daft updateanddaft synckick a detached refresh after they finish (they already talked to the remote);- a
daft listwith theprcolumn in play kicks the same detached refresh — and while the live table is open, the fresh data slots into the PR column the moment it lands, so even a first-ever run decorates within a couple of seconds.
Every list re-verifies: a rendered status always has a verification behind it that the run itself started — or attached to, since lists fired while a refresh is already running share its verdict instead of stacking forge calls. A cold cache never blocks and never errors — cells simply wait for the refresh. The cache also powers tab completion: daft go pr:<Tab> completes the cached open PR numbers, each with its status as of the last refresh (the same ✓/✗/● glyphs as the column), its author, and its title — entirely from local data. Merged and closed PRs don't complete — their branch has usually been deleted on the forge — except ones you already have checked out, whose local branch keeps them a navigation target regardless. Forge words also never trip daft go's fetch-on-miss fallback (the spinner that fetches refs when a word matches no branch): a word with completions isn't a miss, and a pr:/mr: word can never match a branch anyway. All of it works cross-repo: daft go <repo> pr:<Tab> completes against the target repo's cache, and daft go <repo> pr:<number> checks the PR out over there — from anywhere.
Mixed-remote repositories
If a repository has both a GitHub and a GitLab remote, tell daft which forge a bare pr:/mr: reference means:
bash
git config daft.forge.platform gitlabA pasted URL is always unambiguous and ignores this setting. See Configuration for the full list of daft.forge.* keys, including CLI-binary and hostname overrides for Enterprise instances.