@@ -301,6 +301,19 @@ __gitcomp_direct ()
301
301
COMPREPLY=($1 )
302
302
}
303
303
304
+ # Similar to __gitcomp_direct, but appends to COMPREPLY instead.
305
+ # Callers must take care of providing only words that match the current word
306
+ # to be completed and adding any prefix and/or suffix (trailing space!), if
307
+ # necessary.
308
+ # 1: List of newline-separated matching completion words, complete with
309
+ # prefix and suffix.
310
+ __gitcomp_direct_append ()
311
+ {
312
+ local IFS=$' \n '
313
+
314
+ COMPREPLY+=($1 )
315
+ }
316
+
304
317
__gitcompappend ()
305
318
{
306
319
local x i=${# COMPREPLY[@]}
@@ -611,6 +624,19 @@ __git_heads ()
611
624
" refs/heads/$cur_ *" " refs/heads/$cur_ */**"
612
625
}
613
626
627
+ # Lists branches from remote repositories.
628
+ # 1: A prefix to be added to each listed branch (optional).
629
+ # 2: List only branches matching this word (optional; list all branches if
630
+ # unset or empty).
631
+ # 3: A suffix to be appended to each listed branch (optional).
632
+ __git_remote_heads ()
633
+ {
634
+ local pfx=" ${1-} " cur_=" ${2-} " sfx=" ${3-} "
635
+
636
+ __git for-each-ref --format=" ${pfx// \% /%% } %(refname:strip=2)$sfx " \
637
+ " refs/remotes/$cur_ *" " refs/remotes/$cur_ */**"
638
+ }
639
+
614
640
# Lists tags from the local repository.
615
641
# Accepts the same positional parameters as __git_heads() above.
616
642
__git_tags ()
@@ -621,6 +647,26 @@ __git_tags ()
621
647
" refs/tags/$cur_ *" " refs/tags/$cur_ */**"
622
648
}
623
649
650
+ # List unique branches from refs/remotes used for 'git checkout' and 'git
651
+ # switch' tracking DWIMery.
652
+ # 1: A prefix to be added to each listed branch (optional)
653
+ # 2: List only branches matching this word (optional; list all branches if
654
+ # unset or empty).
655
+ # 3: A suffix to be appended to each listed branch (optional).
656
+ __git_dwim_remote_heads ()
657
+ {
658
+ local pfx=" ${1-} " cur_=" ${2-} " sfx=" ${3-} "
659
+ local fer_pfx=" ${pfx// \% /%% } " # "escape" for-each-ref format specifiers
660
+
661
+ # employ the heuristic used by git checkout and git switch
662
+ # Try to find a remote branch that cur_es the completion word
663
+ # but only output if the branch name is unique
664
+ __git for-each-ref --format=" $fer_pfx %(refname:strip=3)$sfx " \
665
+ --sort=" refname:strip=3" \
666
+ " refs/remotes/*/$cur_ *" " refs/remotes/*/$cur_ */**" | \
667
+ uniq -u
668
+ }
669
+
624
670
# Lists refs from the local (by default) or from a remote repository.
625
671
# It accepts 0, 1 or 2 arguments:
626
672
# 1: The remote to list refs from (optional; ignored, if set but empty).
@@ -696,13 +742,7 @@ __git_refs ()
696
742
__git_dir=" $dir " __git for-each-ref --format=" $fer_pfx %($format )$sfx " \
697
743
" ${refs[@]} "
698
744
if [ -n " $track " ]; then
699
- # employ the heuristic used by git checkout
700
- # Try to find a remote branch that matches the completion word
701
- # but only output if the branch name is unique
702
- __git for-each-ref --format=" $fer_pfx %(refname:strip=3)$sfx " \
703
- --sort=" refname:strip=3" \
704
- " refs/remotes/*/$match *" " refs/remotes/*/$match */**" | \
705
- uniq -u
745
+ __git_dwim_remote_heads " $pfx " " $match " " $sfx "
706
746
fi
707
747
return
708
748
fi
@@ -749,29 +789,51 @@ __git_refs ()
749
789
# Usage: __git_complete_refs [<option>]...
750
790
# --remote=<remote>: The remote to list refs from, can be the name of a
751
791
# configured remote, a path, or a URL.
752
- # --track : List unique remote branches for 'git checkout 's tracking DWIMery.
792
+ # --dwim : List unique remote branches for 'git switch 's tracking DWIMery.
753
793
# --pfx=<prefix>: A prefix to be added to each ref.
754
794
# --cur=<word>: The current ref to be completed. Defaults to the current
755
795
# word to be completed.
756
796
# --sfx=<suffix>: A suffix to be appended to each ref instead of the default
757
797
# space.
798
+ # --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to
799
+ # complete all refs, 'heads' to complete only branches, or
800
+ # 'remote-heads' to complete only remote branches. Note that
801
+ # --remote is only compatible with --mode=refs.
758
802
__git_complete_refs ()
759
803
{
760
- local remote track pfx cur_=" $cur " sfx=" "
804
+ local remote dwim pfx cur_=" $cur " sfx=" " mode= " refs "
761
805
762
806
while test $# ! = 0; do
763
807
case " $1 " in
764
808
--remote=* ) remote=" ${1## --remote=} " ;;
765
- --track) track=" yes" ;;
809
+ --dwim) dwim=" yes" ;;
810
+ # --track is an old spelling of --dwim
811
+ --track) dwim=" yes" ;;
766
812
--pfx=* ) pfx=" ${1## --pfx=} " ;;
767
813
--cur=* ) cur_=" ${1## --cur=} " ;;
768
814
--sfx=* ) sfx=" ${1## --sfx=} " ;;
815
+ --mode=* ) mode=" ${1## --mode=} " ;;
769
816
* ) return 1 ;;
770
817
esac
771
818
shift
772
819
done
773
820
774
- __gitcomp_direct " $( __git_refs " $remote " " $track " " $pfx " " $cur_ " " $sfx " ) "
821
+ # complete references based on the specified mode
822
+ case " $mode " in
823
+ refs)
824
+ __gitcomp_direct " $( __git_refs " $remote " " " " $pfx " " $cur_ " " $sfx " ) " ;;
825
+ heads)
826
+ __gitcomp_direct " $( __git_heads " $pfx " " $cur_ " " $sfx " ) " ;;
827
+ remote-heads)
828
+ __gitcomp_direct " $( __git_remote_heads " $pfx " " $cur_ " " $sfx " ) " ;;
829
+ * )
830
+ return 1 ;;
831
+ esac
832
+
833
+ # Append DWIM remote branch names if requested
834
+ if [ " $dwim " = " yes" ]; then
835
+ __gitcomp_direct_append " $( __git_dwim_remote_heads " $pfx " " $cur_ " " $sfx " ) "
836
+ fi
775
837
}
776
838
777
839
# __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -1102,6 +1164,40 @@ __git_find_on_cmdline ()
1102
1164
done
1103
1165
}
1104
1166
1167
+ # Similar to __git_find_on_cmdline, except that it loops backwards and thus
1168
+ # prints the *last* word found. Useful for finding which of two options that
1169
+ # supersede each other came last, such as "--guess" and "--no-guess".
1170
+ #
1171
+ # Usage: __git_find_last_on_cmdline [<option>]... "<wordlist>"
1172
+ # --show-idx: Optionally show the index of the found word in the $words array.
1173
+ __git_find_last_on_cmdline ()
1174
+ {
1175
+ local word c=$cword show_idx
1176
+
1177
+ while test $# -gt 1; do
1178
+ case " $1 " in
1179
+ --show-idx) show_idx=y ;;
1180
+ * ) return 1 ;;
1181
+ esac
1182
+ shift
1183
+ done
1184
+ local wordlist=" $1 "
1185
+
1186
+ while [ $c -gt 1 ]; do
1187
+ (( c-- ))
1188
+ for word in $wordlist ; do
1189
+ if [ " $word " = " ${words[c]} " ]; then
1190
+ if [ -n " $show_idx " ]; then
1191
+ echo " $c $word "
1192
+ else
1193
+ echo " $word "
1194
+ fi
1195
+ return
1196
+ fi
1197
+ done
1198
+ done
1199
+ }
1200
+
1105
1201
# Echo the value of an option set on the command line or config
1106
1202
#
1107
1203
# $1: short option name
@@ -1356,6 +1452,46 @@ _git_bundle ()
1356
1452
esac
1357
1453
}
1358
1454
1455
+ # Helper function to decide whether or not we should enable DWIM logic for
1456
+ # git-switch and git-checkout.
1457
+ #
1458
+ # To decide between the following rules in priority order
1459
+ # 1) the last provided of "--guess" or "--no-guess" explicitly enable or
1460
+ # disable completion of DWIM logic respectively.
1461
+ # 2) If the --no-track option is provided, take this as a hint to disable the
1462
+ # DWIM completion logic
1463
+ # 3) If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
1464
+ # logic, as requested by the user.
1465
+ # 4) Enable DWIM logic otherwise.
1466
+ #
1467
+ __git_checkout_default_dwim_mode ()
1468
+ {
1469
+ local last_option dwim_opt=" --dwim"
1470
+
1471
+ if [ " $GIT_COMPLETION_CHECKOUT_NO_GUESS " = " 1" ]; then
1472
+ dwim_opt=" "
1473
+ fi
1474
+
1475
+ # --no-track disables DWIM, but with lower priority than
1476
+ # --guess/--no-guess
1477
+ if [ -n " $( __git_find_on_cmdline " --no-track" ) " ]; then
1478
+ dwim_opt=" "
1479
+ fi
1480
+
1481
+ # Find the last provided --guess or --no-guess
1482
+ last_option=" $( __git_find_last_on_cmdline " --guess --no-guess" ) "
1483
+ case " $last_option " in
1484
+ --guess)
1485
+ dwim_opt=" --dwim"
1486
+ ;;
1487
+ --no-guess)
1488
+ dwim_opt=" "
1489
+ ;;
1490
+ esac
1491
+
1492
+ echo " $dwim_opt "
1493
+ }
1494
+
1359
1495
_git_checkout ()
1360
1496
{
1361
1497
__git_has_doubledash && return
@@ -1368,14 +1504,38 @@ _git_checkout ()
1368
1504
__gitcomp_builtin checkout
1369
1505
;;
1370
1506
* )
1371
- # check if --track, --no-track, or --no-guess was specified
1372
- # if so, disable DWIM mode
1373
- local flags=" --track --no-track --no-guess" track_opt=" --track"
1374
- if [ " $GIT_COMPLETION_CHECKOUT_NO_GUESS " = " 1" ] ||
1375
- [ -n " $( __git_find_on_cmdline " $flags " ) " ]; then
1376
- track_opt=' '
1507
+ local dwim_opt=" $( __git_checkout_default_dwim_mode) "
1508
+ local prevword prevword=" ${words[cword-1]} "
1509
+
1510
+ case " $prevword " in
1511
+ -b|-B|--orphan)
1512
+ # Complete local branches (and DWIM branch
1513
+ # remote branch names) for an option argument
1514
+ # specifying a new branch name. This is for
1515
+ # convenience, assuming new branches are
1516
+ # possibly based on pre-existing branch names.
1517
+ __git_complete_refs $dwim_opt --mode=" heads"
1518
+ return
1519
+ ;;
1520
+ * )
1521
+ ;;
1522
+ esac
1523
+
1524
+ # At this point, we've already handled special completion for
1525
+ # the arguments to -b/-B, and --orphan. There are 3 main
1526
+ # things left we can possibly complete:
1527
+ # 1) a start-point for -b/-B, -d/--detach, or --orphan
1528
+ # 2) a remote head, for --track
1529
+ # 3) an arbitrary reference, possibly including DWIM names
1530
+ #
1531
+
1532
+ if [ -n " $( __git_find_on_cmdline " -b -B -d --detach --orphan" ) " ]; then
1533
+ __git_complete_refs --mode=" refs"
1534
+ elif [ -n " $( __git_find_on_cmdline " --track" ) " ]; then
1535
+ __git_complete_refs --mode=" remote-heads"
1536
+ else
1537
+ __git_complete_refs $dwim_opt --mode=" refs"
1377
1538
fi
1378
- __git_complete_refs $track_opt
1379
1539
;;
1380
1540
esac
1381
1541
}
@@ -2224,29 +2384,43 @@ _git_switch ()
2224
2384
__gitcomp_builtin switch
2225
2385
;;
2226
2386
* )
2227
- # check if --track, --no-track, or --no-guess was specified
2228
- # if so, disable DWIM mode
2229
- local track_opt=" --track" only_local_ref=n
2230
- if [ " $GIT_COMPLETION_CHECKOUT_NO_GUESS " = " 1" ] ||
2231
- [ -n " $( __git_find_on_cmdline " --track --no-track --no-guess" ) " ]; then
2232
- track_opt=' '
2233
- fi
2234
- # explicit --guess enables DWIM mode regardless of
2235
- # $GIT_COMPLETION_CHECKOUT_NO_GUESS
2236
- if [ -n " $( __git_find_on_cmdline " --guess" ) " ]; then
2237
- track_opt=' --track'
2238
- fi
2239
- if [ -z " $( __git_find_on_cmdline " -d --detach" ) " ]; then
2240
- only_local_ref=y
2241
- else
2242
- # --guess --detach is invalid combination, no
2243
- # dwim will be done when --detach is specified
2244
- track_opt=
2387
+ local dwim_opt=" $( __git_checkout_default_dwim_mode) "
2388
+ local prevword prevword=" ${words[cword-1]} "
2389
+
2390
+ case " $prevword " in
2391
+ -c|-C|--orphan)
2392
+ # Complete local branches (and DWIM branch
2393
+ # remote branch names) for an option argument
2394
+ # specifying a new branch name. This is for
2395
+ # convenience, assuming new branches are
2396
+ # possibly based on pre-existing branch names.
2397
+ __git_complete_refs $dwim_opt --mode=" heads"
2398
+ return
2399
+ ;;
2400
+ * )
2401
+ ;;
2402
+ esac
2403
+
2404
+ # Unlike in git checkout, git switch --orphan does not take
2405
+ # a start point. Thus we really have nothing to complete after
2406
+ # the branch name.
2407
+ if [ -n " $( __git_find_on_cmdline " --orphan" ) " ]; then
2408
+ return
2245
2409
fi
2246
- if [ $only_local_ref = y -a -z " $track_opt " ]; then
2247
- __gitcomp_direct " $( __git_heads " " " $cur " " " ) "
2410
+
2411
+ # At this point, we've already handled special completion for
2412
+ # -c/-C, and --orphan. There are 3 main things left to
2413
+ # complete:
2414
+ # 1) a start-point for -c/-C or -d/--detach
2415
+ # 2) a remote head, for --track
2416
+ # 3) a branch name, possibly including DWIM remote branches
2417
+
2418
+ if [ -n " $( __git_find_on_cmdline " -c -C -d --detach" ) " ]; then
2419
+ __git_complete_refs --mode=" refs"
2420
+ elif [ -n " $( __git_find_on_cmdline " --track" ) " ]; then
2421
+ __git_complete_refs --mode=" remote-heads"
2248
2422
else
2249
- __git_complete_refs $track_opt
2423
+ __git_complete_refs $dwim_opt --mode= " heads "
2250
2424
fi
2251
2425
;;
2252
2426
esac
0 commit comments