Commit | Line | Data |
---|---|---|
690d8824 JH |
1 | # |
2 | # bash completion support for core Git. | |
3 | # | |
4 | # Copyright (C) 2006 Shawn Pearce | |
5 | # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). | |
6 | # | |
7 | # The contained completion routines provide support for completing: | |
8 | # | |
9 | # *) local and remote branch names | |
10 | # *) local and remote tag names | |
11 | # *) .git/remotes file names | |
12 | # *) git 'subcommands' | |
13 | # *) tree paths within 'ref:path/to/file' expressions | |
14 | # | |
15 | # To use these routines: | |
16 | # | |
17 | # 1) Copy this file to somewhere (e.g. ~/.git-completion.sh). | |
18 | # 2) Added the following line to your .bashrc: | |
19 | # source ~/.git-completion.sh | |
20 | # | |
21 | ||
873537fa SP |
22 | __gitdir () |
23 | { | |
24 | echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}" | |
25 | } | |
26 | ||
690d8824 JH |
27 | __git_refs () |
28 | { | |
873537fa SP |
29 | local cmd i is_hash=y dir="${1:-$(__gitdir)}" |
30 | if [ -d "$dir" ]; then | |
690d8824 JH |
31 | cmd=git-peek-remote |
32 | else | |
33 | cmd=git-ls-remote | |
34 | fi | |
873537fa | 35 | for i in $($cmd "$dir" 2>/dev/null); do |
690d8824 JH |
36 | case "$is_hash,$i" in |
37 | y,*) is_hash=n ;; | |
38 | n,*^{}) is_hash=y ;; | |
39 | n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; | |
40 | n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; | |
41 | n,*) is_hash=y; echo "$i" ;; | |
42 | esac | |
43 | done | |
44 | } | |
45 | ||
46 | __git_refs2 () | |
47 | { | |
873537fa SP |
48 | local cmd i is_hash=y dir="${1:-$(__gitdir)}" |
49 | if [ -d "$dir" ]; then | |
690d8824 JH |
50 | cmd=git-peek-remote |
51 | else | |
52 | cmd=git-ls-remote | |
53 | fi | |
873537fa | 54 | for i in $($cmd "$dir" 2>/dev/null); do |
690d8824 JH |
55 | case "$is_hash,$i" in |
56 | y,*) is_hash=n ;; | |
57 | n,*^{}) is_hash=y ;; | |
58 | n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;; | |
59 | n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;; | |
60 | n,*) is_hash=y; echo "$i:$i" ;; | |
61 | esac | |
62 | done | |
63 | } | |
64 | ||
65 | __git_remotes () | |
66 | { | |
873537fa | 67 | local i ngoff IFS=$'\n' d="$(__gitdir)" |
56fc25f2 | 68 | shopt -q nullglob || ngoff=1 |
690d8824 | 69 | shopt -s nullglob |
873537fa SP |
70 | for i in "$d/remotes"/*; do |
71 | echo ${i#$d/remotes/} | |
690d8824 | 72 | done |
56fc25f2 | 73 | [ "$ngoff" ] && shopt -u nullglob |
873537fa | 74 | for i in $(git --git-dir="$d" repo-config --list); do |
56fc25f2 SP |
75 | case "$i" in |
76 | remote.*.url=*) | |
77 | i="${i#remote.}" | |
78 | echo "${i/.url=*/}" | |
79 | ;; | |
80 | esac | |
81 | done | |
690d8824 JH |
82 | } |
83 | ||
4ad91321 SP |
84 | __git_merge_strategies () |
85 | { | |
86 | sed -n "/^all_strategies='/{ | |
87 | s/^all_strategies='// | |
88 | s/'// | |
89 | p | |
90 | q | |
91 | }" "$(git --exec-path)/git-merge" | |
92 | } | |
93 | ||
690d8824 JH |
94 | __git_complete_file () |
95 | { | |
a79c6551 | 96 | local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}" |
690d8824 JH |
97 | case "$cur" in |
98 | ?*:*) | |
a79c6551 SP |
99 | ref="${cur%%:*}" |
100 | cur="${cur#*:}" | |
690d8824 JH |
101 | case "$cur" in |
102 | ?*/*) | |
a79c6551 SP |
103 | pfx="${cur%/*}" |
104 | cur="${cur##*/}" | |
690d8824 JH |
105 | ls="$ref:$pfx" |
106 | pfx="$pfx/" | |
107 | ;; | |
108 | *) | |
109 | ls="$ref" | |
110 | ;; | |
111 | esac | |
112 | COMPREPLY=($(compgen -P "$pfx" \ | |
873537fa | 113 | -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ |
690d8824 JH |
114 | | sed '/^100... blob /s,^.* ,, |
115 | /^040000 tree /{ | |
116 | s,^.* ,, | |
117 | s,$,/, | |
118 | } | |
119 | s/^.* //')" \ | |
120 | -- "$cur")) | |
121 | ;; | |
122 | *) | |
873537fa | 123 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
124 | ;; |
125 | esac | |
126 | } | |
127 | ||
f2bb9f88 SP |
128 | __git_commands () |
129 | { | |
130 | local i IFS=" "$'\n' | |
131 | for i in $(git help -a|egrep '^ ') | |
132 | do | |
133 | case $i in | |
134 | check-ref-format) : plumbing;; | |
135 | commit-tree) : plumbing;; | |
136 | convert-objects) : plumbing;; | |
137 | cvsserver) : daemon;; | |
138 | daemon) : daemon;; | |
139 | fetch-pack) : plumbing;; | |
140 | hash-object) : plumbing;; | |
141 | http-*) : transport;; | |
142 | index-pack) : plumbing;; | |
143 | local-fetch) : plumbing;; | |
144 | mailinfo) : plumbing;; | |
145 | mailsplit) : plumbing;; | |
146 | merge-*) : plumbing;; | |
147 | mktree) : plumbing;; | |
148 | mktag) : plumbing;; | |
149 | pack-objects) : plumbing;; | |
150 | pack-redundant) : plumbing;; | |
151 | pack-refs) : plumbing;; | |
152 | parse-remote) : plumbing;; | |
153 | patch-id) : plumbing;; | |
154 | peek-remote) : plumbing;; | |
155 | read-tree) : plumbing;; | |
156 | receive-pack) : plumbing;; | |
157 | rerere) : plumbing;; | |
158 | rev-list) : plumbing;; | |
159 | rev-parse) : plumbing;; | |
160 | runstatus) : plumbing;; | |
161 | sh-setup) : internal;; | |
162 | shell) : daemon;; | |
163 | send-pack) : plumbing;; | |
164 | show-index) : plumbing;; | |
165 | ssh-*) : transport;; | |
166 | stripspace) : plumbing;; | |
167 | symbolic-ref) : plumbing;; | |
168 | unpack-file) : plumbing;; | |
169 | unpack-objects) : plumbing;; | |
170 | update-ref) : plumbing;; | |
171 | update-server-info) : daemon;; | |
172 | upload-archive) : plumbing;; | |
173 | upload-pack) : plumbing;; | |
174 | write-tree) : plumbing;; | |
175 | *) echo $i;; | |
176 | esac | |
177 | done | |
178 | } | |
179 | ||
367dce2a DS |
180 | __git_aliases () |
181 | { | |
56fc25f2 | 182 | local i IFS=$'\n' |
873537fa | 183 | for i in $(git --git-dir="$(__gitdir)" repo-config --list); do |
56fc25f2 SP |
184 | case "$i" in |
185 | alias.*) | |
186 | i="${i#alias.}" | |
187 | echo "${i/=*/}" | |
188 | ;; | |
189 | esac | |
190 | done | |
367dce2a DS |
191 | } |
192 | ||
193 | __git_aliased_command () | |
194 | { | |
873537fa SP |
195 | local word cmdline=$(git --git-dir="$(__gitdir)" \ |
196 | repo-config --get "alias.$1") | |
367dce2a DS |
197 | for word in $cmdline; do |
198 | if [ "${word##-*}" ]; then | |
199 | echo $word | |
200 | return | |
201 | fi | |
202 | done | |
203 | } | |
204 | ||
690d8824 JH |
205 | _git_branch () |
206 | { | |
207 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 208 | COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur")) |
690d8824 JH |
209 | } |
210 | ||
211 | _git_cat_file () | |
212 | { | |
213 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
214 | case "${COMP_WORDS[0]},$COMP_CWORD" in | |
215 | git-cat-file*,1) | |
216 | COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) | |
217 | ;; | |
218 | git,2) | |
219 | COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) | |
220 | ;; | |
221 | *) | |
222 | __git_complete_file | |
223 | ;; | |
224 | esac | |
225 | } | |
226 | ||
227 | _git_checkout () | |
228 | { | |
229 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 230 | COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur")) |
690d8824 JH |
231 | } |
232 | ||
233 | _git_diff () | |
234 | { | |
235 | __git_complete_file | |
236 | } | |
237 | ||
238 | _git_diff_tree () | |
239 | { | |
240 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 241 | COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur")) |
690d8824 JH |
242 | } |
243 | ||
244 | _git_fetch () | |
245 | { | |
246 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
247 | ||
248 | case "${COMP_WORDS[0]},$COMP_CWORD" in | |
249 | git-fetch*,1) | |
250 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
251 | ;; | |
252 | git,2) | |
253 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
254 | ;; | |
255 | *) | |
256 | case "$cur" in | |
257 | *:*) | |
a79c6551 | 258 | cur="${cur#*:}" |
873537fa | 259 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
260 | ;; |
261 | *) | |
262 | local remote | |
263 | case "${COMP_WORDS[0]}" in | |
264 | git-fetch) remote="${COMP_WORDS[1]}" ;; | |
265 | git) remote="${COMP_WORDS[2]}" ;; | |
266 | esac | |
267 | COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur")) | |
268 | ;; | |
269 | esac | |
270 | ;; | |
271 | esac | |
272 | } | |
273 | ||
274 | _git_ls_remote () | |
275 | { | |
276 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
277 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
278 | } | |
279 | ||
280 | _git_ls_tree () | |
281 | { | |
282 | __git_complete_file | |
283 | } | |
284 | ||
285 | _git_log () | |
286 | { | |
e5d5b21f | 287 | local pfx cur="${COMP_WORDS[COMP_CWORD]}" |
690d8824 | 288 | case "$cur" in |
e5d5b21f SP |
289 | *...*) |
290 | pfx="${cur%...*}..." | |
291 | cur="${cur#*...}" | |
292 | COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) | |
293 | ;; | |
690d8824 | 294 | *..*) |
e5d5b21f SP |
295 | pfx="${cur%..*}.." |
296 | cur="${cur#*..}" | |
873537fa | 297 | COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
298 | ;; |
299 | *) | |
873537fa | 300 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
301 | ;; |
302 | esac | |
303 | } | |
304 | ||
4ad91321 SP |
305 | _git_merge () |
306 | { | |
307 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
308 | case "$cur" in | |
309 | --*) | |
310 | COMPREPLY=($(compgen -W " | |
311 | --no-commit --no-summary --squash | |
312 | " -- "$cur")) | |
313 | return | |
314 | esac | |
315 | if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ] | |
316 | then | |
317 | COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) | |
318 | else | |
319 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) | |
320 | fi | |
321 | } | |
322 | ||
690d8824 JH |
323 | _git_merge_base () |
324 | { | |
325 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 326 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
327 | } |
328 | ||
d33909bf SP |
329 | _git_name_rev () |
330 | { | |
331 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
332 | COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur")) | |
333 | } | |
334 | ||
690d8824 JH |
335 | _git_pull () |
336 | { | |
337 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
338 | ||
339 | case "${COMP_WORDS[0]},$COMP_CWORD" in | |
340 | git-pull*,1) | |
341 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
342 | ;; | |
343 | git,2) | |
344 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
345 | ;; | |
346 | *) | |
347 | local remote | |
348 | case "${COMP_WORDS[0]}" in | |
349 | git-pull) remote="${COMP_WORDS[1]}" ;; | |
350 | git) remote="${COMP_WORDS[2]}" ;; | |
351 | esac | |
352 | COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) | |
353 | ;; | |
354 | esac | |
355 | } | |
356 | ||
357 | _git_push () | |
358 | { | |
359 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
360 | ||
361 | case "${COMP_WORDS[0]},$COMP_CWORD" in | |
362 | git-push*,1) | |
363 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
364 | ;; | |
365 | git,2) | |
366 | COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) | |
367 | ;; | |
368 | *) | |
369 | case "$cur" in | |
370 | *:*) | |
371 | local remote | |
372 | case "${COMP_WORDS[0]}" in | |
373 | git-push) remote="${COMP_WORDS[1]}" ;; | |
374 | git) remote="${COMP_WORDS[2]}" ;; | |
375 | esac | |
a79c6551 | 376 | cur="${cur#*:}" |
690d8824 JH |
377 | COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) |
378 | ;; | |
379 | *) | |
873537fa | 380 | COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur")) |
690d8824 JH |
381 | ;; |
382 | esac | |
383 | ;; | |
384 | esac | |
385 | } | |
386 | ||
67e78c3b SP |
387 | _git_reset () |
388 | { | |
389 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
390 | local opt="--mixed --hard --soft" | |
873537fa | 391 | COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur")) |
67e78c3b SP |
392 | } |
393 | ||
690d8824 JH |
394 | _git_show () |
395 | { | |
396 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 397 | COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) |
690d8824 JH |
398 | } |
399 | ||
400 | _git () | |
401 | { | |
873537fa SP |
402 | local i c=1 command __git_dir |
403 | ||
404 | while [ $c -lt $COMP_CWORD ]; do | |
405 | i="${COMP_WORDS[c]}" | |
406 | case "$i" in | |
407 | --git-dir=*) __git_dir="${i#--git-dir=}" ;; | |
408 | --bare) __git_dir="." ;; | |
409 | --version|--help|-p|--paginate) ;; | |
410 | *) command="$i"; break ;; | |
411 | esac | |
412 | c=$((++c)) | |
413 | done | |
414 | ||
415 | if [ $c -eq $COMP_CWORD -a -z "$command" ]; then | |
f2bb9f88 SP |
416 | COMPREPLY=($(compgen -W " |
417 | --git-dir= --version --exec-path | |
418 | $(__git_commands) | |
419 | $(__git_aliases) | |
420 | " -- "${COMP_WORDS[COMP_CWORD]}")) | |
873537fa SP |
421 | return; |
422 | fi | |
367dce2a | 423 | |
873537fa SP |
424 | local expansion=$(__git_aliased_command "$command") |
425 | [ "$expansion" ] && command="$expansion" | |
367dce2a | 426 | |
873537fa SP |
427 | case "$command" in |
428 | branch) _git_branch ;; | |
429 | cat-file) _git_cat_file ;; | |
430 | checkout) _git_checkout ;; | |
431 | diff) _git_diff ;; | |
432 | diff-tree) _git_diff_tree ;; | |
433 | fetch) _git_fetch ;; | |
434 | log) _git_log ;; | |
435 | ls-remote) _git_ls_remote ;; | |
436 | ls-tree) _git_ls_tree ;; | |
4ad91321 | 437 | merge) _git_merge;; |
873537fa | 438 | merge-base) _git_merge_base ;; |
d33909bf | 439 | name-rev) _git_name_rev ;; |
873537fa SP |
440 | pull) _git_pull ;; |
441 | push) _git_push ;; | |
442 | reset) _git_reset ;; | |
443 | show) _git_show ;; | |
444 | show-branch) _git_log ;; | |
445 | whatchanged) _git_log ;; | |
446 | *) COMPREPLY=() ;; | |
447 | esac | |
690d8824 JH |
448 | } |
449 | ||
450 | _gitk () | |
451 | { | |
452 | local cur="${COMP_WORDS[COMP_CWORD]}" | |
873537fa | 453 | COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur")) |
690d8824 JH |
454 | } |
455 | ||
456 | complete -o default -o nospace -F _git git | |
457 | complete -o default -F _gitk gitk | |
458 | complete -o default -F _git_branch git-branch | |
459 | complete -o default -o nospace -F _git_cat_file git-cat-file | |
460 | complete -o default -F _git_checkout git-checkout | |
461 | complete -o default -o nospace -F _git_diff git-diff | |
462 | complete -o default -F _git_diff_tree git-diff-tree | |
463 | complete -o default -o nospace -F _git_fetch git-fetch | |
464 | complete -o default -o nospace -F _git_log git-log | |
465 | complete -o default -F _git_ls_remote git-ls-remote | |
466 | complete -o default -o nospace -F _git_ls_tree git-ls-tree | |
4ad91321 | 467 | complete -o default -F _git_merge git-merge |
690d8824 | 468 | complete -o default -F _git_merge_base git-merge-base |
d33909bf | 469 | complete -o default -F _git_name_rev git-name-rev |
690d8824 JH |
470 | complete -o default -o nospace -F _git_pull git-pull |
471 | complete -o default -o nospace -F _git_push git-push | |
67e78c3b | 472 | complete -o default -F _git_reset git-reset |
690d8824 | 473 | complete -o default -F _git_show git-show |
144d33de | 474 | complete -o default -o nospace -F _git_log git-show-branch |
690d8824 JH |
475 | complete -o default -o nospace -F _git_log git-whatchanged |
476 | ||
477 | # The following are necessary only for Cygwin, and only are needed | |
478 | # when the user has tab-completed the executable name and consequently | |
479 | # included the '.exe' suffix. | |
480 | # | |
76c3eb51 | 481 | if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then |
144d33de | 482 | complete -o default -o nospace -F _git git.exe |
dfb96092 | 483 | complete -o default -F _git_branch git-branch.exe |
690d8824 JH |
484 | complete -o default -o nospace -F _git_cat_file git-cat-file.exe |
485 | complete -o default -o nospace -F _git_diff git-diff.exe | |
486 | complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe | |
487 | complete -o default -o nospace -F _git_log git-log.exe | |
488 | complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe | |
489 | complete -o default -F _git_merge_base git-merge-base.exe | |
d33909bf | 490 | complete -o default -F _git_name_rev git-name-rev.exe |
690d8824 | 491 | complete -o default -o nospace -F _git_push git-push.exe |
144d33de | 492 | complete -o default -o nospace -F _git_log git-show-branch.exe |
690d8824 | 493 | complete -o default -o nospace -F _git_log git-whatchanged.exe |
76c3eb51 | 494 | fi |