gg is a small Go CLI that resolves a GitHub repository to a local checkout path, clones it if needed, and is designed to be wrapped by a shell function so gg f credim can still land you inside the repo.
Repos are stored as a container directory with a bare Git repo plus worktrees:
<root>/<host>/<owner>/<repo>/
.bare/
main/
worktrees/<name>/
PR/<number>/
mise use -g github:puria/gg@latestgo install github.com/puria/gg@latestgg reads JSON config from:
$XDG_CONFIG_HOME/gg/config
~/.config/gg/config
You can print the active path with:
command gg config-pathYou can persist an alias with:
command gg alias ForkbombEu/credimi credimYou can create a starter config with:
command gg init-configExample config:
{
"root": "/home/puria/src",
"host": "github.com",
"aliases": {
"f": "ForkbombEu",
"credim": "credimi",
"fc": "ForkbombEu/credimi",
"f/credimi": "ForkbombEu/credimi"
}
}Alias resolution works in this order:
- exact alias first, so
gg fccan map straight toForkbombEu/credimi - per-segment alias expansion, so
gg f credimbecomesForkbombEu/credimi - final exact alias pass on
owner/repo, sof/credimican also be overridden directly
After adding an alias like this:
command gg alias ForkbombEu/credimi credimyou can just run:
gg credimIf the repo is missing, gg creates the repo container and checks out the default branch into main/. If it already exists, the shell wrapper cds into <repo>/main.
If you pass just an owner like gg ForkbombEu or an owner alias like gg f, gg goes to <root>/<host>/<owner>.
gg ForkbombEu
gg ForkbombEu/credimi
gg ForkbombEu credimi
gg f/credim
gg f credim
gg credimi newworktree
gg credimi 99
gg md init puria/example
gg md up
gg new puria/example
gg list credimi
gg status credimi
gg prune credimiThe binary prints the local checkout path and clones the repo first if it does not exist yet.
md, new, list, status, and prune are reserved command names. If you ever need an owner or repo alias with one of those names, use gg path ... as the escape hatch.
gg md init <owner/repo> creates a new local repository and seeds it with markdown metadata files from puria/md.
gg md up runs inside an existing Git repository and adds or updates those same metadata files in place.
If the first argument already resolves to a full repo by itself, the next argument is treated as a repo-local target:
gg credimi newworktree
gg credimi 99
gg credimi \#99
gg f credim feature-x
gg ForkbombEu credimi feature-xBehavior:
gg credimi newworktreecreates or reuses<repo>/worktrees/newworktree- it also creates a local branch named
newworktreefor that worktree gg credimi 99creates or reuses<repo>/PR/99- it checks out PR
99there withgh pr checkout 99 --detach - plain
gg credimigoes to<repo>/main - plain
gg ForkbombEugoes to<root>/<host>/ForkbombEu
The shell wrapper then cds into the resulting path, so it feels like the old gg flow.
These commands do not cd; they print information or perform maintenance:
gg list credimi
gg status credimi
gg prune credimi
gg prune --dry-run
gg prune --yesBehavior:
gg list credimiprintsmain,worktrees/*, andPR/*entries for the managed repogg status credimirunsgit status --short --branchfor each known worktreegg prune credimiremoves only greenworktrees/*andPR/*checkouts, then cleans stale Git worktree metadata and leftover empty directories- green entries are clean, have no matching stash, and have no local-only commits
- dirty files, stashes, and committed work that is not pushed or merged are kept
gg prunewithout a repo argument scans the current repo, owner directory, or host root such as~/src/github.comgg prune --dry-runonly prints the green/yellow/red plangg prune --yesapplies the no-argument scan without prompting
A binary cannot cd the parent shell on its own, so the normal setup is a tiny wrapper function that delegates to the Go binary.
command gg shell-init fish > ~/.config/fish/functions/gg.fishOr inline:
function gg --description 'manage git repos'
switch "$argv[1]"
case help -h --help version --version shell-init config-path init-config path alias list ls status prune rm
command gg $argv
return $status
case new md
set -l dir (command gg $argv)
or return $status
cd $dir
return $status
end
set -l dir (command gg path $argv)
or return $status
cd $dir
endeval "$(command gg shell-init bash)"