{"slug": "hop-cli-tool-for-jumping-around-worktrees", "title": "Hop: CLI tool for jumping around worktrees", "summary": "Syntaf released hop v0.1.0, a CLI tool that searches all git repositories under a user's code directory and jumps to the worktree holding a given branch, with tiered matching, tab completion, and an installer that configures zsh. The tool requires zsh, bash 3.2+, git, and curl, and supports pinning versions via HOP_REF.", "body_md": "Jump to the git worktree holding a branch.\n\nIf you keep a lot of worktrees, finding the one holding a given branch means\nrunning `git worktree list`\n\nin repo after repo — and the worktree's directory\nname usually isn't the branch name. `hop`\n\nsearches every repo you have at once\nand `cd`\n\ns you there.\n\n``` bash\n$ hop login-fix\n  repo    webapp\n  branch  feature/login-fix\n  status  in sync with origin, no tracked changes\n  path    ~/Code/webapp/.claude/worktrees/pr-4821-review\ncurl -fsSL https://raw.githubusercontent.com/Syntaf/hop/main/install.sh | bash\n```\n\nIf you would rather not pipe a script into a shell — reasonable — download it,\nread it, then run it. The `&&`\n\nmatters: without it a failed download still gets\nexecuted.\n\n```\ncurl -fsSL https://raw.githubusercontent.com/Syntaf/hop/main/install.sh -o hop-install.sh \\\n  && less hop-install.sh && bash hop-install.sh && rm hop-install.sh\n```\n\nOr from a checkout, which skips the download entirely:\n\n```\ngit clone https://github.com/Syntaf/hop.git && cd hop && ./install.sh\n```\n\nThe installer asks where you keep your repos, guessing from the conventional\ndirectories (`~/Code`\n\n, `~/src`\n\n, `~/Projects`\n\n, …) by counting git repos in each:\n\n```\nhop-install: which directory holds your git repos? [~/Projects]\n```\n\nPress Enter to take the guess, or type a path (`~`\n\nworks). It writes that as\n`HOP_CODE_ROOT`\n\nin your `.zshrc`\n\n. To skip the question — for a scripted or\nunattended install — set it yourself:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/Syntaf/hop/main/install.sh | HOP_CODE_ROOT=~/src bash\n```\n\nWith no terminal to prompt on, it uses the guess and tells you how to change it, rather than hanging.\n\nThen open a new shell. The installer puts `hop-resolve`\n\nin `~/.local/bin`\n\n,\n`hop.zsh`\n\nin `~/.config/hop`\n\n, and adds a short block to your `.zshrc`\n\n. It is\nidempotent — re-run it to upgrade, and it will leave an existing config alone\nrather than re-asking. To uninstall, delete those two files and that block.\n\nRequires zsh, bash 3.2+ (macOS stock is fine), git, and curl.\n\nPin a version with `HOP_REF`\n\n, and change where things land with `HOP_BIN_DIR`\n\n/\n`HOP_CONF_DIR`\n\n:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/Syntaf/hop/main/install.sh | HOP_REF=v0.1.0 bash\n```\n\n`HOP_REF`\n\nis what pins the version — fetching `install.sh`\n\nfrom a tag URL alone\ndoes not, since the script then fetches its payload from `main`\n\n. Note also that\n`raw.githubusercontent.com`\n\ncaches for a few minutes, so an install run\nimmediately after a push may get the previous commit.\n\n```\nhop <query>       cd into the matching worktree (prompts when ambiguous)\nhop               list every worktree it can find\nhop -l <query>    list matches without cd'ing\nhop -p <query>    print the path only:  code \"$(hop -p my-branch)\"\nhop -h            full help\n```\n\nYou never name a repo — every repo under `~/Code`\n\nis searched at once. A repo\nname is just an optional extra word to narrow things down: `hop webapp login-fix`\n\n. Tab completion covers branch and worktree names.\n\nMatching runs in tiers, best tier wins, and ties prompt you to pick rather than guessing:\n\n| tier | matches | example |\n|---|---|---|\n| 1 | exact branch | `claude/fix-thing` |\n| 2 | exact branch leaf | `fix-thing` |\n| 3 | exact worktree directory | `pr-4821-review` |\n| 4 | branch substring | `fix-th` |\n| 5 | worktree directory substring | `4821` |\n| 6 | every word appears somewhere | `webapp alice session` |\n\nWorktrees in the repo you're standing in sort first.\n\nWhen no worktree holds the branch, `hop`\n\nlooks for the branch itself — local\nbranches with no worktree, plus any remote-tracking ref already fetched — and\noffers to make one:\n\n``` bash\n$ hop session-timeout\nhop: no worktree holds alice/4821-session-timeout — it is a local branch in webapp.\n     create a worktree at ~/Code/webapp/.claude/worktrees/alice+4821-session-timeout? [y/N]\n```\n\nRemote-only branches are fetched first and the new local branch is set to track\nthem. Nothing is created without a `y/N`\n\n. If the branch is so new that no\nremote-tracking ref exists, `hop`\n\noffers to fetch the repo you're standing in,\nor you can name it: `hop <repo> <branch>`\n\n.\n\nNew worktrees land in `<repo>/.claude/worktrees/<branch>`\n\n, with `/`\n\nin the\nbranch name written as `+`\n\n. That's where Claude Code puts its worktrees; if you\nkeep yours elsewhere, `hop`\n\nstill *finds* them (it asks git, not the\nfilesystem), it just creates new ones there.\n\n| variable | meaning |\n|---|---|\n`HOP_CODE_ROOT` |\nwhere to scan for repos; set by the installer (default `~/Code` ) |\n`HOP_CACHE_TTL` |\nseconds to cache the scan (default `0` = always fresh) |\n`HOP_BANNER` |\n`0` , `off` or `no` to skip the arrival block |\n`NO_COLOR` |\nhonoured; colour is also dropped when piped |\n\nRepos are found by looking for `.git`\n\none and two levels below\n`HOP_CODE_ROOT`\n\n— so `~/Code/myrepo`\n\nand `~/Code/scratch/myrepo`\n\nboth work.\n\nA few decisions worth knowing, since they're deliberate:\n\n**Ambiguity always prompts.** Two repos with the same branch name give you a numbered picker. Non-interactive callers get the candidate list and a non-zero exit instead of a silently-chosen first match.**Untracked files are not counted.**`status`\n\nreports tracked changes only, because scanning for untracked files costs ~2s in a large monorepo versus ~0.1s without. That's why it says \"no tracked changes\" rather than \"clean\".**Registered-but-deleted worktrees are flagged,** not skipped, with the`git worktree prune`\n\ncommand to fix them.**The scan is fresh by default.** It's one parallel`git worktree list`\n\nper repo, ~0.15s across 30 repos. Set`HOP_CACHE_TTL`\n\nif you want it cached; a cache miss re-scans before reporting failure, so a just-created worktree is never invisible.\n\nA process can't change its parent shell's directory, so `hop`\n\nis a zsh function\nwrapping `hop-resolve`\n\n. The resolver prints a path on stdout and everything else\non stderr, which is what makes `hop -p`\n\ncomposable.\n\nBash users: `hop-resolve`\n\nitself is bash and works fine, but the `hop`\n\nfunction\nis zsh-only. A bash wrapper would be a short addition — it isn't written yet.", "url": "https://wpnews.pro/news/hop-cli-tool-for-jumping-around-worktrees", "canonical_source": "https://github.com/Syntaf/hop", "published_at": "2026-08-13 18:19:26+00:00", "updated_at": "2026-08-13 18:42:46.816938+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Syntaf", "hop", "git"], "alternates": {"html": "https://wpnews.pro/news/hop-cli-tool-for-jumping-around-worktrees", "markdown": "https://wpnews.pro/news/hop-cli-tool-for-jumping-around-worktrees.md", "text": "https://wpnews.pro/news/hop-cli-tool-for-jumping-around-worktrees.txt", "jsonld": "https://wpnews.pro/news/hop-cli-tool-for-jumping-around-worktrees.jsonld"}}