ghq-style repository manager · built-in fuzzy finder

Jump to any repository.
Or any of its worktrees.

gm clones into one predictable host/user/repo tree, and Ctrl+G takes you to any of them — ranked by how well they match and how often you go there.

brew install jedipunkz/gm/gm
View on GitHub →
gm — ~/ghq

Live demo with sample data — type to filter, to move, Enter to jump,/help for commands, and click ctrl-w for worktrees. The ranking is the same algorithm gm uses.

Why gm

What it does beyond ghq

An existing ghq tree works untouched. gm adds the parts you would otherwise glue together yourself.

01

The finder is built in

No ghq list | fzf | cd pipeline to assemble. The best match is the row at the bottom, next to the prompt, so the usual choice costs zero keystrokes.

02

It ranks matches itself

A fuzzy score first — a literal substring in the repository name beats letters scattered across the path — then frecency to break ties.

03

The selection is described

Path, remote, branch, working-tree status, visit count and the last three commits with their refs, so similar clones are easy to tell apart.

04

Worktrees are first-class

Ctrl-W swaps the list for the git worktrees of the repository under the cursor. /create <branch> checks one out without leaving the finder.

05

One config file

Roots, theme and key bindings live in gm.toml. $GHQ_ROOT and ghq.root are still honored, so an existing ghq tree needs no migration.

06

gm create sets up origin

A new repository gets git init and its origin remote in one step — something ghq leaves to you.

Not implemented, deliberately: Mercurial / Subversion / Darcs cloning (they are still listed), bare clones, partial clones, parallel import, --vcs, and per-URL ghq.<url>.root roots.

Get started

Install in three steps

  1. 1

    Install the binary

    Homebrew on macOS or Linux downloads a prebuilt binary; no Go needed.

    brew install jedipunkz/gm/gm

    Or build from source with Go 1.25 or newer:

    go install github.com/jedipunkz/gm@latest
  2. 2

    Bind it to your shell

    gm prints the chosen path, so it composes with $(...). One line in your rc file turns that into a cd on Ctrl+G.

    fish — ~/.config/fish/config.fish
    gm shell fish | source
    zsh — ~/.zshrc
    eval "$(gm shell zsh)"
    bash — ~/.bashrc
    eval "$(gm shell bash)"
  3. 3

    Clone and jump

    Clones land under the root; press Ctrl+G anywhere to jump.

    gm get jedipunkz/gm
    # then press Ctrl-G and type gm

Requirements: git on $PATH, and a true-color terminal for the themes to look as intended.gm version says which version you have.

Usage

Keys

Ctrl+W swaps the repository list for the git worktrees of the repository under the cursor, and swaps it back. Going back keeps the query, the cursor and the highlights as they were.

KeyRepository listWorktree list
any characterFilterFilter
↑ / Ctrl-PMove upMove up
↓ / Ctrl-NMove downMove down
EnterPrint the repository path and exitPrint the worktree path and exit
Ctrl-WShow the worktrees of the selected repositoryBack to the repositories
Ctrl-Alt-BOpen the remote in a browserOpen the remote in a browser
Ctrl-GBack to the repositories
EscClear the query, then the filter, then quitBack to the repositories
Ctrl-CQuit without printingQuit without printing

Everything else is ordinary text editing (Ctrl-A, Ctrl-E, Ctrl-U…), exceptCtrl-W, which no longer deletes the word before the cursor. Esc undoes one layer of narrowing at a time — the worktree list, then the query, then the filter — and only quits once there is nothing left to undo.

Slash commands

A / at the start of the input types a command instead of a filter; Tab completes it./create and /remove ask first, in a panel over the list, and do the work without leaving the finder.

CommandWhat it does
/helpShow the command list; q or Esc closes it
/dirtyShow only repositories with uncommitted work; Esc shows all
/create <repo>Create a repository, after asking; adds it to the list
/create <branch>In the worktree list: check that branch out as a worktree
/get <repo>Same as gm get, then go to the clone
/removeRemove the selected repository, or worktree, after asking
/worktreesSame as Ctrl-W
/remoteSame as Ctrl-Alt-B

Worktrees go where gm decides

The branch name is the only thing to type. /create <branch> starts the branch fromHEAD if it does not exist yet. The leading dot keeps worktrees out of the repository list: the walk never descends into a dotted directory. The main worktree cannot be removed from the worktree list.

~/gm/github.com/jedipunkz/gm/            the repository
~/gm/.worktrees/github.com/jedipunkz/gm/feat/login

Subcommands

<repo> accepts a full URL, git@host:user/repo.git, host/user/repo,user/repo, or a bare repo (resolved against git config github.user).

CommandWhat it does
gmOpen the fuzzy finder; print the selected path
gm get [-u] [-p] [--shallow] [-b <branch>] [-s] [-l] <repo>...Clone into the tree; -u updates an existing clone
gm list [-p] [-e] [--unique] [<query>]List repositories (-p full paths, -e exact match, --unique shortest unambiguous name)
gm status [--dirty] [--unpushed] [-a] [-p]List every repository and worktree with uncommitted or unpushed work; never fetches
gm remove [--dry-run] [-y] <repo>...Remove a repository and its worktrees after confirming, pruning empty parents (gm rm also works)
gm create [-p] <repo>Create and git init a repository with origin already set
gm wt <create|remove> [-y] <repo> <branch>Add or remove a worktree from a script; the finder is better for doing it by hand
gm migrate [--dry-run] [-y] [-r] <dir>...Move an existing clone into the tree, using its origin remote; -r searches the directory
gm root [--all]Print the root directory
gm shell <fish|zsh|bash>Print the Ctrl-G binding
gm versionPrint the installed version

Configuration

One optional file

~/.config/gm/gm.toml (or $XDG_CONFIG_HOME/gm/gm.toml). A file that cannot be parsed, or that holds a key gm does not know, is an error rather than a file half ignored.

gm.toml
root         = "~/ghq"         # or ["~/ghq", "~/src"], searched in order
theme        = "tokyonight"
launch_key   = "ctrl-g"        # the shell key that opens gm
worktree_key = "ctrl-w"        # the finder key that lists worktrees
remote_key   = "ctrl-alt-b"    # the finder key that opens the remote

Root resolution order

The first one set wins, so an existing ghq tree works untouched.

  1. $GM_ROOT
  2. root in gm.toml
  3. git config --get-all gm.root
  4. $GHQ_ROOT
  5. git config --get-all ghq.root
  6. ~/ghq

Key bindings

launch_key is the chord gm shell binds and must be a plain Ctrl chord;worktree_key and remote_key are the finder's own and also take alt andshift. Ctrl is written ctrl-, ctrl+, c- or ^. After changing launch_key, re-run gm shell <shell>.

ChordReaches gm
ctrl-<letter>Everywhere
ctrl-alt-<letter>Nearly everywhere: Alt is sent as an ESC prefix
ctrl-shift-<letter>Only with the Kitty keyboard protocol — Ghostty, kitty, WezTerm, foot, recent Alacritty

Themes

Set theme in gm.toml. An unknown name is an error listing the valid ones. The palettes below are read from gm's source when this site is built.

Ranking

Match quality first, frecency second

Typing filters by fuzzy match, scored so that a literal substring beats a subsequence pieced together from elsewhere, and the repository name counts for more than the user name. The host is shared by nearly every repository, so matches there cost points instead of earning them.

Frecency only breaks ties between equally good matches. Visits are recorded in$XDG_STATE_HOME/gm/frecency.json (default ~/.local/state/gm/frecency.json); delete it to start over.

Last visitScore = visits
within the last hour× 4
within the last day× 2
within the last week× 0.5
older× 0.25