Skip to content
Merged
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
16 changes: 10 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,16 @@ What the suite covers:
- **help/dispatch** — `help`, `--help`, and unknown command
- **outside a repo** — `list` and `root` both exit nonzero
- **init** — happy path over `file://`; the gitdir pointer, bare store, seeded
`AGENTS.md`, and `origin/*` refs it must produce; the container root having no
work tree; refusal on an existing directory; **rollback when the post-clone
fetch fails**, and that a retry then works; `--host`/`--dir` with a missing
value exiting promptly rather than hanging
`AGENTS.md` content matching the template, and `origin/*` refs it must produce;
the container root having no work tree; refusal on an existing directory;
**rollback when the post-clone fetch fails**, and that a retry then works;
`--host`/`--dir` with a missing value exiting promptly rather than hanging;
missing template warns and writes no `AGENTS.md`
- **root** — printing the container, adopting a bare container by writing the
`.git` pointer under the store's own name, rejecting a plain directory
- **root --agents** — seeds when absent; never overwrites a regular file; treats
a **broken symlink** as occupied; no-ops without a template; keeps stdout to
the path alone
a **broken symlink** as occupied; warns and no-ops without a template; keeps
stdout to the path alone
- **add** — `.`, `..` and `'has space'` reported as bad branch names rather than
directory collisions, and rejected before `Preparing worktree`; upstream
exactly `origin/feature-x` for an existing remote branch and exactly
Expand All @@ -114,6 +115,9 @@ What the suite covers:
- **list** — text output, branches with no worktree shown as `(none)`,
`--json` parsing, and a worktree whose path contains `\` and `"` round-tripping
through `json.load`
- **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

Two assertion shapes are easy to get wrong:

Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,23 @@ cd git-trees && ./install.sh # → ~/.local/bin
./install.sh /usr/local/bin # or anywhere else
```

**Convenience — one-line curl.** Fetches only the script:
**Convenience — curl.** Fetches the script and the agents template (skips the
template if that path is already occupied, including a broken symlink):

```bash
mkdir -p ~/.local/bin
curl -o ~/.local/bin/git-trees \
https://raw.githubusercontent.com/brightdigit/git-trees/v1.0.0/git-trees
mkdir -p ~/.local/bin ~/.config/git-trees
tmp=$(mktemp) && curl -fsSL -o "$tmp" \
https://raw.githubusercontent.com/brightdigit/git-trees/v1.0.0/git-trees \
&& mv "$tmp" ~/.local/bin/git-trees
chmod +x ~/.local/bin/git-trees
if [ ! -e ~/.config/git-trees/AGENTS.md ] && [ ! -L ~/.config/git-trees/AGENTS.md ]; then
tmp=$(mktemp) && curl -fsSL -o "$tmp" \
https://raw.githubusercontent.com/brightdigit/git-trees/v1.0.0/AGENTS.md.template \
&& mv "$tmp" ~/.config/git-trees/AGENTS.md
fi
```

One thing to know about this path: it does **not** create
`~/.config/git-trees/AGENTS.md`, so the documented default for
`TREES_AGENTS_TEMPLATE` points at a file you don't have — seeding is simply
skipped, which is harmless, but `init` will not write an `AGENTS.md`. Use the
installer if you want the template too.

The URL above is pinned to the `v1.0.0` tag, so it gives the same script every
The URLs above are pinned to the `v1.0.0` tag, so they give the same files every
time. Swapping `v1.0.0` for `main` tracks the development branch instead — a
moving target, and not what you want for an install you intend to keep.

Expand All @@ -156,7 +157,7 @@ export TREES_ORG=your-org

That's usually all you need — `TREES_HOST` defaults to `github.com` and
`TREES_AGENTS_TEMPLATE` defaults to `~/.config/git-trees/AGENTS.md`, which is
where `install.sh` puts the template.
where `install.sh` and the curl install put the template.

With `TREES_ORG` set, `git trees init my-repo` expands to `your-org/my-repo`.
Without it, bare repo names are rejected and you must pass `org/repo`.
Expand Down
5 changes: 4 additions & 1 deletion git-trees
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ _ensure_git_pointer() {
# 1 when neither test holds — an inverted error path waiting to happen. Both
# guards are written out.
_seed_agents() {
[ -f "$TREES_AGENTS_TEMPLATE" ] || return 0
if [ ! -f "$TREES_AGENTS_TEMPLATE" ]; then
echo "git trees: no agents template at $TREES_AGENTS_TEMPLATE — skipping AGENTS.md seed (run ./install.sh or set TREES_AGENTS_TEMPLATE)" >&2
return 0
fi
# Any existing path (including a broken symlink) is occupied — never overwrite.
# -e is false for a broken symlink; -L is true. Both tests are needed.
if [ -e "$1/AGENTS.md" ] || [ -L "$1/AGENTS.md" ]; then
Expand Down
50 changes: 47 additions & 3 deletions tests/smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ assert_contains "init reports the default branch" "$out" "initialized init-ok (d
assert_ok "init wrote the gitdir pointer" test -f "$TMP/init-ok/.git"
assert_ok "init created the bare store" test -d "$TMP/init-ok/trees-bare.git"
assert_ok "init seeded AGENTS.md" test -f "$TMP/init-ok/AGENTS.md"
assert_eq "init seeded content from the template" \
"$(cat "$TMP/init-ok/AGENTS.md")" "seeded-agents-template"
# The refspec is what gives a bare clone its remote-tracking refs.
assert_ok "init set up origin/* refs" \
git -C "$TMP/init-ok" show-ref --verify --quiet refs/remotes/origin/main
Expand Down Expand Up @@ -193,6 +195,19 @@ assert_ok "init --dir with a value" bash "$T" init "file://$ORIGIN" --dir init-f
assert_fail "init with no argument" bash "$T" init
assert_fail "init unknown option" bash "$T" init "file://$ORIGIN" --nope

# Missing template: init still succeeds, warns on stderr, and writes no AGENTS.md.
err=$(mktemp)
out=$(env TREES_AGENTS_TEMPLATE="$TMP/no-such-template" \
bash "$T" init "file://$ORIGIN" --dir init-notemplate 2>"$err")
rc=$?
assert_eq "init exits 0 without a template" "$rc" "0"
assert_contains "init still reports success without a template" "$out" \
"initialized init-notemplate"
assert_contains "init warns on stderr when the template is missing" "$(cat "$err")" \
"no agents template"
assert_ok "init without a template writes no AGENTS.md" \
test '!' -e "$TMP/init-notemplate/AGENTS.md"

# --- root --------------------------------------------------------------------

section "root"
Expand Down Expand Up @@ -233,11 +248,17 @@ assert_ok "root --agents succeeds with a broken symlink present" bash "$T" root
assert_ok "a broken symlink counts as occupied" test -L "$S3/AGENTS.md"
assert_eq "the symlink was not replaced" "$(readlink "$S3/AGENTS.md")" "$TMP/definitely-absent"

# No template configured → no-op, still succeeds.
# No template configured → no-op with a stderr warning, still succeeds.
S4=$(new_container seed-notemplate)
rm -f "$S4/AGENTS.md"
assert_ok "root --agents is a no-op without a template" \
env TREES_AGENTS_TEMPLATE="$TMP/no-such-template" bash "$T" root "$S4" --agents
err=$(mktemp)
out=$(env TREES_AGENTS_TEMPLATE="$TMP/no-such-template" \
bash "$T" root "$S4" --agents 2>"$err")
rc=$?
assert_eq "root --agents exits 0 without a template" "$rc" "0"
assert_eq "root --agents stdout is only the path without a template" "$out" "$S4"
assert_contains "root --agents warns on stderr when the template is missing" \
"$(cat "$err")" "no agents template"
assert_ok "nothing was written without a template" test '!' -e "$S4/AGENTS.md"

# stdout stays clean for $(git trees root) even while seeding.
Expand Down Expand Up @@ -440,6 +461,29 @@ else
fail "could not create a worktree with a backslash in its path"
fi

# --- install.sh --------------------------------------------------------------

section "install.sh"
REPO=$(dirname "$T")
IHOME="$TMP/install-home"
IDEST="$TMP/install-bin"
mkdir -p "$IHOME" "$IDEST"
out=$(HOME="$IHOME" bash "$REPO/install.sh" "$IDEST" 2>&1)
rc=$?
assert_eq "install.sh exits 0" "$rc" "0"
assert_ok "install.sh placed the binary" test -x "$IDEST/git-trees"
assert_ok "install.sh seeded the agents template" \
test -f "$IHOME/.config/git-trees/AGENTS.md"
assert_eq "install.sh template matches AGENTS.md.template" \
"$(cat "$IHOME/.config/git-trees/AGENTS.md")" \
"$(cat "$REPO/AGENTS.md.template")"
echo CUSTOM > "$IHOME/.config/git-trees/AGENTS.md"
HOME="$IHOME" bash "$REPO/install.sh" "$IDEST" >/dev/null 2>&1
rc=$?
assert_eq "install.sh rerun exits 0" "$rc" "0"
assert_eq "install.sh does not overwrite an existing template" \
"$(cat "$IHOME/.config/git-trees/AGENTS.md")" "CUSTOM"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# --- summary -----------------------------------------------------------------

cd "$TMP" || exit 1
Expand Down
Loading