From 10be5dedb9a2492f5e6f074e7ac22ab99ce72506 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sun, 26 Jul 2026 17:58:40 -0700 Subject: [PATCH] Fix quoted Git path handling for dirty checks --- lib/bash/git/lib_git.sh | 46 +++++++++++++++++++++------------ lib/bash/git/tests/lib_git.bats | 18 +++++++++++++ 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/bash/git/lib_git.sh b/lib/bash/git/lib_git.sh index 7a4729e..bca0cfd 100644 --- a/lib/bash/git/lib_git.sh +++ b/lib/bash/git/lib_git.sh @@ -164,28 +164,42 @@ __git_path_matches_allowed_path__() { __git_only_path_dirty__() { local allowed_path="$1" - local status_output line path source_path destination_path + local status_file status_record status_code path related_path - status_output="$(git status --porcelain --untracked-files=no --ignore-submodules=none)" - [[ -z "$status_output" ]] && return 1 + std_make_temp_file status_file base-git-status || return 1 + if ! git status --porcelain=v1 --untracked-files=no --ignore-submodules=none -z > "$status_file"; then + std_unregister_cleanup_path "$status_file" + rm -f -- "$status_file" + return 1 + fi + if [[ ! -s "$status_file" ]]; then + std_unregister_cleanup_path "$status_file" + rm -f -- "$status_file" + return 1 + fi - while IFS= read -r line; do - [[ -z "$line" ]] && continue - path="${line:3}" - if [[ "$path" == *" -> "* ]]; then - source_path="${path%% -> *}" - destination_path="${path#* -> }" - if ! __git_path_matches_allowed_path__ "$source_path" "$allowed_path" || - ! __git_path_matches_allowed_path__ "$destination_path" "$allowed_path"; then - return 1 - fi - continue - fi + while IFS= read -r -d '' status_record; do + [[ -n "$status_record" ]] || continue + status_code="${status_record:0:2}" + path="${status_record:3}" if ! __git_path_matches_allowed_path__ "$path" "$allowed_path"; then + std_unregister_cleanup_path "$status_file" + rm -f -- "$status_file" return 1 fi - done <<< "$status_output" + if [[ "$status_code" == *R* || "$status_code" == *C* ]]; then + if ! IFS= read -r -d '' related_path || + ! __git_path_matches_allowed_path__ "$related_path" "$allowed_path"; then + std_unregister_cleanup_path "$status_file" + rm -f -- "$status_file" + return 1 + fi + fi + done < "$status_file" + + std_unregister_cleanup_path "$status_file" + rm -f -- "$status_file" return 0 } diff --git a/lib/bash/git/tests/lib_git.bats b/lib/bash/git/tests/lib_git.bats index 9c9954a..1c0e99d 100644 --- a/lib/bash/git/tests/lib_git.bats +++ b/lib/bash/git/tests/lib_git.bats @@ -478,6 +478,24 @@ EOF [ "$rc" -eq 0 ] } +@test "__git_only_path_dirty__ accepts tracked paths containing spaces" { + local repo="$TEST_TMPDIR/repo" + local rc + + init_git_repo "$repo" + mkdir -p "$repo/shared" + printf 'one\n' > "$repo/shared/hello world.txt" + commit_all "$repo" "Initial commit" + printf 'local change\n' >> "$repo/shared/hello world.txt" + + pushd "$repo" >/dev/null + __git_only_path_dirty__ "shared" + rc=$? + popd >/dev/null + + [ "$rc" -eq 0 ] +} + @test "__git_only_path_dirty__ does not treat sibling path prefixes as allowed" { local repo="$TEST_TMPDIR/repo" local rc