THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

892 lines
26KB

  1. #include "builtin.h"
  2. #include "config.h"
  3. #include "exec-cmd.h"
  4. #include "help.h"
  5. #include "run-command.h"
  6. #include "alias.h"
  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. struct cmd_struct {
  19. const char *cmd;
  20. int (*fn)(int, const char **, const char *);
  21. unsigned int option;
  22. };
  23. const char git_usage_string[] =
  24. N_("git [--version] [--help] [-C <path>] [-c <name>=<value>]\n"
  25. " [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
  26. " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
  27. " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
  28. " <command> [<args>]");
  29. const char git_more_info_string[] =
  30. N_("'git help -a' and 'git help -g' list available subcommands and some\n"
  31. "concept guides. See 'git help <command>' or 'git help <concept>'\n"
  32. "to read about a specific subcommand or concept.\n"
  33. "See 'git help git' for an overview of the system.");
  34. static int use_pager = -1;
  35. static void list_builtins(struct string_list *list, unsigned int exclude_option);
  36. static void exclude_helpers_from_list(struct string_list *list)
  37. {
  38. int i = 0;
  39. while (i < list->nr) {
  40. if (strstr(list->items[i].string, "--"))
  41. unsorted_string_list_delete_item(list, i, 0);
  42. else
  43. i++;
  44. }
  45. }
  46. static int match_token(const char *spec, int len, const char *token)
  47. {
  48. int token_len = strlen(token);
  49. return len == token_len && !strncmp(spec, token, token_len);
  50. }
  51. static int list_cmds(const char *spec)
  52. {
  53. struct string_list list = STRING_LIST_INIT_DUP;
  54. int i;
  55. int nongit;
  56. /*
  57. * Set up the repository so we can pick up any repo-level config (like
  58. * completion.commands).
  59. */
  60. setup_git_directory_gently(&nongit);
  61. while (*spec) {
  62. const char *sep = strchrnul(spec, ',');
  63. int len = sep - spec;
  64. if (match_token(spec, len, "builtins"))
  65. list_builtins(&list, 0);
  66. else if (match_token(spec, len, "main"))
  67. list_all_main_cmds(&list);
  68. else if (match_token(spec, len, "others"))
  69. list_all_other_cmds(&list);
  70. else if (match_token(spec, len, "nohelpers"))
  71. exclude_helpers_from_list(&list);
  72. else if (match_token(spec, len, "alias"))
  73. list_aliases(&list);
  74. else if (match_token(spec, len, "config"))
  75. list_cmds_by_config(&list);
  76. else if (len > 5 && !strncmp(spec, "list-", 5)) {
  77. struct strbuf sb = STRBUF_INIT;
  78. strbuf_add(&sb, spec + 5, len - 5);
  79. list_cmds_by_category(&list, sb.buf);
  80. strbuf_release(&sb);
  81. }
  82. else
  83. die(_("unsupported command listing type '%s'"), spec);
  84. spec += len;
  85. if (*spec == ',')
  86. spec++;
  87. }
  88. for (i = 0; i < list.nr; i++)
  89. puts(list.items[i].string);
  90. string_list_clear(&list, 0);
  91. return 0;
  92. }
  93. static void commit_pager_choice(void)
  94. {
  95. switch (use_pager) {
  96. case 0:
  97. setenv("GIT_PAGER", "cat", 1);
  98. break;
  99. case 1:
  100. setup_pager();
  101. break;
  102. default:
  103. break;
  104. }
  105. }
  106. void setup_auto_pager(const char *cmd, int def)
  107. {
  108. if (use_pager != -1 || pager_in_use())
  109. return;
  110. use_pager = check_pager_config(cmd);
  111. if (use_pager == -1)
  112. use_pager = def;
  113. commit_pager_choice();
  114. }
  115. static int handle_options(const char ***argv, int *argc, int *envchanged)
  116. {
  117. const char **orig_argv = *argv;
  118. while (*argc > 0) {
  119. const char *cmd = (*argv)[0];
  120. if (cmd[0] != '-')
  121. break;
  122. /*
  123. * For legacy reasons, the "version" and "help"
  124. * commands can be written with "--" prepended
  125. * to make them look like flags.
  126. */
  127. if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
  128. break;
  129. /*
  130. * Check remaining flags.
  131. */
  132. if (skip_prefix(cmd, "--exec-path", &cmd)) {
  133. if (*cmd == '=')
  134. git_set_exec_path(cmd + 1);
  135. else {
  136. puts(git_exec_path());
  137. trace2_cmd_name("_query_");
  138. exit(0);
  139. }
  140. } else if (!strcmp(cmd, "--html-path")) {
  141. puts(system_path(GIT_HTML_PATH));
  142. trace2_cmd_name("_query_");
  143. exit(0);
  144. } else if (!strcmp(cmd, "--man-path")) {
  145. puts(system_path(GIT_MAN_PATH));
  146. trace2_cmd_name("_query_");
  147. exit(0);
  148. } else if (!strcmp(cmd, "--info-path")) {
  149. puts(system_path(GIT_INFO_PATH));
  150. trace2_cmd_name("_query_");
  151. exit(0);
  152. } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
  153. use_pager = 1;
  154. } else if (!strcmp(cmd, "-P") || !strcmp(cmd, "--no-pager")) {
  155. use_pager = 0;
  156. if (envchanged)
  157. *envchanged = 1;
  158. } else if (!strcmp(cmd, "--no-replace-objects")) {
  159. read_replace_refs = 0;
  160. setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
  161. if (envchanged)
  162. *envchanged = 1;
  163. } else if (!strcmp(cmd, "--git-dir")) {
  164. if (*argc < 2) {
  165. fprintf(stderr, _("no directory given for --git-dir\n" ));
  166. usage(git_usage_string);
  167. }
  168. setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
  169. if (envchanged)
  170. *envchanged = 1;
  171. (*argv)++;
  172. (*argc)--;
  173. } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
  174. setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
  175. if (envchanged)
  176. *envchanged = 1;
  177. } else if (!strcmp(cmd, "--namespace")) {
  178. if (*argc < 2) {
  179. fprintf(stderr, _("no namespace given for --namespace\n" ));
  180. usage(git_usage_string);
  181. }
  182. setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
  183. if (envchanged)
  184. *envchanged = 1;
  185. (*argv)++;
  186. (*argc)--;
  187. } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
  188. setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
  189. if (envchanged)
  190. *envchanged = 1;
  191. } else if (!strcmp(cmd, "--work-tree")) {
  192. if (*argc < 2) {
  193. fprintf(stderr, _("no directory given for --work-tree\n" ));
  194. usage(git_usage_string);
  195. }
  196. setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
  197. if (envchanged)
  198. *envchanged = 1;
  199. (*argv)++;
  200. (*argc)--;
  201. } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
  202. setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
  203. if (envchanged)
  204. *envchanged = 1;
  205. } else if (!strcmp(cmd, "--super-prefix")) {
  206. if (*argc < 2) {
  207. fprintf(stderr, _("no prefix given for --super-prefix\n" ));
  208. usage(git_usage_string);
  209. }
  210. setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
  211. if (envchanged)
  212. *envchanged = 1;
  213. (*argv)++;
  214. (*argc)--;
  215. } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
  216. setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
  217. if (envchanged)
  218. *envchanged = 1;
  219. } else if (!strcmp(cmd, "--bare")) {
  220. char *cwd = xgetcwd();
  221. is_bare_repository_cfg = 1;
  222. setenv(GIT_DIR_ENVIRONMENT, cwd, 0);
  223. free(cwd);
  224. setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
  225. if (envchanged)
  226. *envchanged = 1;
  227. } else if (!strcmp(cmd, "-c")) {
  228. if (*argc < 2) {
  229. fprintf(stderr, _("-c expects a configuration string\n" ));
  230. usage(git_usage_string);
  231. }
  232. git_config_push_parameter((*argv)[1]);
  233. (*argv)++;
  234. (*argc)--;
  235. } else if (!strcmp(cmd, "--literal-pathspecs")) {
  236. setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
  237. if (envchanged)
  238. *envchanged = 1;
  239. } else if (!strcmp(cmd, "--no-literal-pathspecs")) {
  240. setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
  241. if (envchanged)
  242. *envchanged = 1;
  243. } else if (!strcmp(cmd, "--glob-pathspecs")) {
  244. setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1);
  245. if (envchanged)
  246. *envchanged = 1;
  247. } else if (!strcmp(cmd, "--noglob-pathspecs")) {
  248. setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1);
  249. if (envchanged)
  250. *envchanged = 1;
  251. } else if (!strcmp(cmd, "--icase-pathspecs")) {
  252. setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
  253. if (envchanged)
  254. *envchanged = 1;
  255. } else if (!strcmp(cmd, "--no-optional-locks")) {
  256. setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1);
  257. if (envchanged)
  258. *envchanged = 1;
  259. } else if (!strcmp(cmd, "--shallow-file")) {
  260. (*argv)++;
  261. (*argc)--;
  262. set_alternate_shallow_file(the_repository, (*argv)[0], 1);
  263. if (envchanged)
  264. *envchanged = 1;
  265. } else if (!strcmp(cmd, "-C")) {
  266. if (*argc < 2) {
  267. fprintf(stderr, _("no directory given for -C\n" ));
  268. usage(git_usage_string);
  269. }
  270. if ((*argv)[1][0]) {
  271. if (chdir((*argv)[1]))
  272. die_errno("cannot change to '%s'", (*argv)[1]);
  273. if (envchanged)
  274. *envchanged = 1;
  275. }
  276. (*argv)++;
  277. (*argc)--;
  278. } else if (skip_prefix(cmd, "--list-cmds=", &cmd)) {
  279. trace2_cmd_name("_query_");
  280. if (!strcmp(cmd, "parseopt")) {
  281. struct string_list list = STRING_LIST_INIT_DUP;
  282. int i;
  283. list_builtins(&list, NO_PARSEOPT);
  284. for (i = 0; i < list.nr; i++)
  285. printf("%s ", list.items[i].string);
  286. string_list_clear(&list, 0);
  287. exit(0);
  288. } else {
  289. exit(list_cmds(cmd));
  290. }
  291. } else {
  292. fprintf(stderr, _("unknown option: %s\n"), cmd);
  293. usage(git_usage_string);
  294. }
  295. (*argv)++;
  296. (*argc)--;
  297. }
  298. return (*argv) - orig_argv;
  299. }
  300. static int handle_alias(int *argcp, const char ***argv)
  301. {
  302. int envchanged = 0, ret = 0, saved_errno = errno;
  303. int count, option_count;
  304. const char **new_argv;
  305. const char *alias_command;
  306. char *alias_string;
  307. alias_command = (*argv)[0];
  308. alias_string = alias_lookup(alias_command);
  309. if (alias_string) {
  310. if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
  311. fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
  312. alias_command, alias_string);
  313. if (alias_string[0] == '!') {
  314. struct child_process child = CHILD_PROCESS_INIT;
  315. int nongit_ok;
  316. /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
  317. setup_git_directory_gently(&nongit_ok);
  318. commit_pager_choice();
  319. child.use_shell = 1;
  320. child.trace2_child_class = "shell_alias";
  321. argv_array_push(&child.args, alias_string + 1);
  322. argv_array_pushv(&child.args, (*argv) + 1);
  323. trace2_cmd_alias(alias_command, child.args.argv);
  324. trace2_cmd_list_config();
  325. trace2_cmd_name("_run_shell_alias_");
  326. ret = run_command(&child);
  327. if (ret >= 0) /* normal exit */
  328. exit(ret);
  329. die_errno(_("while expanding alias '%s': '%s'"),
  330. alias_command, alias_string + 1);
  331. }
  332. count = split_cmdline(alias_string, &new_argv);
  333. if (count < 0)
  334. die(_("bad alias.%s string: %s"), alias_command,
  335. _(split_cmdline_strerror(count)));
  336. option_count = handle_options(&new_argv, &count, &envchanged);
  337. if (envchanged)
  338. die(_("alias '%s' changes environment variables.\n"
  339. "You can use '!git' in the alias to do this"),
  340. alias_command);
  341. MOVE_ARRAY(new_argv - option_count, new_argv, count);
  342. new_argv -= option_count;
  343. if (count < 1)
  344. die(_("empty alias for %s"), alias_command);
  345. if (!strcmp(alias_command, new_argv[0]))
  346. die(_("recursive alias: %s"), alias_command);
  347. trace_argv_printf(new_argv,
  348. "trace: alias expansion: %s =>",
  349. alias_command);
  350. REALLOC_ARRAY(new_argv, count + *argcp);
  351. /* insert after command name */
  352. COPY_ARRAY(new_argv + count, *argv + 1, *argcp);
  353. trace2_cmd_alias(alias_command, new_argv);
  354. trace2_cmd_list_config();
  355. *argv = new_argv;
  356. *argcp += count - 1;
  357. ret = 1;
  358. }
  359. errno = saved_errno;
  360. return ret;
  361. }
  362. static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
  363. {
  364. int status, help;
  365. struct stat st;
  366. const char *prefix;
  367. prefix = NULL;
  368. help = argc == 2 && !strcmp(argv[1], "-h");
  369. if (!help) {
  370. if (p->option & RUN_SETUP)
  371. prefix = setup_git_directory();
  372. else if (p->option & RUN_SETUP_GENTLY) {
  373. int nongit_ok;
  374. prefix = setup_git_directory_gently(&nongit_ok);
  375. }
  376. if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
  377. !(p->option & DELAY_PAGER_CONFIG))
  378. use_pager = check_pager_config(p->cmd);
  379. if (use_pager == -1 && p->option & USE_PAGER)
  380. use_pager = 1;
  381. if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
  382. startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
  383. trace_repo_setup(prefix);
  384. }
  385. commit_pager_choice();
  386. if (!help && get_super_prefix()) {
  387. if (!(p->option & SUPPORT_SUPER_PREFIX))
  388. die(_("%s doesn't support --super-prefix"), p->cmd);
  389. }
  390. if (!help && p->option & NEED_WORK_TREE)
  391. setup_work_tree();
  392. trace_argv_printf(argv, "trace: built-in: git");
  393. trace2_cmd_name(p->cmd);
  394. trace2_cmd_list_config();
  395. validate_cache_entries(the_repository->index);
  396. status = p->fn(argc, argv, prefix);
  397. validate_cache_entries(the_repository->index);
  398. if (status)
  399. return status;
  400. /* Somebody closed stdout? */
  401. if (fstat(fileno(stdout), &st))
  402. return 0;
  403. /* Ignore write errors for pipes and sockets.. */
  404. if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
  405. return 0;
  406. /* Check for ENOSPC and EIO errors.. */
  407. if (fflush(stdout))
  408. die_errno(_("write failure on standard output"));
  409. if (ferror(stdout))
  410. die(_("unknown write failure on standard output"));
  411. if (fclose(stdout))
  412. die_errno(_("close failed on standard output"));
  413. return 0;
  414. }
  415. static struct cmd_struct commands[] = {
  416. { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
  417. { "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
  418. { "annotate", cmd_annotate, RUN_SETUP | NO_PARSEOPT },
  419. { "apply", cmd_apply, RUN_SETUP_GENTLY },
  420. { "archive", cmd_archive, RUN_SETUP_GENTLY },
  421. { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
  422. { "blame", cmd_blame, RUN_SETUP },
  423. { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
  424. { "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
  425. { "cat-file", cmd_cat_file, RUN_SETUP },
  426. { "check-attr", cmd_check_attr, RUN_SETUP },
  427. { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
  428. { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
  429. { "check-ref-format", cmd_check_ref_format, NO_PARSEOPT },
  430. { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
  431. { "checkout-index", cmd_checkout_index,
  432. RUN_SETUP | NEED_WORK_TREE},
  433. { "cherry", cmd_cherry, RUN_SETUP },
  434. { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
  435. { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
  436. { "clone", cmd_clone },
  437. { "column", cmd_column, RUN_SETUP_GENTLY },
  438. { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
  439. { "commit-graph", cmd_commit_graph, RUN_SETUP },
  440. { "commit-tree", cmd_commit_tree, RUN_SETUP | NO_PARSEOPT },
  441. { "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
  442. { "count-objects", cmd_count_objects, RUN_SETUP },
  443. { "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
  444. { "describe", cmd_describe, RUN_SETUP },
  445. { "diff", cmd_diff, NO_PARSEOPT },
  446. { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
  447. { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
  448. { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
  449. { "difftool", cmd_difftool, RUN_SETUP_GENTLY },
  450. { "env--helper", cmd_env__helper },
  451. { "fast-export", cmd_fast_export, RUN_SETUP },
  452. { "fetch", cmd_fetch, RUN_SETUP },
  453. { "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
  454. { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
  455. { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
  456. { "format-patch", cmd_format_patch, RUN_SETUP },
  457. { "fsck", cmd_fsck, RUN_SETUP },
  458. { "fsck-objects", cmd_fsck, RUN_SETUP },
  459. { "gc", cmd_gc, RUN_SETUP },
  460. { "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
  461. { "grep", cmd_grep, RUN_SETUP_GENTLY },
  462. { "hash-object", cmd_hash_object },
  463. { "help", cmd_help },
  464. { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
  465. { "init", cmd_init_db },
  466. { "init-db", cmd_init_db },
  467. { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
  468. { "log", cmd_log, RUN_SETUP },
  469. { "ls-files", cmd_ls_files, RUN_SETUP },
  470. { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
  471. { "ls-tree", cmd_ls_tree, RUN_SETUP },
  472. { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
  473. { "mailsplit", cmd_mailsplit, NO_PARSEOPT },
  474. { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
  475. { "merge-base", cmd_merge_base, RUN_SETUP },
  476. { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
  477. { "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
  478. { "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
  479. { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
  480. { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
  481. { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
  482. { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
  483. { "merge-tree", cmd_merge_tree, RUN_SETUP | NO_PARSEOPT },
  484. { "mktag", cmd_mktag, RUN_SETUP | NO_PARSEOPT },
  485. { "mktree", cmd_mktree, RUN_SETUP },
  486. { "multi-pack-index", cmd_multi_pack_index, RUN_SETUP_GENTLY },
  487. { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
  488. { "name-rev", cmd_name_rev, RUN_SETUP },
  489. { "notes", cmd_notes, RUN_SETUP },
  490. { "pack-objects", cmd_pack_objects, RUN_SETUP },
  491. { "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT },
  492. { "pack-refs", cmd_pack_refs, RUN_SETUP },
  493. { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
  494. { "pickaxe", cmd_blame, RUN_SETUP },
  495. { "prune", cmd_prune, RUN_SETUP },
  496. { "prune-packed", cmd_prune_packed, RUN_SETUP },
  497. { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
  498. { "push", cmd_push, RUN_SETUP },
  499. { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
  500. { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
  501. { "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
  502. { "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
  503. { "receive-pack", cmd_receive_pack },
  504. { "reflog", cmd_reflog, RUN_SETUP },
  505. { "remote", cmd_remote, RUN_SETUP },
  506. { "remote-ext", cmd_remote_ext, NO_PARSEOPT },
  507. { "remote-fd", cmd_remote_fd, NO_PARSEOPT },
  508. { "repack", cmd_repack, RUN_SETUP },
  509. { "replace", cmd_replace, RUN_SETUP },
  510. { "rerere", cmd_rerere, RUN_SETUP },
  511. { "reset", cmd_reset, RUN_SETUP },
  512. { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
  513. { "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
  514. { "rev-parse", cmd_rev_parse, NO_PARSEOPT },
  515. { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
  516. { "rm", cmd_rm, RUN_SETUP },
  517. { "send-pack", cmd_send_pack, RUN_SETUP },
  518. { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
  519. { "show", cmd_show, RUN_SETUP },
  520. { "show-branch", cmd_show_branch, RUN_SETUP },
  521. { "show-index", cmd_show_index },
  522. { "show-ref", cmd_show_ref, RUN_SETUP },
  523. { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
  524. /*
  525. * NEEDSWORK: Until the builtin stash is thoroughly robust and no
  526. * longer needs redirection to the stash shell script this is kept as
  527. * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
  528. */
  529. { "stash", cmd_stash },
  530. { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
  531. { "stripspace", cmd_stripspace },
  532. { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
  533. { "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
  534. { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
  535. { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
  536. { "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
  537. { "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
  538. { "update-index", cmd_update_index, RUN_SETUP },
  539. { "update-ref", cmd_update_ref, RUN_SETUP },
  540. { "update-server-info", cmd_update_server_info, RUN_SETUP },
  541. { "upload-archive", cmd_upload_archive, NO_PARSEOPT },
  542. { "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
  543. { "upload-pack", cmd_upload_pack },
  544. { "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
  545. { "verify-commit", cmd_verify_commit, RUN_SETUP },
  546. { "verify-pack", cmd_verify_pack },
  547. { "verify-tag", cmd_verify_tag, RUN_SETUP },
  548. { "version", cmd_version },
  549. { "whatchanged", cmd_whatchanged, RUN_SETUP },
  550. { "worktree", cmd_worktree, RUN_SETUP | NO_PARSEOPT },
  551. { "write-tree", cmd_write_tree, RUN_SETUP },
  552. };
  553. static struct cmd_struct *get_builtin(const char *s)
  554. {
  555. int i;
  556. for (i = 0; i < ARRAY_SIZE(commands); i++) {
  557. struct cmd_struct *p = commands + i;
  558. if (!strcmp(s, p->cmd))
  559. return p;
  560. }
  561. return NULL;
  562. }
  563. int is_builtin(const char *s)
  564. {
  565. return !!get_builtin(s);
  566. }
  567. static void list_builtins(struct string_list *out, unsigned int exclude_option)
  568. {
  569. int i;
  570. for (i = 0; i < ARRAY_SIZE(commands); i++) {
  571. if (exclude_option &&
  572. (commands[i].option & exclude_option))
  573. continue;
  574. string_list_append(out, commands[i].cmd);
  575. }
  576. }
  577. #ifdef STRIP_EXTENSION
  578. static void strip_extension(const char **argv)
  579. {
  580. size_t len;
  581. if (strip_suffix(argv[0], STRIP_EXTENSION, &len))
  582. argv[0] = xmemdupz(argv[0], len);
  583. }
  584. #else
  585. #define strip_extension(cmd)
  586. #endif
  587. static void handle_builtin(int argc, const char **argv)
  588. {
  589. struct argv_array args = ARGV_ARRAY_INIT;
  590. const char *cmd;
  591. struct cmd_struct *builtin;
  592. strip_extension(argv);
  593. cmd = argv[0];
  594. /* Turn "git cmd --help" into "git help --exclude-guides cmd" */
  595. if (argc > 1 && !strcmp(argv[1], "--help")) {
  596. int i;
  597. argv[1] = argv[0];
  598. argv[0] = cmd = "help";
  599. for (i = 0; i < argc; i++) {
  600. argv_array_push(&args, argv[i]);
  601. if (!i)
  602. argv_array_push(&args, "--exclude-guides");
  603. }
  604. argc++;
  605. argv = args.argv;
  606. }
  607. builtin = get_builtin(cmd);
  608. if (builtin)
  609. exit(run_builtin(builtin, argc, argv));
  610. argv_array_clear(&args);
  611. }
  612. static void execv_dashed_external(const char **argv)
  613. {
  614. struct child_process cmd = CHILD_PROCESS_INIT;
  615. int status;
  616. if (get_super_prefix())
  617. die(_("%s doesn't support --super-prefix"), argv[0]);
  618. if (use_pager == -1 && !is_builtin(argv[0]))
  619. use_pager = check_pager_config(argv[0]);
  620. commit_pager_choice();
  621. argv_array_pushf(&cmd.args, "git-%s", argv[0]);
  622. argv_array_pushv(&cmd.args, argv + 1);
  623. cmd.clean_on_exit = 1;
  624. cmd.wait_after_clean = 1;
  625. cmd.silent_exec_failure = 1;
  626. cmd.trace2_child_class = "dashed";
  627. trace2_cmd_name("_run_dashed_");
  628. /*
  629. * The code in run_command() logs trace2 child_start/child_exit
  630. * events, so we do not need to report exec/exec_result events here.
  631. */
  632. trace_argv_printf(cmd.args.argv, "trace: exec:");
  633. /*
  634. * If we fail because the command is not found, it is
  635. * OK to return. Otherwise, we just pass along the status code,
  636. * or our usual generic code if we were not even able to exec
  637. * the program.
  638. */
  639. status = run_command(&cmd);
  640. /*
  641. * If the child process ran and we are now going to exit, emit a
  642. * generic string as our trace2 command verb to indicate that we
  643. * launched a dashed command.
  644. */
  645. if (status >= 0)
  646. exit(status);
  647. else if (errno != ENOENT)
  648. exit(128);
  649. }
  650. static int run_argv(int *argcp, const char ***argv)
  651. {
  652. int done_alias = 0;
  653. struct string_list cmd_list = STRING_LIST_INIT_NODUP;
  654. struct string_list_item *seen;
  655. while (1) {
  656. /*
  657. * If we tried alias and futzed with our environment,
  658. * it no longer is safe to invoke builtins directly in
  659. * general. We have to spawn them as dashed externals.
  660. *
  661. * NEEDSWORK: if we can figure out cases
  662. * where it is safe to do, we can avoid spawning a new
  663. * process.
  664. */
  665. if (!done_alias)
  666. handle_builtin(*argcp, *argv);
  667. else if (get_builtin(**argv)) {
  668. struct argv_array args = ARGV_ARRAY_INIT;
  669. int i;
  670. /*
  671. * The current process is committed to launching a
  672. * child process to run the command named in (**argv)
  673. * and exiting. Log a generic string as the trace2
  674. * command verb to indicate this. Note that the child
  675. * process will log the actual verb when it runs.
  676. */
  677. trace2_cmd_name("_run_git_alias_");
  678. if (get_super_prefix())
  679. die("%s doesn't support --super-prefix", **argv);
  680. commit_pager_choice();
  681. argv_array_push(&args, "git");
  682. for (i = 0; i < *argcp; i++)
  683. argv_array_push(&args, (*argv)[i]);
  684. trace_argv_printf(args.argv, "trace: exec:");
  685. /*
  686. * if we fail because the command is not found, it is
  687. * OK to return. Otherwise, we just pass along the status code.
  688. */
  689. i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
  690. RUN_CLEAN_ON_EXIT, "git_alias");
  691. if (i >= 0 || errno != ENOENT)
  692. exit(i);
  693. die("could not execute builtin %s", **argv);
  694. }
  695. /* .. then try the external ones */
  696. execv_dashed_external(*argv);
  697. seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
  698. if (seen) {
  699. int i;
  700. struct strbuf sb = STRBUF_INIT;
  701. for (i = 0; i < cmd_list.nr; i++) {
  702. struct string_list_item *item = &cmd_list.items[i];
  703. strbuf_addf(&sb, "\n %s", item->string);
  704. if (item == seen)
  705. strbuf_addstr(&sb, " <==");
  706. else if (i == cmd_list.nr - 1)
  707. strbuf_addstr(&sb, " ==>");
  708. }
  709. die(_("alias loop detected: expansion of '%s' does"
  710. " not terminate:%s"), cmd_list.items[0].string, sb.buf);
  711. }
  712. string_list_append(&cmd_list, *argv[0]);
  713. /*
  714. * It could be an alias -- this works around the insanity
  715. * of overriding "git log" with "git show" by having
  716. * alias.log = show
  717. */
  718. if (!handle_alias(argcp, argv))
  719. break;
  720. done_alias = 1;
  721. }
  722. string_list_clear(&cmd_list, 0);
  723. return done_alias;
  724. }
  725. int cmd_main(int argc, const char **argv)
  726. {
  727. const char *cmd;
  728. int done_help = 0;
  729. cmd = argv[0];
  730. if (!cmd)
  731. cmd = "git-help";
  732. else {
  733. const char *slash = find_last_dir_sep(cmd);
  734. if (slash)
  735. cmd = slash + 1;
  736. }
  737. trace_command_performance(argv);
  738. /*
  739. * "git-xxxx" is the same as "git xxxx", but we obviously:
  740. *
  741. * - cannot take flags in between the "git" and the "xxxx".
  742. * - cannot execute it externally (since it would just do
  743. * the same thing over again)
  744. *
  745. * So we just directly call the builtin handler, and die if
  746. * that one cannot handle it.
  747. */
  748. if (skip_prefix(cmd, "git-", &cmd)) {
  749. argv[0] = cmd;
  750. handle_builtin(argc, argv);
  751. die(_("cannot handle %s as a builtin"), cmd);
  752. }
  753. /* Look for flags.. */
  754. argv++;
  755. argc--;
  756. handle_options(&argv, &argc, NULL);
  757. if (argc > 0) {
  758. /* translate --help and --version into commands */
  759. skip_prefix(argv[0], "--", &argv[0]);
  760. } else {
  761. /* The user didn't specify a command; give them help */
  762. commit_pager_choice();
  763. printf(_("usage: %s\n\n"), git_usage_string);
  764. list_common_cmds_help();
  765. printf("\n%s\n", _(git_more_info_string));
  766. exit(1);
  767. }
  768. cmd = argv[0];
  769. /*
  770. * We use PATH to find git commands, but we prepend some higher
  771. * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
  772. * environment, and the $(gitexecdir) from the Makefile at build
  773. * time.
  774. */
  775. setup_path();
  776. while (1) {
  777. int was_alias = run_argv(&argc, &argv);
  778. if (errno != ENOENT)
  779. break;
  780. if (was_alias) {
  781. fprintf(stderr, _("expansion of alias '%s' failed; "
  782. "'%s' is not a git command\n"),
  783. cmd, argv[0]);
  784. exit(1);
  785. }
  786. if (!done_help) {
  787. cmd = argv[0] = help_unknown_cmd(cmd);
  788. done_help = 1;
  789. } else
  790. break;
  791. }
  792. fprintf(stderr, _("failed to run command '%s': %s\n"),
  793. cmd, strerror(errno));
  794. return 1;
  795. }