diff options
Diffstat (limited to 'git.c')
| -rw-r--r-- | git.c | 258 |
1 files changed, 147 insertions, 111 deletions
@@ -28,6 +28,7 @@ #define NEED_WORK_TREE (1<<3) #define DELAY_PAGER_CONFIG (1<<4) #define NO_PARSEOPT (1<<5) /* parse-options is not used */ +#define DEPRECATED (1<<6) struct cmd_struct { const char *cmd; @@ -51,11 +52,13 @@ const char git_more_info_string[] = static int use_pager = -1; -static void list_builtins(struct string_list *list, unsigned int exclude_option); +static void list_builtins(struct string_list *list, + unsigned int include_option, + unsigned int exclude_option); static void exclude_helpers_from_list(struct string_list *list) { - int i = 0; + size_t i = 0; while (i < list->nr) { if (strstr(list->items[i].string, "--")) @@ -75,7 +78,6 @@ static int match_token(const char *spec, int len, const char *token) static int list_cmds(const char *spec) { struct string_list list = STRING_LIST_INIT_DUP; - int i; int nongit; /* @@ -89,7 +91,7 @@ static int list_cmds(const char *spec) int len = sep - spec; if (match_token(spec, len, "builtins")) - list_builtins(&list, 0); + list_builtins(&list, 0, 0); else if (match_token(spec, len, "main")) list_all_main_cmds(&list); else if (match_token(spec, len, "others")) @@ -100,6 +102,8 @@ static int list_cmds(const char *spec) list_aliases(&list); else if (match_token(spec, len, "config")) list_cmds_by_config(&list); + else if (match_token(spec, len, "deprecated")) + list_builtins(&list, DEPRECATED, 0); else if (len > 5 && !strncmp(spec, "list-", 5)) { struct strbuf sb = STRBUF_INIT; @@ -113,7 +117,7 @@ static int list_cmds(const char *spec) if (*spec == ',') spec++; } - for (i = 0; i < list.nr; i++) + for (size_t i = 0; i < list.nr; i++) puts(list.items[i].string); string_list_clear(&list, 0); return 0; @@ -126,7 +130,7 @@ static void commit_pager_choice(void) setenv("GIT_PAGER", "cat", 1); break; case 1: - setup_pager(); + setup_pager(the_repository); break; default: break; @@ -137,7 +141,7 @@ void setup_auto_pager(const char *cmd, int def) { if (use_pager != -1 || pager_in_use()) return; - use_pager = check_pager_config(cmd); + use_pager = check_pager_config(the_repository, cmd); if (use_pager == -1) use_pager = def; commit_pager_choice(); @@ -322,10 +326,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) trace2_cmd_name("_query_"); if (!strcmp(cmd, "parseopt")) { struct string_list list = STRING_LIST_INIT_DUP; - int i; - list_builtins(&list, NO_PARSEOPT); - for (i = 0; i < list.nr; i++) + list_builtins(&list, 0, NO_PARSEOPT); + for (size_t i = 0; i < list.nr; i++) printf("%s ", list.items[i].string); string_list_clear(&list, 0); exit(0); @@ -362,7 +365,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) return (*argv) - orig_argv; } -static int handle_alias(int *argcp, const char ***argv) +static int handle_alias(struct strvec *args, struct string_list *expanded_aliases) { int envchanged = 0, ret = 0, saved_errno = errno; int count, option_count; @@ -370,10 +373,12 @@ static int handle_alias(int *argcp, const char ***argv) const char *alias_command; char *alias_string; - alias_command = (*argv)[0]; + alias_command = args->v[0]; alias_string = alias_lookup(alias_command); if (alias_string) { - if (*argcp > 1 && !strcmp((*argv)[1], "-h")) + struct string_list_item *seen; + + if (args->nr == 2 && !strcmp(args->v[1], "-h")) fprintf_ln(stderr, _("'%s' is aliased to '%s'"), alias_command, alias_string); if (alias_string[0] == '!') { @@ -390,7 +395,7 @@ static int handle_alias(int *argcp, const char ***argv) child.wait_after_clean = 1; child.trace2_child_class = "shell_alias"; strvec_push(&child.args, alias_string + 1); - strvec_pushv(&child.args, (*argv) + 1); + strvec_pushv(&child.args, args->v + 1); trace2_cmd_alias(alias_command, child.args.v); trace2_cmd_name("_run_shell_alias_"); @@ -420,18 +425,35 @@ static int handle_alias(int *argcp, const char ***argv) if (!strcmp(alias_command, new_argv[0])) die(_("recursive alias: %s"), alias_command); + string_list_append(expanded_aliases, alias_command); + seen = unsorted_string_list_lookup(expanded_aliases, + new_argv[0]); + + if (seen) { + struct strbuf sb = STRBUF_INIT; + for (size_t i = 0; i < expanded_aliases->nr; i++) { + struct string_list_item *item = &expanded_aliases->items[i]; + + strbuf_addf(&sb, "\n %s", item->string); + if (item == seen) + strbuf_addstr(&sb, " <=="); + else if (i == expanded_aliases->nr - 1) + strbuf_addstr(&sb, " ==>"); + } + die(_("alias loop detected: expansion of '%s' does" + " not terminate:%s"), expanded_aliases->items[0].string, sb.buf); + } + trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); - - REALLOC_ARRAY(new_argv, count + *argcp); - /* insert after command name */ - COPY_ARRAY(new_argv + count, *argv + 1, *argcp); - trace2_cmd_alias(alias_command, new_argv); - *argv = new_argv; - *argcp += count - 1; + /* Replace the alias with the new arguments. */ + strvec_splice(args, 0, 1, new_argv, count); + + free(alias_string); + free(new_argv); ret = 1; } @@ -444,20 +466,21 @@ static int handle_alias(int *argcp, const char ***argv) static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct repository *repo) { int status, help; + int no_repo = 1; struct stat st; const char *prefix; int run_setup = (p->option & (RUN_SETUP | RUN_SETUP_GENTLY)); - help = argc == 2 && !strcmp(argv[1], "-h"); + help = argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help-all")); if (help && (run_setup & RUN_SETUP)) /* demote to GENTLY to allow 'git cmd -h' outside repo */ run_setup = RUN_SETUP_GENTLY; if (run_setup & RUN_SETUP) { prefix = setup_git_directory(); + no_repo = 0; } else if (run_setup & RUN_SETUP_GENTLY) { - int nongit_ok; - prefix = setup_git_directory_gently(&nongit_ok); + prefix = setup_git_directory_gently(&no_repo); } else { prefix = NULL; } @@ -465,12 +488,12 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct precompose_argv_prefix(argc, argv, NULL); if (use_pager == -1 && run_setup && !(p->option & DELAY_PAGER_CONFIG)) - use_pager = check_pager_config(p->cmd); + use_pager = check_pager_config(repo, p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; if (run_setup && startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ - trace_repo_setup(); + trace_repo_setup(repo); commit_pager_choice(); if (!help && p->option & NEED_WORK_TREE) @@ -480,7 +503,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct trace2_cmd_name(p->cmd); validate_cache_entries(repo->index); - status = p->fn(argc, argv, prefix, (p->option & RUN_SETUP)? repo : NULL); + status = p->fn(argc, argv, prefix, no_repo ? NULL : repo); validate_cache_entries(repo->index); if (status) @@ -509,6 +532,7 @@ static struct cmd_struct commands[] = { { "annotate", cmd_annotate, RUN_SETUP }, { "apply", cmd_apply, RUN_SETUP_GENTLY }, { "archive", cmd_archive, RUN_SETUP_GENTLY }, + { "backfill", cmd_backfill, RUN_SETUP }, { "bisect", cmd_bisect, RUN_SETUP }, { "blame", cmd_blame, RUN_SETUP }, { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG }, @@ -543,6 +567,7 @@ static struct cmd_struct commands[] = { { "diff", cmd_diff, NO_PARSEOPT }, { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT }, { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT }, + { "diff-pairs", cmd_diff_pairs, RUN_SETUP | NO_PARSEOPT }, { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT }, { "difftool", cmd_difftool, RUN_SETUP_GENTLY }, { "fast-export", cmd_fast_export, RUN_SETUP }, @@ -566,6 +591,7 @@ static struct cmd_struct commands[] = { { "init", cmd_init_db }, { "init-db", cmd_init_db }, { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY }, + { "last-modified", cmd_last_modified, RUN_SETUP }, { "log", cmd_log, RUN_SETUP }, { "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY }, @@ -590,7 +616,9 @@ static struct cmd_struct commands[] = { { "name-rev", cmd_name_rev, RUN_SETUP }, { "notes", cmd_notes, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, - { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT }, +#ifndef WITH_BREAKING_CHANGES + { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED }, +#endif { "pack-refs", cmd_pack_refs, RUN_SETUP }, { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT }, { "pickaxe", cmd_blame, RUN_SETUP }, @@ -610,6 +638,7 @@ static struct cmd_struct commands[] = { { "repack", cmd_repack, RUN_SETUP }, { "replace", cmd_replace, RUN_SETUP }, { "replay", cmd_replay, RUN_SETUP }, + { "repo", cmd_repo, RUN_SETUP }, { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE }, @@ -645,15 +674,16 @@ static struct cmd_struct commands[] = { { "verify-pack", cmd_verify_pack }, { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, - { "whatchanged", cmd_whatchanged, RUN_SETUP }, +#ifndef WITH_BREAKING_CHANGES + { "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED }, +#endif { "worktree", cmd_worktree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP }, }; static struct cmd_struct *get_builtin(const char *s) { - int i; - for (i = 0; i < ARRAY_SIZE(commands); i++) { + for (size_t i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = commands + i; if (!strcmp(s, p->cmd)) return p; @@ -666,12 +696,16 @@ int is_builtin(const char *s) return !!get_builtin(s); } -static void list_builtins(struct string_list *out, unsigned int exclude_option) +static void list_builtins(struct string_list *out, + unsigned int include_option, + unsigned int exclude_option) { - int i; - for (i = 0; i < ARRAY_SIZE(commands); i++) { - if (exclude_option && - (commands[i].option & exclude_option)) + if (include_option && exclude_option) + BUG("'include_option' and 'exclude_option' are mutually exclusive"); + for (size_t i = 0; i < ARRAY_SIZE(commands); i++) { + if (include_option && !(commands[i].option & include_option)) + continue; + if (exclude_option && (commands[i].option & exclude_option)) continue; string_list_append(out, commands[i].cmd); } @@ -680,7 +714,6 @@ static void list_builtins(struct string_list *out, unsigned int exclude_option) void load_builtin_commands(const char *prefix, struct cmdnames *cmds) { const char *name; - int i; /* * Callers can ask for a subset of the commands based on a certain @@ -691,69 +724,63 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds) if (!skip_prefix(prefix, "git-", &prefix)) BUG("prefix '%s' must start with 'git-'", prefix); - for (i = 0; i < ARRAY_SIZE(commands); i++) + for (size_t i = 0; i < ARRAY_SIZE(commands); i++) if (skip_prefix(commands[i].cmd, prefix, &name)) add_cmdname(cmds, name, strlen(name)); } #ifdef STRIP_EXTENSION -static void strip_extension(const char **argv) +static void strip_extension(struct strvec *args) { size_t len; - if (strip_suffix(argv[0], STRIP_EXTENSION, &len)) - argv[0] = xmemdupz(argv[0], len); + if (strip_suffix(args->v[0], STRIP_EXTENSION, &len)) { + char *stripped = xmemdupz(args->v[0], len); + strvec_replace(args, 0, stripped); + free(stripped); + } } #else #define strip_extension(cmd) #endif -static void handle_builtin(int argc, const char **argv) +static void handle_builtin(struct strvec *args) { - struct strvec args = STRVEC_INIT; - const char **argv_copy = NULL; const char *cmd; struct cmd_struct *builtin; - strip_extension(argv); - cmd = argv[0]; + strip_extension(args); + cmd = args->v[0]; /* Turn "git cmd --help" into "git help --exclude-guides cmd" */ - if (argc > 1 && !strcmp(argv[1], "--help")) { - int i; - - argv[1] = argv[0]; - argv[0] = cmd = "help"; - - for (i = 0; i < argc; i++) { - strvec_push(&args, argv[i]); - if (!i) - strvec_push(&args, "--exclude-guides"); - } + if (args->nr > 1 && !strcmp(args->v[1], "--help")) { + const char *exclude_guides_arg[] = { "--exclude-guides" }; + + strvec_replace(args, 1, args->v[0]); + strvec_replace(args, 0, "help"); + cmd = "help"; + strvec_splice(args, 2, 0, exclude_guides_arg, + ARRAY_SIZE(exclude_guides_arg)); + } - argc++; + builtin = get_builtin(cmd); + if (builtin) { + const char **argv_copy = NULL; + int ret; /* * `run_builtin()` will modify the argv array, so we need to * create a shallow copy such that we can free all of its * strings. */ - CALLOC_ARRAY(argv_copy, argc + 1); - COPY_ARRAY(argv_copy, args.v, argc); - - argv = argv_copy; - } + if (args->nr) + DUP_ARRAY(argv_copy, args->v, args->nr + 1); - builtin = get_builtin(cmd); - if (builtin) { - int ret = run_builtin(builtin, argc, argv, the_repository); - strvec_clear(&args); + ret = run_builtin(builtin, args->nr, argv_copy, the_repository); + strvec_clear(args); free(argv_copy); exit(ret); } - - strvec_clear(&args); - free(argv_copy); } static void execv_dashed_external(const char **argv) @@ -762,7 +789,7 @@ static void execv_dashed_external(const char **argv) int status; if (use_pager == -1 && !is_builtin(argv[0])) - use_pager = check_pager_config(argv[0]); + use_pager = check_pager_config(the_repository, argv[0]); commit_pager_choice(); strvec_pushf(&cmd.args, "git-%s", argv[0]); @@ -799,14 +826,30 @@ static void execv_dashed_external(const char **argv) exit(128); } -static int run_argv(int *argcp, const char ***argv) +static int is_deprecated_command(const char *cmd) +{ + struct cmd_struct *builtin = get_builtin(cmd); + return builtin && (builtin->option & DEPRECATED); +} + +static int run_argv(struct strvec *args) { int done_alias = 0; - struct string_list cmd_list = STRING_LIST_INIT_NODUP; - struct string_list_item *seen; + struct string_list expanded_aliases = STRING_LIST_INIT_DUP; while (1) { /* + * Allow deprecated commands to be overridden by aliases. This + * creates a seamless path forward for people who want to keep + * using the name after it is gone, but want to skip the + * deprecation complaint in the meantime. + */ + if (is_deprecated_command(args->v[0]) && + handle_alias(args, &expanded_aliases)) { + done_alias = 1; + continue; + } + /* * If we tried alias and futzed with our environment, * it no longer is safe to invoke builtins directly in * general. We have to spawn them as dashed externals. @@ -816,10 +859,10 @@ static int run_argv(int *argcp, const char ***argv) * process. */ if (!done_alias) - handle_builtin(*argcp, *argv); - else if (get_builtin(**argv)) { + handle_builtin(args); + else if (get_builtin(args->v[0])) { struct child_process cmd = CHILD_PROCESS_INIT; - int i; + int err; /* * The current process is committed to launching a @@ -833,8 +876,8 @@ static int run_argv(int *argcp, const char ***argv) commit_pager_choice(); strvec_push(&cmd.args, "git"); - for (i = 0; i < *argcp; i++) - strvec_push(&cmd.args, (*argv)[i]); + for (size_t i = 0; i < args->nr; i++) + strvec_push(&cmd.args, args->v[i]); trace_argv_printf(cmd.args.v, "trace: exec:"); @@ -846,51 +889,33 @@ static int run_argv(int *argcp, const char ***argv) cmd.clean_on_exit = 1; cmd.wait_after_clean = 1; cmd.trace2_child_class = "git_alias"; - i = run_command(&cmd); - if (i >= 0 || errno != ENOENT) - exit(i); - die("could not execute builtin %s", **argv); + err = run_command(&cmd); + if (err >= 0 || errno != ENOENT) + exit(err); + die("could not execute builtin %s", args->v[0]); } /* .. then try the external ones */ - execv_dashed_external(*argv); - - seen = unsorted_string_list_lookup(&cmd_list, *argv[0]); - if (seen) { - int i; - struct strbuf sb = STRBUF_INIT; - for (i = 0; i < cmd_list.nr; i++) { - struct string_list_item *item = &cmd_list.items[i]; - - strbuf_addf(&sb, "\n %s", item->string); - if (item == seen) - strbuf_addstr(&sb, " <=="); - else if (i == cmd_list.nr - 1) - strbuf_addstr(&sb, " ==>"); - } - die(_("alias loop detected: expansion of '%s' does" - " not terminate:%s"), cmd_list.items[0].string, sb.buf); - } - - string_list_append(&cmd_list, *argv[0]); + execv_dashed_external(args->v); /* * It could be an alias -- this works around the insanity * of overriding "git log" with "git show" by having * alias.log = show */ - if (!handle_alias(argcp, argv)) + if (!handle_alias(args, &expanded_aliases)) break; done_alias = 1; } - string_list_clear(&cmd_list, 0); + string_list_clear(&expanded_aliases, 0); return done_alias; } int cmd_main(int argc, const char **argv) { + struct strvec args = STRVEC_INIT; const char *cmd; int done_help = 0; @@ -916,8 +941,10 @@ int cmd_main(int argc, const char **argv) * that one cannot handle it. */ if (skip_prefix(cmd, "git-", &cmd)) { - argv[0] = cmd; - handle_builtin(argc, argv); + strvec_push(&args, cmd); + strvec_pushv(&args, argv + 1); + handle_builtin(&args); + strvec_clear(&args); die(_("cannot handle %s as a builtin"), cmd); } @@ -950,25 +977,34 @@ int cmd_main(int argc, const char **argv) */ setup_path(); + for (int i = 0; i < argc; i++) + strvec_push(&args, argv[i]); + while (1) { - int was_alias = run_argv(&argc, &argv); + int was_alias = run_argv(&args); if (errno != ENOENT) break; if (was_alias) { fprintf(stderr, _("expansion of alias '%s' failed; " "'%s' is not a git command\n"), - cmd, argv[0]); + cmd, args.v[0]); + strvec_clear(&args); exit(1); } if (!done_help) { - cmd = argv[0] = help_unknown_cmd(cmd); + char *assumed = help_unknown_cmd(cmd); + strvec_replace(&args, 0, assumed); + free(assumed); + cmd = args.v[0]; done_help = 1; - } else + } else { break; + } } fprintf(stderr, _("failed to run command '%s': %s\n"), cmd, strerror(errno)); + strvec_clear(&args); return 1; } |
