From 6ff00ab7b038fb3b93e9824094eff50b1e616718 Mon Sep 17 00:00:00 2001 From: sandroid Date: Sat, 20 Apr 2024 01:21:09 +0200 Subject: [PATCH 1/3] Fix: file commands do not work with paths containing special characters Git escapes special characters in it's output when core.quotePath is true or unset. Git always expects unquoted file paths as input. This leads to issues when we consume output from git and use it to build input for other git commands. This commit ensures we always feed unqoted paths to git commands. The _forgit_list_files function is introduced to handle usage of git ls-files with the -z flag, which ensures unquoted paths. It replaces the direct calls to git ls-files in _forgit_reset_head, _forgit_stash_push and _forgit_checkout_file. In _forgit_add the current approach of using colors to separate unstaged from staged files is replaced with a call to _forgit_ls_files with the appropriate flags to only list unstaged files. In _git_reset_head the -z option is added to the git diff command to ensure unqoted paths. Since git clean does not support the -z flag, we disable core.quotePath by passing a configuration parameter in _forgit_clean. --- bin/git-forgit | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index 53142683..43c659b9 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -159,6 +159,21 @@ _forgit_is_file_tracked() { git ls-files "$1" --error-unmatch &> /dev/null } +_forgit_list_files() { + local rootdir + rootdir=$(git rev-parse --show-toplevel) + # git escapes special characters in it's output when core.quotePath is + # true or unset. Git always expects unquoted file paths as input. This + # leads to issues when we consume output from git and use it to build + # input for other git commands. Use the -z flag to ensure file paths are + # unquoted. + # uniq is necessary because unmerged files are printed once for each + # merge conflict. + # With the -z flag, git also uses \0 line termination, so we + # have to replace the terminators. + git ls-files -z "$@" "$rootdir" | uniq | tr '\0' '\n' +} + _forgit_log_preview() { local sha sha=$(echo "$1" | _forgit_extract_sha) @@ -329,13 +344,10 @@ _forgit_edit_add_file() { # git add selector _forgit_add() { _forgit_inside_work_tree || return 1 - local changed unmerged untracked files opts + local files opts # Add files if passed as arguments [[ $# -ne 0 ]] && { _forgit_git_add "$@" && git status -su; return $?; } - changed=$(git config --get-color color.status.changed red) - unmerged=$(git config --get-color color.status.unmerged red) - untracked=$(git config --get-color color.status.untracked red) opts=" $FORGIT_FZF_DEFAULT_OPTS -0 -m --nth 2..,.. @@ -346,8 +358,7 @@ _forgit_add() { files=() while IFS='' read -r file; do files+=("$file") - done < <(git -c color.status=always -c status.relativePaths=true status -su | - grep -F -e "$changed" -e "$unmerged" -e "$untracked" | + done < <(_forgit_list_files --exclude-standard --modified --others | sed -E 's/^(..[^[:space:]]*)[[:space:]]+(.*)$/[\1] \2/' | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_get_single_file_from_add_line) @@ -381,7 +392,7 @@ _forgit_reset_head() { files=() while IFS='' read -r file; do files+=("$file") - done < <(git diff --staged --name-only | FZF_DEFAULT_OPTS="$opts" fzf) + done < <(git diff -z --staged --name-only | tr '\0' '\n' | FZF_DEFAULT_OPTS="$opts" fzf) if [[ ${#files} -eq 0 ]]; then echo 'Nothing to unstage.' return 1 @@ -455,19 +466,18 @@ _forgit_stash_push() { *) _forgit_git_stash_push "${args[@]}"; return $? esac done - local opts files rootdir + local opts files opts=" $FORGIT_FZF_DEFAULT_OPTS -m --preview=\"$FORGIT stash_push_preview {}\" $FORGIT_STASH_PUSH_FZF_OPTS " - rootdir=$(git rev-parse --show-toplevel) # Show both modified and untracked files files=() while IFS='' read -r file; do files+=("$file") - done < <(git ls-files "$rootdir" --exclude-standard --modified --others | + done < <(_forgit_list_files --exclude-standard --modified --others | FZF_DEFAULT_OPTS="$opts" fzf --exit-0) [[ "${#files[@]}" -eq 0 ]] && echo "Nothing to stash" && return 1 _forgit_git_stash_push ${msg:+-m "$msg"} -u "${files[@]}" @@ -497,7 +507,7 @@ _forgit_clean() { $FORGIT_CLEAN_FZF_OPTS " # Note: Postfix '/' in directory path should be removed. Otherwise the directory itself will not be removed. - files=$(git clean -xdffn "$@"| sed 's/^Would remove //' | FZF_DEFAULT_OPTS="$opts" fzf |sed 's#/$##') + files=$(git -c core.quotePath=false clean -xdffn "$@"| sed 's/^Would remove //' | FZF_DEFAULT_OPTS="$opts" fzf |sed 's#/$##') [[ -n "$files" ]] && echo "$files" | tr '\n' '\0' | xargs -0 -I% git clean "${_forgit_clean_git_opts[@]}" -xdff '%' && git status --short && return echo 'Nothing to clean.' } @@ -675,7 +685,7 @@ _forgit_checkout_file() { files=() while IFS='' read -r file; do files+=("$file") - done < <(git ls-files --modified "$(git rev-parse --show-toplevel)" | + done < <(_forgit_list_files --modified | FZF_DEFAULT_OPTS="$opts" fzf) [[ "${#files[@]}" -gt 0 ]] && _forgit_git_checkout_file "${files[@]}" } From 16274403945552b5bbedc820045787d5d96aaae4 Mon Sep 17 00:00:00 2001 From: sandroid Date: Thu, 25 Apr 2024 22:51:05 +0200 Subject: [PATCH 2/3] Fix: empty line showing up when when adding files --- bin/git-forgit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/git-forgit b/bin/git-forgit index 43c659b9..3ecfb28d 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -171,7 +171,7 @@ _forgit_list_files() { # merge conflict. # With the -z flag, git also uses \0 line termination, so we # have to replace the terminators. - git ls-files -z "$@" "$rootdir" | uniq | tr '\0' '\n' + git ls-files -z "$@" "$rootdir" | tr '\0' '\n' | uniq } _forgit_log_preview() { From b0826102bf991adf1c42b59fdbe841b5103c53b9 Mon Sep 17 00:00:00 2001 From: sandroid Date: Tue, 30 Apr 2024 21:26:47 +0200 Subject: [PATCH 3/3] Fix: _forgit_add is missing status indicators and files can not be added when the sed pattern matches (due to missing indicator) --- bin/git-forgit | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/git-forgit b/bin/git-forgit index 3ecfb28d..1e00576d 100755 --- a/bin/git-forgit +++ b/bin/git-forgit @@ -344,10 +344,13 @@ _forgit_edit_add_file() { # git add selector _forgit_add() { _forgit_inside_work_tree || return 1 - local files opts + local changed unmerged untracked files opts # Add files if passed as arguments [[ $# -ne 0 ]] && { _forgit_git_add "$@" && git status -su; return $?; } + changed=$(git config --get-color color.status.changed red) + unmerged=$(git config --get-color color.status.unmerged red) + untracked=$(git config --get-color color.status.untracked red) opts=" $FORGIT_FZF_DEFAULT_OPTS -0 -m --nth 2..,.. @@ -358,7 +361,8 @@ _forgit_add() { files=() while IFS='' read -r file; do files+=("$file") - done < <(_forgit_list_files --exclude-standard --modified --others | + done < <(git -c color.status=always -c status.relativePaths=true -c core.quotePath=false status -su | + grep -F -e "$changed" -e "$unmerged" -e "$untracked" | sed -E 's/^(..[^[:space:]]*)[[:space:]]+(.*)$/[\1] \2/' | FZF_DEFAULT_OPTS="$opts" fzf | _forgit_get_single_file_from_add_line)