setup: improve error diagnosis for invalid .git files
'read_gitfile_gently()' treats any non-regular file as
'READ_GITFILE_ERR_NOT_A_FILE' and fails to discern between 'ENOENT'
and other stat failures. This flawed error reporting is noted by two
'NEEDSWORK' comments.
Address these comments by introducing two new error codes:
'READ_GITFILE_ERR_MISSING'(which groups the "file missing" scenarios
together) and 'READ_GITFILE_ERR_IS_A_DIR':
1. Update 'read_gitfile_error_die()' to treat 'IS_A_DIR', 'MISSING',
'NOT_A_FILE' and 'STAT_FAILED' as non-fatal no-ops. This accommodates
intentional non-repo scenarios (e.g., GIT_DIR=/dev/null).
2. Explicitly catch 'NOT_A_FILE' and 'STAT_FAILED' during
discovery and call 'die()' if 'die_on_error' is set.
3. Unconditionally pass '&error_code' to 'read_gitfile_gently()'.
4. Only invoke 'is_git_directory()' when we explicitly receive
'READ_GITFILE_ERR_IS_A_DIR', avoiding redundant checks.
Additionally, audit external callers of 'read_gitfile_gently()' in
'submodule.c' and 'worktree.c' to accommodate the refined error codes.
Signed-off-by: Tian Yuchen <a3205153416@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Tian Yuchen committedMar 4, 2026 at 22:15 UTC1dd27bfbfdc0f3b2071ecb8b505476f4caa56a13
6 files changed+121-14
setup.c
+36-11
index b723f8b339..0ae9c88466 100644--- a/setup.c+++ b/setup.c@@ -895,8 +895,10 @@ int verify_repository_format(const struct repository_format *format, void read_gitfile_error_die(int error_code, const char *path, const char *dir) { switch (error_code) {- case READ_GITFILE_ERR_STAT_FAILED: case READ_GITFILE_ERR_NOT_A_FILE:+ case READ_GITFILE_ERR_STAT_FAILED:+ case READ_GITFILE_ERR_MISSING:+ case READ_GITFILE_ERR_IS_A_DIR: /* non-fatal; follow return path */ break; case READ_GITFILE_ERR_OPEN_FAILED:@@ -939,8 +941,14 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) static struct strbuf realpath = STRBUF_INIT; if (stat(path, &st)) {- /* NEEDSWORK: discern between ENOENT vs other errors */- error_code = READ_GITFILE_ERR_STAT_FAILED;+ if (errno == ENOENT || errno == ENOTDIR)+ error_code = READ_GITFILE_ERR_MISSING;+ else+ error_code = READ_GITFILE_ERR_STAT_FAILED;+ goto cleanup_return;+ }+ if (S_ISDIR(st.st_mode)) {+ error_code = READ_GITFILE_ERR_IS_A_DIR; goto cleanup_return; } if (!S_ISREG(st.st_mode)) {@@ -1576,20 +1584,37 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (offset > min_offset) strbuf_addch(dir, '/'); strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);- gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?- NULL : &error_code);+ gitdirenv = read_gitfile_gently(dir->buf, &error_code); if (!gitdirenv) {- if (die_on_error ||- error_code == READ_GITFILE_ERR_NOT_A_FILE) {- /* NEEDSWORK: fail if .git is not file nor dir */+ switch (error_code) {+ case READ_GITFILE_ERR_MISSING:+ /* no .git in this directory, move on */+ break;+ case READ_GITFILE_ERR_IS_A_DIR: if (is_git_directory(dir->buf)) { gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; gitdir_path = xstrdup(dir->buf); }- } else if (error_code != READ_GITFILE_ERR_STAT_FAILED)- return GIT_DIR_INVALID_GITFILE;- } else+ break;+ case READ_GITFILE_ERR_STAT_FAILED:+ if (die_on_error)+ die(_("error reading '%s'"), dir->buf);+ else+ return GIT_DIR_INVALID_GITFILE;+ case READ_GITFILE_ERR_NOT_A_FILE:+ if (die_on_error)+ die(_("not a regular file: '%s'"), dir->buf);+ else+ return GIT_DIR_INVALID_GITFILE;+ default:+ if (die_on_error)+ read_gitfile_error_die(error_code, dir->buf, NULL);+ else+ return GIT_DIR_INVALID_GITFILE;+ }+ } else { gitfile = xstrdup(dir->buf);+ } /* * Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT * to check that directory for a repository.