clean [URL|REMOTE] Scan/Clean unencrypted files from remote
clean -f, --force Actually delete files (default is scan only)
clean -i, --init Scan even if no manifest found (DANGEROUS with --force)
-
+ stat [URL|REMOTE] Show diagnostics (file counts, tracked vs untracked)
Git Protocol Commands (for debugging):
capabilities List remote helper capabilities
list List refs in remote repository
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD - 1]}"
opts="-h --help -v --version"
- commands="capabilities check clean fetch list push"
+ commands="check clean stat"
# 1. First argument: complete commands and global options
if [[ $COMP_CWORD -eq 1 ]]; then
case "${COMP_WORDS[1]}" in
clean)
local remotes=$( git remote -v 2>/dev/null | grep 'gcrypt::' | awk '{print $1}' | sort -u || : )
- COMPREPLY=( $( compgen -W " $remotes" -- "$cur" ) )
+ COMPREPLY=( $( compgen -W "-f --force -i --init $remotes" -- "$cur" ) )
return 0
;;
- check)
+ check|stat)
local remotes=$( git remote 2>/dev/null || : )
COMPREPLY=( $( compgen -W "$remotes" -- "$cur" ) )
return 0
complete -c git-remote-gcrypt -s v -l version -d 'Show version information'
# Subcommands
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'check' -d 'Check if URL is a gcrypt repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'clean' -d 'Scan/Clean unencrypted files from remote'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from check clean stat" -a 'check' -d 'Check if URL is a gcrypt repository'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from check clean stat" -a 'clean' -d 'Scan/Clean unencrypted files from remote'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from check clean stat" -a 'stat' -d 'Show diagnostics'
complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from clean" -a "(git remote -v 2>/dev/null | grep 'gcrypt::' | awk '{print \$1}' | sort -u)" -d 'Gcrypt Remote'
complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from check" -a "(git remote 2>/dev/null)" -d 'Git Remote'
+complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from stat" -a "(git remote 2>/dev/null)" -d 'Git Remote'
# Clean flags
+complete -c git-remote-gcrypt -f -n "__fish_seen_subcommand_from clean" -s f -l force -d 'Flag';
+complete -c git-remote-gcrypt -f -n "__fish_seen_subcommand_from clean" -s i -l init -d 'Flag';
-
-# Git protocol commands
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'capabilities' -d 'Show git remote helper capabilities'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'list' -d 'List refs in remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'push' -d 'Push refs to remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'fetch' -d 'Fetch refs from remote repository'
# 2. Parse Commands and Flags for Completions
# Extract command names (first word after 2 spaces)
-COMMANDS_LIST=$(echo "$RAW_HELP" | awk '/^ [a-z]+ / {print $1}' | grep -vE "^(help|version)$" | sort | tr '\n' ' ' | sed 's/ $//')
+COMMANDS_LIST=$(echo "$RAW_HELP" | awk '/^ [a-z]+ / {print $1}' | grep -vE "^(help|version|capabilities|list|push|fetch)$" | sort | tr '\n' ' ' | sed 's/ $//')
# Extract clean flags
# Text: " clean -f, --force Actually delete files..."
# We want: "-f --force -i --init" for Bash
-CLEAN_FLAGS_RAW=$(echo "$RAW_HELP" | grep "^ clean -" | awk -F' ' '{print $2}' | sed 's/,//g')
+CLEAN_FLAGS_RAW=$(echo "$RAW_HELP" | grep "^ clean -" | awk '{print $2, $3}' | sed 's/,//g')
CLEAN_FLAGS_BASH=$(echo "$CLEAN_FLAGS_RAW" | tr '\n' ' ' | sed 's/ $//')
# For Zsh: we want simple list for now as per plan, user asked for dynamic but safe.
COMPREPLY=( $( compgen -W "{clean_flags_bash} $remotes" -- "$cur" ) )
return 0
;;
- check)
+ check|stat)
local remotes=$( git remote 2>/dev/null || : )
COMPREPLY=( $( compgen -W "$remotes" -- "$cur" ) )
return 0
# Subcommands
complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'check' -d 'Check if URL is a gcrypt repository'
complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'clean' -d 'Scan/Clean unencrypted files from remote'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'stat' -d 'Show diagnostics'
complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from clean" -a "(git remote -v 2>/dev/null | grep 'gcrypt::' | awk '{print \$1}' | sort -u)" -d 'Gcrypt Remote'
complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from check" -a "(git remote 2>/dev/null)" -d 'Git Remote'
+complete -c git-remote-gcrypt -n "__fish_seen_subcommand_from stat" -a "(git remote 2>/dev/null)" -d 'Git Remote'
# Clean flags
{clean_flags_fish}
-
-# Git protocol commands
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'capabilities' -d 'Show git remote helper capabilities'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'list' -d 'List refs in remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'push' -d 'Push refs to remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from {not_sc_list}" -a 'fetch' -d 'Fetch refs from remote repository'
_arguments {clean_flags_zsh} \
'*:gcrypt URL: _alternative "remotes:gcrypt remote:($(git remote -v 2>/dev/null | grep "gcrypt::" | awk "{print \$1}" | sort -u))" "files:file:_files"'
;;
- check)
+ check|stat)
_arguments \
'*:gcrypt URL: _alternative "remotes:git remote:($(git remote 2>/dev/null))" "files:file:_files"'
;;
args=(
'(- *)'{-h,--help}'[show help message]'
'(- *)'{-v,--version}'[show version information]'
- '1:command:(capabilities check clean fetch list push)'
+ '1:command:(check clean stat)'
'*::subcommand arguments:->args'
)
_arguments -s -S $args
case $line[1] in
clean)
- _arguments \
+ _arguments '(-f --force -i --init)'{-f,--force,-i,--init}'[flag]' \
'*:gcrypt URL: _alternative "remotes:gcrypt remote:($(git remote -v 2>/dev/null | grep "gcrypt::" | awk "{print \$1}" | sort -u))" "files:file:_files"'
;;
- check)
+ check|stat)
_arguments \
'*:gcrypt URL: _alternative "remotes:git remote:($(git remote 2>/dev/null))" "files:file:_files"'
;;
clean [URL|REMOTE] Scan/Clean unencrypted files from remote
clean -f, --force Actually delete files (default is scan only)
clean -i, --init Scan even if no manifest found (DANGEROUS with --force)
-
+ stat [URL|REMOTE] Show diagnostics (file counts, tracked vs untracked)
Git Protocol Commands (for debugging):
capabilities List remote helper capabilities
list List refs in remote repository
done
break # Stop parsing outer loop
;;
+ stat)
+ NAME=gcrypt-stat
+ shift
+ if [ $# -gt 0 ]; then
+ if [ -z "$URL" ]; then
+ URL="$1"
+ else
+ echo "Error: Multiple URLs/remotes provided to stat" >&2
+ exit 1
+ fi
+ shift
+ fi
+ ;;
-*)
echo "Unknown option: $1" >&2
exit 1
exit 0
}
+cmd_stat()
+{
+ local remote_files="" file_count=0 tracked_count=0 untracked_count=0 valid_files="" f=""
+
+ if ! ensure_connected; then
+ echo_die "Could not connect to $URL."
+ fi
+
+ get_remote_file_list @remote_files || echo_die "Failed to list remote files."
+ file_count=$(line_count "$remote_files")
+ if isnull "$remote_files"; then
+ file_count=0
+ fi
+
+ echo "Remote: $URL"
+ echo "Total files: $file_count"
+
+ if [ "$Did_find_repo" = "yes" ]; then
+ echo "Gcrypt repository: detected"
+ echo "Manifest: found"
+
+ # Build whitelist of valid gcrypt files to count tracked
+ valid_files="$Manifestfile"
+ for f in $(xecho "$Packlist" | cut -d: -f3 | cut -d' ' -f1); do
+ valid_files="$valid_files$Newline$f"
+ done
+
+ tracked_count=0
+ for f in $remote_files; do
+ if xfeed "$valid_files" grep -qxF "$f"; then
+ tracked_count=$((tracked_count + 1))
+ fi
+ done
+ untracked_count=$((file_count - tracked_count))
+
+ echo "Tracked files: $tracked_count"
+ echo "Untracked files: $untracked_count"
+ else
+ echo "Gcrypt repository: not detected (no manifest)"
+ echo "Tracked files: 0"
+ echo "Untracked files: $file_count"
+ fi
+
+ CLEAN_FINAL "$URL"
+ git remote remove "$NAME" 2>/dev/null || true
+ exit 0
+}
+
if [ "$NAME" = "gcrypt-check" ]; then
resolve_url check
echo_info "Checking remote: $URL"
echo_info "Checking remote: $URL"
setup
cmd_clean
+elif [ "$NAME" = "gcrypt-stat" ]; then
+ resolve_url stat
+ echo_info "Getting statistics for remote: $URL"
+ setup
+ cmd_stat
elif [ "$1" = --version ] || [ "$1" = -v ]; then
echo "git-remote-gcrypt version $VERSION"
exit 0
--- /dev/null
+#!/bin/bash
+# tests/test-completions.sh
+# Verifies that bash completion script offers correct commands and excludes plumbing.
+
+# Mock commands if necessary?
+# The completion script calls `git remote` etc. We can mock git.
+
+# Setup
+TEST_DIR=$(dirname "$0")
+COMP_FILE="$TEST_DIR/../completions/bash/git-remote-gcrypt"
+
+if [ ! -f "$COMP_FILE" ]; then
+ echo "FAIL: Completion file not found at $COMP_FILE"
+ exit 1
+fi
+
+# shellcheck source=/dev/null
+source "$COMP_FILE"
+
+# Mock variables used by completion
+COMP_WORDS=()
+COMP_CWORD=0
+COMPREPLY=()
+
+# --- Mock git ---
+# shellcheck disable=SC2329
+git() {
+ if [[ $1 == "remote" ]]; then
+ echo "origin"
+ echo "backup"
+ fi
+}
+export -f git
+
+# --- Helper to run completion ---
+run_completion() {
+ COMP_WORDS=("$@")
+ COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
+ COMPREPLY=()
+ _git_remote_gcrypt
+}
+
+# --- Tests ---
+
+FAILURES=0
+
+echo "Test 1: Top-level commands (git-remote-gcrypt [TAB])"
+run_completion "git-remote-gcrypt" ""
+# Expect: check clean stat -h --help -v --version
+# Expect NO: capabilities list push fetch
+EXPECTED="check clean stat"
+FORBIDDEN="capabilities list push fetch"
+
+OUTPUT="${COMPREPLY[*]}"
+
+for cmd in $EXPECTED; do
+ if [[ ! $OUTPUT =~ $cmd ]]; then
+ echo " FAIL: Expected '$cmd' in completion output."
+ FAILURES=$((FAILURES + 1))
+ fi
+done
+
+for cmd in $FORBIDDEN; do
+ if [[ $OUTPUT =~ $cmd ]]; then
+ echo " FAIL: Forbidden '$cmd' found in completion output."
+ FAILURES=$((FAILURES + 1))
+ fi
+done
+
+if [[ $OUTPUT =~ check ]] && [[ ! $OUTPUT =~ capabilities ]]; then
+ echo " PASS: Top-level commands look correct."
+fi
+
+echo "Test 2: 'stat' subcommand (git-remote-gcrypt stat [TAB])"
+run_completion "git-remote-gcrypt" "stat" ""
+# Should complete remotes (mocked as origin backup)
+OUTPUT="${COMPREPLY[*]}"
+if [[ $OUTPUT =~ "origin" ]] && [[ $OUTPUT =~ "backup" ]]; then
+ echo " PASS: 'stat' completes remotes."
+else
+ echo " FAIL: 'stat' did not complete remotes. Got: $OUTPUT"
+ FAILURES=$((FAILURES + 1))
+fi
+
+echo "Test 3: 'clean' subcommand flags (git-remote-gcrypt clean [TAB])"
+run_completion "git-remote-gcrypt" "clean" ""
+# Should have -f --force etc.
+OUTPUT="${COMPREPLY[*]}"
+if [[ $OUTPUT =~ "--force" ]] && [[ $OUTPUT =~ "--init" ]]; then
+ echo " PASS: 'clean' completes flags."
+else
+ echo " FAIL: 'clean' missing flags. Got: $OUTPUT"
+ FAILURES=$((FAILURES + 1))
+fi
+
+if [ "$FAILURES" -eq 0 ]; then
+ echo "--------------------------"
+ echo "All completion tests passed."
+ exit 0
+else
+ echo "--------------------------"
+ echo "$FAILURES completion tests FAILED."
+ exit 1
+fi
# 2. Parse Commands and Flags for Completions
# Extract command names (first word after 2 spaces)
-COMMANDS_LIST=$(echo "$RAW_HELP" | awk '/^ [a-z]+ / {print $1}' | grep -vE "^(help|version)$" | sort | tr '\n' ' ' | sed 's/ $//')
+COMMANDS_LIST=$(echo "$RAW_HELP" | awk '/^ [a-z]+ / {print $1}' | grep -vE "^(help|version|capabilities|list|push|fetch)$" | sort | tr '\n' ' ' | sed 's/ $//')
# Extract clean flags
# Text: " clean -f, --force Actually delete files..."
# We want: "-f --force -i --init" for Bash
-CLEAN_FLAGS_RAW=$(echo "$RAW_HELP" | grep "^ clean -" | awk -F' ' '{print $2}' | sed 's/,//g')
+CLEAN_FLAGS_RAW=$(echo "$RAW_HELP" | grep "^ clean -" | awk '{print $2, $3}' | sed 's/,//g')
CLEAN_FLAGS_BASH=$(echo "$CLEAN_FLAGS_RAW" | tr '\n' ' ' | sed 's/ $//')
# For Zsh: we want simple list for now as per plan, user asked for dynamic but safe.
short=$(echo "$line" | awk '{print $1}' | sed 's/-//')
long=$(echo "$line" | awk '{print $2}' | sed 's/--//')
# Escape quotes if needed (none usually)
- CLEAN_FLAGS_FISH="${CLEAN_FLAGS_FISH}complete -c git-remote-gcrypt -f -n \"__fish_seen_subcommand_from clean\" -s $short -l $long -d 'Flag';\n"
+ CLEAN_FLAGS_FISH="${CLEAN_FLAGS_FISH}complete -c git-remote-gcrypt -f -n \"__fish_seen_subcommand_from clean\" -s $short -l $long -d 'Flag';
+"
done
unset IFS