@@ -30,6 +30,7 @@ struct checkout_opts {
30
30
int quiet ;
31
31
int merge ;
32
32
int force ;
33
+ int force_detach ;
33
34
int writeout_stage ;
34
35
int writeout_error ;
35
36
@@ -541,7 +542,17 @@ static void update_refs_for_switch(struct checkout_opts *opts,
541
542
strbuf_addf (& msg , "checkout: moving from %s to %s" ,
542
543
old_desc ? old_desc : "(invalid)" , new -> name );
543
544
544
- if (new -> path ) {
545
+ if (!strcmp (new -> name , "HEAD" ) && !new -> path && !opts -> force_detach ) {
546
+ /* Nothing to do. */
547
+ } else if (opts -> force_detach || !new -> path ) { /* No longer on any branch. */
548
+ update_ref (msg .buf , "HEAD" , new -> commit -> object .sha1 , NULL ,
549
+ REF_NODEREF , DIE_ON_ERR );
550
+ if (!opts -> quiet ) {
551
+ if (old -> path && advice_detached_head )
552
+ detach_advice (old -> path , new -> name );
553
+ describe_detached_head ("HEAD is now at" , new -> commit );
554
+ }
555
+ } else if (new -> path ) { /* Switch branches. */
545
556
create_symref ("HEAD" , new -> path , msg .buf );
546
557
if (!opts -> quiet ) {
547
558
if (old -> path && !strcmp (new -> path , old -> path ))
@@ -563,18 +574,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,
563
574
if (!file_exists (ref_file ) && file_exists (log_file ))
564
575
remove_path (log_file );
565
576
}
566
- } else if (strcmp (new -> name , "HEAD" )) {
567
- update_ref (msg .buf , "HEAD" , new -> commit -> object .sha1 , NULL ,
568
- REF_NODEREF , DIE_ON_ERR );
569
- if (!opts -> quiet ) {
570
- if (old -> path && advice_detached_head )
571
- detach_advice (old -> path , new -> name );
572
- describe_detached_head ("HEAD is now at" , new -> commit );
573
- }
574
577
}
575
578
remove_branch_state ();
576
579
strbuf_release (& msg );
577
- if (!opts -> quiet && (new -> path || !strcmp (new -> name , "HEAD" )))
580
+ if (!opts -> quiet &&
581
+ (new -> path || (!opts -> force_detach && !strcmp (new -> name , "HEAD" ))))
578
582
report_tracking (new );
579
583
}
580
584
@@ -675,11 +679,123 @@ static const char *unique_tracking_name(const char *name)
675
679
return NULL ;
676
680
}
677
681
682
+ static int parse_branchname_arg (int argc , const char * * argv ,
683
+ int dwim_new_local_branch_ok ,
684
+ struct branch_info * new ,
685
+ struct tree * * source_tree ,
686
+ unsigned char rev [20 ],
687
+ const char * * new_branch )
688
+ {
689
+ int argcount = 0 ;
690
+ unsigned char branch_rev [20 ];
691
+ const char * arg ;
692
+ int has_dash_dash ;
693
+
694
+ /*
695
+ * case 1: git checkout <ref> -- [<paths>]
696
+ *
697
+ * <ref> must be a valid tree, everything after the '--' must be
698
+ * a path.
699
+ *
700
+ * case 2: git checkout -- [<paths>]
701
+ *
702
+ * everything after the '--' must be paths.
703
+ *
704
+ * case 3: git checkout <something> [<paths>]
705
+ *
706
+ * With no paths, if <something> is a commit, that is to
707
+ * switch to the branch or detach HEAD at it. As a special case,
708
+ * if <something> is A...B (missing A or B means HEAD but you can
709
+ * omit at most one side), and if there is a unique merge base
710
+ * between A and B, A...B names that merge base.
711
+ *
712
+ * With no paths, if <something> is _not_ a commit, no -t nor -b
713
+ * was given, and there is a tracking branch whose name is
714
+ * <something> in one and only one remote, then this is a short-hand
715
+ * to fork local <something> from that remote-tracking branch.
716
+ *
717
+ * Otherwise <something> shall not be ambiguous.
718
+ * - If it's *only* a reference, treat it like case (1).
719
+ * - If it's only a path, treat it like case (2).
720
+ * - else: fail.
721
+ *
722
+ */
723
+ if (!argc )
724
+ return 0 ;
725
+
726
+ if (!strcmp (argv [0 ], "--" )) /* case (2) */
727
+ return 1 ;
728
+
729
+ arg = argv [0 ];
730
+ has_dash_dash = (argc > 1 ) && !strcmp (argv [1 ], "--" );
731
+
732
+ if (!strcmp (arg , "-" ))
733
+ arg = "@{-1}" ;
734
+
735
+ if (get_sha1_mb (arg , rev )) {
736
+ if (has_dash_dash ) /* case (1) */
737
+ die ("invalid reference: %s" , arg );
738
+ if (dwim_new_local_branch_ok &&
739
+ !check_filename (NULL , arg ) &&
740
+ argc == 1 ) {
741
+ const char * remote = unique_tracking_name (arg );
742
+ if (!remote || get_sha1 (remote , rev ))
743
+ return argcount ;
744
+ * new_branch = arg ;
745
+ arg = remote ;
746
+ /* DWIMmed to create local branch */
747
+ } else {
748
+ return argcount ;
749
+ }
750
+ }
751
+
752
+ /* we can't end up being in (2) anymore, eat the argument */
753
+ argcount ++ ;
754
+ argv ++ ;
755
+ argc -- ;
756
+
757
+ new -> name = arg ;
758
+ setup_branch_path (new );
759
+
760
+ if (check_ref_format (new -> path ) == CHECK_REF_FORMAT_OK &&
761
+ resolve_ref (new -> path , branch_rev , 1 , NULL ))
762
+ hashcpy (rev , branch_rev );
763
+ else
764
+ new -> path = NULL ; /* not an existing branch */
765
+
766
+ new -> commit = lookup_commit_reference_gently (rev , 1 );
767
+ if (!new -> commit ) {
768
+ /* not a commit */
769
+ * source_tree = parse_tree_indirect (rev );
770
+ } else {
771
+ parse_commit (new -> commit );
772
+ * source_tree = new -> commit -> tree ;
773
+ }
774
+
775
+ if (!* source_tree ) /* case (1): want a tree */
776
+ die ("reference is not a tree: %s" , arg );
777
+ if (!has_dash_dash ) {/* case (3 -> 1) */
778
+ /*
779
+ * Do not complain the most common case
780
+ * git checkout branch
781
+ * even if there happen to be a file called 'branch';
782
+ * it would be extremely annoying.
783
+ */
784
+ if (argc )
785
+ verify_non_filename (NULL , arg );
786
+ } else {
787
+ argcount ++ ;
788
+ argv ++ ;
789
+ argc -- ;
790
+ }
791
+
792
+ return argcount ;
793
+ }
794
+
678
795
int cmd_checkout (int argc , const char * * argv , const char * prefix )
679
796
{
680
797
struct checkout_opts opts ;
681
798
unsigned char rev [20 ];
682
- const char * arg ;
683
799
struct branch_info new ;
684
800
struct tree * source_tree = NULL ;
685
801
char * conflict_style = NULL ;
@@ -692,6 +808,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
692
808
OPT_STRING ('B' , NULL , & opts .new_branch_force , "branch" ,
693
809
"create/reset and checkout a branch" ),
694
810
OPT_BOOLEAN ('l' , NULL , & opts .new_branch_log , "create reflog for new branch" ),
811
+ OPT_BOOLEAN (0 , "detach" , & opts .force_detach , "detach the HEAD at named commit" ),
695
812
OPT_SET_INT ('t' , "track" , & opts .track , "set upstream info for new branch" ,
696
813
BRANCH_TRACK_EXPLICIT ),
697
814
OPT_STRING (0 , "orphan" , & opts .new_orphan_branch , "new branch" , "new unparented branch" ),
@@ -709,7 +826,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
709
826
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
710
827
OPT_END (),
711
828
};
712
- int has_dash_dash ;
713
829
714
830
memset (& opts , 0 , sizeof (opts ));
715
831
memset (& new , 0 , sizeof (new ));
@@ -731,9 +847,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
731
847
opts .new_branch = opts .new_branch_force ;
732
848
733
849
if (patch_mode && (opts .track > 0 || opts .new_branch
734
- || opts .new_branch_log || opts .merge || opts .force ))
850
+ || opts .new_branch_log || opts .merge || opts .force
851
+ || opts .force_detach ))
735
852
die ("--patch is incompatible with all other options" );
736
853
854
+ if (opts .force_detach && (opts .new_branch || opts .new_orphan_branch ))
855
+ die ("--detach cannot be used with -b/-B/--orphan" );
856
+ if (opts .force_detach && 0 < opts .track )
857
+ die ("--detach cannot be used with -t" );
858
+
737
859
/* --track without -b should DWIM */
738
860
if (0 < opts .track && !opts .new_branch ) {
739
861
const char * argv0 = argv [0 ];
@@ -766,105 +888,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
766
888
die ("git checkout: -f and -m are incompatible" );
767
889
768
890
/*
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.
777
- *
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.
891
+ * Extract branch name from command line arguments, so
892
+ * all that is left is pathspecs.
785
893
*
786
- * With no paths, if <something> is _not_ a commit, no -t nor -b
787
- * was given, and there is a remote-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.
894
+ * Handle
790
895
*
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.
896
+ * 1) git checkout <tree> -- [<paths>]
897
+ * 2) git checkout -- [<paths>]
898
+ * 3) git checkout <something> [<paths>]
795
899
*
900
+ * including "last branch" syntax and DWIM-ery for names of
901
+ * remote branches, erroring out for invalid or ambiguous cases.
796
902
*/
797
903
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
- if ((new .commit = lookup_commit_reference_gently (rev , 1 ))) {
836
- setup_branch_path (& new );
837
-
838
- if ((check_ref_format (new .path ) == CHECK_REF_FORMAT_OK ) &&
839
- resolve_ref (new .path , rev , 1 , NULL ))
840
- ;
841
- else
842
- new .path = NULL ;
843
- parse_commit (new .commit );
844
- source_tree = new .commit -> tree ;
845
- } else
846
- source_tree = parse_tree_indirect (rev );
847
-
848
- if (!source_tree ) /* case (1): want a tree */
849
- die ("reference is not a tree: %s" , arg );
850
- if (!has_dash_dash ) {/* case (3 -> 1) */
851
- /*
852
- * Do not complain the most common case
853
- * git checkout branch
854
- * even if there happen to be a file called 'branch';
855
- * it would be extremely annoying.
856
- */
857
- if (argc )
858
- verify_non_filename (NULL , arg );
859
- }
860
- else {
861
- argv ++ ;
862
- argc -- ;
863
- }
904
+ int dwim_ok =
905
+ !patch_mode &&
906
+ dwim_new_local_branch &&
907
+ opts .track == BRANCH_TRACK_UNSPECIFIED &&
908
+ !opts .new_branch ;
909
+ int n = parse_branchname_arg (argc , argv , dwim_ok ,
910
+ & new , & source_tree , rev , & opts .new_branch );
911
+ argv += n ;
912
+ argc -= n ;
864
913
}
865
914
866
- no_reference :
867
-
868
915
if (opts .track == BRANCH_TRACK_UNSPECIFIED )
869
916
opts .track = git_branch_track ;
870
917
@@ -886,6 +933,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
886
933
}
887
934
}
888
935
936
+ if (opts .force_detach )
937
+ die ("git checkout: --detach does not take a path argument" );
938
+
889
939
if (1 < !!opts .writeout_stage + !!opts .force + !!opts .merge )
890
940
die ("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index." );
891
941
0 commit comments