Skip to content

Commit ef06d74

Browse files
committed
Merge branch 'nd/parseopt-completion-more'
The mechanism to use parse-options API to automate the command line completion continues to get extended and polished. * nd/parseopt-completion-more: completion: use __gitcomp_builtin in _git_cherry completion: use __gitcomp_builtin in _git_ls_tree completion: delete option-only completion commands completion: add --option completion for most builtin commands completion: factor out _git_xxx calling code completion: mention the oldest version we need to support git.c: add hidden option --list-parseopt-builtins git.c: move cmd_struct declaration up
2 parents 51f813c + c55c4a5 commit ef06d74

File tree

3 files changed

+127
-76
lines changed

3 files changed

+127
-76
lines changed

contrib/completion/git-completion.bash

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
# tell the completion to use commit completion. This also works with aliases
3030
# of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '".
3131
#
32+
# Compatible with bash 3.2.57.
33+
#
3234
# You can set the following environment variables to influence the behavior of
3335
# the completion routines:
3436
#
@@ -1284,6 +1286,12 @@ _git_checkout ()
12841286

12851287
_git_cherry ()
12861288
{
1289+
case "$cur" in
1290+
--*)
1291+
__gitcomp_builtin cherry
1292+
return
1293+
esac
1294+
12871295
__git_complete_refs
12881296
}
12891297

@@ -1503,16 +1511,6 @@ _git_fsck ()
15031511
esac
15041512
}
15051513

1506-
_git_gc ()
1507-
{
1508-
case "$cur" in
1509-
--*)
1510-
__gitcomp_builtin gc
1511-
return
1512-
;;
1513-
esac
1514-
}
1515-
15161514
_git_gitk ()
15171515
{
15181516
_gitk
@@ -1637,6 +1635,13 @@ _git_ls_remote ()
16371635

16381636
_git_ls_tree ()
16391637
{
1638+
case "$cur" in
1639+
--*)
1640+
__gitcomp_builtin ls-tree
1641+
return
1642+
;;
1643+
esac
1644+
16401645
__git_complete_file
16411646
}
16421647

@@ -1812,11 +1817,6 @@ _git_mv ()
18121817
fi
18131818
}
18141819

1815-
_git_name_rev ()
1816-
{
1817-
__gitcomp_builtin name-rev
1818-
}
1819-
18201820
_git_notes ()
18211821
{
18221822
local subcommands='add append copy edit get-ref list merge prune remove show'
@@ -3036,6 +3036,45 @@ _git_worktree ()
30363036
fi
30373037
}
30383038

3039+
__git_complete_common () {
3040+
local command="$1"
3041+
3042+
case "$cur" in
3043+
--*)
3044+
__gitcomp_builtin "$command"
3045+
;;
3046+
esac
3047+
}
3048+
3049+
__git_cmds_with_parseopt_helper=
3050+
__git_support_parseopt_helper () {
3051+
test -n "$__git_cmds_with_parseopt_helper" ||
3052+
__git_cmds_with_parseopt_helper="$(__git --list-parseopt-builtins)"
3053+
3054+
case " $__git_cmds_with_parseopt_helper " in
3055+
*" $1 "*)
3056+
return 0
3057+
;;
3058+
*)
3059+
return 1
3060+
;;
3061+
esac
3062+
}
3063+
3064+
__git_complete_command () {
3065+
local command="$1"
3066+
local completion_func="_git_${command//-/_}"
3067+
if declare -f $completion_func >/dev/null 2>/dev/null; then
3068+
$completion_func
3069+
return 0
3070+
elif __git_support_parseopt_helper "$command"; then
3071+
__git_complete_common "$command"
3072+
return 0
3073+
else
3074+
return 1
3075+
fi
3076+
}
3077+
30393078
__git_main ()
30403079
{
30413080
local i c=1 command __git_dir __git_repo_path
@@ -3095,14 +3134,12 @@ __git_main ()
30953134
return
30963135
fi
30973136

3098-
local completion_func="_git_${command//-/_}"
3099-
declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return
3137+
__git_complete_command "$command" && return
31003138

31013139
local expansion=$(__git_aliased_command "$command")
31023140
if [ -n "$expansion" ]; then
31033141
words[1]=$expansion
3104-
completion_func="_git_${expansion//-/_}"
3105-
declare -f $completion_func >/dev/null 2>/dev/null && $completion_func
3142+
__git_complete_command "$expansion"
31063143
fi
31073144
}
31083145

git.c

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@
44
#include "help.h"
55
#include "run-command.h"
66

7+
#define RUN_SETUP (1<<0)
8+
#define RUN_SETUP_GENTLY (1<<1)
9+
#define USE_PAGER (1<<2)
10+
/*
11+
* require working tree to be present -- anything uses this needs
12+
* RUN_SETUP for reading from the configuration file.
13+
*/
14+
#define NEED_WORK_TREE (1<<3)
15+
#define SUPPORT_SUPER_PREFIX (1<<4)
16+
#define DELAY_PAGER_CONFIG (1<<5)
17+
#define NO_PARSEOPT (1<<6) /* parse-options is not used */
18+
19+
struct cmd_struct {
20+
const char *cmd;
21+
int (*fn)(int, const char **, const char *);
22+
unsigned int option;
23+
};
24+
725
const char git_usage_string[] =
826
N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
927
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
@@ -18,7 +36,7 @@ const char git_more_info_string[] =
1836

1937
static int use_pager = -1;
2038

21-
static void list_builtins(void);
39+
static void list_builtins(unsigned int exclude_option, char sep);
2240

2341
static void commit_pager_choice(void) {
2442
switch (use_pager) {
@@ -206,7 +224,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
206224
(*argv)++;
207225
(*argc)--;
208226
} else if (!strcmp(cmd, "--list-builtins")) {
209-
list_builtins();
227+
list_builtins(0, '\n');
228+
exit(0);
229+
} else if (!strcmp(cmd, "--list-parseopt-builtins")) {
230+
list_builtins(NO_PARSEOPT, ' ');
210231
exit(0);
211232
} else {
212233
fprintf(stderr, _("unknown option: %s\n"), cmd);
@@ -288,23 +309,6 @@ static int handle_alias(int *argcp, const char ***argv)
288309
return ret;
289310
}
290311

291-
#define RUN_SETUP (1<<0)
292-
#define RUN_SETUP_GENTLY (1<<1)
293-
#define USE_PAGER (1<<2)
294-
/*
295-
* require working tree to be present -- anything uses this needs
296-
* RUN_SETUP for reading from the configuration file.
297-
*/
298-
#define NEED_WORK_TREE (1<<3)
299-
#define SUPPORT_SUPER_PREFIX (1<<4)
300-
#define DELAY_PAGER_CONFIG (1<<5)
301-
302-
struct cmd_struct {
303-
const char *cmd;
304-
int (*fn)(int, const char **, const char *);
305-
int option;
306-
};
307-
308312
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
309313
{
310314
int status, help;
@@ -367,18 +371,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
367371
static struct cmd_struct commands[] = {
368372
{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
369373
{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
370-
{ "annotate", cmd_annotate, RUN_SETUP },
374+
{ "annotate", cmd_annotate, RUN_SETUP | NO_PARSEOPT },
371375
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
372376
{ "archive", cmd_archive, RUN_SETUP_GENTLY },
373377
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
374378
{ "blame", cmd_blame, RUN_SETUP },
375379
{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
376-
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
380+
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
377381
{ "cat-file", cmd_cat_file, RUN_SETUP },
378382
{ "check-attr", cmd_check_attr, RUN_SETUP },
379383
{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
380384
{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
381-
{ "check-ref-format", cmd_check_ref_format },
385+
{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT },
382386
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
383387
{ "checkout-index", cmd_checkout_index,
384388
RUN_SETUP | NEED_WORK_TREE},
@@ -388,58 +392,58 @@ static struct cmd_struct commands[] = {
388392
{ "clone", cmd_clone },
389393
{ "column", cmd_column, RUN_SETUP_GENTLY },
390394
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
391-
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
395+
{ "commit-tree", cmd_commit_tree, RUN_SETUP | NO_PARSEOPT },
392396
{ "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
393397
{ "count-objects", cmd_count_objects, RUN_SETUP },
394-
{ "credential", cmd_credential, RUN_SETUP_GENTLY },
398+
{ "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
395399
{ "describe", cmd_describe, RUN_SETUP },
396-
{ "diff", cmd_diff },
397-
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
398-
{ "diff-index", cmd_diff_index, RUN_SETUP },
399-
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
400+
{ "diff", cmd_diff, NO_PARSEOPT },
401+
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
402+
{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
403+
{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
400404
{ "difftool", cmd_difftool, RUN_SETUP | NEED_WORK_TREE },
401405
{ "fast-export", cmd_fast_export, RUN_SETUP },
402406
{ "fetch", cmd_fetch, RUN_SETUP },
403-
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
407+
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
404408
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
405409
{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
406410
{ "format-patch", cmd_format_patch, RUN_SETUP },
407411
{ "fsck", cmd_fsck, RUN_SETUP },
408412
{ "fsck-objects", cmd_fsck, RUN_SETUP },
409413
{ "gc", cmd_gc, RUN_SETUP },
410-
{ "get-tar-commit-id", cmd_get_tar_commit_id },
414+
{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
411415
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
412416
{ "hash-object", cmd_hash_object },
413417
{ "help", cmd_help },
414-
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
418+
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
415419
{ "init", cmd_init_db },
416420
{ "init-db", cmd_init_db },
417421
{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
418422
{ "log", cmd_log, RUN_SETUP },
419423
{ "ls-files", cmd_ls_files, RUN_SETUP },
420424
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
421425
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
422-
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
423-
{ "mailsplit", cmd_mailsplit },
426+
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
427+
{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
424428
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
425429
{ "merge-base", cmd_merge_base, RUN_SETUP },
426430
{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
427-
{ "merge-index", cmd_merge_index, RUN_SETUP },
428-
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
429-
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
430-
{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
431-
{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
432-
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
433-
{ "merge-tree", cmd_merge_tree, RUN_SETUP },
434-
{ "mktag", cmd_mktag, RUN_SETUP },
431+
{ "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
432+
{ "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
433+
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
434+
{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
435+
{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
436+
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
437+
{ "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
438+
{ "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
435439
{ "mktree", cmd_mktree, RUN_SETUP },
436440
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
437441
{ "name-rev", cmd_name_rev, RUN_SETUP },
438442
{ "notes", cmd_notes, RUN_SETUP },
439443
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
440-
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
444+
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
441445
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
442-
{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY },
446+
{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
443447
{ "pickaxe", cmd_blame, RUN_SETUP },
444448
{ "prune", cmd_prune, RUN_SETUP },
445449
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -450,14 +454,14 @@ static struct cmd_struct commands[] = {
450454
{ "receive-pack", cmd_receive_pack },
451455
{ "reflog", cmd_reflog, RUN_SETUP },
452456
{ "remote", cmd_remote, RUN_SETUP },
453-
{ "remote-ext", cmd_remote_ext },
454-
{ "remote-fd", cmd_remote_fd },
457+
{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
458+
{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
455459
{ "repack", cmd_repack, RUN_SETUP },
456460
{ "replace", cmd_replace, RUN_SETUP },
457461
{ "rerere", cmd_rerere, RUN_SETUP },
458462
{ "reset", cmd_reset, RUN_SETUP },
459-
{ "rev-list", cmd_rev_list, RUN_SETUP },
460-
{ "rev-parse", cmd_rev_parse },
463+
{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
464+
{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
461465
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
462466
{ "rm", cmd_rm, RUN_SETUP },
463467
{ "send-pack", cmd_send_pack, RUN_SETUP },
@@ -468,23 +472,23 @@ static struct cmd_struct commands[] = {
468472
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
469473
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
470474
{ "stripspace", cmd_stripspace },
471-
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
475+
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
472476
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
473477
{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
474-
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
475-
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
478+
{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
479+
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
476480
{ "update-index", cmd_update_index, RUN_SETUP },
477481
{ "update-ref", cmd_update_ref, RUN_SETUP },
478482
{ "update-server-info", cmd_update_server_info, RUN_SETUP },
479-
{ "upload-archive", cmd_upload_archive },
480-
{ "upload-archive--writer", cmd_upload_archive_writer },
481-
{ "var", cmd_var, RUN_SETUP_GENTLY },
483+
{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
484+
{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
485+
{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
482486
{ "verify-commit", cmd_verify_commit, RUN_SETUP },
483487
{ "verify-pack", cmd_verify_pack },
484488
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
485489
{ "version", cmd_version },
486490
{ "whatchanged", cmd_whatchanged, RUN_SETUP },
487-
{ "worktree", cmd_worktree, RUN_SETUP },
491+
{ "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT },
488492
{ "write-tree", cmd_write_tree, RUN_SETUP },
489493
};
490494

@@ -504,11 +508,15 @@ int is_builtin(const char *s)
504508
return !!get_builtin(s);
505509
}
506510

507-
static void list_builtins(void)
511+
static void list_builtins(unsigned int exclude_option, char sep)
508512
{
509513
int i;
510-
for (i = 0; i < ARRAY_SIZE(commands); i++)
511-
printf("%s\n", commands[i].cmd);
514+
for (i = 0; i < ARRAY_SIZE(commands); i++) {
515+
if (exclude_option &&
516+
(commands[i].option & exclude_option))
517+
continue;
518+
printf("%s%c", commands[i].cmd, sep);
519+
}
512520
}
513521

514522
#ifdef STRIP_EXTENSION

t/t9902-completion.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,12 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
14541454
EOF
14551455
'
14561456

1457+
test_expect_success 'completion without explicit _git_xxx function' '
1458+
test_completion "git version --" <<-\EOF
1459+
--build-options Z
1460+
EOF
1461+
'
1462+
14571463
test_expect_failure 'complete with tilde expansion' '
14581464
git init tmp && cd tmp &&
14591465
test_when_finished "cd .. && rm -rf tmp" &&

0 commit comments

Comments
 (0)