wt is a tool for git worktrees. It also controls the files that git ignores.
The command git worktree add makes a new checkout. It does not make the
files that git ignores. Each new worktree is therefore not
complete.
wt keeps one file and makes a link in each worktree. A git hook makes the
links.
wt clone git@github.com:you/project.git
cd project/mainThe command makes this structure:
project/
├── .bare/ the bare repository. It holds the store and the hook
├── .git the text `gitdir: ./.bare`
└── main/ the first worktree
Go into a worktree of the repository. Make the store, the config entry and the hook:
wt initThen move each ignored file into the store:
wt share .env .auth .envrcThe command moves each file one time. It then makes a link in each worktree that is already present. One command is therefore sufficient for the full repository.
The command refuses a path that contains a tracked file. Git supplies each
tracked file, and a link hides it. Give the ignored path instead. For example,
give terraform/terraform.tfvars and not terraform/.
wt add eng-1234The hook makes the links in the new worktree. The program that makes the
worktree is not important. wt add, git worktree add, your editor and an
agent all start the hook.
The command finds the branch in this sequence:
- A local branch.
- A remote branch
origin/eng-1234. - A fetch of
origin/eng-1234from the network. - A new branch from the default branch.
Steps 1 and 2 do not use the network. The command does step 3 only if steps 1
and 2 find no branch. Step 3 prevents an error: without it, wt can make a
local branch while a remote branch with the same name is already present.
Use the option --no-fetch to stay offline. Use the option --fetch to do a
fetch first.
wt denywt <worktree> is the short form of wt cd <worktree>. Both accept the name
of the worktree directory. Both also accept the name of the branch, because a
branch can move after you make the worktree. The start of a directory name is
sufficient when only one worktree matches.
A command wins over a worktree with the same name. wt ls therefore prints
the worktrees, also when a worktree has the name ls. The long form
wt cd ls goes to that worktree, and wt add prints a warning when it makes
a directory with the name of a command.
wt lsThe command prints each worktree, the branch it is on, and how many links are correct. It changes nothing.
The command git clean -xdf removes a link. A worktree can also be older than
the config. The command wt sync makes each link that is absent:
wt syncwt delete eng-1234The command removes the worktree. It then deletes the branch if git merged the branch.
wt pruneThe command asks origin which branches are still present. It then removes each worktree whose work is complete, and it deletes the branch:
| Condition | Example |
|---|---|
| The base branch contains the branch, and origin still has it. | A merge commit, and the branch is not deleted yet. |
| Origin no longer has the upstream branch. | A squash merge or a rebase merge, and the branch is deleted. |
The second condition is the one that matters for a squash merge. Such a merge writes a new commit, so the branch is not an ancestor of the base branch, and the merge is complete nonetheless.
wt prune removes no branch that is only on your machine. Each condition
above needs proof that the branch reached origin: origin has it now, or the
config names an upstream branch that origin no longer has. A new branch from
wt add holds no commit of its own, so the base branch contains it, and the
test for a merge alone cannot tell that branch from finished work. The proof
of a push is the rule that keeps wt add eng-1234 safe until you push it.
The command also keeps:
| Condition | Reason |
|---|---|
| The worktree has a modified file or an untracked file. | Use --force. |
| You are in the worktree. | A removal would leave your shell in a path that is absent. |
| The worktree is on the base branch, or on no branch. | There is no branch to test. |
Use --dry-run to read the list and change nothing. Use --no-fetch to stay
offline; the command then uses the refs it already has.
The command also removes the administrative files of a worktree whose directory you deleted by hand.
Git does not count the files that it ignores. An ignored file that is not in
the store is therefore lost without a warning, as in wt delete.
The commands wt share and wt sync compare the bytes before they make a
link.
| Condition | Result |
|---|---|
| The two files are the same. | wt replaces the file with a link. |
| The two files are different. | wt keeps the file and prints a message. |
Use the option --force to replace a file that is different.
| Command | Function |
|---|---|
wt init |
Make the store, the config entry and the hook. Move no files. |
wt share <path>… |
Move ignored paths into the store. Link them in each worktree. |
wt add <branch> [dir] |
Make a worktree for a branch. |
wt cd <worktree> |
Print the path of a worktree. The shell integration then changes directory. |
wt <worktree> |
The short form of wt cd <worktree>. |
wt ls |
Print each worktree, its branch and the condition of its links. |
wt sync |
Compare each worktree with the config. Make the links that are absent. |
wt delete <worktree> |
Remove a worktree. Delete its branch if git merged the branch. |
wt prune |
Remove each worktree whose work is finished. Delete its branch. |
wt clone <url> [dir] |
Clone into the .bare layout. Make the first worktree. |
wt shell-init fish |
Print the shell integration. |
One global file holds the shared paths for each repository:
# ~/.config/wt/config.toml
[repos."github.com/you/project"]
link = [".env.override", ".auth", ".envrc"]The key is the remote URL in a normal form. Each form of the URL gives the same key, so one file is correct for SSH and for HTTPS, and on each machine. Put this file in your dotfiles to keep the list.
wt reads $XDG_CONFIG_HOME/wt/config.toml when that variable is set.
The file holds no secret. It holds only path names. The content stays in the
store, and the store stays in the repository at <common-directory>/shared/.
cargo build --release
ln -sf "$PWD/target/release/wt" ~/.local/bin/wtnix profile install github:lorenzolfm/wtAdd this line to config.fish:
wt shell-init fish | sourceThe integration also gives completion, and it changes the directory after
wt add, wt clone, wt cd and wt <worktree>:
| Position | Candidates |
|---|---|
wt <TAB> |
each command and each worktree |
wt add <TAB> |
each local branch and each remote branch |
wt delete <TAB> |
each worktree |
wt cd <TAB> |
each worktree |
wt share <TAB> |
each ignored path that the config does not have, and files |
MIT