@matheusbsbs / matheus-bagliano / commits / 09ebad6fae
Fork 0

checkout: split off a function to peel away branchname arg

The code to parse and consume the tree name and "--" in commands such as "git checkout @{-1} -- '*.c'" is intimidatingly long. Split it out into a separate function and make it easier to skip on first reading by making the data it uses and produces more explicit. No functional change intended. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Nieder committed Feb 8, 2011 at 04:29 UTC 09ebad6faeec11c3dbad0bdaf95faed57be5dcba
1 file changed +131 -98
builtin/checkout.c
+131 -98
@@ -675,11 +675,123 @@ static const char *unique_tracking_name(const char *name)
675 return NULL;
676 }
677
678 +static int parse_branchname_arg(int argc, const char **argv,
679 + int dwim_new_local_branch_ok,
680 + struct branch_info *new,
681 + struct tree **source_tree,
682 + unsigned char rev[20],
683 + const char **new_branch)
684 +{
685 + int argcount = 0;
686 + unsigned char branch_rev[20];
687 + const char *arg;
688 + int has_dash_dash;
689 +
690 + /*
691 + * case 1: git checkout <ref> -- [<paths>]
692 + *
693 + * <ref> must be a valid tree, everything after the '--' must be
694 + * a path.
695 + *
696 + * case 2: git checkout -- [<paths>]
697 + *
698 + * everything after the '--' must be paths.
699 + *
700 + * case 3: git checkout <something> [<paths>]
701 + *
702 + * With no paths, if <something> is a commit, that is to
703 + * switch to the branch or detach HEAD at it. As a special case,
704 + * if <something> is A...B (missing A or B means HEAD but you can
705 + * omit at most one side), and if there is a unique merge base
706 + * between A and B, A...B names that merge base.
707 + *
708 + * With no paths, if <something> is _not_ a commit, no -t nor -b
709 + * was given, and there is a tracking branch whose name is
710 + * <something> in one and only one remote, then this is a short-hand
711 + * to fork local <something> from that remote tracking branch.
712 + *
713 + * Otherwise <something> shall not be ambiguous.
714 + * - If it's *only* a reference, treat it like case (1).
715 + * - If it's only a path, treat it like case (2).
716 + * - else: fail.
717 + *
718 + */
719 + if (!argc)
720 + return 0;
721 +
722 + if (!strcmp(argv[0], "--")) /* case (2) */
723 + return 1;
724 +
725 + arg = argv[0];
726 + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
727 +
728 + if (!strcmp(arg, "-"))
729 + arg = "@{-1}";
730 +
731 + if (get_sha1_mb(arg, rev)) {
732 + if (has_dash_dash) /* case (1) */
733 + die("invalid reference: %s", arg);
734 + if (dwim_new_local_branch_ok &&
735 + !check_filename(NULL, arg) &&
736 + argc == 1) {
737 + const char *remote = unique_tracking_name(arg);
738 + if (!remote || get_sha1(remote, rev))
739 + return argcount;
740 + *new_branch = arg;
741 + arg = remote;
742 + /* DWIMmed to create local branch */
743 + } else {
744 + return argcount;
745 + }
746 + }
747 +
748 + /* we can't end up being in (2) anymore, eat the argument */
749 + argcount++;
750 + argv++;
751 + argc--;
752 +
753 + new->name = arg;
754 + setup_branch_path(new);
755 +
756 + if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
757 + resolve_ref(new->path, branch_rev, 1, NULL))
758 + hashcpy(rev, branch_rev);
759 + else
760 + new->path = NULL; /* not an existing branch */
761 +
762 + new->commit = lookup_commit_reference_gently(rev, 1);
763 + if (!new->commit) {
764 + /* not a commit */
765 + *source_tree = parse_tree_indirect(rev);
766 + } else {
767 + parse_commit(new->commit);
768 + *source_tree = new->commit->tree;
769 + }
770 +
771 + if (!*source_tree) /* case (1): want a tree */
772 + die("reference is not a tree: %s", arg);
773 + if (!has_dash_dash) {/* case (3 -> 1) */
774 + /*
775 + * Do not complain the most common case
776 + * git checkout branch
777 + * even if there happen to be a file called 'branch';
778 + * it would be extremely annoying.
779 + */
780 + if (argc)
781 + verify_non_filename(NULL, arg);
782 + } else {
783 + argcount++;
784 + argv++;
785 + argc--;
786 + }
787 +
788 + return argcount;
789 +}
790 +
791 int cmd_checkout(int argc, const char **argv, const char *prefix)
792 {
793 struct checkout_opts opts;
681 - unsigned char rev[20], branch_rev[20];
682 - const char *arg;
794 + unsigned char rev[20];
795 struct branch_info new;
796 struct tree *source_tree = NULL;
797 char *conflict_style = NULL;
@@ -709,7 +821,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
821 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
822 OPT_END(),
823 };
712 - int has_dash_dash;
824
825 memset(&opts, 0, sizeof(opts));
826 memset(&new, 0, sizeof(new));
@@ -766,108 +877,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
877 die("git checkout: -f and -m are incompatible");
878
879 /*
769 - * case 1: git checkout <ref> -- [<paths>]
770 - *
771 - * <ref> must be a valid tree, everything after the '--' must be
772 - * a path.
773 - *
774 - * case 2: git checkout -- [<paths>]
775 - *
776 - * everything after the '--' must be paths.
880 + * Extract branch name from command line arguments, so
881 + * all that is left is pathspecs.
882 *
778 - * case 3: git checkout <something> [<paths>]
779 - *
780 - * With no paths, if <something> is a commit, that is to
781 - * switch to the branch or detach HEAD at it. As a special case,
782 - * if <something> is A...B (missing A or B means HEAD but you can
783 - * omit at most one side), and if there is a unique merge base
784 - * between A and B, A...B names that merge base.
785 - *
786 - * With no paths, if <something> is _not_ a commit, no -t nor -b
787 - * was given, and there is a tracking branch whose name is
788 - * <something> in one and only one remote, then this is a short-hand
789 - * to fork local <something> from that remote tracking branch.
883 + * Handle
884 *
791 - * Otherwise <something> shall not be ambiguous.
792 - * - If it's *only* a reference, treat it like case (1).
793 - * - If it's only a path, treat it like case (2).
794 - * - else: fail.
885 + * 1) git checkout <tree> -- [<paths>]
886 + * 2) git checkout -- [<paths>]
887 + * 3) git checkout <something> [<paths>]
888 *
889 + * including "last branch" syntax and DWIM-ery for names of
890 + * remote branches, erroring out for invalid or ambiguous cases.
891 */
892 if (argc) {
798 - if (!strcmp(argv[0], "--")) { /* case (2) */
799 - argv++;
800 - argc--;
801 - goto no_reference;
802 - }
803 -
804 - arg = argv[0];
805 - has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
806 -
807 - if (!strcmp(arg, "-"))
808 - arg = "@{-1}";
809 -
810 - if (get_sha1_mb(arg, rev)) {
811 - if (has_dash_dash) /* case (1) */
812 - die("invalid reference: %s", arg);
813 - if (!patch_mode &&
814 - dwim_new_local_branch &&
815 - opts.track == BRANCH_TRACK_UNSPECIFIED &&
816 - !opts.new_branch &&
817 - !check_filename(NULL, arg) &&
818 - argc == 1) {
819 - const char *remote = unique_tracking_name(arg);
820 - if (!remote || get_sha1(remote, rev))
821 - goto no_reference;
822 - opts.new_branch = arg;
823 - arg = remote;
824 - /* DWIMmed to create local branch */
825 - }
826 - else
827 - goto no_reference;
828 - }
829 -
830 - /* we can't end up being in (2) anymore, eat the argument */
831 - argv++;
832 - argc--;
833 -
834 - new.name = arg;
835 - setup_branch_path(&new);
836 -
837 - if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK &&
838 - resolve_ref(new.path, branch_rev, 1, NULL))
839 - hashcpy(rev, branch_rev);
840 - else
841 - new.path = NULL; /* not an existing branch */
842 -
843 - if (!(new.commit = lookup_commit_reference_gently(rev, 1))) {
844 - /* not a commit */
845 - source_tree = parse_tree_indirect(rev);
846 - } else {
847 - parse_commit(new.commit);
848 - source_tree = new.commit->tree;
849 - }
850 -
851 - if (!source_tree) /* case (1): want a tree */
852 - die("reference is not a tree: %s", arg);
853 - if (!has_dash_dash) {/* case (3 -> 1) */
854 - /*
855 - * Do not complain the most common case
856 - * git checkout branch
857 - * even if there happen to be a file called 'branch';
858 - * it would be extremely annoying.
859 - */
860 - if (argc)
861 - verify_non_filename(NULL, arg);
862 - }
863 - else {
864 - argv++;
865 - argc--;
866 - }
893 + int dwim_ok =
894 + !patch_mode &&
895 + dwim_new_local_branch &&
896 + opts.track == BRANCH_TRACK_UNSPECIFIED &&
897 + !opts.new_branch;
898 + int n = parse_branchname_arg(argc, argv, dwim_ok,
899 + &new, &source_tree, rev, &opts.new_branch);
900 + argv += n;
901 + argc -= n;
902 }
903
869 -no_reference:
870 -
904 if (opts.track == BRANCH_TRACK_UNSPECIFIED)
905 opts.track = git_branch_track;
906