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 #
22 __gitdir ()
23 {
24 echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
25 }
27 __git_refs ()
28 {
29 local cmd i is_hash=y dir="${1:-$(__gitdir)}"
30 if [ -d "$dir" ]; then
31 cmd=git-peek-remote
32 else
33 cmd=git-ls-remote
34 fi
35 for i in $($cmd "$dir" 2>/dev/null); do
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 }
46 __git_refs2 ()
47 {
48 local cmd i is_hash=y dir="${1:-$(__gitdir)}"
49 if [ -d "$dir" ]; then
50 cmd=git-peek-remote
51 else
52 cmd=git-ls-remote
53 fi
54 for i in $($cmd "$dir" 2>/dev/null); do
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 }
65 __git_remotes ()
66 {
67 local i ngoff IFS=$'\n' d="$(__gitdir)"
68 shopt -q nullglob || ngoff=1
69 shopt -s nullglob
70 for i in "$d/remotes"/*; do
71 echo ${i#$d/remotes/}
72 done
73 [ "$ngoff" ] && shopt -u nullglob
74 for i in $(git --git-dir="$d" repo-config --list); do
75 case "$i" in
76 remote.*.url=*)
77 i="${i#remote.}"
78 echo "${i/.url=*/}"
79 ;;
80 esac
81 done
82 }
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 }
94 __git_complete_file ()
95 {
96 local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
97 case "$cur" in
98 ?*:*)
99 ref="${cur%%:*}"
100 cur="${cur#*:}"
101 case "$cur" in
102 ?*/*)
103 pfx="${cur%/*}"
104 cur="${cur##*/}"
105 ls="$ref:$pfx"
106 pfx="$pfx/"
107 ;;
108 *)
109 ls="$ref"
110 ;;
111 esac
112 COMPREPLY=($(compgen -P "$pfx" \
113 -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
114 | sed '/^100... blob /s,^.* ,,
115 /^040000 tree /{
116 s,^.* ,,
117 s,$,/,
118 }
119 s/^.* //')" \
120 -- "$cur"))
121 ;;
122 *)
123 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
124 ;;
125 esac
126 }
128 __git_aliases ()
129 {
130 local i IFS=$'\n'
131 for i in $(git --git-dir="$(__gitdir)" repo-config --list); do
132 case "$i" in
133 alias.*)
134 i="${i#alias.}"
135 echo "${i/=*/}"
136 ;;
137 esac
138 done
139 }
141 __git_aliased_command ()
142 {
143 local word cmdline=$(git --git-dir="$(__gitdir)" \
144 repo-config --get "alias.$1")
145 for word in $cmdline; do
146 if [ "${word##-*}" ]; then
147 echo $word
148 return
149 fi
150 done
151 }
153 _git_branch ()
154 {
155 local cur="${COMP_WORDS[COMP_CWORD]}"
156 COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur"))
157 }
159 _git_cat_file ()
160 {
161 local cur="${COMP_WORDS[COMP_CWORD]}"
162 case "${COMP_WORDS[0]},$COMP_CWORD" in
163 git-cat-file*,1)
164 COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
165 ;;
166 git,2)
167 COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
168 ;;
169 *)
170 __git_complete_file
171 ;;
172 esac
173 }
175 _git_checkout ()
176 {
177 local cur="${COMP_WORDS[COMP_CWORD]}"
178 COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
179 }
181 _git_diff ()
182 {
183 __git_complete_file
184 }
186 _git_diff_tree ()
187 {
188 local cur="${COMP_WORDS[COMP_CWORD]}"
189 COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur"))
190 }
192 _git_fetch ()
193 {
194 local cur="${COMP_WORDS[COMP_CWORD]}"
196 case "${COMP_WORDS[0]},$COMP_CWORD" in
197 git-fetch*,1)
198 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
199 ;;
200 git,2)
201 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
202 ;;
203 *)
204 case "$cur" in
205 *:*)
206 cur="${cur#*:}"
207 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
208 ;;
209 *)
210 local remote
211 case "${COMP_WORDS[0]}" in
212 git-fetch) remote="${COMP_WORDS[1]}" ;;
213 git) remote="${COMP_WORDS[2]}" ;;
214 esac
215 COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
216 ;;
217 esac
218 ;;
219 esac
220 }
222 _git_ls_remote ()
223 {
224 local cur="${COMP_WORDS[COMP_CWORD]}"
225 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
226 }
228 _git_ls_tree ()
229 {
230 __git_complete_file
231 }
233 _git_log ()
234 {
235 local pfx cur="${COMP_WORDS[COMP_CWORD]}"
236 case "$cur" in
237 *...*)
238 pfx="${cur%...*}..."
239 cur="${cur#*...}"
240 COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
241 ;;
242 *..*)
243 pfx="${cur%..*}.."
244 cur="${cur#*..}"
245 COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
246 ;;
247 *)
248 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
249 ;;
250 esac
251 }
253 _git_merge ()
254 {
255 local cur="${COMP_WORDS[COMP_CWORD]}"
256 case "$cur" in
257 --*)
258 COMPREPLY=($(compgen -W "
259 --no-commit --no-summary --squash
260 " -- "$cur"))
261 return
262 esac
263 if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ]
264 then
265 COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
266 else
267 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
268 fi
269 }
271 _git_merge_base ()
272 {
273 local cur="${COMP_WORDS[COMP_CWORD]}"
274 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
275 }
277 _git_pull ()
278 {
279 local cur="${COMP_WORDS[COMP_CWORD]}"
281 case "${COMP_WORDS[0]},$COMP_CWORD" in
282 git-pull*,1)
283 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
284 ;;
285 git,2)
286 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
287 ;;
288 *)
289 local remote
290 case "${COMP_WORDS[0]}" in
291 git-pull) remote="${COMP_WORDS[1]}" ;;
292 git) remote="${COMP_WORDS[2]}" ;;
293 esac
294 COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
295 ;;
296 esac
297 }
299 _git_push ()
300 {
301 local cur="${COMP_WORDS[COMP_CWORD]}"
303 case "${COMP_WORDS[0]},$COMP_CWORD" in
304 git-push*,1)
305 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
306 ;;
307 git,2)
308 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
309 ;;
310 *)
311 case "$cur" in
312 *:*)
313 local remote
314 case "${COMP_WORDS[0]}" in
315 git-push) remote="${COMP_WORDS[1]}" ;;
316 git) remote="${COMP_WORDS[2]}" ;;
317 esac
318 cur="${cur#*:}"
319 COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
320 ;;
321 *)
322 COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur"))
323 ;;
324 esac
325 ;;
326 esac
327 }
329 _git_reset ()
330 {
331 local cur="${COMP_WORDS[COMP_CWORD]}"
332 local opt="--mixed --hard --soft"
333 COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
334 }
336 _git_show ()
337 {
338 local cur="${COMP_WORDS[COMP_CWORD]}"
339 COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
340 }
342 _git ()
343 {
344 local i c=1 command __git_dir
346 while [ $c -lt $COMP_CWORD ]; do
347 i="${COMP_WORDS[c]}"
348 case "$i" in
349 --git-dir=*) __git_dir="${i#--git-dir=}" ;;
350 --bare) __git_dir="." ;;
351 --version|--help|-p|--paginate) ;;
352 *) command="$i"; break ;;
353 esac
354 c=$((++c))
355 done
357 if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
358 COMPREPLY=($(compgen \
359 -W "--git-dir= --version \
360 $(git help -a|egrep '^ ') \
361 $(__git_aliases)" \
362 -- "${COMP_WORDS[COMP_CWORD]}"))
363 return;
364 fi
366 local expansion=$(__git_aliased_command "$command")
367 [ "$expansion" ] && command="$expansion"
369 case "$command" in
370 branch) _git_branch ;;
371 cat-file) _git_cat_file ;;
372 checkout) _git_checkout ;;
373 diff) _git_diff ;;
374 diff-tree) _git_diff_tree ;;
375 fetch) _git_fetch ;;
376 log) _git_log ;;
377 ls-remote) _git_ls_remote ;;
378 ls-tree) _git_ls_tree ;;
379 merge) _git_merge;;
380 merge-base) _git_merge_base ;;
381 pull) _git_pull ;;
382 push) _git_push ;;
383 reset) _git_reset ;;
384 show) _git_show ;;
385 show-branch) _git_log ;;
386 whatchanged) _git_log ;;
387 *) COMPREPLY=() ;;
388 esac
389 }
391 _gitk ()
392 {
393 local cur="${COMP_WORDS[COMP_CWORD]}"
394 COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur"))
395 }
397 complete -o default -o nospace -F _git git
398 complete -o default -F _gitk gitk
399 complete -o default -F _git_branch git-branch
400 complete -o default -o nospace -F _git_cat_file git-cat-file
401 complete -o default -F _git_checkout git-checkout
402 complete -o default -o nospace -F _git_diff git-diff
403 complete -o default -F _git_diff_tree git-diff-tree
404 complete -o default -o nospace -F _git_fetch git-fetch
405 complete -o default -o nospace -F _git_log git-log
406 complete -o default -F _git_ls_remote git-ls-remote
407 complete -o default -o nospace -F _git_ls_tree git-ls-tree
408 complete -o default -F _git_merge git-merge
409 complete -o default -F _git_merge_base git-merge-base
410 complete -o default -o nospace -F _git_pull git-pull
411 complete -o default -o nospace -F _git_push git-push
412 complete -o default -F _git_reset git-reset
413 complete -o default -F _git_show git-show
414 complete -o default -o nospace -F _git_log git-show-branch
415 complete -o default -o nospace -F _git_log git-whatchanged
417 # The following are necessary only for Cygwin, and only are needed
418 # when the user has tab-completed the executable name and consequently
419 # included the '.exe' suffix.
420 #
421 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
422 complete -o default -o nospace -F _git git.exe
423 complete -o default -F _git_branch git-branch.exe
424 complete -o default -o nospace -F _git_cat_file git-cat-file.exe
425 complete -o default -o nospace -F _git_diff git-diff.exe
426 complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
427 complete -o default -o nospace -F _git_log git-log.exe
428 complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
429 complete -o default -F _git_merge_base git-merge-base.exe
430 complete -o default -o nospace -F _git_push git-push.exe
431 complete -o default -o nospace -F _git_log git-show-branch.exe
432 complete -o default -o nospace -F _git_log git-whatchanged.exe
433 fi