name: marcgit
description: Use when the user wants to manage git worktrees with marcgit (a.k.a. mg) — creating a worktree for a feature/branch/PR, switching to trunk, listing or pruning worktrees, or moving in-progress edits into a fresh worktree. Triggers on phrases like "marcgit", "mg work", "create a worktree", "make a worktree for this feature", "spin up a branch worktree", "checkout PR N as a worktree", "prune old worktrees".
marcgit (mg)
marcgit is a git worktree manager. Each project is laid out as a trunk/
directory (the main worktree) plus sibling folders next to it, one per
branch / PR:
myproject/
├── trunk/ # main worktree — the anchor, never rename it
├── feat-login/ # mg work feat/login
└── fix-typo/ # mg pr 42 (PR head branch fix/typo)
A branch name's slashes become dashes in the folder name: feat/login →
feat-login. No nested subfolders are created.
CRITICAL: call the binary directly, not mg
mg is a shell function users install into their interactive shell
(eval "$(marcgit init bash)"). It will not exist in the non-interactive
shell you run Bash commands in. Always invoke the real binary marcgit, and
handle the printed path yourself.
How output works:
- Navigating commands (
work,pr,new,clone,trunk) print the target directory to stdout (themgwrapper wouldcdthere). Capture stdout to learn where the worktree is. - Everything else (status/info,
🌱/🧹messages) goes to stderr.
So instead of relying on cd, capture the path and act on it explicitly:
dir="$(marcgit work feat/login)" # stdout = new/existing worktree path
git -C "$dir" status # operate without changing your own cwd
If marcgit is not on PATH, tell the user to install it
(cargo install --git https://github.com/marc2332/git); do not silently fall
back to raw git worktree unless they ask.
Command reference
| Command | Alias | Effect | stdout |
|---|---|---|---|
marcgit new <project> |
n |
Create <project>/trunk and git init -b main it. |
trunk path |
marcgit clone <url> <project> |
c |
Clone <url> into <project>/trunk. |
trunk path |
marcgit work <branch> |
w |
Create (or jump to) a sibling worktree for <branch>; creates the branch off trunk's HEAD if it doesn't exist. Idempotent. |
worktree path |
marcgit pr <number> |
p |
Use gh to resolve PR #N's head branch, fetch it (pull/N/head), add a worktree named after that branch. Idempotent. Needs gh. |
worktree path |
marcgit trunk |
t |
Resolve the project's trunk path. |
trunk path |
marcgit list |
l |
git worktree list for the current project. |
listing |
marcgit remove <branch> |
r |
Force-remove the worktree and delete its branch, then point back at trunk. | trunk path |
marcgit prune <days> [-n|--dry-run] |
— | Remove sibling worktrees whose last commit is older than <days> days. Prompts [y/N] on stdin and skips dirty/stashed worktrees. -n previews only. |
— |
marcgit init <bash|zsh|fish|nushell> |
— | Print the shell wrapper code. | wrapper |
marcgit config init |
— | Write a default marcgit.toml at the project root. |
config path |
marcgit config path |
— | Print the marcgit.toml reachable from cwd. |
config path |
Notes:
- Every command except
new,clone, andinitmust run inside a project (any worktree of it). marcgit finds the project by locating the worktree literally namedtrunk. If it errors with "notrunkworktree found", you're not in a marcgit project — usemarcgit new/clonefirst, orcdinto one. workandprare idempotent: if the folder already exists they just return its path.pruneblocks on ay/Nconfirmation prompt. When running it non-interactively, always do a--dry-runfirst, show the user what would be pruned, and only then run the real prune (pipingyesonly with explicit user approval). Never auto-confirm destructive prunes.config init:init-submodules = true(default) makeswork/prrungit submodule update --init --recursivewhen a.gitmodulesexists.
Workflow: "create a worktree for this feature and copy my changes into it"
This is the headline use case. The user is working in one worktree (often
trunk), has in-progress edits, and wants a clean feature worktree that
contains those same edits. Literally copy the file edits across.
- Identify the source worktree — your current location, or the path the
user names. Capture its absolute path:
src="$(git rev-parse --show-toplevel)" - Pick the branch name from what the user said (e.g. "login feature" →
feat/login). Confirm the name if it's ambiguous. - Create the worktree and capture its path:
dst="$(marcgit work feat/login)" - Copy the edits from
srcintodst. Cover both tracked and untracked:
If# tracked, staged + unstaged modifications → as a patch git -C "$src" diff HEAD > /tmp/mg-feature.patch if [ -s /tmp/mg-feature.patch ]; then git -C "$dst" apply /tmp/mg-feature.patch fi # untracked files → copy verbatim, preserving relative paths git -C "$src" ls-files --others --exclude-standard -z | while IFS= read -r -d '' f; do mkdir -p "$dst/$(dirname "$f")" cp -p "$src/$f" "$dst/$f" donegit applyfails (e.g. the worktree branched from a different base), fall back to a 3-way apply:git -C "$dst" apply --3way /tmp/mg-feature.patch, and if that still conflicts, tell the user which files conflicted instead of guessing. - Verify the copy landed and report the destination:
Tell the user the new worktree path (git -C "$dst" status --short$dst) and what was copied.
Decide tracked-vs-everything by intent: git diff HEAD carries every change
relative to the last commit (staged + unstaged). If the user only wants specific
files, scope the patch with pathspecs: git -C "$src" diff HEAD -- path/a path/b.
Do NOT remove the edits from the source unless the user asks to move (not
copy) them. If they want a move, after verifying the copy you can
git -C "$src" checkout -- . / clean untracked — but confirm first, it's
destructive.
When the user instead describes a feature in prose (no existing edits), just
create the worktree (step 3), cd/operate inside $dst, and implement the work
there fresh.
Other common requests
- "work on PR 42" →
dst="$(marcgit pr 42)", then operate in$dst. Requiresghauthenticated for the repo. - "go back to trunk" →
marcgit trunk(path on stdout). - "what worktrees exist" →
marcgit list. - "delete the login worktree" →
marcgit remove feat/login(force-removes worktree + branch; confirm with the user since it deletes the branch). - "clean up stale worktrees older than 30 days" →
marcgit prune 30 -nfirst, show the list, thenmarcgit prune 30with user sign-off. - "start a new project" →
marcgit new <name>ormarcgit clone <url> <name>.
Gotchas
- The shell wrapper only
cds when stdout is a single existing directory; multi-line output (likelist) is printed, notcd'd. You read stdout directly anyway, so this only matters when you advise the user. - Folder names are sanitized (
/→-); when you need the folder for a branch, derive it the same way, or just trust the path marcgit prints. prunemeasures age by each worktree's last commit (git log -1) and refuses worktrees with uncommitted changes, untracked files, or stashes on their branch — so "nothing to prune" can mean "all candidates were dirty."