[RFC PATCH 2/3] setup: introduce new helper 'is_git_directory_verbose'
From: Kaartic Sivaraam <hidden>
Date: 2026-09-24 12:05:18
Subsystem:
the rest · Maintainer:
Linus Torvalds
Introduce a new helper is_git_directory_verbose() as a counterpart to the existing is_git_directory(). is_git_directory_verbose() also populates an optional string strbuf with reasoning around why the given suspect is not a valid git directory. This strbuf in turn can be used to improve the error reporting which is currently blunt: fatal: not a git repository This is not helpful as the user does not get any hint about "why" the repository is not considered valid. Call-site(s) will be made to use this helper in a follow-up commit. Signed-off-by: Kaartic Sivaraam <redacted> --- setup.c | 152 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 40 deletions(-)
diff --git a/setup.c b/setup.c
index 0d157ac254..b3b53a1cfc 100644
--- a/setup.c
+++ b/setup.c@@ -347,7 +347,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) return ret; } -static int validate_headref(const char *path) +static int validate_headref(const char *path, struct strbuf *err) { struct stat st; char buffer[256];
@@ -356,14 +356,32 @@ static int validate_headref(const char *path) int fd; ssize_t len; - if (lstat(path, &st) < 0) + if (lstat(path, &st) < 0) { + if (err) + strbuf_addf( + err, _("could not stat HEAD at '%s'"), path + ); return -1; + } /* Make sure it is a "refs/.." symlink */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); if (len >= 5 && !memcmp("refs/", buffer, 5)) return 0; + if (len == -1 && err) + strbuf_addf( + err, + _("could not read the symlink HEAD at '%s'"), + path + ); + else if (err) + strbuf_addf( + err, + _("HEAD is a symlink ('%s') but target" + " lives outside refs/"), + path + ); return -1; }
@@ -371,13 +389,23 @@ static int validate_headref(const char *path) * Anything else, just open it and try to see if it is a symbolic ref. */ fd = open(path, O_RDONLY); - if (fd < 0) + if (fd < 0) { + if (err) + strbuf_addf( + err, _("could not open HEAD at '%s'"), path + ); return -1; + } len = read_in_full(fd, buffer, sizeof(buffer)-1); close(fd); - if (len < 0) + if (len < 0) { + if (err) + strbuf_addf( + err, _("could not read HEAD at '%s'"), path + ); return -1; + } buffer[len] = '\0'; /*
@@ -396,9 +424,88 @@ static int validate_headref(const char *path) if (get_oid_hex_any(buffer, &oid) != GIT_HASH_UNKNOWN) return 0; + if (err) + strbuf_addf( + err, + _("HEAD at '%s' does not point to a valid" + " symbolic link or an object ID"), + path + ); + return -1; } +/* + * A variant of is_git_directory that gives additional + * context via 'err' about why a given suspect is not + * a valid git repository. + */ +static int is_git_directory_verbose(const char *suspect, struct strbuf *err) +{ + struct strbuf path = STRBUF_INIT; + char *objdir; + int ret = 0; + size_t len; + + /* Check worktree-related signatures */ + strbuf_addstr(&path, suspect); + strbuf_complete(&path, '/'); + strbuf_addstr(&path, "HEAD"); + if (validate_headref(path.buf, err)) + goto done; + + strbuf_reset(&path); + get_common_dir(&path, suspect); + len = path.len; + + /* Check non-worktree-related signatures */ + objdir = getenv(DB_ENVIRONMENT); + if (objdir) { + if (access(objdir, X_OK)) { + if (err) + strbuf_addf( + err, + _("cannot access object directory '%s'" + " set via $%s\n"), + objdir, + DB_ENVIRONMENT + ); + goto done; + } + } + else { + strbuf_setlen(&path, len); + strbuf_addstr(&path, "/objects"); + if (access(path.buf, X_OK)) { + if (err) + strbuf_addf( + err, + _("cannot access object directory '%s'"), + path.buf + ); + goto done; + } + } + + strbuf_setlen(&path, len); + strbuf_addstr(&path, "/refs"); + if (access(path.buf, X_OK)) { + if (err) + strbuf_addf( + err, + _("cannot access refs directory '%s'"), + path.buf + ); + goto done; + } + + ret = 1; +done: + strbuf_release(&path); + return ret; + +} + /* * Test if it looks like we're at a git directory. * We want to see:
@@ -412,42 +519,7 @@ static int validate_headref(const char *path) */ int is_git_directory(const char *suspect) { - struct strbuf path = STRBUF_INIT; - int ret = 0; - size_t len; - - /* Check worktree-related signatures */ - strbuf_addstr(&path, suspect); - strbuf_complete(&path, '/'); - strbuf_addstr(&path, "HEAD"); - if (validate_headref(path.buf)) - goto done; - - strbuf_reset(&path); - get_common_dir(&path, suspect); - len = path.len; - - /* Check non-worktree-related signatures */ - if (getenv(DB_ENVIRONMENT)) { - if (access(getenv(DB_ENVIRONMENT), X_OK)) - goto done; - } - else { - strbuf_setlen(&path, len); - strbuf_addstr(&path, "/objects"); - if (access(path.buf, X_OK)) - goto done; - } - - strbuf_setlen(&path, len); - strbuf_addstr(&path, "/refs"); - if (access(path.buf, X_OK)) - goto done; - - ret = 1; -done: - strbuf_release(&path); - return ret; + return is_git_directory_verbose(suspect, NULL); } int is_nonbare_repository_dir(struct strbuf *path)
--
2.56.0.rc1.12.g2c9c8d64bb