@matheusbsbs / matheus-bagliano / commits / 309be813c9
Fork 0

update-index: migrate to parse-options API

--refresh and --really-refresh accept flags (like -q) and modify an error indicator. It might make sense to make the error indicator global, but just pass the flags and a pointer to the error indicator in a struct instead. --cacheinfo wants 3 arguments. Use the OPTION_LOWLEVEL_CALLBACK extension to grab them and PARSE_OPT_NOARG to disallow the "sticked" --cacheinfo=foo form. (The resulting message $ git update-index --cacheinfo=foo error: option `cacheinfo' takes no value is unfortunately incorrect.) --assume-unchanged and --no-assume-unchanged probably should use the OPT_UYN feature; but use a callback for now so the existing MARK_FLAG and UNMARK_FLAG values can be used. --stdin and --index-info are still constrained to be the last argument (implemented using the OPTION_LOWLEVEL_CALLBACK extension). --unresolve and --again consume all arguments that come after them (also using OPTION_LOWLEVEL_CALLBACK). The order of options matters. Each path on the command line is affected only by the options that come before it. A custom argument-parsing loop with parse_options_step() brings that about. In exchange for all the fuss, we get the usual perks: support for un-sticked options, better usage error messages, more useful -h output, and argument parsing code that should be easier to tweak in the future. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Nieder committed Dec 1, 2010 at 17:34 UTC 309be813c9b75fed70f3a03bf803bf47c6670cfc
1 file changed +243 -149
builtin/update-index.c
+243 -149
@@ -10,6 +10,7 @@
10 #include "builtin.h"
11 #include "refs.h"
12 #include "resolve-undo.h"
13 +#include "parse-options.h"
14
15 /*
16 * Default to not allowing changes to the list of files. The
@@ -397,8 +398,10 @@ static void read_index_info(int line_termination)
398 strbuf_release(&uq);
399 }
400
400 -static const char update_index_usage[] =
401 -"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] [<file>...]";
401 +static const char * const update_index_usage[] = {
402 + "git update-index [options] [--] [<file>...]",
403 + NULL
404 +};
405
406 static unsigned char head_sha1[20];
407 static unsigned char merge_head_sha1[20];
@@ -578,16 +581,211 @@ static int do_reupdate(int ac, const char **av,
581 return 0;
582 }
583
584 +struct refresh_params {
585 + unsigned int flags;
586 + int *has_errors;
587 +};
588 +
589 +static int refresh(struct refresh_params *o, unsigned int flag)
590 +{
591 + setup_work_tree();
592 + *o->has_errors |= refresh_cache(o->flags | flag);
593 + return 0;
594 +}
595 +
596 +static int refresh_callback(const struct option *opt,
597 + const char *arg, int unset)
598 +{
599 + return refresh(opt->value, 0);
600 +}
601 +
602 +static int really_refresh_callback(const struct option *opt,
603 + const char *arg, int unset)
604 +{
605 + return refresh(opt->value, REFRESH_REALLY);
606 +}
607 +
608 +static int chmod_callback(const struct option *opt,
609 + const char *arg, int unset)
610 +{
611 + char *flip = opt->value;
612 + if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
613 + return error("option 'chmod' expects \"+x\" or \"-x\"");
614 + *flip = arg[0];
615 + return 0;
616 +}
617 +
618 +static int resolve_undo_clear_callback(const struct option *opt,
619 + const char *arg, int unset)
620 +{
621 + resolve_undo_clear();
622 + return 0;
623 +}
624 +
625 +static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
626 + const struct option *opt, int unset)
627 +{
628 + unsigned char sha1[20];
629 + unsigned int mode;
630 +
631 + if (ctx->argc <= 3)
632 + return error("option 'cacheinfo' expects three arguments");
633 + if (strtoul_ui(*++ctx->argv, 8, &mode) ||
634 + get_sha1_hex(*++ctx->argv, sha1) ||
635 + add_cacheinfo(mode, sha1, *++ctx->argv, 0))
636 + die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
637 + ctx->argc -= 3;
638 + return 0;
639 +}
640 +
641 +static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
642 + const struct option *opt, int unset)
643 +{
644 + int *line_termination = opt->value;
645 +
646 + if (ctx->argc != 1)
647 + return error("option '%s' must be the last argument", opt->long_name);
648 + allow_add = allow_replace = allow_remove = 1;
649 + read_index_info(*line_termination);
650 + return 0;
651 +}
652 +
653 +static int stdin_callback(struct parse_opt_ctx_t *ctx,
654 + const struct option *opt, int unset)
655 +{
656 + int *read_from_stdin = opt->value;
657 +
658 + if (ctx->argc != 1)
659 + return error("option '%s' must be the last argument", opt->long_name);
660 + *read_from_stdin = 1;
661 + return 0;
662 +}
663 +
664 +static int unresolve_callback(struct parse_opt_ctx_t *ctx,
665 + const struct option *opt, int flags)
666 +{
667 + int *has_errors = opt->value;
668 + const char *prefix = startup_info->prefix;
669 +
670 + /* consume remaining arguments. */
671 + *has_errors = do_unresolve(ctx->argc, ctx->argv,
672 + prefix, prefix ? strlen(prefix) : 0);
673 + if (*has_errors)
674 + active_cache_changed = 0;
675 +
676 + ctx->argv += ctx->argc - 1;
677 + ctx->argc = 1;
678 + return 0;
679 +}
680 +
681 +static int reupdate_callback(struct parse_opt_ctx_t *ctx,
682 + const struct option *opt, int flags)
683 +{
684 + int *has_errors = opt->value;
685 + const char *prefix = startup_info->prefix;
686 +
687 + /* consume remaining arguments. */
688 + setup_work_tree();
689 + *has_errors = do_reupdate(ctx->argc, ctx->argv,
690 + prefix, prefix ? strlen(prefix) : 0);
691 + if (*has_errors)
692 + active_cache_changed = 0;
693 +
694 + ctx->argv += ctx->argc - 1;
695 + ctx->argc = 1;
696 + return 0;
697 +}
698 +
699 int cmd_update_index(int argc, const char **argv, const char *prefix)
700 {
583 - int i, newfd, entries, has_errors = 0, line_termination = '\n';
584 - int allow_options = 1;
701 + int newfd, entries, has_errors = 0, line_termination = '\n';
702 int read_from_stdin = 0;
703 int prefix_length = prefix ? strlen(prefix) : 0;
704 char set_executable_bit = 0;
588 - unsigned int refresh_flags = 0;
705 + struct refresh_params refresh_args = {0, &has_errors};
706 int lock_error = 0;
707 struct lock_file *lock_file;
708 + struct parse_opt_ctx_t ctx;
709 + int parseopt_state = PARSE_OPT_UNKNOWN;
710 + struct option options[] = {
711 + OPT_BIT('q', NULL, &refresh_args.flags,
712 + "continue refresh even when index needs update",
713 + REFRESH_QUIET),
714 + OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
715 + "refresh: ignore submodules",
716 + REFRESH_IGNORE_SUBMODULES),
717 + OPT_SET_INT(0, "add", &allow_add,
718 + "do not ignore new files", 1),
719 + OPT_SET_INT(0, "replace", &allow_replace,
720 + "let files replace directories and vice-versa", 1),
721 + OPT_SET_INT(0, "remove", &allow_remove,
722 + "notice files missing from worktree", 1),
723 + OPT_BIT(0, "unmerged", &refresh_args.flags,
724 + "refresh even if index contains unmerged entries",
725 + REFRESH_UNMERGED),
726 + {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
727 + "refresh stat information",
728 + PARSE_OPT_NOARG | PARSE_OPT_NONEG,
729 + refresh_callback},
730 + {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
731 + "like --refresh, but ignore assume-unchanged setting",
732 + PARSE_OPT_NOARG | PARSE_OPT_NONEG,
733 + really_refresh_callback},
734 + {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
735 + "<mode> <object> <path>",
736 + "add the specified entry to the index",
737 + PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
738 + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
739 + (parse_opt_cb *) cacheinfo_callback},
740 + {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
741 + "override the executable bit of the listed files",
742 + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
743 + chmod_callback},
744 + {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
745 + "mark files as \"not changing\"",
746 + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
747 + {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
748 + "clear assumed-unchanged bit",
749 + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
750 + {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
751 + "mark files as \"index-only\"",
752 + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
753 + {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
754 + "clear skip-worktree bit",
755 + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
756 + OPT_SET_INT(0, "info-only", &info_only,
757 + "add to index only; do not add content to object database", 1),
758 + OPT_SET_INT(0, "force-remove", &force_remove,
759 + "remove named paths even if present in worktree", 1),
760 + OPT_SET_INT('z', NULL, &line_termination,
761 + "with --stdin: input lines are terminated by null bytes", '\0'),
762 + {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
763 + "read list of paths to be updated from standard input",
764 + PARSE_OPT_NONEG | PARSE_OPT_NOARG,
765 + (parse_opt_cb *) stdin_callback},
766 + {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
767 + "add entries from standard input to the index",
768 + PARSE_OPT_NONEG | PARSE_OPT_NOARG,
769 + (parse_opt_cb *) stdin_cacheinfo_callback},
770 + {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
771 + "repopulate stages #2 and #3 for the listed paths",
772 + PARSE_OPT_NONEG | PARSE_OPT_NOARG,
773 + (parse_opt_cb *) unresolve_callback},
774 + {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
775 + "only update entries that differ from HEAD",
776 + PARSE_OPT_NONEG | PARSE_OPT_NOARG,
777 + (parse_opt_cb *) reupdate_callback},
778 + OPT_BIT(0, "ignore-missing", &refresh_args.flags,
779 + "ignore files missing from worktree",
780 + REFRESH_IGNORE_MISSING),
781 + OPT_SET_INT(0, "verbose", &verbose,
782 + "report actions to standard output", 1),
783 + {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
784 + "(for porcelains) forget saved unresolved conflicts",
785 + PARSE_OPT_NOARG | PARSE_OPT_NONEG,
786 + resolve_undo_clear_callback},
787 + OPT_END()
788 + };
789
790 git_config(git_default_config, NULL);
791
@@ -602,151 +800,48 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
800 if (entries < 0)
801 die("cache corrupted");
802
605 - for (i = 1 ; i < argc; i++) {
606 - const char *path = argv[i];
607 - const char *p;
803 + /*
804 + * Custom copy of parse_options() because we want to handle
805 + * filename arguments as they come.
806 + */
807 + parse_options_start(&ctx, argc, argv, prefix,
808 + options, PARSE_OPT_STOP_AT_NON_OPTION);
809 + while (ctx.argc) {
810 + if (parseopt_state != PARSE_OPT_DONE)
811 + parseopt_state = parse_options_step(&ctx, options,
812 + update_index_usage);
813 + if (!ctx.argc)
814 + break;
815 + switch (parseopt_state) {
816 + case PARSE_OPT_HELP:
817 + exit(129);
818 + case PARSE_OPT_NON_OPTION:
819 + case PARSE_OPT_DONE:
820 + {
821 + const char *path = ctx.argv[0];
822 + const char *p;
823
609 - if (allow_options && *path == '-') {
610 - if (!strcmp(path, "--")) {
611 - allow_options = 0;
612 - continue;
613 - }
614 - if (!strcmp(path, "-q")) {
615 - refresh_flags |= REFRESH_QUIET;
616 - continue;
617 - }
618 - if (!strcmp(path, "--ignore-submodules")) {
619 - refresh_flags |= REFRESH_IGNORE_SUBMODULES;
620 - continue;
621 - }
622 - if (!strcmp(path, "--add")) {
623 - allow_add = 1;
624 - continue;
625 - }
626 - if (!strcmp(path, "--replace")) {
627 - allow_replace = 1;
628 - continue;
629 - }
630 - if (!strcmp(path, "--remove")) {
631 - allow_remove = 1;
632 - continue;
633 - }
634 - if (!strcmp(path, "--unmerged")) {
635 - refresh_flags |= REFRESH_UNMERGED;
636 - continue;
637 - }
638 - if (!strcmp(path, "--refresh")) {
639 - setup_work_tree();
640 - has_errors |= refresh_cache(refresh_flags);
641 - continue;
642 - }
643 - if (!strcmp(path, "--really-refresh")) {
644 - setup_work_tree();
645 - has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
646 - continue;
647 - }
648 - if (!strcmp(path, "--cacheinfo")) {
649 - unsigned char sha1[20];
650 - unsigned int mode;
651 -
652 - if (i+3 >= argc)
653 - die("git update-index: --cacheinfo <mode> <sha1> <path>");
654 -
655 - if (strtoul_ui(argv[i+1], 8, &mode) ||
656 - get_sha1_hex(argv[i+2], sha1) ||
657 - add_cacheinfo(mode, sha1, argv[i+3], 0))
658 - die("git update-index: --cacheinfo"
659 - " cannot add %s", argv[i+3]);
660 - i += 3;
661 - continue;
662 - }
663 - if (!strcmp(path, "--chmod=-x") ||
664 - !strcmp(path, "--chmod=+x")) {
665 - if (argc <= i+1)
666 - die("git update-index: %s <path>", path);
667 - set_executable_bit = path[8];
668 - continue;
669 - }
670 - if (!strcmp(path, "--assume-unchanged")) {
671 - mark_valid_only = MARK_FLAG;
672 - continue;
673 - }
674 - if (!strcmp(path, "--no-assume-unchanged")) {
675 - mark_valid_only = UNMARK_FLAG;
676 - continue;
677 - }
678 - if (!strcmp(path, "--no-skip-worktree")) {
679 - mark_skip_worktree_only = UNMARK_FLAG;
680 - continue;
681 - }
682 - if (!strcmp(path, "--skip-worktree")) {
683 - mark_skip_worktree_only = MARK_FLAG;
684 - continue;
685 - }
686 - if (!strcmp(path, "--info-only")) {
687 - info_only = 1;
688 - continue;
689 - }
690 - if (!strcmp(path, "--force-remove")) {
691 - force_remove = 1;
692 - continue;
693 - }
694 - if (!strcmp(path, "-z")) {
695 - line_termination = 0;
696 - continue;
697 - }
698 - if (!strcmp(path, "--stdin")) {
699 - if (i != argc - 1)
700 - die("--stdin must be at the end");
701 - read_from_stdin = 1;
702 - break;
703 - }
704 - if (!strcmp(path, "--index-info")) {
705 - if (i != argc - 1)
706 - die("--index-info must be at the end");
707 - allow_add = allow_replace = allow_remove = 1;
708 - read_index_info(line_termination);
709 - break;
710 - }
711 - if (!strcmp(path, "--unresolve")) {
712 - has_errors = do_unresolve(argc - i, argv + i,
713 - prefix, prefix_length);
714 - if (has_errors)
715 - active_cache_changed = 0;
716 - goto finish;
717 - }
718 - if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
719 - setup_work_tree();
720 - has_errors = do_reupdate(argc - i, argv + i,
721 - prefix, prefix_length);
722 - if (has_errors)
723 - active_cache_changed = 0;
724 - goto finish;
725 - }
726 - if (!strcmp(path, "--ignore-missing")) {
727 - refresh_flags |= REFRESH_IGNORE_MISSING;
728 - continue;
729 - }
730 - if (!strcmp(path, "--verbose")) {
731 - verbose = 1;
732 - continue;
733 - }
734 - if (!strcmp(path, "--clear-resolve-undo")) {
735 - resolve_undo_clear();
736 - continue;
737 - }
738 - if (!strcmp(path, "-h") || !strcmp(path, "--help"))
739 - usage(update_index_usage);
740 - die("unknown option %s", path);
824 + setup_work_tree();
825 + p = prefix_path(prefix, prefix_length, path);
826 + update_one(p, NULL, 0);
827 + if (set_executable_bit)
828 + chmod_path(set_executable_bit, p);
829 + if (p < path || p > path + strlen(path))
830 + free((char *)p);
831 + ctx.argc--;
832 + ctx.argv++;
833 + break;
834 + }
835 + case PARSE_OPT_UNKNOWN:
836 + if (ctx.argv[0][1] == '-')
837 + error("unknown option '%s'", ctx.argv[0] + 2);
838 + else
839 + error("unknown switch '%c'", *ctx.opt);
840 + usage_with_options(update_index_usage, options);
841 }
742 - setup_work_tree();
743 - p = prefix_path(prefix, prefix_length, path);
744 - update_one(p, NULL, 0);
745 - if (set_executable_bit)
746 - chmod_path(set_executable_bit, p);
747 - if (p < path || p > path + strlen(path))
748 - free((char *)p);
842 }
843 + argc = parse_options_end(&ctx);
844 +
845 if (read_from_stdin) {
846 struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
847
@@ -770,10 +865,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
865 strbuf_release(&buf);
866 }
867
773 - finish:
868 if (active_cache_changed) {
869 if (newfd < 0) {
776 - if (refresh_flags & REFRESH_QUIET)
870 + if (refresh_args.flags & REFRESH_QUIET)
871 exit(128);
872 unable_to_lock_index_die(get_index_file(), lock_error);
873 }