Docs
Reference
How to do things with gm, and what each command, key and setting does. To install it, seeInstall.
Finder
Ctrl-G in the shell (after gm shell) or gm opens it. The best match is the bottom row, next to the prompt; details of the selected row are on the right. While gm waits on git or GitHub, the line under the prompt says what it is waiting on.
Lists
| List | Open with | Shows | Enter |
|---|---|---|---|
| Repositories | Ctrl-G in the shell, or gm | Every repository under every root | Go there |
| Worktrees | Ctrl-W | Worktrees of the selected repository | Go there |
| Branches | Ctrl-L | Local branches, and remote ones with no local branch | Go to its worktree, creating it if needed |
| Pull requests | Ctrl-J | Open pull requests, drafts marked [draft] | Go to its worktree, creating it with gh pr checkout --worktree if needed |
| Issues | Ctrl-[ | Open issues | Open the issue's detail panel: q back, ↑↓ scroll, Ctrl-B browser, Ctrl-Y copy the URL |
- The other lists belong to the repository selected when they were opened. Going back restores the query and cursor.
- In the pull request list,
#42finds number 42. - In the issue list,
#42finds number 42;Enteropens its text full screen — the description and each comment in their own frame, headings, code, lists and links drawn — ,qorEscgoes back,Spacepages,Shift-Spaceorbpages back (bworks everywhere;Shift-Spaceneeds a terminal that reports keys — Ghostty, kitty, WezTerm, foot, recent Windows Terminal),jkgo a line,Ctrl-Bopens it in a browser,Ctrl-Ycopies its URL (OSC52: tmux needsset-clipboard, and some terminals ask to be allowed). - Picking a remote branch makes a local branch that tracks it.
Keys
| Key | Action |
|---|---|
| type | Filter the list |
↑ ↓ / Ctrl-P Ctrl-N | Move the cursor; PgUp PgDn move a screen |
Enter | Go to the selected row (gm prints its path and the shell cds there). With a command typed, run it |
Ctrl-W Ctrl-L Ctrl-J | Open the worktree / branch / pull request list. The same key goes back to the repositories |
Ctrl-[ | Open the issue list; the same key goes back to the repositories. Its byte is Esc, so at the repositories, with nothing to undo, it opens the issue list instead of quitting — Ctrl-C always quits |
Ctrl-Alt-B | Open the repository's remote in a browser |
Tab | Accept the command completion shown after the cursor |
Esc | Undo one step: back to the repositories, clear the query, clear /dirty /unpushed, then (unless the issue list owns it, above) quit |
Ctrl-G | Same as Esc, but never quits |
Ctrl-C | Quit at once |
Ctrl-W Ctrl-L Ctrl-J Ctrl-[ Ctrl-Alt-B can be changed: see Key bindings.
Slash commands
- Type
/in an empty box to start a command;Tabcompletes it,Enterruns it. - To act on a search result, end the query with
;:gm;/removeselectsgmand removes it. /create,/removeand/expireshow what will happen, uncommitted changes included, and ask first:ygoes ahead,norEsccancels.
| Command | Action | Works in |
|---|---|---|
/get [flags] <repo> | Clone (runs gm get) and go to it | All |
/create [--ssh] <repo> | Create a new repository with origin set (--ssh: SSH URL) | Repositories |
/create <branch> | Check the branch out as a worktree; a new branch starts from HEAD | Worktrees, branches, PRs |
/remove | Remove the selected repository with its worktrees, or the selected worktree and its merged branch. Never the main worktree | Repositories, worktrees |
/expire <days>d | Remove worktrees whose HEAD has not moved for that long, and their merged branches | Worktrees |
/dirty /unpushed | Toggle a filter: only repositories with uncommitted changes / unpushed commits, counting their worktrees. Both on: both must hold. Switching one on scans again | Repositories |
/worktrees /branches /prs /issues /browse | Same as Ctrl-W Ctrl-L Ctrl-J Ctrl-[ Ctrl-Alt-B | All |
/help | Show the command list | All |
Commands
| Command | Does |
|---|---|
gm | Open the finder; print the chosen path |
gm get <repo>... | Clone (alias clone) |
gm list [<query>] | Print repositories (alias ls) |
gm status | List uncommitted and unpushed work |
gm create <repo> | Start a new repository (alias new) |
gm remove <repo>... | Remove repositories and their worktrees (alias rm) |
gm wt create|remove|expire | Manage worktrees from scripts |
gm migrate <dir>... | Move existing clones into the tree |
gm root [--all] | Print the root(s) |
gm shell <fish|zsh|bash> | Print the shell key binding |
gm version, gm help | Version, usage. -h after a subcommand lists its flags |
<repo> to clone or create is a URL or a shorthand: see Repository references.remove and wt take a query instead: an exact name, user/repo orhost/user/repo, else a substring; more than one match is an error listing them.
gm get [flags] <repo>...
Clones each repository under the first root, submodules included. One already cloned is skipped unless -u is given.
| Flag | Effect |
|---|---|
-u, --update | Update a repository already cloned instead of skipping it |
--ssh | Clone over SSH |
--shallow | git clone --depth 1 |
-b <branch> | Clone that branch only |
--no-recursive | Skip submodules (cloned by default) |
-s, --silent | Quiet git clone |
-l, --look | Afterwards, start a shell inside the repository |
-u fetches and fast-forwards the checked-out branch. It never merges: a dirty tree or a diverged branch is left alone, with a skip line saying why.
gm list [flags] [<query>]
Prints host/user/repo for every repository whose path contains the query (case-sensitive).
| Flag | Effect |
|---|---|
-p, --full-path | Absolute paths |
-e, --exact | The query must equal the name, user/repo, or host/user/repo |
--unique | The shortest name no other repository shares: gm, else jedipunkz/gm, else the full path |
gm status [flags]
Lists repositories and worktrees with uncommitted changes or unpushed commits, e.g.3 changed files, 2 unpushed. It never fetches, so it works offline; behind counts are as of the last fetch.
| Flag | Effect |
|---|---|
--dirty | Only rows with uncommitted changes |
--unpushed | Only rows with unpushed commits (with --dirty: both) |
-a | Also rows that are clean and in sync |
-p, --full-path | Absolute paths |
gm create [--ssh] <repo>
Makes the directory under the first root, runs git init, sets origin (SSH with--ssh), and prints the path. Refused if the repository already exists or the directory is not empty.
gm remove [--dry-run] [-y] <repo>...
Removes each repository and every worktree checked out of it. Before asking, it lists what would be lost: uncommitted changes in the repository and in each worktree.
| Flag | Effect |
|---|---|
--dry-run | Show what would happen, change nothing |
-y | Do not ask for confirmation |
gm wt create | remove | expire
The finder's worktree actions, for scripts. remove and expire take --dry-run and -y.
| Command | Effect |
|---|---|
gm wt create <repo> <branch> | Check the branch out as a worktree (new branches start from HEAD); prints its path |
gm wt remove <repo> <branch> | Remove that worktree, then its branch if merged. Never the main worktree |
gm wt expire <repo> <days>d | Remove worktrees whose HEAD has not moved for that many days, and their merged branches. Ones with uncommitted changes are kept |
gm migrate [--dry-run] [-y] [-r] <directory>...
Moves existing clones to the path their origin says, asking for each, and repairs their worktrees.-r searches each directory for repositories instead and skips the ones it cannot move (noorigin, destination exists, already cloned under another root, a worktree or submodule).
gm root / gm shell / gm version
gm rootprints the first root, where new repositories go;--allprints every root.gm shell <fish|zsh|bash>prints thelaunch_keybinding: it runsgmandcds to the result. Re-run it after changinglaunch_key.gm versionprints the version;gm helpthe usage.
| Shell | Add to |
|---|---|
| fish | gm shell fish | source in ~/.config/fish/config.fish |
| zsh | eval "$(gm shell zsh)" in ~/.zshrc |
| bash | eval "$(gm shell bash)" in ~/.bashrc |
Scripting
- Paths go to stdout (the finder's choice,
create,wt create,migrate); progress, warnings and prompts go to stderr.cd "$(gm)"works. - Exit status:
0success (quitting the finder and answering no included),1error,2wrong usage. - Prompts read
[y/N]; a closed stdin answers no. Pass-yto skip them. - Flags go before the arguments;
-b mainand--branch=mainare the same.
Where repositories go
Repository references
A <repo> to clone or create becomes a URL, and its host/user/repo is the directory under the root.
| Written as | Directory |
|---|---|
https://github.com/u/r | github.com/u/r |
git@github.com:u/r.git | github.com/u/r |
example.com/u/r | example.com/u/r (over https) |
u/r | github.com/u/r |
r | github.com/<you>/r: git config github.user, else $USER |
Directory layout
~/ghq/github.com/jedipunkz/gm/ repository
~/ghq/.worktrees/github.com/jedipunkz/gm/feat/login worktree of branch feat/login
~/ghq/.worktrees/github.com/jedipunkz/gm/.forks/bob/main worktree of a pull request from bob's fork- Worktrees live under
<root>/.worktrees/. gm never looks inside dot-directories, so they are not listed as repositories. - Mercurial and Subversion working copies are listed, but gm only clones with git.
Configuration
gm.toml
~/.config/gm/gm.toml (or $XDG_CONFIG_HOME/gm/gm.toml), optional. Every key is optional too. A typo, an unknown key or a wrong type is an error naming the file, so a setting is never silently ignored.
root = "~/ghq" # or ["~/ghq", "~/src"], searched in order
theme = "tokyonight"
launch_key = "ctrl-g"
worktree_key = "ctrl-w"
branch_key = "ctrl-l"
pr_key = "ctrl-j"
issue_key = "ctrl-["
remote_key = "ctrl-alt-b"| Key | Default | Meaning |
|---|---|---|
root | ~/ghq | Where repositories live; a list is searched in order, the first gets new clones |
theme | tokyonight | The finder's colors |
launch_key | ctrl-g | The shell key that opens gm |
worktree_key | ctrl-w | Finder: worktree list |
branch_key | ctrl-l | Finder: branch list |
pr_key | ctrl-j | Finder: pull request list |
issue_key | ctrl-[ | Finder: issue list |
remote_key | ctrl-alt-b | Finder: open the remote in a browser |
Roots
The first source that is set wins, so an existing ghq setup works untouched:
| # | Source |
|---|---|
| 1 | $GM_ROOT (colon-separated) |
| 2 | root in gm.toml |
| 3 | git config gm.root (every value) |
| 4 | $GHQ_ROOT (colon-separated) |
| 5 | git config ghq.root (every value) |
| 6 | ~/ghq |
~ expands to your home directory; every root must be absolute. New clones go to the first root; all roots are listed and searched.
Key bindings
Write a chord as Ctrl, optionally Alt and Shift, plus one letter: ctrl-g, ctrl-alt-b(Ctrl+Alt+B, c-a-b and ^b also work). An invalid chord is an error.
launch_keymust be a plain Ctrl chord, and notctrl-mctrl-ictrl-j(terminals send them as Enter, Tab, newline).- The five finder keys must differ from each other, and cannot take
ctrl-mctrl-ior, as plain chords,ctrl-cctrl-nctrl-pctrl-g.issue_keymay also bectrl-[, the byteEscis sent as. - Avoid what the shell keeps for itself:
ctrl-r,ctrl-c,ctrl-d,ctrl-z.
| Chord | Works in |
|---|---|
ctrl-<letter> | Every terminal |
ctrl-alt-<letter> | Nearly every terminal |
ctrl-shift-<letter> | Only terminals with the Kitty keyboard protocol (Ghostty, kitty, WezTerm, foot, recent Alacritty); elsewhere it arrives as ctrl-<letter> |
Themes
tokyonight catppuccin-frappe catppuccin-latte catppuccin-macchiato catppuccin-mocha dracula kanagawa-wave rose-pine solarized-dark solarized-light
The default is tokyonight. See the palettes on the home page.
Ranking
- Typing keeps repositories whose
host/user/repoholds the typed characters in order, ignoring case. - Better matches go lower: a literal substring beats scattered characters, and a match in the repository name counts more than one in the user name or host.
- Ties, and the list before you type, are ordered by frecency: how often and how recently you visited.
- A visit is choosing a row in the finder, or cloning, creating or moving a repository with gm.
Requirements and environment
- macOS or Linux (WSL2 included),
giton$PATH, and a true-color terminal for the themes. - The pull request and issue lists need
gh, logged in. The branch list uses git's own credentials. - Network calls never prompt for a password and give up after a deadline (20 s for the branch list, 30 s for pull requests and issues), saying what timed out.
| File | Purpose |
|---|---|
~/.config/gm/gm.toml | Settings ($XDG_CONFIG_HOME/gm/ if set). Optional |
~/.local/state/gm/frecency.json | Visit log ($XDG_STATE_HOME/gm/ if set). Delete it to reset the ranking |
<root>/.worktrees/ | Worktrees |
| Variable | Effect |
|---|---|
GM_ROOT | Roots, colon-separated; overrides every other setting |
GHQ_ROOT | Roots, colon-separated; used when gm has no setting of its own |
XDG_CONFIG_HOME, XDG_STATE_HOME | Where gm.toml and the visit log live |
GM_LOOK | Set by gm get -l in the shell it starts: the repository's path |