Bash completion support for aliases
[git] / contrib / completion / git-completion.bash
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
22 __git_refs ()
23 {
24         local cmd i is_hash=y
25         if [ -d "$1" ]; then
26                 cmd=git-peek-remote
27         else
28                 cmd=git-ls-remote
29         fi
30         for i in $($cmd "$1" 2>/dev/null); do
31                 case "$is_hash,$i" in
32                 y,*) is_hash=n ;;
33                 n,*^{}) is_hash=y ;;
34                 n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
35                 n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
36                 n,*) is_hash=y; echo "$i" ;;
37                 esac
38         done
39 }
40
41 __git_refs2 ()
42 {
43         local cmd i is_hash=y
44         if [ -d "$1" ]; then
45                 cmd=git-peek-remote
46         else
47                 cmd=git-ls-remote
48         fi
49         for i in $($cmd "$1" 2>/dev/null); do
50                 case "$is_hash,$i" in
51                 y,*) is_hash=n ;;
52                 n,*^{}) is_hash=y ;;
53                 n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
54                 n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
55                 n,*) is_hash=y; echo "$i:$i" ;;
56                 esac
57         done
58 }
59
60 __git_remotes ()
61 {
62         local i REVERTGLOB=$(shopt -p nullglob)
63         shopt -s nullglob
64         for i in .git/remotes/*; do
65                 echo ${i#.git/remotes/}
66         done
67         $REVERTGLOB
68 }
69
70 __git_complete_file ()
71 {
72         local cur="${COMP_WORDS[COMP_CWORD]}"
73         case "$cur" in
74         ?*:*)
75                 local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')"
76                 cur="$(echo "$cur" | sed 's,^.*:,,')"
77                 case "$cur" in
78                 ?*/*)
79                         pfx="$(echo "$cur" | sed 's,/[^/]*$,,')"
80                         cur="$(echo "$cur" | sed 's,^.*/,,')"
81                         ls="$ref:$pfx"
82                         pfx="$pfx/"
83                         ;;
84                 *)
85                         ls="$ref"
86                         ;;
87             esac
88                 COMPREPLY=($(compgen -P "$pfx" \
89                         -W "$(git-ls-tree "$ls" \
90                                 | sed '/^100... blob /s,^.*     ,,
91                                        /^040000 tree /{
92                                            s,^.*        ,,
93                                            s,$,/,
94                                        }
95                                        s/^.*    //')" \
96                         -- "$cur"))
97                 ;;
98         *)
99                 COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
100                 ;;
101         esac
102 }
103
104 __git_aliases ()
105 {
106         git repo-config --list | grep '^alias\.' \
107                 | sed -e 's/^alias\.//' -e 's/=.*$//'
108 }
109
110 __git_aliased_command ()
111 {
112         local cmdline=$(git repo-config alias.$1)
113         for word in $cmdline; do
114                 if [ "${word##-*}" ]; then
115                         echo $word
116                         return
117                 fi
118         done
119 }
120
121 _git_branch ()
122 {
123         local cur="${COMP_WORDS[COMP_CWORD]}"
124         COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs .)" -- "$cur"))
125 }
126
127 _git_cat_file ()
128 {
129         local cur="${COMP_WORDS[COMP_CWORD]}"
130         case "${COMP_WORDS[0]},$COMP_CWORD" in
131         git-cat-file*,1)
132                 COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
133                 ;;
134         git,2)
135                 COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur"))
136                 ;;
137         *)
138                 __git_complete_file
139                 ;;
140         esac
141 }
142
143 _git_checkout ()
144 {
145         local cur="${COMP_WORDS[COMP_CWORD]}"
146         COMPREPLY=($(compgen -W "-l -b $(__git_refs .)" -- "$cur"))
147 }
148
149 _git_diff ()
150 {
151         __git_complete_file
152 }
153
154 _git_diff_tree ()
155 {
156         local cur="${COMP_WORDS[COMP_CWORD]}"
157         COMPREPLY=($(compgen -W "-r -p -M $(__git_refs .)" -- "$cur"))
158 }
159
160 _git_fetch ()
161 {
162         local cur="${COMP_WORDS[COMP_CWORD]}"
163
164         case "${COMP_WORDS[0]},$COMP_CWORD" in
165         git-fetch*,1)
166                 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
167                 ;;
168         git,2)
169                 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
170                 ;;
171         *)
172                 case "$cur" in
173                 *:*)
174                 cur=$(echo "$cur" | sed 's/^.*://')
175                         COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
176                         ;;
177                 *)
178                         local remote
179                         case "${COMP_WORDS[0]}" in
180                         git-fetch) remote="${COMP_WORDS[1]}" ;;
181                         git)       remote="${COMP_WORDS[2]}" ;;
182                         esac
183                         COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur"))
184                         ;;
185                 esac
186                 ;;
187         esac
188 }
189
190 _git_ls_remote ()
191 {
192         local cur="${COMP_WORDS[COMP_CWORD]}"
193         COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
194 }
195
196 _git_ls_tree ()
197 {
198         __git_complete_file
199 }
200
201 _git_log ()
202 {
203         local cur="${COMP_WORDS[COMP_CWORD]}"
204         case "$cur" in
205         *..*)
206                 local pfx=$(echo "$cur" | sed 's/\.\..*$/../')
207                 cur=$(echo "$cur" | sed 's/^.*\.\.//')
208                 COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs .)" -- "$cur"))
209                 ;;
210         *)
211                 COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
212                 ;;
213         esac
214 }
215
216 _git_merge_base ()
217 {
218         local cur="${COMP_WORDS[COMP_CWORD]}"
219         COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
220 }
221
222 _git_pull ()
223 {
224         local cur="${COMP_WORDS[COMP_CWORD]}"
225
226         case "${COMP_WORDS[0]},$COMP_CWORD" in
227         git-pull*,1)
228                 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
229                 ;;
230         git,2)
231                 COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
232                 ;;
233         *)
234                 local remote
235                 case "${COMP_WORDS[0]}" in
236                 git-pull)  remote="${COMP_WORDS[1]}" ;;
237                 git)       remote="${COMP_WORDS[2]}" ;;
238                 esac
239                 COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
240                 ;;
241         esac
242 }
243
244 _git_push ()
245 {
246         local cur="${COMP_WORDS[COMP_CWORD]}"
247
248         case "${COMP_WORDS[0]},$COMP_CWORD" in
249         git-push*,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                 *:*)
258                         local remote
259                         case "${COMP_WORDS[0]}" in
260                         git-push)  remote="${COMP_WORDS[1]}" ;;
261                         git)       remote="${COMP_WORDS[2]}" ;;
262                         esac
263                 cur=$(echo "$cur" | sed 's/^.*://')
264                         COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur"))
265                         ;;
266                 *)
267                         COMPREPLY=($(compgen -W "$(__git_refs2 .)" -- "$cur"))
268                         ;;
269                 esac
270                 ;;
271         esac
272 }
273
274 _git_show ()
275 {
276         local cur="${COMP_WORDS[COMP_CWORD]}"
277         COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur"))
278 }
279
280 _git ()
281 {
282         if [ $COMP_CWORD = 1 ]; then
283                 COMPREPLY=($(compgen \
284                         -W "--version $(git help -a|egrep '^ ') \
285                             $(__git_aliases)" \
286                         -- "${COMP_WORDS[COMP_CWORD]}"))
287         else
288                 local command="${COMP_WORDS[1]}"
289                 local expansion=$(__git_aliased_command "$command")
290
291                 if [ "$expansion" ]; then
292                         command="$expansion"
293                 fi
294
295                 case "$command" in
296                 branch)      _git_branch ;;
297                 cat-file)    _git_cat_file ;;
298                 checkout)    _git_checkout ;;
299                 diff)        _git_diff ;;
300                 diff-tree)   _git_diff_tree ;;
301                 fetch)       _git_fetch ;;
302                 log)         _git_log ;;
303                 ls-remote)   _git_ls_remote ;;
304                 ls-tree)     _git_ls_tree ;;
305                 pull)        _git_pull ;;
306                 push)        _git_push ;;
307                 show)        _git_show ;;
308                 show-branch) _git_log ;;
309                 whatchanged) _git_log ;;
310                 *)           COMPREPLY=() ;;
311                 esac
312         fi
313 }
314
315 _gitk ()
316 {
317         local cur="${COMP_WORDS[COMP_CWORD]}"
318         COMPREPLY=($(compgen -W "--all $(__git_refs .)" -- "$cur"))
319 }
320
321 complete -o default -o nospace -F _git git
322 complete -o default            -F _gitk gitk
323 complete -o default            -F _git_branch git-branch
324 complete -o default -o nospace -F _git_cat_file git-cat-file
325 complete -o default            -F _git_checkout git-checkout
326 complete -o default -o nospace -F _git_diff git-diff
327 complete -o default            -F _git_diff_tree git-diff-tree
328 complete -o default -o nospace -F _git_fetch git-fetch
329 complete -o default -o nospace -F _git_log git-log
330 complete -o default            -F _git_ls_remote git-ls-remote
331 complete -o default -o nospace -F _git_ls_tree git-ls-tree
332 complete -o default            -F _git_merge_base git-merge-base
333 complete -o default -o nospace -F _git_pull git-pull
334 complete -o default -o nospace -F _git_push git-push
335 complete -o default            -F _git_show git-show
336 complete -o default -o nospace -F _git_log git-whatchanged
337
338 # The following are necessary only for Cygwin, and only are needed
339 # when the user has tab-completed the executable name and consequently
340 # included the '.exe' suffix.
341 #
342 complete -o default -o nospace -F _git_cat_file git-cat-file.exe
343 complete -o default -o nospace -F _git_diff git-diff.exe
344 complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
345 complete -o default -o nospace -F _git_log git-log.exe
346 complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
347 complete -o default            -F _git_merge_base git-merge-base.exe
348 complete -o default -o nospace -F _git_push git-push.exe
349 complete -o default -o nospace -F _git_log git-whatchanged.exe