@@ -430,6 +430,11 @@ struct branch_info {
430430 const char * name ; /* The short name used */
431431 const char * path ; /* The full name of a real branch */
432432 struct commit * commit ; /* The named commit */
433+ /*
434+ * if not null the branch is detached because it's already
435+ * checked out in this checkout
436+ */
437+ char * checkout ;
433438};
434439
435440static void setup_branch_path (struct branch_info * branch )
@@ -958,12 +963,78 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1)
958963 return NULL ;
959964}
960965
966+ static void check_linked_checkout (struct branch_info * new , const char * id )
967+ {
968+ struct strbuf sb = STRBUF_INIT ;
969+ struct strbuf path = STRBUF_INIT ;
970+ struct strbuf gitdir = STRBUF_INIT ;
971+ const char * start , * end ;
972+
973+ if (id )
974+ strbuf_addf (& path , "%s/worktrees/%s/HEAD" , get_git_common_dir (), id );
975+ else
976+ strbuf_addf (& path , "%s/HEAD" , get_git_common_dir ());
977+
978+ if (strbuf_read_file (& sb , path .buf , 0 ) < 0 ||
979+ !skip_prefix (sb .buf , "ref:" , & start ))
980+ goto done ;
981+ while (isspace (* start ))
982+ start ++ ;
983+ end = start ;
984+ while (* end && !isspace (* end ))
985+ end ++ ;
986+ if (strncmp (start , new -> path , end - start ) || new -> path [end - start ] != '\0' )
987+ goto done ;
988+ if (id ) {
989+ strbuf_reset (& path );
990+ strbuf_addf (& path , "%s/worktrees/%s/gitdir" , get_git_common_dir (), id );
991+ if (strbuf_read_file (& gitdir , path .buf , 0 ) <= 0 )
992+ goto done ;
993+ strbuf_rtrim (& gitdir );
994+ } else
995+ strbuf_addstr (& gitdir , get_git_common_dir ());
996+ die (_ ("'%s' is already checked out at '%s'" ), new -> name , gitdir .buf );
997+ done :
998+ strbuf_release (& path );
999+ strbuf_release (& sb );
1000+ strbuf_release (& gitdir );
1001+ }
1002+
1003+ static void check_linked_checkouts (struct branch_info * new )
1004+ {
1005+ struct strbuf path = STRBUF_INIT ;
1006+ DIR * dir ;
1007+ struct dirent * d ;
1008+
1009+ strbuf_addf (& path , "%s/worktrees" , get_git_common_dir ());
1010+ if ((dir = opendir (path .buf )) == NULL ) {
1011+ strbuf_release (& path );
1012+ return ;
1013+ }
1014+
1015+ /*
1016+ * $GIT_COMMON_DIR/HEAD is practically outside
1017+ * $GIT_DIR so resolve_ref_unsafe() won't work (it
1018+ * uses git_path). Parse the ref ourselves.
1019+ */
1020+ check_linked_checkout (new , NULL );
1021+
1022+ while ((d = readdir (dir )) != NULL ) {
1023+ if (!strcmp (d -> d_name , "." ) || !strcmp (d -> d_name , ".." ))
1024+ continue ;
1025+ check_linked_checkout (new , d -> d_name );
1026+ }
1027+ strbuf_release (& path );
1028+ closedir (dir );
1029+ }
1030+
9611031static int parse_branchname_arg (int argc , const char * * argv ,
9621032 int dwim_new_local_branch_ok ,
9631033 struct branch_info * new ,
9641034 struct tree * * source_tree ,
9651035 unsigned char rev [20 ],
966- const char * * new_branch )
1036+ const char * * new_branch ,
1037+ int force_detach )
9671038{
9681039 int argcount = 0 ;
9691040 unsigned char branch_rev [20 ];
@@ -1085,6 +1156,16 @@ static int parse_branchname_arg(int argc, const char **argv,
10851156 else
10861157 new -> path = NULL ; /* not an existing branch */
10871158
1159+ if (new -> path && !force_detach && !* new_branch ) {
1160+ unsigned char sha1 [20 ];
1161+ int flag ;
1162+ char * head_ref = resolve_refdup ("HEAD" , 0 , sha1 , & flag );
1163+ if (head_ref &&
1164+ (!(flag & REF_ISSYMREF ) || strcmp (head_ref , new -> path )))
1165+ check_linked_checkouts (new );
1166+ free (head_ref );
1167+ }
1168+
10881169 new -> commit = lookup_commit_reference_gently (rev , 1 );
10891170 if (!new -> commit ) {
10901171 /* not a commit */
@@ -1289,7 +1370,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
12891370 !opts .new_branch ;
12901371 int n = parse_branchname_arg (argc , argv , dwim_ok ,
12911372 & new , & opts .source_tree ,
1292- rev , & opts .new_branch );
1373+ rev , & opts .new_branch ,
1374+ opts .force_detach );
12931375 argv += n ;
12941376 argc -= n ;
12951377 }
0 commit comments