├── README.md ├── _git-annex └── git-annex.plugin.zsh /README.md: -------------------------------------------------------------------------------- 1 | # ZSH completion for git-annex 2 | 3 | This compdef allows tab completion for most of the [git-annex][] commands. 4 | See also [tips in the git-annex ikiwiki](http://git-annex.branchable.com/tips/ZSH_completion/). 5 | 6 | # Install 7 | 8 | To install it: 9 | 10 | 1. make sure your have Python 3 installed (as `python3` somewhere in your `$PATH`; tested with 3.4, should work with 3.2+) 11 | 2. get it from [GitHub](https://github.com/Schnouki/git-annex-zsh-completion) 12 | 3. copy `_git-annex` to somewhere in your `$fpath` (I use `$HOME/.config/zsh/completion`) 13 | 4. run `autoload -U path/to/_git-annex` 14 | 5. type `git annex ` 15 | 16 | If you use [oh-my-zsh][] then just clone the repository inside your oh-my-zsh repo: 17 | 18 | ```Shell 19 | git clone https://github.com/Schnouki/git-annex-zsh-completion.git ~/.oh-my-zsh/custom/plugins/git-annex 20 | ``` 21 | 22 | and enable it in your `.zshrc`: 23 | 24 | ```zsh 25 | plugins+=(git-annex) 26 | ``` 27 | 28 | If you use [antigen][] then you could add the bundle and be done with: 29 | 30 | ```Shell 31 | antigen bundle add Schnouki/git-annex-zsh-completion 32 | ``` 33 | 34 | [antigen]: http://antigen.sharats.me/ 35 | [git-annex]: http://git-annex.branchable.com/ 36 | [oh-my-zsh]: http://github.com/robbyrussell/oh-my-zsh 37 | -------------------------------------------------------------------------------- /_git-annex: -------------------------------------------------------------------------------- 1 | #compdef git-annex 2 | #description manage files while ignoring their content 3 | 4 | # ZSH Completion for git-annex (http://git-annex.branchable.com/) 5 | # 6 | # This completion was written for git-annex version: 5.20140517 7 | # 8 | # Note that this completion is still very much early alpha work in progress. 9 | # 10 | # This completion depends on Python for a json parser, sorry. Unfortunately 11 | # there is no such thing in zsh (yet). 12 | # 13 | # Skeleton implementation by Frank Terbeck 14 | # Blanks filled in by Valentin Haenel 15 | # Updated by Thomas Jost 16 | # Updated by Robin Schneider 17 | # Licence: WTFPL (http://sam.zoy.org/wtfpl/) 18 | # 19 | # To use this completion drop it somewhere in you $fpath, e.g.: 20 | # 21 | # $ git clone $CLONEURL 22 | # $ fpath+=$PWD/git-annex-zsh-completion 23 | # $ compinit git-annex 24 | # 25 | # TODO 26 | # ---- 27 | # 28 | # * 'git annex add' to only complete files not in annex 29 | # * user defined groups are not detected 30 | # * remotes with spaces in their description are not handled 31 | # 32 | 33 | local state line context 34 | local -A opt_args 35 | 36 | __annex_backend(){ 37 | local -a backends 38 | backends=($(git annex version | awk -F': ' '/key\/value backends/ {print $2}')) 39 | _describe -t backends backend backends 40 | } 41 | 42 | __annex_groups(){ 43 | local -a groups 44 | groups=(client transfer backup incrementalbackup smallarchive archive source manual public unwanted) 45 | _describe -t groups 'standard groups' groups 46 | } 47 | 48 | __annex_remotes(){ 49 | local -a remotes 50 | remotes=($(git remote 2>/dev/null)) 51 | _describe -t remotes 'locally available remotes' remotes 52 | } 53 | 54 | __annex_repository(){ 55 | local -a currents 56 | currents=('here') 57 | _describe -t current current currents 58 | 59 | __annex_remotes 60 | 61 | local -a descriptions 62 | descriptions=($(git annex info --fast --json 2>/dev/null | 63 | python3 -c " 64 | import json, re, shlex, sys 65 | parsed = json.load(sys.stdin) 66 | descriptions = [] 67 | for repo in (parsed['trusted repositories'] + parsed['semitrusted repositories'] + parsed['untrusted repositories']): 68 | desc = repo['description'] 69 | m = re.match(r'(?:(.+?)\s*)?\[(.+)\]', desc) 70 | if m is not None: 71 | desc = m.group(2) + ('' if m.group(1) is None else (':' + m.group(1))) 72 | descriptions.append(desc) 73 | print(' '.join(descriptions))")) 74 | _describe -t descriptions 'known remotes' descriptions 75 | 76 | local -a uuids 77 | uuids=($(git annex info --fast --json 2>/dev/null | 78 | python3 -c " 79 | import json, re, sys 80 | parsed = json.load(sys.stdin) 81 | uuids = [] 82 | for repo in (parsed['trusted repositories'] + parsed['semitrusted repositories'] + parsed['untrusted repositories']): 83 | desc = repo['description'] 84 | m = re.match(r'(?:(?P.+?)\s*)?\[(?P.+)\]', desc) 85 | if m is not None: 86 | desc = m.group(1) if m.group(1) is not None else m.group(2) 87 | uuids.append('%s:%s' % (repo['uuid'], desc)) 88 | print(' '.join(uuids))")) 89 | _describe -t uuids uuid uuids 90 | } 91 | 92 | local -a common_opts matching_opts 93 | common_opts=( 94 | '--force[allow actions that may lose annexed data]' 95 | '(-F --fast)'{-F,--fast}'[avoid slow operations]' 96 | '(-a --auto)'{-a,--auto}'[automatic mode]' 97 | '--all[operate on all data, including old versions of files]' 98 | '(-U --unused)'{-U,--unused}'[operate on all unused data]' 99 | '(-k --key=)'{-k,--key=}'[specifies a key to operate on]:key' 100 | '(-q --quiet)'{-q,--quiet}'[avoid verbose output]' 101 | '(-v --verbose)'{-v,--verbose}'[allow verbose output (default)]' 102 | '(-j --json)'{-j,--json}'[generate machine readable JSON]' 103 | '(-d --debug)'{-d,--debug}'[show debug messages]' 104 | '--no-debug[don''t show debug messages]' 105 | '(-f --from=)'{-f,--from=}'[copy/move from repository]:repository:__annex_remotes' 106 | '(-t --to=)'{-t,--to=}'[copy/move to repository]:repository:__annex_remotes' 107 | '(-N --numcopies=)'{-N,--numcopies=}'[override default number of copies]: :_guard "[0-9]#" "number of copies"' 108 | '(-T --time-limit=)'{-T,--time-limit=}'[stop after the specified amount of time]:time' 109 | '--trust=[override trust setting]:repository:__annex_repository' 110 | '--semitrust=[override trust setting back to default]:repository:__annex_repository' 111 | '--untrust=[override trust setting to untrusted]:repository:__annex_repository' 112 | '--trust-glacier-inventory[trust Amazon Glacier inventory]' 113 | '(-b --backend=)'{-b,--backend=}'[specify key-value backend to use]:backend:__annex_backend' 114 | '--format=[specifies a custom output format]:format' 115 | '--user-agent=[override default User-Agent]:User-Agent' 116 | '--notify-finish[show desktop notification after transfer finishes]' 117 | '--notify-start[show desktop notification after transfer starts]' 118 | '-c name=[override git configuration setting]:git configuration setting' 119 | '--help[show help on a specific command]' 120 | ) 121 | matching_opts=( 122 | '(-x --exclude=)'{-x,--exclude=}'[skip files matching the glob pattern]:glob pattern' 123 | '(-I --include=)'{-I,--include=}'[limit to files matching the glob pattern]:glob pattern' 124 | '(-i --in=)'{-i,--in=}'[match files present in a remote]:repository:__annex_repository' 125 | '(-C --copies=)'{-C,--copies=}'[skip files with fewer copies]: :_guard "[0-9]#" "number of copies"' 126 | '--lackingcopies=[match files that need more copies]: :_guard "[0-9]#" "number of copies"' 127 | '--approxlackingcopies=[match files that need more copies (faster)]: :_guard "[0-9]#" "number of copies"' 128 | '(-B --inbackend=)'{-B,--inbackend=}'[match files using a key-value backend]:backend:__annex_backend' 129 | '--inallgroup=[match files present in all remotes in a group]:group:__annex_groups' 130 | '--smallerthan=[match files smaller than a size]:size' 131 | '--largerthan=[match files larger than a size]:size' 132 | '--metadata=[match files with attached metadata]:field=glob' 133 | '--want-get[match files the repository wants to get]' 134 | '--want-drop[match files the repository wants to drop]' 135 | '--not[negate next option]' 136 | '--and[both previous and next option must match]' 137 | '--or[either previous or next option must match]' 138 | '-\\([open group of options]' 139 | '-\\)[close group of options]' 140 | ) 141 | 142 | _arguments -C \ 143 | ': :->command' \ 144 | '*:: :->subcmd' 145 | 146 | case $state in 147 | (command) 148 | local -a common setup maintenance query metadata utility plumbing 149 | common=( 150 | add:'adds files in the path to the annex' 151 | get:'makes the content of annexed files available in this repository' 152 | drop:'drops the content of annexed files from this repository' 153 | move:'move files from/to repositories' 154 | copy:'copy from/to repositories' 155 | unlock:'unlock a file for editing' 156 | edit:'alias for the unlock command' 157 | lock:'lock file again after editing' 158 | sync:'synchronize with one or more remotes' 159 | merge:'merge the git-annex branches of remotes' 160 | mirror:'mirror a source repository to a destination repository' 161 | addurl:'downloads each url to a file, which is added to the annex' 162 | rmurl:'record that a file is no longer available at a url' 163 | import:'move files from somewhere else and add them to the annex' 164 | importfeed:'import the contents of podcast feeds' 165 | watch:'watches for changes and automatically add files' 166 | assistant:'like watch but automatically syncs changes to other remotes' 167 | webapp:'runs a web app to setup and control the git-annex assistant' 168 | ) 169 | setup=( 170 | init:'initialize an annex' 171 | describe:'changes the description of a repository' 172 | initremote:'set up a special remote' 173 | enableremote:'enable an existing special remote for use in the current repository' 174 | numcopies:'tells git-annex how many copies it should preserve of files' 175 | trust:'records that a repository is trusted to not unexpectedly lose content' 176 | untrust:'records that a repository is not trusted and could lose content at any time' 177 | semitrust:'returns a repository to the default semi trusted state' 178 | dead:'Indicates that the repository has been irretrievably lost. (To undo, use semitrust.)' 179 | group:'adds a repository to a group, such as "archival", "enduser", or "transfer"' 180 | ungroup:'removes a repository from a group' 181 | wanted:'display or configure the preferred content setting for a repository' 182 | schedule:'display or configure scheduled jobs' 183 | vicfg:'opens $EDITOR on a temp file containing most of the configuration settings' 184 | direct:'switches a repository to use direct mode' 185 | indirect:'switches a repository back from direct mode to the default, indirect mode' 186 | ) 187 | maintenance=( 188 | fsck:'check annex consistency' 189 | unused:'check for data that has no symlinks pointing to it' 190 | dropunused:'drop unused data, as reported by unused' 191 | addunused:'adds back files listed by unused' 192 | fix:'fix up symlinks that have become broken' 193 | upgrade:'upgrades the repository to current layout' 194 | forget:'rewrites the git-annex branch, throwing away historical data' 195 | repair:'repairs many of the problem with git repositories' 196 | ) 197 | query=( 198 | find:'search for present/missing files in current annex' 199 | whereis:'show all repositories that contain a file' 200 | list:'displays a table of remotes that contain the contents of the specified files' 201 | log:'displays the location log for the specified files' 202 | info:'displays some statistics and other information' 203 | version:'print version and repository information' 204 | help:'print the most frequently used git-annex commands' 205 | map:'generate a visual map of repositories' 206 | ) 207 | metadata=( 208 | metadata:'display or change metadata fields for the specified files' 209 | view:'use metadata to build a view branch of the files in the current branch' 210 | vpop:'switches from the currently active view back to the previous one' 211 | vfilter:'filters the current view' 212 | vadd:'adds an additional level of directories to categorize the files in the current view' 213 | vcycle:'cycles the order of a view involving nested subdirectories' 214 | ) 215 | utility=( 216 | migrate:'migrate annexd files to new backend' 217 | reinject:'moves a src file into the annex as the content of the dest file.' 218 | unannex:'undo an accidental git annex add command' 219 | uninit:'stop using git-annex entirely' 220 | reinit:'reinit a repository, reusing its UUID' 221 | ) 222 | plumbing=( 223 | pre-commit:'fixes up symlinks that are staged as part of a commit' 224 | lookupkey:'looks up the key used for a file in the index' 225 | examinekey:'prints information that can be determined purely looking at a key' 226 | fromkey:'set file to link to given key' 227 | dropkey:'drop annexd data for specified keys' 228 | transferkey:'request a single key to be transferred' 229 | transferkeys:'internal command for the assistant' 230 | rekey:'like migrate, but specifying both the file and its new key' 231 | findref:'like find, but for a specific git ref instead of the current work tree' 232 | test:'runs git-annex''s built-in test suite' 233 | remotedaemon:'detects when network remotes have received git pushes and fetches from them' 234 | xmppgit:'internal command to perform git pulls over XMPP' 235 | fuzztest:'generates fuzz test files (developer tool)' 236 | ) 237 | _describe -t common_commands "common commands" common 238 | _describe -t setup_commands "setup commands" setup 239 | _describe -t maintenance_commands "maintenance commands" maintenance 240 | _describe -t query_commands "query commands" query 241 | _describe -t metadata_commands "metadata commands" metadata 242 | _describe -t utility_commands "utility commands" utility 243 | _describe -t plumbing_commands "plumbing commands" plumbing 244 | ;; 245 | 246 | (subcmd) 247 | case ${line[1]} in 248 | (direct|indirect|map|merge|repair|uninit|upgrade|vcycle|version|vicfg|vpop) 249 | _message 'No more arguments' 250 | ;; 251 | (dead|semitrust|trust|untrust) 252 | __annex_repository 253 | ;; 254 | (fix|reinject|unannex) 255 | _path_files 256 | ;; 257 | (unused) 258 | _arguments $common_opts 259 | ;; 260 | 261 | (add) 262 | _arguments $common_opts $matching_opts \ 263 | '--include-dotfiles[include dotfiles that are not explicitly listed]' \ 264 | '*:path:_path_files' 265 | ;; 266 | (addunused|dropunused) 267 | _alternative $common_opts \ 268 | 'range:Numbers:_guard "[0-9]#" "range of unused objects"' \ 269 | 'all:all objects:(all)' 270 | ;; 271 | (addurl) 272 | _arguments $common_opts \ 273 | '--relaxed[don''t store the size of the url''s content]' \ 274 | '--pathdepth=[path depth to use for the file name]: :_guard "-#[0-9]#" "integer"' \ 275 | '--file=[destination file]:path:_path_files' \ 276 | '*:url:_urls' 277 | ;; 278 | (assistant) 279 | _arguments '--foreground[do not daemonize]' \ 280 | '--stop[stop the daemon]' \ 281 | '--autostart[start in known repositories]' \ 282 | '--startdelay=[delay before running startup scan]:delay' 283 | ;; 284 | (describe) 285 | _arguments ':repository:__annex_repository' \ 286 | ':description:' 287 | ;; 288 | (enableremote|initremote) 289 | _arguments ':repository:__annex_repository' \ 290 | '*:param=value parameters:' 291 | ;; 292 | (find) 293 | _arguments $common_opts $matching_opts \ 294 | '--print0[output filenames terminated with nulls]' \ 295 | '*:path:_path_files' 296 | ;; 297 | (forget) 298 | _arguments '--drop-dead[prune references to dead repositories]' 299 | ;; 300 | (fsck) 301 | _arguments $common_opts $matching_opts\ 302 | '(-S --incremental)'{-S,--incremental}'[start an incremental fsck]' \ 303 | '(-m --more)'{-m,--more}'[continue an incremental fsck]' \ 304 | '--incremental-schedule=[schedule incremental fscking]:schedule expression' \ 305 | '*:path:_path_files' 306 | ;; 307 | (group|ungroup) 308 | _arguments ':repository:__annex_repository' \ 309 | ':group name:__annex_groups' 310 | ;; 311 | (help) 312 | _arguments ':list common options:(options)' 313 | ;; 314 | (import) 315 | _arguments $common_opts \ 316 | '--duplicate[do not delete source files]' \ 317 | '--deduplicate[delete source files whose content was imported before]' \ 318 | '--clean-duplicates[delete duplicate source files (import nothing)]' \ 319 | '--skip-duplicates[import only new files]' \ 320 | '*:path:_path_files' 321 | ;; 322 | (importfeed) 323 | _arguments $common_opts \ 324 | '--template=[template for filenames]:template' \ 325 | '--relaxed[skip size check]' \ 326 | '*:feed url:_urls' 327 | ;; 328 | (info) 329 | _arguments $common_opts $matching_opts \ 330 | '*:directory:_path_files -/' 331 | ;; 332 | (init|reinit) 333 | _arguments '::description:' 334 | ;; 335 | (list) 336 | _arguments $common_opts $matching_opts \ 337 | '--allrepos[list all repositories]' \ 338 | '*:path:_path_files' 339 | ;; 340 | (log) 341 | _arguments $common_opts $matching_opts \ 342 | '--since=[show log since date]:since date' \ 343 | '--after=[show log after date]:after date' \ 344 | '--until=[show log until date]:until date' \ 345 | '--before=[show log before date]:before date' \ 346 | '(-n --max-count=)'{-m,--max-count}'[limit number of logs displayed]: :_guard "[0-9]#" "number of logs"' \ 347 | '--gource[generate output suitable for gource]' \ 348 | '*:path:_path_files' 349 | ;; 350 | (metadata) 351 | # No $common_opts: -t is for --tag, not --to. 352 | _arguments $matching_opts \ 353 | '--all[operate on all versions of all files]' \ 354 | '(-k --key=)'{-k,--key=}'[operate on specified key]' \ 355 | '(-j --json)'{-j,--json}'[enable JSON output]' \ 356 | '(-U --unused)'{-U,--unused}'[operate on files found y last run of git-annex unused]' \ 357 | '(-s --set=)'{-s,--set}'[change a field value]:field=value' \ 358 | '(-g --get=)'{-g,--get}'[get a field value]:field' \ 359 | '(-t --tag=)'{-t,--tag}'[set a tag]:tag' \ 360 | '(-u --untag=)'{-u,--untag}'[remove a tag]:tag' \ 361 | '*:path:_path_files' 362 | ;; 363 | (numcopies) 364 | _arguments ': :_guard "[0-9]#" "number of copies"' 365 | ;; 366 | (rmurl) 367 | _arguments ':file:_files' \ 368 | ':url:_urls' 369 | ;; 370 | (schedule|wanted) 371 | _arguments ':repository:__annex_repository' \ 372 | ':epxression:' 373 | ;; 374 | (sync) 375 | _arguments $common_opts \ 376 | '--content[transfer annexed files contents]' \ 377 | '*:remote:__annex_remotes' 378 | ;; 379 | (vadd|vfilter|view) 380 | _message 'field=value, field=glob, field/=path, field!=value, /=path, tag, !tag' 381 | ;; 382 | (vpop) 383 | _arguments ': :_guard "[0-9]#" "number views to pop"' 384 | ;; 385 | (watch) 386 | _arguments '--foreground[do not daemonize]' \ 387 | '--stop[stop the daemon]' 388 | ;; 389 | (webapp) 390 | _arguments '--listen=[accept connections to this address]:address' 391 | ;; 392 | (*) 393 | _arguments $common_opts $matching_opts \ 394 | '*:path:_path_files' 395 | ;; 396 | esac 397 | ;; 398 | esac 399 | -------------------------------------------------------------------------------- /git-annex.plugin.zsh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schnouki/git-annex-zsh-completion/e87e7db7f01505e82fc0af36e9427b04f906223c/git-annex.plugin.zsh --------------------------------------------------------------------------------