Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .claude/agent-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ update or remove the stale line rather than leaving both.
- Branch names with `/` are supported and slug to `-` in the directory name; do not reject slash branches.
- User-facing docs (README, CHANGELOG) must not mention features that never shipped; 1.0.0 is the first release, so there is no prior version to reference.
- CHANGELOG follows GitHub release-notes format (`## What's Changed` + PR URLs), listing shipped features only — not Keep a Changelog / Unreleased / pre-1.0 fix archaeology. PR URLs remain even after the git history wipe.
- Do not mention previous code, removed subcommands, or pre-v1.0 archaeology anywhere in the tree (docs, comments, tests). Forward-looking constraints and current git-behavior rationale are fine; unused merged-branch/`rev-list` guidance and a dedicated `clean` unknown-command test are not.
- Do not mention previous code, removed subcommands, or pre-v1.0 archaeology anywhere in the tree (docs, comments, tests). Forward-looking constraints and current git-behavior rationale are fine.
- Do not remove an `init` container on agent-seeding failure; return nonzero and leave the directory.
- `main` is always the stable release; README curl install pins `main` (not version tags). Do not describe `main` as a development/moving target.
- `clean` supports `--gone` and `--merged` (detecting direct, rebased, and squash-merged PRs); `--older-than` is omitted.
- When resolving CodeRabbit review comments, verify each claim against the code before acting; report skipped findings with the reason rather than silently dropping them.
40 changes: 36 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,23 @@ consequence is that `feature/x` and `feature-x` compete for one directory;
the directory (`_branch_at`). Do not "fix" that by inventing a suffixed variant:
a directory whose name the user cannot predict is worse than an error.

**Nothing destructive.** No subcommand removes a worktree or deletes a branch.
Anything that destroys user data must report by default and act only under an
explicit `--apply`, must use `git branch -d` and never `-D`, and must route
directory removal through a user-configurable command.
**Nothing destructive without `--apply`.** `rm` and `clean` report by default and modify state only when `--apply` is explicitly passed. Local branch deletions use `git branch -d` (falling back to `-D` on `clean` once confirmed gone/merged, or on `rm` when `--apply` is passed), and worktree directory removals route through `TREES_RM_CMD` when configured (defaulting to `git worktree remove`).

**`TREES_RM_CMD` is the one place the safety net comes off.** `git worktree
remove` refuses a worktree with uncommitted changes or untracked files; a custom
command gets the path and nothing else. So a path target must be validated
against `git worktree list` before removal — `cmd_rm`'s path arm does this, and
without it `TREES_RM_CMD="rm -rf" git trees rm . --apply` would delete the
container root and the bare store. Keep the gate on worktree registration rather
than on the resolved branch: a detached-HEAD worktree legitimately has none.

**`clean --apply` reports partial failure.** Its loops continue past a failed
worktree removal or branch delete, but the exit status is nonzero if any failed,
matching `cmd_rm`. Do not turn that back into an unconditional `return 0` —
scripting `clean` depends on it.




**`track` only ever sets `origin/<branch>`.** Same remote, same name. There is
no flag for an arbitrary upstream, and `origin` is hardcoded throughout —
Expand All @@ -68,6 +81,15 @@ created from `origin/main` silently gets `origin/main` as its upstream and will
push there. The new-branch path must pass `--no-track`, then let `cmd_track` set
the correct upstream. Live in `cmd_add`; any change there needs a fresh test.

## Git pitfall: worktree paths are physical

`git worktree list` reports the *physical* path. Resolve any user-supplied
directory with `pwd -P`, never plain `pwd`, before comparing against it or
passing it to `_branch_at` — on macOS `$TMPDIR` lives under `/var`, a symlink to
`/private/var`, so the logical path matches nothing and a real worktree looks
unregistered. `cmd_rm`'s path arm depends on this; the smoke suite catches it
because its fixtures are built under `mktemp -d`.

## Testing

Run the suite:
Expand Down Expand Up @@ -118,6 +140,16 @@ What the suite covers:
- **install.sh** — places the binary; seeds `~/.config/git-trees/AGENTS.md` from
the template under a redirected `HOME`; does not overwrite an existing config
file
- **rm** — dry run vs `--apply`, worktree removal by branch and by path (a
slugged directory whose name is not a branch name, so the path arm is the one
that runs), `-d` escalating to `-D` so an unmerged branch is still deleted
under `--apply`, refusal of a directory that is not a registered worktree even
with `TREES_RM_CMD` set, and custom `TREES_RM_CMD` routing
- **clean** — `--gone` identification and deletion, `--merged` identification
across direct merges, rebased commits, and squash-merged PRs, zero-commit
fresh branch preservation, dry run vs `--apply`, worktree directories actually
gone after `--apply`, each selector run on its own, and custom `TREES_RM_CMD`
routing

Two assertion shapes are easy to get wrong:

Expand Down
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,57 @@ One entry per branch with upstream, ahead/behind, last commit date, clean/dirty,
and path (relative to the project root). Includes branches with no worktree,
shown with path `(none)`. `--json` emits the same fields as an array.

### `git trees rm <branch|path> [--apply]`

Removes a worktree and deletes its branch. Accepts either a branch name or a worktree path.

By default (without `--apply`), reports what would be removed without making any changes. Pass `--apply` to perform the removal.

Worktree directories are removed via `TREES_RM_CMD` if configured, or `git worktree remove`. Local branches are deleted using `git branch -d` (falling back to `git branch -D` if needed).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

A path target must be a worktree git already knows about; `rm` refuses any other
directory.

> **`TREES_RM_CMD` removes git's safety net.** `git worktree remove` refuses a
> worktree that has uncommitted changes or untracked files. A custom command such
> as `rm -rf` receives only the path and makes no such check, so `rm --apply` and
> `clean --apply` will destroy uncommitted work without warning. Set it only if
> you want `git worktree remove --force` semantics deliberately.


### `git trees clean [--merged|--gone] [--apply]`

Reports or removes stale worktrees and branches.

Selectors:
- `--gone`: branches whose upstream remote branch was deleted (`[gone]`)
- `--merged`: branches merged into the default branch. Automatically detects direct merges, rebased/cherry-picked commits, and squash-merged PRs while leaving fresh 0-commit branches intact.

Passing neither selector runs both `--gone` and `--merged`.

By default (without `--apply`), `clean` operates in dry-run mode and prints matching branches/worktrees without deleting them. Pass `--apply` to execute removals. Worktree directories are removed via `TREES_RM_CMD` if set (defaulting to `git worktree remove`) — see the warning above — and branches are deleted using `git branch -d` (falling back to `git branch -D` for gone/squash-merged branches).

`clean --apply` keeps going when an individual removal fails, reporting each one
on stderr, and exits nonzero if any of them did.



## Removing worktrees

`git-trees` does not delete anything. Remove a worktree and its branch with git:
`git-trees` provides `rm` and `clean` for removing worktrees and branches:

```bash
git trees rm feature-x --apply # remove a single worktree and its branch
git trees clean --apply # remove merged and gone branches/worktrees
```

Both subcommands default to dry-run mode (report only) unless `--apply` is passed.
Both also delete unmerged work once `--apply` is given — `-d` escalates to `-D`.
If you have set `TREES_RM_CMD`, read the warning under [`git trees
rm`](#git-trees-rm-branchpath---apply) first: it removes git's check for
uncommitted changes.
Comment on lines +300 to +304

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Specify “unmerged branches,” not “unmerged work.”

The phrase can be confused with uncommitted changes. The following warning concerns uncommitted changes, while -d and -D control branch merge status. Use precise terms to avoid misunderstanding the deletion behavior.

Proposed wording
-Both also delete unmerged work once `--apply` is given — `-d` escalates to `-D`.
+Both can delete unmerged branches once `--apply` is given — branch deletion starts
+with `git branch -d` and escalates to `git branch -D`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Both subcommands default to dry-run mode (report only) unless `--apply` is passed.
Both also delete unmerged work once `--apply` is given — `-d` escalates to `-D`.
If you have set `TREES_RM_CMD`, read the warning under [`git trees
rm`](#git-trees-rm-branchpath---apply) first: it removes git's check for
uncommitted changes.
Both subcommands default to dry-run mode (report only) unless `--apply` is passed.
Both can delete unmerged branches once `--apply` is given — branch deletion starts
with `git branch -d` and escalates to `git branch -D`.
If you have set `TREES_RM_CMD`, read the warning under [`git trees
rm`](`#git-trees-rm-branchpath---apply`) first: it removes git's check for
uncommitted changes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 300 - 304, Update the README description of the two
subcommands to say they delete unmerged branches when --apply is provided,
replacing the ambiguous “unmerged work” wording while preserving the separate
warning about uncommitted changes.


Alternatively, you can use plain git:

```bash
git worktree remove <path>
Expand All @@ -271,6 +319,7 @@ git worktree prune
| `TREES_ORG` | *(unset)* | Default org; if unset, bare repo names are rejected |
| `TREES_AGENTS_TEMPLATE` | `~/.config/git-trees/AGENTS.md` | Seeded at the container root by `init` (and `root --agents`) |
| `TREES_NO_PUSH` | *(unset)* | Any non-empty value: `add`/`track` never create a branch on `origin` |
| `TREES_RM_CMD` | *(unset)* | Custom command for worktree directory removal (defaults to `git worktree remove`). Bypasses git's uncommitted-work check — see [`git trees rm`](#git-trees-rm-branchpath---apply) |

## Shell wrapper (optional)

Expand All @@ -288,8 +337,8 @@ trees() {

## Known limitations

- Removing stale worktrees and branches is manual; nothing here deletes.
- `list` spawns several processes per branch — fine for dozens, slow for hundreds.

- `add` ignores `base` when the branch already exists rather than failing.
- Branch names beginning with `-` are unsupported: `add` parses them as options
and reports `unknown option`. There is no `--` end-of-options marker.
Expand Down
Loading
Loading