@@ -161,6 +161,34 @@ derive_context_config_group() {
161161 fi
162162}
163163
164+ # Internal function that returns a list of filenames for encrypted files in the
165+ # repo, where the filenames are verbatim and not quoted in any way even if they
166+ # contain unusual characters like double-quotes, backslash and control
167+ # characters. We must avoid quoting of filenames to support names containing
168+ # double quotes. #173
169+ _list_encrypted_files () {
170+ local strict_context=${1:- }
171+
172+ IFS=$' \n '
173+ # List files with -z option to disable quoting of filenames, then
174+ # immediately convert NUL-delimited filenames to be newline-delimited to be
175+ # compatibility with bash variables
176+ for file in $( git ls-files -z | tr ' \0' ' \n' ) ; do
177+ # Check for the suffix ': filter: crypt' that identifies encrypted file
178+ local check
179+ check=$( git check-attr filter " $file " 2> /dev/null)
180+
181+ # Only output names of encrypted files matching the context, either
182+ # strictly (if $1 = "true") or loosely (if $1 is false or unset)
183+ if [[ " $strict_context " == " true" ]] &&
184+ [[ " $check " == * " : filter: crypt${CONTEXT_CRYPT_SUFFIX:- } " ]]; then
185+ echo " $file "
186+ elif [[ " $check " == * " : filter: crypt${CONTEXT_CRYPT_SUFFIX:- } " * ]]; then
187+ echo " $file "
188+ fi
189+ done
190+ }
191+
164192# Detect OpenSSL major version 3 or later which requires a compatibility
165193# work-around to include the prefix 'Salted__' and salt value when encrypting.
166194#
@@ -325,7 +353,7 @@ git_pre_commit() {
325353 tmp=$( mktemp)
326354 IFS=$' \n '
327355 slow_mode_if_failed () {
328- for secret_file in $( git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk ' BEGIN { FS = ":" }; /crypt$/{ print $1 } ' ) ; do
356+ for secret_file in $( _list_encrypted_files ) ; do
329357 # Skip symlinks, they contain the linked target file path not plaintext
330358 if [[ -L $secret_file ]]; then
331359 continue
@@ -373,7 +401,7 @@ git_pre_commit() {
373401 if [[ " ${BASH_VERSINFO[0]} " -ge 4 ]] && [[ " ${BASH_VERSINFO[1]} " -ge 4 ]]; then
374402 num_procs=$( nproc)
375403 num_jobs=" \j"
376- for secret_file in $( git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk ' BEGIN { FS = ":" }; /crypt$/{ print $1 } ' ) ; do
404+ for secret_file in $( _list_encrypted_files ) ; do
377405 while (( ${num_jobs@ P} >= num_procs)) ; do
378406 wait -n
379407 done
@@ -679,15 +707,15 @@ save_configuration() {
679707 git config merge.crypt.name ' Merge transcrypt secret files'
680708
681709 # add git alias for listing ALL encrypted files regardless of context
682- git config alias.ls-crypt " !git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \" : \" }; / crypt/{ print \$ 1 }' "
710+ git config alias.ls-crypt " !$transcrypt_path --list "
683711
684712 # add a git alias for listing encrypted files in specific context, including 'default'
685713 if [[ " $CONTEXT " = ' default' ]]; then
686714 # List files with gitattribute 'filter=crypt'
687- git config alias.ls-crypt-default " !git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \" : \" }; / crypt$/{ print \$ 1 }' "
715+ git config alias.ls-crypt-default " !$transcrypt_path --list "
688716 else
689717 # List files with gitattribute 'filter=crypt-<CONTEXT>'
690- git config " alias.ls-crypt-${CONTEXT} " " !git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \" : \" }; / crypt- ${CONTEXT} $/{ print \$ 1 }' "
718+ git config " alias.ls-crypt-${CONTEXT} " " !$transcrypt_path --context= ${CONTEXT} --list "
691719 fi
692720}
693721
@@ -832,6 +860,15 @@ uninstall_transcrypt() {
832860 remove_cached_plaintext
833861 fi
834862
863+ # touch all encrypted files to prevent stale stat info
864+ local encrypted_files
865+ encrypted_files=$( git ls-crypt)
866+ if [[ $encrypted_files ]] && [[ $IS_BARE == ' false' ]]; then
867+ cd " $REPO " > /dev/null || die 1 ' could not change into the "%s" directory' " $REPO "
868+ # shellcheck disable=SC2086
869+ touch $encrypted_files
870+ fi
871+
835872 # remove helper scripts
836873 # Keep obsolete clean,smudge,textconv,merge refs here to remove them on upgrade
837874 for script in {transcrypt,clean,smudge,textconv,merge}; do
@@ -853,15 +890,6 @@ uninstall_transcrypt() {
853890 fi
854891 [[ -f " $pre_commit_hook_installed " ]] && rm " $pre_commit_hook_installed "
855892
856- # touch all encrypted files to prevent stale stat info
857- local encrypted_files
858- encrypted_files=$( git ls-crypt)
859- if [[ $encrypted_files ]] && [[ $IS_BARE == ' false' ]]; then
860- cd " $REPO " > /dev/null || die 1 ' could not change into the "%s" directory' " $REPO "
861- # shellcheck disable=SC2086
862- touch $encrypted_files
863- fi
864-
865893 # remove context settings: cipher & password config, ls-crypt alias variant,
866894 # crypt filter/diff/merge attributes. We do it here instead of `clean_gitconfig`
867895 # to avoid interfering with flushing of credentials
@@ -1005,7 +1033,7 @@ upgrade_transcrypt() {
10051033list_files () {
10061034 if [[ $IS_BARE == ' false' ]]; then
10071035 cd " $REPO " > /dev/null || die 1 ' could not change into the "%s" directory' " $REPO "
1008- git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk ' BEGIN { FS = ":" }; /crypt/{ print $1 } '
1036+ _list_encrypted_files true
10091037 fi
10101038}
10111039
@@ -1014,13 +1042,12 @@ show_raw_file() {
10141042 if [[ -f $show_file ]]; then
10151043 # ensure the file is currently being tracked
10161044 local escaped_file=${show_file// \/ / \\\/ }
1017- if git -c core.quotePath=false ls-files --others -- " $show_file " | awk " /${escaped_file} /{ exit 1 }" ; then
1018- file_paths=$( git -c core.quotePath=false ls-tree --name-only --full-name HEAD " $show_file " )
1019- else
1045+ file_paths=$( _list_encrypted_files | grep " $escaped_file " )
1046+ if [[ -z " $file_paths " ]]; then
10201047 die 1 ' the file "%s" is not currently being tracked by git' " $show_file "
10211048 fi
10221049 elif [[ $show_file == ' *' ]]; then
1023- file_paths=$( git ls-crypt )
1050+ file_paths=$( _list_encrypted_files )
10241051 else
10251052 die 1 ' the file "%s" does not exist' " $show_file "
10261053 fi
0 commit comments