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