docs(edu): write git worktrees reference; update project status
Mark git worktrees and Markov Chains as complete in PROJECTS.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>quotesdb
parent
ce48ffac5a
commit
d01b5db905
@ -0,0 +1,170 @@
|
||||
# Git Worktrees
|
||||
|
||||
## What Are They?
|
||||
|
||||
A git worktree is a checked-out copy of a branch that lives in a separate directory on your filesystem, but shares the same underlying git repository (`.git` database) as your main clone.
|
||||
|
||||
Normally, a single git clone has one working directory — the files you see and edit. A worktree lets you have **multiple working directories at once**, each on a different branch, all tied to the same repository. You don't clone the repo a second time; you project a second (or third, or fourth) working directory from the one clone.
|
||||
|
||||
```
|
||||
~/.git/ ← shared: all objects, refs, config
|
||||
~/projects/myapp/ ← main worktree (branch: main)
|
||||
~/projects/myapp-feature/ ← linked worktree (branch: feature/auth)
|
||||
~/projects/myapp-hotfix/ ← linked worktree (branch: hotfix/crash)
|
||||
```
|
||||
|
||||
All three directories above read and write to the same `.git` object store. Commits made in any worktree are immediately visible to all others.
|
||||
|
||||
---
|
||||
|
||||
## Core Commands
|
||||
|
||||
### Add a worktree
|
||||
|
||||
```sh
|
||||
# Check out an existing branch into a new directory
|
||||
git worktree add ../myapp-feature feature/auth
|
||||
|
||||
# Create a new branch and check it out in one step
|
||||
git worktree add -b feature/new-thing ../myapp-new-thing main
|
||||
```
|
||||
|
||||
The first argument is the path for the new directory. The second is the branch name (or starting commit).
|
||||
|
||||
### List worktrees
|
||||
|
||||
```sh
|
||||
git worktree list
|
||||
```
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
/home/you/projects/myapp abc1234 [main]
|
||||
/home/you/projects/myapp-feature def5678 [feature/auth]
|
||||
```
|
||||
|
||||
### Remove a worktree
|
||||
|
||||
```sh
|
||||
# Remove the directory and deregister the worktree
|
||||
git worktree remove ../myapp-feature
|
||||
|
||||
# Force-remove even if there are untracked or modified files
|
||||
git worktree remove --force ../myapp-feature
|
||||
```
|
||||
|
||||
After deleting a linked worktree directory manually (e.g. with `rm -rf`), git still knows about it. Clean up the stale reference with:
|
||||
|
||||
```sh
|
||||
git worktree prune
|
||||
```
|
||||
|
||||
### Move a worktree
|
||||
|
||||
```sh
|
||||
git worktree move ../myapp-feature ../myapp-auth-feature
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Typical Workflows
|
||||
|
||||
### Work on two branches simultaneously
|
||||
|
||||
You're deep in a feature branch when a urgent bug report comes in. Instead of stashing your work or committing a WIP, you add a worktree:
|
||||
|
||||
```sh
|
||||
git worktree add ../myapp-hotfix main
|
||||
cd ../myapp-hotfix
|
||||
# fix the bug, commit, push — then come back to your feature
|
||||
```
|
||||
|
||||
Your feature working directory is untouched.
|
||||
|
||||
### Run tests against a different branch without switching
|
||||
|
||||
```sh
|
||||
git worktree add /tmp/myapp-test origin/release/2.0
|
||||
cd /tmp/myapp-test
|
||||
cargo test
|
||||
```
|
||||
|
||||
You keep your editor open on `main` while tests run elsewhere.
|
||||
|
||||
### Review a colleague's PR locally
|
||||
|
||||
```sh
|
||||
git fetch origin
|
||||
git worktree add ../review-pr-123 origin/pr/123
|
||||
cd ../review-pr-123
|
||||
# read, run, evaluate — then remove when done
|
||||
git worktree remove ../review-pr-123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How It Works Internally
|
||||
|
||||
When you run `git worktree add`, git:
|
||||
|
||||
1. Creates a subdirectory inside `.git/worktrees/<name>/` to store the worktree's private state (its `HEAD`, index, and a back-reference to the working directory path).
|
||||
2. Writes a `.git` *file* (not a directory) into the new working directory that points back to `.git/worktrees/<name>/`.
|
||||
3. Checks out the requested branch into the new directory.
|
||||
|
||||
The object database (all commits, trees, blobs) is shared. Only the index and `HEAD` are per-worktree.
|
||||
|
||||
---
|
||||
|
||||
## Limitations and When to Avoid Them
|
||||
|
||||
### Each branch can only be checked out once
|
||||
|
||||
The most important constraint: **a single branch cannot be checked out in two worktrees at the same time.** Git enforces this to prevent the two trees from diverging their indexes silently. If you try, you'll get an error:
|
||||
|
||||
```
|
||||
fatal: 'feature/auth' is already checked out at '/home/you/projects/myapp'
|
||||
```
|
||||
|
||||
Workaround: check out the second worktree at a specific commit (detached HEAD), or create a local tracking branch.
|
||||
|
||||
### Tools that look for `.git` as a directory can break
|
||||
|
||||
Some tools assume `.git` is a directory, not a file. A linked worktree has a `.git` *file* instead, which confuses:
|
||||
|
||||
- Older git GUIs that scan for `.git/` directories
|
||||
- Some shell prompts and plugins that detect git repos naively
|
||||
- Scripts that do `test -d .git`
|
||||
|
||||
### Build systems that use the working directory path
|
||||
|
||||
If your build system or language tooling caches absolute paths (e.g. Cargo's `target/` directory is placed relative to the workspace root), separate worktrees work fine — each has its own `target/` directory. But if a tool bakes the source path into cached artifacts, switching between worktrees may cause spurious rebuilds or cache invalidation.
|
||||
|
||||
### Hooks run in the worktree context, not the main repo
|
||||
|
||||
Git hooks in `.git/hooks/` apply to all worktrees. But the working directory (`$GIT_WORK_TREE`) will be the linked worktree path, not the main repo path. Hook scripts that assume a fixed working directory can behave unexpectedly.
|
||||
|
||||
### Not a substitute for branches in CI
|
||||
|
||||
Worktrees are a local developer convenience. Don't model CI pipelines around them — use proper branch checkouts in ephemeral CI environments instead.
|
||||
|
||||
### Submodules require extra care
|
||||
|
||||
Submodules are not automatically initialized in a new worktree. You need to run `git submodule update --init` inside each linked worktree where you need them.
|
||||
|
||||
### Avoid for long-lived parallel development
|
||||
|
||||
If you find yourself maintaining two worktrees of the same repo for weeks at a time, that's a signal you might want two separate clones or a proper branch strategy — not worktrees. Worktrees shine for short-lived parallel work (hotfixes, reviews, quick tests), not as a permanent parallel development setup.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Command |
|
||||
|---|---|
|
||||
| Add a worktree for an existing branch | `git worktree add <path> <branch>` |
|
||||
| Add a worktree and create a new branch | `git worktree add -b <branch> <path> <start-point>` |
|
||||
| List all worktrees | `git worktree list` |
|
||||
| Remove a worktree | `git worktree remove <path>` |
|
||||
| Clean up stale worktree records | `git worktree prune` |
|
||||
| Move a worktree | `git worktree move <old-path> <new-path>` |
|
||||
Loading…
Reference in New Issue