Sfoglia il codice sorgente

Merge pull request #8850 from ThomasWaldmann/shell-completions

add new commands and options to shell completions
TW 1 settimana fa
parent
commit
d197810bde

+ 1 - 0
.github/workflows/ci.yml

@@ -97,6 +97,7 @@ jobs:
         sudo apt-get install -y libssl-dev libacl1-dev libxxhash-dev liblz4-dev libzstd-dev
         sudo apt-get install -y libfuse-dev fuse || true  # Required for Python llfuse module
         sudo apt-get install -y libfuse3-dev fuse3 || true  # Required for Python pyfuse3 module
+        sudo apt-get install -y bash zsh fish  # for shell completion tests
 
     - name: Install Python requirements
       run: |

+ 44 - 26
scripts/shell_completions/bash/borg

@@ -12,12 +12,13 @@ _borg()
     local cur="${COMP_WORDS[COMP_CWORD]}"
     local prev="${COMP_WORDS[COMP_CWORD-1]}"
     local prevprev="${COMP_WORDS[COMP_CWORD-2]}"
-    local common_opts="-h --help --critical --error --warning --info -v --verbose --debug --debug-topic -p --progress --iec --log-json --lock-wait --show-version --show-rc --umask --remote-path --upload-ratelimit --upload-buffer --consider-part-files --debug-profile --rsh -r --repo"
+    local common_opts="-h --help --critical --error --warning --info -v --verbose --debug --debug-topic -p --progress --iec --log-json --lock-wait --show-version --show-rc --umask --remote-path --upload-ratelimit --upload-buffer --debug-profile --rsh -r --repo"
+    local archive_filter_opts="--sort-by --first --last --oldest --newest --older --newer"
     local opts="${common_opts}"
 
     # Commands
     if [[ ${COMP_CWORD} == 1 ]] ; then
-        local borg_commands="benchmark break-lock check compact config create delete diff export-tar extract help import-tar info key list mount prune rcompress rcreate rdelete recreate rename rinfo rlist serve transfer umount with-lock"
+        local borg_commands="analyze benchmark break-lock check compact create debug delete diff export-tar extract help import-tar info key list mount prune recreate rename repo-compress repo-create repo-delete repo-info repo-list repo-space serve tag transfer umount undelete version with-lock"
         COMPREPLY=( $(compgen -W "${borg_commands}" -- ${cur}) )
         compopt +o default
         return 0
@@ -32,6 +33,10 @@ _borg()
             COMPREPLY=( $(compgen -W "cpu crud" -- ${cur}) )
             return 0
             ;;
+        'debug')
+            COMPREPLY=( $(compgen -W "info dump-archive-items dump-archive dump-manifest dump-repo-objs search-repo-objs get-obj id-hash parse-obj format-obj put-obj delete-obj convert-profile" -- ${cur}) )
+            return 0
+            ;;
         'help')
             COMPREPLY=( $(compgen -W "patterns placeholders compression" -- ${cur}) )
             return 0
@@ -52,12 +57,12 @@ _borg()
             return 0
             ;;
         '--sort-by')
-            local sort_keys="timestamp name id"
+            local sort_keys="timestamp archive name id tags host user"
             COMPREPLY=( $(compgen -W "${sort_keys}" -- ${cur}) )
             return 0
             ;;
         '-o')
-            # FIXME This list is probably not full, but I tried to pick only those that are relevant to borg mount -o:
+            # FIXME This list is probably not complete, but I tried to pick only those that are relevant to borg mount -o:
             local fuse_options="ac_attr_timeout= allow_damaged_files allow_other allow_root attr_timeout= auto auto_cache auto_unmount default_permissions entry_timeout= gid= group_id= kernel_cache max_read= negative_timeout= noauto noforget remember= remount rootmode= uid= umask= user user_id= versions"
             COMPREPLY=( $(compgen -W "${fuse_options}" -- ${cur}) )
             return 0
@@ -69,36 +74,42 @@ _borg()
             ;;
         '--upgrader')
             local upgraders="From12To20 NoOp"
-            COMPREPLY=( $(compgren -W "${upgraders}" -- ${cur}) )
+            COMPREPLY=( $(compgen -W "${upgraders}" -- ${cur}) )
             return 0
             ;;
     esac
 
     if [[ ${cur} == -* ]] ; then
         case "${COMP_LINE}" in
-            *' rcreate '*)
-                local opts="--other-repo -e --encryption --append-only --storage-quota --make-parent-dirs --copy-crypt-key ${common_opts}"
+            *' analyze '*)
+                local opts="-a --match-archives ${archive_filter_opts} ${common_opts}"
+                ;;
+            *' repo-create '*)
+                local opts="--other-repo --from-borg1 -e --encryption --copy-crypt-key ${common_opts}"
                 ;;
-            *' rlist '*)
-                local opts="--consider-checkpoints --short --format --json ${common_opts} -a --match-archives --sort-by --first --last"
+            *' repo-list '*)
+                local opts="--short --format --json ${common_opts} -a --match-archives ${archive_filter_opts} --deleted"
                 ;;
-            *' rinfo '*)
+            *' repo-info '*)
                 local opts="--json ${common_opts}"
                 ;;
-            *' rcompress '*)
-                local opts="-C --compression -s --stats -c --checkpoint-interval ${common_opts}"
+            *' repo-compress '*)
+                local opts="-C --compression -s --stats ${common_opts}"
                 ;;
-            *' rdelete '*)
+            *' repo-delete '*)
                 local opts="-n --dry-run --list --force --cache-only --keep-security-info ${common_opts}"
                 ;;
+            *' repo-space '*)
+                local opts="--reserve --free ${common_opts}"
+                ;;
             *' create '*)
-                local opts="-n --dry-run -s --stats --list --filter --json --no-cache-sync --stdin-name --stdin-user --stdin-group --stdin-mode --content-from-command --paths-from-stdin --paths-from-command --paths-delimiter -e --exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags --exclude-nodump -x --one-file-system --numeric-ids --atime --noctime --nobirthtime --noflags --noacls --noxattrs --sparse --files-cache --read-special --comment --timestamp -c --checkpoint-interval --chunker-params -C --compression ${common_opts}"
+                local opts="-n --dry-run -s --stats --list --filter --json --stdin-name --stdin-user --stdin-group --stdin-mode --content-from-command --paths-from-stdin --paths-from-command --paths-delimiter -e --exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags --exclude-nodump -x --one-file-system --numeric-ids --atime --noctime --nobirthtime --noflags --noacls --noxattrs --sparse --files-cache --read-special --comment --timestamp --chunker-params -C --compression ${common_opts}"
                 ;;
             *' extract '*)
                 local opts="--list -n --dry-run --numeric-ids --noflags --noacls --noxattrs --stdout --sparse -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}"
                 ;;
             *' check '*)
-                local opts="--repository-only --archives-only --verify-data --repair --max-duration -a --match-archives --sort-by --first --last ${common_opts}"
+                local opts="--repository-only --archives-only --verify-data --repair --find-lost-archives --max-duration -a --match-archives ${archive_filter_opts} ${common_opts}"
                 ;;
             # rename
             #   no specific options
@@ -109,19 +120,19 @@ _borg()
                 local opts="--numeric-ids --same-chunker-params --sort --json-lines -e --exclude --exclude-from --pattern --patterns-from ${common_opts}"
                 ;;
             *' delete '*)
-                local opts="-n --dry-run --list --consider-checkpoints -s --stats --cache-only --force -c --checkpoint-interval -a --match-archives --sort-by --first --last ${common_opts}"
+                local opts="-n --dry-run --list -s --stats --cache-only --force -a --match-archives ${archive_filter_opts} ${common_opts}"
                 ;;
             *' prune '*)
-                local opts="-n --dry-run --force -s --stats --list --keep-within --keep-last --keep-secondly --keep-minutely -H --keep-hourly -d --keep-daily -w --keep-weekly -m --keep-monthly -y --keep-yearly -c --checkpoint-interval -a --match-archives ${common_opts}"
+                local opts="-n --dry-run --force -s --stats --list --keep-within --keep-last --keep-secondly --keep-minutely -H --keep-hourly -d --keep-daily -w --keep-weekly -m --keep-monthly --keep-13weekly --keep-3monthly -y --keep-yearly -a --match-archives ${common_opts}"
                 ;;
             *' compact '*)
-                local opts="--threshold ${common_opts}"
+                local opts="-n --dry-run -s --stats ${common_opts}"
                 ;;
             *' info '*)
-                local opts="--json -a --match-archives --sort-by --first --last ${common_opts}"
+                local opts="--json -a --match-archives ${archive_filter_opts} ${common_opts}"
                 ;;
             *' mount '*)
-                local opts="--consider-checkpoints -f --foreground -o --numeric-ids -a --match-archives --sort-by --first --last -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}"
+                local opts="-f --foreground -o --numeric-ids -a --match-archives ${archive_filter_opts} -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}"
                 ;;
             # umount
             #   no specific options
@@ -137,23 +148,30 @@ _borg()
                 local opts="--paper ${common_opts}"
                 ;;
             *' recreate '*)
-                local opts="--list --filter -n dry-run -s stats -e exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags -a --match-archives --sort-by --first --last --target -c --checkpoint-interval --comment --timestamp -C --compression --recompress --chunker-params ${common_opts}"
+                local opts="--list --filter -n --dry-run -s --stats -e --exclude --exclude-from --pattern --patterns-from --exclude-caches --exclude-if-present --keep-exclude-tags -a --match-archives ${archive_filter_opts} --target --comment --timestamp -C --compression --chunker-params ${common_opts}"
                 ;;
             *' export-tar '*)
-                local opts="--tar-filter --list --tar-format -e exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}"
+                local opts="--tar-filter --list --tar-format -e --exclude --exclude-from --pattern --patterns-from --strip-components ${common_opts}"
                 ;;
             *' import-tar '*)
-                local opts="--tar-filter -s --stats --list --filter --json ${common_opts} --comment --timestamp -c --checkpoint-interval --chunker-params -C --compression"
+                local opts="--tar-filter -s --stats --list --filter --json --ignore-zeros ${common_opts} --comment --timestamp --chunker-params -C --compression"
                 ;;
             *' transfer '*)
-                local opts="-n --dry-run --other-repo --upgrader ${common_opts} -a --match-archives --sort-by --first --last"
+                local opts="-n --dry-run --other-repo --upgrader ${common_opts} -a --match-archives ${archive_filter_opts}"
                 ;;
             *' serve '*)
                 local opts="--restrict-to-path --restrict-to-repository --append-only --storage-quota ${common_opts}"
                 ;;
-            *' config '*)
-                local opts="-c --cache -d --delete --list ${common_opts}"
+            *' tag '*)
+                local opts="--set --add --remove -a --match-archives ${archive_filter_opts} ${common_opts}"
+                ;;
+            *' undelete '*)
+                local opts="-n --dry-run --list -a --match-archives ${archive_filter_opts} ${common_opts}"
                 ;;
+            # debug
+            #   has subcommands, handled separately
+            # version
+            #   no specific options
             # with-lock
             #   no specific options
             # break-lock

+ 168 - 45
scripts/shell_completions/fish/borg.fish

@@ -7,19 +7,29 @@
 
 # Commands
 
-complete -c borg -f -n __fish_is_first_token -a 'init' -d 'Initialize an empty repository'
-complete -c borg -f -n __fish_is_first_token -a 'create' -d 'Create new archive'
+complete -c borg -f -n __fish_is_first_token -a 'analyze' -d 'Analyze archives to find "hot spots"'
+complete -c borg -f -n __fish_is_first_token -a 'create' -d 'Create a new archive'
 complete -c borg -f -n __fish_is_first_token -a 'extract' -d 'Extract archive contents'
 complete -c borg -f -n __fish_is_first_token -a 'check' -d 'Check repository consistency'
 complete -c borg -f -n __fish_is_first_token -a 'rename' -d 'Rename an existing archive'
 complete -c borg -f -n __fish_is_first_token -a 'list' -d 'List archive or repository contents'
 complete -c borg -f -n __fish_is_first_token -a 'diff' -d 'Find differences between archives'
-complete -c borg -f -n __fish_is_first_token -a 'delete' -d 'Delete a repository or archive'
+complete -c borg -f -n __fish_is_first_token -a 'delete' -d 'Delete an archive'
 complete -c borg -f -n __fish_is_first_token -a 'prune' -d 'Prune repository archives'
 complete -c borg -f -n __fish_is_first_token -a 'compact' -d 'Free repository space'
 complete -c borg -f -n __fish_is_first_token -a 'info' -d 'Show archive details'
 complete -c borg -f -n __fish_is_first_token -a 'mount' -d 'Mount archive or a repository'
 complete -c borg -f -n __fish_is_first_token -a 'umount' -d 'Un-mount the mounted archive'
+complete -c borg -f -n __fish_is_first_token -a 'repo-compress' -d 'Repository (re-)compression'
+complete -c borg -f -n __fish_is_first_token -a 'repo-create' -d 'Create a new, empty repository'
+complete -c borg -f -n __fish_is_first_token -a 'repo-delete' -d 'Delete a repository'
+complete -c borg -f -n __fish_is_first_token -a 'repo-info' -d 'Show repository information'
+complete -c borg -f -n __fish_is_first_token -a 'repo-list' -d 'List repository contents'
+complete -c borg -f -n __fish_is_first_token -a 'repo-space' -d 'Manage reserved space in a repository'
+complete -c borg -f -n __fish_is_first_token -a 'tag' -d 'Tag archives'
+complete -c borg -f -n __fish_is_first_token -a 'transfer' -d 'Transfer of archives from another repository'
+complete -c borg -f -n __fish_is_first_token -a 'undelete' -d 'Undelete archives'
+complete -c borg -f -n __fish_is_first_token -a 'version' -d 'Display borg client version / borg server version'
 
 function __fish_borg_seen_key
     if __fish_seen_subcommand_from key
@@ -34,12 +44,10 @@ complete -c borg -f -n __fish_borg_seen_key  -a 'export' -d 'Export a repository
 complete -c borg -f -n __fish_borg_seen_key  -a 'change-passphrase' -d 'Change key file passphrase'
 
 complete -c borg -f -n __fish_is_first_token -a 'serve' -d 'Start in server mode'
-complete -c borg -f -n __fish_is_first_token -a 'upgrade' -d 'Upgrade a repository'
 complete -c borg -f -n __fish_is_first_token -a 'recreate' -d 'Recreate contents of existing archives'
 complete -c borg -f -n __fish_is_first_token -a 'export-tar' -d 'Create tarball from an archive'
 complete -c borg -f -n __fish_is_first_token -a 'with-lock' -d 'Run a command while repository lock held'
 complete -c borg -f -n __fish_is_first_token -a 'break-lock' -d 'Break the repository lock'
-complete -c borg -f -n __fish_is_first_token -a 'config' -d 'Get/set options in repo/cache config'
 
 function __fish_borg_seen_benchmark
     if __fish_seen_subcommand_from benchmark
@@ -74,29 +82,42 @@ complete -c borg -f -s v -l 'verbose'               -d 'Log level INFO'
 complete -c borg -f      -l 'debug'                 -d 'Log level DEBUG'
 complete -c borg -f      -l 'debug-topic'           -d 'Enable TOPIC debugging'
 complete -c borg -f -s p -l 'progress'              -d 'Show progress information'
+complete -c borg -f      -l 'iec'                   -d 'Format using IEC units (1KiB = 1024B)'
 complete -c borg -f      -l 'log-json'              -d 'Output one JSON object per log line'
 complete -c borg -f      -l 'lock-wait'             -d 'Wait for lock max N seconds [1]'
 complete -c borg -f      -l 'show-version'          -d 'Log version information'
 complete -c borg -f      -l 'show-rc'               -d 'Log the return code'
 complete -c borg -f      -l 'umask'                 -d 'Set umask to M [0077]'
 complete -c borg         -l 'remote-path'           -d 'Use PATH as remote borg executable'
-complete -c borg -f      -l 'remote-ratelimit'      -d 'Set remote network upload RATE limit'
-complete -c borg -f      -l 'consider-part-files'   -d 'Treat part files like normal files'
+complete -c borg -f      -l 'upload-ratelimit'      -d 'Set network upload rate limit in kiByte/s'
+complete -c borg -f      -l 'upload-buffer'         -d 'Set network upload buffer size in MiB'
 complete -c borg         -l 'debug-profile'         -d 'Write execution profile into FILE'
 complete -c borg         -l 'rsh'                   -d 'Use COMMAND instead of ssh'
-
-# borg init options
-set -l encryption_modes "none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2"
-complete -c borg -f -s e -l 'encryption'            -d 'Encryption key MODE' -a "$encryption_modes" -n "__fish_seen_subcommand_from init"
-complete -c borg -f      -l 'make-parent-dirs'      -d 'Create parent directories'                  -n "__fish_seen_subcommand_from init"
+complete -c borg         -l 'socket'                -d 'Use UNIX DOMAIN socket at PATH'
+
+# borg analyze options
+complete -c borg -f -s a -l 'match-archives'        -d 'Only archive names matching PATTERN'        -n "__fish_seen_subcommand_from analyze"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from analyze"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from analyze"
+
+# borg repo-compress options
+# Define compression methods once at the top
+set -l compression_methods "none auto lz4 zstd,1 zstd,2 zstd,3 zstd,4 zstd,5 zstd,6 zstd,7 zstd,8 zstd,9 zstd,10 zstd,11 zstd,12 zstd,13 zstd,14 zstd,15 zstd,16 zstd,17 zstd,18 zstd,19 zstd,20 zstd,21 zstd,22 zlib,1 zlib,2 zlib,3 zlib,4 zlib,5 zlib,6 zlib,7 zlib,8 zlib,9 lzma,0 lzma,1 lzma,2 lzma,3 lzma,4 lzma,5 lzma,6 lzma,7 lzma,8 lzma,9"
+complete -c borg -f -s C -l 'compression'           -d 'Select compression ALGORITHM,LEVEL [lz4]' -a "$compression_methods" -n "__fish_seen_subcommand_from repo-compress"
+complete -c borg -f -s s -l 'stats'                 -d 'Print statistics'                            -n "__fish_seen_subcommand_from repo-compress"
 
 # borg create options
-complete -c borg -f -s n -l 'dry-run'               -d 'Do not change the repository'                   -n "__fish_seen_subcommand_from create"
+complete -c borg -f -s n -l 'dry-run'               -d 'Do not create a backup archive'                 -n "__fish_seen_subcommand_from create"
 complete -c borg -f -s s -l 'stats'                 -d 'Print verbose statistics'                       -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'list'                  -d 'Print verbose list of items'                    -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'filter'                -d 'Only items with given STATUSCHARS'              -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'json'                  -d 'Print verbose stats as json'                    -n "__fish_seen_subcommand_from create"
-complete -c borg -f      -l 'no-cache-sync'         -d 'Do not synchronize the cache'                   -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'stdin-name'            -d 'Use NAME in archive for stdin data'             -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'content-from-command'  -d 'Interpret PATH as command and store its stdout' -n "__fish_seen_subcommand_from create"
 # Exclusion options
@@ -107,11 +128,10 @@ complete -c borg         -l 'patterns-from'         -d 'Include/exclude paths fr
 complete -c borg -f      -l 'exclude-caches'        -d 'Exclude directories tagged as cache'            -n "__fish_seen_subcommand_from create"
 complete -c borg         -l 'exclude-if-present'    -d 'Exclude directories that contain FILENAME'      -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'keep-exclude-tags'     -d 'Keep tag files of excluded directories'         -n "__fish_seen_subcommand_from create"
-complete -c borg -f      -l 'keep-tag-files'        -d 'Keep tag files of excluded directories'         -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'exclude-nodump'        -d 'Exclude files flagged NODUMP'                   -n "__fish_seen_subcommand_from create"
 # Filesystem options
 complete -c borg -f -s x -l 'one-file-system'       -d 'Stay in the same file system'                   -n "__fish_seen_subcommand_from create"
-complete -c borg -f      -l 'numeric-owner'         -d 'Only store numeric user:group identifiers'      -n "__fish_seen_subcommand_from create"
+complete -c borg -f      -l 'numeric-ids'           -d 'Only store numeric user:group identifiers'      -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'noatime'               -d 'Do not store atime'                             -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'noctime'               -d 'Do not store ctime'                             -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'nobirthtime'           -d 'Do not store creation date'                     -n "__fish_seen_subcommand_from create"
@@ -126,15 +146,13 @@ complete -c borg -f      -l 'read-special'          -d 'Open device files like r
 complete -c borg -f      -l 'comment'               -d 'Add COMMENT to the archive'                     -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'timestamp'             -d 'Set creation TIME (yyyy-mm-ddThh:mm:ss)'        -n "__fish_seen_subcommand_from create"
 complete -c borg         -l 'timestamp'             -d 'Set creation time by reference FILE'            -n "__fish_seen_subcommand_from create"
-complete -c borg -f -s c -l 'checkpoint-interval'   -d 'Write checkpoint every N seconds [1800]'        -n "__fish_seen_subcommand_from create"
 complete -c borg -f      -l 'chunker-params'        -d 'Chunker PARAMETERS [19,23,21,4095]'             -n "__fish_seen_subcommand_from create"
-set -l compression_methods "none auto lz4 zstd,1 zstd,2 zstd,3 zstd,4 zstd,5 zstd,6 zstd,7 zstd,8 zstd,9 zstd,10 zstd,11 zstd,12 zstd,13 zstd,14 zstd,15 zstd,16 zstd,17 zstd,18 zstd,19 zstd,20 zstd,21 zstd,22 zlib,1 zlib,2 zlib,3 zlib,4 zlib,5 zlib,6 zlib,7 zlib,8 zlib,9 lzma,0 lzma,1 lzma,2 lzma,3 lzma,4 lzma,5 lzma,6 lzma,7 lzma,8 lzma,9"
 complete -c borg -f -s C -l 'compression'           -d 'Select compression ALGORITHM,LEVEL [lz4]' -a "$compression_methods" -n "__fish_seen_subcommand_from create"
 
 # borg extract options
 complete -c borg -f      -l 'list'                  -d 'Print verbose list of items'                -n "__fish_seen_subcommand_from extract"
 complete -c borg -f -s n -l 'dry-run'               -d 'Do not actually extract any files'          -n "__fish_seen_subcommand_from extract"
-complete -c borg -f      -l 'numeric-owner'         -d 'Only obey numeric user:group identifiers'   -n "__fish_seen_subcommand_from extract"
+complete -c borg -f      -l 'numeric-ids'           -d 'Only obey numeric user:group identifiers'   -n "__fish_seen_subcommand_from extract"
 complete -c borg -f      -l 'nobsdflags'            -d 'Do not extract/set bsdflags'                -n "__fish_seen_subcommand_from extract"
 complete -c borg -f      -l 'noflags'               -d 'Do not extract/set flags'                   -n "__fish_seen_subcommand_from extract"
 complete -c borg -f      -l 'noacls'                -d 'Do not extract/set ACLs'                    -n "__fish_seen_subcommand_from extract"
@@ -153,15 +171,18 @@ complete -c borg -f      -l 'repository-only'       -d 'Only perform repository
 complete -c borg -f      -l 'archives-only'         -d 'Only perform archives checks'               -n "__fish_seen_subcommand_from check"
 complete -c borg -f      -l 'verify-data'           -d 'Cryptographic integrity verification'       -n "__fish_seen_subcommand_from check"
 complete -c borg -f      -l 'repair'                -d 'Attempt to repair found inconsistencies'    -n "__fish_seen_subcommand_from check"
-complete -c borg -f      -l 'save-space'            -d 'Work slower but using less space'           -n "__fish_seen_subcommand_from check"
 complete -c borg -f      -l 'max-duration'          -d 'Partial repo check for max. SECONDS'        -n "__fish_seen_subcommand_from check"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from check"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from check"
-set -l sort_keys "timestamp name id"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from check"
+set -l sort_keys "timestamp archive name id tags host user"
 complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from check"
 complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from check"
 complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from check"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from check"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from check"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from check"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from check"
 
 # borg rename
 # no specific options
@@ -173,10 +194,15 @@ complete -c borg -f      -l 'json'                  -d 'List contents in json fo
 complete -c borg -f      -l 'json-lines'            -d 'List contents in json lines format'         -n "__fish_seen_subcommand_from list"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from list"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from list"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from list"
+set -l sort_keys "timestamp archive name id tags host user"
 complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from list"
 complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from list"
 complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from list"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from list"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from list"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from list"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from list"
 # Exclusion options
 complete -c borg    -s e -l 'exclude'               -d 'Exclude paths matching PATTERN'             -n "__fish_seen_subcommand_from list"
 complete -c borg         -l 'exclude-from'          -d 'Read exclude patterns from EXCLUDEFILE'     -n "__fish_seen_subcommand_from list"
@@ -184,7 +210,7 @@ complete -c borg -f      -l 'pattern'               -d 'Include/exclude paths ma
 complete -c borg         -l 'patterns-from'         -d 'Include/exclude paths from PATTERNFILE'     -n "__fish_seen_subcommand_from list"
 
 # borg diff options
-complete -c borg -f      -l 'numeric-owner'         -d 'Only consider numeric user:group'           -n "__fish_seen_subcommand_from diff"
+complete -c borg -f      -l 'numeric-ids'           -d 'Only consider numeric user:group'           -n "__fish_seen_subcommand_from diff"
 complete -c borg -f      -l 'same-chunker-params'   -d 'Override check of chunker parameters'       -n "__fish_seen_subcommand_from diff"
 complete -c borg -f      -l 'sort'                  -d 'Sort the output lines by file path'         -n "__fish_seen_subcommand_from diff"
 complete -c borg -f      -l 'json-lines'            -d 'Format output as JSON Lines'                -n "__fish_seen_subcommand_from diff"
@@ -196,16 +222,18 @@ complete -c borg         -l 'patterns-from'         -d 'Include/exclude paths fr
 
 # borg delete options
 complete -c borg -f -s n -l 'dry-run'               -d 'Do not change the repository'               -n "__fish_seen_subcommand_from delete"
-complete -c borg -f -s s -l 'stats'                 -d 'Print verbose statistics'                   -n "__fish_seen_subcommand_from delete"
-complete -c borg -f      -l 'cache-only'            -d "Delete only the local cache"                -n "__fish_seen_subcommand_from delete"
-complete -c borg -f      -l 'force'                 -d 'Force deletion of corrupted archives'       -n "__fish_seen_subcommand_from delete"
-complete -c borg -f      -l 'save-space'            -d 'Work slower but using less space'           -n "__fish_seen_subcommand_from delete"
+complete -c borg -f      -l 'list'                  -d 'Output verbose list of archives'              -n "__fish_seen_subcommand_from delete"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from delete"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from delete"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from delete"
+set -l sort_keys "timestamp archive name id tags host user"
 complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from delete"
 complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from delete"
 complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from delete"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from delete"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from delete"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from delete"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from delete"
 
 # borg prune options
 complete -c borg -f -s n -l 'dry-run'               -d 'Do not change the repository'               -n "__fish_seen_subcommand_from prune"
@@ -220,23 +248,47 @@ complete -c borg -f -s H -l 'keep-hourly'           -d 'NUMBER of hourly archive
 complete -c borg -f -s d -l 'keep-daily'            -d 'NUMBER of daily archives to keep'           -n "__fish_seen_subcommand_from prune"
 complete -c borg -f -s w -l 'keep-weekly'           -d 'NUMBER of weekly archives to keep'          -n "__fish_seen_subcommand_from prune"
 complete -c borg -f -s m -l 'keep-monthly'          -d 'NUMBER of monthly archives to keep'         -n "__fish_seen_subcommand_from prune"
+complete -c borg -f      -l 'keep-13weekly'         -d 'NUMBER of quarterly archives to keep (13 week strategy)' -n "__fish_seen_subcommand_from prune"
+complete -c borg -f      -l 'keep-3monthly'         -d 'NUMBER of quarterly archives to keep (3 month strategy)' -n "__fish_seen_subcommand_from prune"
 complete -c borg -f -s y -l 'keep-yearly'           -d 'NUMBER of yearly archives to keep'          -n "__fish_seen_subcommand_from prune"
-complete -c borg -f      -l 'save-space'            -d 'Work slower but using less space'           -n "__fish_seen_subcommand_from prune"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from prune"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from prune"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from prune"
 
 # borg compact options
-complete -c borg -f -s n -l 'cleanup-commits'       -d 'Cleanup commit-only segment files'          -n "__fish_seen_subcommand_from compact"
+complete -c borg -f -s n -l 'dry-run'               -d 'Do nothing'                                  -n "__fish_seen_subcommand_from compact"
+complete -c borg -f -s s -l 'stats'                 -d 'Print statistics (might be much slower)'     -n "__fish_seen_subcommand_from compact"
 
 # borg info options
 complete -c borg -f      -l 'json'                  -d 'Format output in json format'               -n "__fish_seen_subcommand_from info"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from info"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from info"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from info"
+set -l sort_keys "timestamp archive name id tags host user"
 complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from info"
 complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from info"
 complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from info"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from info"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from info"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from info"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from info"
+
+# borg repo-list options
+complete -c borg -f      -l 'short'                 -d 'Only print the archive IDs, nothing else'   -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'format'                -d 'Specify format for archive listing'         -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'json'                  -d 'Format output as JSON'                      -n "__fish_seen_subcommand_from repo-list"
+# Archive filters
+complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from repo-list"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from repo-list"
+complete -c borg -f      -l 'deleted'               -d 'Consider only soft-deleted archives'        -n "__fish_seen_subcommand_from repo-list"
 
 # borg mount options
 complete -c borg -f -s f -l 'foreground'            -d 'Stay in foreground, do not daemonize'       -n "__fish_seen_subcommand_from mount"
@@ -245,10 +297,15 @@ set -l fuse_options "ac_attr_timeout= allow_damaged_files allow_other allow_root
 complete -c borg -f -s o                            -d 'Fuse mount OPTION' -a "$fuse_options"       -n "__fish_seen_subcommand_from mount"
 # Archive filters
 complete -c borg -f -s P -l 'prefix'                -d 'Only archive names starting with PREFIX'    -n "__fish_seen_subcommand_from mount"
-complete -c borg -f -s a -l 'glob-archives'         -d 'Only archive names matching GLOB'           -n "__fish_seen_subcommand_from mount"
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from mount"
+set -l sort_keys "timestamp archive name id tags host user"
 complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from mount"
 complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from mount"
 complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from mount"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from mount"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from mount"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from mount"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from mount"
 # Exclusion options
 complete -c borg    -s e -l 'exclude'               -d 'Exclude paths matching PATTERN'             -n "__fish_seen_subcommand_from mount"
 complete -c borg         -l 'exclude-from'          -d 'Read exclude patterns from EXCLUDEFILE'     -n "__fish_seen_subcommand_from mount"
@@ -259,6 +316,21 @@ complete -c borg -f      -l 'strip-components'      -d 'Remove NUMBER of leading
 # borg umount
 # no specific options
 
+# borg tag options
+complete -c borg -f      -l 'set'                   -d 'Set tags (can be given multiple times)'      -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'add'                   -d 'Add tags (can be given multiple times)'      -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'remove'                -d 'Remove tags (can be given multiple times)'   -n "__fish_seen_subcommand_from tag"
+# Archive filters
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from tag"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from tag"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from tag"
+
 # borg key change-passphrase
 # no specific options
 
@@ -266,15 +338,42 @@ complete -c borg -f      -l 'strip-components'      -d 'Remove NUMBER of leading
 complete -c borg -f      -l 'paper'                 -d 'Create an export for printing'              -n "__fish_seen_subcommand_from export"
 complete -c borg -f      -l 'qr-html'               -d 'Create an html file for printing and qr'    -n "__fish_seen_subcommand_from export"
 
+# borg transfer options
+complete -c borg -f -s n -l 'dry-run'               -d 'Do not change repository, just check'       -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'other-repo'            -d 'Transfer archives from the other repository' -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'from-borg1'            -d 'Other repository is borg 1.x'               -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'upgrader'              -d 'Use the upgrader to convert transferred data' -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f -s C -l 'compression'           -d 'Select compression algorithm' -a "$compression_methods" -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'recompress'            -d 'Recompress chunks CONDITION' -a "$recompress_when" -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'chunker-params'        -d 'Chunker PARAMETERS [19,23,21,4095]'         -n "__fish_seen_subcommand_from transfer"
+# Archive filters
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from transfer"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from transfer"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from transfer"
+
 # borg key import
 complete -c borg -f      -l 'paper'                 -d 'Import from a backup done with --paper'     -n "__fish_seen_subcommand_from import"
 
-# borg upgrade
-complete -c borg -f -s n -l 'dry-run'               -d 'Do not change the repository'               -n "__fish_seen_subcommand_from upgrade"
-complete -c borg -f      -l 'inplace'               -d 'Rewrite repository in place'                -n "__fish_seen_subcommand_from upgrade"
-complete -c borg -f      -l 'force'                 -d 'Force upgrade'                              -n "__fish_seen_subcommand_from upgrade"
-complete -c borg -f      -l 'tam'                   -d 'Enable manifest authentication'             -n "__fish_seen_subcommand_from upgrade"
-complete -c borg -f      -l 'disable-tam'           -d 'Disable manifest authentication'            -n "__fish_seen_subcommand_from upgrade"
+# borg undelete options
+complete -c borg -f -s n -l 'dry-run'               -d 'Do not change repository'                   -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'list'                  -d 'Output verbose list of archives'            -n "__fish_seen_subcommand_from undelete"
+# Archive filters
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from undelete"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from undelete"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from undelete"
+
 
 # borg recreate
 complete -c borg -f      -l 'list'                  -d 'Print verbose list of items'                -n "__fish_seen_subcommand_from recreate"
@@ -289,9 +388,18 @@ complete -c borg         -l 'patterns-from'         -d 'Include/exclude paths fr
 complete -c borg -f      -l 'exclude-caches'        -d 'Exclude directories tagged as cache'        -n "__fish_seen_subcommand_from recreate"
 complete -c borg         -l 'exclude-if-present'    -d 'Exclude directories that contain FILENAME'  -n "__fish_seen_subcommand_from recreate"
 complete -c borg -f      -l 'keep-exclude-tags'     -d 'Keep tag files of excluded directories'     -n "__fish_seen_subcommand_from recreate"
+# Archive filters
+complete -c borg -f -s a -l 'match-archives'        -d 'Only consider archives matching all patterns' -n "__fish_seen_subcommand_from recreate"
+set -l sort_keys "timestamp archive name id tags host user"
+complete -c borg -f      -l 'sort-by'               -d 'Sorting KEYS [timestamp]' -a "$sort_keys"   -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'first'                 -d 'Only first N archives'                      -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'last'                  -d 'Only last N archives'                       -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'oldest'                -d 'Consider archives within TIMESPAN from oldest' -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'newest'                -d 'Consider archives within TIMESPAN from newest' -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'older'                 -d 'Consider archives older than TIMESPAN'      -n "__fish_seen_subcommand_from recreate"
+complete -c borg -f      -l 'newer'                 -d 'Consider archives newer than TIMESPAN'      -n "__fish_seen_subcommand_from recreate"
 # Archive options
 complete -c borg -f      -l 'target'                -d "Create a new ARCHIVE"                       -n "__fish_seen_subcommand_from recreate"
-complete -c borg -f -s c -l 'checkpoint-interval'   -d 'Write checkpoint every N seconds [1800]'    -n "__fish_seen_subcommand_from recreate"
 complete -c borg -f      -l 'comment'               -d 'Add COMMENT to the archive'                 -n "__fish_seen_subcommand_from recreate"
 complete -c borg -f      -l 'timestamp'             -d 'Set creation TIME (yyyy-mm-ddThh:mm:ss)'    -n "__fish_seen_subcommand_from recreate"
 complete -c borg         -l 'timestamp'             -d 'Set creation time using reference FILE'     -n "__fish_seen_subcommand_from recreate"
@@ -303,6 +411,25 @@ complete -c borg -f      -l 'chunker-params'        -d 'Chunker PARAMETERS [19,2
 # borg export-tar options
 complete -c borg         -l 'tar-filter'            -d 'Filter program to pipe data through'        -n "__fish_seen_subcommand_from export-tar"
 complete -c borg -f      -l 'list'                  -d 'Print verbose list of items'                -n "__fish_seen_subcommand_from export-tar"
+complete -c borg -f      -l 'tar-format'            -d 'Select tar format: BORG, PAX or GNU'        -n "__fish_seen_subcommand_from export-tar"
+# Exclusion options
+complete -c borg    -s e -l 'exclude'               -d 'Exclude paths matching PATTERN'             -n "__fish_seen_subcommand_from export-tar"
+complete -c borg         -l 'exclude-from'          -d 'Read exclude patterns from EXCLUDEFILE'     -n "__fish_seen_subcommand_from export-tar"
+complete -c borg -f      -l 'pattern'               -d 'Include/exclude paths matching PATTERN'     -n "__fish_seen_subcommand_from export-tar"
+complete -c borg         -l 'patterns-from'         -d 'Include/exclude paths from PATTERNFILE'     -n "__fish_seen_subcommand_from export-tar"
+complete -c borg -f      -l 'strip-components'      -d 'Remove NUMBER of leading path elements'     -n "__fish_seen_subcommand_from export-tar"
+
+# borg import-tar options
+complete -c borg         -l 'tar-filter'            -d 'Filter program to pipe data through'        -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f -s s -l 'stats'                 -d 'Print statistics for the created archive'   -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'list'                  -d 'Print verbose list of items'                -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'filter'                -d 'Only display items with given STATUSCHARS'  -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'json'                  -d 'Output stats as JSON'                       -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'ignore-zeros'          -d 'Ignore zero-filled blocks in the input'     -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'comment'               -d 'Add COMMENT to the archive'                 -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'timestamp'             -d 'Set creation TIME (yyyy-mm-ddThh:mm:ss)'    -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f      -l 'chunker-params'        -d 'Chunker PARAMETERS [19,23,21,4095]'         -n "__fish_seen_subcommand_from import-tar"
+complete -c borg -f -s C -l 'compression'           -d 'Select compression ALGORITHM,LEVEL [lz4]' -a "$compression_methods" -n "__fish_seen_subcommand_from import-tar"
 # Exclusion options
 complete -c borg    -s e -l 'exclude'               -d 'Exclude paths matching PATTERN'             -n "__fish_seen_subcommand_from recreate"
 complete -c borg         -l 'exclude-from'          -d 'Read exclude patterns from EXCLUDEFILE'     -n "__fish_seen_subcommand_from recreate"
@@ -314,10 +441,6 @@ complete -c borg -f      -l 'strip-components'      -d 'Remove NUMBER of leading
 complete -c borg         -l 'restrict-to-path'      -d 'Restrict repository access to PATH'         -n "__fish_seen_subcommand_from serve"
 complete -c borg         -l 'restrict-to-repository' -d 'Restrict repository access at PATH'        -n "__fish_seen_subcommand_from serve"
 
-# borg config
-complete -c borg -f -s c -l 'cache'                 -d 'Get/set/list values in the repo cache'      -n "__fish_seen_subcommand_from config"
-complete -c borg -f -s d -l 'delete'                -d 'Delete the KEY from the config'             -n "__fish_seen_subcommand_from config"
-complete -c borg -f      -l 'list'                  -d 'List the configuration of the repo'         -n "__fish_seen_subcommand_from config"
 
 # borg with-lock
 # no specific options

+ 41 - 17
scripts/shell_completions/zsh/_borg

@@ -56,6 +56,7 @@ _borg_commands() {
     'tag:tag archives'
     'transfer:transfer of archives from another repository'
     'umount:un-mount the FUSE filesystem'
+    'undelete:undelete archive'
     'version:display borg client version / borg server version'
     'with-lock:run a user specified command with the repository lock held'
   )
@@ -115,6 +116,8 @@ _borg-compact() {
   __borg_setup_common_options
 
   _arguments -s -w -S : \
+    '(-n --dry-run)'{-n,--dry-run}'[do nothing]' \
+    '(-s --stats)'{-s,--stats}'[print statistics (might be much slower)]' \
     $common_options
 }
 
@@ -140,7 +143,7 @@ _borg-create() {
     '--content-from-command[interpret PATH as command and store its stdout]' \
     '--exclude-nodump[exclude files flagged NODUMP]' \
     '(-x --one-file-system)'{-x,--one-file-system}'[stay in the same file system]' \
-    '--numeric-owner[only store numeric user and group identifiers]' \
+    '--numeric-ids[only store numeric user and group identifiers]' \
     '--atime[do store atime into archive]' \
     '--noctime[do not store ctime into archive]' \
     '--nobirthtime[do not store birthtime (creation date) into archive]' \
@@ -284,7 +287,7 @@ _borg-diff() {
   __borg_setup_common_exclude_options
 
   _arguments -s -w -S : \
-    '--numeric-owner[only obey numeric user and group identifiers]' \
+    '--numeric-ids[only obey numeric user and group identifiers]' \
     '--same-chunker-params[override check of chunker parameters]' \
     '--sort[sort the output lines by file path]' \
     '--json-lines[format output as JSON Lines]' \
@@ -304,6 +307,7 @@ _borg-export-tar() {
   _arguments -s -w -S : \
     '--tar-filter[filter program to pipe data through]: :_cmdstring' \
     '--list[output verbose list of items (files, dirs, ...)]' \
+    '--tar-format[select tar format: BORG, PAX or GNU]:(BORG PAX GNU)' \
     $common_exclude_extract_options \
     $common_options \
     ':ARCHIVE: _borg_archive -a' \
@@ -320,7 +324,7 @@ _borg-extract() {
   _arguments -s -w -S : \
     '--list[output verbose list of items (files, dirs, ...)]' \
     '(-n --dry-run)'{-n,--dry-run}'[do not actually change any files]' \
-    '--numeric-owner[only obey numeric user and group identifiers]' \
+    '--numeric-ids[only obey numeric user and group identifiers]' \
     '--noacls[do not extract/set ACLs]' \
     '--noxattrs[do not extract/set xattrs]' \
     '--noflags[do not extract/set flags (e.g. NODUMP, IMMUTABLE)]' \
@@ -352,8 +356,15 @@ _borg-import-tar() {
 
   _arguments -s -w -S : \
     '--tar-filter[filter program to pipe data through]: :_cmdstring' \
+    '(-s --stats)'{-s,--stats}'[print statistics for the created archive]' \
     '--list[output verbose list of items (files, dirs, ...)]' \
-    $common_exclude_extract_options \
+    '--filter[only display items with the given status characters]: :' \
+    '--json[output stats as JSON (implies --stats)]' \
+    '--ignore-zeros[ignore zero-filled blocks in the input tarball]' \
+    '--comment[add a comment text to the archive]: :' \
+    '--timestamp[manually specify the archive creation date/time]: :' \
+    '--chunker-params[specify the chunker parameters]: :' \
+    '(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression' \
     $common_options \
     ':ARCHIVE: _borg_archive -a' \
     ':FILE:_files' \
@@ -498,18 +509,9 @@ _borg-recreate() {
   __borg_setup_common_options
   __borg_setup_common_create_options
 
-  local -a mods=(
-    'if-different:recompress if current compression is with a different compression algorithm (the level is not considered)'
-    'always:recompress even if current compression is with the same compression algorithm (use this to change the compression level)'
-    'never:do not recompress (use this option explicitly to prevent recompression)'
-  )
-  mods=( ${(q)mods//\\/\\\\} )
-  mods=( ${mods//:/\\:} )
-
   _arguments -s -w -S : \
     $common_create_options \
     '--target=[create a new archive with the name ARCHIVE]:ARCHIVE: _borg_placeholder_or_archive "${line[1]%%\:\:*}"' \
-    '--recompress=[recompress data chunks according to "MODE" and "--compression"]:MODE:'"(($mods))" \
     $common_options \
     ':ARCHIVE: _borg_archive' \
     '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
@@ -628,6 +630,9 @@ _borg-tag() {
 
   _arguments -s -w -S : \
     $common_dry_run_stats_options \
+    '--set=[set tags (can be given multiple times)]:TAG' \
+    '--add=[add tags (can be given multiple times)]:TAG' \
+    '--remove=[remove tags (can be given multiple times)]:TAG' \
     $common_archive_filters_options \
     $common_options \
     ':ARCHIVE: _borg_archive' \
@@ -643,9 +648,28 @@ _borg-transfer() {
 
   _arguments -s -w -S : \
     $common_dry_run_stats_options \
+    '--other-repo=[transfer archives from the other repository]:SRC_REPOSITORY:_borg_repository' \
+    '--from-borg1[other repository is borg 1.x]' \
+    '--upgrader=[use the upgrader to convert transferred data]:UPGRADER:(From12To20 NoOp)' \
+    '(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression' \
+    '--recompress=[recompress chunks CONDITION]:WHEN:(always never)' \
+    '--chunker-params=[specify the chunker parameters]: :_borg_chunker_params_examples' \
+    $common_archive_filters_options \
+    $common_options
+}
+
+(( $+functions[_borg-undelete] )) ||
+_borg-undelete() {
+  local -a common_options common_archive_filters_options
+  __borg_setup_common_options
+  __borg_setup_common_archive_filters_options
+
+  _arguments -s -w -S : \
+    '(-n --dry-run)'{-n,--dry-run}'[do not change repository]' \
+    '--list[output verbose list of archives]' \
     $common_archive_filters_options \
     $common_options \
-    '(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression'
+    '::ARCHIVE: _borg_archive'
 }
 
 (( $+functions[_borg-umount] )) ||
@@ -708,8 +732,8 @@ __borg_setup_common_options() {
     '--show-rc[show/log the return code (rc)]'
     '--umask=[set umask to M (local only, default: 0077)]:M'
     '--remote-path=[set remote path to executable (default: "borg")]: :_cmdstring'
-    '--remote-ratelimit=[set remote network upload rate limit in kiByte/s (default: 0=unlimited)]: : _borg_guard_unsigned_number "RATE"'
-    '--remote-buffer=[set upload buffer size in MiB. (default: 0=no buffer)]: : _borg_guard_unsigned_number "UPLOAD_BUFFER"'
+    '--upload-ratelimit=[set network upload rate limit in kiByte/s (default: 0=unlimited)]: : _borg_guard_unsigned_number "RATE"'
+    '--upload-buffer=[set network upload buffer size in MiB. (default: 0=no buffer)]: : _borg_guard_unsigned_number "UPLOAD_BUFFER"'
     '--debug-profile=[write execution profile in Borg format into FILE]:FILE:_files'
     '--rsh=[use COMMAND instead of ssh]: :_cmdstring'
     '(-r --repo)'{-r,--repo}'=[repository]'
@@ -749,7 +773,7 @@ __borg_setup_common_archive_filters_options() {
   __borg_setup_common_match_archives_filter_options
   typeset -ga common_archive_filters_options=(
     $common_match_archives_filter_options
-    '--sort-by=[Comma-separated list of sorting keys, default: timestamp]:KEYS:(timestamp name id)'
+    '--sort-by=[Comma-separated list of sorting keys, default: timestamp]:KEYS:(timestamp archive name id tags host user)'
     '(--last)--first=[consider first N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
     '(--first)--last=[consider last N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
   )

+ 55 - 0
src/borg/testsuite/shell_completions_test.py

@@ -0,0 +1,55 @@
+import os
+import subprocess
+import pytest
+
+SHELL_COMPLETIONS_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts", "shell_completions")
+
+
+def test_bash_completion_is_valid():
+    """Test that the bash completion file is valid bash syntax."""
+    bash_completion_file = os.path.join(SHELL_COMPLETIONS_DIR, "bash", "borg")
+    assert os.path.isfile(bash_completion_file)
+
+    # Check if bash is available
+    try:
+        subprocess.run(["bash", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
+    except (subprocess.SubprocessError, FileNotFoundError):
+        pytest.skip("bash not available")
+
+    # Test if the bash completion file can be sourced without errors
+    result = subprocess.run(["bash", "-n", bash_completion_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    assert result.returncode == 0, f"Bash completion file has syntax errors: {result.stderr.decode()}"
+
+
+def test_fish_completion_is_valid():
+    """Test that the fish completion file is valid fish syntax."""
+    fish_completion_file = os.path.join(SHELL_COMPLETIONS_DIR, "fish", "borg.fish")
+    assert os.path.isfile(fish_completion_file)
+
+    # Check if fish is available
+    try:
+        subprocess.run(["fish", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
+    except (subprocess.SubprocessError, FileNotFoundError):
+        pytest.skip("fish not available")
+
+    # Test if the fish completion file can be sourced without errors
+    result = subprocess.run(
+        ["fish", "-c", f"source {fish_completion_file}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
+    assert result.returncode == 0, f"Fish completion file has syntax errors: {result.stderr.decode()}"
+
+
+def test_zsh_completion_is_valid():
+    """Test that the zsh completion file is valid zsh syntax."""
+    zsh_completion_file = os.path.join(SHELL_COMPLETIONS_DIR, "zsh", "_borg")
+    assert os.path.isfile(zsh_completion_file)
+
+    # Check if zsh is available
+    try:
+        subprocess.run(["zsh", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
+    except (subprocess.SubprocessError, FileNotFoundError):
+        pytest.skip("zsh not available")
+
+    # Test if the zsh completion file can be sourced without errors
+    result = subprocess.run(["zsh", "-n", zsh_completion_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    assert result.returncode == 0, f"Zsh completion file has syntax errors: {result.stderr.decode()}"