Skip to content

Commit b230dd7

Browse files
committed
Address CodeRabbit review: escape inventory paths
1 parent f3c1b37 commit b230dd7

4 files changed

Lines changed: 64 additions & 35 deletions

File tree

lib/commands/clean.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ _clean_merged() {
149149
is_main="${line#is_main }"
150150
;;
151151
"path "*)
152-
dir="${line#path }"
152+
dir=$(_tsv_unescape_field "${line#path }")
153153
;;
154154
"branch "*)
155-
branch="${line#branch }"
155+
branch=$(_tsv_unescape_field "${line#branch }")
156156
;;
157157
esac
158158
done <<EOF

lib/commands/list.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ cmd_list() {
3535
is_main="${line#is_main }"
3636
;;
3737
"path "*)
38-
path="${line#path }"
38+
path=$(_tsv_unescape_field "${line#path }")
3939
;;
4040
"branch "*)
41-
branch="${line#branch }"
41+
branch=$(_tsv_unescape_field "${line#branch }")
4242
;;
4343
"status "*)
4444
status="${line#status }"
@@ -87,10 +87,10 @@ EOF
8787
is_main="${line#is_main }"
8888
;;
8989
"path "*)
90-
path="${line#path }"
90+
path=$(_tsv_unescape_field "${line#path }")
9191
;;
9292
"branch "*)
93-
branch="${line#branch }"
93+
branch=$(_tsv_unescape_field "${line#branch }")
9494
;;
9595
"status "*)
9696
status="${line#status }"

lib/core.sh

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -223,28 +223,19 @@ _emit_worktree_record() {
223223
status=$(_worktree_record_status "$wt_detached" "$wt_locked" "$wt_prunable")
224224

225225
printf "is_main %s\n" "$is_main"
226-
printf "path %s\n" "$wt_path"
227-
printf "branch %s\n" "$branch"
226+
printf "path %s\n" "$(_tsv_escape_field "$wt_path")"
227+
printf "branch %s\n" "$(_tsv_escape_field "$branch")"
228228
printf "status %s\n\n" "$status"
229229
}
230230

231-
# List registered git worktrees for a repository.
232-
# Usage: list_worktree_records repo_root
233-
# Output: blank-line-delimited records with is_main/path/branch/status fields
234-
list_worktree_records() {
235-
local repo_root="$1"
236-
local repo_root_canonical
237-
repo_root_canonical=$(canonicalize_path "$repo_root" || printf "%s" "$repo_root")
238-
239-
local porcelain_output
240-
241-
porcelain_output=$(git -C "$repo_root" worktree list --porcelain 2>/dev/null) || return 0
242-
231+
_parse_worktree_records() {
232+
local repo_root_canonical="$1"
233+
local delimiter="$2"
243234
local wt_path="" wt_branch="" wt_detached=0 wt_locked=0 wt_prunable=0
235+
local field
244236

245-
local line
246-
while IFS= read -r line; do
247-
case "$line" in
237+
while IFS= read -r -d "$delimiter" field; do
238+
case "$field" in
248239
"")
249240
_emit_worktree_record "$repo_root_canonical" "$wt_path" "$wt_branch" "$wt_detached" "$wt_locked" "$wt_prunable"
250241
wt_path=""
@@ -261,13 +252,13 @@ list_worktree_records() {
261252
wt_locked=0
262253
wt_prunable=0
263254
fi
264-
wt_path="${line#worktree }"
255+
wt_path="${field#worktree }"
265256
;;
266257
"branch refs/heads/"*)
267-
wt_branch="${line#branch refs/heads/}"
258+
wt_branch="${field#branch refs/heads/}"
268259
;;
269260
"branch "*)
270-
wt_branch="${line#branch }"
261+
wt_branch="${field#branch }"
271262
;;
272263
detached)
273264
wt_detached=1
@@ -279,13 +270,26 @@ list_worktree_records() {
279270
wt_prunable=1
280271
;;
281272
esac
282-
done <<EOF
283-
$porcelain_output
284-
EOF
273+
done
285274

286275
_emit_worktree_record "$repo_root_canonical" "$wt_path" "$wt_branch" "$wt_detached" "$wt_locked" "$wt_prunable"
287276
}
288277

278+
# List registered git worktrees for a repository.
279+
# Usage: list_worktree_records repo_root
280+
# Output: blank-line-delimited records with is_main/path/branch/status fields
281+
list_worktree_records() {
282+
local repo_root="$1"
283+
local repo_root_canonical
284+
repo_root_canonical=$(canonicalize_path "$repo_root" || printf "%s" "$repo_root")
285+
286+
if git -C "$repo_root" worktree list --porcelain -z >/dev/null 2>&1; then
287+
_parse_worktree_records "$repo_root_canonical" "" < <(git -C "$repo_root" worktree list --porcelain -z 2>/dev/null)
288+
else
289+
_parse_worktree_records "$repo_root_canonical" $'\n' < <(git -C "$repo_root" worktree list --porcelain 2>/dev/null)
290+
fi
291+
}
292+
289293
# Get the status of a worktree from git
290294
# Usage: worktree_status worktree_path
291295
# Returns: status (ok, detached, locked, prunable, or missing)
@@ -319,10 +323,10 @@ worktree_status() {
319323
is_main="${line#is_main }"
320324
;;
321325
"path "*)
322-
path="${line#path }"
326+
path=$(_tsv_unescape_field "${line#path }")
323327
;;
324328
"branch "*)
325-
branch="${line#branch }"
329+
branch=$(_tsv_unescape_field "${line#branch }")
326330
;;
327331
"status "*)
328332
record_status="${line#status }"
@@ -463,10 +467,10 @@ resolve_target() {
463467
is_main="${line#is_main }"
464468
;;
465469
"path "*)
466-
wt_path="${line#path }"
470+
wt_path=$(_tsv_unescape_field "${line#path }")
467471
;;
468472
"branch "*)
469-
wt_branch="${line#branch }"
473+
wt_branch=$(_tsv_unescape_field "${line#branch }")
470474
;;
471475
esac
472476
done <<EOF
@@ -748,10 +752,10 @@ list_worktree_branches() {
748752
is_main="${line#is_main }"
749753
;;
750754
"path "*)
751-
path="${line#path }"
755+
path=$(_tsv_unescape_field "${line#path }")
752756
;;
753757
"branch "*)
754-
branch="${line#branch }"
758+
branch=$(_tsv_unescape_field "${line#branch }")
755759
;;
756760
esac
757761
done <<EOF

tests/core_resolve_target.bats

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,31 @@ teardown() {
159159
[ "$status" = "ok" ]
160160
}
161161

162+
@test "list_worktree_records preserves registered path containing newline" {
163+
if ! git -C "$TEST_REPO" worktree list --porcelain -z >/dev/null 2>&1; then
164+
skip "git worktree list --porcelain -z is not available"
165+
fi
166+
167+
local newline_path="${TEST_REPO}-external"$'\n'"newline"
168+
git -C "$TEST_REPO" worktree add "$newline_path" -b newline-path --quiet
169+
local expected_path
170+
expected_path=$(cd "$newline_path" && pwd -P)
171+
172+
local records escaped_path
173+
escaped_path=$(_tsv_escape_field "$expected_path")
174+
records=$(list_worktree_records "$TEST_REPO")
175+
176+
[[ "$records" == *"path $escaped_path"$'\n'"branch newline-path"* ]]
177+
178+
local status
179+
status=$(worktree_status "$expected_path")
180+
[ "$status" = "ok" ]
181+
182+
resolve_worktree "newline-path" "$TEST_REPO" "$TEST_WORKTREES_DIR" ""
183+
[ "$_ctx_worktree_path" = "$expected_path" ]
184+
[ "$_ctx_branch" = "newline-path" ]
185+
}
186+
162187
# ── discover_repo_root from worktree ──────────────────────────────────────────
163188

164189
@test "discover_repo_root returns main repo root when called from a worktree" {

0 commit comments

Comments
 (0)