@@ -818,66 +818,65 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
818
818
}
819
819
}
820
820
821
+ enum discovery_result {
822
+ GIT_DIR_NONE = 0 ,
823
+ GIT_DIR_EXPLICIT ,
824
+ GIT_DIR_DISCOVERED ,
825
+ GIT_DIR_BARE ,
826
+ /* these are errors */
827
+ GIT_DIR_HIT_CEILING = -1 ,
828
+ GIT_DIR_HIT_MOUNT_POINT = -2
829
+ };
830
+
821
831
/*
822
832
* We cannot decide in this function whether we are in the work tree or
823
833
* not, since the config can only be read _after_ this function was called.
834
+ *
835
+ * Also, we avoid changing any global state (such as the current working
836
+ * directory) to allow early callers.
837
+ *
838
+ * The directory where the search should start needs to be passed in via the
839
+ * `dir` parameter; upon return, the `dir` buffer will contain the path of
840
+ * the directory where the search ended, and `gitdir` will contain the path of
841
+ * the discovered .git/ directory, if any. If `gitdir` is not absolute, it
842
+ * is relative to `dir` (i.e. *not* necessarily the cwd).
824
843
*/
825
- static const char * setup_git_directory_gently_1 (int * nongit_ok )
844
+ static enum discovery_result setup_git_directory_gently_1 (struct strbuf * dir ,
845
+ struct strbuf * gitdir )
826
846
{
827
847
const char * env_ceiling_dirs = getenv (CEILING_DIRECTORIES_ENVIRONMENT );
828
848
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP ;
829
- static struct strbuf cwd = STRBUF_INIT ;
830
- const char * gitdirenv , * ret ;
831
- char * gitfile ;
832
- int offset , offset_parent , ceil_offset = -1 ;
849
+ const char * gitdirenv ;
850
+ int ceil_offset = -1 , min_offset = has_dos_drive_prefix (dir -> buf ) ? 3 : 1 ;
833
851
dev_t current_device = 0 ;
834
852
int one_filesystem = 1 ;
835
853
836
- /*
837
- * We may have read an incomplete configuration before
838
- * setting-up the git directory. If so, clear the cache so
839
- * that the next queries to the configuration reload complete
840
- * configuration (including the per-repo config file that we
841
- * ignored previously).
842
- */
843
- git_config_clear ();
844
-
845
- /*
846
- * Let's assume that we are in a git repository.
847
- * If it turns out later that we are somewhere else, the value will be
848
- * updated accordingly.
849
- */
850
- if (nongit_ok )
851
- * nongit_ok = 0 ;
852
-
853
- if (strbuf_getcwd (& cwd ))
854
- die_errno (_ ("Unable to read current working directory" ));
855
- offset = cwd .len ;
856
-
857
854
/*
858
855
* If GIT_DIR is set explicitly, we're not going
859
856
* to do any discovery, but we still do repository
860
857
* validation.
861
858
*/
862
859
gitdirenv = getenv (GIT_DIR_ENVIRONMENT );
863
- if (gitdirenv )
864
- return setup_explicit_git_dir (gitdirenv , & cwd , nongit_ok );
860
+ if (gitdirenv ) {
861
+ strbuf_addstr (gitdir , gitdirenv );
862
+ return GIT_DIR_EXPLICIT ;
863
+ }
865
864
866
865
if (env_ceiling_dirs ) {
867
866
int empty_entry_found = 0 ;
868
867
869
868
string_list_split (& ceiling_dirs , env_ceiling_dirs , PATH_SEP , -1 );
870
869
filter_string_list (& ceiling_dirs , 0 ,
871
870
canonicalize_ceiling_entry , & empty_entry_found );
872
- ceil_offset = longest_ancestor_length (cwd . buf , & ceiling_dirs );
871
+ ceil_offset = longest_ancestor_length (dir -> buf , & ceiling_dirs );
873
872
string_list_clear (& ceiling_dirs , 0 );
874
873
}
875
874
876
- if (ceil_offset < 0 && has_dos_drive_prefix ( cwd . buf ) )
877
- ceil_offset = 1 ;
875
+ if (ceil_offset < 0 )
876
+ ceil_offset = min_offset - 2 ;
878
877
879
878
/*
880
- * Test in the following order (relative to the cwd ):
879
+ * Test in the following order (relative to the dir ):
881
880
* - .git (file containing "gitdir: <path>")
882
881
* - .git/
883
882
* - ./ (bare)
@@ -889,63 +888,104 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
889
888
*/
890
889
one_filesystem = !git_env_bool ("GIT_DISCOVERY_ACROSS_FILESYSTEM" , 0 );
891
890
if (one_filesystem )
892
- current_device = get_device_or_die ("." , NULL , 0 );
891
+ current_device = get_device_or_die (dir -> buf , NULL , 0 );
893
892
for (;;) {
894
- gitfile = (char * )read_gitfile (DEFAULT_GIT_DIR_ENVIRONMENT );
895
- if (gitfile )
896
- gitdirenv = gitfile = xstrdup (gitfile );
897
- else {
898
- if (is_git_directory (DEFAULT_GIT_DIR_ENVIRONMENT ))
899
- gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
893
+ int offset = dir -> len ;
894
+
895
+ if (offset > min_offset )
896
+ strbuf_addch (dir , '/' );
897
+ strbuf_addstr (dir , DEFAULT_GIT_DIR_ENVIRONMENT );
898
+ gitdirenv = read_gitfile (dir -> buf );
899
+ if (!gitdirenv && is_git_directory (dir -> buf ))
900
+ gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
901
+ strbuf_setlen (dir , offset );
902
+ if (gitdirenv ) {
903
+ strbuf_addstr (gitdir , gitdirenv );
904
+ return GIT_DIR_DISCOVERED ;
900
905
}
901
906
902
- if (gitdirenv ) {
903
- ret = setup_discovered_git_dir (gitdirenv ,
904
- & cwd , offset ,
905
- nongit_ok );
906
- free (gitfile );
907
- return ret ;
907
+ if (is_git_directory (dir -> buf )) {
908
+ strbuf_addstr (gitdir , "." );
909
+ return GIT_DIR_BARE ;
908
910
}
909
- free (gitfile );
910
911
911
- if (is_git_directory ( "." ) )
912
- return setup_bare_git_dir ( & cwd , offset , nongit_ok ) ;
912
+ if (offset <= min_offset )
913
+ return GIT_DIR_HIT_CEILING ;
913
914
914
- offset_parent = offset ;
915
- while (-- offset_parent > ceil_offset &&
916
- !is_dir_sep (cwd .buf [offset_parent ]))
915
+ while (-- offset > ceil_offset && !is_dir_sep (dir -> buf [offset ]))
917
916
; /* continue */
918
- if (offset_parent <= ceil_offset )
919
- return setup_nongit (cwd .buf , nongit_ok );
920
- if (one_filesystem ) {
921
- dev_t parent_device = get_device_or_die (".." , cwd .buf ,
922
- offset );
923
- if (parent_device != current_device ) {
924
- if (nongit_ok ) {
925
- if (chdir (cwd .buf ))
926
- die_errno (_ ("Cannot come back to cwd" ));
927
- * nongit_ok = 1 ;
928
- return NULL ;
929
- }
930
- strbuf_setlen (& cwd , offset );
931
- die (_ ("Not a git repository (or any parent up to mount point %s)\n"
932
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)." ),
933
- cwd .buf );
934
- }
935
- }
936
- if (chdir (".." )) {
937
- strbuf_setlen (& cwd , offset );
938
- die_errno (_ ("Cannot change to '%s/..'" ), cwd .buf );
939
- }
940
- offset = offset_parent ;
917
+ if (offset <= ceil_offset )
918
+ return GIT_DIR_HIT_CEILING ;
919
+
920
+ strbuf_setlen (dir , offset > min_offset ? offset : min_offset );
921
+ if (one_filesystem &&
922
+ current_device != get_device_or_die (dir -> buf , NULL , offset ))
923
+ return GIT_DIR_HIT_MOUNT_POINT ;
941
924
}
942
925
}
943
926
944
927
const char * setup_git_directory_gently (int * nongit_ok )
945
928
{
929
+ static struct strbuf cwd = STRBUF_INIT ;
930
+ struct strbuf dir = STRBUF_INIT , gitdir = STRBUF_INIT ;
946
931
const char * prefix ;
947
932
948
- prefix = setup_git_directory_gently_1 (nongit_ok );
933
+ /*
934
+ * We may have read an incomplete configuration before
935
+ * setting-up the git directory. If so, clear the cache so
936
+ * that the next queries to the configuration reload complete
937
+ * configuration (including the per-repo config file that we
938
+ * ignored previously).
939
+ */
940
+ git_config_clear ();
941
+
942
+ /*
943
+ * Let's assume that we are in a git repository.
944
+ * If it turns out later that we are somewhere else, the value will be
945
+ * updated accordingly.
946
+ */
947
+ if (nongit_ok )
948
+ * nongit_ok = 0 ;
949
+
950
+ if (strbuf_getcwd (& cwd ))
951
+ die_errno (_ ("Unable to read current working directory" ));
952
+ strbuf_addbuf (& dir , & cwd );
953
+
954
+ switch (setup_git_directory_gently_1 (& dir , & gitdir )) {
955
+ case GIT_DIR_NONE :
956
+ prefix = NULL ;
957
+ break ;
958
+ case GIT_DIR_EXPLICIT :
959
+ prefix = setup_explicit_git_dir (gitdir .buf , & cwd , nongit_ok );
960
+ break ;
961
+ case GIT_DIR_DISCOVERED :
962
+ if (dir .len < cwd .len && chdir (dir .buf ))
963
+ die (_ ("Cannot change to '%s'" ), dir .buf );
964
+ prefix = setup_discovered_git_dir (gitdir .buf , & cwd , dir .len ,
965
+ nongit_ok );
966
+ break ;
967
+ case GIT_DIR_BARE :
968
+ if (dir .len < cwd .len && chdir (dir .buf ))
969
+ die (_ ("Cannot change to '%s'" ), dir .buf );
970
+ prefix = setup_bare_git_dir (& cwd , dir .len , nongit_ok );
971
+ break ;
972
+ case GIT_DIR_HIT_CEILING :
973
+ prefix = setup_nongit (cwd .buf , nongit_ok );
974
+ break ;
975
+ case GIT_DIR_HIT_MOUNT_POINT :
976
+ if (nongit_ok ) {
977
+ * nongit_ok = 1 ;
978
+ strbuf_release (& cwd );
979
+ strbuf_release (& dir );
980
+ return NULL ;
981
+ }
982
+ die (_ ("Not a git repository (or any parent up to mount point %s)\n"
983
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)." ),
984
+ dir .buf );
985
+ default :
986
+ die ("BUG: unhandled setup_git_directory_1() result" );
987
+ }
988
+
949
989
if (prefix )
950
990
setenv (GIT_PREFIX_ENVIRONMENT , prefix , 1 );
951
991
else
@@ -954,6 +994,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
954
994
startup_info -> have_repository = !nongit_ok || !* nongit_ok ;
955
995
startup_info -> prefix = prefix ;
956
996
997
+ strbuf_release (& dir );
998
+ strbuf_release (& gitdir );
999
+
957
1000
return prefix ;
958
1001
}
959
1002
0 commit comments