@@ -818,66 +818,65 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
818818 }
819819}
820820
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+
821831/*
822832 * We cannot decide in this function whether we are in the work tree or
823833 * 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).
824843 */
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 )
826846{
827847 const char * env_ceiling_dirs = getenv (CEILING_DIRECTORIES_ENVIRONMENT );
828848 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 ;
833851 dev_t current_device = 0 ;
834852 int one_filesystem = 1 ;
835853
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-
857854 /*
858855 * If GIT_DIR is set explicitly, we're not going
859856 * to do any discovery, but we still do repository
860857 * validation.
861858 */
862859 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+ }
865864
866865 if (env_ceiling_dirs ) {
867866 int empty_entry_found = 0 ;
868867
869868 string_list_split (& ceiling_dirs , env_ceiling_dirs , PATH_SEP , -1 );
870869 filter_string_list (& ceiling_dirs , 0 ,
871870 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 );
873872 string_list_clear (& ceiling_dirs , 0 );
874873 }
875874
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 ;
878877
879878 /*
880- * Test in the following order (relative to the cwd ):
879+ * Test in the following order (relative to the dir ):
881880 * - .git (file containing "gitdir: <path>")
882881 * - .git/
883882 * - ./ (bare)
@@ -889,63 +888,104 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
889888 */
890889 one_filesystem = !git_env_bool ("GIT_DISCOVERY_ACROSS_FILESYSTEM" , 0 );
891890 if (one_filesystem )
892- current_device = get_device_or_die ("." , NULL , 0 );
891+ current_device = get_device_or_die (dir -> buf , NULL , 0 );
893892 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 ;
900905 }
901906
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 ;
908910 }
909- free (gitfile );
910911
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 ;
913914
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 ]))
917916 ; /* 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 ;
941924 }
942925}
943926
944927const char * setup_git_directory_gently (int * nongit_ok )
945928{
929+ static struct strbuf cwd = STRBUF_INIT ;
930+ struct strbuf dir = STRBUF_INIT , gitdir = STRBUF_INIT ;
946931 const char * prefix ;
947932
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+
949989 if (prefix )
950990 setenv (GIT_PREFIX_ENVIRONMENT , prefix , 1 );
951991 else
@@ -954,6 +994,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
954994 startup_info -> have_repository = !nongit_ok || !* nongit_ok ;
955995 startup_info -> prefix = prefix ;
956996
997+ strbuf_release (& dir );
998+ strbuf_release (& gitdir );
999+
9571000 return prefix ;
9581001}
9591002
0 commit comments