git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: git complains packed-refs is not a directory when used with GIT_DIR and GIT_WORK_TREE envvars.

From
Jeff King <peff@peff.net>
Date
Mar 27, 2018, 16:47 UTC
Message-ID
<20180327164757.GB24747@sigill.intra.peff.net>
In-Reply-To
<CACsJy8DP53Og1crS1bLoJf6w8cJhjGKy=ggfbsqzJ6AU4eNhPA@mail.gmail.com>
On Tue, Mar 27, 2018 at 04:56:00PM +0200, Duy Nguyen wrote:
Show 9 quoted lines
> The way setup_work_tree() does it is just bad to me. When it calls
> set_git_dir() again, the assumption is the new path is exactly the
> same as the old one. The only difference is relative vs absolute. But
> this is super hard to see from set_git_dir implementation. The new
> struct repository design also inherits this (i.e. allowing to call
> set_git_dir multiple times, which really does not make sense), but
> this won't fly long term. When cwd moves, all cached relative paths
> must be adjusted, we need a better mechanism to tell everybody that,
> not just do it for $GIT_DIR and related paths.
Yeah, I agree it's questionable.
Show 14 quoted lines
> I am planning to fix this. This is part of the "setup cleanup" effort
> to keep repository.c design less messy and hopefully unify how the
> main repo and submodule repo are created/set up. But frankly that may
> take a long while before I could submit anything substantial (I also
> have the "report multiple worktree's HEADs correctly and make fsck
> count all HEADs" task, which is not small and to me have higher
> priority).
> 
> So I would not mind papering over it right now (with an understanding
> that absolute path pays some more overhead in path walking, which was
> th reason we tried to avoid it in setup code). A slightly better patch
> is trigger this path absolutization from setup_work_tree(), near
> set_git_dir(). But then you face some ugliness there: how to find out
> all ref stores to update, or just update the main ref store alone.

I don't quite get why f57f37e2 doesn't want to call git_path(). Is it to avoid the way the path is munged? Or is it to avoid some lazy-setup that is triggered by calling get_git_dir() at all (which doesn't make much sense to me, because we'd already have called get_git_dir() much earlier). Or is it just because we may sometimes fill in refs->git_dir with something besides get_git_dir() (for a submodule or worktree or something)?

I.e., can we do one of (depending on which of those answers is "yes"):
  1. Stop caching the value of get_git_dir(), and instead call it
     on-demand instead of looking at refs->git_dir? (If we just want to
     avoid git_path()).
  2. If we need to avoid even calling get_git_dir(), can we add a
     "light" version of it that avoids whatever side effects we're
     trying to skip?
  3. If the problem is just that sometimes we need get_git_dir() and
     sometimes not, could we perhaps store NULL as a sentinel to mean
     "look up get_git_dir() when you need it"?
     That would let submodules and worktrees fill in their paths as
     necessary (assuming they never change after init), but handle the
     case of get_git_dir() changing.

Hmm. Typing that out, it seems like (3) is probably the right path. Something like the patch below seems to fix it and passes the tests.

diff --git a/refs.c b/refs.c
index 20ba82b434..4094f0e7d4 100644
--- a/refs.c
+++ b/refs.c
@@ -1656,7 +1656,7 @@ struct ref_store *get_main_ref_store(void)
 	if (main_ref_store)
 		return main_ref_store;
 
-	main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
+	main_ref_store = ref_store_init(NULL, REF_STORE_ALL_CAPS);
 	return main_ref_store;
 }
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bec8e30e9e..22118b5764 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -69,6 +69,7 @@ struct files_ref_store {
 	struct ref_store base;
 	unsigned int store_flags;
 
+	/* may be NULL to force lookup of get_git_dir() */
 	char *gitdir;
 	char *gitcommondir;
 
@@ -94,17 +95,23 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
 {
 	struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
 	struct ref_store *ref_store = (struct ref_store *)refs;
-	struct strbuf sb = STRBUF_INIT;
 
 	base_ref_store_init(ref_store, &refs_be_files);
 	refs->store_flags = flags;
 
-	refs->gitdir = xstrdup(gitdir);
-	get_common_dir_noenv(&sb, gitdir);
-	refs->gitcommondir = strbuf_detach(&sb, NULL);
-	strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
-	refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
-	strbuf_release(&sb);
+	if (gitdir) {
+		struct strbuf sb = STRBUF_INIT;
+		refs->gitdir = xstrdup(gitdir);
+		get_common_dir_noenv(&sb, gitdir);
+		refs->gitcommondir = strbuf_detach(&sb, NULL);
+		strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
+		refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+		strbuf_release(&sb);
+	} else {
+		refs->gitdir = NULL;
+		refs->gitcommondir = NULL;
+		refs->packed_ref_store = packed_ref_store_create(NULL, flags);
+	}
 
 	return ref_store;
 }
@@ -147,6 +154,16 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
 	return refs;
 }
 
+static const char *files_gitdir(const struct files_ref_store *refs)
+{
+	return refs->gitdir ? refs->gitdir : get_git_dir();
+}
+
+static const char *files_gitcommondir(const struct files_ref_store *refs)
+{
+	return refs->gitcommondir ? refs->gitcommondir : get_git_common_dir();
+}
+
 static void files_reflog_path(struct files_ref_store *refs,
 			      struct strbuf *sb,
 			      const char *refname)
@@ -154,10 +171,10 @@ static void files_reflog_path(struct files_ref_store *refs,
 	switch (ref_type(refname)) {
 	case REF_TYPE_PER_WORKTREE:
 	case REF_TYPE_PSEUDOREF:
-		strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
+		strbuf_addf(sb, "%s/logs/%s", files_gitdir(refs), refname);
 		break;
 	case REF_TYPE_NORMAL:
-		strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
+		strbuf_addf(sb, "%s/logs/%s", files_gitcommondir(refs), refname);
 		break;
 	default:
 		die("BUG: unknown ref type %d of ref %s",
@@ -172,10 +189,10 @@ static void files_ref_path(struct files_ref_store *refs,
 	switch (ref_type(refname)) {
 	case REF_TYPE_PER_WORKTREE:
 	case REF_TYPE_PSEUDOREF:
-		strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
+		strbuf_addf(sb, "%s/%s", files_gitdir(refs), refname);
 		break;
 	case REF_TYPE_NORMAL:
-		strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
+		strbuf_addf(sb, "%s/%s", files_gitcommondir(refs), refname);
 		break;
 	default:
 		die("BUG: unknown ref type %d of ref %s",
@@ -2151,13 +2168,13 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
 		files_downcast(ref_store, REF_STORE_READ,
 			       "reflog_iterator_begin");
 
-	if (!strcmp(refs->gitdir, refs->gitcommondir)) {
-		return reflog_iterator_begin(ref_store, refs->gitcommondir);
+	if (!strcmp(files_gitdir(refs), files_gitcommondir(refs))) {
+		return reflog_iterator_begin(ref_store, files_gitcommondir(refs));
 	} else {
 		return merge_ref_iterator_begin(
 			0,
-			reflog_iterator_begin(ref_store, refs->gitdir),
-			reflog_iterator_begin(ref_store, refs->gitcommondir),
+			reflog_iterator_begin(ref_store, files_gitdir(refs)),
+			reflog_iterator_begin(ref_store, files_gitcommondir(refs)),
 			reflog_iterator_select, refs);
 	}
 }
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 65288c6472..e5a5af1285 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -158,6 +158,16 @@ static void acquire_snapshot(struct snapshot *snapshot)
 	snapshot->referrers++;
 }
 
+static const char *packed_refs_path(const struct packed_ref_store *refs)
+{
+	return refs->path ? refs->path : git_path("packed-refs");
+}
+
+static const char *snapshot_path(const struct snapshot *snapshot)
+{
+	return packed_refs_path(snapshot->refs);
+}
+
 /*
  * If the buffer in `snapshot` is active, then either munmap the
  * memory and close the file, or free the memory. Then set the buffer
@@ -168,7 +178,7 @@ static void clear_snapshot_buffer(struct snapshot *snapshot)
 	if (snapshot->mmapped) {
 		if (munmap(snapshot->buf, snapshot->eof - snapshot->buf))
 			die_errno("error ummapping packed-refs file %s",
-				  snapshot->refs->path);
+				  snapshot_path(snapshot));
 		snapshot->mmapped = 0;
 	} else {
 		free(snapshot->buf);
@@ -201,7 +211,7 @@ struct ref_store *packed_ref_store_create(const char *path,
 	base_ref_store_init(ref_store, &refs_be_packed);
 	refs->store_flags = store_flags;
 
-	refs->path = xstrdup(path);
+	refs->path = xstrdup_or_null(path);
 	return ref_store;
 }
 
@@ -342,7 +352,7 @@ static void sort_snapshot(struct snapshot *snapshot)
 			/* The safety check should prevent this. */
 			BUG("unterminated line found in packed-refs");
 		if (eol - pos < GIT_SHA1_HEXSZ + 2)
-			die_invalid_line(snapshot->refs->path,
+			die_invalid_line(snapshot_path(snapshot),
 					 pos, eof - pos);
 		eol++;
 		if (eol < eof && *eol == '^') {
@@ -454,7 +464,7 @@ static void verify_buffer_safe(struct snapshot *snapshot)
 
 	last_line = find_start_of_record(start, eof - 1);
 	if (*(eof - 1) != '\n' || eof - last_line < GIT_SHA1_HEXSZ + 2)
-		die_invalid_line(snapshot->refs->path,
+		die_invalid_line(snapshot_path(snapshot),
 				 last_line, eof - last_line);
 }
 
@@ -473,7 +483,7 @@ static int load_contents(struct snapshot *snapshot)
 	size_t size;
 	ssize_t bytes_read;
 
-	fd = open(snapshot->refs->path, O_RDONLY);
+	fd = open(snapshot_path(snapshot), O_RDONLY);
 	if (fd < 0) {
 		if (errno == ENOENT) {
 			/*
@@ -485,14 +495,14 @@ static int load_contents(struct snapshot *snapshot)
 			 */
 			return 0;
 		} else {
-			die_errno("couldn't read %s", snapshot->refs->path);
+			die_errno("couldn't read %s", snapshot_path(snapshot));
 		}
 	}
 
 	stat_validity_update(&snapshot->validity, fd);
 
 	if (fstat(fd, &st) < 0)
-		die_errno("couldn't stat %s", snapshot->refs->path);
+		die_errno("couldn't stat %s", snapshot_path(snapshot));
 	size = xsize_t(st.st_size);
 
 	if (!size) {
@@ -501,7 +511,7 @@ static int load_contents(struct snapshot *snapshot)
 		snapshot->buf = xmalloc(size);
 		bytes_read = read_in_full(fd, snapshot->buf, size);
 		if (bytes_read < 0 || bytes_read != size)
-			die_errno("couldn't read %s", snapshot->refs->path);
+			die_errno("couldn't read %s", snapshot_path(snapshot));
 		snapshot->mmapped = 0;
 	} else {
 		snapshot->buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -629,14 +639,14 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
 		eol = memchr(snapshot->buf, '\n',
 			     snapshot->eof - snapshot->buf);
 		if (!eol)
-			die_unterminated_line(refs->path,
+			die_unterminated_line(packed_refs_path(refs),
 					      snapshot->buf,
 					      snapshot->eof - snapshot->buf);
 
 		tmp = xmemdupz(snapshot->buf, eol - snapshot->buf);
 
 		if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p))
-			die_invalid_line(refs->path,
+			die_invalid_line(packed_refs_path(refs),
 					 snapshot->buf,
 					 snapshot->eof - snapshot->buf);
 
@@ -695,7 +705,8 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
 static void validate_snapshot(struct packed_ref_store *refs)
 {
 	if (refs->snapshot &&
-	    !stat_validity_check(&refs->snapshot->validity, refs->path))
+	    !stat_validity_check(&refs->snapshot->validity,
+				 packed_refs_path(refs)))
 		clear_snapshot(refs);
 }
 
@@ -739,7 +750,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
 	}
 
 	if (get_oid_hex(rec, oid))
-		die_invalid_line(refs->path, rec, snapshot->eof - rec);
+		die_invalid_line(packed_refs_path(refs), rec, snapshot->eof - rec);
 
 	*type = REF_ISPACKED;
 	return 0;
@@ -795,12 +806,12 @@ static int next_record(struct packed_ref_iterator *iter)
 	if (iter->eof - p < GIT_SHA1_HEXSZ + 2 ||
 	    parse_oid_hex(p, &iter->oid, &p) ||
 	    !isspace(*p++))
-		die_invalid_line(iter->snapshot->refs->path,
+		die_invalid_line(snapshot_path(iter->snapshot),
 				 iter->pos, iter->eof - iter->pos);
 
 	eol = memchr(p, '\n', iter->eof - p);
 	if (!eol)
-		die_unterminated_line(iter->snapshot->refs->path,
+		die_unterminated_line(snapshot_path(iter->snapshot),
 				      iter->pos, iter->eof - iter->pos);
 
 	strbuf_add(&iter->refname_buf, p, eol - p);
@@ -825,7 +836,7 @@ static int next_record(struct packed_ref_iterator *iter)
 		if (iter->eof - p < GIT_SHA1_HEXSZ + 1 ||
 		    parse_oid_hex(p, &iter->peeled, &p) ||
 		    *p++ != '\n')
-			die_invalid_line(iter->snapshot->refs->path,
+			die_invalid_line(snapshot_path(iter->snapshot),
 					 iter->pos, iter->eof - iter->pos);
 		iter->pos = p;
 
@@ -995,14 +1006,14 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
 	 */
 	if (hold_lock_file_for_update_timeout(
 			    &refs->lock,
-			    refs->path,
+			    packed_refs_path(refs),
 			    flags, timeout_value) < 0) {
-		unable_to_lock_message(refs->path, errno, err);
+		unable_to_lock_message(packed_refs_path(refs), errno, err);
 		return -1;
 	}
 
 	if (close_lock_file_gently(&refs->lock)) {
-		strbuf_addf(err, "unable to close %s: %s", refs->path, strerror(errno));
+		strbuf_addf(err, "unable to close %s: %s", packed_refs_path(refs), strerror(errno));
 		rollback_lock_file(&refs->lock);
 		return -1;
 	}
@@ -1471,21 +1482,21 @@ static int packed_transaction_finish(struct ref_store *ref_store,
 			REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
 			"ref_transaction_finish");
 	int ret = TRANSACTION_GENERIC_ERROR;
-	char *packed_refs_path;
+	char *path;
 
 	clear_snapshot(refs);
 
-	packed_refs_path = get_locked_file_path(&refs->lock);
-	if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
+	path = get_locked_file_path(&refs->lock);
+	if (rename_tempfile(&refs->tempfile, path)) {
 		strbuf_addf(err, "error replacing %s: %s",
-			    refs->path, strerror(errno));
+			    path, strerror(errno));
 		goto cleanup;
 	}
 
 	ret = 0;
 
 cleanup:
-	free(packed_refs_path);
+	free(path);
 	packed_transaction_cleanup(refs, transaction);
 	return ret;
 }
Previous: Duy NguyenNext: Duy Nguyen
Message 5 of 54 in “git complains packed-refs is not a directory when used with GIT_DIR and GIT_WORK_TREE envvars.”
  1. Rafael AscensaoMar 26, 2018
  2. Ævar Arnfjörð BjarmasonMar 26, 2018
  3. Jeff KingMar 27, 2018
  4. Duy NguyenMar 27, 2018
  5. Jeff KingMar 27, 2018
  6. Duy NguyenMar 27, 2018
  7. Duy NguyenMar 27, 2018
  8. Jeff KingMar 28, 2018
  9. Duy NguyenMar 28, 2018
  10. Jeff KingMar 28, 2018
  11. 1/4 set_git_dir: die when setenv() failsJeff King, Mar 28, 2018
  12. 2/4 add chdir-notify APIJeff King, Mar 28, 2018
  13. Eric SunshineMar 28, 2018
  14. Jeff KingMar 28, 2018
  15. Duy NguyenMar 29, 2018
  16. Jeff KingMar 29, 2018
  17. Duy NguyenMar 29, 2018
  18. 3/4 set_work_tree: use chdir_notifyJeff King, Mar 28, 2018
  19. Duy NguyenMar 29, 2018
  20. Duy NguyenMar 29, 2018
  21. Jeff KingMar 29, 2018
  22. Jeff KingMar 29, 2018
  23. Duy NguyenMar 29, 2018
  24. Jeff KingMar 30, 2018
  25. 4/4 refs: use chdir_notify to update cached relative pathsJeff King, Mar 28, 2018
  26. 0/5 re-parenting relative directories after chdirJeff King, Mar 30, 2018
  27. 1/5 set_git_dir: die when setenv() failsJeff King, Mar 30, 2018
  28. 2/5 trace.c: export trace_setup_keyJeff King, Mar 30, 2018
  29. Junio C HamanoMar 30, 2018
  30. Jeff KingMar 30, 2018
  31. Junio C HamanoMar 30, 2018
  32. Jeff KingMar 30, 2018
  33. 3/5 add chdir-notify APIJeff King, Mar 30, 2018
  34. 4/5 set_work_tree: use chdir_notifyJeff King, Mar 30, 2018
  35. 5/5 refs: use chdir_notify to update cached relative pathsJeff King, Mar 30, 2018
  36. Duy NguyenMar 30, 2018
  37. Jeff KingMar 28, 2018
  38. 0/8 Re: git complains packed-refs is not a directory when used with GIT_DIR and GIT_WORK_TREE envvars.Nguyễn Thái Ngọc Duy, Mar 28, 2018
  39. 1/8 strbuf.c: add strbuf_ensure_trailing_dr_sep()Nguyễn Thái Ngọc Duy, Mar 28, 2018
  40. 2/8 strbuf.c: reintroduce get_pwd_cwd() (with strbuf_ prefix)Nguyễn Thái Ngọc Duy, Mar 28, 2018
  41. Stefan BellerMar 28, 2018
  42. Duy NguyenMar 28, 2018
  43. 3/8 trace.c: export trace_setup_keyNguyễn Thái Ngọc Duy, Mar 28, 2018
  44. 4/8 setup.c: introduce setup_adjust_path()Nguyễn Thái Ngọc Duy, Mar 28, 2018
  45. 5/8 setup.c: allow other code to be notified when $CWD movesNguyễn Thái Ngọc Duy, Mar 28, 2018
  46. 6/8 environment.c: adjust env containing relpath when $CWD is movedNguyễn Thái Ngọc Duy, Mar 28, 2018
  47. Jeff KingMar 28, 2018
  48. Duy NguyenMar 28, 2018
  49. 8/8 refs: adjust main repo paths when $CWD movesNguyễn Thái Ngọc Duy, Mar 28, 2018
  50. 7/8 repository: adjust repo paths when $CWD movesNguyễn Thái Ngọc Duy, Mar 28, 2018
  51. Jeff KingMar 28, 2018
  52. Duy NguyenMar 29, 2018
  53. Jeff KingMar 30, 2018
  54. Junio C HamanoMar 28, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.