git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/16] path: remove dependency on `the_repository`
@ 2025-02-06  7:57 Patrick Steinhardt
  2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
                   ` (18 more replies)
  0 siblings, 19 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:57 UTC (permalink / raw)
  To: git

Hi,

this patch series removes the dependency on `the_repository` from the
"path" subsystem. The series is structured as follows:

  - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
    repository paths (gitdir, commondir, workdir and submodule paths)
    and adapts callers accodringly.

  - Patches 6 to 12 drop repository path functions that had an implicit
    dependency on `the_repository`.

  - Patches 13 to 16 refactor "path.c"-internal code to stop depending
    on `the_repository`.

Thanks!

Patrick

---
Patrick Steinhardt (16):
      path: refactor `repo_common_path()` family of functions
      path: refactor `repo_git_path()` family of functions
      path: refactor `repo_worktree_path()` family of functions
      submodule: refactor `submodule_to_gitdir()` to accept a repo
      path: refactor `repo_submodule_path()` family of functions
      path: drop unused `strbuf_git_path()` function
      path: drop `git_pathdup()` in favor of `repo_git_path()`
      path: drop `git_path_buf()` in favor of `repo_git_path_replace()`
      worktree: return allocated string from `get_worktree_git_dir()`
      path: drop `git_common_path()` in favor of `repo_common_path()`
      rerere: let `rerere_path()` write paths into a caller-provided buffer
      path: drop `git_path()` in favor of `repo_git_path()`
      repo-settings: introduce function to clear struct
      environment: move access to "core.hooksPath" into repo settings
      environment: move access to "core.sharedRepository" into repo settings
      path: adjust last remaining users of `the_repository`

 bisect.c                    |   2 +-
 branch.c                    |   7 +-
 builtin/am.c                |   2 +-
 builtin/clone.c             |   4 +-
 builtin/commit.c            |   8 +-
 builtin/config.c            |   8 +-
 builtin/fast-import.c       |   4 +-
 builtin/fsck.c              |  10 ++-
 builtin/gc.c                |  25 ++++--
 builtin/init-db.c           |   8 +-
 builtin/log.c               |   6 +-
 builtin/notes.c             |   9 ++-
 builtin/rebase.c            |   2 +-
 builtin/receive-pack.c      |   4 +-
 builtin/remote.c            |   6 +-
 builtin/replace.c           |   2 +-
 builtin/rerere.c            |  11 ++-
 builtin/rev-parse.c         |   6 +-
 builtin/submodule--helper.c |   4 +-
 builtin/tag.c               |   2 +-
 builtin/worktree.c          |  43 +++++++---
 commit-graph.c              |   2 +-
 compat/precompose_utf8.c    |   6 +-
 config.c                    |   5 --
 copy.c                      |   4 +-
 dir.c                       |   2 +-
 editor.c                    |   6 +-
 environment.c               |  27 -------
 environment.h               |  11 ---
 hook.c                      |   3 +-
 http-backend.c              |   2 +-
 loose.c                     |   8 +-
 midx-write.c                |   2 +-
 notes-merge.c               |  26 +++---
 object-file.c               |  14 ++--
 pack-bitmap-write.c         |   2 +-
 pack-write.c                |  10 ++-
 path.c                      | 166 +++++++++++++++++++++++++++-----------
 path.h                      | 191 ++++++++++++--------------------------------
 reachable.c                 |   6 +-
 read-cache.c                |  26 ++++--
 refs.c                      |   6 +-
 refs/files-backend.c        |  10 +--
 refs/reftable-backend.c     |  10 +--
 remote.c                    |  21 ++---
 repo-settings.c             |  44 +++++++++-
 repo-settings.h             |  14 ++++
 repository.c                |   6 +-
 rerere.c                    | 101 ++++++++++++++++-------
 rerere.h                    |   3 +-
 revision.c                  |   7 +-
 server-info.c               |   2 +-
 setup.c                     |  70 ++++++++--------
 shallow.c                   |   4 +-
 submodule.c                 |  15 ++--
 submodule.h                 |   3 +-
 t/helper/test-ref-store.c   |   7 +-
 tempfile.c                  |   4 +-
 tmp-objdir.c                |  17 ++--
 worktree.c                  |  49 ++++++++----
 worktree.h                  |   2 +-
 wt-status.c                 |  42 ++++++----
 62 files changed, 639 insertions(+), 490 deletions(-)


---
base-commit: bc204b742735ae06f65bb20291c95985c9633b7f
change-id: 20250204-b4-pks-path-drop-the-repository-9ed0f9956361


^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
@ 2025-02-06  7:57 ` Patrick Steinhardt
  2025-02-06 11:17   ` Karthik Nayak
  2025-02-06 14:21   ` shejialuo
  2025-02-06  7:57 ` [PATCH 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
                   ` (17 subsequent siblings)
  18 siblings, 2 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:57 UTC (permalink / raw)
  To: git

The functions provided by the "path" subsystem to derive repository
paths for the commondir, gitdir, worktrees and submodules are quite
inconsistent. Some functions have a `strbuf_` prefix, others have
different return values, some don't provide a variant working on top of
`strbuf`s.

We're thus about to refactor all of these family of functions so that
they follow a common pattern:

  - `repo_*_path()` returns an allocated string.

  - `repo_*_path_append()` appends the path to the caller-provided
    buffer while returning a constant pointer to the buffer. This
    clarifies whether the buffer is being appended to or rewritten,
    which otherwise wasn't immediately obvious.

  - `repo_*_path_replace()` replaces contents of the buffer with the
    computed path, again returning a pointer to the buffer contents.

Refactor the commondir family of functions accordingly and adapt all
callers.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 loose.c    |  6 +++---
 path.c     | 32 ++++++++++++++++++++++++++++----
 path.h     | 30 +++++++++++++++++-------------
 refs.c     |  4 ++--
 setup.c    |  4 ++--
 worktree.c |  5 ++---
 6 files changed, 54 insertions(+), 27 deletions(-)

diff --git a/loose.c b/loose.c
index 897ba389da..51ef490f93 100644
--- a/loose.c
+++ b/loose.c
@@ -75,7 +75,7 @@ static int load_one_loose_object_map(struct repository *repo, struct object_dire
 	insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
 	insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	fp = fopen(path.buf, "rb");
 	if (!fp) {
 		strbuf_release(&path);
@@ -133,7 +133,7 @@ int repo_write_loose_object_map(struct repository *repo)
 	if (!should_use_loose_object_map(repo))
 		return 0;
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
 	iter = kh_begin(map);
 	if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
@@ -174,7 +174,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
 	struct stat st;
 	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
 
 	fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
diff --git a/path.c b/path.c
index 07964f5d32..273b649e00 100644
--- a/path.c
+++ b/path.c
@@ -414,7 +414,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 	else if (!wt->id)
 		strbuf_addstr(buf, repo->commondir);
 	else
-		strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
+		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
 void repo_git_pathv(const struct repository *repo,
@@ -596,14 +596,38 @@ void repo_common_pathv(const struct repository *repo,
 	strbuf_cleanup_path(sb);
 }
 
-void strbuf_git_common_path(struct strbuf *sb,
-			    const struct repository *repo,
-			    const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+		       const char *fmt, ...)
+{
+	struct strbuf sb = STRBUF_INIT;
+	va_list args;
+	va_start(args, fmt);
+	repo_common_pathv(repo, &sb, fmt, args);
+	va_end(args);
+	return strbuf_detach(&sb, NULL);
+}
+
+const char *repo_common_path_append(const struct repository *repo,
+				    struct strbuf *sb,
+				    const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	repo_common_pathv(repo, sb, fmt, args);
+	va_end(args);
+	return sb->buf;
+}
+
+const char *repo_common_path_replace(const struct repository *repo,
+				     struct strbuf *sb,
+				     const char *fmt, ...)
 {
 	va_list args;
+	strbuf_reset(sb);
 	va_start(args, fmt);
 	repo_common_pathv(repo, sb, fmt, args);
 	va_end(args);
+	return sb->buf;
 }
 
 static struct passwd *getpw_str(const char *username, size_t len)
diff --git a/path.h b/path.h
index 5f6c85e5f8..3c75495e1a 100644
--- a/path.h
+++ b/path.h
@@ -25,22 +25,20 @@ char *mkpathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
- * The `strbuf_git_common_path` family of functions will construct a path into a
+ * The `repo_common_path` family of functions will construct a path into a
  * repository's common git directory, which is shared by all worktrees.
  */
-
-/*
- * Constructs a path into the common git directory of repository `repo` and
- * append it in the provided buffer `sb`.
- */
-void strbuf_git_common_path(struct strbuf *sb,
-			    const struct repository *repo,
-			    const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+		       const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+const char *repo_common_path_append(const struct repository *repo,
+				    struct strbuf *sb,
+				    const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
+const char *repo_common_path_replace(const struct repository *repo,
+				     struct strbuf *sb,
+				     const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *buf,
-		       const char *fmt,
-		       va_list args);
 
 /*
  * The `repo_git_path` family of functions will construct a path into a repository's
@@ -243,6 +241,12 @@ struct strbuf *get_pathname(void);
 #  include "strbuf.h"
 #  include "repository.h"
 
+/* Internal implementation detail that should not be used. */
+void repo_common_pathv(const struct repository *repo,
+		       struct strbuf *buf,
+		       const char *fmt,
+		       va_list args);
+
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) common git directory.
diff --git a/refs.c b/refs.c
index f4094a326a..daf6a84205 100644
--- a/refs.c
+++ b/refs.c
@@ -2184,8 +2184,8 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
 
 	if (wt->id) {
 		struct strbuf common_path = STRBUF_INIT;
-		strbuf_git_common_path(&common_path, wt->repo,
-				      "worktrees/%s", wt->id);
+		repo_common_path_append(wt->repo, &common_path,
+					"worktrees/%s", wt->id);
 		refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
 				      common_path.buf, REF_STORE_ALL_CAPS);
 		strbuf_release(&common_path);
diff --git a/setup.c b/setup.c
index 8a488f3e7c..74b5ba5325 100644
--- a/setup.c
+++ b/setup.c
@@ -792,7 +792,7 @@ int upgrade_repository_format(int target_version)
 	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 	int ret;
 
-	strbuf_git_common_path(&sb, the_repository, "config");
+	repo_common_path_append(the_repository, &sb, "config");
 	read_repository_format(&repo_fmt, sb.buf);
 	strbuf_release(&sb);
 
@@ -2242,7 +2242,7 @@ void initialize_repository_version(int hash_algo,
 		struct strbuf config = STRBUF_INIT;
 		struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 
-		strbuf_git_common_path(&config, the_repository, "config");
+		repo_common_path_append(the_repository, &config, "config");
 		read_repository_format(&repo_fmt, config.buf);
 
 		if (repo_fmt.v1_only_extensions.nr)
diff --git a/worktree.c b/worktree.c
index 248bbb39d4..f8d6e7127f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -104,7 +104,7 @@ struct worktree *get_linked_worktree(const char *id,
 	if (!id)
 		die("Missing linked worktree name");
 
-	strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
+	repo_common_path_append(the_repository, &path, "worktrees/%s/gitdir", id);
 	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
 		/* invalid gitdir file */
 		goto done;
@@ -731,8 +731,7 @@ static ssize_t infer_backlink(const char *gitfile, struct strbuf *inferred)
 	id++; /* advance past '/' to point at <id> */
 	if (!*id)
 		goto error;
-	strbuf_reset(inferred);
-	strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
+	repo_common_path_replace(the_repository, inferred, "worktrees/%s", id);
 	if (!is_directory(inferred->buf))
 		goto error;
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 02/16] path: refactor `repo_git_path()` family of functions
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
  2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
@ 2025-02-06  7:57 ` Patrick Steinhardt
  2025-02-06 11:53   ` Karthik Nayak
  2025-02-06  7:57 ` [PATCH 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:57 UTC (permalink / raw)
  To: git

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "gitdir" family of
functions accordingly.

Note that the `repo_git_pathv()` function is converted into an internal
implementation detail. It is only used to implement `the_repository`
compatibility shims and will eventually be removed from the public
interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 editor.c    |  6 ++----
 hook.c      |  3 +--
 path.c      | 23 ++++++++++++++++++-----
 path.h      | 34 ++++++++++++----------------------
 submodule.c |  4 ++--
 5 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/editor.c b/editor.c
index 6b9ce81d5f..b79d97b0e7 100644
--- a/editor.c
+++ b/editor.c
@@ -142,10 +142,8 @@ int strbuf_edit_interactively(struct repository *r,
 	struct strbuf sb = STRBUF_INIT;
 	int fd, res = 0;
 
-	if (!is_absolute_path(path)) {
-		strbuf_repo_git_path(&sb, r, "%s", path);
-		path = sb.buf;
-	}
+	if (!is_absolute_path(path))
+		path = repo_git_path_append(r, &sb, "%s", path);
 
 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
 	if (fd < 0)
diff --git a/hook.c b/hook.c
index 9ddbdee06d..b3de1048bf 100644
--- a/hook.c
+++ b/hook.c
@@ -16,8 +16,7 @@ const char *find_hook(struct repository *r, const char *name)
 
 	int found_hook;
 
-	strbuf_reset(&path);
-	strbuf_repo_git_path(&path, r, "hooks/%s", name);
+	repo_git_path_replace(r, &path, "hooks/%s", name);
 	found_hook = access(path.buf, X_OK) >= 0;
 #ifdef STRIP_EXTENSION
 	if (!found_hook) {
diff --git a/path.c b/path.c
index 273b649e00..f386a5ff52 100644
--- a/path.c
+++ b/path.c
@@ -417,7 +417,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
-void repo_git_pathv(const struct repository *repo,
+void repo_git_pathv(struct repository *repo,
 		    const struct worktree *wt, struct strbuf *buf,
 		    const char *fmt, va_list args)
 {
@@ -432,7 +432,7 @@ void repo_git_pathv(const struct repository *repo,
 	strbuf_cleanup_path(buf);
 }
 
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
 		    const char *fmt, ...)
 {
 	struct strbuf path = STRBUF_INIT;
@@ -443,14 +443,27 @@ char *repo_git_path(const struct repository *repo,
 	return strbuf_detach(&path, NULL);
 }
 
-void strbuf_repo_git_path(struct strbuf *sb,
-			  const struct repository *repo,
-			  const char *fmt, ...)
+const char *repo_git_path_append(struct repository *repo,
+				 struct strbuf *sb,
+				 const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
 	repo_git_pathv(repo, NULL, sb, fmt, args);
 	va_end(args);
+	return sb->buf;
+}
+
+const char *repo_git_path_replace(struct repository *repo,
+				  struct strbuf *sb,
+				  const char *fmt, ...)
+{
+	va_list args;
+	strbuf_reset(sb);
+	va_start(args, fmt);
+	repo_git_pathv(repo, NULL, sb, fmt, args);
+	va_end(args);
+	return sb->buf;
 }
 
 char *mkpathdup(const char *fmt, ...)
diff --git a/path.h b/path.h
index 3c75495e1a..c1cb3ce3be 100644
--- a/path.h
+++ b/path.h
@@ -52,29 +52,16 @@ const char *repo_common_path_replace(const struct repository *repo,
  * For an exhaustive list of the adjustments made look at `common_list` and
  * `adjust_git_path` in path.c.
  */
-
-/*
- * Return a path into the git directory of repository `repo`.
- */
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
 		    const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-
-/*
- * Print a path into the git directory of repository `repo` into the provided
- * buffer.
- */
-void repo_git_pathv(const struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args);
-
-/*
- * Construct a path into the git directory of repository `repo` and append it
- * to the provided buffer `sb`.
- */
-void strbuf_repo_git_path(struct strbuf *sb,
-			  const struct repository *repo,
-			  const char *fmt, ...)
+const char *repo_git_path_append(struct repository *repo,
+				 struct strbuf *sb,
+				 const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
+const char *repo_git_path_replace(struct repository *repo,
+				  struct strbuf *sb,
+				  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
 /*
@@ -241,11 +228,14 @@ struct strbuf *get_pathname(void);
 #  include "strbuf.h"
 #  include "repository.h"
 
-/* Internal implementation detail that should not be used. */
+/* Internal implementation details that should not be used. */
 void repo_common_pathv(const struct repository *repo,
 		       struct strbuf *buf,
 		       const char *fmt,
 		       va_list args);
+void repo_git_pathv(struct repository *repo,
+		    const struct worktree *wt, struct strbuf *buf,
+		    const char *fmt, va_list args);
 
 /*
  * Return a statically allocated path into the main repository's
diff --git a/submodule.c b/submodule.c
index b361076c5b..211ead54a0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1315,7 +1315,7 @@ static int repo_has_absorbed_submodules(struct repository *r)
 	int ret;
 	struct strbuf buf = STRBUF_INIT;
 
-	strbuf_repo_git_path(&buf, r, "modules/");
+	repo_git_path_append(r, &buf, "modules/");
 	ret = file_exists(buf.buf) && !is_empty_dir(buf.buf);
 	strbuf_release(&buf);
 	return ret;
@@ -2629,6 +2629,6 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
 	 * administrators can explicitly set. Nothing has been decided,
 	 * so for now, just append the name at the end of the path.
 	 */
-	strbuf_repo_git_path(buf, r, "modules/");
+	repo_git_path_append(r, buf, "modules/");
 	strbuf_addstr(buf, submodule_name);
 }

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 03/16] path: refactor `repo_worktree_path()` family of functions
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
  2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
  2025-02-06  7:57 ` [PATCH 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
@ 2025-02-06  7:57 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:57 UTC (permalink / raw)
  To: git

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "worktree" family of
functions accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 path.c       | 28 ++++++++++++++++++++++------
 path.h       | 20 +++++++++-----------
 repository.c |  4 ++--
 3 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/path.c b/path.c
index f386a5ff52..be75808acc 100644
--- a/path.c
+++ b/path.c
@@ -519,28 +519,44 @@ char *repo_worktree_path(const struct repository *repo, const char *fmt, ...)
 	struct strbuf path = STRBUF_INIT;
 	va_list args;
 
+	va_start(args, fmt);
+	do_worktree_path(repo, &path, fmt, args);
+	va_end(args);
+
+	return strbuf_detach(&path, NULL);
+}
+
+const char *repo_worktree_path_append(const struct repository *repo,
+				      struct strbuf *sb,
+				      const char *fmt, ...)
+{
+	va_list args;
+
 	if (!repo->worktree)
 		return NULL;
 
 	va_start(args, fmt);
-	do_worktree_path(repo, &path, fmt, args);
+	do_worktree_path(repo, sb, fmt, args);
 	va_end(args);
 
-	return strbuf_detach(&path, NULL);
+	return sb->buf;
 }
 
-void strbuf_repo_worktree_path(struct strbuf *sb,
-			       const struct repository *repo,
-			       const char *fmt, ...)
+const char *repo_worktree_path_replace(const struct repository *repo,
+				       struct strbuf *sb,
+				       const char *fmt, ...)
 {
 	va_list args;
 
+	strbuf_reset(sb);
 	if (!repo->worktree)
-		return;
+		return NULL;
 
 	va_start(args, fmt);
 	do_worktree_path(repo, sb, fmt, args);
 	va_end(args);
+
+	return sb->buf;
 }
 
 /* Returns 0 on success, negative on failure. */
diff --git a/path.h b/path.h
index c1cb3ce3be..8761c4c660 100644
--- a/path.h
+++ b/path.h
@@ -75,24 +75,22 @@ const char *worktree_git_path(struct repository *r,
 	__attribute__((format (printf, 3, 4)));
 
 /*
- * Return a path into the worktree of repository `repo`.
+ * The `repo_worktree_path` family of functions will construct a path into a
+ * repository's worktree.
  *
- * If the repository doesn't have a worktree NULL is returned.
+ * Returns a `NULL` pointer in case the repository has no worktree.
  */
 char *repo_worktree_path(const struct repository *repo,
 				const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into the worktree of repository `repo` and append it
- * to the provided buffer `sb`.
- *
- * If the repository doesn't have a worktree nothing will be appended to `sb`.
- */
-void strbuf_repo_worktree_path(struct strbuf *sb,
-				      const struct repository *repo,
+const char *repo_worktree_path_append(const struct repository *repo,
+				      struct strbuf *sb,
 				      const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
+const char *repo_worktree_path_replace(const struct repository *repo,
+				       struct strbuf *sb,
+				       const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
 
 /*
  * Return a path into a submodule's git directory located at `path`.  `path`
diff --git a/repository.c b/repository.c
index 1a6a62bbd0..648cd88474 100644
--- a/repository.c
+++ b/repository.c
@@ -312,8 +312,8 @@ int repo_submodule_init(struct repository *subrepo,
 	struct strbuf worktree = STRBUF_INIT;
 	int ret = 0;
 
-	strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
-	strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
+	repo_worktree_path_append(superproject, &gitdir, "%s/.git", path);
+	repo_worktree_path_append(superproject, &worktree, "%s", path);
 
 	if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
 		/*

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (2 preceding siblings ...)
  2025-02-06  7:57 ` [PATCH 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

The `submodule_to_gitdir()` function implicitly uses `the_repository` to
resolve submodule paths. Refactor the function to instead accept a repo
as parameter to remove the dependency on global state.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/submodule--helper.c |  2 +-
 path.c                      |  2 +-
 refs.c                      |  2 +-
 submodule.c                 | 11 ++++++-----
 submodule.h                 |  3 ++-
 5 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f9b970f8a6..3a64f7e605 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1301,7 +1301,7 @@ static void sync_submodule(const char *path, const char *prefix,
 	remote_key = xstrfmt("remote.%s.url", default_remote);
 	free(default_remote);
 
-	submodule_to_gitdir(&sb, path);
+	submodule_to_gitdir(the_repository, &sb, path);
 	strbuf_addstr(&sb, "/config");
 
 	if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
diff --git a/path.c b/path.c
index be75808acc..d918d0409e 100644
--- a/path.c
+++ b/path.c
@@ -567,7 +567,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
 	struct strbuf git_submodule_dir = STRBUF_INIT;
 	int ret;
 
-	ret = submodule_to_gitdir(&git_submodule_dir, path);
+	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
 	if (ret)
 		goto cleanup;
 
diff --git a/refs.c b/refs.c
index daf6a84205..e1293e53aa 100644
--- a/refs.c
+++ b/refs.c
@@ -2146,7 +2146,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
 	if (!is_nonbare_repository_dir(&submodule_sb))
 		goto done;
 
-	if (submodule_to_gitdir(&submodule_sb, submodule))
+	if (submodule_to_gitdir(repo, &submodule_sb, submodule))
 		goto done;
 
 	subrepo = xmalloc(sizeof(*subrepo));
diff --git a/submodule.c b/submodule.c
index 211ead54a0..0530e8cf24 100644
--- a/submodule.c
+++ b/submodule.c
@@ -536,7 +536,8 @@ static struct repository *open_submodule(const char *path)
 	struct strbuf sb = STRBUF_INIT;
 	struct repository *out = xmalloc(sizeof(*out));
 
-	if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) {
+	if (submodule_to_gitdir(the_repository, &sb, path) ||
+	    repo_init(out, sb.buf, NULL)) {
 		strbuf_release(&sb);
 		free(out);
 		return NULL;
@@ -2572,7 +2573,8 @@ int get_superproject_working_tree(struct strbuf *buf)
  * Put the gitdir for a submodule (given relative to the main
  * repository worktree) into `buf`, or return -1 on error.
  */
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+int submodule_to_gitdir(struct repository *repo,
+			struct strbuf *buf, const char *submodule)
 {
 	const struct submodule *sub;
 	const char *git_dir;
@@ -2592,14 +2594,13 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
 		strbuf_addstr(buf, git_dir);
 	}
 	if (!is_git_directory(buf->buf)) {
-		sub = submodule_from_path(the_repository, null_oid(),
-					  submodule);
+		sub = submodule_from_path(repo, null_oid(), submodule);
 		if (!sub) {
 			ret = -1;
 			goto cleanup;
 		}
 		strbuf_reset(buf);
-		submodule_name_to_gitdir(buf, the_repository, sub->name);
+		submodule_name_to_gitdir(buf, repo, sub->name);
 	}
 
 cleanup:
diff --git a/submodule.h b/submodule.h
index 4deb1b5f84..db980c1d08 100644
--- a/submodule.h
+++ b/submodule.h
@@ -136,7 +136,8 @@ int push_unpushed_submodules(struct repository *r,
  * path of that submodule in 'buf'. Return -1 on error or when the
  * submodule is not initialized.
  */
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
+int submodule_to_gitdir(struct repository *repo,
+			struct strbuf *buf, const char *submodule);
 
 /*
  * Given a submodule name, create a path to where the submodule's gitdir lives

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (3 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06 12:05   ` Karthik Nayak
  2025-02-06 15:03   ` shejialuo
  2025-02-06  7:58 ` [PATCH 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
                   ` (13 subsequent siblings)
  18 siblings, 2 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "submodule" family of
functions accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/submodule--helper.c |  2 +-
 path.c                      | 37 +++++++++++++++++++++++++++++--------
 path.h                      | 30 ++++++++++++++++++------------
 t/helper/test-ref-store.c   |  7 +++----
 worktree.c                  |  3 ++-
 5 files changed, 53 insertions(+), 26 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3a64f7e605..c1a8029714 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1826,7 +1826,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
 
-	p = git_pathdup_submodule(clone_data_path, "config");
+	p = repo_submodule_path(the_repository, clone_data_path, "config");
 	if (!p)
 		die(_("could not get submodule directory for '%s'"), clone_data_path);
 
diff --git a/path.c b/path.c
index d918d0409e..d721507be8 100644
--- a/path.c
+++ b/path.c
@@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
 }
 
 /* Returns 0 on success, negative on failure. */
-static int do_submodule_path(struct strbuf *buf, const char *path,
+static int do_submodule_path(struct repository *repo,
+			     struct strbuf *buf, const char *path,
 			     const char *fmt, va_list args)
 {
 	struct strbuf git_submodule_common_dir = STRBUF_INIT;
 	struct strbuf git_submodule_dir = STRBUF_INIT;
 	int ret;
 
-	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
+	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
 	if (ret)
 		goto cleanup;
 
@@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
 	return ret;
 }
 
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+			  const char *path, const char *fmt, ...)
 {
 	int err;
 	va_list args;
 	struct strbuf buf = STRBUF_INIT;
 	va_start(args, fmt);
-	err = do_submodule_path(&buf, path, fmt, args);
+	err = do_submodule_path(repo, &buf, path, fmt, args);
 	va_end(args);
 	if (err) {
 		strbuf_release(&buf);
@@ -601,16 +603,35 @@ char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 	return strbuf_detach(&buf, NULL);
 }
 
-int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
-			      const char *fmt, ...)
+const char *repo_submodule_path_append(struct repository *repo,
+				       struct strbuf *buf,
+				       const char *path,
+				       const char *fmt, ...)
 {
 	int err;
 	va_list args;
 	va_start(args, fmt);
-	err = do_submodule_path(buf, path, fmt, args);
+	err = do_submodule_path(repo, buf, path, fmt, args);
 	va_end(args);
+	if (err)
+		return NULL;
+	return buf->buf;
+}
 
-	return err;
+const char *repo_submodule_path_replace(struct repository *repo,
+					struct strbuf *buf,
+					const char *path,
+					const char *fmt, ...)
+{
+	int err;
+	va_list args;
+	strbuf_reset(buf);
+	va_start(args, fmt);
+	err = do_submodule_path(repo, buf, path, fmt, args);
+	va_end(args);
+	if (err)
+		return NULL;
+	return buf->buf;
 }
 
 void repo_common_pathv(const struct repository *repo,
diff --git a/path.h b/path.h
index 8761c4c660..63a8f91947 100644
--- a/path.h
+++ b/path.h
@@ -93,20 +93,26 @@ const char *repo_worktree_path_replace(const struct repository *repo,
 	__attribute__((format (printf, 3, 4)));
 
 /*
- * Return a path into a submodule's git directory located at `path`.  `path`
- * must only reference a submodule of the main repository (the_repository).
- */
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
-	__attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into a submodule's git directory located at `path` and
- * append it to the provided buffer `sb`.  `path` must only reference a
- * submodule of the main repository (the_repository).
+ * The `repo_submodule_path` family of functions will construct a path into a
+ * submodule's git directory located at `path`. `path` must be a submodule path
+ * as found in the index and must be part of the given repository.
+ *
+ * Returns a `NULL` pointer in case the submodule cannot be found.
  */
-int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
-				     const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+			  const char *path,
+			  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
+const char *repo_submodule_path_append(struct repository *repo,
+				       struct strbuf *sb,
+				       const char *path,
+				       const char *fmt, ...)
+	__attribute__((format (printf, 4, 5)));
+const char *repo_submodule_path_replace(struct repository *repo,
+					struct strbuf *sb,
+					const char *path,
+					const char *fmt, ...)
+	__attribute__((format (printf, 4, 5)));
 
 void report_linked_checkout_garbage(struct repository *r);
 
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 1cc05f043a..e00fce592b 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -75,11 +75,10 @@ static const char **get_store(const char **argv, struct ref_store **refs)
 		*refs = get_main_ref_store(the_repository);
 	} else if (skip_prefix(argv[0], "submodule:", &gitdir)) {
 		struct strbuf sb = STRBUF_INIT;
-		int ret;
 
-		ret = strbuf_git_path_submodule(&sb, gitdir, "objects/");
-		if (ret)
-			die("strbuf_git_path_submodule failed: %d", ret);
+		if (!repo_submodule_path_append(the_repository,
+						&sb, gitdir, "objects/"))
+			die("computing submodule path failed");
 		add_to_alternates_memory(sb.buf);
 		strbuf_release(&sb);
 
diff --git a/worktree.c b/worktree.c
index f8d6e7127f..8f4fc10c44 100644
--- a/worktree.c
+++ b/worktree.c
@@ -487,7 +487,8 @@ int submodule_uses_worktrees(const char *path)
 	int ret = 0;
 	struct repository_format format = REPOSITORY_FORMAT_INIT;
 
-	submodule_gitdir = git_pathdup_submodule(path, "%s", "");
+	submodule_gitdir = repo_submodule_path(the_repository,
+					       path, "%s", "");
 	if (!submodule_gitdir)
 		return 0;
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 06/16] path: drop unused `strbuf_git_path()` function
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (4 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

The `strbuf_git_path()` function isn't used anywhere, and neither should
it grow any callers because it depends on `the_repository`. Remove it.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 path.h | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/path.h b/path.h
index 63a8f91947..cebc93cb0a 100644
--- a/path.h
+++ b/path.h
@@ -272,19 +272,6 @@ static inline char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	return buf->buf;
 }
 
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and append it to the provided buffer `sb`.
- */
-__attribute__((format (printf, 2, 3)))
-static inline void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
-{
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, sb, fmt, args);
-	va_end(args);
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (5 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Remove `git_pathdup()` in favor of `repo_git_path()`. The latter does
essentially the same, with the only exception that it does not rely on
`the_repository` but takes the repo as separate parameter.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 bisect.c              |  2 +-
 builtin/am.c          |  2 +-
 builtin/clone.c       |  2 +-
 builtin/config.c      |  8 ++++----
 builtin/fast-import.c |  4 ++--
 builtin/fsck.c        |  2 +-
 builtin/gc.c          |  4 ++--
 builtin/notes.c       |  2 +-
 builtin/replace.c     |  2 +-
 builtin/tag.c         |  2 +-
 builtin/worktree.c    |  4 ++--
 dir.c                 |  2 +-
 http-backend.c        |  2 +-
 notes-merge.c         |  2 +-
 object-file.c         |  2 +-
 path.h                | 16 +---------------
 16 files changed, 22 insertions(+), 36 deletions(-)

diff --git a/bisect.c b/bisect.c
index 7a3c77c6d8..269a98bf97 100644
--- a/bisect.c
+++ b/bisect.c
@@ -930,7 +930,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
 	if (!current_bad_oid)
 		return error(_("a %s revision is needed"), term_bad);
 
-	filename = git_pathdup("BISECT_ANCESTORS_OK");
+	filename = repo_git_path(the_repository, "BISECT_ANCESTORS_OK");
 
 	/* Check if file BISECT_ANCESTORS_OK exists. */
 	if (!stat(filename, &st) && S_ISREG(st.st_mode))
diff --git a/builtin/am.c b/builtin/am.c
index 390b463144..2921bb89ef 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -158,7 +158,7 @@ static void am_state_init(struct am_state *state)
 
 	memset(state, 0, sizeof(*state));
 
-	state->dir = git_pathdup("rebase-apply");
+	state->dir = repo_git_path(the_repository, "rebase-apply");
 
 	state->prec = 4;
 
diff --git a/builtin/clone.c b/builtin/clone.c
index fd001d800c..5ae6ee9db9 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -938,7 +938,7 @@ static void write_refspec_config(const char *src_ref_prefix,
 
 static void dissociate_from_references(void)
 {
-	char *alternates = git_pathdup("objects/info/alternates");
+	char *alternates = repo_git_path(the_repository, "objects/info/alternates");
 
 	if (!access(alternates, F_OK)) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
diff --git a/builtin/config.c b/builtin/config.c
index 16e6e30555..53a90094e3 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -775,13 +775,13 @@ static void location_options_init(struct config_location_options *opts,
 		opts->source.file = opts->file_to_free = git_system_config();
 		opts->source.scope = CONFIG_SCOPE_SYSTEM;
 	} else if (opts->use_local_config) {
-		opts->source.file = opts->file_to_free = git_pathdup("config");
+		opts->source.file = opts->file_to_free = repo_git_path(the_repository, "config");
 		opts->source.scope = CONFIG_SCOPE_LOCAL;
 	} else if (opts->use_worktree_config) {
 		struct worktree **worktrees = get_worktrees();
 		if (the_repository->repository_format_worktree_config)
 			opts->source.file = opts->file_to_free =
-				git_pathdup("config.worktree");
+				repo_git_path(the_repository, "config.worktree");
 		else if (worktrees[0] && worktrees[1])
 			die(_("--worktree cannot be used with multiple "
 			      "working trees unless the config\n"
@@ -790,7 +790,7 @@ static void location_options_init(struct config_location_options *opts,
 			      "section in \"git help worktree\" for details"));
 		else
 			opts->source.file = opts->file_to_free =
-				git_pathdup("config");
+				repo_git_path(the_repository, "config");
 		opts->source.scope = CONFIG_SCOPE_LOCAL;
 		free_worktrees(worktrees);
 	} else if (opts->source.file) {
@@ -1087,7 +1087,7 @@ static int show_editor(struct config_location_options *opts)
 	git_config(git_default_config, NULL);
 	config_file = opts->source.file ?
 			xstrdup(opts->source.file) :
-			git_pathdup("config");
+			repo_git_path(the_repository, "config");
 	if (opts->use_global_config) {
 		int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
 		if (fd >= 0) {
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index a6a84058cd..c6f5147e8b 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -328,7 +328,7 @@ static void write_branch_report(FILE *rpt, struct branch *b)
 
 static void write_crash_report(const char *err)
 {
-	char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+	char *loc = repo_git_path(the_repository, "fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
 	FILE *rpt = fopen(loc, "w");
 	struct branch *b;
 	unsigned long lu;
@@ -3280,7 +3280,7 @@ static char* make_fast_import_path(const char *path)
 {
 	if (!relative_marks_paths || is_absolute_path(path))
 		return prefix_filename(global_prefix, path);
-	return git_pathdup("info/fast-import/%s", path);
+	return repo_git_path(the_repository, "info/fast-import/%s", path);
 }
 
 static void option_import_marks(const char *marks,
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 7a4dcb0716..c12203e012 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -326,7 +326,7 @@ static void check_unreachable_object(struct object *obj)
 				  printable_type(&obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
-			char *filename = git_pathdup("lost-found/%s/%s",
+			char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				describe_object(&obj->oid));
 			FILE *f;
diff --git a/builtin/gc.c b/builtin/gc.c
index 0bf3533494..57f6aee174 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -546,7 +546,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	if (xgethostname(my_host, sizeof(my_host)))
 		xsnprintf(my_host, sizeof(my_host), "unknown");
 
-	pidfile_path = git_pathdup("gc.pid");
+	pidfile_path = repo_git_path(the_repository, "gc.pid");
 	fd = hold_lock_file_for_update(&lock, pidfile_path,
 				       LOCK_DIE_ON_ERROR);
 	if (!force) {
@@ -607,7 +607,7 @@ static int report_last_gc_error(void)
 	int ret = 0;
 	ssize_t len;
 	struct stat st;
-	char *gc_log_path = git_pathdup("gc.log");
+	char *gc_log_path = repo_git_path(the_repository, "gc.log");
 
 	if (stat(gc_log_path, &st)) {
 		if (errno == ENOENT)
diff --git a/builtin/notes.c b/builtin/notes.c
index d051abf6df..18bcbb2f91 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -197,7 +197,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
 		struct strbuf buf = STRBUF_INIT;
 
 		/* write the template message before editing: */
-		d->edit_path = git_pathdup("NOTES_EDITMSG");
+		d->edit_path = repo_git_path(the_repository, "NOTES_EDITMSG");
 		fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
 		if (d->msg_nr)
diff --git a/builtin/replace.c b/builtin/replace.c
index a4eaadff91..15ec0922ce 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -345,7 +345,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
 	}
 	strbuf_release(&ref);
 
-	tmpfile = git_pathdup("REPLACE_EDITOBJ");
+	tmpfile = repo_git_path(the_repository, "REPLACE_EDITOBJ");
 	if (export_object(&old_oid, type, raw, tmpfile)) {
 		free(tmpfile);
 		return -1;
diff --git a/builtin/tag.c b/builtin/tag.c
index e8a344b926..d3e0943b73 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -667,7 +667,7 @@ int cmd_tag(int argc,
 	if (create_tag_object) {
 		if (force_sign_annotate && !annotate)
 			opt.sign = 1;
-		path = git_pathdup("TAG_EDITMSG");
+		path = repo_git_path(the_repository, "TAG_EDITMSG");
 		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
 			   &trailer_args, path);
 	}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index c043d4d523..c84e6aa2cb 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -337,7 +337,7 @@ static void check_candidate_path(const char *path,
 
 static void copy_sparse_checkout(const char *worktree_git_dir)
 {
-	char *from_file = git_pathdup("info/sparse-checkout");
+	char *from_file = repo_git_path(the_repository, "info/sparse-checkout");
 	char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
 
 	if (file_exists(from_file)) {
@@ -353,7 +353,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)
 
 static void copy_filtered_worktree_config(const char *worktree_git_dir)
 {
-	char *from_file = git_pathdup("config.worktree");
+	char *from_file = repo_git_path(the_repository, "config.worktree");
 	char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
 
 	if (file_exists(from_file)) {
diff --git a/dir.c b/dir.c
index 5b2181e589..4122f6513d 100644
--- a/dir.c
+++ b/dir.c
@@ -3455,7 +3455,7 @@ void setup_standard_excludes(struct dir_struct *dir)
 
 char *get_sparse_checkout_filename(void)
 {
-	return git_pathdup("info/sparse-checkout");
+	return repo_git_path(the_repository, "info/sparse-checkout");
 }
 
 int get_sparse_checkout_patterns(struct pattern_list *pl)
diff --git a/http-backend.c b/http-backend.c
index 33cf378282..50b2858fad 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -183,7 +183,7 @@ static void send_strbuf(struct strbuf *hdr,
 static void send_local_file(struct strbuf *hdr, const char *the_type,
 				const char *name)
 {
-	char *p = git_pathdup("%s", name);
+	char *p = repo_git_path(the_repository, "%s", name);
 	size_t buf_alloc = 8192;
 	char *buf = xmalloc(buf_alloc);
 	int fd;
diff --git a/notes-merge.c b/notes-merge.c
index 8d701ed428..c997c0c1e3 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -309,7 +309,7 @@ static void write_buf_to_worktree(const struct object_id *obj,
 				  const char *buf, unsigned long size)
 {
 	int fd;
-	char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
+	char *path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
 	if (safe_create_leading_directories_const(path))
 		die_errno("unable to create directory for '%s'", path);
 
diff --git a/object-file.c b/object-file.c
index 6ce1caacae..335cc2a5da 100644
--- a/object-file.c
+++ b/object-file.c
@@ -717,7 +717,7 @@ static void read_info_alternates(struct repository *r,
 void add_to_alternates_file(const char *reference)
 {
 	struct lock_file lock = LOCK_INIT;
-	char *alts = git_pathdup("objects/info/alternates");
+	char *alts = repo_git_path(the_repository, "objects/info/alternates");
 	FILE *in, *out;
 	int found = 0;
 
diff --git a/path.h b/path.h
index cebc93cb0a..df16dd23bd 100644
--- a/path.h
+++ b/path.h
@@ -292,24 +292,10 @@ static inline const char *git_path(const char *fmt, ...)
 	{ \
 		static char *ret; \
 		if (!ret) \
-			ret = git_pathdup(filename); \
+			ret = repo_git_path(the_repository, filename); \
 		return ret; \
 	}
 
-/*
- * Return a path into the main repository's (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline char *git_pathdup(const char *fmt, ...)
-{
-	struct strbuf path = STRBUF_INIT;
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, &path, fmt, args);
-	va_end(args);
-	return strbuf_detach(&path, NULL);
-}
-
 # endif /* USE_THE_REPOSITORY_VARIABLE */
 
 #endif /* PATH_H */

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (6 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Remove `git_path_buf()` in favor of `repo_git_path_replace()`. The
latter does essentially the same, with the only exception that it does
not rely on `the_repository` but takes the repo as separate parameter.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/worktree.c       |  2 +-
 compat/precompose_utf8.c |  6 +++---
 notes-merge.c            |  4 ++--
 object-file.c            |  4 ++--
 path.h                   | 16 ----------------
 setup.c                  | 33 ++++++++++++++++-----------------
 6 files changed, 24 insertions(+), 41 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index c84e6aa2cb..7959b10d26 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -457,7 +457,7 @@ static int add_worktree(const char *path, const char *refname,
 		BUG("How come '%s' becomes empty after sanitization?", sb.buf);
 	strbuf_reset(&sb);
 	name = sb_name.buf;
-	git_path_buf(&sb_repo, "worktrees/%s", name);
+	repo_git_path_replace(the_repository, &sb_repo, "worktrees/%s", name);
 	len = sb_repo.len;
 	if (safe_create_leading_directories_const(sb_repo.buf))
 		die_errno(_("could not create leading directories of '%s'"),
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index f7cc7b3be5..12e38e0ea3 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -50,15 +50,15 @@ void probe_utf8_pathname_composition(void)
 	int output_fd;
 	if (precomposed_unicode != -1)
 		return; /* We found it defined in the global config, respect it */
-	git_path_buf(&path, "%s", auml_nfc);
+	repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
 	output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);
 	if (output_fd >= 0) {
 		close(output_fd);
-		git_path_buf(&path, "%s", auml_nfd);
+		repo_git_path_replace(the_repository, &path, "%s", auml_nfd);
 		precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
 		git_config_set("core.precomposeunicode",
 			       precomposed_unicode ? "true" : "false");
-		git_path_buf(&path, "%s", auml_nfc);
+		repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
 		if (unlink(path.buf))
 			die_errno(_("failed to unlink '%s'"), path.buf);
 	}
diff --git a/notes-merge.c b/notes-merge.c
index c997c0c1e3..8c22a171c1 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -695,7 +695,7 @@ int notes_merge_commit(struct notes_merge_options *o,
 	const char *msg = strstr(buffer, "\n\n");
 	int baselen;
 
-	git_path_buf(&path, NOTES_MERGE_WORKTREE);
+	repo_git_path_replace(the_repository, &path, NOTES_MERGE_WORKTREE);
 	if (o->verbosity >= 3)
 		printf("Committing notes in notes merge worktree at %s\n",
 			path.buf);
@@ -757,7 +757,7 @@ int notes_merge_abort(struct notes_merge_options *o)
 	struct strbuf buf = STRBUF_INIT;
 	int ret;
 
-	git_path_buf(&buf, NOTES_MERGE_WORKTREE);
+	repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE);
 	if (o->verbosity >= 3)
 		printf("Removing notes merge worktree at %s/*\n", buf.buf);
 	ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
diff --git a/object-file.c b/object-file.c
index 335cc2a5da..dc9fcaf3e9 100644
--- a/object-file.c
+++ b/object-file.c
@@ -476,14 +476,14 @@ int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
 	 * restrictive except to remove write permission.
 	 */
 	int mode = 0444;
-	git_path_buf(temp_filename, "objects/%s", pattern);
+	repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
 	fd = git_mkstemp_mode(temp_filename->buf, mode);
 	if (0 <= fd)
 		return fd;
 
 	/* slow path */
 	/* some mkstemp implementations erase temp_filename on failure */
-	git_path_buf(temp_filename, "objects/%s", pattern);
+	repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
 	safe_create_leading_directories(temp_filename->buf);
 	return xmkstemp_mode(temp_filename->buf, mode);
 }
diff --git a/path.h b/path.h
index df16dd23bd..904eeac068 100644
--- a/path.h
+++ b/path.h
@@ -256,22 +256,6 @@ static inline const char *git_common_path(const char *fmt, ...)
 	return pathname->buf;
 }
 
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and place it in the provided buffer `buf`, the contents of the buffer will
- * be overridden.
- */
-__attribute__((format (printf, 2, 3)))
-static inline char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
-{
-	va_list args;
-	strbuf_reset(buf);
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, buf, fmt, args);
-	va_end(args);
-	return buf->buf;
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
diff --git a/setup.c b/setup.c
index 74b5ba5325..30889386f7 100644
--- a/setup.c
+++ b/setup.c
@@ -2264,7 +2264,7 @@ static int is_reinit(void)
 	char junk[2];
 	int ret;
 
-	git_path_buf(&buf, "HEAD");
+	repo_git_path_replace(the_repository, &buf, "HEAD");
 	ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
 	strbuf_release(&buf);
 	return ret;
@@ -2316,8 +2316,7 @@ static int create_default_files(const char *template_path,
 				int init_shared_repository)
 {
 	struct stat st1;
-	struct strbuf buf = STRBUF_INIT;
-	char *path;
+	struct strbuf path = STRBUF_INIT;
 	int reinit;
 	int filemode;
 	const char *work_tree = repo_get_work_tree(the_repository);
@@ -2358,14 +2357,14 @@ static int create_default_files(const char *template_path,
 	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
 
 	/* Check filemode trustability */
-	path = git_path_buf(&buf, "config");
+	repo_git_path_replace(the_repository, &path, "config");
 	filemode = TEST_FILEMODE;
-	if (TEST_FILEMODE && !lstat(path, &st1)) {
+	if (TEST_FILEMODE && !lstat(path.buf, &st1)) {
 		struct stat st2;
-		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
-				!lstat(path, &st2) &&
+		filemode = (!chmod(path.buf, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path.buf, &st2) &&
 				st1.st_mode != st2.st_mode &&
-				!chmod(path, st1.st_mode));
+				!chmod(path.buf, st1.st_mode));
 		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
 			filemode = 0;
 	}
@@ -2384,24 +2383,24 @@ static int create_default_files(const char *template_path,
 
 	if (!reinit) {
 		/* Check if symlink is supported in the work tree */
-		path = git_path_buf(&buf, "tXXXXXX");
-		if (!close(xmkstemp(path)) &&
-		    !unlink(path) &&
-		    !symlink("testing", path) &&
-		    !lstat(path, &st1) &&
+		repo_git_path_replace(the_repository, &path, "tXXXXXX");
+		if (!close(xmkstemp(path.buf)) &&
+		    !unlink(path.buf) &&
+		    !symlink("testing", path.buf) &&
+		    !lstat(path.buf, &st1) &&
 		    S_ISLNK(st1.st_mode))
-			unlink(path); /* good */
+			unlink(path.buf); /* good */
 		else
 			git_config_set("core.symlinks", "false");
 
 		/* Check if the filesystem is case-insensitive */
-		path = git_path_buf(&buf, "CoNfIg");
-		if (!access(path, F_OK))
+		repo_git_path_replace(the_repository, &path, "CoNfIg");
+		if (!access(path.buf, F_OK))
 			git_config_set("core.ignorecase", "true");
 		probe_utf8_pathname_composition();
 	}
 
-	strbuf_release(&buf);
+	strbuf_release(&path);
 	return reinit;
 }
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (7 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-07  7:15   ` Karthik Nayak
  2025-02-06  7:58 ` [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

The `get_worktree_git_dir()` function returns a string constant that
does not need to be free'd by the caller. For `repo_get_git_dir()` and
`repo_get_common_dir()` this is because we return strings owned by
`the_repository`.

But for `git_common_path()` it's a bit less obvious though, because that
function does end up allocating memory. This doesn't result in a memory
leak either because we write into a buffer returned by `get_pathname()`,
which returns one out of four static buffers.

We're about to drop `git_common_path()` in favor of `repo_common_path()`,
which doesn't use the same mechanism but instead returns an allocated
string owned by the caller. While we could adapt `get_worktree_git_dir()`
to also use `get_pathname()` and print the derived common path into that
buffer, the whole schema feels a lot like premature optimization in this
context. There are some callsites where we call `get_worktree_git_dir()`
in a loop that iterates through all worktrees. But none of these loops
seem to be even remotely in the hot path, so saving a single allocation
there does not feel worth it.

Refactor the function to instead consistently return an allocated path
so that we can start using `repo_common_path()` in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 branch.c               |  7 +++++--
 builtin/fsck.c         |  8 ++++++--
 builtin/receive-pack.c |  4 +++-
 builtin/worktree.c     | 10 ++++++++--
 reachable.c            |  6 +++++-
 revision.c             |  7 ++++++-
 worktree.c             | 11 ++++++-----
 worktree.h             |  2 +-
 8 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/branch.c b/branch.c
index 77716966fe..91297d55ac 100644
--- a/branch.c
+++ b/branch.c
@@ -397,7 +397,7 @@ static void prepare_checked_out_branches(void)
 	worktrees = get_worktrees();
 
 	while (worktrees[i]) {
-		char *old;
+		char *old, *wt_gitdir;
 		struct wt_status_state state = { 0 };
 		struct worktree *wt = worktrees[i++];
 		struct string_list update_refs = STRING_LIST_INIT_DUP;
@@ -437,7 +437,8 @@ static void prepare_checked_out_branches(void)
 		}
 		wt_status_state_free_buffers(&state);
 
-		if (!sequencer_get_update_refs_state(get_worktree_git_dir(wt),
+		wt_gitdir = get_worktree_git_dir(wt);
+		if (!sequencer_get_update_refs_state(wt_gitdir,
 						     &update_refs)) {
 			struct string_list_item *item;
 			for_each_string_list_item(item, &update_refs) {
@@ -448,6 +449,8 @@ static void prepare_checked_out_branches(void)
 			}
 			string_list_clear(&update_refs, 1);
 		}
+
+		free(wt_gitdir);
 	}
 
 	free_worktrees(worktrees);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index c12203e012..eea1d43647 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1057,7 +1057,7 @@ int cmd_fsck(int argc,
 			struct worktree *wt = *p;
 			struct index_state istate =
 				INDEX_STATE_INIT(the_repository);
-			char *path;
+			char *path, *wt_gitdir;
 
 			/*
 			 * Make a copy since the buffer is reusable
@@ -1065,9 +1065,13 @@ int cmd_fsck(int argc,
 			 * while we're examining the index.
 			 */
 			path = xstrdup(worktree_git_path(the_repository, wt, "index"));
-			read_index_from(&istate, path, get_worktree_git_dir(wt));
+			wt_gitdir = get_worktree_git_dir(wt);
+
+			read_index_from(&istate, path, wt_gitdir);
 			fsck_index(&istate, path, wt->is_current);
+
 			discard_index(&istate);
+			free(wt_gitdir);
 			free(path);
 		}
 		free_worktrees(worktrees);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b7ea774609..d65a441f32 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1435,7 +1435,8 @@ static const char *push_to_checkout(unsigned char *hash,
 
 static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
 {
-	const char *retval, *git_dir;
+	const char *retval;
+	char *git_dir;
 	struct strvec env = STRVEC_INIT;
 	int invoked_hook;
 
@@ -1453,6 +1454,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 		retval = push_to_deploy(sha1, &env, worktree->path);
 
 	strvec_clear(&env);
+	free(git_dir);
 	return retval;
 }
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7959b10d26..2cea9441a6 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -657,8 +657,9 @@ static int can_use_local_refs(const struct add_opts *opts)
 		if (!opts->quiet) {
 			struct strbuf path = STRBUF_INIT;
 			struct strbuf contents = STRBUF_INIT;
+			char *wt_gitdir = get_worktree_git_dir(NULL);
 
-			strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+			strbuf_add_real_path(&path, wt_gitdir);
 			strbuf_addstr(&path, "/HEAD");
 			strbuf_read_file(&contents, path.buf, 64);
 			strbuf_stripspace(&contents, NULL);
@@ -670,6 +671,7 @@ static int can_use_local_refs(const struct add_opts *opts)
 				  path.buf, contents.buf);
 			strbuf_release(&path);
 			strbuf_release(&contents);
+			free(wt_gitdir);
 		}
 		return 1;
 	}
@@ -1157,6 +1159,9 @@ static void validate_no_submodules(const struct worktree *wt)
 	struct index_state istate = INDEX_STATE_INIT(the_repository);
 	struct strbuf path = STRBUF_INIT;
 	int i, found_submodules = 0;
+	char *wt_gitdir;
+
+	wt_gitdir = get_worktree_git_dir(wt);
 
 	if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
 		/*
@@ -1166,7 +1171,7 @@ static void validate_no_submodules(const struct worktree *wt)
 		 */
 		found_submodules = 1;
 	} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
-				   get_worktree_git_dir(wt)) > 0) {
+				   wt_gitdir) > 0) {
 		for (i = 0; i < istate.cache_nr; i++) {
 			struct cache_entry *ce = istate.cache[i];
 			int err;
@@ -1185,6 +1190,7 @@ static void validate_no_submodules(const struct worktree *wt)
 	}
 	discard_index(&istate);
 	strbuf_release(&path);
+	free(wt_gitdir);
 
 	if (found_submodules)
 		die(_("working trees containing submodules cannot be moved or removed"));
diff --git a/reachable.c b/reachable.c
index ecf7ccf504..9ee04c89ec 100644
--- a/reachable.c
+++ b/reachable.c
@@ -65,8 +65,10 @@ static void add_rebase_files(struct rev_info *revs)
 	struct worktree **worktrees = get_worktrees();
 
 	for (struct worktree **wt = worktrees; *wt; wt++) {
+		char *wt_gitdir = get_worktree_git_dir(*wt);
+
 		strbuf_reset(&buf);
-		strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+		strbuf_addstr(&buf, wt_gitdir);
 		strbuf_complete(&buf, '/');
 		len = buf.len;
 		for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
@@ -74,6 +76,8 @@ static void add_rebase_files(struct rev_info *revs)
 			strbuf_addstr(&buf, path[i]);
 			add_one_file(buf.buf, revs);
 		}
+
+		free(wt_gitdir);
 	}
 	strbuf_release(&buf);
 	free_worktrees(worktrees);
diff --git a/revision.c b/revision.c
index 474fa1e767..be72f226f3 100644
--- a/revision.c
+++ b/revision.c
@@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
 	for (p = worktrees; *p; p++) {
 		struct worktree *wt = *p;
 		struct index_state istate = INDEX_STATE_INIT(revs->repo);
+		char *gitdir;
 
 		if (wt->is_current)
 			continue; /* current index already taken care of */
 
+		gitdir = get_worktree_git_dir(wt);
+
 		if (read_index_from(&istate,
 				    worktree_git_path(the_repository, wt, "index"),
-				    get_worktree_git_dir(wt)) > 0)
+				    gitdir) > 0)
 			do_add_index_objects_to_pending(revs, &istate, flags);
+
 		discard_index(&istate);
+		free(gitdir);
 	}
 	free_worktrees(worktrees);
 }
diff --git a/worktree.c b/worktree.c
index 8f4fc10c44..3b94535963 100644
--- a/worktree.c
+++ b/worktree.c
@@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
 static int is_current_worktree(struct worktree *wt)
 {
 	char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
-	const char *wt_git_dir = get_worktree_git_dir(wt);
+	char *wt_git_dir = get_worktree_git_dir(wt);
 	int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+	free(wt_git_dir);
 	free(git_dir);
 	return is_current;
 }
@@ -175,14 +176,14 @@ struct worktree **get_worktrees(void)
 	return get_worktrees_internal(0);
 }
 
-const char *get_worktree_git_dir(const struct worktree *wt)
+char *get_worktree_git_dir(const struct worktree *wt)
 {
 	if (!wt)
-		return repo_get_git_dir(the_repository);
+		return xstrdup(repo_get_git_dir(the_repository));
 	else if (!wt->id)
-		return repo_get_common_dir(the_repository);
+		return xstrdup(repo_get_common_dir(the_repository));
 	else
-		return git_common_path("worktrees/%s", wt->id);
+		return xstrdup(git_common_path("worktrees/%s", wt->id));
 }
 
 static struct worktree *find_worktree_by_suffix(struct worktree **list,
diff --git a/worktree.h b/worktree.h
index 38145df80f..16368588a0 100644
--- a/worktree.h
+++ b/worktree.h
@@ -39,7 +39,7 @@ int submodule_uses_worktrees(const char *path);
  * Return git dir of the worktree. Note that the path may be relative.
  * If wt is NULL, git dir of current worktree is returned.
  */
-const char *get_worktree_git_dir(const struct worktree *wt);
+char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
  * Search for the worktree identified unambiguously by `arg` -- typically

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (8 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06 15:54   ` shejialuo
  2025-02-06  7:58 ` [PATCH 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Remove `git_common_path()` in favor of the `repo_common_path()` family
of functions, which makes the implicit dependency on `the_repository` go
away.

Note that `git_common_path()` used to return a string allocated via
`get_pathname()`, which uses a rotating set of statically allocated
buffers. Consequently, callers didn't have to free the returned string.
The same isn't true for `repo_common_path()`, so we also have to add
logic to free the returned strings.

This refactoring also allows us to remove `repo_common_pathv()` from the
public interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/worktree.c | 16 ++++++++++++----
 path.c             |  8 ++++----
 path.h             | 19 -------------------
 worktree.c         | 32 ++++++++++++++++++++++++--------
 4 files changed, 40 insertions(+), 35 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 2cea9441a6..761e302a36 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -151,7 +151,7 @@ static int delete_git_dir(const char *id)
 	struct strbuf sb = STRBUF_INIT;
 	int ret;
 
-	strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
+	repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
 	ret = remove_dir_recursively(&sb, 0);
 	if (ret < 0 && errno == ENOTDIR)
 		ret = unlink(sb.buf);
@@ -1102,6 +1102,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
 		OPT_END()
 	};
 	struct worktree **worktrees, *wt;
+	char *path;
 
 	ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
 	if (ac != 1)
@@ -1122,9 +1123,11 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
 		die(_("'%s' is already locked"), av[0]);
 	}
 
-	write_file(git_common_path("worktrees/%s/locked", wt->id),
-		   "%s", reason);
+	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+	write_file(path, "%s", reason);
+
 	free_worktrees(worktrees);
+	free(path);
 	return 0;
 }
 
@@ -1135,6 +1138,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
 		OPT_END()
 	};
 	struct worktree **worktrees, *wt;
+	char *path;
 	int ret;
 
 	ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
@@ -1149,8 +1153,12 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
 		die(_("The main working tree cannot be locked or unlocked"));
 	if (!worktree_lock_reason(wt))
 		die(_("'%s' is not locked"), av[0]);
-	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+
+	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+	ret = unlink_or_warn(path);
+
 	free_worktrees(worktrees);
+	free(path);
 	return ret;
 }
 
diff --git a/path.c b/path.c
index d721507be8..f6b795d75f 100644
--- a/path.c
+++ b/path.c
@@ -634,10 +634,10 @@ const char *repo_submodule_path_replace(struct repository *repo,
 	return buf->buf;
 }
 
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *sb,
-		       const char *fmt,
-		       va_list args)
+static void repo_common_pathv(const struct repository *repo,
+			      struct strbuf *sb,
+			      const char *fmt,
+			      va_list args)
 {
 	strbuf_addstr(sb, repo->commondir);
 	if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
diff --git a/path.h b/path.h
index 904eeac068..496f27fdfd 100644
--- a/path.h
+++ b/path.h
@@ -233,29 +233,10 @@ struct strbuf *get_pathname(void);
 #  include "repository.h"
 
 /* Internal implementation details that should not be used. */
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *buf,
-		       const char *fmt,
-		       va_list args);
 void repo_git_pathv(struct repository *repo,
 		    const struct worktree *wt, struct strbuf *buf,
 		    const char *fmt, va_list args);
 
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) common git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_common_path(const char *fmt, ...)
-{
-	struct strbuf *pathname = get_pathname();
-	va_list args;
-	va_start(args, fmt);
-	repo_common_pathv(the_repository, pathname, fmt, args);
-	va_end(args);
-	return pathname->buf;
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
diff --git a/worktree.c b/worktree.c
index 3b94535963..d5d07d7a84 100644
--- a/worktree.c
+++ b/worktree.c
@@ -183,7 +183,7 @@ char *get_worktree_git_dir(const struct worktree *wt)
 	else if (!wt->id)
 		return xstrdup(repo_get_common_dir(the_repository));
 	else
-		return xstrdup(git_common_path("worktrees/%s", wt->id));
+		return repo_common_path(the_repository, "worktrees/%s", wt->id);
 }
 
 static struct worktree *find_worktree_by_suffix(struct worktree **list,
@@ -314,6 +314,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 {
 	struct strbuf wt_path = STRBUF_INIT;
 	struct strbuf realpath = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
 	char *path = NULL;
 	int err, ret = -1;
 
@@ -343,7 +344,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 	if (!is_absolute_path(wt->path)) {
 		strbuf_addf_gently(errmsg,
 				   _("'%s' file does not contain absolute path to the working tree location"),
-				   git_common_path("worktrees/%s/gitdir", wt->id));
+				   repo_common_path_replace(the_repository, &buf, "worktrees/%s/gitdir", wt->id));
 		goto done;
 	}
 
@@ -365,14 +366,16 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 		goto done;
 	}
 
-	strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
+	strbuf_realpath(&realpath, repo_common_path_replace(the_repository, &buf, "worktrees/%s", wt->id), 1);
 	ret = fspathcmp(path, realpath.buf);
 
 	if (ret)
 		strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
-				   wt->path, git_common_path("worktrees/%s", wt->id));
+				   wt->path, repo_common_path_replace(the_repository, &buf,
+								      "worktrees/%s", wt->id));
 done:
 	free(path);
+	strbuf_release(&buf);
 	strbuf_release(&wt_path);
 	strbuf_release(&realpath);
 	return ret;
@@ -384,11 +387,13 @@ void update_worktree_location(struct worktree *wt, const char *path_,
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf dotgit = STRBUF_INIT;
 	struct strbuf gitdir = STRBUF_INIT;
+	char *wt_gitdir;
 
 	if (is_main_worktree(wt))
 		BUG("can't relocate main worktree");
 
-	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+	wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+	strbuf_realpath(&gitdir, wt_gitdir, 1);
 	strbuf_realpath(&path, path_, 1);
 	strbuf_addf(&dotgit, "%s/.git", path.buf);
 	if (fspathcmp(wt->path, path.buf)) {
@@ -400,6 +405,7 @@ void update_worktree_location(struct worktree *wt, const char *path_,
 	strbuf_release(&path);
 	strbuf_release(&dotgit);
 	strbuf_release(&gitdir);
+	free(wt_gitdir);
 }
 
 int is_worktree_being_rebased(const struct worktree *wt,
@@ -585,6 +591,7 @@ static void repair_gitfile(struct worktree *wt,
 	struct strbuf backlink = STRBUF_INIT;
 	char *dotgit_contents = NULL;
 	const char *repair = NULL;
+	char *path = NULL;
 	int err;
 
 	/* missing worktree can't be repaired */
@@ -596,7 +603,8 @@ static void repair_gitfile(struct worktree *wt,
 		goto done;
 	}
 
-	strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+	path = repo_common_path(the_repository, "worktrees/%s", wt->id);
+	strbuf_realpath(&repo, path, 1);
 	strbuf_addf(&dotgit, "%s/.git", wt->path);
 	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
 	dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
@@ -626,6 +634,7 @@ static void repair_gitfile(struct worktree *wt,
 
 done:
 	free(dotgit_contents);
+	free(path);
 	strbuf_release(&repo);
 	strbuf_release(&dotgit);
 	strbuf_release(&gitdir);
@@ -657,11 +666,13 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
 	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf dotgit = STRBUF_INIT;
 	int is_relative_path;
+	char *path = NULL;
 
 	if (is_main_worktree(wt))
 		goto done;
 
-	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+	path = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+	strbuf_realpath(&gitdir, path, 1);
 
 	if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
 		goto done;
@@ -680,6 +691,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
 done:
 	strbuf_release(&gitdir);
 	strbuf_release(&dotgit);
+	free(path);
 }
 
 void repair_worktrees_after_gitdir_move(const char *old_path)
@@ -871,7 +883,11 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
 	ssize_t read_result;
 
 	*wtpath = NULL;
-	strbuf_realpath(&repo, git_common_path("worktrees/%s", id), 1);
+
+	path = repo_common_path(the_repository, "worktrees/%s", id);
+	strbuf_realpath(&repo, path, 1);
+	FREE_AND_NULL(path);
+
 	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
 	if (!is_directory(repo.buf)) {
 		strbuf_addstr(reason, _("not a valid directory"));

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (9 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Same as with `get_worktree_git_dir()` a couple of commits ago, the
`rerere_path()` function returns paths that need not be free'd by the
caller because `git_path()` internally uses `get_pathname()`.

Refactor the function to instead accept a caller-provided buffer that
the path will be written into, passing on ownership to the caller. This
refactoring prepares us for the removal of `git_path()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rerere.c | 11 ++++---
 rerere.c         | 87 ++++++++++++++++++++++++++++++++++++++------------------
 rerere.h         |  3 +-
 3 files changed, 69 insertions(+), 32 deletions(-)

diff --git a/builtin/rerere.c b/builtin/rerere.c
index 41127e24e5..1312e79d89 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -4,9 +4,9 @@
 #include "config.h"
 #include "gettext.h"
 #include "parse-options.h"
-
-#include "string-list.h"
 #include "rerere.h"
+#include "strbuf.h"
+#include "string-list.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 #include "pathspec.h"
@@ -112,15 +112,18 @@ int cmd_rerere(int argc,
 				merge_rr.items[i].util = NULL;
 		}
 	} else if (!strcmp(argv[0], "diff")) {
+		struct strbuf buf = STRBUF_INIT;
 		if (setup_rerere(the_repository, &merge_rr,
 				 flags | RERERE_READONLY) < 0)
 			return 0;
 		for (size_t i = 0; i < merge_rr.nr; i++) {
 			const char *path = merge_rr.items[i].string;
 			const struct rerere_id *id = merge_rr.items[i].util;
-			if (diff_two(rerere_path(id, "preimage"), path, path, path))
-				die(_("unable to generate diff for '%s'"), rerere_path(id, NULL));
+			if (diff_two(rerere_path(&buf, id, "preimage"), path, path, path))
+				die(_("unable to generate diff for '%s'"), rerere_path(&buf, id, NULL));
 		}
+
+		strbuf_release(&buf);
 	} else
 		usage_with_options(rerere_usage, options);
 
diff --git a/rerere.c b/rerere.c
index e7fa6426b3..763cb715a6 100644
--- a/rerere.c
+++ b/rerere.c
@@ -91,16 +91,18 @@ static void assign_variant(struct rerere_id *id)
 	id->variant = variant;
 }
 
-const char *rerere_path(const struct rerere_id *id, const char *file)
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *id, const char *file)
 {
 	if (!file)
-		return git_path("rr-cache/%s", rerere_id_hex(id));
+		return repo_git_path_replace(the_repository, buf, "rr-cache/%s",
+					     rerere_id_hex(id));
 
 	if (id->variant <= 0)
-		return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+		return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s",
+					     rerere_id_hex(id), file);
 
-	return git_path("rr-cache/%s/%s.%d",
-			rerere_id_hex(id), file, id->variant);
+	return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s.%d",
+				     rerere_id_hex(id), file, id->variant);
 }
 
 static int is_rr_file(const char *name, const char *filename, int *variant)
@@ -624,9 +626,10 @@ static int try_merge(struct index_state *istate,
 {
 	enum ll_merge_result ret;
 	mmfile_t base = {NULL, 0}, other = {NULL, 0};
+	struct strbuf buf = STRBUF_INIT;
 
-	if (read_mmfile(&base, rerere_path(id, "preimage")) ||
-	    read_mmfile(&other, rerere_path(id, "postimage"))) {
+	if (read_mmfile(&base, rerere_path(&buf, id, "preimage")) ||
+	    read_mmfile(&other, rerere_path(&buf, id, "postimage"))) {
 		ret = LL_MERGE_CONFLICT;
 	} else {
 		/*
@@ -637,6 +640,7 @@ static int try_merge(struct index_state *istate,
 			       istate, NULL);
 	}
 
+	strbuf_release(&buf);
 	free(base.ptr);
 	free(other.ptr);
 
@@ -657,6 +661,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 {
 	FILE *f;
 	int ret;
+	struct strbuf buf = STRBUF_INIT;
 	mmfile_t cur = {NULL, 0};
 	mmbuffer_t result = {NULL, 0};
 
@@ -664,8 +669,8 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 	 * Normalize the conflicts in path and write it out to
 	 * "thisimage" temporary file.
 	 */
-	if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
-	    read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+	if ((handle_file(istate, path, NULL, rerere_path(&buf, id, "thisimage")) < 0) ||
+	    read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
 		ret = 1;
 		goto out;
 	}
@@ -678,9 +683,9 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 	 * A successful replay of recorded resolution.
 	 * Mark that "postimage" was used to help gc.
 	 */
-	if (utime(rerere_path(id, "postimage"), NULL) < 0)
+	if (utime(rerere_path(&buf, id, "postimage"), NULL) < 0)
 		warning_errno(_("failed utime() on '%s'"),
-			      rerere_path(id, "postimage"));
+			      rerere_path(&buf, id, "postimage"));
 
 	/* Update "path" with the resolution */
 	f = fopen(path, "w");
@@ -694,6 +699,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 out:
 	free(cur.ptr);
 	free(result.ptr);
+	strbuf_release(&buf);
 
 	return ret;
 }
@@ -720,9 +726,11 @@ static void update_paths(struct repository *r, struct string_list *update)
 
 static void remove_variant(struct rerere_id *id)
 {
-	unlink_or_warn(rerere_path(id, "postimage"));
-	unlink_or_warn(rerere_path(id, "preimage"));
+	struct strbuf buf = STRBUF_INIT;
+	unlink_or_warn(rerere_path(&buf, id, "postimage"));
+	unlink_or_warn(rerere_path(&buf, id, "preimage"));
 	id->collection->status[id->variant] = 0;
+	strbuf_release(&buf);
 }
 
 /*
@@ -739,6 +747,7 @@ static void do_rerere_one_path(struct index_state *istate,
 	const char *path = rr_item->string;
 	struct rerere_id *id = rr_item->util;
 	struct rerere_dir *rr_dir = id->collection;
+	struct strbuf buf = STRBUF_INIT;
 	int variant;
 
 	variant = id->variant;
@@ -746,12 +755,12 @@ static void do_rerere_one_path(struct index_state *istate,
 	/* Has the user resolved it already? */
 	if (variant >= 0) {
 		if (!handle_file(istate, path, NULL, NULL)) {
-			copy_file(rerere_path(id, "postimage"), path, 0666);
+			copy_file(rerere_path(&buf, id, "postimage"), path, 0666);
 			id->collection->status[variant] |= RR_HAS_POSTIMAGE;
 			fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
 			free_rerere_id(rr_item);
 			rr_item->util = NULL;
-			return;
+			goto out;
 		}
 		/*
 		 * There may be other variants that can cleanly
@@ -787,22 +796,25 @@ static void do_rerere_one_path(struct index_state *istate,
 				   path);
 		free_rerere_id(rr_item);
 		rr_item->util = NULL;
-		return;
+		goto out;
 	}
 
 	/* None of the existing one applies; we need a new variant */
 	assign_variant(id);
 
 	variant = id->variant;
-	handle_file(istate, path, NULL, rerere_path(id, "preimage"));
+	handle_file(istate, path, NULL, rerere_path(&buf, id, "preimage"));
 	if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
-		const char *path = rerere_path(id, "postimage");
+		const char *path = rerere_path(&buf, id, "postimage");
 		if (unlink(path))
 			die_errno(_("cannot unlink stray '%s'"), path);
 		id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
 	}
 	id->collection->status[variant] |= RR_HAS_PREIMAGE;
 	fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
+
+out:
+	strbuf_release(&buf);
 }
 
 static int do_plain_rerere(struct repository *r,
@@ -810,6 +822,7 @@ static int do_plain_rerere(struct repository *r,
 {
 	struct string_list conflict = STRING_LIST_INIT_DUP;
 	struct string_list update = STRING_LIST_INIT_DUP;
+	struct strbuf buf = STRBUF_INIT;
 	int i;
 
 	find_conflict(r, &conflict);
@@ -843,7 +856,7 @@ static int do_plain_rerere(struct repository *r,
 		string_list_insert(rr, path)->util = id;
 
 		/* Ensure that the directory exists. */
-		mkdir_in_gitdir(rerere_path(id, NULL));
+		mkdir_in_gitdir(rerere_path(&buf, id, NULL));
 	}
 
 	for (i = 0; i < rr->nr; i++)
@@ -854,6 +867,7 @@ static int do_plain_rerere(struct repository *r,
 
 	string_list_clear(&conflict, 0);
 	string_list_clear(&update, 0);
+	strbuf_release(&buf);
 	return write_rr(rr, fd);
 }
 
@@ -1033,6 +1047,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 	struct rerere_id *id;
 	unsigned char hash[GIT_MAX_RAWSZ];
 	int ret;
+	struct strbuf buf = STRBUF_INIT;
 	struct string_list_item *item;
 
 	/*
@@ -1056,8 +1071,8 @@ static int rerere_forget_one_path(struct index_state *istate,
 		if (!has_rerere_resolution(id))
 			continue;
 
-		handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
-		if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+		handle_cache(istate, path, hash, rerere_path(&buf, id, "thisimage"));
+		if (read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
 			free(cur.ptr);
 			error(_("failed to update conflicted state in '%s'"), path);
 			goto fail_exit;
@@ -1074,7 +1089,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 		goto fail_exit;
 	}
 
-	filename = rerere_path(id, "postimage");
+	filename = rerere_path(&buf, id, "postimage");
 	if (unlink(filename)) {
 		if (errno == ENOENT)
 			error(_("no remembered resolution for '%s'"), path);
@@ -1088,7 +1103,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 	 * conflict in the working tree, run us again to record
 	 * the postimage.
 	 */
-	handle_cache(istate, path, hash, rerere_path(id, "preimage"));
+	handle_cache(istate, path, hash, rerere_path(&buf, id, "preimage"));
 	fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
 
 	/*
@@ -1099,9 +1114,11 @@ static int rerere_forget_one_path(struct index_state *istate,
 	free_rerere_id(item);
 	item->util = id;
 	fprintf(stderr, _("Forgot resolution for '%s'\n"), path);
+	strbuf_release(&buf);
 	return 0;
 
 fail_exit:
+	strbuf_release(&buf);
 	free(id);
 	return -1;
 }
@@ -1147,16 +1164,26 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
 
 static timestamp_t rerere_created_at(struct rerere_id *id)
 {
+	struct strbuf buf = STRBUF_INIT;
 	struct stat st;
+	int ret;
 
-	return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+	ret = stat(rerere_path(&buf, id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+
+	strbuf_release(&buf);
+	return ret;
 }
 
 static timestamp_t rerere_last_used_at(struct rerere_id *id)
 {
+	struct strbuf buf = STRBUF_INIT;
 	struct stat st;
+	int ret;
+
+	ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
 
-	return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+	strbuf_release(&buf);
+	return ret;
 }
 
 /*
@@ -1164,9 +1191,11 @@ static timestamp_t rerere_last_used_at(struct rerere_id *id)
  */
 static void unlink_rr_item(struct rerere_id *id)
 {
-	unlink_or_warn(rerere_path(id, "thisimage"));
+	struct strbuf buf = STRBUF_INIT;
+	unlink_or_warn(rerere_path(&buf, id, "thisimage"));
 	remove_variant(id);
 	id->collection->status[id->variant] = 0;
+	strbuf_release(&buf);
 }
 
 static void prune_one(struct rerere_id *id,
@@ -1264,10 +1293,14 @@ void rerere_clear(struct repository *r, struct string_list *merge_rr)
 
 	for (i = 0; i < merge_rr->nr; i++) {
 		struct rerere_id *id = merge_rr->items[i].util;
+		struct strbuf buf = STRBUF_INIT;
+
 		if (!has_rerere_resolution(id)) {
 			unlink_rr_item(id);
-			rmdir(rerere_path(id, NULL));
+			rmdir(rerere_path(&buf, id, NULL));
 		}
+
+		strbuf_release(&buf);
 	}
 	unlink_or_warn(git_path_merge_rr(r));
 	rollback_lock_file(&write_lock);
diff --git a/rerere.h b/rerere.h
index 5d6cb63879..d4b5f7c932 100644
--- a/rerere.h
+++ b/rerere.h
@@ -32,7 +32,8 @@ int repo_rerere(struct repository *, int);
  * path to that filesystem entity.  With "file" specified with NULL,
  * return the path to the directory that houses these files.
  */
-const char *rerere_path(const struct rerere_id *, const char *file);
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
+			const char *file);
 int rerere_forget(struct repository *, struct pathspec *);
 int rerere_remaining(struct repository *, struct string_list *);
 void rerere_clear(struct repository *, struct string_list *);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (10 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06 16:01   ` shejialuo
  2025-02-06  7:58 ` [PATCH 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Remove `git_path()` in favor of the `repo_git_path()` family of
functions, which makes the implicit dependency on `the_repository` go
away.

Note that `git_path()` returned a string allocated via `get_pathname()`,
which uses a rotating set of statically allocated buffers. Consequently,
callers didn't have to free the returned string. The same isn't true for
`repo_common_path()`, so we also have to add logic to free the returned
strings.

This refactoring also allows us to remove `repo_common_pathv()` as well
as `get_pathname()` from the public interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/commit.c    |  8 +++++---
 builtin/gc.c        | 21 +++++++++++++++------
 builtin/notes.c     |  7 ++++++-
 builtin/rebase.c    |  2 +-
 builtin/remote.c    |  6 ++++--
 builtin/rev-parse.c |  6 +++---
 builtin/worktree.c  | 11 +++++++++--
 notes-merge.c       | 20 ++++++++++++--------
 path.c              |  8 ++++----
 path.h              | 27 ---------------------------
 read-cache.c        | 24 +++++++++++++++++-------
 remote.c            | 21 ++++++++++++---------
 rerere.c            | 14 +++++++++++---
 shallow.c           |  4 +++-
 wt-status.c         | 42 ++++++++++++++++++++++++++----------------
 15 files changed, 128 insertions(+), 93 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 9fb405dd4a..2f45968222 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -352,6 +352,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	struct pathspec pathspec;
 	int refresh_flags = REFRESH_QUIET;
 	const char *ret;
+	char *path = NULL;
 
 	if (is_status)
 		refresh_flags |= REFRESH_UNMERGED;
@@ -524,9 +525,9 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	if (write_locked_index(the_repository->index, &index_lock, 0))
 		die(_("unable to write new index file"));
 
-	hold_lock_file_for_update(&false_lock,
-				  git_path("next-index-%"PRIuMAX,
-					   (uintmax_t) getpid()),
+	path = repo_git_path(the_repository, "next-index-%"PRIuMAX,
+			     (uintmax_t) getpid());
+	hold_lock_file_for_update(&false_lock, path,
 				  LOCK_DIE_ON_ERROR);
 
 	create_base_index(current_head);
@@ -542,6 +543,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 out:
 	string_list_clear(&partial, 0);
 	clear_pathspec(&pathspec);
+	free(path);
 	return ret;
 }
 
diff --git a/builtin/gc.c b/builtin/gc.c
index 57f6aee174..5923d9a05b 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -99,9 +99,11 @@ static void process_log_file(void)
 		/* There was some error recorded in the lock file */
 		commit_lock_file(&log_lock);
 	} else {
+		char *path = repo_git_path(the_repository, "gc.log");
 		/* No error, clean up any old gc.log */
-		unlink(git_path("gc.log"));
+		unlink(path);
 		rollback_lock_file(&log_lock);
+		free(path);
 	}
 }
 
@@ -299,8 +301,11 @@ static int too_many_loose_objects(struct gc_config *cfg)
 	int num_loose = 0;
 	int needed = 0;
 	const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
+	char *path;
 
-	dir = opendir(git_path("objects/17"));
+	path = repo_git_path(the_repository, "objects/17");
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return 0;
 
@@ -821,11 +826,12 @@ struct repository *repo UNUSED)
 	}
 
 	if (daemonized) {
-		hold_lock_file_for_update(&log_lock,
-					  git_path("gc.log"),
+		char *path = repo_git_path(the_repository, "gc.log");
+		hold_lock_file_for_update(&log_lock, path,
 					  LOCK_DIE_ON_ERROR);
 		dup2(get_lock_file_fd(&log_lock), 2);
 		atexit(process_log_file_at_exit);
+		free(path);
 	}
 
 	gc_before_repack(&opts, &cfg);
@@ -887,8 +893,11 @@ struct repository *repo UNUSED)
 		warning(_("There are too many unreachable loose objects; "
 			"run 'git prune' to remove them."));
 
-	if (!daemonized)
-		unlink(git_path("gc.log"));
+	if (!daemonized) {
+		char *path = repo_git_path(the_repository, "gc.log");
+		unlink(path);
+		free(path);
+	}
 
 out:
 	gc_config_release(&cfg);
diff --git a/builtin/notes.c b/builtin/notes.c
index 18bcbb2f91..ff61ec5f2d 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -979,6 +979,8 @@ static int merge(int argc, const char **argv, const char *prefix,
 	else { /* Merge has unresolved conflicts */
 		struct worktree **worktrees;
 		const struct worktree *wt;
+		char *path;
+
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
 				"NOTES_MERGE_PARTIAL", &result_oid, NULL,
@@ -994,10 +996,13 @@ static int merge(int argc, const char **argv, const char *prefix,
 		if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", notes_ref, NULL))
 			die(_("failed to store link to current notes ref (%s)"),
 			    notes_ref);
+
+		path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE);
 		fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
 				  "and commit the result with 'git notes merge --commit', "
 				  "or abort the merge with 'git notes merge --abort'.\n"),
-			git_path(NOTES_MERGE_WORKTREE));
+			path);
+		free(path);
 	}
 
 	free_notes(t);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6c9eaf3788..d4715ed35d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -644,7 +644,7 @@ static int run_am(struct rebase_options *opts)
 		return run_command(&am);
 	}
 
-	rebased_patches = xstrdup(git_path("rebased-patches"));
+	rebased_patches = repo_git_path(the_repository, "rebased-patches");
 	format_patch.out = open(rebased_patches,
 				O_WRONLY | O_CREAT | O_TRUNC, 0666);
 	if (format_patch.out < 0) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 71d84fb3cf..0489fcc8f3 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -644,9 +644,11 @@ static int migrate_file(struct remote *remote)
 		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
 #ifndef WITH_BREAKING_CHANGES
 	if (remote->origin == REMOTE_REMOTES)
-		unlink_or_warn(git_path("remotes/%s", remote->name));
+		unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+						     "remotes/%s", remote->name));
 	else if (remote->origin == REMOTE_BRANCHES)
-		unlink_or_warn(git_path("branches/%s", remote->name));
+		unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+						     "branches/%s", remote->name));
 #endif /* WITH_BREAKING_CHANGES */
 	strbuf_release(&buf);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 428c866c05..490da33bec 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -789,8 +789,8 @@ int cmd_rev_parse(int argc,
 			if (!strcmp(arg, "--git-path")) {
 				if (!argv[i + 1])
 					die(_("--git-path requires an argument"));
-				strbuf_reset(&buf);
-				print_path(git_path("%s", argv[i + 1]), prefix,
+				print_path(repo_git_path_replace(the_repository, &buf,
+								 "%s", argv[i + 1]), prefix,
 						format,
 						DEFAULT_RELATIVE_IF_SHARED);
 				i++;
@@ -1083,7 +1083,7 @@ int cmd_rev_parse(int argc,
 					die(_("Could not read the index"));
 				if (the_repository->index->split_index) {
 					const struct object_id *oid = &the_repository->index->split_index->base_oid;
-					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
+					const char *path = repo_git_path_replace(the_repository, &buf, "sharedindex.%s", oid_to_hex(oid));
 					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
 				continue;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 761e302a36..48448a8355 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -163,7 +163,9 @@ static int delete_git_dir(const char *id)
 
 static void delete_worktrees_dir_if_empty(void)
 {
-	rmdir(git_path("worktrees")); /* ignore failed removal */
+	char *path = repo_git_path(the_repository, "worktrees");
+	rmdir(path); /* ignore failed removal */
+	free(path);
 }
 
 static void prune_worktree(const char *id, const char *reason)
@@ -212,8 +214,13 @@ static void prune_worktrees(void)
 	struct strbuf reason = STRBUF_INIT;
 	struct strbuf main_path = STRBUF_INIT;
 	struct string_list kept = STRING_LIST_INIT_DUP;
-	DIR *dir = opendir(git_path("worktrees"));
+	char *path;
+	DIR *dir;
 	struct dirent *d;
+
+	path = repo_git_path(the_repository, "worktrees");
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return;
 	while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
diff --git a/notes-merge.c b/notes-merge.c
index 8c22a171c1..67a472020d 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -275,34 +275,38 @@ static void diff_tree_local(struct notes_merge_options *o,
 
 static void check_notes_merge_worktree(struct notes_merge_options *o)
 {
+	struct strbuf buf = STRBUF_INIT;
+
 	if (!o->has_worktree) {
 		/*
 		 * Must establish NOTES_MERGE_WORKTREE.
 		 * Abort if NOTES_MERGE_WORKTREE already exists
 		 */
-		if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
-		    !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
+		if (file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)) &&
+		    !is_empty_dir(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE))) {
 			if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
 				die(_("You have not concluded your previous "
 				    "notes merge (%s exists).\nPlease, use "
 				    "'git notes merge --commit' or 'git notes "
 				    "merge --abort' to commit/abort the "
 				    "previous merge before you start a new "
-				    "notes merge."), git_path("NOTES_MERGE_*"));
+				    "notes merge."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
 			else
 				die(_("You have not concluded your notes merge "
-				    "(%s exists)."), git_path("NOTES_MERGE_*"));
+				    "(%s exists)."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
 		}
 
-		if (safe_create_leading_directories_const(git_path(
+		if (safe_create_leading_directories_const(repo_git_path_replace(the_repository, &buf,
 				NOTES_MERGE_WORKTREE "/.test")))
 			die_errno("unable to create directory %s",
-				  git_path(NOTES_MERGE_WORKTREE));
+				  repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
 		o->has_worktree = 1;
-	} else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
+	} else if (!file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)))
 		/* NOTES_MERGE_WORKTREE should already be established */
 		die("missing '%s'. This should not happen",
-		    git_path(NOTES_MERGE_WORKTREE));
+		    repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
+
+	strbuf_release(&buf);
 }
 
 static void write_buf_to_worktree(const struct object_id *obj,
diff --git a/path.c b/path.c
index f6b795d75f..ddfc901661 100644
--- a/path.c
+++ b/path.c
@@ -30,7 +30,7 @@ static int get_st_mode_bits(const char *path, int *mode)
 	return 0;
 }
 
-struct strbuf *get_pathname(void)
+static struct strbuf *get_pathname(void)
 {
 	static struct strbuf pathname_array[4] = {
 		STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -417,9 +417,9 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
-void repo_git_pathv(struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args)
+static void repo_git_pathv(struct repository *repo,
+			   const struct worktree *wt, struct strbuf *buf,
+			   const char *fmt, va_list args)
 {
 	int gitdir_len;
 	strbuf_worktree_gitdir(buf, repo, wt);
diff --git a/path.h b/path.h
index 496f27fdfd..373404dd9d 100644
--- a/path.h
+++ b/path.h
@@ -221,37 +221,10 @@ char *xdg_cache_home(const char *filename);
  */
 void safe_create_dir(const char *dir, int share);
 
-/*
- * Do not use this function. It is only exported to other subsystems until we
- * can get rid of the below block of functions that implicitly rely on
- * `the_repository`.
- */
-struct strbuf *get_pathname(void);
-
 # ifdef USE_THE_REPOSITORY_VARIABLE
 #  include "strbuf.h"
 #  include "repository.h"
 
-/* Internal implementation details that should not be used. */
-void repo_git_pathv(struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args);
-
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_path(const char *fmt, ...)
-{
-	struct strbuf *pathname = get_pathname();
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, pathname, fmt, args);
-	va_end(args);
-	return pathname->buf;
-}
-
 #define GIT_PATH_FUNC(func, filename) \
 	const char *func(void) \
 	{ \
diff --git a/read-cache.c b/read-cache.c
index d54be2c172..66ad0015a7 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3251,15 +3251,18 @@ static int clean_shared_index_files(const char *current_hex)
 
 	while ((de = readdir(dir)) != NULL) {
 		const char *sha1_hex;
-		const char *shared_index_path;
+		char *shared_index_path;
 		if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex))
 			continue;
 		if (!strcmp(sha1_hex, current_hex))
 			continue;
-		shared_index_path = git_path("%s", de->d_name);
+
+		shared_index_path = repo_git_path(the_repository, "%s", de->d_name);
 		if (should_delete_shared_index(shared_index_path) > 0 &&
 		    unlink(shared_index_path))
 			warning_errno(_("unable to unlink: %s"), shared_index_path);
+
+		free(shared_index_path);
 	}
 	closedir(dir);
 
@@ -3271,6 +3274,7 @@ static int write_shared_index(struct index_state *istate,
 {
 	struct split_index *si = istate->split_index;
 	int ret, was_full = !istate->sparse_index;
+	char *path;
 
 	move_cache_to_base_index(istate);
 	convert_to_sparse(istate, 0);
@@ -3291,13 +3295,15 @@ static int write_shared_index(struct index_state *istate,
 		error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
 		return ret;
 	}
-	ret = rename_tempfile(temp,
-			      git_path("sharedindex.%s", oid_to_hex(&si->base->oid)));
+
+	path = repo_git_path(the_repository, "sharedindex.%s", oid_to_hex(&si->base->oid));
+	ret = rename_tempfile(temp, path);
 	if (!ret) {
 		oidcpy(&si->base_oid, &si->base->oid);
 		clean_shared_index_files(oid_to_hex(&si->base->oid));
 	}
 
+	free(path);
 	return ret;
 }
 
@@ -3378,9 +3384,12 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
 	if (new_shared_index) {
 		struct tempfile *temp;
 		int saved_errno;
+		char *path;
 
 		/* Same initial permissions as the main .git/index file */
-		temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
+		path = repo_git_path(the_repository, "sharedindex_XXXXXX");
+		temp = mks_tempfile_sm(path, 0, 0666);
+		free(path);
 		if (!temp) {
 			ret = do_write_locked_index(istate, lock, flags,
 						    ~WRITE_SPLIT_INDEX_EXTENSION);
@@ -3401,9 +3410,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
 
 	/* Freshen the shared index only if the split-index was written */
 	if (!ret && !new_shared_index && !is_null_oid(&si->base_oid)) {
-		const char *shared_index = git_path("sharedindex.%s",
-						    oid_to_hex(&si->base_oid));
+		char *shared_index = repo_git_path(the_repository, "sharedindex.%s",
+						   oid_to_hex(&si->base_oid));
 		freshen_shared_index(shared_index, 1);
+		free(shared_index);
 	}
 
 out:
diff --git a/remote.c b/remote.c
index 1779f0e7bb..81d151a507 100644
--- a/remote.c
+++ b/remote.c
@@ -321,10 +321,11 @@ static void read_remotes_file(struct remote_state *remote_state,
 			      struct remote *remote)
 {
 	struct strbuf buf = STRBUF_INIT;
-	FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r");
+	FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						     "remotes/%s", remote->name), "r");
 
 	if (!f)
-		return;
+		goto out;
 
 	warn_about_deprecated_remote_type("remotes", remote);
 
@@ -343,8 +344,10 @@ static void read_remotes_file(struct remote_state *remote_state,
 		else if (skip_prefix(buf.buf, "Pull:", &v))
 			refspec_append(&remote->fetch, skip_spaces(v));
 	}
-	strbuf_release(&buf);
 	fclose(f);
+
+out:
+	strbuf_release(&buf);
 }
 
 static void read_branches_file(struct remote_state *remote_state,
@@ -352,20 +355,19 @@ static void read_branches_file(struct remote_state *remote_state,
 {
 	char *frag, *to_free = NULL;
 	struct strbuf buf = STRBUF_INIT;
-	FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
+	FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						     "branches/%s", remote->name), "r");
 
 	if (!f)
-		return;
+		goto out;
 
 	warn_about_deprecated_remote_type("branches", remote);
 
 	strbuf_getline_lf(&buf, f);
 	fclose(f);
 	strbuf_trim(&buf);
-	if (!buf.len) {
-		strbuf_release(&buf);
-		return;
-	}
+	if (!buf.len)
+		goto out;
 
 	remote->configured_in_repo = 1;
 	remote->origin = REMOTE_BRANCHES;
@@ -393,6 +395,7 @@ static void read_branches_file(struct remote_state *remote_state,
 	refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
 	remote->fetch_tags = 1; /* always auto-follow */
 
+out:
 	strbuf_release(&buf);
 	free(to_free);
 }
diff --git a/rerere.c b/rerere.c
index 763cb715a6..2239c2d775 100644
--- a/rerere.c
+++ b/rerere.c
@@ -127,8 +127,12 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
 static void scan_rerere_dir(struct rerere_dir *rr_dir)
 {
 	struct dirent *de;
-	DIR *dir = opendir(git_path("rr-cache/%s", rr_dir->name));
+	char *path;
+	DIR *dir;
 
+	path = repo_git_path(the_repository, "rr-cache/%s", rr_dir->name);
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return;
 	while ((de = readdir(dir)) != NULL) {
@@ -1234,6 +1238,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 	timestamp_t now = time(NULL);
 	timestamp_t cutoff_noresolve = now - 15 * 86400;
 	timestamp_t cutoff_resolve = now - 60 * 86400;
+	struct strbuf buf = STRBUF_INIT;
 
 	if (setup_rerere(r, rr, 0) < 0)
 		return;
@@ -1243,7 +1248,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 	repo_config_get_expiry_in_days(the_repository, "gc.rerereunresolved",
 				       &cutoff_noresolve, now);
 	git_config(git_default_config, NULL);
-	dir = opendir(git_path("rr-cache"));
+	dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache"));
 	if (!dir)
 		die_errno(_("unable to open rr-cache directory"));
 	/* Collect stale conflict IDs ... */
@@ -1272,9 +1277,12 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 
 	/* ... and then remove the empty directories */
 	for (i = 0; i < to_remove.nr; i++)
-		rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
+		rmdir(repo_git_path_replace(the_repository, &buf,
+					    "rr-cache/%s", to_remove.items[i].string));
+
 	string_list_clear(&to_remove, 0);
 	rollback_lock_file(&write_lock);
+	strbuf_release(&buf);
 }
 
 /*
diff --git a/shallow.c b/shallow.c
index b54244ffa9..4bd9342c9a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -364,7 +364,9 @@ const char *setup_temporary_shallow(const struct oid_array *extra)
 	struct strbuf sb = STRBUF_INIT;
 
 	if (write_shallow_commits(&sb, 0, extra)) {
-		temp = xmks_tempfile(git_path("shallow_XXXXXX"));
+		char *path = repo_git_path(the_repository, "shallow_XXXXXX");
+		temp = xmks_tempfile(path);
+		free(path);
 
 		if (write_in_full(temp->fd, sb.buf, sb.len) < 0 ||
 		    close_tempfile_gently(temp) < 0)
diff --git a/wt-status.c b/wt-status.c
index 3ee9181764..1da5732f57 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1289,7 +1289,8 @@ static void show_am_in_progress(struct wt_status *s,
 static char *read_line_from_git_path(const char *filename)
 {
 	struct strbuf buf = STRBUF_INIT;
-	FILE *fp = fopen_or_warn(git_path("%s", filename), "r");
+	FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						      "%s", filename), "r");
 
 	if (!fp) {
 		strbuf_release(&buf);
@@ -1383,27 +1384,33 @@ static void abbrev_oid_in_line(struct strbuf *line)
 
 static int read_rebase_todolist(const char *fname, struct string_list *lines)
 {
-	struct strbuf line = STRBUF_INIT;
-	FILE *f = fopen(git_path("%s", fname), "r");
+	struct strbuf buf = STRBUF_INIT;
+	FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r");
+	int ret;
 
 	if (!f) {
-		if (errno == ENOENT)
-			return -1;
+		if (errno == ENOENT) {
+			ret = -1;
+			goto out;
+		}
 		die_errno("Could not open file %s for reading",
-			  git_path("%s", fname));
+			  repo_git_path_replace(the_repository, &buf, "%s", fname));
 	}
-	while (!strbuf_getline_lf(&line, f)) {
-		if (starts_with(line.buf, comment_line_str))
+	while (!strbuf_getline_lf(&buf, f)) {
+		if (starts_with(buf.buf, comment_line_str))
 			continue;
-		strbuf_trim(&line);
-		if (!line.len)
+		strbuf_trim(&buf);
+		if (!buf.len)
 			continue;
-		abbrev_oid_in_line(&line);
-		string_list_append(lines, line.buf);
+		abbrev_oid_in_line(&buf);
+		string_list_append(lines, buf.buf);
 	}
 	fclose(f);
-	strbuf_release(&line);
-	return 0;
+
+	ret = 0;
+out:
+	strbuf_release(&buf);
+	return ret;
 }
 
 static void show_rebase_information(struct wt_status *s,
@@ -1434,9 +1441,12 @@ static void show_rebase_information(struct wt_status *s,
 				i < have_done.nr;
 				i++)
 				status_printf_ln(s, color, "   %s", have_done.items[i].string);
-			if (have_done.nr > nr_lines_to_show && s->hints)
+			if (have_done.nr > nr_lines_to_show && s->hints) {
+				char *path = repo_git_path(the_repository, "rebase-merge/done");
 				status_printf_ln(s, color,
-					_("  (see more in file %s)"), git_path("rebase-merge/done"));
+					_("  (see more in file %s)"), path);
+				free(path);
+			}
 		}
 
 		if (yet_to_do.nr == 0)

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 13/16] repo-settings: introduce function to clear struct
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (11 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

We don't provide a way to clear a `struct repo_settings`, and instead
open-code this in `repo_clear()`. This is mixing up concerns and means
that developers have to touch multiple files whenever they add a new
field to the structure in case the associated resources need to be
released.

Provide a new `repo_settings_clear()` function to improve this.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 repo-settings.c | 10 ++++++++--
 repo-settings.h |  1 +
 repository.c    |  2 +-
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/repo-settings.c b/repo-settings.c
index 9d16d5399e..719cd7c85c 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -21,7 +21,6 @@ static void repo_cfg_int(struct repository *r, const char *key, int *dest,
 
 void prepare_repo_settings(struct repository *r)
 {
-	const struct repo_settings defaults = REPO_SETTINGS_INIT;
 	int experimental;
 	int value;
 	const char *strval;
@@ -35,7 +34,7 @@ void prepare_repo_settings(struct repository *r)
 	if (r->settings.initialized)
 		return;
 
-	memcpy(&r->settings, &defaults, sizeof(defaults));
+	repo_settings_clear(r);
 	r->settings.initialized++;
 
 	/* Booleans config or default, cascades to other settings */
@@ -143,6 +142,13 @@ void prepare_repo_settings(struct repository *r)
 		r->settings.packed_git_limit = ulongval;
 }
 
+void repo_settings_clear(struct repository *r)
+{
+	struct repo_settings empty = REPO_SETTINGS_INIT;
+	FREE_AND_NULL(r->settings.fsmonitor);
+	r->settings = empty;
+}
+
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo)
 {
 	const char *value;
diff --git a/repo-settings.h b/repo-settings.h
index 93ea0c3274..c4f7e3bd8a 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -73,6 +73,7 @@ struct repo_settings {
 }
 
 void prepare_repo_settings(struct repository *r);
+void repo_settings_clear(struct repository *r);
 
 /* Read the value for "core.logAllRefUpdates". */
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
diff --git a/repository.c b/repository.c
index 648cd88474..6cbaf2e3da 100644
--- a/repository.c
+++ b/repository.c
@@ -380,7 +380,7 @@ void repo_clear(struct repository *repo)
 	parsed_object_pool_clear(repo->parsed_objects);
 	FREE_AND_NULL(repo->parsed_objects);
 
-	FREE_AND_NULL(repo->settings.fsmonitor);
+	repo_settings_clear(repo);
 
 	if (repo->config) {
 		git_configset_clear(repo->config);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 14/16] environment: move access to "core.hooksPath" into repo settings
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (12 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

The "core.hooksPath" setting is stored in a global variable and
populated via the `git_default_core_config`. This may cause issues in
the case where one is handling multiple different repositories in a
single process with different values for that config key, as we may or
may not see the correct value in that case. Furthermore, global state
blocks our path towards libification.

Refactor the code so that we instead store the value in `struct
repo_settings`. The value is computed as-needed and cached. The result
should be functionally the same as there aren't ever any code paths
where we'd execute hooks outside the context of a repository.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.c        | 5 -----
 environment.c   | 1 -
 environment.h   | 1 -
 path.c          | 7 ++++---
 repo-settings.c | 8 ++++++++
 repo-settings.h | 4 ++++
 6 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/config.c b/config.c
index 50f2d17b39..d932d4b134 100644
--- a/config.c
+++ b/config.c
@@ -1436,11 +1436,6 @@ static int git_default_core_config(const char *var, const char *value,
 		return git_config_pathname(&git_attributes_file, var, value);
 	}
 
-	if (!strcmp(var, "core.hookspath")) {
-		FREE_AND_NULL(git_hooks_path);
-		return git_config_pathname(&git_hooks_path, var, value);
-	}
-
 	if (!strcmp(var, "core.bare")) {
 		is_bare_repository_cfg = git_config_bool(var, value);
 		return 0;
diff --git a/environment.c b/environment.c
index 8389a27270..39755873ee 100644
--- a/environment.c
+++ b/environment.c
@@ -42,7 +42,6 @@ char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
 char *git_attributes_file;
-char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
diff --git a/environment.h b/environment.h
index 2f43340f0b..66989afbac 100644
--- a/environment.h
+++ b/environment.h
@@ -160,7 +160,6 @@ extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
 extern char *git_attributes_file;
-extern char *git_hooks_path;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern size_t packed_git_window_size;
diff --git a/path.c b/path.c
index ddfc901661..e81ebd3b5c 100644
--- a/path.c
+++ b/path.c
@@ -387,10 +387,11 @@ void report_linked_checkout_garbage(struct repository *r)
 	strbuf_release(&sb);
 }
 
-static void adjust_git_path(const struct repository *repo,
+static void adjust_git_path(struct repository *repo,
 			    struct strbuf *buf, int git_dir_len)
 {
 	const char *base = buf->buf + git_dir_len;
+
 	if (is_dir_file(base, "info", "grafts"))
 		strbuf_splice(buf, 0, buf->len,
 			      repo->graft_file, strlen(repo->graft_file));
@@ -399,8 +400,8 @@ static void adjust_git_path(const struct repository *repo,
 			      repo->index_file, strlen(repo->index_file));
 	else if (dir_prefix(base, "objects"))
 		replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
-	else if (git_hooks_path && dir_prefix(base, "hooks"))
-		replace_dir(buf, git_dir_len + 5, git_hooks_path);
+	else if (repo_settings_get_hooks_path(repo) && dir_prefix(base, "hooks"))
+		replace_dir(buf, git_dir_len + 5, repo_settings_get_hooks_path(repo));
 	else if (repo->different_commondir)
 		update_common_dir(buf, git_dir_len, repo->commondir);
 }
diff --git a/repo-settings.c b/repo-settings.c
index 719cd7c85c..876d527581 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -146,6 +146,7 @@ void repo_settings_clear(struct repository *r)
 {
 	struct repo_settings empty = REPO_SETTINGS_INIT;
 	FREE_AND_NULL(r->settings.fsmonitor);
+	FREE_AND_NULL(r->settings.hooks_path);
 	r->settings = empty;
 }
 
@@ -173,3 +174,10 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo)
 			      &repo->settings.warn_ambiguous_refs, 1);
 	return repo->settings.warn_ambiguous_refs;
 }
+
+const char *repo_settings_get_hooks_path(struct repository *repo)
+{
+	if (!repo->settings.hooks_path)
+		repo_config_get_pathname(repo, "core.hookspath", &repo->settings.hooks_path);
+	return repo->settings.hooks_path;
+}
diff --git a/repo-settings.h b/repo-settings.h
index c4f7e3bd8a..0cef970443 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -61,6 +61,8 @@ struct repo_settings {
 	size_t delta_base_cache_limit;
 	size_t packed_git_window_size;
 	size_t packed_git_limit;
+
+	char *hooks_path;
 };
 #define REPO_SETTINGS_INIT { \
 	.index_version = -1, \
@@ -79,5 +81,7 @@ void repo_settings_clear(struct repository *r);
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
 /* Read the value for "core.warnAmbiguousRefs". */
 int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
+/* Read the value for "core.hooksPath". */
+const char *repo_settings_get_hooks_path(struct repository *repo);
 
 #endif /* REPO_SETTINGS_H */

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 15/16] environment: move access to "core.sharedRepository" into repo settings
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (13 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06  7:58 ` [PATCH 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

Similar as with the preceding commit, we track "core.sharedRepository"
via a pair of global variables. Move them into `struct repo_settings` so
that we can instead track them per-repository.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/init-db.c |  8 ++++----
 builtin/log.c     |  6 +++---
 environment.c     | 26 --------------------------
 environment.h     | 10 ----------
 path.c            | 10 +++++-----
 repo-settings.c   | 26 ++++++++++++++++++++++++++
 repo-settings.h   |  9 +++++++++
 setup.c           | 21 +++++++++++----------
 8 files changed, 58 insertions(+), 58 deletions(-)

diff --git a/builtin/init-db.c b/builtin/init-db.c
index 096f96b9c4..196dccdd77 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -132,8 +132,8 @@ int cmd_init_db(int argc,
 				 * and we know shared_repository should always be 0;
 				 * but just in case we play safe.
 				 */
-				saved = get_shared_repository();
-				set_shared_repository(0);
+				saved = repo_settings_get_shared_repository(the_repository);
+				repo_settings_set_shared_repository(the_repository, 0);
 				switch (safe_create_leading_directories_const(argv[0])) {
 				case SCLD_OK:
 				case SCLD_PERMS:
@@ -145,7 +145,7 @@ int cmd_init_db(int argc,
 					die_errno(_("cannot mkdir %s"), argv[0]);
 					break;
 				}
-				set_shared_repository(saved);
+				repo_settings_set_shared_repository(the_repository, saved);
 				if (mkdir(argv[0], 0777) < 0)
 					die_errno(_("cannot mkdir %s"), argv[0]);
 				mkdir_tried = 1;
@@ -175,7 +175,7 @@ int cmd_init_db(int argc,
 	}
 
 	if (init_shared_repository != -1)
-		set_shared_repository(init_shared_repository);
+		repo_settings_set_shared_repository(the_repository, init_shared_repository);
 
 	/*
 	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
diff --git a/builtin/log.c b/builtin/log.c
index e41f88945e..04a6ef97bc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -2309,8 +2309,8 @@ int cmd_format_patch(int argc,
 		 * We consider <outdir> as 'outside of gitdir', therefore avoid
 		 * applying adjust_shared_perm in s-c-l-d.
 		 */
-		saved = get_shared_repository();
-		set_shared_repository(0);
+		saved = repo_settings_get_shared_repository(the_repository);
+		repo_settings_set_shared_repository(the_repository, 0);
 		switch (safe_create_leading_directories_const(output_directory)) {
 		case SCLD_OK:
 		case SCLD_EXISTS:
@@ -2319,7 +2319,7 @@ int cmd_format_patch(int argc,
 			die(_("could not create leading directories "
 			      "of '%s'"), output_directory);
 		}
-		set_shared_repository(saved);
+		repo_settings_set_shared_repository(the_repository, saved);
 		if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
 			die_errno(_("could not create directory '%s'"),
 				  output_directory);
diff --git a/environment.c b/environment.c
index 39755873ee..c79acc69e7 100644
--- a/environment.c
+++ b/environment.c
@@ -206,32 +206,6 @@ const char *get_commit_output_encoding(void)
 	return git_commit_encoding ? git_commit_encoding : "UTF-8";
 }
 
-static int the_shared_repository = PERM_UMASK;
-static int need_shared_repository_from_config = 1;
-
-void set_shared_repository(int value)
-{
-	the_shared_repository = value;
-	need_shared_repository_from_config = 0;
-}
-
-int get_shared_repository(void)
-{
-	if (need_shared_repository_from_config) {
-		const char *var = "core.sharedrepository";
-		const char *value;
-		if (!git_config_get_value(var, &value))
-			the_shared_repository = git_config_perm(var, value);
-		need_shared_repository_from_config = 0;
-	}
-	return the_shared_repository;
-}
-
-void reset_shared_repository(void)
-{
-	need_shared_repository_from_config = 1;
-}
-
 int use_optional_locks(void)
 {
 	return git_env_bool(GIT_OPTIONAL_LOCKS_ENVIRONMENT, 1);
diff --git a/environment.h b/environment.h
index 66989afbac..45e690f203 100644
--- a/environment.h
+++ b/environment.h
@@ -134,16 +134,6 @@ void setup_git_env(const char *git_dir);
  */
 int have_git_dir(void);
 
-/*
- * Accessors for the core.sharedrepository config which lazy-load the value
- * from the config (if not already set). The "reset" function can be
- * used to unset "set" or cached value, meaning that the value will be loaded
- * fresh from the config file on the next call to get_shared_repository().
- */
-void set_shared_repository(int value);
-int get_shared_repository(void);
-void reset_shared_repository(void);
-
 extern int is_bare_repository_cfg;
 int is_bare_repository(void);
 extern char *git_work_tree_cfg;
diff --git a/path.c b/path.c
index e81ebd3b5c..a2f402baec 100644
--- a/path.c
+++ b/path.c
@@ -844,17 +844,17 @@ int calc_shared_perm(int mode)
 {
 	int tweak;
 
-	if (get_shared_repository() < 0)
-		tweak = -get_shared_repository();
+	if (repo_settings_get_shared_repository(the_repository) < 0)
+		tweak = -repo_settings_get_shared_repository(the_repository);
 	else
-		tweak = get_shared_repository();
+		tweak = repo_settings_get_shared_repository(the_repository);
 
 	if (!(mode & S_IWUSR))
 		tweak &= ~0222;
 	if (mode & S_IXUSR)
 		/* Copy read bits to execute bits */
 		tweak |= (tweak & 0444) >> 2;
-	if (get_shared_repository() < 0)
+	if (repo_settings_get_shared_repository(the_repository) < 0)
 		mode = (mode & ~0777) | tweak;
 	else
 		mode |= tweak;
@@ -867,7 +867,7 @@ int adjust_shared_perm(const char *path)
 {
 	int old_mode, new_mode;
 
-	if (!get_shared_repository())
+	if (!repo_settings_get_shared_repository(the_repository))
 		return 0;
 	if (get_st_mode_bits(path, &old_mode) < 0)
 		return -1;
diff --git a/repo-settings.c b/repo-settings.c
index 876d527581..67e9cfd2e6 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -4,6 +4,7 @@
 #include "repository.h"
 #include "midx.h"
 #include "pack-objects.h"
+#include "setup.h"
 
 static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
 			  int def)
@@ -181,3 +182,28 @@ const char *repo_settings_get_hooks_path(struct repository *repo)
 		repo_config_get_pathname(repo, "core.hookspath", &repo->settings.hooks_path);
 	return repo->settings.hooks_path;
 }
+
+int repo_settings_get_shared_repository(struct repository *repo)
+{
+	if (!repo->settings.shared_repository_initialized) {
+		const char *var = "core.sharedrepository";
+		const char *value;
+		if (!repo_config_get_value(repo, var, &value))
+			repo->settings.shared_repository = git_config_perm(var, value);
+		else
+			repo->settings.shared_repository = PERM_UMASK;
+		repo->settings.shared_repository_initialized = 1;
+	}
+	return repo->settings.shared_repository;
+}
+
+void repo_settings_set_shared_repository(struct repository *repo, int value)
+{
+	repo->settings.shared_repository = value;
+	repo->settings.shared_repository_initialized = 1;
+}
+
+void repo_settings_reset_shared_repository(struct repository *repo)
+{
+	repo->settings.shared_repository_initialized = 0;
+}
diff --git a/repo-settings.h b/repo-settings.h
index 0cef970443..ddc11967e0 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -37,6 +37,9 @@ struct repo_settings {
 	int pack_use_bitmap_boundary_traversal;
 	int pack_use_multi_pack_reuse;
 
+	int shared_repository;
+	int shared_repository_initialized;
+
 	/*
 	 * Does this repository have core.useReplaceRefs=true (on by
 	 * default)? This provides a repository-scoped version of this
@@ -65,6 +68,7 @@ struct repo_settings {
 	char *hooks_path;
 };
 #define REPO_SETTINGS_INIT { \
+	.shared_repository = -1, \
 	.index_version = -1, \
 	.core_untracked_cache = UNTRACKED_CACHE_KEEP, \
 	.fetch_negotiation_algorithm = FETCH_NEGOTIATION_CONSECUTIVE, \
@@ -84,4 +88,9 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
 /* Read the value for "core.hooksPath". */
 const char *repo_settings_get_hooks_path(struct repository *repo);
 
+/* Read, set or reset the value for "core.sharedRepository". */
+int repo_settings_get_shared_repository(struct repository *repo);
+void repo_settings_set_shared_repository(struct repository *repo, int value);
+void repo_settings_reset_shared_repository(struct repository *repo);
+
 #endif /* REPO_SETTINGS_H */
diff --git a/setup.c b/setup.c
index 30889386f7..aa65b93f53 100644
--- a/setup.c
+++ b/setup.c
@@ -2332,7 +2332,7 @@ static int create_default_files(const char *template_path,
 	 */
 	copy_templates(template_path);
 	git_config_clear();
-	reset_shared_repository();
+	repo_settings_reset_shared_repository(the_repository);
 	git_config(git_default_config, NULL);
 
 	reinit = is_reinit();
@@ -2342,7 +2342,8 @@ static int create_default_files(const char *template_path,
 	 * values we might have just re-read from the config.
 	 */
 	if (init_shared_repository != -1)
-		set_shared_repository(init_shared_repository);
+		repo_settings_set_shared_repository(the_repository,
+						    init_shared_repository);
 
 	is_bare_repository_cfg = !work_tree;
 
@@ -2350,7 +2351,7 @@ static int create_default_files(const char *template_path,
 	 * We would have created the above under user's umask -- under
 	 * shared-repository settings, we would need to fix them up.
 	 */
-	if (get_shared_repository()) {
+	if (repo_settings_get_shared_repository(the_repository)) {
 		adjust_shared_perm(repo_get_git_dir(the_repository));
 	}
 
@@ -2597,7 +2598,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 					  initial_branch, flags & INIT_DB_QUIET);
 	create_object_directory();
 
-	if (get_shared_repository()) {
+	if (repo_settings_get_shared_repository(the_repository)) {
 		char buf[10];
 		/* We do not spell "group" and such, so that
 		 * the configuration can be read by older version
@@ -2605,12 +2606,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		 * and compatibility values for PERM_GROUP and
 		 * PERM_EVERYBODY.
 		 */
-		if (get_shared_repository() < 0)
+		if (repo_settings_get_shared_repository(the_repository) < 0)
 			/* force to the mode value */
-			xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
-		else if (get_shared_repository() == PERM_GROUP)
+			xsnprintf(buf, sizeof(buf), "0%o", -repo_settings_get_shared_repository(the_repository));
+		else if (repo_settings_get_shared_repository(the_repository) == PERM_GROUP)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
-		else if (get_shared_repository() == PERM_EVERYBODY)
+		else if (repo_settings_get_shared_repository(the_repository) == PERM_EVERYBODY)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
 		else
 			BUG("invalid value for shared_repository");
@@ -2622,12 +2623,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		int len = strlen(git_dir);
 
 		if (reinit)
-			printf(get_shared_repository()
+			printf(repo_settings_get_shared_repository(the_repository)
 			       ? _("Reinitialized existing shared Git repository in %s%s\n")
 			       : _("Reinitialized existing Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 		else
-			printf(get_shared_repository()
+			printf(repo_settings_get_shared_repository(the_repository)
 			       ? _("Initialized empty shared Git repository in %s%s\n")
 			       : _("Initialized empty Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH 16/16] path: adjust last remaining users of `the_repository`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (14 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
@ 2025-02-06  7:58 ` Patrick Steinhardt
  2025-02-06 16:14 ` [PATCH 00/16] path: remove dependency on `the_repository` shejialuo
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-06  7:58 UTC (permalink / raw)
  To: git

With the preceding refactorings we now only have a couple of implicit
users of `the_repository` left in the "path" subsystem, all of which
depend on global state via `calc_shared_perm()`. Make the dependency on
`the_repository` explicit by passing the repo as a parameter instead and
adjust callers accordingly.

Note that this change bubbles up into a couple of subsystems that were
previously declared as free from `the_repository`. Instead of marking
all of them as `the_repository`-dependent again, we instead use the
repository that is available in the calling context. There are three
exceptions though with "copy.c", "pack-write.c" and "tempfile.c".
Adjusting these would require us to adapt callsites all over the place,
so this is left for a future iteration.

Mark "path.c" as free from `the_repository`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c         |  2 +-
 commit-graph.c          |  2 +-
 copy.c                  |  4 +++-
 loose.c                 |  2 +-
 midx-write.c            |  2 +-
 object-file.c           |  8 ++++----
 pack-bitmap-write.c     |  2 +-
 pack-write.c            | 10 ++++++----
 path.c                  | 25 ++++++++++++-------------
 path.h                  |  6 +++---
 read-cache.c            |  2 +-
 refs/files-backend.c    | 10 +++++-----
 refs/reftable-backend.c | 10 +++++-----
 server-info.c           |  2 +-
 setup.c                 | 12 ++++++------
 tempfile.c              |  4 +++-
 tmp-objdir.c            | 17 ++++++++++-------
 17 files changed, 64 insertions(+), 56 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 5ae6ee9db9..23eeb782aa 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1220,7 +1220,7 @@ int cmd_clone(int argc,
 
 	strbuf_reset(&buf);
 	strbuf_addf(&buf, "%s/refs", git_dir);
-	safe_create_dir(buf.buf, 1);
+	safe_create_dir(the_repository, buf.buf, 1);
 
 	/*
 	 * additional config can be injected with -c, make sure it's included
diff --git a/commit-graph.c b/commit-graph.c
index 2a2999a6b8..1021ccb983 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -2084,7 +2084,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 			return -1;
 		}
 
-		if (adjust_shared_perm(get_tempfile_path(graph_layer))) {
+		if (adjust_shared_perm(the_repository, get_tempfile_path(graph_layer))) {
 			error(_("unable to adjust shared permissions for '%s'"),
 			      get_tempfile_path(graph_layer));
 			return -1;
diff --git a/copy.c b/copy.c
index d9d2092012..b668209b6c 100644
--- a/copy.c
+++ b/copy.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "copy.h"
 #include "path.h"
@@ -57,7 +59,7 @@ int copy_file(const char *dst, const char *src, int mode)
 	if (close(fdo) != 0)
 		return error_errno("%s: close error", dst);
 
-	if (!status && adjust_shared_perm(dst))
+	if (!status && adjust_shared_perm(the_repository, dst))
 		return -1;
 
 	return status;
diff --git a/loose.c b/loose.c
index 51ef490f93..bb602aaa36 100644
--- a/loose.c
+++ b/loose.c
@@ -190,7 +190,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
 		goto errout;
 	if (close(fd))
 		goto errout;
-	adjust_shared_perm(path.buf);
+	adjust_shared_perm(repo, path.buf);
 	rollback_lock_file(&lock);
 	strbuf_release(&buf);
 	strbuf_release(&path);
diff --git a/midx-write.c b/midx-write.c
index 61b59d557d..48d6558253 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1336,7 +1336,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
 			return -1;
 		}
 
-		if (adjust_shared_perm(get_tempfile_path(incr))) {
+		if (adjust_shared_perm(r, get_tempfile_path(incr))) {
 			error(_("unable to adjust shared permissions for '%s'"),
 			      get_tempfile_path(incr));
 			return -1;
diff --git a/object-file.c b/object-file.c
index dc9fcaf3e9..5d782417e3 100644
--- a/object-file.c
+++ b/object-file.c
@@ -388,7 +388,7 @@ int mkdir_in_gitdir(const char *path)
 		}
 		strbuf_release(&sb);
 	}
-	return adjust_shared_perm(path);
+	return adjust_shared_perm(the_repository, path);
 }
 
 static enum scld_error safe_create_leading_directories_1(char *path, int share)
@@ -437,7 +437,7 @@ static enum scld_error safe_create_leading_directories_1(char *path, int share)
 				ret = SCLD_VANISHED;
 			else
 				ret = SCLD_FAILED;
-		} else if (share && adjust_shared_perm(path)) {
+		} else if (share && adjust_shared_perm(the_repository, path)) {
 			ret = SCLD_PERMS;
 		}
 		*slash = slash_character;
@@ -2105,7 +2105,7 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
 	}
 
 out:
-	if (adjust_shared_perm(filename))
+	if (adjust_shared_perm(the_repository, filename))
 		return error(_("unable to set permission to '%s'"), filename);
 	return 0;
 }
@@ -2181,7 +2181,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
 		strbuf_add(tmp, filename, dirlen - 1);
 		if (mkdir(tmp->buf, 0777) && errno != EEXIST)
 			return -1;
-		if (adjust_shared_perm(tmp->buf))
+		if (adjust_shared_perm(the_repository, tmp->buf))
 			return -1;
 
 		/* Try again */
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index a06a1f35c6..34e86d4994 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1072,7 +1072,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
 			  CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
 
-	if (adjust_shared_perm(tmp_file.buf))
+	if (adjust_shared_perm(the_repository, tmp_file.buf))
 		die_errno("unable to make temporary bitmap file readable");
 
 	if (rename(tmp_file.buf, filename))
diff --git a/pack-write.c b/pack-write.c
index a2faeb1895..c3f4e66f02 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "environment.h"
 #include "gettext.h"
@@ -287,7 +289,7 @@ char *write_rev_file_order(const struct git_hash_algo *hash_algo,
 	write_rev_index_positions(f, pack_order, nr_objects);
 	write_rev_trailer(hash_algo, f, hash);
 
-	if (adjust_shared_perm(path) < 0)
+	if (adjust_shared_perm(the_repository, path) < 0)
 		die(_("failed to make %s readable"), path);
 
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -350,7 +352,7 @@ static char *write_mtimes_file(const struct git_hash_algo *hash_algo,
 	write_mtimes_objects(f, to_pack, objects, nr_objects);
 	write_mtimes_trailer(hash_algo, f, hash);
 
-	if (adjust_shared_perm(mtimes_name) < 0)
+	if (adjust_shared_perm(the_repository, mtimes_name) < 0)
 		die(_("failed to make %s readable"), mtimes_name);
 
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -565,12 +567,12 @@ void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
 	char *rev_tmp_name = NULL;
 	char *mtimes_tmp_name = NULL;
 
-	if (adjust_shared_perm(pack_tmp_name))
+	if (adjust_shared_perm(the_repository, pack_tmp_name))
 		die_errno("unable to make temporary pack file readable");
 
 	*idx_tmp_name = (char *)write_idx_file(hash_algo, NULL, written_list,
 					       nr_written, pack_idx_opts, hash);
-	if (adjust_shared_perm(*idx_tmp_name))
+	if (adjust_shared_perm(the_repository, *idx_tmp_name))
 		die_errno("unable to make temporary index file readable");
 
 	rev_tmp_name = write_rev_file(hash_algo, NULL, written_list, nr_written,
diff --git a/path.c b/path.c
index a2f402baec..910756c8b3 100644
--- a/path.c
+++ b/path.c
@@ -2,8 +2,6 @@
  * Utilities for paths and pathnames
  */
 
-#define USE_THE_REPOSITORY_VARIABLE
-
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "environment.h"
@@ -840,21 +838,22 @@ const char *enter_repo(const char *path, unsigned flags)
 	return NULL;
 }
 
-int calc_shared_perm(int mode)
+int calc_shared_perm(struct repository *repo,
+		     int mode)
 {
 	int tweak;
 
-	if (repo_settings_get_shared_repository(the_repository) < 0)
-		tweak = -repo_settings_get_shared_repository(the_repository);
+	if (repo_settings_get_shared_repository(repo) < 0)
+		tweak = -repo_settings_get_shared_repository(repo);
 	else
-		tweak = repo_settings_get_shared_repository(the_repository);
+		tweak = repo_settings_get_shared_repository(repo);
 
 	if (!(mode & S_IWUSR))
 		tweak &= ~0222;
 	if (mode & S_IXUSR)
 		/* Copy read bits to execute bits */
 		tweak |= (tweak & 0444) >> 2;
-	if (repo_settings_get_shared_repository(the_repository) < 0)
+	if (repo_settings_get_shared_repository(repo) < 0)
 		mode = (mode & ~0777) | tweak;
 	else
 		mode |= tweak;
@@ -862,17 +861,17 @@ int calc_shared_perm(int mode)
 	return mode;
 }
 
-
-int adjust_shared_perm(const char *path)
+int adjust_shared_perm(struct repository *repo,
+		       const char *path)
 {
 	int old_mode, new_mode;
 
-	if (!repo_settings_get_shared_repository(the_repository))
+	if (!repo_settings_get_shared_repository(repo))
 		return 0;
 	if (get_st_mode_bits(path, &old_mode) < 0)
 		return -1;
 
-	new_mode = calc_shared_perm(old_mode);
+	new_mode = calc_shared_perm(repo, old_mode);
 	if (S_ISDIR(old_mode)) {
 		/* Copy read bits to execute bits */
 		new_mode |= (new_mode & 0444) >> 2;
@@ -891,7 +890,7 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
-void safe_create_dir(const char *dir, int share)
+void safe_create_dir(struct repository *repo, const char *dir, int share)
 {
 	if (mkdir(dir, 0777) < 0) {
 		if (errno != EEXIST) {
@@ -899,7 +898,7 @@ void safe_create_dir(const char *dir, int share)
 			exit(1);
 		}
 	}
-	else if (share && adjust_shared_perm(dir))
+	else if (share && adjust_shared_perm(repo, dir))
 		die(_("Could not make %s writable by group"), dir);
 }
 
diff --git a/path.h b/path.h
index 373404dd9d..65fe968a13 100644
--- a/path.h
+++ b/path.h
@@ -141,8 +141,8 @@ const char *git_path_shallow(struct repository *r);
 
 int ends_with_path_components(const char *path, const char *components);
 
-int calc_shared_perm(int mode);
-int adjust_shared_perm(const char *path);
+int calc_shared_perm(struct repository *repo, int mode);
+int adjust_shared_perm(struct repository *repo, const char *path);
 
 char *interpolate_path(const char *path, int real_home);
 
@@ -219,7 +219,7 @@ char *xdg_cache_home(const char *filename);
  * directories under $GIT_DIR.  Don't use it for working tree
  * directories.
  */
-void safe_create_dir(const char *dir, int share);
+void safe_create_dir(struct repository *repo, const char *dir, int share);
 
 # ifdef USE_THE_REPOSITORY_VARIABLE
 #  include "strbuf.h"
diff --git a/read-cache.c b/read-cache.c
index 66ad0015a7..900738e7a8 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3290,7 +3290,7 @@ static int write_shared_index(struct index_state *istate,
 
 	if (ret)
 		return ret;
-	ret = adjust_shared_perm(get_tempfile_path(*temp));
+	ret = adjust_shared_perm(the_repository, get_tempfile_path(*temp));
 	if (ret) {
 		error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
 		return ret;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 29f08dced4..6c6e67dc1c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1831,7 +1831,7 @@ static int log_ref_setup(struct files_ref_store *refs,
 	}
 
 	if (*logfd >= 0)
-		adjust_shared_perm(logfile);
+		adjust_shared_perm(the_repository, logfile);
 
 	free(logfile);
 	return 0;
@@ -3488,8 +3488,8 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
 	 *   they do not understand the reference format extension.
 	 */
 	strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
-	safe_create_dir(sb.buf, 1);
-	adjust_shared_perm(sb.buf);
+	safe_create_dir(the_repository, sb.buf, 1);
+	adjust_shared_perm(the_repository, sb.buf);
 
 	/*
 	 * There is no need to create directories for common refs when creating
@@ -3501,11 +3501,11 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
 		 */
 		strbuf_reset(&sb);
 		files_ref_path(refs, &sb, "refs/heads");
-		safe_create_dir(sb.buf, 1);
+		safe_create_dir(the_repository, sb.buf, 1);
 
 		strbuf_reset(&sb);
 		files_ref_path(refs, &sb, "refs/tags");
-		safe_create_dir(sb.buf, 1);
+		safe_create_dir(the_repository, sb.buf, 1);
 	}
 
 	strbuf_release(&sb);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d39a14c5a4..9c54e2c173 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -380,7 +380,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 	default:
 		BUG("unknown hash algorithm %d", repo->hash_algo->format_id);
 	}
-	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
+	refs->write_options.default_permissions = calc_shared_perm(the_repository, 0666 & ~mask);
 	refs->write_options.disable_auto_compact =
 		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
 	refs->write_options.lock_timeout_ms = 100;
@@ -470,21 +470,21 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
 	struct strbuf sb = STRBUF_INIT;
 
 	strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
-	safe_create_dir(sb.buf, 1);
+	safe_create_dir(the_repository, sb.buf, 1);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
 	write_file(sb.buf, "ref: refs/heads/.invalid");
-	adjust_shared_perm(sb.buf);
+	adjust_shared_perm(the_repository, sb.buf);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
-	safe_create_dir(sb.buf, 1);
+	safe_create_dir(the_repository, sb.buf, 1);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
 	write_file(sb.buf, "this repository uses the reftable format");
-	adjust_shared_perm(sb.buf);
+	adjust_shared_perm(the_repository, sb.buf);
 
 	strbuf_release(&sb);
 	return 0;
diff --git a/server-info.c b/server-info.c
index 31c3fdc118..1ca0e00d51 100644
--- a/server-info.c
+++ b/server-info.c
@@ -125,7 +125,7 @@ static int update_info_file(struct repository *r, char *path,
 	uic.cur_fp = NULL;
 
 	if (uic_is_stale(&uic)) {
-		if (adjust_shared_perm(get_tempfile_path(f)) < 0)
+		if (adjust_shared_perm(r, get_tempfile_path(f)) < 0)
 			goto out;
 		if (rename_tempfile(&f, path) < 0)
 			goto out;
diff --git a/setup.c b/setup.c
index aa65b93f53..71a3d66f05 100644
--- a/setup.c
+++ b/setup.c
@@ -2088,7 +2088,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
 	 * with the way the namespace under .git/ is organized, should
 	 * be really carefully chosen.
 	 */
-	safe_create_dir(path->buf, 1);
+	safe_create_dir(the_repository, path->buf, 1);
 	while ((de = readdir(dir)) != NULL) {
 		struct stat st_git, st_template;
 		int exists = 0;
@@ -2352,7 +2352,7 @@ static int create_default_files(const char *template_path,
 	 * shared-repository settings, we would need to fix them up.
 	 */
 	if (repo_settings_get_shared_repository(the_repository)) {
-		adjust_shared_perm(repo_get_git_dir(the_repository));
+		adjust_shared_perm(the_repository, repo_get_git_dir(the_repository));
 	}
 
 	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
@@ -2413,15 +2413,15 @@ static void create_object_directory(void)
 	strbuf_addstr(&path, repo_get_object_directory(the_repository));
 	baselen = path.len;
 
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/pack");
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/info");
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_release(&path);
 }
@@ -2588,7 +2588,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 	 */
 	git_config(platform_core_config, NULL);
 
-	safe_create_dir(git_dir, 0);
+	safe_create_dir(the_repository, git_dir, 0);
 
 	reinit = create_default_files(template_dir, original_git_dir,
 				      &repo_fmt, init_shared_repository);
diff --git a/tempfile.c b/tempfile.c
index ed88cf8431..82dfa3d82f 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -42,6 +42,8 @@
  * file created by its parent.
  */
 
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "path.h"
@@ -148,7 +150,7 @@ struct tempfile *create_tempfile_mode(const char *path, int mode)
 		return NULL;
 	}
 	activate_tempfile(tempfile);
-	if (adjust_shared_perm(tempfile->filename.buf)) {
+	if (adjust_shared_perm(the_repository, tempfile->filename.buf)) {
 		int save_errno = errno;
 		error("cannot fix permission bits on %s", tempfile->filename.buf);
 		delete_tempfile(&tempfile);
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 0ea078a5c5..31d16a4c2c 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -207,10 +207,12 @@ static int read_dir_paths(struct string_list *out, const char *path)
 	return 0;
 }
 
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+			 struct strbuf *src, struct strbuf *dst,
 			 enum finalize_object_file_flags flags);
 
-static int migrate_one(struct strbuf *src, struct strbuf *dst,
+static int migrate_one(struct tmp_objdir *t,
+		       struct strbuf *src, struct strbuf *dst,
 		       enum finalize_object_file_flags flags)
 {
 	struct stat st;
@@ -219,11 +221,11 @@ static int migrate_one(struct strbuf *src, struct strbuf *dst,
 		return -1;
 	if (S_ISDIR(st.st_mode)) {
 		if (!mkdir(dst->buf, 0777)) {
-			if (adjust_shared_perm(dst->buf))
+			if (adjust_shared_perm(t->repo, dst->buf))
 				return -1;
 		} else if (errno != EEXIST)
 			return -1;
-		return migrate_paths(src, dst, flags);
+		return migrate_paths(t, src, dst, flags);
 	}
 	return finalize_object_file_flags(src->buf, dst->buf, flags);
 }
@@ -233,7 +235,8 @@ static int is_loose_object_shard(const char *name)
 	return strlen(name) == 2 && isxdigit(name[0]) && isxdigit(name[1]);
 }
 
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+			 struct strbuf *src, struct strbuf *dst,
 			 enum finalize_object_file_flags flags)
 {
 	size_t src_len = src->len, dst_len = dst->len;
@@ -255,7 +258,7 @@ static int migrate_paths(struct strbuf *src, struct strbuf *dst,
 		if (is_loose_object_shard(name))
 			flags_copy |= FOF_SKIP_COLLISION_CHECK;
 
-		ret |= migrate_one(src, dst, flags_copy);
+		ret |= migrate_one(t, src, dst, flags_copy);
 
 		strbuf_setlen(src, src_len);
 		strbuf_setlen(dst, dst_len);
@@ -283,7 +286,7 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
 	strbuf_addbuf(&src, &t->path);
 	strbuf_addstr(&dst, repo_get_object_directory(t->repo));
 
-	ret = migrate_paths(&src, &dst, 0);
+	ret = migrate_paths(t, &src, &dst, 0);
 
 	strbuf_release(&src);
 	strbuf_release(&dst);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* Re: [PATCH 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
@ 2025-02-06 11:17   ` Karthik Nayak
  2025-02-07  6:16     ` Patrick Steinhardt
  2025-02-06 14:21   ` shejialuo
  1 sibling, 1 reply; 64+ messages in thread
From: Karthik Nayak @ 2025-02-06 11:17 UTC (permalink / raw)
  To: Patrick Steinhardt, git

[-- Attachment #1: Type: text/plain, Size: 683 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

[snip]

> diff --git a/path.c b/path.c
> index 07964f5d32..273b649e00 100644
> --- a/path.c
> +++ b/path.c
> @@ -414,7 +414,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
>  	else if (!wt->id)
>  		strbuf_addstr(buf, repo->commondir);
>  	else
> -		strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
> +		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);

This is the only place we replace `strbuf_git_common_path` with
`repo_common_path_append`, whereas other places it is
`repo_common_path_replace`. I assume that is because in those places the
buf is newly initialized.

[snip]

Rest of the patch looks good!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/16] path: refactor `repo_git_path()` family of functions
  2025-02-06  7:57 ` [PATCH 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
@ 2025-02-06 11:53   ` Karthik Nayak
  2025-02-07  6:15     ` Patrick Steinhardt
  0 siblings, 1 reply; 64+ messages in thread
From: Karthik Nayak @ 2025-02-06 11:53 UTC (permalink / raw)
  To: Patrick Steinhardt, git

[-- Attachment #1: Type: text/plain, Size: 839 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

[snip]

> @@ -241,11 +228,14 @@ struct strbuf *get_pathname(void);
>  #  include "strbuf.h"
>  #  include "repository.h"
>
> -/* Internal implementation detail that should not be used. */
> +/* Internal implementation details that should not be used. */
>  void repo_common_pathv(const struct repository *repo,
>  		       struct strbuf *buf,
>  		       const char *fmt,
>  		       va_list args);
> +void repo_git_pathv(struct repository *repo,
> +		    const struct worktree *wt, struct strbuf *buf,
> +		    const char *fmt, va_list args);
>

The only thing that stood out to me was that we loose the `const` here
for `repository`, I couldn't really find a reason why we do that,
especially since `repo_common_pathv()` right above still has it.
Everything else looks good in this patch.

[snip]

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
@ 2025-02-06 12:05   ` Karthik Nayak
  2025-02-07  6:16     ` Patrick Steinhardt
  2025-02-06 15:03   ` shejialuo
  1 sibling, 1 reply; 64+ messages in thread
From: Karthik Nayak @ 2025-02-06 12:05 UTC (permalink / raw)
  To: Patrick Steinhardt, git

[-- Attachment #1: Type: text/plain, Size: 2613 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

[snip]

> diff --git a/path.c b/path.c
> index d918d0409e..d721507be8 100644
> --- a/path.c
> +++ b/path.c
> @@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
>  }
>
>  /* Returns 0 on success, negative on failure. */
> -static int do_submodule_path(struct strbuf *buf, const char *path,
> +static int do_submodule_path(struct repository *repo,
> +			     struct strbuf *buf, const char *path,
>  			     const char *fmt, va_list args)
>  {
>  	struct strbuf git_submodule_common_dir = STRBUF_INIT;
>  	struct strbuf git_submodule_dir = STRBUF_INIT;
>  	int ret;
>
> -	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
> +	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
>  	if (ret)
>  		goto cleanup;
>
> @@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
>  	return ret;
>  }
>
> -char *git_pathdup_submodule(const char *path, const char *fmt, ...)
> +char *repo_submodule_path(struct repository *repo,

To stay consistent with the other repo_* functions, should we change
`struct repository *repo` to `const struct repository *repo`?

> +			  const char *path, const char *fmt, ...)
>  {
>  	int err;
>  	va_list args;
>  	struct strbuf buf = STRBUF_INIT;
>  	va_start(args, fmt);
> -	err = do_submodule_path(&buf, path, fmt, args);
> +	err = do_submodule_path(repo, &buf, path, fmt, args);
>  	va_end(args);
>  	if (err) {
>  		strbuf_release(&buf);
> @@ -601,16 +603,35 @@ char *git_pathdup_submodule(const char *path, const char *fmt, ...)
>  	return strbuf_detach(&buf, NULL);
>  }
>
> -int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
> -			      const char *fmt, ...)
> +const char *repo_submodule_path_append(struct repository *repo,
> +				       struct strbuf *buf,
> +				       const char *path,
> +				       const char *fmt, ...)
>  {
>  	int err;
>  	va_list args;
>  	va_start(args, fmt);
> -	err = do_submodule_path(buf, path, fmt, args);
> +	err = do_submodule_path(repo, buf, path, fmt, args);
>  	va_end(args);
> +	if (err)
> +		return NULL;
> +	return buf->buf;
> +}
>
> -	return err;
> +const char *repo_submodule_path_replace(struct repository *repo,
> +					struct strbuf *buf,
> +					const char *path,
> +					const char *fmt, ...)
> +{
> +	int err;
> +	va_list args;
> +	strbuf_reset(buf);
> +	va_start(args, fmt);
> +	err = do_submodule_path(repo, buf, path, fmt, args);
> +	va_end(args);
> +	if (err)
> +		return NULL;
> +	return buf->buf;
>  }
>
>  void repo_common_pathv(const struct repository *repo,

[snip]

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
  2025-02-06 11:17   ` Karthik Nayak
@ 2025-02-06 14:21   ` shejialuo
  2025-02-07  6:16     ` Patrick Steinhardt
  1 sibling, 1 reply; 64+ messages in thread
From: shejialuo @ 2025-02-06 14:21 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Thu, Feb 06, 2025 at 08:57:57AM +0100, Patrick Steinhardt wrote:
> The functions provided by the "path" subsystem to derive repository
> paths for the commondir, gitdir, worktrees and submodules are quite
> inconsistent. Some functions have a `strbuf_` prefix, others have
> different return values, some don't provide a variant working on top of
> `strbuf`s.
> 
> We're thus about to refactor all of these family of functions so that
> they follow a common pattern:
> 
>   - `repo_*_path()` returns an allocated string.
> 
>   - `repo_*_path_append()` appends the path to the caller-provided
>     buffer while returning a constant pointer to the buffer. This
>     clarifies whether the buffer is being appended to or rewritten,
>     which otherwise wasn't immediately obvious.
> 
>   - `repo_*_path_replace()` replaces contents of the buffer with the
>     computed path, again returning a pointer to the buffer contents.
> 

I want to ask a design question about this. Why do we need to return the
raw pointer to the `struct strbuf` for the last two cases? I somehow
understand why you want to do this. You want to follow a common pattern
for those three functions. But I wonder should we let the caller to
decide whether they want to use the raw pointer?

And in this patch, the return value of the last two cases has never been
used. Until I read the next patch, I have seen the usage of the return
value thus I could understand your motivation.

> diff --git a/path.h b/path.h
> index 5f6c85e5f8..3c75495e1a 100644
> --- a/path.h
> +++ b/path.h
> @@ -25,22 +25,20 @@ char *mkpathdup(const char *fmt, ...)
>  	__attribute__((format (printf, 1, 2)));
>  
>  /*
> - * The `strbuf_git_common_path` family of functions will construct a path into a
> + * The `repo_common_path` family of functions will construct a path into a
>   * repository's common git directory, which is shared by all worktrees.
>   */
> -
> -/*
> - * Constructs a path into the common git directory of repository `repo` and
> - * append it in the provided buffer `sb`.
> - */
> -void strbuf_git_common_path(struct strbuf *sb,
> -			    const struct repository *repo,
> -			    const char *fmt, ...)
> +char *repo_common_path(const struct repository *repo,
> +		       const char *fmt, ...)
> +	__attribute__((format (printf, 2, 3)));
> +const char *repo_common_path_append(const struct repository *repo,
> +				    struct strbuf *sb,
> +				    const char *fmt, ...)
> +	__attribute__((format (printf, 3, 4)));
> +const char *repo_common_path_replace(const struct repository *repo,
> +				     struct strbuf *sb,
> +				     const char *fmt, ...)
>  	__attribute__((format (printf, 3, 4)));
> -void repo_common_pathv(const struct repository *repo,
> -		       struct strbuf *buf,
> -		       const char *fmt,
> -		       va_list args);
>  
>  /*
>   * The `repo_git_path` family of functions will construct a path into a repository's
> @@ -243,6 +241,12 @@ struct strbuf *get_pathname(void);
>  #  include "strbuf.h"
>  #  include "repository.h"
>  
> +/* Internal implementation detail that should not be used. */
> +void repo_common_pathv(const struct repository *repo,
> +		       struct strbuf *buf,
> +		       const char *fmt,
> +		       va_list args);
> +

If we decide to make this as internal implementation, why we don't just
delete this declaration in the header file? Do I miss out something
here?

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
  2025-02-06 12:05   ` Karthik Nayak
@ 2025-02-06 15:03   ` shejialuo
  1 sibling, 0 replies; 64+ messages in thread
From: shejialuo @ 2025-02-06 15:03 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Thu, Feb 06, 2025 at 08:58:01AM +0100, Patrick Steinhardt wrote:
> As explained in an earlier commit, we're refactoring path-related
> functions to provide a consistent interface for computing paths into the
> commondir, gitdir and worktree. Refactor the "submodule" family of
> functions accordingly.
> 
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  builtin/submodule--helper.c |  2 +-
>  path.c                      | 37 +++++++++++++++++++++++++++++--------
>  path.h                      | 30 ++++++++++++++++++------------
>  t/helper/test-ref-store.c   |  7 +++----
>  worktree.c                  |  3 ++-
>  5 files changed, 53 insertions(+), 26 deletions(-)
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 3a64f7e605..c1a8029714 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1826,7 +1826,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
>  
>  	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
>  
> -	p = git_pathdup_submodule(clone_data_path, "config");
> +	p = repo_submodule_path(the_repository, clone_data_path, "config");
>  	if (!p)
>  		die(_("could not get submodule directory for '%s'"), clone_data_path);
>  
> diff --git a/path.c b/path.c
> index d918d0409e..d721507be8 100644
> --- a/path.c
> +++ b/path.c
> @@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
>  }
>  
>  /* Returns 0 on success, negative on failure. */
> -static int do_submodule_path(struct strbuf *buf, const char *path,
> +static int do_submodule_path(struct repository *repo,
> +			     struct strbuf *buf, const char *path,
>  			     const char *fmt, va_list args)
>  {
>  	struct strbuf git_submodule_common_dir = STRBUF_INIT;
>  	struct strbuf git_submodule_dir = STRBUF_INIT;
>  	int ret;
>  
> -	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
> +	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
>  	if (ret)
>  		goto cleanup;
>  
> @@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
>  	return ret;
>  }
>  
> -char *git_pathdup_submodule(const char *path, const char *fmt, ...)
> +char *repo_submodule_path(struct repository *repo,
> +			  const char *path, const char *fmt, ...)
>  {
>  	int err;
>  	va_list args;
>  	struct strbuf buf = STRBUF_INIT;
>  	va_start(args, fmt);
> -	err = do_submodule_path(&buf, path, fmt, args);
> +	err = do_submodule_path(repo, &buf, path, fmt, args);
>  	va_end(args);
>  	if (err) {
>  		strbuf_release(&buf);
> @@ -601,16 +603,35 @@ char *git_pathdup_submodule(const char *path, const char *fmt, ...)
>  	return strbuf_detach(&buf, NULL);
>  }
>  
> -int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
> -			      const char *fmt, ...)
> +const char *repo_submodule_path_append(struct repository *repo,
> +				       struct strbuf *buf,
> +				       const char *path,
> +				       const char *fmt, ...)
>  {
>  	int err;
>  	va_list args;
>  	va_start(args, fmt);
> -	err = do_submodule_path(buf, path, fmt, args);
> +	err = do_submodule_path(repo, buf, path, fmt, args);
>  	va_end(args);
> +	if (err)
> +		return NULL;
> +	return buf->buf;
> +}
>  
> -	return err;
> +const char *repo_submodule_path_replace(struct repository *repo,
> +					struct strbuf *buf,
> +					const char *path,
> +					const char *fmt, ...)
> +{
> +	int err;
> +	va_list args;
> +	strbuf_reset(buf);
> +	va_start(args, fmt);
> +	err = do_submodule_path(repo, buf, path, fmt, args);
> +	va_end(args);
> +	if (err)
> +		return NULL;
> +	return buf->buf;
>  }

By reading through the patches from 1 to this. I gradually understand
your design now. For every refactor, we will provide three kinds of
functions. All of these functions will return `const char *` and we
could elegantly use `NULL` to indicate the error.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()`
  2025-02-06  7:58 ` [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
@ 2025-02-06 15:54   ` shejialuo
  2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 1 reply; 64+ messages in thread
From: shejialuo @ 2025-02-06 15:54 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Thu, Feb 06, 2025 at 08:58:06AM +0100, Patrick Steinhardt wrote:


> diff --git a/builtin/worktree.c b/builtin/worktree.c
> index 2cea9441a6..761e302a36 100644
> --- a/builtin/worktree.c
> +++ b/builtin/worktree.c
> @@ -151,7 +151,7 @@ static int delete_git_dir(const char *id)
>  	struct strbuf sb = STRBUF_INIT;
>  	int ret;
>  
> -	strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
> +	repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
>  	ret = remove_dir_recursively(&sb, 0);
>  	if (ret < 0 && errno == ENOTDIR)
>  		ret = unlink(sb.buf);
> @@ -1102,6 +1102,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
>  		OPT_END()
>  	};
>  	struct worktree **worktrees, *wt;
> +	char *path;
>  
>  	ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
>  	if (ac != 1)
> @@ -1122,9 +1123,11 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
>  		die(_("'%s' is already locked"), av[0]);
>  	}
>  
> -	write_file(git_common_path("worktrees/%s/locked", wt->id),
> -		   "%s", reason);
> +	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);

From my perspective, we may use `repo_common_path_replace` here to avoid
using the raw string pointer? This is because we return a changeable
pointer "char *". But we pass this pointer to a "const char *". This is
not critical, but we may make the semantics clearer.

> +	write_file(path, "%s", reason);
> +
>  	free_worktrees(worktrees);
> +	free(path);
>  	return 0;
>  }
>  
> @@ -1135,6 +1138,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
>  		OPT_END()
>  	};
>  	struct worktree **worktrees, *wt;
> +	char *path;
>  	int ret;
>  
>  	ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
> @@ -1149,8 +1153,12 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
>  		die(_("The main working tree cannot be locked or unlocked"));
>  	if (!worktree_lock_reason(wt))
>  		die(_("'%s' is not locked"), av[0]);
> -	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
> +
> +	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);

This one as above.

> +	ret = unlink_or_warn(path);
> +
>  	free_worktrees(worktrees);
> +	free(path);
>  	return ret;
>  }
>  
> diff --git a/path.c b/path.c
> index d721507be8..f6b795d75f 100644
> --- a/path.c
> +++ b/path.c
> @@ -634,10 +634,10 @@ const char *repo_submodule_path_replace(struct repository *repo,
>  	return buf->buf;
>  }
>  
> -void repo_common_pathv(const struct repository *repo,
> -		       struct strbuf *sb,
> -		       const char *fmt,
> -		       va_list args)
> +static void repo_common_pathv(const struct repository *repo,
> +			      struct strbuf *sb,
> +			      const char *fmt,
> +			      va_list args)
>  {
>  	strbuf_addstr(sb, repo->commondir);
>  	if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
> diff --git a/path.h b/path.h
> index 904eeac068..496f27fdfd 100644
> --- a/path.h
> +++ b/path.h
> @@ -233,29 +233,10 @@ struct strbuf *get_pathname(void);
>  #  include "repository.h"
>  
>  /* Internal implementation details that should not be used. */
> -void repo_common_pathv(const struct repository *repo,
> -		       struct strbuf *buf,
> -		       const char *fmt,
> -		       va_list args);

So, we finally mark this function "static" and delete the declaration in
this patch. We cannot do this in the earlier patch because
"git_common_path" is defined in the header file and it needs to use this
function. Make sense.

However, I somehow feel a little strange especially in [PATCH 01/16]
that you have added a comment:

    /* Internal implementation detail that should not be used. *

When I see this comment, my first intuitive thinking is that if we
should not use this function, why do we need to expose this in the first
place?

This really introduces confusion.

> @@ -343,7 +344,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
>  	if (!is_absolute_path(wt->path)) {
>  		strbuf_addf_gently(errmsg,
>  				   _("'%s' file does not contain absolute path to the working tree location"),
> -				   git_common_path("worktrees/%s/gitdir", wt->id));
> +				   repo_common_path_replace(the_repository, &buf, "worktrees/%s/gitdir", wt->id));
>  		goto done;
>  	}
>  
> @@ -365,14 +366,16 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
>  		goto done;
>  	}
>  
> -	strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
> +	strbuf_realpath(&realpath, repo_common_path_replace(the_repository, &buf, "worktrees/%s", wt->id), 1);

We rely on the return value of `repo_common_path_replace` to elegantly
do this. Make sense.

>  	ret = fspathcmp(path, realpath.buf);
>  
>  	if (ret)
>  		strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
> -				   wt->path, git_common_path("worktrees/%s", wt->id));
> +				   wt->path, repo_common_path_replace(the_repository, &buf,
> +								      "worktrees/%s", wt->id));
>  done:
>  	free(path);
> +	strbuf_release(&buf);
>  	strbuf_release(&wt_path);
>  	strbuf_release(&realpath);
>  	return ret;
> @@ -384,11 +387,13 @@ void update_worktree_location(struct worktree *wt, const char *path_,
>  	struct strbuf path = STRBUF_INIT;
>  	struct strbuf dotgit = STRBUF_INIT;
>  	struct strbuf gitdir = STRBUF_INIT;
> +	char *wt_gitdir;
>  
>  	if (is_main_worktree(wt))
>  		BUG("can't relocate main worktree");
>  
> -	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
> +	wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
> +	strbuf_realpath(&gitdir, wt_gitdir, 1);

Why we don't use above pattern which means the following:

    strbuf_realpath(&gitdir, git_common_path_replace(...), ...);

I think we should be consistent. And we should not use "char *" type to
pass to a "const char *" type here although this won't be harmful to the
program. However, git_common_path_replace will return a "const char *"
to make sure the caller cannot change this pointer.

>  	strbuf_realpath(&path, path_, 1);
>  	strbuf_addf(&dotgit, "%s/.git", path.buf);
>  	if (fspathcmp(wt->path, path.buf)) {
> @@ -400,6 +405,7 @@ void update_worktree_location(struct worktree *wt, const char *path_,
>  	strbuf_release(&path);
>  	strbuf_release(&dotgit);
>  	strbuf_release(&gitdir);
> +	free(wt_gitdir);
>  }
>  
>  int is_worktree_being_rebased(const struct worktree *wt,
> @@ -585,6 +591,7 @@ static void repair_gitfile(struct worktree *wt,
>  	struct strbuf backlink = STRBUF_INIT;
>  	char *dotgit_contents = NULL;
>  	const char *repair = NULL;
> +	char *path = NULL;
>  	int err;
>  
>  	/* missing worktree can't be repaired */
> @@ -596,7 +603,8 @@ static void repair_gitfile(struct worktree *wt,
>  		goto done;
>  	}
>  
> -	strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
> +	path = repo_common_path(the_repository, "worktrees/%s", wt->id);
> +	strbuf_realpath(&repo, path, 1);

This one as above.

>  	strbuf_addf(&dotgit, "%s/.git", wt->path);
>  	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
>  	dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
> @@ -626,6 +634,7 @@ static void repair_gitfile(struct worktree *wt,
>  
>  done:
>  	free(dotgit_contents);
> +	free(path);
>  	strbuf_release(&repo);
>  	strbuf_release(&dotgit);
>  	strbuf_release(&gitdir);
> @@ -657,11 +666,13 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
>  	struct strbuf gitdir = STRBUF_INIT;
>  	struct strbuf dotgit = STRBUF_INIT;
>  	int is_relative_path;
> +	char *path = NULL;
>  
>  	if (is_main_worktree(wt))
>  		goto done;
>  
> -	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
> +	path = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
> +	strbuf_realpath(&gitdir, path, 1);
>  

Also this one.

>  	if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
>  		goto done;
> @@ -680,6 +691,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
>  done:
>  	strbuf_release(&gitdir);
>  	strbuf_release(&dotgit);
> +	free(path);
>  }
>  
>  void repair_worktrees_after_gitdir_move(const char *old_path)
> @@ -871,7 +883,11 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
>  	ssize_t read_result;
>  
>  	*wtpath = NULL;
> -	strbuf_realpath(&repo, git_common_path("worktrees/%s", id), 1);
> +
> +	path = repo_common_path(the_repository, "worktrees/%s", id);
> +	strbuf_realpath(&repo, path, 1);
> +	FREE_AND_NULL(path);
> +

I somehow agree that we could use `repo_common_path` in this way where
we want to reuse "path" variable.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()`
  2025-02-06  7:58 ` [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
@ 2025-02-06 16:01   ` shejialuo
  2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 1 reply; 64+ messages in thread
From: shejialuo @ 2025-02-06 16:01 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Thu, Feb 06, 2025 at 08:58:08AM +0100, Patrick Steinhardt wrote:
> Remove `git_path()` in favor of the `repo_git_path()` family of
> functions, which makes the implicit dependency on `the_repository` go
> away.
> 

In this commit, I have concerns that I have already commented at
[PATCH 10/16]. I don't think we should use "repo_git_path" in most of
time, we may use "repo_git_path_replace" for most situation.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 00/16] path: remove dependency on `the_repository`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (15 preceding siblings ...)
  2025-02-06  7:58 ` [PATCH 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
@ 2025-02-06 16:14 ` shejialuo
  2025-02-07  6:16   ` Patrick Steinhardt
  2025-02-07  8:17 ` Karthik Nayak
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
  18 siblings, 1 reply; 64+ messages in thread
From: shejialuo @ 2025-02-06 16:14 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Thu, Feb 06, 2025 at 08:57:56AM +0100, Patrick Steinhardt wrote:
> Hi,
> 
> this patch series removes the dependency on `the_repository` from the
> "path" subsystem. The series is structured as follows:
> 
>   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
>     repository paths (gitdir, commondir, workdir and submodule paths)
>     and adapts callers accodringly.
> 
>   - Patches 6 to 12 drop repository path functions that had an implicit
>     dependency on `the_repository`.
> 
>   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
>     on `the_repository`.
> 

Thanks for working on this. However, I feel a little hard when reviewing
the code for patch 13 to patch 16. They are not so relevant to the
previous twelfth patches.

One thing I want to recommend is that we may combine the first and the
second step. It is a little wired that we first refactor the code then
we use the new one. When reading the code, I have to step back to the
previous patch to understand something.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 02/16] path: refactor `repo_git_path()` family of functions
  2025-02-06 11:53   ` Karthik Nayak
@ 2025-02-07  6:15     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:15 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git

On Thu, Feb 06, 2025 at 03:53:59AM -0800, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> [snip]
> 
> > @@ -241,11 +228,14 @@ struct strbuf *get_pathname(void);
> >  #  include "strbuf.h"
> >  #  include "repository.h"
> >
> > -/* Internal implementation detail that should not be used. */
> > +/* Internal implementation details that should not be used. */
> >  void repo_common_pathv(const struct repository *repo,
> >  		       struct strbuf *buf,
> >  		       const char *fmt,
> >  		       va_list args);
> > +void repo_git_pathv(struct repository *repo,
> > +		    const struct worktree *wt, struct strbuf *buf,
> > +		    const char *fmt, va_list args);
> >
> 
> The only thing that stood out to me was that we loose the `const` here
> for `repository`, I couldn't really find a reason why we do that,
> especially since `repo_common_pathv()` right above still has it.
> Everything else looks good in this patch.

You're right, there is none yet over here, so it's pointless to adapt
`repo_git_pathv()`. Later on this'll change because we're converting the
"core.hooksPath" variable to be stored in `struct repo_settings`, and
that'll require us to pass in a non-const pointer.

I'll shuffle this around a bit.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()`
  2025-02-06 15:54   ` shejialuo
@ 2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: shejialuo; +Cc: git

On Thu, Feb 06, 2025 at 11:54:24PM +0800, shejialuo wrote:
> On Thu, Feb 06, 2025 at 08:58:06AM +0100, Patrick Steinhardt wrote:
> 
> 
> > diff --git a/builtin/worktree.c b/builtin/worktree.c
> > index 2cea9441a6..761e302a36 100644
> > --- a/builtin/worktree.c
> > +++ b/builtin/worktree.c
> > @@ -151,7 +151,7 @@ static int delete_git_dir(const char *id)
> >  	struct strbuf sb = STRBUF_INIT;
> >  	int ret;
> >  
> > -	strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
> > +	repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
> >  	ret = remove_dir_recursively(&sb, 0);
> >  	if (ret < 0 && errno == ENOTDIR)
> >  		ret = unlink(sb.buf);
> > @@ -1102,6 +1102,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
> >  		OPT_END()
> >  	};
> >  	struct worktree **worktrees, *wt;
> > +	char *path;
> >  
> >  	ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
> >  	if (ac != 1)
> > @@ -1122,9 +1123,11 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
> >  		die(_("'%s' is already locked"), av[0]);
> >  	}
> >  
> > -	write_file(git_common_path("worktrees/%s/locked", wt->id),
> > -		   "%s", reason);
> > +	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
> 
> From my perspective, we may use `repo_common_path_replace` here to avoid
> using the raw string pointer? This is because we return a changeable
> pointer "char *". But we pass this pointer to a "const char *". This is
> not critical, but we may make the semantics clearer.

I don't think there's much of a point doing so though. We only use the
string a single time, so using a `strbuf` doesn't buy us anything. This
was different if we already had a buffer available that we could reuse,
but we don't.

> > diff --git a/path.c b/path.c
> > index d721507be8..f6b795d75f 100644
> > --- a/path.c
> > +++ b/path.c
> > @@ -634,10 +634,10 @@ const char *repo_submodule_path_replace(struct repository *repo,
> >  	return buf->buf;
> >  }
> >  
> > -void repo_common_pathv(const struct repository *repo,
> > -		       struct strbuf *sb,
> > -		       const char *fmt,
> > -		       va_list args)
> > +static void repo_common_pathv(const struct repository *repo,
> > +			      struct strbuf *sb,
> > +			      const char *fmt,
> > +			      va_list args)
> >  {
> >  	strbuf_addstr(sb, repo->commondir);
> >  	if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
> > diff --git a/path.h b/path.h
> > index 904eeac068..496f27fdfd 100644
> > --- a/path.h
> > +++ b/path.h
> > @@ -233,29 +233,10 @@ struct strbuf *get_pathname(void);
> >  #  include "repository.h"
> >  
> >  /* Internal implementation details that should not be used. */
> > -void repo_common_pathv(const struct repository *repo,
> > -		       struct strbuf *buf,
> > -		       const char *fmt,
> > -		       va_list args);
> 
> So, we finally mark this function "static" and delete the declaration in
> this patch. We cannot do this in the earlier patch because
> "git_common_path" is defined in the header file and it needs to use this
> function. Make sense.
> 
> However, I somehow feel a little strange especially in [PATCH 01/16]
> that you have added a comment:
> 
>     /* Internal implementation detail that should not be used. *
> 
> When I see this comment, my first intuitive thinking is that if we
> should not use this function, why do we need to expose this in the first
> place?
> 
> This really introduces confusion.

I've touched up the first commit message to explain this a bit better.

> > @@ -384,11 +387,13 @@ void update_worktree_location(struct worktree *wt, const char *path_,
> >  	struct strbuf path = STRBUF_INIT;
> >  	struct strbuf dotgit = STRBUF_INIT;
> >  	struct strbuf gitdir = STRBUF_INIT;
> > +	char *wt_gitdir;
> >  
> >  	if (is_main_worktree(wt))
> >  		BUG("can't relocate main worktree");
> >  
> > -	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
> > +	wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
> > +	strbuf_realpath(&gitdir, wt_gitdir, 1);
> 
> Why we don't use above pattern which means the following:
> 
>     strbuf_realpath(&gitdir, git_common_path_replace(...), ...);
> 
> I think we should be consistent.

We can't because `strbuf_realpath()` is not prepared to use the buffer
as in-out parameter.

> And we should not use "char *" type to pass to a "const char *" type
> here although this won't be harmful to the program. However,
> git_common_path_replace will return a "const char *" to make sure the
> caller cannot change this pointer.

Passing a `char *` to a `const char *` parameter is fine in general and
expected in places where the string is allocated. Using a `strbuf`
everywhere wouldn't buy us much in cases where we cannot reuse it.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()`
  2025-02-06 16:01   ` shejialuo
@ 2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: shejialuo; +Cc: git

On Fri, Feb 07, 2025 at 12:01:19AM +0800, shejialuo wrote:
> On Thu, Feb 06, 2025 at 08:58:08AM +0100, Patrick Steinhardt wrote:
> > Remove `git_path()` in favor of the `repo_git_path()` family of
> > functions, which makes the implicit dependency on `the_repository` go
> > away.
> > 
> 
> In this commit, I have concerns that I have already commented at
> [PATCH 10/16]. I don't think we should use "repo_git_path" in most of
> time, we may use "repo_git_path_replace" for most situation.

As mentioned in that commit, I don't quite agree with you. We should do
so if it allows us to reuse buffers, but if it doesn't it is totally
fine to allocate and free C strings.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 00/16] path: remove dependency on `the_repository`
  2025-02-06 16:14 ` [PATCH 00/16] path: remove dependency on `the_repository` shejialuo
@ 2025-02-07  6:16   ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: shejialuo; +Cc: git

On Fri, Feb 07, 2025 at 12:14:14AM +0800, shejialuo wrote:
> On Thu, Feb 06, 2025 at 08:57:56AM +0100, Patrick Steinhardt wrote:
> > Hi,
> > 
> > this patch series removes the dependency on `the_repository` from the
> > "path" subsystem. The series is structured as follows:
> > 
> >   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
> >     repository paths (gitdir, commondir, workdir and submodule paths)
> >     and adapts callers accodringly.
> > 
> >   - Patches 6 to 12 drop repository path functions that had an implicit
> >     dependency on `the_repository`.
> > 
> >   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
> >     on `the_repository`.
> > 
> 
> Thanks for working on this. However, I feel a little hard when reviewing
> the code for patch 13 to patch 16. They are not so relevant to the
> previous twelfth patches.

Thanks for your review!

> One thing I want to recommend is that we may combine the first and the
> second step. It is a little wired that we first refactor the code then
> we use the new one. When reading the code, I have to step back to the
> previous patch to understand something.

But those are about very different things. The first step fills in
missing interfaces and unifies the calling conventions, whereas the
second step removes users of functions that don't have a dependency on
`the_repository`. From my perspective, mixing these two steps up with
one another would only lead to confusion as we're doing too many things
at once.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-06 14:21   ` shejialuo
@ 2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: shejialuo; +Cc: git

On Thu, Feb 06, 2025 at 10:21:24PM +0800, shejialuo wrote:
> On Thu, Feb 06, 2025 at 08:57:57AM +0100, Patrick Steinhardt wrote:
> > The functions provided by the "path" subsystem to derive repository
> > paths for the commondir, gitdir, worktrees and submodules are quite
> > inconsistent. Some functions have a `strbuf_` prefix, others have
> > different return values, some don't provide a variant working on top of
> > `strbuf`s.
> > 
> > We're thus about to refactor all of these family of functions so that
> > they follow a common pattern:
> > 
> >   - `repo_*_path()` returns an allocated string.
> > 
> >   - `repo_*_path_append()` appends the path to the caller-provided
> >     buffer while returning a constant pointer to the buffer. This
> >     clarifies whether the buffer is being appended to or rewritten,
> >     which otherwise wasn't immediately obvious.
> > 
> >   - `repo_*_path_replace()` replaces contents of the buffer with the
> >     computed path, again returning a pointer to the buffer contents.
> > 
> 
> I want to ask a design question about this. Why do we need to return the
> raw pointer to the `struct strbuf` for the last two cases? I somehow
> understand why you want to do this. You want to follow a common pattern
> for those three functions. But I wonder should we let the caller to
> decide whether they want to use the raw pointer?

It allows patterns like this:

    if (stat(&st, repo_common_path_replace(...)) ||
        unlink(&st, repo_common_path_replace(...)))
            ...

So the reason is not that I want to follow a common pattern, the reason
is that it's useful to some callers.

> And in this patch, the return value of the last two cases has never been
> used. Until I read the next patch, I have seen the usage of the return
> value thus I could understand your motivation.

Yeah, that's fair. I'll adapt the commit message to explain this better.

> > diff --git a/path.h b/path.h
> > index 5f6c85e5f8..3c75495e1a 100644
> > --- a/path.h
> > +++ b/path.h
> > @@ -243,6 +241,12 @@ struct strbuf *get_pathname(void);
> >  #  include "strbuf.h"
> >  #  include "repository.h"
> >  
> > +/* Internal implementation detail that should not be used. */
> > +void repo_common_pathv(const struct repository *repo,
> > +		       struct strbuf *buf,
> > +		       const char *fmt,
> > +		       va_list args);
> > +
> 
> If we decide to make this as internal implementation, why we don't just
> delete this declaration in the header file? Do I miss out something
> here?

We can't, it's still used to implement `git_common_path()` in the
header. We'll remove it in a subsequent commit.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-06 11:17   ` Karthik Nayak
@ 2025-02-07  6:16     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git

On Thu, Feb 06, 2025 at 03:17:12AM -0800, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> [snip]
> 
> > diff --git a/path.c b/path.c
> > index 07964f5d32..273b649e00 100644
> > --- a/path.c
> > +++ b/path.c
> > @@ -414,7 +414,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
> >  	else if (!wt->id)
> >  		strbuf_addstr(buf, repo->commondir);
> >  	else
> > -		strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
> > +		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
> 
> This is the only place we replace `strbuf_git_common_path` with
> `repo_common_path_append`, whereas other places it is
> `repo_common_path_replace`. I assume that is because in those places the
> buf is newly initialized.

Yes, exactly. I think that it's easier to reason about to replace
callsites with `_replace()` where we can. But I'm happy to adjust the
approach when it's putting too much of a burden on the reviewer.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-06 12:05   ` Karthik Nayak
@ 2025-02-07  6:16     ` Patrick Steinhardt
  2025-02-07  7:04       ` Karthik Nayak
  0 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07  6:16 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git

On Thu, Feb 06, 2025 at 04:05:13AM -0800, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> [snip]
> 
> > diff --git a/path.c b/path.c
> > index d918d0409e..d721507be8 100644
> > --- a/path.c
> > +++ b/path.c
> > @@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
> >  }
> >
> >  /* Returns 0 on success, negative on failure. */
> > -static int do_submodule_path(struct strbuf *buf, const char *path,
> > +static int do_submodule_path(struct repository *repo,
> > +			     struct strbuf *buf, const char *path,
> >  			     const char *fmt, va_list args)
> >  {
> >  	struct strbuf git_submodule_common_dir = STRBUF_INIT;
> >  	struct strbuf git_submodule_dir = STRBUF_INIT;
> >  	int ret;
> >
> > -	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
> > +	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
> >  	if (ret)
> >  		goto cleanup;
> >
> > @@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
> >  	return ret;
> >  }
> >
> > -char *git_pathdup_submodule(const char *path, const char *fmt, ...)
> > +char *repo_submodule_path(struct repository *repo,
> 
> To stay consistent with the other repo_* functions, should we change
> `struct repository *repo` to `const struct repository *repo`?

Somebody noticed :) But no, we cannot, we need to internally pass the
repo to functions that expect a non-const pointer. This is because deep
down in the callstack we end up calling `repo_read_gitmodules()`, which
modifies the repository.

I'll add a comment to the commit message.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-07  6:16     ` Patrick Steinhardt
@ 2025-02-07  7:04       ` Karthik Nayak
  0 siblings, 0 replies; 64+ messages in thread
From: Karthik Nayak @ 2025-02-07  7:04 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 1752 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

> On Thu, Feb 06, 2025 at 04:05:13AM -0800, Karthik Nayak wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>>
>> [snip]
>>
>> > diff --git a/path.c b/path.c
>> > index d918d0409e..d721507be8 100644
>> > --- a/path.c
>> > +++ b/path.c
>> > @@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
>> >  }
>> >
>> >  /* Returns 0 on success, negative on failure. */
>> > -static int do_submodule_path(struct strbuf *buf, const char *path,
>> > +static int do_submodule_path(struct repository *repo,
>> > +			     struct strbuf *buf, const char *path,
>> >  			     const char *fmt, va_list args)
>> >  {
>> >  	struct strbuf git_submodule_common_dir = STRBUF_INIT;
>> >  	struct strbuf git_submodule_dir = STRBUF_INIT;
>> >  	int ret;
>> >
>> > -	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
>> > +	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
>> >  	if (ret)
>> >  		goto cleanup;
>> >
>> > @@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
>> >  	return ret;
>> >  }
>> >
>> > -char *git_pathdup_submodule(const char *path, const char *fmt, ...)
>> > +char *repo_submodule_path(struct repository *repo,
>>
>> To stay consistent with the other repo_* functions, should we change
>> `struct repository *repo` to `const struct repository *repo`?
>
> Somebody noticed :) But no, we cannot, we need to internally pass the
> repo to functions that expect a non-const pointer. This is because deep
> down in the callstack we end up calling `repo_read_gitmodules()`, which
> modifies the repository.
>

Okay that makes sense.

> I'll add a comment to the commit message.
>
> Patrick
>

Okay perfect, thanks!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()`
  2025-02-06  7:58 ` [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
@ 2025-02-07  7:15   ` Karthik Nayak
  2025-02-07 10:49     ` Patrick Steinhardt
  0 siblings, 1 reply; 64+ messages in thread
From: Karthik Nayak @ 2025-02-07  7:15 UTC (permalink / raw)
  To: Patrick Steinhardt, git

[-- Attachment #1: Type: text/plain, Size: 4070 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

> The `get_worktree_git_dir()` function returns a string constant that
> does not need to be free'd by the caller. For `repo_get_git_dir()` and
> `repo_get_common_dir()` this is because we return strings owned by
> `the_repository`.
>

Not sure what the second sentence is signifying here. What relation does
`get_worktree_git_dir()` have with `repo_get_git_dir()` and
`repo_get_common_dir()`?

> But for `git_common_path()` it's a bit less obvious though, because that
> function does end up allocating memory. This doesn't result in a memory
> leak either because we write into a buffer returned by `get_pathname()`,
> which returns one out of four static buffers.
>

Now `git_common_path()`, what is binding all these functions together?
Sneaking down to the code, it looks like `get_worktree_git_dir()` calls
one of the other three functions. Maybe we should start with that?

>
> We're about to drop `git_common_path()` in favor of `repo_common_path()`,
> which doesn't use the same mechanism but instead returns an allocated
> string owned by the caller. While we could adapt `get_worktree_git_dir()`
> to also use `get_pathname()` and print the derived common path into that
> buffer, the whole schema feels a lot like premature optimization in this
> context. There are some callsites where we call `get_worktree_git_dir()`
> in a loop that iterates through all worktrees. But none of these loops
> seem to be even remotely in the hot path, so saving a single allocation
> there does not feel worth it.
>
> Refactor the function to instead consistently return an allocated path
> so that we can start using `repo_common_path()` in a subsequent commit.
>

This makes sense.

[snip]


> diff --git a/revision.c b/revision.c
> index 474fa1e767..be72f226f3 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
>  	for (p = worktrees; *p; p++) {
>  		struct worktree *wt = *p;
>  		struct index_state istate = INDEX_STATE_INIT(revs->repo);
> +		char *gitdir;
>

Nit: should this be named `wt_gitdir` to stay consistent?

>  		if (wt->is_current)
>  			continue; /* current index already taken care of */
>
> +		gitdir = get_worktree_git_dir(wt);
> +
>  		if (read_index_from(&istate,
>  				    worktree_git_path(the_repository, wt, "index"),
> -				    get_worktree_git_dir(wt)) > 0)
> +				    gitdir) > 0)
>  			do_add_index_objects_to_pending(revs, &istate, flags);
> +
>  		discard_index(&istate);
> +		free(gitdir);
>  	}
>  	free_worktrees(worktrees);
>  }

[snip]

> diff --git a/worktree.c b/worktree.c
> index 8f4fc10c44..3b94535963 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
>  static int is_current_worktree(struct worktree *wt)
>  {
>  	char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
> -	const char *wt_git_dir = get_worktree_git_dir(wt);
> +	char *wt_git_dir = get_worktree_git_dir(wt);

Nit: here too, even though it is a pre-existing name. Perhaps it is just
me. So feel free to ignore :)

>  	int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
> +	free(wt_git_dir);
>  	free(git_dir);
>  	return is_current;
>  }
> @@ -175,14 +176,14 @@ struct worktree **get_worktrees(void)
>  	return get_worktrees_internal(0);
>  }
>
> -const char *get_worktree_git_dir(const struct worktree *wt)
> +char *get_worktree_git_dir(const struct worktree *wt)
>  {
>  	if (!wt)
> -		return repo_get_git_dir(the_repository);
> +		return xstrdup(repo_get_git_dir(the_repository));
>  	else if (!wt->id)
> -		return repo_get_common_dir(the_repository);
> +		return xstrdup(repo_get_common_dir(the_repository));
>  	else
> -		return git_common_path("worktrees/%s", wt->id);
> +		return xstrdup(git_common_path("worktrees/%s", wt->id));
>  }
>

So this is the crux of the patch, we allocate the string for the first
two function's return value to ensure that `get_worktree_git_dir()`
returns a string which needs to be free'd. Alright!

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 00/16] path: remove dependency on `the_repository`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (16 preceding siblings ...)
  2025-02-06 16:14 ` [PATCH 00/16] path: remove dependency on `the_repository` shejialuo
@ 2025-02-07  8:17 ` Karthik Nayak
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
  18 siblings, 0 replies; 64+ messages in thread
From: Karthik Nayak @ 2025-02-07  8:17 UTC (permalink / raw)
  To: Patrick Steinhardt, git

[-- Attachment #1: Type: text/plain, Size: 727 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

> Hi,
>
> this patch series removes the dependency on `the_repository` from the
> "path" subsystem. The series is structured as follows:
>
>   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
>     repository paths (gitdir, commondir, workdir and submodule paths)
>     and adapts callers accodringly.
>
>   - Patches 6 to 12 drop repository path functions that had an implicit
>     dependency on `the_repository`.
>
>   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
>     on `the_repository`.
>
> Thanks!
>
> Patrick
>

I've reviewed the series, apart from some small nits, the series looks
great. Thanks for working on this.

Karthik

[snip]

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()`
  2025-02-07  7:15   ` Karthik Nayak
@ 2025-02-07 10:49     ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 10:49 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git

On Thu, Feb 06, 2025 at 11:15:58PM -0800, Karthik Nayak wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > The `get_worktree_git_dir()` function returns a string constant that
> > does not need to be free'd by the caller. For `repo_get_git_dir()` and
> > `repo_get_common_dir()` this is because we return strings owned by
> > `the_repository`.
> >
> 
> Not sure what the second sentence is signifying here. What relation does
> `get_worktree_git_dir()` have with `repo_get_git_dir()` and
> `repo_get_common_dir()`?

These are getting called in `get_worktree_git_dir()`. I'll rephrase the
message to make this clearer.

> > But for `git_common_path()` it's a bit less obvious though, because that
> > function does end up allocating memory. This doesn't result in a memory
> > leak either because we write into a buffer returned by `get_pathname()`,
> > which returns one out of four static buffers.
> >
> 
> Now `git_common_path()`, what is binding all these functions together?
> Sneaking down to the code, it looks like `get_worktree_git_dir()` calls
> one of the other three functions. Maybe we should start with that?

Yeah, I've reworded the commit message accordingly.

> > diff --git a/revision.c b/revision.c
> > index 474fa1e767..be72f226f3 100644
> > --- a/revision.c
> > +++ b/revision.c
> > @@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
> >  	for (p = worktrees; *p; p++) {
> >  		struct worktree *wt = *p;
> >  		struct index_state istate = INDEX_STATE_INIT(revs->repo);
> > +		char *gitdir;
> >
> 
> Nit: should this be named `wt_gitdir` to stay consistent?

Makes sense.

> > diff --git a/worktree.c b/worktree.c
> > index 8f4fc10c44..3b94535963 100644
> > --- a/worktree.c
> > +++ b/worktree.c
> > @@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
> >  static int is_current_worktree(struct worktree *wt)
> >  {
> >  	char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
> > -	const char *wt_git_dir = get_worktree_git_dir(wt);
> > +	char *wt_git_dir = get_worktree_git_dir(wt);
> 
> Nit: here too, even though it is a pre-existing name. Perhaps it is just
> me. So feel free to ignore :)

I'll leave that one as-is, even though I share your sentiment.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH v2 00/16] path: remove dependency on `the_repository`
  2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
                   ` (17 preceding siblings ...)
  2025-02-07  8:17 ` Karthik Nayak
@ 2025-02-07 11:03 ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
                     ` (17 more replies)
  18 siblings, 18 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Hi,

this patch series removes the dependency on `the_repository` from the
"path" subsystem. The series is structured as follows:

  - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
    repository paths (gitdir, commondir, workdir and submodule paths)
    and adapts callers accodringly.

  - Patches 6 to 12 drop repository path functions that had an implicit
    dependency on `the_repository`.

  - Patches 13 to 16 refactor "path.c"-internal code to stop depending
    on `the_repository`.

Changes in v2:
  - Rename variable to `wt_gitdir` for the sake of consistency.
  - Improve several commit messages.
  - Link to v1: https://lore.kernel.org/r/20250206-b4-pks-path-drop-the-repository-v1-0-4e77f0313206@pks.im

Thanks!

Patrick

---
Patrick Steinhardt (16):
      path: refactor `repo_common_path()` family of functions
      path: refactor `repo_git_path()` family of functions
      path: refactor `repo_worktree_path()` family of functions
      submodule: refactor `submodule_to_gitdir()` to accept a repo
      path: refactor `repo_submodule_path()` family of functions
      path: drop unused `strbuf_git_path()` function
      path: drop `git_pathdup()` in favor of `repo_git_path()`
      path: drop `git_path_buf()` in favor of `repo_git_path_replace()`
      worktree: return allocated string from `get_worktree_git_dir()`
      path: drop `git_common_path()` in favor of `repo_common_path()`
      rerere: let `rerere_path()` write paths into a caller-provided buffer
      path: drop `git_path()` in favor of `repo_git_path()`
      repo-settings: introduce function to clear struct
      environment: move access to "core.hooksPath" into repo settings
      environment: move access to "core.sharedRepository" into repo settings
      path: adjust last remaining users of `the_repository`

 bisect.c                    |   2 +-
 branch.c                    |   7 +-
 builtin/am.c                |   2 +-
 builtin/clone.c             |   4 +-
 builtin/commit.c            |   8 +-
 builtin/config.c            |   8 +-
 builtin/fast-import.c       |   4 +-
 builtin/fsck.c              |  10 ++-
 builtin/gc.c                |  25 ++++--
 builtin/init-db.c           |   8 +-
 builtin/log.c               |   6 +-
 builtin/notes.c             |   9 ++-
 builtin/rebase.c            |   2 +-
 builtin/receive-pack.c      |   4 +-
 builtin/remote.c            |   6 +-
 builtin/replace.c           |   2 +-
 builtin/rerere.c            |  11 ++-
 builtin/rev-parse.c         |   6 +-
 builtin/submodule--helper.c |   4 +-
 builtin/tag.c               |   2 +-
 builtin/worktree.c          |  43 +++++++---
 commit-graph.c              |   2 +-
 compat/precompose_utf8.c    |   6 +-
 config.c                    |   5 --
 copy.c                      |   4 +-
 dir.c                       |   2 +-
 editor.c                    |   6 +-
 environment.c               |  27 -------
 environment.h               |  11 ---
 hook.c                      |   3 +-
 http-backend.c              |   2 +-
 loose.c                     |   8 +-
 midx-write.c                |   2 +-
 notes-merge.c               |  26 +++---
 object-file.c               |  14 ++--
 pack-bitmap-write.c         |   2 +-
 pack-write.c                |  10 ++-
 path.c                      | 166 +++++++++++++++++++++++++++-----------
 path.h                      | 191 ++++++++++++--------------------------------
 reachable.c                 |   6 +-
 read-cache.c                |  26 ++++--
 refs.c                      |   6 +-
 refs/files-backend.c        |  10 +--
 refs/reftable-backend.c     |  10 +--
 remote.c                    |  21 ++---
 repo-settings.c             |  44 +++++++++-
 repo-settings.h             |  14 ++++
 repository.c                |   6 +-
 rerere.c                    | 101 ++++++++++++++++-------
 rerere.h                    |   3 +-
 revision.c                  |   7 +-
 server-info.c               |   2 +-
 setup.c                     |  70 ++++++++--------
 shallow.c                   |   4 +-
 submodule.c                 |  15 ++--
 submodule.h                 |   3 +-
 t/helper/test-ref-store.c   |   7 +-
 tempfile.c                  |   4 +-
 tmp-objdir.c                |  17 ++--
 worktree.c                  |  49 ++++++++----
 worktree.h                  |   2 +-
 wt-status.c                 |  42 ++++++----
 62 files changed, 639 insertions(+), 490 deletions(-)

Range-diff versus v1:

 1:  3b24ba58d4 !  1:  a1bbae7e0d path: refactor `repo_common_path()` family of functions
    @@ Commit message
           - `repo_*_path_replace()` replaces contents of the buffer with the
             computed path, again returning a pointer to the buffer contents.
     
    +    The returned constant pointer isn't being used anywhere yet, but it will
    +    be used in subsequent commits. Its intent is to allow calling patterns
    +    like the following somewhat contrived example:
    +
    +        if (!stat(&st, repo_common_path_replace(repo, &buf, ...)) &&
    +            !unlink(repo_common_path_replace(repo, &buf, ...)))
    +                ...
    +
         Refactor the commondir family of functions accordingly and adapt all
         callers.
     
    +    Note that `repo_common_pathv()` is converted into an internal
    +    implementation detail. It is only used to implement `the_repository`
    +    compatibility shims and will eventually be removed from the public
    +    interface.
    +
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## loose.c ##
 2:  8bf77754df !  2:  f7f2013b55 path: refactor `repo_git_path()` family of functions
    @@ hook.c: const char *find_hook(struct repository *r, const char *name)
      	if (!found_hook) {
     
      ## path.c ##
    -@@ path.c: static void strbuf_worktree_gitdir(struct strbuf *buf,
    - 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
    - }
    - 
    --void repo_git_pathv(const struct repository *repo,
    -+void repo_git_pathv(struct repository *repo,
    - 		    const struct worktree *wt, struct strbuf *buf,
    - 		    const char *fmt, va_list args)
    - {
    -@@ path.c: void repo_git_pathv(const struct repository *repo,
    - 	strbuf_cleanup_path(buf);
    - }
    - 
    --char *repo_git_path(const struct repository *repo,
    -+char *repo_git_path(struct repository *repo,
    - 		    const char *fmt, ...)
    - {
    - 	struct strbuf path = STRBUF_INIT;
     @@ path.c: char *repo_git_path(const struct repository *repo,
      	return strbuf_detach(&path, NULL);
      }
    @@ path.c: char *repo_git_path(const struct repository *repo,
     -void strbuf_repo_git_path(struct strbuf *sb,
     -			  const struct repository *repo,
     -			  const char *fmt, ...)
    -+const char *repo_git_path_append(struct repository *repo,
    ++const char *repo_git_path_append(const struct repository *repo,
     +				 struct strbuf *sb,
     +				 const char *fmt, ...)
      {
    @@ path.c: char *repo_git_path(const struct repository *repo,
     +	return sb->buf;
     +}
     +
    -+const char *repo_git_path_replace(struct repository *repo,
    ++const char *repo_git_path_replace(const struct repository *repo,
     +				  struct strbuf *sb,
     +				  const char *fmt, ...)
     +{
    @@ path.h: const char *repo_common_path_replace(const struct repository *repo,
     -/*
     - * Return a path into the git directory of repository `repo`.
     - */
    --char *repo_git_path(const struct repository *repo,
    -+char *repo_git_path(struct repository *repo,
    + char *repo_git_path(const struct repository *repo,
      		    const char *fmt, ...)
      	__attribute__((format (printf, 2, 3)));
     -
    @@ path.h: const char *repo_common_path_replace(const struct repository *repo,
     -void strbuf_repo_git_path(struct strbuf *sb,
     -			  const struct repository *repo,
     -			  const char *fmt, ...)
    -+const char *repo_git_path_append(struct repository *repo,
    ++const char *repo_git_path_append(const struct repository *repo,
     +				 struct strbuf *sb,
     +				 const char *fmt, ...)
     +	__attribute__((format (printf, 3, 4)));
    -+const char *repo_git_path_replace(struct repository *repo,
    ++const char *repo_git_path_replace(const struct repository *repo,
     +				  struct strbuf *sb,
     +				  const char *fmt, ...)
      	__attribute__((format (printf, 3, 4)));
    @@ path.h: struct strbuf *get_pathname(void);
      		       struct strbuf *buf,
      		       const char *fmt,
      		       va_list args);
    -+void repo_git_pathv(struct repository *repo,
    ++void repo_git_pathv(const struct repository *repo,
     +		    const struct worktree *wt, struct strbuf *buf,
     +		    const char *fmt, va_list args);
      
 3:  06b86c01fd =  3:  36eef25a6e path: refactor `repo_worktree_path()` family of functions
 4:  aced89e60e =  4:  d86ab046c3 submodule: refactor `submodule_to_gitdir()` to accept a repo
 5:  0495f12932 !  5:  d58c55e9db path: refactor `repo_submodule_path()` family of functions
    @@ Commit message
         commondir, gitdir and worktree. Refactor the "submodule" family of
         functions accordingly.
     
    +    Note that in contrast to the other `repo_*_path()` families, we have to
    +    pass in the repository as a non-constant pointer. This is because we end
    +    up calling `repo_read_gitmodules()` deep down in the callstack, which
    +    may end up modifying the repository.
    +
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/submodule--helper.c ##
 6:  ecba20e416 =  6:  438a055bdf path: drop unused `strbuf_git_path()` function
 7:  e204e037bf =  7:  8ab37db627 path: drop `git_pathdup()` in favor of `repo_git_path()`
 8:  473c8befb3 =  8:  17c4dddf2b path: drop `git_path_buf()` in favor of `repo_git_path_replace()`
 9:  d2f9c302e6 !  9:  d0d6af1ea4 worktree: return allocated string from `get_worktree_git_dir()`
    @@ Commit message
         worktree: return allocated string from `get_worktree_git_dir()`
     
         The `get_worktree_git_dir()` function returns a string constant that
    -    does not need to be free'd by the caller. For `repo_get_git_dir()` and
    -    `repo_get_common_dir()` this is because we return strings owned by
    -    `the_repository`.
    +    does not need to be free'd by the caller. This string is computed for
    +    three different cases:
     
    -    But for `git_common_path()` it's a bit less obvious though, because that
    -    function does end up allocating memory. This doesn't result in a memory
    -    leak either because we write into a buffer returned by `get_pathname()`,
    -    which returns one out of four static buffers.
    +      - If we don't have a worktree we return a path into the Git directory.
    +        The returned string is owned by `the_repository`, so there is no
    +        need for the caller to free it.
    +
    +      - If we have a worktree, but no worktree ID then the caller requests
    +        the main worktree. In this case we return a path into the common
    +        directory, which again is owned by `the_repository` and thus does
    +        not need to be free'd.
    +
    +      - In the third case, where we have an actual worktree, we compute the
    +        path relative to "$GIT_COMMON_DIR/worktrees/". This string does not
    +        need to be released either, even though `git_common_path()` ends up
    +        allocating memory. But this doesn't result in a memory leak either
    +        because we write into a buffer returned by `get_pathname()`, which
    +        returns one out of four static buffers.
     
         We're about to drop `git_common_path()` in favor of `repo_common_path()`,
         which doesn't use the same mechanism but instead returns an allocated
    @@ revision.c: void add_index_objects_to_pending(struct rev_info *revs, unsigned in
      	for (p = worktrees; *p; p++) {
      		struct worktree *wt = *p;
      		struct index_state istate = INDEX_STATE_INIT(revs->repo);
    -+		char *gitdir;
    ++		char *wt_gitdir;
      
      		if (wt->is_current)
      			continue; /* current index already taken care of */
      
    -+		gitdir = get_worktree_git_dir(wt);
    ++		wt_gitdir = get_worktree_git_dir(wt);
     +
      		if (read_index_from(&istate,
      				    worktree_git_path(the_repository, wt, "index"),
     -				    get_worktree_git_dir(wt)) > 0)
    -+				    gitdir) > 0)
    ++				    wt_gitdir) > 0)
      			do_add_index_objects_to_pending(revs, &istate, flags);
     +
      		discard_index(&istate);
    -+		free(gitdir);
    ++		free(wt_gitdir);
      	}
      	free_worktrees(worktrees);
      }
10:  1fd3722960 ! 10:  aa07ddb6f9 path: drop `git_common_path()` in favor of `repo_common_path()`
    @@ path.h: struct strbuf *get_pathname(void);
     -		       struct strbuf *buf,
     -		       const char *fmt,
     -		       va_list args);
    - void repo_git_pathv(struct repository *repo,
    + void repo_git_pathv(const struct repository *repo,
      		    const struct worktree *wt, struct strbuf *buf,
      		    const char *fmt, va_list args);
      
11:  44df197055 = 11:  e70348a141 rerere: let `rerere_path()` write paths into a caller-provided buffer
12:  d774d8ca66 ! 12:  a5a67d9a14 path: drop `git_path()` in favor of `repo_git_path()`
    @@ path.c: static void strbuf_worktree_gitdir(struct strbuf *buf,
      		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
      }
      
    --void repo_git_pathv(struct repository *repo,
    +-void repo_git_pathv(const struct repository *repo,
     -		    const struct worktree *wt, struct strbuf *buf,
     -		    const char *fmt, va_list args)
    -+static void repo_git_pathv(struct repository *repo,
    ++static void repo_git_pathv(const struct repository *repo,
     +			   const struct worktree *wt, struct strbuf *buf,
     +			   const char *fmt, va_list args)
      {
    @@ path.h: char *xdg_cache_home(const char *filename);
      #  include "repository.h"
      
     -/* Internal implementation details that should not be used. */
    --void repo_git_pathv(struct repository *repo,
    +-void repo_git_pathv(const struct repository *repo,
     -		    const struct worktree *wt, struct strbuf *buf,
     -		    const char *fmt, va_list args);
     -
13:  4981b2524b = 13:  e38ee5e724 repo-settings: introduce function to clear struct
14:  b90c3c3bee ! 14:  7f7e3522d3 environment: move access to "core.hooksPath" into repo settings
    @@ Commit message
         should be functionally the same as there aren't ever any code paths
         where we'd execute hooks outside the context of a repository.
     
    +    Note that this requires us to change the passed-in repository in the
    +    `repo_git_path()` family of functions to be non-constant, as we call
    +    `adjust_git_path()` there.
    +
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## config.c ##
    @@ path.c: static void adjust_git_path(const struct repository *repo,
      	else if (repo->different_commondir)
      		update_common_dir(buf, git_dir_len, repo->commondir);
      }
    +@@ path.c: static void strbuf_worktree_gitdir(struct strbuf *buf,
    + 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
    + }
    + 
    +-static void repo_git_pathv(const struct repository *repo,
    ++static void repo_git_pathv(struct repository *repo,
    + 			   const struct worktree *wt, struct strbuf *buf,
    + 			   const char *fmt, va_list args)
    + {
    +@@ path.c: static void repo_git_pathv(const struct repository *repo,
    + 	strbuf_cleanup_path(buf);
    + }
    + 
    +-char *repo_git_path(const struct repository *repo,
    ++char *repo_git_path(struct repository *repo,
    + 		    const char *fmt, ...)
    + {
    + 	struct strbuf path = STRBUF_INIT;
    +@@ path.c: char *repo_git_path(const struct repository *repo,
    + 	return strbuf_detach(&path, NULL);
    + }
    + 
    +-const char *repo_git_path_append(const struct repository *repo,
    ++const char *repo_git_path_append(struct repository *repo,
    + 				 struct strbuf *sb,
    + 				 const char *fmt, ...)
    + {
    +@@ path.c: const char *repo_git_path_append(const struct repository *repo,
    + 	return sb->buf;
    + }
    + 
    +-const char *repo_git_path_replace(const struct repository *repo,
    ++const char *repo_git_path_replace(struct repository *repo,
    + 				  struct strbuf *sb,
    + 				  const char *fmt, ...)
    + {
    +
    + ## path.h ##
    +@@ path.h: const char *repo_common_path_replace(const struct repository *repo,
    +  * For an exhaustive list of the adjustments made look at `common_list` and
    +  * `adjust_git_path` in path.c.
    +  */
    +-char *repo_git_path(const struct repository *repo,
    ++char *repo_git_path(struct repository *repo,
    + 		    const char *fmt, ...)
    + 	__attribute__((format (printf, 2, 3)));
    +-const char *repo_git_path_append(const struct repository *repo,
    ++const char *repo_git_path_append(struct repository *repo,
    + 				 struct strbuf *sb,
    + 				 const char *fmt, ...)
    + 	__attribute__((format (printf, 3, 4)));
    +-const char *repo_git_path_replace(const struct repository *repo,
    ++const char *repo_git_path_replace(struct repository *repo,
    + 				  struct strbuf *sb,
    + 				  const char *fmt, ...)
    + 	__attribute__((format (printf, 3, 4)));
     
      ## repo-settings.c ##
     @@ repo-settings.c: void repo_settings_clear(struct repository *r)
15:  392af38fbb = 15:  cff542e247 environment: move access to "core.sharedRepository" into repo settings
16:  f5aeb9c3e6 = 16:  6a64329ef4 path: adjust last remaining users of `the_repository`

---
base-commit: bc204b742735ae06f65bb20291c95985c9633b7f
change-id: 20250204-b4-pks-path-drop-the-repository-9ed0f9956361


^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
                     ` (16 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

The functions provided by the "path" subsystem to derive repository
paths for the commondir, gitdir, worktrees and submodules are quite
inconsistent. Some functions have a `strbuf_` prefix, others have
different return values, some don't provide a variant working on top of
`strbuf`s.

We're thus about to refactor all of these family of functions so that
they follow a common pattern:

  - `repo_*_path()` returns an allocated string.

  - `repo_*_path_append()` appends the path to the caller-provided
    buffer while returning a constant pointer to the buffer. This
    clarifies whether the buffer is being appended to or rewritten,
    which otherwise wasn't immediately obvious.

  - `repo_*_path_replace()` replaces contents of the buffer with the
    computed path, again returning a pointer to the buffer contents.

The returned constant pointer isn't being used anywhere yet, but it will
be used in subsequent commits. Its intent is to allow calling patterns
like the following somewhat contrived example:

    if (!stat(&st, repo_common_path_replace(repo, &buf, ...)) &&
        !unlink(repo_common_path_replace(repo, &buf, ...)))
            ...

Refactor the commondir family of functions accordingly and adapt all
callers.

Note that `repo_common_pathv()` is converted into an internal
implementation detail. It is only used to implement `the_repository`
compatibility shims and will eventually be removed from the public
interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 loose.c    |  6 +++---
 path.c     | 32 ++++++++++++++++++++++++++++----
 path.h     | 30 +++++++++++++++++-------------
 refs.c     |  4 ++--
 setup.c    |  4 ++--
 worktree.c |  5 ++---
 6 files changed, 54 insertions(+), 27 deletions(-)

diff --git a/loose.c b/loose.c
index 897ba389da..51ef490f93 100644
--- a/loose.c
+++ b/loose.c
@@ -75,7 +75,7 @@ static int load_one_loose_object_map(struct repository *repo, struct object_dire
 	insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
 	insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	fp = fopen(path.buf, "rb");
 	if (!fp) {
 		strbuf_release(&path);
@@ -133,7 +133,7 @@ int repo_write_loose_object_map(struct repository *repo)
 	if (!should_use_loose_object_map(repo))
 		return 0;
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
 	iter = kh_begin(map);
 	if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
@@ -174,7 +174,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
 	struct stat st;
 	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
 
-	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	repo_common_path_replace(repo, &path, "objects/loose-object-idx");
 	hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
 
 	fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
diff --git a/path.c b/path.c
index 07964f5d32..273b649e00 100644
--- a/path.c
+++ b/path.c
@@ -414,7 +414,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 	else if (!wt->id)
 		strbuf_addstr(buf, repo->commondir);
 	else
-		strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
+		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
 void repo_git_pathv(const struct repository *repo,
@@ -596,14 +596,38 @@ void repo_common_pathv(const struct repository *repo,
 	strbuf_cleanup_path(sb);
 }
 
-void strbuf_git_common_path(struct strbuf *sb,
-			    const struct repository *repo,
-			    const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+		       const char *fmt, ...)
+{
+	struct strbuf sb = STRBUF_INIT;
+	va_list args;
+	va_start(args, fmt);
+	repo_common_pathv(repo, &sb, fmt, args);
+	va_end(args);
+	return strbuf_detach(&sb, NULL);
+}
+
+const char *repo_common_path_append(const struct repository *repo,
+				    struct strbuf *sb,
+				    const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	repo_common_pathv(repo, sb, fmt, args);
+	va_end(args);
+	return sb->buf;
+}
+
+const char *repo_common_path_replace(const struct repository *repo,
+				     struct strbuf *sb,
+				     const char *fmt, ...)
 {
 	va_list args;
+	strbuf_reset(sb);
 	va_start(args, fmt);
 	repo_common_pathv(repo, sb, fmt, args);
 	va_end(args);
+	return sb->buf;
 }
 
 static struct passwd *getpw_str(const char *username, size_t len)
diff --git a/path.h b/path.h
index 5f6c85e5f8..3c75495e1a 100644
--- a/path.h
+++ b/path.h
@@ -25,22 +25,20 @@ char *mkpathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
- * The `strbuf_git_common_path` family of functions will construct a path into a
+ * The `repo_common_path` family of functions will construct a path into a
  * repository's common git directory, which is shared by all worktrees.
  */
-
-/*
- * Constructs a path into the common git directory of repository `repo` and
- * append it in the provided buffer `sb`.
- */
-void strbuf_git_common_path(struct strbuf *sb,
-			    const struct repository *repo,
-			    const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+		       const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
+const char *repo_common_path_append(const struct repository *repo,
+				    struct strbuf *sb,
+				    const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
+const char *repo_common_path_replace(const struct repository *repo,
+				     struct strbuf *sb,
+				     const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *buf,
-		       const char *fmt,
-		       va_list args);
 
 /*
  * The `repo_git_path` family of functions will construct a path into a repository's
@@ -243,6 +241,12 @@ struct strbuf *get_pathname(void);
 #  include "strbuf.h"
 #  include "repository.h"
 
+/* Internal implementation detail that should not be used. */
+void repo_common_pathv(const struct repository *repo,
+		       struct strbuf *buf,
+		       const char *fmt,
+		       va_list args);
+
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) common git directory.
diff --git a/refs.c b/refs.c
index f4094a326a..daf6a84205 100644
--- a/refs.c
+++ b/refs.c
@@ -2184,8 +2184,8 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
 
 	if (wt->id) {
 		struct strbuf common_path = STRBUF_INIT;
-		strbuf_git_common_path(&common_path, wt->repo,
-				      "worktrees/%s", wt->id);
+		repo_common_path_append(wt->repo, &common_path,
+					"worktrees/%s", wt->id);
 		refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
 				      common_path.buf, REF_STORE_ALL_CAPS);
 		strbuf_release(&common_path);
diff --git a/setup.c b/setup.c
index 8a488f3e7c..74b5ba5325 100644
--- a/setup.c
+++ b/setup.c
@@ -792,7 +792,7 @@ int upgrade_repository_format(int target_version)
 	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 	int ret;
 
-	strbuf_git_common_path(&sb, the_repository, "config");
+	repo_common_path_append(the_repository, &sb, "config");
 	read_repository_format(&repo_fmt, sb.buf);
 	strbuf_release(&sb);
 
@@ -2242,7 +2242,7 @@ void initialize_repository_version(int hash_algo,
 		struct strbuf config = STRBUF_INIT;
 		struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 
-		strbuf_git_common_path(&config, the_repository, "config");
+		repo_common_path_append(the_repository, &config, "config");
 		read_repository_format(&repo_fmt, config.buf);
 
 		if (repo_fmt.v1_only_extensions.nr)
diff --git a/worktree.c b/worktree.c
index 248bbb39d4..f8d6e7127f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -104,7 +104,7 @@ struct worktree *get_linked_worktree(const char *id,
 	if (!id)
 		die("Missing linked worktree name");
 
-	strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
+	repo_common_path_append(the_repository, &path, "worktrees/%s/gitdir", id);
 	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
 		/* invalid gitdir file */
 		goto done;
@@ -731,8 +731,7 @@ static ssize_t infer_backlink(const char *gitfile, struct strbuf *inferred)
 	id++; /* advance past '/' to point at <id> */
 	if (!*id)
 		goto error;
-	strbuf_reset(inferred);
-	strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
+	repo_common_path_replace(the_repository, inferred, "worktrees/%s", id);
 	if (!is_directory(inferred->buf))
 		goto error;
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 02/16] path: refactor `repo_git_path()` family of functions
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
                     ` (15 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "gitdir" family of
functions accordingly.

Note that the `repo_git_pathv()` function is converted into an internal
implementation detail. It is only used to implement `the_repository`
compatibility shims and will eventually be removed from the public
interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 editor.c    |  6 ++----
 hook.c      |  3 +--
 path.c      | 19 ++++++++++++++++---
 path.h      | 32 +++++++++++---------------------
 submodule.c |  4 ++--
 5 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/editor.c b/editor.c
index 6b9ce81d5f..b79d97b0e7 100644
--- a/editor.c
+++ b/editor.c
@@ -142,10 +142,8 @@ int strbuf_edit_interactively(struct repository *r,
 	struct strbuf sb = STRBUF_INIT;
 	int fd, res = 0;
 
-	if (!is_absolute_path(path)) {
-		strbuf_repo_git_path(&sb, r, "%s", path);
-		path = sb.buf;
-	}
+	if (!is_absolute_path(path))
+		path = repo_git_path_append(r, &sb, "%s", path);
 
 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
 	if (fd < 0)
diff --git a/hook.c b/hook.c
index 9ddbdee06d..b3de1048bf 100644
--- a/hook.c
+++ b/hook.c
@@ -16,8 +16,7 @@ const char *find_hook(struct repository *r, const char *name)
 
 	int found_hook;
 
-	strbuf_reset(&path);
-	strbuf_repo_git_path(&path, r, "hooks/%s", name);
+	repo_git_path_replace(r, &path, "hooks/%s", name);
 	found_hook = access(path.buf, X_OK) >= 0;
 #ifdef STRIP_EXTENSION
 	if (!found_hook) {
diff --git a/path.c b/path.c
index 273b649e00..779aa94b56 100644
--- a/path.c
+++ b/path.c
@@ -443,14 +443,27 @@ char *repo_git_path(const struct repository *repo,
 	return strbuf_detach(&path, NULL);
 }
 
-void strbuf_repo_git_path(struct strbuf *sb,
-			  const struct repository *repo,
-			  const char *fmt, ...)
+const char *repo_git_path_append(const struct repository *repo,
+				 struct strbuf *sb,
+				 const char *fmt, ...)
 {
 	va_list args;
 	va_start(args, fmt);
 	repo_git_pathv(repo, NULL, sb, fmt, args);
 	va_end(args);
+	return sb->buf;
+}
+
+const char *repo_git_path_replace(const struct repository *repo,
+				  struct strbuf *sb,
+				  const char *fmt, ...)
+{
+	va_list args;
+	strbuf_reset(sb);
+	va_start(args, fmt);
+	repo_git_pathv(repo, NULL, sb, fmt, args);
+	va_end(args);
+	return sb->buf;
 }
 
 char *mkpathdup(const char *fmt, ...)
diff --git a/path.h b/path.h
index 3c75495e1a..c45311b0a8 100644
--- a/path.h
+++ b/path.h
@@ -52,29 +52,16 @@ const char *repo_common_path_replace(const struct repository *repo,
  * For an exhaustive list of the adjustments made look at `common_list` and
  * `adjust_git_path` in path.c.
  */
-
-/*
- * Return a path into the git directory of repository `repo`.
- */
 char *repo_git_path(const struct repository *repo,
 		    const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-
-/*
- * Print a path into the git directory of repository `repo` into the provided
- * buffer.
- */
-void repo_git_pathv(const struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args);
-
-/*
- * Construct a path into the git directory of repository `repo` and append it
- * to the provided buffer `sb`.
- */
-void strbuf_repo_git_path(struct strbuf *sb,
-			  const struct repository *repo,
-			  const char *fmt, ...)
+const char *repo_git_path_append(const struct repository *repo,
+				 struct strbuf *sb,
+				 const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
+const char *repo_git_path_replace(const struct repository *repo,
+				  struct strbuf *sb,
+				  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
 /*
@@ -241,11 +228,14 @@ struct strbuf *get_pathname(void);
 #  include "strbuf.h"
 #  include "repository.h"
 
-/* Internal implementation detail that should not be used. */
+/* Internal implementation details that should not be used. */
 void repo_common_pathv(const struct repository *repo,
 		       struct strbuf *buf,
 		       const char *fmt,
 		       va_list args);
+void repo_git_pathv(const struct repository *repo,
+		    const struct worktree *wt, struct strbuf *buf,
+		    const char *fmt, va_list args);
 
 /*
  * Return a statically allocated path into the main repository's
diff --git a/submodule.c b/submodule.c
index b361076c5b..211ead54a0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1315,7 +1315,7 @@ static int repo_has_absorbed_submodules(struct repository *r)
 	int ret;
 	struct strbuf buf = STRBUF_INIT;
 
-	strbuf_repo_git_path(&buf, r, "modules/");
+	repo_git_path_append(r, &buf, "modules/");
 	ret = file_exists(buf.buf) && !is_empty_dir(buf.buf);
 	strbuf_release(&buf);
 	return ret;
@@ -2629,6 +2629,6 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
 	 * administrators can explicitly set. Nothing has been decided,
 	 * so for now, just append the name at the end of the path.
 	 */
-	strbuf_repo_git_path(buf, r, "modules/");
+	repo_git_path_append(r, buf, "modules/");
 	strbuf_addstr(buf, submodule_name);
 }

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 03/16] path: refactor `repo_worktree_path()` family of functions
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
                     ` (14 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "worktree" family of
functions accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 path.c       | 28 ++++++++++++++++++++++------
 path.h       | 20 +++++++++-----------
 repository.c |  4 ++--
 3 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/path.c b/path.c
index 779aa94b56..499116dd1e 100644
--- a/path.c
+++ b/path.c
@@ -519,28 +519,44 @@ char *repo_worktree_path(const struct repository *repo, const char *fmt, ...)
 	struct strbuf path = STRBUF_INIT;
 	va_list args;
 
+	va_start(args, fmt);
+	do_worktree_path(repo, &path, fmt, args);
+	va_end(args);
+
+	return strbuf_detach(&path, NULL);
+}
+
+const char *repo_worktree_path_append(const struct repository *repo,
+				      struct strbuf *sb,
+				      const char *fmt, ...)
+{
+	va_list args;
+
 	if (!repo->worktree)
 		return NULL;
 
 	va_start(args, fmt);
-	do_worktree_path(repo, &path, fmt, args);
+	do_worktree_path(repo, sb, fmt, args);
 	va_end(args);
 
-	return strbuf_detach(&path, NULL);
+	return sb->buf;
 }
 
-void strbuf_repo_worktree_path(struct strbuf *sb,
-			       const struct repository *repo,
-			       const char *fmt, ...)
+const char *repo_worktree_path_replace(const struct repository *repo,
+				       struct strbuf *sb,
+				       const char *fmt, ...)
 {
 	va_list args;
 
+	strbuf_reset(sb);
 	if (!repo->worktree)
-		return;
+		return NULL;
 
 	va_start(args, fmt);
 	do_worktree_path(repo, sb, fmt, args);
 	va_end(args);
+
+	return sb->buf;
 }
 
 /* Returns 0 on success, negative on failure. */
diff --git a/path.h b/path.h
index c45311b0a8..d3f85f0676 100644
--- a/path.h
+++ b/path.h
@@ -75,24 +75,22 @@ const char *worktree_git_path(struct repository *r,
 	__attribute__((format (printf, 3, 4)));
 
 /*
- * Return a path into the worktree of repository `repo`.
+ * The `repo_worktree_path` family of functions will construct a path into a
+ * repository's worktree.
  *
- * If the repository doesn't have a worktree NULL is returned.
+ * Returns a `NULL` pointer in case the repository has no worktree.
  */
 char *repo_worktree_path(const struct repository *repo,
 				const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into the worktree of repository `repo` and append it
- * to the provided buffer `sb`.
- *
- * If the repository doesn't have a worktree nothing will be appended to `sb`.
- */
-void strbuf_repo_worktree_path(struct strbuf *sb,
-				      const struct repository *repo,
+const char *repo_worktree_path_append(const struct repository *repo,
+				      struct strbuf *sb,
 				      const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
+const char *repo_worktree_path_replace(const struct repository *repo,
+				       struct strbuf *sb,
+				       const char *fmt, ...)
+	__attribute__((format (printf, 3, 4)));
 
 /*
  * Return a path into a submodule's git directory located at `path`.  `path`
diff --git a/repository.c b/repository.c
index 1a6a62bbd0..648cd88474 100644
--- a/repository.c
+++ b/repository.c
@@ -312,8 +312,8 @@ int repo_submodule_init(struct repository *subrepo,
 	struct strbuf worktree = STRBUF_INIT;
 	int ret = 0;
 
-	strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
-	strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
+	repo_worktree_path_append(superproject, &gitdir, "%s/.git", path);
+	repo_worktree_path_append(superproject, &worktree, "%s", path);
 
 	if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
 		/*

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
                     ` (13 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

The `submodule_to_gitdir()` function implicitly uses `the_repository` to
resolve submodule paths. Refactor the function to instead accept a repo
as parameter to remove the dependency on global state.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/submodule--helper.c |  2 +-
 path.c                      |  2 +-
 refs.c                      |  2 +-
 submodule.c                 | 11 ++++++-----
 submodule.h                 |  3 ++-
 5 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f9b970f8a6..3a64f7e605 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1301,7 +1301,7 @@ static void sync_submodule(const char *path, const char *prefix,
 	remote_key = xstrfmt("remote.%s.url", default_remote);
 	free(default_remote);
 
-	submodule_to_gitdir(&sb, path);
+	submodule_to_gitdir(the_repository, &sb, path);
 	strbuf_addstr(&sb, "/config");
 
 	if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
diff --git a/path.c b/path.c
index 499116dd1e..a7fa42162e 100644
--- a/path.c
+++ b/path.c
@@ -567,7 +567,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
 	struct strbuf git_submodule_dir = STRBUF_INIT;
 	int ret;
 
-	ret = submodule_to_gitdir(&git_submodule_dir, path);
+	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
 	if (ret)
 		goto cleanup;
 
diff --git a/refs.c b/refs.c
index daf6a84205..e1293e53aa 100644
--- a/refs.c
+++ b/refs.c
@@ -2146,7 +2146,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
 	if (!is_nonbare_repository_dir(&submodule_sb))
 		goto done;
 
-	if (submodule_to_gitdir(&submodule_sb, submodule))
+	if (submodule_to_gitdir(repo, &submodule_sb, submodule))
 		goto done;
 
 	subrepo = xmalloc(sizeof(*subrepo));
diff --git a/submodule.c b/submodule.c
index 211ead54a0..0530e8cf24 100644
--- a/submodule.c
+++ b/submodule.c
@@ -536,7 +536,8 @@ static struct repository *open_submodule(const char *path)
 	struct strbuf sb = STRBUF_INIT;
 	struct repository *out = xmalloc(sizeof(*out));
 
-	if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) {
+	if (submodule_to_gitdir(the_repository, &sb, path) ||
+	    repo_init(out, sb.buf, NULL)) {
 		strbuf_release(&sb);
 		free(out);
 		return NULL;
@@ -2572,7 +2573,8 @@ int get_superproject_working_tree(struct strbuf *buf)
  * Put the gitdir for a submodule (given relative to the main
  * repository worktree) into `buf`, or return -1 on error.
  */
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+int submodule_to_gitdir(struct repository *repo,
+			struct strbuf *buf, const char *submodule)
 {
 	const struct submodule *sub;
 	const char *git_dir;
@@ -2592,14 +2594,13 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
 		strbuf_addstr(buf, git_dir);
 	}
 	if (!is_git_directory(buf->buf)) {
-		sub = submodule_from_path(the_repository, null_oid(),
-					  submodule);
+		sub = submodule_from_path(repo, null_oid(), submodule);
 		if (!sub) {
 			ret = -1;
 			goto cleanup;
 		}
 		strbuf_reset(buf);
-		submodule_name_to_gitdir(buf, the_repository, sub->name);
+		submodule_name_to_gitdir(buf, repo, sub->name);
 	}
 
 cleanup:
diff --git a/submodule.h b/submodule.h
index 4deb1b5f84..db980c1d08 100644
--- a/submodule.h
+++ b/submodule.h
@@ -136,7 +136,8 @@ int push_unpushed_submodules(struct repository *r,
  * path of that submodule in 'buf'. Return -1 on error or when the
  * submodule is not initialized.
  */
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
+int submodule_to_gitdir(struct repository *repo,
+			struct strbuf *buf, const char *submodule);
 
 /*
  * Given a submodule name, create a path to where the submodule's gitdir lives

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 05/16] path: refactor `repo_submodule_path()` family of functions
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
                     ` (12 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

As explained in an earlier commit, we're refactoring path-related
functions to provide a consistent interface for computing paths into the
commondir, gitdir and worktree. Refactor the "submodule" family of
functions accordingly.

Note that in contrast to the other `repo_*_path()` families, we have to
pass in the repository as a non-constant pointer. This is because we end
up calling `repo_read_gitmodules()` deep down in the callstack, which
may end up modifying the repository.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/submodule--helper.c |  2 +-
 path.c                      | 37 +++++++++++++++++++++++++++++--------
 path.h                      | 30 ++++++++++++++++++------------
 t/helper/test-ref-store.c   |  7 +++----
 worktree.c                  |  3 ++-
 5 files changed, 53 insertions(+), 26 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3a64f7e605..c1a8029714 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1826,7 +1826,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
 
 	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
 
-	p = git_pathdup_submodule(clone_data_path, "config");
+	p = repo_submodule_path(the_repository, clone_data_path, "config");
 	if (!p)
 		die(_("could not get submodule directory for '%s'"), clone_data_path);
 
diff --git a/path.c b/path.c
index a7fa42162e..0d81e9fc32 100644
--- a/path.c
+++ b/path.c
@@ -560,14 +560,15 @@ const char *repo_worktree_path_replace(const struct repository *repo,
 }
 
 /* Returns 0 on success, negative on failure. */
-static int do_submodule_path(struct strbuf *buf, const char *path,
+static int do_submodule_path(struct repository *repo,
+			     struct strbuf *buf, const char *path,
 			     const char *fmt, va_list args)
 {
 	struct strbuf git_submodule_common_dir = STRBUF_INIT;
 	struct strbuf git_submodule_dir = STRBUF_INIT;
 	int ret;
 
-	ret = submodule_to_gitdir(the_repository, &git_submodule_dir, path);
+	ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
 	if (ret)
 		goto cleanup;
 
@@ -586,13 +587,14 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
 	return ret;
 }
 
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+			  const char *path, const char *fmt, ...)
 {
 	int err;
 	va_list args;
 	struct strbuf buf = STRBUF_INIT;
 	va_start(args, fmt);
-	err = do_submodule_path(&buf, path, fmt, args);
+	err = do_submodule_path(repo, &buf, path, fmt, args);
 	va_end(args);
 	if (err) {
 		strbuf_release(&buf);
@@ -601,16 +603,35 @@ char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 	return strbuf_detach(&buf, NULL);
 }
 
-int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
-			      const char *fmt, ...)
+const char *repo_submodule_path_append(struct repository *repo,
+				       struct strbuf *buf,
+				       const char *path,
+				       const char *fmt, ...)
 {
 	int err;
 	va_list args;
 	va_start(args, fmt);
-	err = do_submodule_path(buf, path, fmt, args);
+	err = do_submodule_path(repo, buf, path, fmt, args);
 	va_end(args);
+	if (err)
+		return NULL;
+	return buf->buf;
+}
 
-	return err;
+const char *repo_submodule_path_replace(struct repository *repo,
+					struct strbuf *buf,
+					const char *path,
+					const char *fmt, ...)
+{
+	int err;
+	va_list args;
+	strbuf_reset(buf);
+	va_start(args, fmt);
+	err = do_submodule_path(repo, buf, path, fmt, args);
+	va_end(args);
+	if (err)
+		return NULL;
+	return buf->buf;
 }
 
 void repo_common_pathv(const struct repository *repo,
diff --git a/path.h b/path.h
index d3f85f0676..4fe523626c 100644
--- a/path.h
+++ b/path.h
@@ -93,20 +93,26 @@ const char *repo_worktree_path_replace(const struct repository *repo,
 	__attribute__((format (printf, 3, 4)));
 
 /*
- * Return a path into a submodule's git directory located at `path`.  `path`
- * must only reference a submodule of the main repository (the_repository).
- */
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
-	__attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into a submodule's git directory located at `path` and
- * append it to the provided buffer `sb`.  `path` must only reference a
- * submodule of the main repository (the_repository).
+ * The `repo_submodule_path` family of functions will construct a path into a
+ * submodule's git directory located at `path`. `path` must be a submodule path
+ * as found in the index and must be part of the given repository.
+ *
+ * Returns a `NULL` pointer in case the submodule cannot be found.
  */
-int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
-				     const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+			  const char *path,
+			  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
+const char *repo_submodule_path_append(struct repository *repo,
+				       struct strbuf *sb,
+				       const char *path,
+				       const char *fmt, ...)
+	__attribute__((format (printf, 4, 5)));
+const char *repo_submodule_path_replace(struct repository *repo,
+					struct strbuf *sb,
+					const char *path,
+					const char *fmt, ...)
+	__attribute__((format (printf, 4, 5)));
 
 void report_linked_checkout_garbage(struct repository *r);
 
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 1cc05f043a..e00fce592b 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -75,11 +75,10 @@ static const char **get_store(const char **argv, struct ref_store **refs)
 		*refs = get_main_ref_store(the_repository);
 	} else if (skip_prefix(argv[0], "submodule:", &gitdir)) {
 		struct strbuf sb = STRBUF_INIT;
-		int ret;
 
-		ret = strbuf_git_path_submodule(&sb, gitdir, "objects/");
-		if (ret)
-			die("strbuf_git_path_submodule failed: %d", ret);
+		if (!repo_submodule_path_append(the_repository,
+						&sb, gitdir, "objects/"))
+			die("computing submodule path failed");
 		add_to_alternates_memory(sb.buf);
 		strbuf_release(&sb);
 
diff --git a/worktree.c b/worktree.c
index f8d6e7127f..8f4fc10c44 100644
--- a/worktree.c
+++ b/worktree.c
@@ -487,7 +487,8 @@ int submodule_uses_worktrees(const char *path)
 	int ret = 0;
 	struct repository_format format = REPOSITORY_FORMAT_INIT;
 
-	submodule_gitdir = git_pathdup_submodule(path, "%s", "");
+	submodule_gitdir = repo_submodule_path(the_repository,
+					       path, "%s", "");
 	if (!submodule_gitdir)
 		return 0;
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 06/16] path: drop unused `strbuf_git_path()` function
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
                     ` (11 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

The `strbuf_git_path()` function isn't used anywhere, and neither should
it grow any callers because it depends on `the_repository`. Remove it.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 path.h | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/path.h b/path.h
index 4fe523626c..8798db7469 100644
--- a/path.h
+++ b/path.h
@@ -272,19 +272,6 @@ static inline char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	return buf->buf;
 }
 
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and append it to the provided buffer `sb`.
- */
-__attribute__((format (printf, 2, 3)))
-static inline void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
-{
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, sb, fmt, args);
-	va_end(args);
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
                     ` (10 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Remove `git_pathdup()` in favor of `repo_git_path()`. The latter does
essentially the same, with the only exception that it does not rely on
`the_repository` but takes the repo as separate parameter.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 bisect.c              |  2 +-
 builtin/am.c          |  2 +-
 builtin/clone.c       |  2 +-
 builtin/config.c      |  8 ++++----
 builtin/fast-import.c |  4 ++--
 builtin/fsck.c        |  2 +-
 builtin/gc.c          |  4 ++--
 builtin/notes.c       |  2 +-
 builtin/replace.c     |  2 +-
 builtin/tag.c         |  2 +-
 builtin/worktree.c    |  4 ++--
 dir.c                 |  2 +-
 http-backend.c        |  2 +-
 notes-merge.c         |  2 +-
 object-file.c         |  2 +-
 path.h                | 16 +---------------
 16 files changed, 22 insertions(+), 36 deletions(-)

diff --git a/bisect.c b/bisect.c
index 7a3c77c6d8..269a98bf97 100644
--- a/bisect.c
+++ b/bisect.c
@@ -930,7 +930,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
 	if (!current_bad_oid)
 		return error(_("a %s revision is needed"), term_bad);
 
-	filename = git_pathdup("BISECT_ANCESTORS_OK");
+	filename = repo_git_path(the_repository, "BISECT_ANCESTORS_OK");
 
 	/* Check if file BISECT_ANCESTORS_OK exists. */
 	if (!stat(filename, &st) && S_ISREG(st.st_mode))
diff --git a/builtin/am.c b/builtin/am.c
index 390b463144..2921bb89ef 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -158,7 +158,7 @@ static void am_state_init(struct am_state *state)
 
 	memset(state, 0, sizeof(*state));
 
-	state->dir = git_pathdup("rebase-apply");
+	state->dir = repo_git_path(the_repository, "rebase-apply");
 
 	state->prec = 4;
 
diff --git a/builtin/clone.c b/builtin/clone.c
index fd001d800c..5ae6ee9db9 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -938,7 +938,7 @@ static void write_refspec_config(const char *src_ref_prefix,
 
 static void dissociate_from_references(void)
 {
-	char *alternates = git_pathdup("objects/info/alternates");
+	char *alternates = repo_git_path(the_repository, "objects/info/alternates");
 
 	if (!access(alternates, F_OK)) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
diff --git a/builtin/config.c b/builtin/config.c
index 16e6e30555..53a90094e3 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -775,13 +775,13 @@ static void location_options_init(struct config_location_options *opts,
 		opts->source.file = opts->file_to_free = git_system_config();
 		opts->source.scope = CONFIG_SCOPE_SYSTEM;
 	} else if (opts->use_local_config) {
-		opts->source.file = opts->file_to_free = git_pathdup("config");
+		opts->source.file = opts->file_to_free = repo_git_path(the_repository, "config");
 		opts->source.scope = CONFIG_SCOPE_LOCAL;
 	} else if (opts->use_worktree_config) {
 		struct worktree **worktrees = get_worktrees();
 		if (the_repository->repository_format_worktree_config)
 			opts->source.file = opts->file_to_free =
-				git_pathdup("config.worktree");
+				repo_git_path(the_repository, "config.worktree");
 		else if (worktrees[0] && worktrees[1])
 			die(_("--worktree cannot be used with multiple "
 			      "working trees unless the config\n"
@@ -790,7 +790,7 @@ static void location_options_init(struct config_location_options *opts,
 			      "section in \"git help worktree\" for details"));
 		else
 			opts->source.file = opts->file_to_free =
-				git_pathdup("config");
+				repo_git_path(the_repository, "config");
 		opts->source.scope = CONFIG_SCOPE_LOCAL;
 		free_worktrees(worktrees);
 	} else if (opts->source.file) {
@@ -1087,7 +1087,7 @@ static int show_editor(struct config_location_options *opts)
 	git_config(git_default_config, NULL);
 	config_file = opts->source.file ?
 			xstrdup(opts->source.file) :
-			git_pathdup("config");
+			repo_git_path(the_repository, "config");
 	if (opts->use_global_config) {
 		int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
 		if (fd >= 0) {
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index a6a84058cd..c6f5147e8b 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -328,7 +328,7 @@ static void write_branch_report(FILE *rpt, struct branch *b)
 
 static void write_crash_report(const char *err)
 {
-	char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+	char *loc = repo_git_path(the_repository, "fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
 	FILE *rpt = fopen(loc, "w");
 	struct branch *b;
 	unsigned long lu;
@@ -3280,7 +3280,7 @@ static char* make_fast_import_path(const char *path)
 {
 	if (!relative_marks_paths || is_absolute_path(path))
 		return prefix_filename(global_prefix, path);
-	return git_pathdup("info/fast-import/%s", path);
+	return repo_git_path(the_repository, "info/fast-import/%s", path);
 }
 
 static void option_import_marks(const char *marks,
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 7a4dcb0716..c12203e012 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -326,7 +326,7 @@ static void check_unreachable_object(struct object *obj)
 				  printable_type(&obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
-			char *filename = git_pathdup("lost-found/%s/%s",
+			char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				describe_object(&obj->oid));
 			FILE *f;
diff --git a/builtin/gc.c b/builtin/gc.c
index 0bf3533494..57f6aee174 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -546,7 +546,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 	if (xgethostname(my_host, sizeof(my_host)))
 		xsnprintf(my_host, sizeof(my_host), "unknown");
 
-	pidfile_path = git_pathdup("gc.pid");
+	pidfile_path = repo_git_path(the_repository, "gc.pid");
 	fd = hold_lock_file_for_update(&lock, pidfile_path,
 				       LOCK_DIE_ON_ERROR);
 	if (!force) {
@@ -607,7 +607,7 @@ static int report_last_gc_error(void)
 	int ret = 0;
 	ssize_t len;
 	struct stat st;
-	char *gc_log_path = git_pathdup("gc.log");
+	char *gc_log_path = repo_git_path(the_repository, "gc.log");
 
 	if (stat(gc_log_path, &st)) {
 		if (errno == ENOENT)
diff --git a/builtin/notes.c b/builtin/notes.c
index d051abf6df..18bcbb2f91 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -197,7 +197,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
 		struct strbuf buf = STRBUF_INIT;
 
 		/* write the template message before editing: */
-		d->edit_path = git_pathdup("NOTES_EDITMSG");
+		d->edit_path = repo_git_path(the_repository, "NOTES_EDITMSG");
 		fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
 		if (d->msg_nr)
diff --git a/builtin/replace.c b/builtin/replace.c
index a4eaadff91..15ec0922ce 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -345,7 +345,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
 	}
 	strbuf_release(&ref);
 
-	tmpfile = git_pathdup("REPLACE_EDITOBJ");
+	tmpfile = repo_git_path(the_repository, "REPLACE_EDITOBJ");
 	if (export_object(&old_oid, type, raw, tmpfile)) {
 		free(tmpfile);
 		return -1;
diff --git a/builtin/tag.c b/builtin/tag.c
index e8a344b926..d3e0943b73 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -667,7 +667,7 @@ int cmd_tag(int argc,
 	if (create_tag_object) {
 		if (force_sign_annotate && !annotate)
 			opt.sign = 1;
-		path = git_pathdup("TAG_EDITMSG");
+		path = repo_git_path(the_repository, "TAG_EDITMSG");
 		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
 			   &trailer_args, path);
 	}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index c043d4d523..c84e6aa2cb 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -337,7 +337,7 @@ static void check_candidate_path(const char *path,
 
 static void copy_sparse_checkout(const char *worktree_git_dir)
 {
-	char *from_file = git_pathdup("info/sparse-checkout");
+	char *from_file = repo_git_path(the_repository, "info/sparse-checkout");
 	char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
 
 	if (file_exists(from_file)) {
@@ -353,7 +353,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)
 
 static void copy_filtered_worktree_config(const char *worktree_git_dir)
 {
-	char *from_file = git_pathdup("config.worktree");
+	char *from_file = repo_git_path(the_repository, "config.worktree");
 	char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
 
 	if (file_exists(from_file)) {
diff --git a/dir.c b/dir.c
index 5b2181e589..4122f6513d 100644
--- a/dir.c
+++ b/dir.c
@@ -3455,7 +3455,7 @@ void setup_standard_excludes(struct dir_struct *dir)
 
 char *get_sparse_checkout_filename(void)
 {
-	return git_pathdup("info/sparse-checkout");
+	return repo_git_path(the_repository, "info/sparse-checkout");
 }
 
 int get_sparse_checkout_patterns(struct pattern_list *pl)
diff --git a/http-backend.c b/http-backend.c
index 33cf378282..50b2858fad 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -183,7 +183,7 @@ static void send_strbuf(struct strbuf *hdr,
 static void send_local_file(struct strbuf *hdr, const char *the_type,
 				const char *name)
 {
-	char *p = git_pathdup("%s", name);
+	char *p = repo_git_path(the_repository, "%s", name);
 	size_t buf_alloc = 8192;
 	char *buf = xmalloc(buf_alloc);
 	int fd;
diff --git a/notes-merge.c b/notes-merge.c
index 8d701ed428..c997c0c1e3 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -309,7 +309,7 @@ static void write_buf_to_worktree(const struct object_id *obj,
 				  const char *buf, unsigned long size)
 {
 	int fd;
-	char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
+	char *path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
 	if (safe_create_leading_directories_const(path))
 		die_errno("unable to create directory for '%s'", path);
 
diff --git a/object-file.c b/object-file.c
index 6ce1caacae..335cc2a5da 100644
--- a/object-file.c
+++ b/object-file.c
@@ -717,7 +717,7 @@ static void read_info_alternates(struct repository *r,
 void add_to_alternates_file(const char *reference)
 {
 	struct lock_file lock = LOCK_INIT;
-	char *alts = git_pathdup("objects/info/alternates");
+	char *alts = repo_git_path(the_repository, "objects/info/alternates");
 	FILE *in, *out;
 	int found = 0;
 
diff --git a/path.h b/path.h
index 8798db7469..65a8f21c4c 100644
--- a/path.h
+++ b/path.h
@@ -292,24 +292,10 @@ static inline const char *git_path(const char *fmt, ...)
 	{ \
 		static char *ret; \
 		if (!ret) \
-			ret = git_pathdup(filename); \
+			ret = repo_git_path(the_repository, filename); \
 		return ret; \
 	}
 
-/*
- * Return a path into the main repository's (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline char *git_pathdup(const char *fmt, ...)
-{
-	struct strbuf path = STRBUF_INIT;
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, &path, fmt, args);
-	va_end(args);
-	return strbuf_detach(&path, NULL);
-}
-
 # endif /* USE_THE_REPOSITORY_VARIABLE */
 
 #endif /* PATH_H */

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
                     ` (9 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Remove `git_path_buf()` in favor of `repo_git_path_replace()`. The
latter does essentially the same, with the only exception that it does
not rely on `the_repository` but takes the repo as separate parameter.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/worktree.c       |  2 +-
 compat/precompose_utf8.c |  6 +++---
 notes-merge.c            |  4 ++--
 object-file.c            |  4 ++--
 path.h                   | 16 ----------------
 setup.c                  | 33 ++++++++++++++++-----------------
 6 files changed, 24 insertions(+), 41 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index c84e6aa2cb..7959b10d26 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -457,7 +457,7 @@ static int add_worktree(const char *path, const char *refname,
 		BUG("How come '%s' becomes empty after sanitization?", sb.buf);
 	strbuf_reset(&sb);
 	name = sb_name.buf;
-	git_path_buf(&sb_repo, "worktrees/%s", name);
+	repo_git_path_replace(the_repository, &sb_repo, "worktrees/%s", name);
 	len = sb_repo.len;
 	if (safe_create_leading_directories_const(sb_repo.buf))
 		die_errno(_("could not create leading directories of '%s'"),
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index f7cc7b3be5..12e38e0ea3 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -50,15 +50,15 @@ void probe_utf8_pathname_composition(void)
 	int output_fd;
 	if (precomposed_unicode != -1)
 		return; /* We found it defined in the global config, respect it */
-	git_path_buf(&path, "%s", auml_nfc);
+	repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
 	output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);
 	if (output_fd >= 0) {
 		close(output_fd);
-		git_path_buf(&path, "%s", auml_nfd);
+		repo_git_path_replace(the_repository, &path, "%s", auml_nfd);
 		precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
 		git_config_set("core.precomposeunicode",
 			       precomposed_unicode ? "true" : "false");
-		git_path_buf(&path, "%s", auml_nfc);
+		repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
 		if (unlink(path.buf))
 			die_errno(_("failed to unlink '%s'"), path.buf);
 	}
diff --git a/notes-merge.c b/notes-merge.c
index c997c0c1e3..8c22a171c1 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -695,7 +695,7 @@ int notes_merge_commit(struct notes_merge_options *o,
 	const char *msg = strstr(buffer, "\n\n");
 	int baselen;
 
-	git_path_buf(&path, NOTES_MERGE_WORKTREE);
+	repo_git_path_replace(the_repository, &path, NOTES_MERGE_WORKTREE);
 	if (o->verbosity >= 3)
 		printf("Committing notes in notes merge worktree at %s\n",
 			path.buf);
@@ -757,7 +757,7 @@ int notes_merge_abort(struct notes_merge_options *o)
 	struct strbuf buf = STRBUF_INIT;
 	int ret;
 
-	git_path_buf(&buf, NOTES_MERGE_WORKTREE);
+	repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE);
 	if (o->verbosity >= 3)
 		printf("Removing notes merge worktree at %s/*\n", buf.buf);
 	ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
diff --git a/object-file.c b/object-file.c
index 335cc2a5da..dc9fcaf3e9 100644
--- a/object-file.c
+++ b/object-file.c
@@ -476,14 +476,14 @@ int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
 	 * restrictive except to remove write permission.
 	 */
 	int mode = 0444;
-	git_path_buf(temp_filename, "objects/%s", pattern);
+	repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
 	fd = git_mkstemp_mode(temp_filename->buf, mode);
 	if (0 <= fd)
 		return fd;
 
 	/* slow path */
 	/* some mkstemp implementations erase temp_filename on failure */
-	git_path_buf(temp_filename, "objects/%s", pattern);
+	repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
 	safe_create_leading_directories(temp_filename->buf);
 	return xmkstemp_mode(temp_filename->buf, mode);
 }
diff --git a/path.h b/path.h
index 65a8f21c4c..cdc26acb74 100644
--- a/path.h
+++ b/path.h
@@ -256,22 +256,6 @@ static inline const char *git_common_path(const char *fmt, ...)
 	return pathname->buf;
 }
 
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and place it in the provided buffer `buf`, the contents of the buffer will
- * be overridden.
- */
-__attribute__((format (printf, 2, 3)))
-static inline char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
-{
-	va_list args;
-	strbuf_reset(buf);
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, buf, fmt, args);
-	va_end(args);
-	return buf->buf;
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
diff --git a/setup.c b/setup.c
index 74b5ba5325..30889386f7 100644
--- a/setup.c
+++ b/setup.c
@@ -2264,7 +2264,7 @@ static int is_reinit(void)
 	char junk[2];
 	int ret;
 
-	git_path_buf(&buf, "HEAD");
+	repo_git_path_replace(the_repository, &buf, "HEAD");
 	ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
 	strbuf_release(&buf);
 	return ret;
@@ -2316,8 +2316,7 @@ static int create_default_files(const char *template_path,
 				int init_shared_repository)
 {
 	struct stat st1;
-	struct strbuf buf = STRBUF_INIT;
-	char *path;
+	struct strbuf path = STRBUF_INIT;
 	int reinit;
 	int filemode;
 	const char *work_tree = repo_get_work_tree(the_repository);
@@ -2358,14 +2357,14 @@ static int create_default_files(const char *template_path,
 	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
 
 	/* Check filemode trustability */
-	path = git_path_buf(&buf, "config");
+	repo_git_path_replace(the_repository, &path, "config");
 	filemode = TEST_FILEMODE;
-	if (TEST_FILEMODE && !lstat(path, &st1)) {
+	if (TEST_FILEMODE && !lstat(path.buf, &st1)) {
 		struct stat st2;
-		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
-				!lstat(path, &st2) &&
+		filemode = (!chmod(path.buf, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path.buf, &st2) &&
 				st1.st_mode != st2.st_mode &&
-				!chmod(path, st1.st_mode));
+				!chmod(path.buf, st1.st_mode));
 		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
 			filemode = 0;
 	}
@@ -2384,24 +2383,24 @@ static int create_default_files(const char *template_path,
 
 	if (!reinit) {
 		/* Check if symlink is supported in the work tree */
-		path = git_path_buf(&buf, "tXXXXXX");
-		if (!close(xmkstemp(path)) &&
-		    !unlink(path) &&
-		    !symlink("testing", path) &&
-		    !lstat(path, &st1) &&
+		repo_git_path_replace(the_repository, &path, "tXXXXXX");
+		if (!close(xmkstemp(path.buf)) &&
+		    !unlink(path.buf) &&
+		    !symlink("testing", path.buf) &&
+		    !lstat(path.buf, &st1) &&
 		    S_ISLNK(st1.st_mode))
-			unlink(path); /* good */
+			unlink(path.buf); /* good */
 		else
 			git_config_set("core.symlinks", "false");
 
 		/* Check if the filesystem is case-insensitive */
-		path = git_path_buf(&buf, "CoNfIg");
-		if (!access(path, F_OK))
+		repo_git_path_replace(the_repository, &path, "CoNfIg");
+		if (!access(path.buf, F_OK))
 			git_config_set("core.ignorecase", "true");
 		probe_utf8_pathname_composition();
 	}
 
-	strbuf_release(&buf);
+	strbuf_release(&path);
 	return reinit;
 }
 

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 09/16] worktree: return allocated string from `get_worktree_git_dir()`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
                     ` (8 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

The `get_worktree_git_dir()` function returns a string constant that
does not need to be free'd by the caller. This string is computed for
three different cases:

  - If we don't have a worktree we return a path into the Git directory.
    The returned string is owned by `the_repository`, so there is no
    need for the caller to free it.

  - If we have a worktree, but no worktree ID then the caller requests
    the main worktree. In this case we return a path into the common
    directory, which again is owned by `the_repository` and thus does
    not need to be free'd.

  - In the third case, where we have an actual worktree, we compute the
    path relative to "$GIT_COMMON_DIR/worktrees/". This string does not
    need to be released either, even though `git_common_path()` ends up
    allocating memory. But this doesn't result in a memory leak either
    because we write into a buffer returned by `get_pathname()`, which
    returns one out of four static buffers.

We're about to drop `git_common_path()` in favor of `repo_common_path()`,
which doesn't use the same mechanism but instead returns an allocated
string owned by the caller. While we could adapt `get_worktree_git_dir()`
to also use `get_pathname()` and print the derived common path into that
buffer, the whole schema feels a lot like premature optimization in this
context. There are some callsites where we call `get_worktree_git_dir()`
in a loop that iterates through all worktrees. But none of these loops
seem to be even remotely in the hot path, so saving a single allocation
there does not feel worth it.

Refactor the function to instead consistently return an allocated path
so that we can start using `repo_common_path()` in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 branch.c               |  7 +++++--
 builtin/fsck.c         |  8 ++++++--
 builtin/receive-pack.c |  4 +++-
 builtin/worktree.c     | 10 ++++++++--
 reachable.c            |  6 +++++-
 revision.c             |  7 ++++++-
 worktree.c             | 11 ++++++-----
 worktree.h             |  2 +-
 8 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/branch.c b/branch.c
index 77716966fe..91297d55ac 100644
--- a/branch.c
+++ b/branch.c
@@ -397,7 +397,7 @@ static void prepare_checked_out_branches(void)
 	worktrees = get_worktrees();
 
 	while (worktrees[i]) {
-		char *old;
+		char *old, *wt_gitdir;
 		struct wt_status_state state = { 0 };
 		struct worktree *wt = worktrees[i++];
 		struct string_list update_refs = STRING_LIST_INIT_DUP;
@@ -437,7 +437,8 @@ static void prepare_checked_out_branches(void)
 		}
 		wt_status_state_free_buffers(&state);
 
-		if (!sequencer_get_update_refs_state(get_worktree_git_dir(wt),
+		wt_gitdir = get_worktree_git_dir(wt);
+		if (!sequencer_get_update_refs_state(wt_gitdir,
 						     &update_refs)) {
 			struct string_list_item *item;
 			for_each_string_list_item(item, &update_refs) {
@@ -448,6 +449,8 @@ static void prepare_checked_out_branches(void)
 			}
 			string_list_clear(&update_refs, 1);
 		}
+
+		free(wt_gitdir);
 	}
 
 	free_worktrees(worktrees);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index c12203e012..eea1d43647 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1057,7 +1057,7 @@ int cmd_fsck(int argc,
 			struct worktree *wt = *p;
 			struct index_state istate =
 				INDEX_STATE_INIT(the_repository);
-			char *path;
+			char *path, *wt_gitdir;
 
 			/*
 			 * Make a copy since the buffer is reusable
@@ -1065,9 +1065,13 @@ int cmd_fsck(int argc,
 			 * while we're examining the index.
 			 */
 			path = xstrdup(worktree_git_path(the_repository, wt, "index"));
-			read_index_from(&istate, path, get_worktree_git_dir(wt));
+			wt_gitdir = get_worktree_git_dir(wt);
+
+			read_index_from(&istate, path, wt_gitdir);
 			fsck_index(&istate, path, wt->is_current);
+
 			discard_index(&istate);
+			free(wt_gitdir);
 			free(path);
 		}
 		free_worktrees(worktrees);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b7ea774609..d65a441f32 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1435,7 +1435,8 @@ static const char *push_to_checkout(unsigned char *hash,
 
 static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
 {
-	const char *retval, *git_dir;
+	const char *retval;
+	char *git_dir;
 	struct strvec env = STRVEC_INIT;
 	int invoked_hook;
 
@@ -1453,6 +1454,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 		retval = push_to_deploy(sha1, &env, worktree->path);
 
 	strvec_clear(&env);
+	free(git_dir);
 	return retval;
 }
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7959b10d26..2cea9441a6 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -657,8 +657,9 @@ static int can_use_local_refs(const struct add_opts *opts)
 		if (!opts->quiet) {
 			struct strbuf path = STRBUF_INIT;
 			struct strbuf contents = STRBUF_INIT;
+			char *wt_gitdir = get_worktree_git_dir(NULL);
 
-			strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+			strbuf_add_real_path(&path, wt_gitdir);
 			strbuf_addstr(&path, "/HEAD");
 			strbuf_read_file(&contents, path.buf, 64);
 			strbuf_stripspace(&contents, NULL);
@@ -670,6 +671,7 @@ static int can_use_local_refs(const struct add_opts *opts)
 				  path.buf, contents.buf);
 			strbuf_release(&path);
 			strbuf_release(&contents);
+			free(wt_gitdir);
 		}
 		return 1;
 	}
@@ -1157,6 +1159,9 @@ static void validate_no_submodules(const struct worktree *wt)
 	struct index_state istate = INDEX_STATE_INIT(the_repository);
 	struct strbuf path = STRBUF_INIT;
 	int i, found_submodules = 0;
+	char *wt_gitdir;
+
+	wt_gitdir = get_worktree_git_dir(wt);
 
 	if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
 		/*
@@ -1166,7 +1171,7 @@ static void validate_no_submodules(const struct worktree *wt)
 		 */
 		found_submodules = 1;
 	} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
-				   get_worktree_git_dir(wt)) > 0) {
+				   wt_gitdir) > 0) {
 		for (i = 0; i < istate.cache_nr; i++) {
 			struct cache_entry *ce = istate.cache[i];
 			int err;
@@ -1185,6 +1190,7 @@ static void validate_no_submodules(const struct worktree *wt)
 	}
 	discard_index(&istate);
 	strbuf_release(&path);
+	free(wt_gitdir);
 
 	if (found_submodules)
 		die(_("working trees containing submodules cannot be moved or removed"));
diff --git a/reachable.c b/reachable.c
index ecf7ccf504..9ee04c89ec 100644
--- a/reachable.c
+++ b/reachable.c
@@ -65,8 +65,10 @@ static void add_rebase_files(struct rev_info *revs)
 	struct worktree **worktrees = get_worktrees();
 
 	for (struct worktree **wt = worktrees; *wt; wt++) {
+		char *wt_gitdir = get_worktree_git_dir(*wt);
+
 		strbuf_reset(&buf);
-		strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+		strbuf_addstr(&buf, wt_gitdir);
 		strbuf_complete(&buf, '/');
 		len = buf.len;
 		for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
@@ -74,6 +76,8 @@ static void add_rebase_files(struct rev_info *revs)
 			strbuf_addstr(&buf, path[i]);
 			add_one_file(buf.buf, revs);
 		}
+
+		free(wt_gitdir);
 	}
 	strbuf_release(&buf);
 	free_worktrees(worktrees);
diff --git a/revision.c b/revision.c
index 474fa1e767..c4390f0938 100644
--- a/revision.c
+++ b/revision.c
@@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
 	for (p = worktrees; *p; p++) {
 		struct worktree *wt = *p;
 		struct index_state istate = INDEX_STATE_INIT(revs->repo);
+		char *wt_gitdir;
 
 		if (wt->is_current)
 			continue; /* current index already taken care of */
 
+		wt_gitdir = get_worktree_git_dir(wt);
+
 		if (read_index_from(&istate,
 				    worktree_git_path(the_repository, wt, "index"),
-				    get_worktree_git_dir(wt)) > 0)
+				    wt_gitdir) > 0)
 			do_add_index_objects_to_pending(revs, &istate, flags);
+
 		discard_index(&istate);
+		free(wt_gitdir);
 	}
 	free_worktrees(worktrees);
 }
diff --git a/worktree.c b/worktree.c
index 8f4fc10c44..3b94535963 100644
--- a/worktree.c
+++ b/worktree.c
@@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
 static int is_current_worktree(struct worktree *wt)
 {
 	char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
-	const char *wt_git_dir = get_worktree_git_dir(wt);
+	char *wt_git_dir = get_worktree_git_dir(wt);
 	int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+	free(wt_git_dir);
 	free(git_dir);
 	return is_current;
 }
@@ -175,14 +176,14 @@ struct worktree **get_worktrees(void)
 	return get_worktrees_internal(0);
 }
 
-const char *get_worktree_git_dir(const struct worktree *wt)
+char *get_worktree_git_dir(const struct worktree *wt)
 {
 	if (!wt)
-		return repo_get_git_dir(the_repository);
+		return xstrdup(repo_get_git_dir(the_repository));
 	else if (!wt->id)
-		return repo_get_common_dir(the_repository);
+		return xstrdup(repo_get_common_dir(the_repository));
 	else
-		return git_common_path("worktrees/%s", wt->id);
+		return xstrdup(git_common_path("worktrees/%s", wt->id));
 }
 
 static struct worktree *find_worktree_by_suffix(struct worktree **list,
diff --git a/worktree.h b/worktree.h
index 38145df80f..16368588a0 100644
--- a/worktree.h
+++ b/worktree.h
@@ -39,7 +39,7 @@ int submodule_uses_worktrees(const char *path);
  * Return git dir of the worktree. Note that the path may be relative.
  * If wt is NULL, git dir of current worktree is returned.
  */
-const char *get_worktree_git_dir(const struct worktree *wt);
+char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
  * Search for the worktree identified unambiguously by `arg` -- typically

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 10/16] path: drop `git_common_path()` in favor of `repo_common_path()`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
                     ` (7 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Remove `git_common_path()` in favor of the `repo_common_path()` family
of functions, which makes the implicit dependency on `the_repository` go
away.

Note that `git_common_path()` used to return a string allocated via
`get_pathname()`, which uses a rotating set of statically allocated
buffers. Consequently, callers didn't have to free the returned string.
The same isn't true for `repo_common_path()`, so we also have to add
logic to free the returned strings.

This refactoring also allows us to remove `repo_common_pathv()` from the
public interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/worktree.c | 16 ++++++++++++----
 path.c             |  8 ++++----
 path.h             | 19 -------------------
 worktree.c         | 32 ++++++++++++++++++++++++--------
 4 files changed, 40 insertions(+), 35 deletions(-)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 2cea9441a6..761e302a36 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -151,7 +151,7 @@ static int delete_git_dir(const char *id)
 	struct strbuf sb = STRBUF_INIT;
 	int ret;
 
-	strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
+	repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
 	ret = remove_dir_recursively(&sb, 0);
 	if (ret < 0 && errno == ENOTDIR)
 		ret = unlink(sb.buf);
@@ -1102,6 +1102,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
 		OPT_END()
 	};
 	struct worktree **worktrees, *wt;
+	char *path;
 
 	ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
 	if (ac != 1)
@@ -1122,9 +1123,11 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
 		die(_("'%s' is already locked"), av[0]);
 	}
 
-	write_file(git_common_path("worktrees/%s/locked", wt->id),
-		   "%s", reason);
+	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+	write_file(path, "%s", reason);
+
 	free_worktrees(worktrees);
+	free(path);
 	return 0;
 }
 
@@ -1135,6 +1138,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
 		OPT_END()
 	};
 	struct worktree **worktrees, *wt;
+	char *path;
 	int ret;
 
 	ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
@@ -1149,8 +1153,12 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
 		die(_("The main working tree cannot be locked or unlocked"));
 	if (!worktree_lock_reason(wt))
 		die(_("'%s' is not locked"), av[0]);
-	ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+
+	path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+	ret = unlink_or_warn(path);
+
 	free_worktrees(worktrees);
+	free(path);
 	return ret;
 }
 
diff --git a/path.c b/path.c
index 0d81e9fc32..2d07ba723d 100644
--- a/path.c
+++ b/path.c
@@ -634,10 +634,10 @@ const char *repo_submodule_path_replace(struct repository *repo,
 	return buf->buf;
 }
 
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *sb,
-		       const char *fmt,
-		       va_list args)
+static void repo_common_pathv(const struct repository *repo,
+			      struct strbuf *sb,
+			      const char *fmt,
+			      va_list args)
 {
 	strbuf_addstr(sb, repo->commondir);
 	if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
diff --git a/path.h b/path.h
index cdc26acb74..bed0a4c6f9 100644
--- a/path.h
+++ b/path.h
@@ -233,29 +233,10 @@ struct strbuf *get_pathname(void);
 #  include "repository.h"
 
 /* Internal implementation details that should not be used. */
-void repo_common_pathv(const struct repository *repo,
-		       struct strbuf *buf,
-		       const char *fmt,
-		       va_list args);
 void repo_git_pathv(const struct repository *repo,
 		    const struct worktree *wt, struct strbuf *buf,
 		    const char *fmt, va_list args);
 
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) common git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_common_path(const char *fmt, ...)
-{
-	struct strbuf *pathname = get_pathname();
-	va_list args;
-	va_start(args, fmt);
-	repo_common_pathv(the_repository, pathname, fmt, args);
-	va_end(args);
-	return pathname->buf;
-}
-
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
diff --git a/worktree.c b/worktree.c
index 3b94535963..d5d07d7a84 100644
--- a/worktree.c
+++ b/worktree.c
@@ -183,7 +183,7 @@ char *get_worktree_git_dir(const struct worktree *wt)
 	else if (!wt->id)
 		return xstrdup(repo_get_common_dir(the_repository));
 	else
-		return xstrdup(git_common_path("worktrees/%s", wt->id));
+		return repo_common_path(the_repository, "worktrees/%s", wt->id);
 }
 
 static struct worktree *find_worktree_by_suffix(struct worktree **list,
@@ -314,6 +314,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 {
 	struct strbuf wt_path = STRBUF_INIT;
 	struct strbuf realpath = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
 	char *path = NULL;
 	int err, ret = -1;
 
@@ -343,7 +344,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 	if (!is_absolute_path(wt->path)) {
 		strbuf_addf_gently(errmsg,
 				   _("'%s' file does not contain absolute path to the working tree location"),
-				   git_common_path("worktrees/%s/gitdir", wt->id));
+				   repo_common_path_replace(the_repository, &buf, "worktrees/%s/gitdir", wt->id));
 		goto done;
 	}
 
@@ -365,14 +366,16 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 		goto done;
 	}
 
-	strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
+	strbuf_realpath(&realpath, repo_common_path_replace(the_repository, &buf, "worktrees/%s", wt->id), 1);
 	ret = fspathcmp(path, realpath.buf);
 
 	if (ret)
 		strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
-				   wt->path, git_common_path("worktrees/%s", wt->id));
+				   wt->path, repo_common_path_replace(the_repository, &buf,
+								      "worktrees/%s", wt->id));
 done:
 	free(path);
+	strbuf_release(&buf);
 	strbuf_release(&wt_path);
 	strbuf_release(&realpath);
 	return ret;
@@ -384,11 +387,13 @@ void update_worktree_location(struct worktree *wt, const char *path_,
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf dotgit = STRBUF_INIT;
 	struct strbuf gitdir = STRBUF_INIT;
+	char *wt_gitdir;
 
 	if (is_main_worktree(wt))
 		BUG("can't relocate main worktree");
 
-	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+	wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+	strbuf_realpath(&gitdir, wt_gitdir, 1);
 	strbuf_realpath(&path, path_, 1);
 	strbuf_addf(&dotgit, "%s/.git", path.buf);
 	if (fspathcmp(wt->path, path.buf)) {
@@ -400,6 +405,7 @@ void update_worktree_location(struct worktree *wt, const char *path_,
 	strbuf_release(&path);
 	strbuf_release(&dotgit);
 	strbuf_release(&gitdir);
+	free(wt_gitdir);
 }
 
 int is_worktree_being_rebased(const struct worktree *wt,
@@ -585,6 +591,7 @@ static void repair_gitfile(struct worktree *wt,
 	struct strbuf backlink = STRBUF_INIT;
 	char *dotgit_contents = NULL;
 	const char *repair = NULL;
+	char *path = NULL;
 	int err;
 
 	/* missing worktree can't be repaired */
@@ -596,7 +603,8 @@ static void repair_gitfile(struct worktree *wt,
 		goto done;
 	}
 
-	strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+	path = repo_common_path(the_repository, "worktrees/%s", wt->id);
+	strbuf_realpath(&repo, path, 1);
 	strbuf_addf(&dotgit, "%s/.git", wt->path);
 	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
 	dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
@@ -626,6 +634,7 @@ static void repair_gitfile(struct worktree *wt,
 
 done:
 	free(dotgit_contents);
+	free(path);
 	strbuf_release(&repo);
 	strbuf_release(&dotgit);
 	strbuf_release(&gitdir);
@@ -657,11 +666,13 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
 	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf dotgit = STRBUF_INIT;
 	int is_relative_path;
+	char *path = NULL;
 
 	if (is_main_worktree(wt))
 		goto done;
 
-	strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+	path = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+	strbuf_realpath(&gitdir, path, 1);
 
 	if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
 		goto done;
@@ -680,6 +691,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
 done:
 	strbuf_release(&gitdir);
 	strbuf_release(&dotgit);
+	free(path);
 }
 
 void repair_worktrees_after_gitdir_move(const char *old_path)
@@ -871,7 +883,11 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
 	ssize_t read_result;
 
 	*wtpath = NULL;
-	strbuf_realpath(&repo, git_common_path("worktrees/%s", id), 1);
+
+	path = repo_common_path(the_repository, "worktrees/%s", id);
+	strbuf_realpath(&repo, path, 1);
+	FREE_AND_NULL(path);
+
 	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
 	if (!is_directory(repo.buf)) {
 		strbuf_addstr(reason, _("not a valid directory"));

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-22  7:20     ` Jeff King
  2025-02-07 11:03   ` [PATCH v2 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
                     ` (6 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Same as with `get_worktree_git_dir()` a couple of commits ago, the
`rerere_path()` function returns paths that need not be free'd by the
caller because `git_path()` internally uses `get_pathname()`.

Refactor the function to instead accept a caller-provided buffer that
the path will be written into, passing on ownership to the caller. This
refactoring prepares us for the removal of `git_path()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rerere.c | 11 ++++---
 rerere.c         | 87 ++++++++++++++++++++++++++++++++++++++------------------
 rerere.h         |  3 +-
 3 files changed, 69 insertions(+), 32 deletions(-)

diff --git a/builtin/rerere.c b/builtin/rerere.c
index 41127e24e5..1312e79d89 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -4,9 +4,9 @@
 #include "config.h"
 #include "gettext.h"
 #include "parse-options.h"
-
-#include "string-list.h"
 #include "rerere.h"
+#include "strbuf.h"
+#include "string-list.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 #include "pathspec.h"
@@ -112,15 +112,18 @@ int cmd_rerere(int argc,
 				merge_rr.items[i].util = NULL;
 		}
 	} else if (!strcmp(argv[0], "diff")) {
+		struct strbuf buf = STRBUF_INIT;
 		if (setup_rerere(the_repository, &merge_rr,
 				 flags | RERERE_READONLY) < 0)
 			return 0;
 		for (size_t i = 0; i < merge_rr.nr; i++) {
 			const char *path = merge_rr.items[i].string;
 			const struct rerere_id *id = merge_rr.items[i].util;
-			if (diff_two(rerere_path(id, "preimage"), path, path, path))
-				die(_("unable to generate diff for '%s'"), rerere_path(id, NULL));
+			if (diff_two(rerere_path(&buf, id, "preimage"), path, path, path))
+				die(_("unable to generate diff for '%s'"), rerere_path(&buf, id, NULL));
 		}
+
+		strbuf_release(&buf);
 	} else
 		usage_with_options(rerere_usage, options);
 
diff --git a/rerere.c b/rerere.c
index e7fa6426b3..763cb715a6 100644
--- a/rerere.c
+++ b/rerere.c
@@ -91,16 +91,18 @@ static void assign_variant(struct rerere_id *id)
 	id->variant = variant;
 }
 
-const char *rerere_path(const struct rerere_id *id, const char *file)
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *id, const char *file)
 {
 	if (!file)
-		return git_path("rr-cache/%s", rerere_id_hex(id));
+		return repo_git_path_replace(the_repository, buf, "rr-cache/%s",
+					     rerere_id_hex(id));
 
 	if (id->variant <= 0)
-		return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+		return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s",
+					     rerere_id_hex(id), file);
 
-	return git_path("rr-cache/%s/%s.%d",
-			rerere_id_hex(id), file, id->variant);
+	return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s.%d",
+				     rerere_id_hex(id), file, id->variant);
 }
 
 static int is_rr_file(const char *name, const char *filename, int *variant)
@@ -624,9 +626,10 @@ static int try_merge(struct index_state *istate,
 {
 	enum ll_merge_result ret;
 	mmfile_t base = {NULL, 0}, other = {NULL, 0};
+	struct strbuf buf = STRBUF_INIT;
 
-	if (read_mmfile(&base, rerere_path(id, "preimage")) ||
-	    read_mmfile(&other, rerere_path(id, "postimage"))) {
+	if (read_mmfile(&base, rerere_path(&buf, id, "preimage")) ||
+	    read_mmfile(&other, rerere_path(&buf, id, "postimage"))) {
 		ret = LL_MERGE_CONFLICT;
 	} else {
 		/*
@@ -637,6 +640,7 @@ static int try_merge(struct index_state *istate,
 			       istate, NULL);
 	}
 
+	strbuf_release(&buf);
 	free(base.ptr);
 	free(other.ptr);
 
@@ -657,6 +661,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 {
 	FILE *f;
 	int ret;
+	struct strbuf buf = STRBUF_INIT;
 	mmfile_t cur = {NULL, 0};
 	mmbuffer_t result = {NULL, 0};
 
@@ -664,8 +669,8 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 	 * Normalize the conflicts in path and write it out to
 	 * "thisimage" temporary file.
 	 */
-	if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
-	    read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+	if ((handle_file(istate, path, NULL, rerere_path(&buf, id, "thisimage")) < 0) ||
+	    read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
 		ret = 1;
 		goto out;
 	}
@@ -678,9 +683,9 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 	 * A successful replay of recorded resolution.
 	 * Mark that "postimage" was used to help gc.
 	 */
-	if (utime(rerere_path(id, "postimage"), NULL) < 0)
+	if (utime(rerere_path(&buf, id, "postimage"), NULL) < 0)
 		warning_errno(_("failed utime() on '%s'"),
-			      rerere_path(id, "postimage"));
+			      rerere_path(&buf, id, "postimage"));
 
 	/* Update "path" with the resolution */
 	f = fopen(path, "w");
@@ -694,6 +699,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
 out:
 	free(cur.ptr);
 	free(result.ptr);
+	strbuf_release(&buf);
 
 	return ret;
 }
@@ -720,9 +726,11 @@ static void update_paths(struct repository *r, struct string_list *update)
 
 static void remove_variant(struct rerere_id *id)
 {
-	unlink_or_warn(rerere_path(id, "postimage"));
-	unlink_or_warn(rerere_path(id, "preimage"));
+	struct strbuf buf = STRBUF_INIT;
+	unlink_or_warn(rerere_path(&buf, id, "postimage"));
+	unlink_or_warn(rerere_path(&buf, id, "preimage"));
 	id->collection->status[id->variant] = 0;
+	strbuf_release(&buf);
 }
 
 /*
@@ -739,6 +747,7 @@ static void do_rerere_one_path(struct index_state *istate,
 	const char *path = rr_item->string;
 	struct rerere_id *id = rr_item->util;
 	struct rerere_dir *rr_dir = id->collection;
+	struct strbuf buf = STRBUF_INIT;
 	int variant;
 
 	variant = id->variant;
@@ -746,12 +755,12 @@ static void do_rerere_one_path(struct index_state *istate,
 	/* Has the user resolved it already? */
 	if (variant >= 0) {
 		if (!handle_file(istate, path, NULL, NULL)) {
-			copy_file(rerere_path(id, "postimage"), path, 0666);
+			copy_file(rerere_path(&buf, id, "postimage"), path, 0666);
 			id->collection->status[variant] |= RR_HAS_POSTIMAGE;
 			fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
 			free_rerere_id(rr_item);
 			rr_item->util = NULL;
-			return;
+			goto out;
 		}
 		/*
 		 * There may be other variants that can cleanly
@@ -787,22 +796,25 @@ static void do_rerere_one_path(struct index_state *istate,
 				   path);
 		free_rerere_id(rr_item);
 		rr_item->util = NULL;
-		return;
+		goto out;
 	}
 
 	/* None of the existing one applies; we need a new variant */
 	assign_variant(id);
 
 	variant = id->variant;
-	handle_file(istate, path, NULL, rerere_path(id, "preimage"));
+	handle_file(istate, path, NULL, rerere_path(&buf, id, "preimage"));
 	if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
-		const char *path = rerere_path(id, "postimage");
+		const char *path = rerere_path(&buf, id, "postimage");
 		if (unlink(path))
 			die_errno(_("cannot unlink stray '%s'"), path);
 		id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
 	}
 	id->collection->status[variant] |= RR_HAS_PREIMAGE;
 	fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
+
+out:
+	strbuf_release(&buf);
 }
 
 static int do_plain_rerere(struct repository *r,
@@ -810,6 +822,7 @@ static int do_plain_rerere(struct repository *r,
 {
 	struct string_list conflict = STRING_LIST_INIT_DUP;
 	struct string_list update = STRING_LIST_INIT_DUP;
+	struct strbuf buf = STRBUF_INIT;
 	int i;
 
 	find_conflict(r, &conflict);
@@ -843,7 +856,7 @@ static int do_plain_rerere(struct repository *r,
 		string_list_insert(rr, path)->util = id;
 
 		/* Ensure that the directory exists. */
-		mkdir_in_gitdir(rerere_path(id, NULL));
+		mkdir_in_gitdir(rerere_path(&buf, id, NULL));
 	}
 
 	for (i = 0; i < rr->nr; i++)
@@ -854,6 +867,7 @@ static int do_plain_rerere(struct repository *r,
 
 	string_list_clear(&conflict, 0);
 	string_list_clear(&update, 0);
+	strbuf_release(&buf);
 	return write_rr(rr, fd);
 }
 
@@ -1033,6 +1047,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 	struct rerere_id *id;
 	unsigned char hash[GIT_MAX_RAWSZ];
 	int ret;
+	struct strbuf buf = STRBUF_INIT;
 	struct string_list_item *item;
 
 	/*
@@ -1056,8 +1071,8 @@ static int rerere_forget_one_path(struct index_state *istate,
 		if (!has_rerere_resolution(id))
 			continue;
 
-		handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
-		if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+		handle_cache(istate, path, hash, rerere_path(&buf, id, "thisimage"));
+		if (read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
 			free(cur.ptr);
 			error(_("failed to update conflicted state in '%s'"), path);
 			goto fail_exit;
@@ -1074,7 +1089,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 		goto fail_exit;
 	}
 
-	filename = rerere_path(id, "postimage");
+	filename = rerere_path(&buf, id, "postimage");
 	if (unlink(filename)) {
 		if (errno == ENOENT)
 			error(_("no remembered resolution for '%s'"), path);
@@ -1088,7 +1103,7 @@ static int rerere_forget_one_path(struct index_state *istate,
 	 * conflict in the working tree, run us again to record
 	 * the postimage.
 	 */
-	handle_cache(istate, path, hash, rerere_path(id, "preimage"));
+	handle_cache(istate, path, hash, rerere_path(&buf, id, "preimage"));
 	fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
 
 	/*
@@ -1099,9 +1114,11 @@ static int rerere_forget_one_path(struct index_state *istate,
 	free_rerere_id(item);
 	item->util = id;
 	fprintf(stderr, _("Forgot resolution for '%s'\n"), path);
+	strbuf_release(&buf);
 	return 0;
 
 fail_exit:
+	strbuf_release(&buf);
 	free(id);
 	return -1;
 }
@@ -1147,16 +1164,26 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
 
 static timestamp_t rerere_created_at(struct rerere_id *id)
 {
+	struct strbuf buf = STRBUF_INIT;
 	struct stat st;
+	int ret;
 
-	return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+	ret = stat(rerere_path(&buf, id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+
+	strbuf_release(&buf);
+	return ret;
 }
 
 static timestamp_t rerere_last_used_at(struct rerere_id *id)
 {
+	struct strbuf buf = STRBUF_INIT;
 	struct stat st;
+	int ret;
+
+	ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
 
-	return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+	strbuf_release(&buf);
+	return ret;
 }
 
 /*
@@ -1164,9 +1191,11 @@ static timestamp_t rerere_last_used_at(struct rerere_id *id)
  */
 static void unlink_rr_item(struct rerere_id *id)
 {
-	unlink_or_warn(rerere_path(id, "thisimage"));
+	struct strbuf buf = STRBUF_INIT;
+	unlink_or_warn(rerere_path(&buf, id, "thisimage"));
 	remove_variant(id);
 	id->collection->status[id->variant] = 0;
+	strbuf_release(&buf);
 }
 
 static void prune_one(struct rerere_id *id,
@@ -1264,10 +1293,14 @@ void rerere_clear(struct repository *r, struct string_list *merge_rr)
 
 	for (i = 0; i < merge_rr->nr; i++) {
 		struct rerere_id *id = merge_rr->items[i].util;
+		struct strbuf buf = STRBUF_INIT;
+
 		if (!has_rerere_resolution(id)) {
 			unlink_rr_item(id);
-			rmdir(rerere_path(id, NULL));
+			rmdir(rerere_path(&buf, id, NULL));
 		}
+
+		strbuf_release(&buf);
 	}
 	unlink_or_warn(git_path_merge_rr(r));
 	rollback_lock_file(&write_lock);
diff --git a/rerere.h b/rerere.h
index 5d6cb63879..d4b5f7c932 100644
--- a/rerere.h
+++ b/rerere.h
@@ -32,7 +32,8 @@ int repo_rerere(struct repository *, int);
  * path to that filesystem entity.  With "file" specified with NULL,
  * return the path to the directory that houses these files.
  */
-const char *rerere_path(const struct rerere_id *, const char *file);
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
+			const char *file);
 int rerere_forget(struct repository *, struct pathspec *);
 int rerere_remaining(struct repository *, struct string_list *);
 void rerere_clear(struct repository *, struct string_list *);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 12/16] path: drop `git_path()` in favor of `repo_git_path()`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
                     ` (5 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Remove `git_path()` in favor of the `repo_git_path()` family of
functions, which makes the implicit dependency on `the_repository` go
away.

Note that `git_path()` returned a string allocated via `get_pathname()`,
which uses a rotating set of statically allocated buffers. Consequently,
callers didn't have to free the returned string. The same isn't true for
`repo_common_path()`, so we also have to add logic to free the returned
strings.

This refactoring also allows us to remove `repo_common_pathv()` as well
as `get_pathname()` from the public interface.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/commit.c    |  8 +++++---
 builtin/gc.c        | 21 +++++++++++++++------
 builtin/notes.c     |  7 ++++++-
 builtin/rebase.c    |  2 +-
 builtin/remote.c    |  6 ++++--
 builtin/rev-parse.c |  6 +++---
 builtin/worktree.c  | 11 +++++++++--
 notes-merge.c       | 20 ++++++++++++--------
 path.c              |  8 ++++----
 path.h              | 27 ---------------------------
 read-cache.c        | 24 +++++++++++++++++-------
 remote.c            | 21 ++++++++++++---------
 rerere.c            | 14 +++++++++++---
 shallow.c           |  4 +++-
 wt-status.c         | 42 ++++++++++++++++++++++++++----------------
 15 files changed, 128 insertions(+), 93 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 9fb405dd4a..2f45968222 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -352,6 +352,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	struct pathspec pathspec;
 	int refresh_flags = REFRESH_QUIET;
 	const char *ret;
+	char *path = NULL;
 
 	if (is_status)
 		refresh_flags |= REFRESH_UNMERGED;
@@ -524,9 +525,9 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	if (write_locked_index(the_repository->index, &index_lock, 0))
 		die(_("unable to write new index file"));
 
-	hold_lock_file_for_update(&false_lock,
-				  git_path("next-index-%"PRIuMAX,
-					   (uintmax_t) getpid()),
+	path = repo_git_path(the_repository, "next-index-%"PRIuMAX,
+			     (uintmax_t) getpid());
+	hold_lock_file_for_update(&false_lock, path,
 				  LOCK_DIE_ON_ERROR);
 
 	create_base_index(current_head);
@@ -542,6 +543,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 out:
 	string_list_clear(&partial, 0);
 	clear_pathspec(&pathspec);
+	free(path);
 	return ret;
 }
 
diff --git a/builtin/gc.c b/builtin/gc.c
index 57f6aee174..5923d9a05b 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -99,9 +99,11 @@ static void process_log_file(void)
 		/* There was some error recorded in the lock file */
 		commit_lock_file(&log_lock);
 	} else {
+		char *path = repo_git_path(the_repository, "gc.log");
 		/* No error, clean up any old gc.log */
-		unlink(git_path("gc.log"));
+		unlink(path);
 		rollback_lock_file(&log_lock);
+		free(path);
 	}
 }
 
@@ -299,8 +301,11 @@ static int too_many_loose_objects(struct gc_config *cfg)
 	int num_loose = 0;
 	int needed = 0;
 	const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
+	char *path;
 
-	dir = opendir(git_path("objects/17"));
+	path = repo_git_path(the_repository, "objects/17");
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return 0;
 
@@ -821,11 +826,12 @@ struct repository *repo UNUSED)
 	}
 
 	if (daemonized) {
-		hold_lock_file_for_update(&log_lock,
-					  git_path("gc.log"),
+		char *path = repo_git_path(the_repository, "gc.log");
+		hold_lock_file_for_update(&log_lock, path,
 					  LOCK_DIE_ON_ERROR);
 		dup2(get_lock_file_fd(&log_lock), 2);
 		atexit(process_log_file_at_exit);
+		free(path);
 	}
 
 	gc_before_repack(&opts, &cfg);
@@ -887,8 +893,11 @@ struct repository *repo UNUSED)
 		warning(_("There are too many unreachable loose objects; "
 			"run 'git prune' to remove them."));
 
-	if (!daemonized)
-		unlink(git_path("gc.log"));
+	if (!daemonized) {
+		char *path = repo_git_path(the_repository, "gc.log");
+		unlink(path);
+		free(path);
+	}
 
 out:
 	gc_config_release(&cfg);
diff --git a/builtin/notes.c b/builtin/notes.c
index 18bcbb2f91..ff61ec5f2d 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -979,6 +979,8 @@ static int merge(int argc, const char **argv, const char *prefix,
 	else { /* Merge has unresolved conflicts */
 		struct worktree **worktrees;
 		const struct worktree *wt;
+		char *path;
+
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
 				"NOTES_MERGE_PARTIAL", &result_oid, NULL,
@@ -994,10 +996,13 @@ static int merge(int argc, const char **argv, const char *prefix,
 		if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", notes_ref, NULL))
 			die(_("failed to store link to current notes ref (%s)"),
 			    notes_ref);
+
+		path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE);
 		fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
 				  "and commit the result with 'git notes merge --commit', "
 				  "or abort the merge with 'git notes merge --abort'.\n"),
-			git_path(NOTES_MERGE_WORKTREE));
+			path);
+		free(path);
 	}
 
 	free_notes(t);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6c9eaf3788..d4715ed35d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -644,7 +644,7 @@ static int run_am(struct rebase_options *opts)
 		return run_command(&am);
 	}
 
-	rebased_patches = xstrdup(git_path("rebased-patches"));
+	rebased_patches = repo_git_path(the_repository, "rebased-patches");
 	format_patch.out = open(rebased_patches,
 				O_WRONLY | O_CREAT | O_TRUNC, 0666);
 	if (format_patch.out < 0) {
diff --git a/builtin/remote.c b/builtin/remote.c
index 71d84fb3cf..0489fcc8f3 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -644,9 +644,11 @@ static int migrate_file(struct remote *remote)
 		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
 #ifndef WITH_BREAKING_CHANGES
 	if (remote->origin == REMOTE_REMOTES)
-		unlink_or_warn(git_path("remotes/%s", remote->name));
+		unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+						     "remotes/%s", remote->name));
 	else if (remote->origin == REMOTE_BRANCHES)
-		unlink_or_warn(git_path("branches/%s", remote->name));
+		unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+						     "branches/%s", remote->name));
 #endif /* WITH_BREAKING_CHANGES */
 	strbuf_release(&buf);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 428c866c05..490da33bec 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -789,8 +789,8 @@ int cmd_rev_parse(int argc,
 			if (!strcmp(arg, "--git-path")) {
 				if (!argv[i + 1])
 					die(_("--git-path requires an argument"));
-				strbuf_reset(&buf);
-				print_path(git_path("%s", argv[i + 1]), prefix,
+				print_path(repo_git_path_replace(the_repository, &buf,
+								 "%s", argv[i + 1]), prefix,
 						format,
 						DEFAULT_RELATIVE_IF_SHARED);
 				i++;
@@ -1083,7 +1083,7 @@ int cmd_rev_parse(int argc,
 					die(_("Could not read the index"));
 				if (the_repository->index->split_index) {
 					const struct object_id *oid = &the_repository->index->split_index->base_oid;
-					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
+					const char *path = repo_git_path_replace(the_repository, &buf, "sharedindex.%s", oid_to_hex(oid));
 					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
 				continue;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 761e302a36..48448a8355 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -163,7 +163,9 @@ static int delete_git_dir(const char *id)
 
 static void delete_worktrees_dir_if_empty(void)
 {
-	rmdir(git_path("worktrees")); /* ignore failed removal */
+	char *path = repo_git_path(the_repository, "worktrees");
+	rmdir(path); /* ignore failed removal */
+	free(path);
 }
 
 static void prune_worktree(const char *id, const char *reason)
@@ -212,8 +214,13 @@ static void prune_worktrees(void)
 	struct strbuf reason = STRBUF_INIT;
 	struct strbuf main_path = STRBUF_INIT;
 	struct string_list kept = STRING_LIST_INIT_DUP;
-	DIR *dir = opendir(git_path("worktrees"));
+	char *path;
+	DIR *dir;
 	struct dirent *d;
+
+	path = repo_git_path(the_repository, "worktrees");
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return;
 	while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
diff --git a/notes-merge.c b/notes-merge.c
index 8c22a171c1..67a472020d 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -275,34 +275,38 @@ static void diff_tree_local(struct notes_merge_options *o,
 
 static void check_notes_merge_worktree(struct notes_merge_options *o)
 {
+	struct strbuf buf = STRBUF_INIT;
+
 	if (!o->has_worktree) {
 		/*
 		 * Must establish NOTES_MERGE_WORKTREE.
 		 * Abort if NOTES_MERGE_WORKTREE already exists
 		 */
-		if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
-		    !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
+		if (file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)) &&
+		    !is_empty_dir(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE))) {
 			if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
 				die(_("You have not concluded your previous "
 				    "notes merge (%s exists).\nPlease, use "
 				    "'git notes merge --commit' or 'git notes "
 				    "merge --abort' to commit/abort the "
 				    "previous merge before you start a new "
-				    "notes merge."), git_path("NOTES_MERGE_*"));
+				    "notes merge."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
 			else
 				die(_("You have not concluded your notes merge "
-				    "(%s exists)."), git_path("NOTES_MERGE_*"));
+				    "(%s exists)."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
 		}
 
-		if (safe_create_leading_directories_const(git_path(
+		if (safe_create_leading_directories_const(repo_git_path_replace(the_repository, &buf,
 				NOTES_MERGE_WORKTREE "/.test")))
 			die_errno("unable to create directory %s",
-				  git_path(NOTES_MERGE_WORKTREE));
+				  repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
 		o->has_worktree = 1;
-	} else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
+	} else if (!file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)))
 		/* NOTES_MERGE_WORKTREE should already be established */
 		die("missing '%s'. This should not happen",
-		    git_path(NOTES_MERGE_WORKTREE));
+		    repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
+
+	strbuf_release(&buf);
 }
 
 static void write_buf_to_worktree(const struct object_id *obj,
diff --git a/path.c b/path.c
index 2d07ba723d..a42f72800d 100644
--- a/path.c
+++ b/path.c
@@ -30,7 +30,7 @@ static int get_st_mode_bits(const char *path, int *mode)
 	return 0;
 }
 
-struct strbuf *get_pathname(void)
+static struct strbuf *get_pathname(void)
 {
 	static struct strbuf pathname_array[4] = {
 		STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -417,9 +417,9 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
-void repo_git_pathv(const struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args)
+static void repo_git_pathv(const struct repository *repo,
+			   const struct worktree *wt, struct strbuf *buf,
+			   const char *fmt, va_list args)
 {
 	int gitdir_len;
 	strbuf_worktree_gitdir(buf, repo, wt);
diff --git a/path.h b/path.h
index bed0a4c6f9..f28d5a7ca9 100644
--- a/path.h
+++ b/path.h
@@ -221,37 +221,10 @@ char *xdg_cache_home(const char *filename);
  */
 void safe_create_dir(const char *dir, int share);
 
-/*
- * Do not use this function. It is only exported to other subsystems until we
- * can get rid of the below block of functions that implicitly rely on
- * `the_repository`.
- */
-struct strbuf *get_pathname(void);
-
 # ifdef USE_THE_REPOSITORY_VARIABLE
 #  include "strbuf.h"
 #  include "repository.h"
 
-/* Internal implementation details that should not be used. */
-void repo_git_pathv(const struct repository *repo,
-		    const struct worktree *wt, struct strbuf *buf,
-		    const char *fmt, va_list args);
-
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_path(const char *fmt, ...)
-{
-	struct strbuf *pathname = get_pathname();
-	va_list args;
-	va_start(args, fmt);
-	repo_git_pathv(the_repository, NULL, pathname, fmt, args);
-	va_end(args);
-	return pathname->buf;
-}
-
 #define GIT_PATH_FUNC(func, filename) \
 	const char *func(void) \
 	{ \
diff --git a/read-cache.c b/read-cache.c
index d54be2c172..66ad0015a7 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3251,15 +3251,18 @@ static int clean_shared_index_files(const char *current_hex)
 
 	while ((de = readdir(dir)) != NULL) {
 		const char *sha1_hex;
-		const char *shared_index_path;
+		char *shared_index_path;
 		if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex))
 			continue;
 		if (!strcmp(sha1_hex, current_hex))
 			continue;
-		shared_index_path = git_path("%s", de->d_name);
+
+		shared_index_path = repo_git_path(the_repository, "%s", de->d_name);
 		if (should_delete_shared_index(shared_index_path) > 0 &&
 		    unlink(shared_index_path))
 			warning_errno(_("unable to unlink: %s"), shared_index_path);
+
+		free(shared_index_path);
 	}
 	closedir(dir);
 
@@ -3271,6 +3274,7 @@ static int write_shared_index(struct index_state *istate,
 {
 	struct split_index *si = istate->split_index;
 	int ret, was_full = !istate->sparse_index;
+	char *path;
 
 	move_cache_to_base_index(istate);
 	convert_to_sparse(istate, 0);
@@ -3291,13 +3295,15 @@ static int write_shared_index(struct index_state *istate,
 		error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
 		return ret;
 	}
-	ret = rename_tempfile(temp,
-			      git_path("sharedindex.%s", oid_to_hex(&si->base->oid)));
+
+	path = repo_git_path(the_repository, "sharedindex.%s", oid_to_hex(&si->base->oid));
+	ret = rename_tempfile(temp, path);
 	if (!ret) {
 		oidcpy(&si->base_oid, &si->base->oid);
 		clean_shared_index_files(oid_to_hex(&si->base->oid));
 	}
 
+	free(path);
 	return ret;
 }
 
@@ -3378,9 +3384,12 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
 	if (new_shared_index) {
 		struct tempfile *temp;
 		int saved_errno;
+		char *path;
 
 		/* Same initial permissions as the main .git/index file */
-		temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
+		path = repo_git_path(the_repository, "sharedindex_XXXXXX");
+		temp = mks_tempfile_sm(path, 0, 0666);
+		free(path);
 		if (!temp) {
 			ret = do_write_locked_index(istate, lock, flags,
 						    ~WRITE_SPLIT_INDEX_EXTENSION);
@@ -3401,9 +3410,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
 
 	/* Freshen the shared index only if the split-index was written */
 	if (!ret && !new_shared_index && !is_null_oid(&si->base_oid)) {
-		const char *shared_index = git_path("sharedindex.%s",
-						    oid_to_hex(&si->base_oid));
+		char *shared_index = repo_git_path(the_repository, "sharedindex.%s",
+						   oid_to_hex(&si->base_oid));
 		freshen_shared_index(shared_index, 1);
+		free(shared_index);
 	}
 
 out:
diff --git a/remote.c b/remote.c
index 1779f0e7bb..81d151a507 100644
--- a/remote.c
+++ b/remote.c
@@ -321,10 +321,11 @@ static void read_remotes_file(struct remote_state *remote_state,
 			      struct remote *remote)
 {
 	struct strbuf buf = STRBUF_INIT;
-	FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r");
+	FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						     "remotes/%s", remote->name), "r");
 
 	if (!f)
-		return;
+		goto out;
 
 	warn_about_deprecated_remote_type("remotes", remote);
 
@@ -343,8 +344,10 @@ static void read_remotes_file(struct remote_state *remote_state,
 		else if (skip_prefix(buf.buf, "Pull:", &v))
 			refspec_append(&remote->fetch, skip_spaces(v));
 	}
-	strbuf_release(&buf);
 	fclose(f);
+
+out:
+	strbuf_release(&buf);
 }
 
 static void read_branches_file(struct remote_state *remote_state,
@@ -352,20 +355,19 @@ static void read_branches_file(struct remote_state *remote_state,
 {
 	char *frag, *to_free = NULL;
 	struct strbuf buf = STRBUF_INIT;
-	FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
+	FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						     "branches/%s", remote->name), "r");
 
 	if (!f)
-		return;
+		goto out;
 
 	warn_about_deprecated_remote_type("branches", remote);
 
 	strbuf_getline_lf(&buf, f);
 	fclose(f);
 	strbuf_trim(&buf);
-	if (!buf.len) {
-		strbuf_release(&buf);
-		return;
-	}
+	if (!buf.len)
+		goto out;
 
 	remote->configured_in_repo = 1;
 	remote->origin = REMOTE_BRANCHES;
@@ -393,6 +395,7 @@ static void read_branches_file(struct remote_state *remote_state,
 	refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
 	remote->fetch_tags = 1; /* always auto-follow */
 
+out:
 	strbuf_release(&buf);
 	free(to_free);
 }
diff --git a/rerere.c b/rerere.c
index 763cb715a6..2239c2d775 100644
--- a/rerere.c
+++ b/rerere.c
@@ -127,8 +127,12 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
 static void scan_rerere_dir(struct rerere_dir *rr_dir)
 {
 	struct dirent *de;
-	DIR *dir = opendir(git_path("rr-cache/%s", rr_dir->name));
+	char *path;
+	DIR *dir;
 
+	path = repo_git_path(the_repository, "rr-cache/%s", rr_dir->name);
+	dir = opendir(path);
+	free(path);
 	if (!dir)
 		return;
 	while ((de = readdir(dir)) != NULL) {
@@ -1234,6 +1238,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 	timestamp_t now = time(NULL);
 	timestamp_t cutoff_noresolve = now - 15 * 86400;
 	timestamp_t cutoff_resolve = now - 60 * 86400;
+	struct strbuf buf = STRBUF_INIT;
 
 	if (setup_rerere(r, rr, 0) < 0)
 		return;
@@ -1243,7 +1248,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 	repo_config_get_expiry_in_days(the_repository, "gc.rerereunresolved",
 				       &cutoff_noresolve, now);
 	git_config(git_default_config, NULL);
-	dir = opendir(git_path("rr-cache"));
+	dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache"));
 	if (!dir)
 		die_errno(_("unable to open rr-cache directory"));
 	/* Collect stale conflict IDs ... */
@@ -1272,9 +1277,12 @@ void rerere_gc(struct repository *r, struct string_list *rr)
 
 	/* ... and then remove the empty directories */
 	for (i = 0; i < to_remove.nr; i++)
-		rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
+		rmdir(repo_git_path_replace(the_repository, &buf,
+					    "rr-cache/%s", to_remove.items[i].string));
+
 	string_list_clear(&to_remove, 0);
 	rollback_lock_file(&write_lock);
+	strbuf_release(&buf);
 }
 
 /*
diff --git a/shallow.c b/shallow.c
index b54244ffa9..4bd9342c9a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -364,7 +364,9 @@ const char *setup_temporary_shallow(const struct oid_array *extra)
 	struct strbuf sb = STRBUF_INIT;
 
 	if (write_shallow_commits(&sb, 0, extra)) {
-		temp = xmks_tempfile(git_path("shallow_XXXXXX"));
+		char *path = repo_git_path(the_repository, "shallow_XXXXXX");
+		temp = xmks_tempfile(path);
+		free(path);
 
 		if (write_in_full(temp->fd, sb.buf, sb.len) < 0 ||
 		    close_tempfile_gently(temp) < 0)
diff --git a/wt-status.c b/wt-status.c
index 3ee9181764..1da5732f57 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1289,7 +1289,8 @@ static void show_am_in_progress(struct wt_status *s,
 static char *read_line_from_git_path(const char *filename)
 {
 	struct strbuf buf = STRBUF_INIT;
-	FILE *fp = fopen_or_warn(git_path("%s", filename), "r");
+	FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+						      "%s", filename), "r");
 
 	if (!fp) {
 		strbuf_release(&buf);
@@ -1383,27 +1384,33 @@ static void abbrev_oid_in_line(struct strbuf *line)
 
 static int read_rebase_todolist(const char *fname, struct string_list *lines)
 {
-	struct strbuf line = STRBUF_INIT;
-	FILE *f = fopen(git_path("%s", fname), "r");
+	struct strbuf buf = STRBUF_INIT;
+	FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r");
+	int ret;
 
 	if (!f) {
-		if (errno == ENOENT)
-			return -1;
+		if (errno == ENOENT) {
+			ret = -1;
+			goto out;
+		}
 		die_errno("Could not open file %s for reading",
-			  git_path("%s", fname));
+			  repo_git_path_replace(the_repository, &buf, "%s", fname));
 	}
-	while (!strbuf_getline_lf(&line, f)) {
-		if (starts_with(line.buf, comment_line_str))
+	while (!strbuf_getline_lf(&buf, f)) {
+		if (starts_with(buf.buf, comment_line_str))
 			continue;
-		strbuf_trim(&line);
-		if (!line.len)
+		strbuf_trim(&buf);
+		if (!buf.len)
 			continue;
-		abbrev_oid_in_line(&line);
-		string_list_append(lines, line.buf);
+		abbrev_oid_in_line(&buf);
+		string_list_append(lines, buf.buf);
 	}
 	fclose(f);
-	strbuf_release(&line);
-	return 0;
+
+	ret = 0;
+out:
+	strbuf_release(&buf);
+	return ret;
 }
 
 static void show_rebase_information(struct wt_status *s,
@@ -1434,9 +1441,12 @@ static void show_rebase_information(struct wt_status *s,
 				i < have_done.nr;
 				i++)
 				status_printf_ln(s, color, "   %s", have_done.items[i].string);
-			if (have_done.nr > nr_lines_to_show && s->hints)
+			if (have_done.nr > nr_lines_to_show && s->hints) {
+				char *path = repo_git_path(the_repository, "rebase-merge/done");
 				status_printf_ln(s, color,
-					_("  (see more in file %s)"), git_path("rebase-merge/done"));
+					_("  (see more in file %s)"), path);
+				free(path);
+			}
 		}
 
 		if (yet_to_do.nr == 0)

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 13/16] repo-settings: introduce function to clear struct
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
                     ` (4 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

We don't provide a way to clear a `struct repo_settings`, and instead
open-code this in `repo_clear()`. This is mixing up concerns and means
that developers have to touch multiple files whenever they add a new
field to the structure in case the associated resources need to be
released.

Provide a new `repo_settings_clear()` function to improve this.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 repo-settings.c | 10 ++++++++--
 repo-settings.h |  1 +
 repository.c    |  2 +-
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/repo-settings.c b/repo-settings.c
index 9d16d5399e..719cd7c85c 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -21,7 +21,6 @@ static void repo_cfg_int(struct repository *r, const char *key, int *dest,
 
 void prepare_repo_settings(struct repository *r)
 {
-	const struct repo_settings defaults = REPO_SETTINGS_INIT;
 	int experimental;
 	int value;
 	const char *strval;
@@ -35,7 +34,7 @@ void prepare_repo_settings(struct repository *r)
 	if (r->settings.initialized)
 		return;
 
-	memcpy(&r->settings, &defaults, sizeof(defaults));
+	repo_settings_clear(r);
 	r->settings.initialized++;
 
 	/* Booleans config or default, cascades to other settings */
@@ -143,6 +142,13 @@ void prepare_repo_settings(struct repository *r)
 		r->settings.packed_git_limit = ulongval;
 }
 
+void repo_settings_clear(struct repository *r)
+{
+	struct repo_settings empty = REPO_SETTINGS_INIT;
+	FREE_AND_NULL(r->settings.fsmonitor);
+	r->settings = empty;
+}
+
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo)
 {
 	const char *value;
diff --git a/repo-settings.h b/repo-settings.h
index 93ea0c3274..c4f7e3bd8a 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -73,6 +73,7 @@ struct repo_settings {
 }
 
 void prepare_repo_settings(struct repository *r);
+void repo_settings_clear(struct repository *r);
 
 /* Read the value for "core.logAllRefUpdates". */
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
diff --git a/repository.c b/repository.c
index 648cd88474..6cbaf2e3da 100644
--- a/repository.c
+++ b/repository.c
@@ -380,7 +380,7 @@ void repo_clear(struct repository *repo)
 	parsed_object_pool_clear(repo->parsed_objects);
 	FREE_AND_NULL(repo->parsed_objects);
 
-	FREE_AND_NULL(repo->settings.fsmonitor);
+	repo_settings_clear(repo);
 
 	if (repo->config) {
 		git_configset_clear(repo->config);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 14/16] environment: move access to "core.hooksPath" into repo settings
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
                     ` (3 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

The "core.hooksPath" setting is stored in a global variable and
populated via the `git_default_core_config`. This may cause issues in
the case where one is handling multiple different repositories in a
single process with different values for that config key, as we may or
may not see the correct value in that case. Furthermore, global state
blocks our path towards libification.

Refactor the code so that we instead store the value in `struct
repo_settings`. The value is computed as-needed and cached. The result
should be functionally the same as there aren't ever any code paths
where we'd execute hooks outside the context of a repository.

Note that this requires us to change the passed-in repository in the
`repo_git_path()` family of functions to be non-constant, as we call
`adjust_git_path()` there.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.c        |  5 -----
 environment.c   |  1 -
 environment.h   |  1 -
 path.c          | 15 ++++++++-------
 path.h          |  6 +++---
 repo-settings.c |  8 ++++++++
 repo-settings.h |  4 ++++
 7 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/config.c b/config.c
index 50f2d17b39..d932d4b134 100644
--- a/config.c
+++ b/config.c
@@ -1436,11 +1436,6 @@ static int git_default_core_config(const char *var, const char *value,
 		return git_config_pathname(&git_attributes_file, var, value);
 	}
 
-	if (!strcmp(var, "core.hookspath")) {
-		FREE_AND_NULL(git_hooks_path);
-		return git_config_pathname(&git_hooks_path, var, value);
-	}
-
 	if (!strcmp(var, "core.bare")) {
 		is_bare_repository_cfg = git_config_bool(var, value);
 		return 0;
diff --git a/environment.c b/environment.c
index 8389a27270..39755873ee 100644
--- a/environment.c
+++ b/environment.c
@@ -42,7 +42,6 @@ char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
 char *git_attributes_file;
-char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
diff --git a/environment.h b/environment.h
index 2f43340f0b..66989afbac 100644
--- a/environment.h
+++ b/environment.h
@@ -160,7 +160,6 @@ extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
 extern char *git_attributes_file;
-extern char *git_hooks_path;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern size_t packed_git_window_size;
diff --git a/path.c b/path.c
index a42f72800d..e81ebd3b5c 100644
--- a/path.c
+++ b/path.c
@@ -387,10 +387,11 @@ void report_linked_checkout_garbage(struct repository *r)
 	strbuf_release(&sb);
 }
 
-static void adjust_git_path(const struct repository *repo,
+static void adjust_git_path(struct repository *repo,
 			    struct strbuf *buf, int git_dir_len)
 {
 	const char *base = buf->buf + git_dir_len;
+
 	if (is_dir_file(base, "info", "grafts"))
 		strbuf_splice(buf, 0, buf->len,
 			      repo->graft_file, strlen(repo->graft_file));
@@ -399,8 +400,8 @@ static void adjust_git_path(const struct repository *repo,
 			      repo->index_file, strlen(repo->index_file));
 	else if (dir_prefix(base, "objects"))
 		replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
-	else if (git_hooks_path && dir_prefix(base, "hooks"))
-		replace_dir(buf, git_dir_len + 5, git_hooks_path);
+	else if (repo_settings_get_hooks_path(repo) && dir_prefix(base, "hooks"))
+		replace_dir(buf, git_dir_len + 5, repo_settings_get_hooks_path(repo));
 	else if (repo->different_commondir)
 		update_common_dir(buf, git_dir_len, repo->commondir);
 }
@@ -417,7 +418,7 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
 		repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
 }
 
-static void repo_git_pathv(const struct repository *repo,
+static void repo_git_pathv(struct repository *repo,
 			   const struct worktree *wt, struct strbuf *buf,
 			   const char *fmt, va_list args)
 {
@@ -432,7 +433,7 @@ static void repo_git_pathv(const struct repository *repo,
 	strbuf_cleanup_path(buf);
 }
 
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
 		    const char *fmt, ...)
 {
 	struct strbuf path = STRBUF_INIT;
@@ -443,7 +444,7 @@ char *repo_git_path(const struct repository *repo,
 	return strbuf_detach(&path, NULL);
 }
 
-const char *repo_git_path_append(const struct repository *repo,
+const char *repo_git_path_append(struct repository *repo,
 				 struct strbuf *sb,
 				 const char *fmt, ...)
 {
@@ -454,7 +455,7 @@ const char *repo_git_path_append(const struct repository *repo,
 	return sb->buf;
 }
 
-const char *repo_git_path_replace(const struct repository *repo,
+const char *repo_git_path_replace(struct repository *repo,
 				  struct strbuf *sb,
 				  const char *fmt, ...)
 {
diff --git a/path.h b/path.h
index f28d5a7ca9..373404dd9d 100644
--- a/path.h
+++ b/path.h
@@ -52,14 +52,14 @@ const char *repo_common_path_replace(const struct repository *repo,
  * For an exhaustive list of the adjustments made look at `common_list` and
  * `adjust_git_path` in path.c.
  */
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
 		    const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
-const char *repo_git_path_append(const struct repository *repo,
+const char *repo_git_path_append(struct repository *repo,
 				 struct strbuf *sb,
 				 const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
-const char *repo_git_path_replace(const struct repository *repo,
+const char *repo_git_path_replace(struct repository *repo,
 				  struct strbuf *sb,
 				  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
diff --git a/repo-settings.c b/repo-settings.c
index 719cd7c85c..876d527581 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -146,6 +146,7 @@ void repo_settings_clear(struct repository *r)
 {
 	struct repo_settings empty = REPO_SETTINGS_INIT;
 	FREE_AND_NULL(r->settings.fsmonitor);
+	FREE_AND_NULL(r->settings.hooks_path);
 	r->settings = empty;
 }
 
@@ -173,3 +174,10 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo)
 			      &repo->settings.warn_ambiguous_refs, 1);
 	return repo->settings.warn_ambiguous_refs;
 }
+
+const char *repo_settings_get_hooks_path(struct repository *repo)
+{
+	if (!repo->settings.hooks_path)
+		repo_config_get_pathname(repo, "core.hookspath", &repo->settings.hooks_path);
+	return repo->settings.hooks_path;
+}
diff --git a/repo-settings.h b/repo-settings.h
index c4f7e3bd8a..0cef970443 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -61,6 +61,8 @@ struct repo_settings {
 	size_t delta_base_cache_limit;
 	size_t packed_git_window_size;
 	size_t packed_git_limit;
+
+	char *hooks_path;
 };
 #define REPO_SETTINGS_INIT { \
 	.index_version = -1, \
@@ -79,5 +81,7 @@ void repo_settings_clear(struct repository *r);
 enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
 /* Read the value for "core.warnAmbiguousRefs". */
 int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
+/* Read the value for "core.hooksPath". */
+const char *repo_settings_get_hooks_path(struct repository *repo);
 
 #endif /* REPO_SETTINGS_H */

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 15/16] environment: move access to "core.sharedRepository" into repo settings
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:03   ` [PATCH v2 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
                     ` (2 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

Similar as with the preceding commit, we track "core.sharedRepository"
via a pair of global variables. Move them into `struct repo_settings` so
that we can instead track them per-repository.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/init-db.c |  8 ++++----
 builtin/log.c     |  6 +++---
 environment.c     | 26 --------------------------
 environment.h     | 10 ----------
 path.c            | 10 +++++-----
 repo-settings.c   | 26 ++++++++++++++++++++++++++
 repo-settings.h   |  9 +++++++++
 setup.c           | 21 +++++++++++----------
 8 files changed, 58 insertions(+), 58 deletions(-)

diff --git a/builtin/init-db.c b/builtin/init-db.c
index 096f96b9c4..196dccdd77 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -132,8 +132,8 @@ int cmd_init_db(int argc,
 				 * and we know shared_repository should always be 0;
 				 * but just in case we play safe.
 				 */
-				saved = get_shared_repository();
-				set_shared_repository(0);
+				saved = repo_settings_get_shared_repository(the_repository);
+				repo_settings_set_shared_repository(the_repository, 0);
 				switch (safe_create_leading_directories_const(argv[0])) {
 				case SCLD_OK:
 				case SCLD_PERMS:
@@ -145,7 +145,7 @@ int cmd_init_db(int argc,
 					die_errno(_("cannot mkdir %s"), argv[0]);
 					break;
 				}
-				set_shared_repository(saved);
+				repo_settings_set_shared_repository(the_repository, saved);
 				if (mkdir(argv[0], 0777) < 0)
 					die_errno(_("cannot mkdir %s"), argv[0]);
 				mkdir_tried = 1;
@@ -175,7 +175,7 @@ int cmd_init_db(int argc,
 	}
 
 	if (init_shared_repository != -1)
-		set_shared_repository(init_shared_repository);
+		repo_settings_set_shared_repository(the_repository, init_shared_repository);
 
 	/*
 	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
diff --git a/builtin/log.c b/builtin/log.c
index e41f88945e..04a6ef97bc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -2309,8 +2309,8 @@ int cmd_format_patch(int argc,
 		 * We consider <outdir> as 'outside of gitdir', therefore avoid
 		 * applying adjust_shared_perm in s-c-l-d.
 		 */
-		saved = get_shared_repository();
-		set_shared_repository(0);
+		saved = repo_settings_get_shared_repository(the_repository);
+		repo_settings_set_shared_repository(the_repository, 0);
 		switch (safe_create_leading_directories_const(output_directory)) {
 		case SCLD_OK:
 		case SCLD_EXISTS:
@@ -2319,7 +2319,7 @@ int cmd_format_patch(int argc,
 			die(_("could not create leading directories "
 			      "of '%s'"), output_directory);
 		}
-		set_shared_repository(saved);
+		repo_settings_set_shared_repository(the_repository, saved);
 		if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
 			die_errno(_("could not create directory '%s'"),
 				  output_directory);
diff --git a/environment.c b/environment.c
index 39755873ee..c79acc69e7 100644
--- a/environment.c
+++ b/environment.c
@@ -206,32 +206,6 @@ const char *get_commit_output_encoding(void)
 	return git_commit_encoding ? git_commit_encoding : "UTF-8";
 }
 
-static int the_shared_repository = PERM_UMASK;
-static int need_shared_repository_from_config = 1;
-
-void set_shared_repository(int value)
-{
-	the_shared_repository = value;
-	need_shared_repository_from_config = 0;
-}
-
-int get_shared_repository(void)
-{
-	if (need_shared_repository_from_config) {
-		const char *var = "core.sharedrepository";
-		const char *value;
-		if (!git_config_get_value(var, &value))
-			the_shared_repository = git_config_perm(var, value);
-		need_shared_repository_from_config = 0;
-	}
-	return the_shared_repository;
-}
-
-void reset_shared_repository(void)
-{
-	need_shared_repository_from_config = 1;
-}
-
 int use_optional_locks(void)
 {
 	return git_env_bool(GIT_OPTIONAL_LOCKS_ENVIRONMENT, 1);
diff --git a/environment.h b/environment.h
index 66989afbac..45e690f203 100644
--- a/environment.h
+++ b/environment.h
@@ -134,16 +134,6 @@ void setup_git_env(const char *git_dir);
  */
 int have_git_dir(void);
 
-/*
- * Accessors for the core.sharedrepository config which lazy-load the value
- * from the config (if not already set). The "reset" function can be
- * used to unset "set" or cached value, meaning that the value will be loaded
- * fresh from the config file on the next call to get_shared_repository().
- */
-void set_shared_repository(int value);
-int get_shared_repository(void);
-void reset_shared_repository(void);
-
 extern int is_bare_repository_cfg;
 int is_bare_repository(void);
 extern char *git_work_tree_cfg;
diff --git a/path.c b/path.c
index e81ebd3b5c..a2f402baec 100644
--- a/path.c
+++ b/path.c
@@ -844,17 +844,17 @@ int calc_shared_perm(int mode)
 {
 	int tweak;
 
-	if (get_shared_repository() < 0)
-		tweak = -get_shared_repository();
+	if (repo_settings_get_shared_repository(the_repository) < 0)
+		tweak = -repo_settings_get_shared_repository(the_repository);
 	else
-		tweak = get_shared_repository();
+		tweak = repo_settings_get_shared_repository(the_repository);
 
 	if (!(mode & S_IWUSR))
 		tweak &= ~0222;
 	if (mode & S_IXUSR)
 		/* Copy read bits to execute bits */
 		tweak |= (tweak & 0444) >> 2;
-	if (get_shared_repository() < 0)
+	if (repo_settings_get_shared_repository(the_repository) < 0)
 		mode = (mode & ~0777) | tweak;
 	else
 		mode |= tweak;
@@ -867,7 +867,7 @@ int adjust_shared_perm(const char *path)
 {
 	int old_mode, new_mode;
 
-	if (!get_shared_repository())
+	if (!repo_settings_get_shared_repository(the_repository))
 		return 0;
 	if (get_st_mode_bits(path, &old_mode) < 0)
 		return -1;
diff --git a/repo-settings.c b/repo-settings.c
index 876d527581..67e9cfd2e6 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -4,6 +4,7 @@
 #include "repository.h"
 #include "midx.h"
 #include "pack-objects.h"
+#include "setup.h"
 
 static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
 			  int def)
@@ -181,3 +182,28 @@ const char *repo_settings_get_hooks_path(struct repository *repo)
 		repo_config_get_pathname(repo, "core.hookspath", &repo->settings.hooks_path);
 	return repo->settings.hooks_path;
 }
+
+int repo_settings_get_shared_repository(struct repository *repo)
+{
+	if (!repo->settings.shared_repository_initialized) {
+		const char *var = "core.sharedrepository";
+		const char *value;
+		if (!repo_config_get_value(repo, var, &value))
+			repo->settings.shared_repository = git_config_perm(var, value);
+		else
+			repo->settings.shared_repository = PERM_UMASK;
+		repo->settings.shared_repository_initialized = 1;
+	}
+	return repo->settings.shared_repository;
+}
+
+void repo_settings_set_shared_repository(struct repository *repo, int value)
+{
+	repo->settings.shared_repository = value;
+	repo->settings.shared_repository_initialized = 1;
+}
+
+void repo_settings_reset_shared_repository(struct repository *repo)
+{
+	repo->settings.shared_repository_initialized = 0;
+}
diff --git a/repo-settings.h b/repo-settings.h
index 0cef970443..ddc11967e0 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -37,6 +37,9 @@ struct repo_settings {
 	int pack_use_bitmap_boundary_traversal;
 	int pack_use_multi_pack_reuse;
 
+	int shared_repository;
+	int shared_repository_initialized;
+
 	/*
 	 * Does this repository have core.useReplaceRefs=true (on by
 	 * default)? This provides a repository-scoped version of this
@@ -65,6 +68,7 @@ struct repo_settings {
 	char *hooks_path;
 };
 #define REPO_SETTINGS_INIT { \
+	.shared_repository = -1, \
 	.index_version = -1, \
 	.core_untracked_cache = UNTRACKED_CACHE_KEEP, \
 	.fetch_negotiation_algorithm = FETCH_NEGOTIATION_CONSECUTIVE, \
@@ -84,4 +88,9 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
 /* Read the value for "core.hooksPath". */
 const char *repo_settings_get_hooks_path(struct repository *repo);
 
+/* Read, set or reset the value for "core.sharedRepository". */
+int repo_settings_get_shared_repository(struct repository *repo);
+void repo_settings_set_shared_repository(struct repository *repo, int value);
+void repo_settings_reset_shared_repository(struct repository *repo);
+
 #endif /* REPO_SETTINGS_H */
diff --git a/setup.c b/setup.c
index 30889386f7..aa65b93f53 100644
--- a/setup.c
+++ b/setup.c
@@ -2332,7 +2332,7 @@ static int create_default_files(const char *template_path,
 	 */
 	copy_templates(template_path);
 	git_config_clear();
-	reset_shared_repository();
+	repo_settings_reset_shared_repository(the_repository);
 	git_config(git_default_config, NULL);
 
 	reinit = is_reinit();
@@ -2342,7 +2342,8 @@ static int create_default_files(const char *template_path,
 	 * values we might have just re-read from the config.
 	 */
 	if (init_shared_repository != -1)
-		set_shared_repository(init_shared_repository);
+		repo_settings_set_shared_repository(the_repository,
+						    init_shared_repository);
 
 	is_bare_repository_cfg = !work_tree;
 
@@ -2350,7 +2351,7 @@ static int create_default_files(const char *template_path,
 	 * We would have created the above under user's umask -- under
 	 * shared-repository settings, we would need to fix them up.
 	 */
-	if (get_shared_repository()) {
+	if (repo_settings_get_shared_repository(the_repository)) {
 		adjust_shared_perm(repo_get_git_dir(the_repository));
 	}
 
@@ -2597,7 +2598,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 					  initial_branch, flags & INIT_DB_QUIET);
 	create_object_directory();
 
-	if (get_shared_repository()) {
+	if (repo_settings_get_shared_repository(the_repository)) {
 		char buf[10];
 		/* We do not spell "group" and such, so that
 		 * the configuration can be read by older version
@@ -2605,12 +2606,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		 * and compatibility values for PERM_GROUP and
 		 * PERM_EVERYBODY.
 		 */
-		if (get_shared_repository() < 0)
+		if (repo_settings_get_shared_repository(the_repository) < 0)
 			/* force to the mode value */
-			xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
-		else if (get_shared_repository() == PERM_GROUP)
+			xsnprintf(buf, sizeof(buf), "0%o", -repo_settings_get_shared_repository(the_repository));
+		else if (repo_settings_get_shared_repository(the_repository) == PERM_GROUP)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
-		else if (get_shared_repository() == PERM_EVERYBODY)
+		else if (repo_settings_get_shared_repository(the_repository) == PERM_EVERYBODY)
 			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
 		else
 			BUG("invalid value for shared_repository");
@@ -2622,12 +2623,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
 		int len = strlen(git_dir);
 
 		if (reinit)
-			printf(get_shared_repository()
+			printf(repo_settings_get_shared_repository(the_repository)
 			       ? _("Reinitialized existing shared Git repository in %s%s\n")
 			       : _("Reinitialized existing Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 		else
-			printf(get_shared_repository()
+			printf(repo_settings_get_shared_repository(the_repository)
 			       ? _("Initialized empty shared Git repository in %s%s\n")
 			       : _("Initialized empty Git repository in %s%s\n"),
 			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v2 16/16] path: adjust last remaining users of `the_repository`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
@ 2025-02-07 11:03   ` Patrick Steinhardt
  2025-02-07 11:44   ` [PATCH v2 00/16] path: remove dependency on `the_repository` Karthik Nayak
  2025-02-08 15:31   ` shejialuo
  17 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-07 11:03 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, shejialuo

With the preceding refactorings we now only have a couple of implicit
users of `the_repository` left in the "path" subsystem, all of which
depend on global state via `calc_shared_perm()`. Make the dependency on
`the_repository` explicit by passing the repo as a parameter instead and
adjust callers accordingly.

Note that this change bubbles up into a couple of subsystems that were
previously declared as free from `the_repository`. Instead of marking
all of them as `the_repository`-dependent again, we instead use the
repository that is available in the calling context. There are three
exceptions though with "copy.c", "pack-write.c" and "tempfile.c".
Adjusting these would require us to adapt callsites all over the place,
so this is left for a future iteration.

Mark "path.c" as free from `the_repository`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c         |  2 +-
 commit-graph.c          |  2 +-
 copy.c                  |  4 +++-
 loose.c                 |  2 +-
 midx-write.c            |  2 +-
 object-file.c           |  8 ++++----
 pack-bitmap-write.c     |  2 +-
 pack-write.c            | 10 ++++++----
 path.c                  | 25 ++++++++++++-------------
 path.h                  |  6 +++---
 read-cache.c            |  2 +-
 refs/files-backend.c    | 10 +++++-----
 refs/reftable-backend.c | 10 +++++-----
 server-info.c           |  2 +-
 setup.c                 | 12 ++++++------
 tempfile.c              |  4 +++-
 tmp-objdir.c            | 17 ++++++++++-------
 17 files changed, 64 insertions(+), 56 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 5ae6ee9db9..23eeb782aa 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1220,7 +1220,7 @@ int cmd_clone(int argc,
 
 	strbuf_reset(&buf);
 	strbuf_addf(&buf, "%s/refs", git_dir);
-	safe_create_dir(buf.buf, 1);
+	safe_create_dir(the_repository, buf.buf, 1);
 
 	/*
 	 * additional config can be injected with -c, make sure it's included
diff --git a/commit-graph.c b/commit-graph.c
index 2a2999a6b8..1021ccb983 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -2084,7 +2084,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 			return -1;
 		}
 
-		if (adjust_shared_perm(get_tempfile_path(graph_layer))) {
+		if (adjust_shared_perm(the_repository, get_tempfile_path(graph_layer))) {
 			error(_("unable to adjust shared permissions for '%s'"),
 			      get_tempfile_path(graph_layer));
 			return -1;
diff --git a/copy.c b/copy.c
index d9d2092012..b668209b6c 100644
--- a/copy.c
+++ b/copy.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "copy.h"
 #include "path.h"
@@ -57,7 +59,7 @@ int copy_file(const char *dst, const char *src, int mode)
 	if (close(fdo) != 0)
 		return error_errno("%s: close error", dst);
 
-	if (!status && adjust_shared_perm(dst))
+	if (!status && adjust_shared_perm(the_repository, dst))
 		return -1;
 
 	return status;
diff --git a/loose.c b/loose.c
index 51ef490f93..bb602aaa36 100644
--- a/loose.c
+++ b/loose.c
@@ -190,7 +190,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
 		goto errout;
 	if (close(fd))
 		goto errout;
-	adjust_shared_perm(path.buf);
+	adjust_shared_perm(repo, path.buf);
 	rollback_lock_file(&lock);
 	strbuf_release(&buf);
 	strbuf_release(&path);
diff --git a/midx-write.c b/midx-write.c
index 61b59d557d..48d6558253 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1336,7 +1336,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
 			return -1;
 		}
 
-		if (adjust_shared_perm(get_tempfile_path(incr))) {
+		if (adjust_shared_perm(r, get_tempfile_path(incr))) {
 			error(_("unable to adjust shared permissions for '%s'"),
 			      get_tempfile_path(incr));
 			return -1;
diff --git a/object-file.c b/object-file.c
index dc9fcaf3e9..5d782417e3 100644
--- a/object-file.c
+++ b/object-file.c
@@ -388,7 +388,7 @@ int mkdir_in_gitdir(const char *path)
 		}
 		strbuf_release(&sb);
 	}
-	return adjust_shared_perm(path);
+	return adjust_shared_perm(the_repository, path);
 }
 
 static enum scld_error safe_create_leading_directories_1(char *path, int share)
@@ -437,7 +437,7 @@ static enum scld_error safe_create_leading_directories_1(char *path, int share)
 				ret = SCLD_VANISHED;
 			else
 				ret = SCLD_FAILED;
-		} else if (share && adjust_shared_perm(path)) {
+		} else if (share && adjust_shared_perm(the_repository, path)) {
 			ret = SCLD_PERMS;
 		}
 		*slash = slash_character;
@@ -2105,7 +2105,7 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
 	}
 
 out:
-	if (adjust_shared_perm(filename))
+	if (adjust_shared_perm(the_repository, filename))
 		return error(_("unable to set permission to '%s'"), filename);
 	return 0;
 }
@@ -2181,7 +2181,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
 		strbuf_add(tmp, filename, dirlen - 1);
 		if (mkdir(tmp->buf, 0777) && errno != EEXIST)
 			return -1;
-		if (adjust_shared_perm(tmp->buf))
+		if (adjust_shared_perm(the_repository, tmp->buf))
 			return -1;
 
 		/* Try again */
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index a06a1f35c6..34e86d4994 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1072,7 +1072,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
 			  CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
 
-	if (adjust_shared_perm(tmp_file.buf))
+	if (adjust_shared_perm(the_repository, tmp_file.buf))
 		die_errno("unable to make temporary bitmap file readable");
 
 	if (rename(tmp_file.buf, filename))
diff --git a/pack-write.c b/pack-write.c
index a2faeb1895..c3f4e66f02 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "environment.h"
 #include "gettext.h"
@@ -287,7 +289,7 @@ char *write_rev_file_order(const struct git_hash_algo *hash_algo,
 	write_rev_index_positions(f, pack_order, nr_objects);
 	write_rev_trailer(hash_algo, f, hash);
 
-	if (adjust_shared_perm(path) < 0)
+	if (adjust_shared_perm(the_repository, path) < 0)
 		die(_("failed to make %s readable"), path);
 
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -350,7 +352,7 @@ static char *write_mtimes_file(const struct git_hash_algo *hash_algo,
 	write_mtimes_objects(f, to_pack, objects, nr_objects);
 	write_mtimes_trailer(hash_algo, f, hash);
 
-	if (adjust_shared_perm(mtimes_name) < 0)
+	if (adjust_shared_perm(the_repository, mtimes_name) < 0)
 		die(_("failed to make %s readable"), mtimes_name);
 
 	finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -565,12 +567,12 @@ void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
 	char *rev_tmp_name = NULL;
 	char *mtimes_tmp_name = NULL;
 
-	if (adjust_shared_perm(pack_tmp_name))
+	if (adjust_shared_perm(the_repository, pack_tmp_name))
 		die_errno("unable to make temporary pack file readable");
 
 	*idx_tmp_name = (char *)write_idx_file(hash_algo, NULL, written_list,
 					       nr_written, pack_idx_opts, hash);
-	if (adjust_shared_perm(*idx_tmp_name))
+	if (adjust_shared_perm(the_repository, *idx_tmp_name))
 		die_errno("unable to make temporary index file readable");
 
 	rev_tmp_name = write_rev_file(hash_algo, NULL, written_list, nr_written,
diff --git a/path.c b/path.c
index a2f402baec..910756c8b3 100644
--- a/path.c
+++ b/path.c
@@ -2,8 +2,6 @@
  * Utilities for paths and pathnames
  */
 
-#define USE_THE_REPOSITORY_VARIABLE
-
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "environment.h"
@@ -840,21 +838,22 @@ const char *enter_repo(const char *path, unsigned flags)
 	return NULL;
 }
 
-int calc_shared_perm(int mode)
+int calc_shared_perm(struct repository *repo,
+		     int mode)
 {
 	int tweak;
 
-	if (repo_settings_get_shared_repository(the_repository) < 0)
-		tweak = -repo_settings_get_shared_repository(the_repository);
+	if (repo_settings_get_shared_repository(repo) < 0)
+		tweak = -repo_settings_get_shared_repository(repo);
 	else
-		tweak = repo_settings_get_shared_repository(the_repository);
+		tweak = repo_settings_get_shared_repository(repo);
 
 	if (!(mode & S_IWUSR))
 		tweak &= ~0222;
 	if (mode & S_IXUSR)
 		/* Copy read bits to execute bits */
 		tweak |= (tweak & 0444) >> 2;
-	if (repo_settings_get_shared_repository(the_repository) < 0)
+	if (repo_settings_get_shared_repository(repo) < 0)
 		mode = (mode & ~0777) | tweak;
 	else
 		mode |= tweak;
@@ -862,17 +861,17 @@ int calc_shared_perm(int mode)
 	return mode;
 }
 
-
-int adjust_shared_perm(const char *path)
+int adjust_shared_perm(struct repository *repo,
+		       const char *path)
 {
 	int old_mode, new_mode;
 
-	if (!repo_settings_get_shared_repository(the_repository))
+	if (!repo_settings_get_shared_repository(repo))
 		return 0;
 	if (get_st_mode_bits(path, &old_mode) < 0)
 		return -1;
 
-	new_mode = calc_shared_perm(old_mode);
+	new_mode = calc_shared_perm(repo, old_mode);
 	if (S_ISDIR(old_mode)) {
 		/* Copy read bits to execute bits */
 		new_mode |= (new_mode & 0444) >> 2;
@@ -891,7 +890,7 @@ int adjust_shared_perm(const char *path)
 	return 0;
 }
 
-void safe_create_dir(const char *dir, int share)
+void safe_create_dir(struct repository *repo, const char *dir, int share)
 {
 	if (mkdir(dir, 0777) < 0) {
 		if (errno != EEXIST) {
@@ -899,7 +898,7 @@ void safe_create_dir(const char *dir, int share)
 			exit(1);
 		}
 	}
-	else if (share && adjust_shared_perm(dir))
+	else if (share && adjust_shared_perm(repo, dir))
 		die(_("Could not make %s writable by group"), dir);
 }
 
diff --git a/path.h b/path.h
index 373404dd9d..65fe968a13 100644
--- a/path.h
+++ b/path.h
@@ -141,8 +141,8 @@ const char *git_path_shallow(struct repository *r);
 
 int ends_with_path_components(const char *path, const char *components);
 
-int calc_shared_perm(int mode);
-int adjust_shared_perm(const char *path);
+int calc_shared_perm(struct repository *repo, int mode);
+int adjust_shared_perm(struct repository *repo, const char *path);
 
 char *interpolate_path(const char *path, int real_home);
 
@@ -219,7 +219,7 @@ char *xdg_cache_home(const char *filename);
  * directories under $GIT_DIR.  Don't use it for working tree
  * directories.
  */
-void safe_create_dir(const char *dir, int share);
+void safe_create_dir(struct repository *repo, const char *dir, int share);
 
 # ifdef USE_THE_REPOSITORY_VARIABLE
 #  include "strbuf.h"
diff --git a/read-cache.c b/read-cache.c
index 66ad0015a7..900738e7a8 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3290,7 +3290,7 @@ static int write_shared_index(struct index_state *istate,
 
 	if (ret)
 		return ret;
-	ret = adjust_shared_perm(get_tempfile_path(*temp));
+	ret = adjust_shared_perm(the_repository, get_tempfile_path(*temp));
 	if (ret) {
 		error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
 		return ret;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 29f08dced4..6c6e67dc1c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1831,7 +1831,7 @@ static int log_ref_setup(struct files_ref_store *refs,
 	}
 
 	if (*logfd >= 0)
-		adjust_shared_perm(logfile);
+		adjust_shared_perm(the_repository, logfile);
 
 	free(logfile);
 	return 0;
@@ -3488,8 +3488,8 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
 	 *   they do not understand the reference format extension.
 	 */
 	strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
-	safe_create_dir(sb.buf, 1);
-	adjust_shared_perm(sb.buf);
+	safe_create_dir(the_repository, sb.buf, 1);
+	adjust_shared_perm(the_repository, sb.buf);
 
 	/*
 	 * There is no need to create directories for common refs when creating
@@ -3501,11 +3501,11 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
 		 */
 		strbuf_reset(&sb);
 		files_ref_path(refs, &sb, "refs/heads");
-		safe_create_dir(sb.buf, 1);
+		safe_create_dir(the_repository, sb.buf, 1);
 
 		strbuf_reset(&sb);
 		files_ref_path(refs, &sb, "refs/tags");
-		safe_create_dir(sb.buf, 1);
+		safe_create_dir(the_repository, sb.buf, 1);
 	}
 
 	strbuf_release(&sb);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d39a14c5a4..9c54e2c173 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -380,7 +380,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 	default:
 		BUG("unknown hash algorithm %d", repo->hash_algo->format_id);
 	}
-	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
+	refs->write_options.default_permissions = calc_shared_perm(the_repository, 0666 & ~mask);
 	refs->write_options.disable_auto_compact =
 		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
 	refs->write_options.lock_timeout_ms = 100;
@@ -470,21 +470,21 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
 	struct strbuf sb = STRBUF_INIT;
 
 	strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
-	safe_create_dir(sb.buf, 1);
+	safe_create_dir(the_repository, sb.buf, 1);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
 	write_file(sb.buf, "ref: refs/heads/.invalid");
-	adjust_shared_perm(sb.buf);
+	adjust_shared_perm(the_repository, sb.buf);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
-	safe_create_dir(sb.buf, 1);
+	safe_create_dir(the_repository, sb.buf, 1);
 	strbuf_reset(&sb);
 
 	strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
 	write_file(sb.buf, "this repository uses the reftable format");
-	adjust_shared_perm(sb.buf);
+	adjust_shared_perm(the_repository, sb.buf);
 
 	strbuf_release(&sb);
 	return 0;
diff --git a/server-info.c b/server-info.c
index 31c3fdc118..1ca0e00d51 100644
--- a/server-info.c
+++ b/server-info.c
@@ -125,7 +125,7 @@ static int update_info_file(struct repository *r, char *path,
 	uic.cur_fp = NULL;
 
 	if (uic_is_stale(&uic)) {
-		if (adjust_shared_perm(get_tempfile_path(f)) < 0)
+		if (adjust_shared_perm(r, get_tempfile_path(f)) < 0)
 			goto out;
 		if (rename_tempfile(&f, path) < 0)
 			goto out;
diff --git a/setup.c b/setup.c
index aa65b93f53..71a3d66f05 100644
--- a/setup.c
+++ b/setup.c
@@ -2088,7 +2088,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
 	 * with the way the namespace under .git/ is organized, should
 	 * be really carefully chosen.
 	 */
-	safe_create_dir(path->buf, 1);
+	safe_create_dir(the_repository, path->buf, 1);
 	while ((de = readdir(dir)) != NULL) {
 		struct stat st_git, st_template;
 		int exists = 0;
@@ -2352,7 +2352,7 @@ static int create_default_files(const char *template_path,
 	 * shared-repository settings, we would need to fix them up.
 	 */
 	if (repo_settings_get_shared_repository(the_repository)) {
-		adjust_shared_perm(repo_get_git_dir(the_repository));
+		adjust_shared_perm(the_repository, repo_get_git_dir(the_repository));
 	}
 
 	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
@@ -2413,15 +2413,15 @@ static void create_object_directory(void)
 	strbuf_addstr(&path, repo_get_object_directory(the_repository));
 	baselen = path.len;
 
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/pack");
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_setlen(&path, baselen);
 	strbuf_addstr(&path, "/info");
-	safe_create_dir(path.buf, 1);
+	safe_create_dir(the_repository, path.buf, 1);
 
 	strbuf_release(&path);
 }
@@ -2588,7 +2588,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 	 */
 	git_config(platform_core_config, NULL);
 
-	safe_create_dir(git_dir, 0);
+	safe_create_dir(the_repository, git_dir, 0);
 
 	reinit = create_default_files(template_dir, original_git_dir,
 				      &repo_fmt, init_shared_repository);
diff --git a/tempfile.c b/tempfile.c
index ed88cf8431..82dfa3d82f 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -42,6 +42,8 @@
  * file created by its parent.
  */
 
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "abspath.h"
 #include "path.h"
@@ -148,7 +150,7 @@ struct tempfile *create_tempfile_mode(const char *path, int mode)
 		return NULL;
 	}
 	activate_tempfile(tempfile);
-	if (adjust_shared_perm(tempfile->filename.buf)) {
+	if (adjust_shared_perm(the_repository, tempfile->filename.buf)) {
 		int save_errno = errno;
 		error("cannot fix permission bits on %s", tempfile->filename.buf);
 		delete_tempfile(&tempfile);
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 0ea078a5c5..31d16a4c2c 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -207,10 +207,12 @@ static int read_dir_paths(struct string_list *out, const char *path)
 	return 0;
 }
 
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+			 struct strbuf *src, struct strbuf *dst,
 			 enum finalize_object_file_flags flags);
 
-static int migrate_one(struct strbuf *src, struct strbuf *dst,
+static int migrate_one(struct tmp_objdir *t,
+		       struct strbuf *src, struct strbuf *dst,
 		       enum finalize_object_file_flags flags)
 {
 	struct stat st;
@@ -219,11 +221,11 @@ static int migrate_one(struct strbuf *src, struct strbuf *dst,
 		return -1;
 	if (S_ISDIR(st.st_mode)) {
 		if (!mkdir(dst->buf, 0777)) {
-			if (adjust_shared_perm(dst->buf))
+			if (adjust_shared_perm(t->repo, dst->buf))
 				return -1;
 		} else if (errno != EEXIST)
 			return -1;
-		return migrate_paths(src, dst, flags);
+		return migrate_paths(t, src, dst, flags);
 	}
 	return finalize_object_file_flags(src->buf, dst->buf, flags);
 }
@@ -233,7 +235,8 @@ static int is_loose_object_shard(const char *name)
 	return strlen(name) == 2 && isxdigit(name[0]) && isxdigit(name[1]);
 }
 
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+			 struct strbuf *src, struct strbuf *dst,
 			 enum finalize_object_file_flags flags)
 {
 	size_t src_len = src->len, dst_len = dst->len;
@@ -255,7 +258,7 @@ static int migrate_paths(struct strbuf *src, struct strbuf *dst,
 		if (is_loose_object_shard(name))
 			flags_copy |= FOF_SKIP_COLLISION_CHECK;
 
-		ret |= migrate_one(src, dst, flags_copy);
+		ret |= migrate_one(t, src, dst, flags_copy);
 
 		strbuf_setlen(src, src_len);
 		strbuf_setlen(dst, dst_len);
@@ -283,7 +286,7 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
 	strbuf_addbuf(&src, &t->path);
 	strbuf_addstr(&dst, repo_get_object_directory(t->repo));
 
-	ret = migrate_paths(&src, &dst, 0);
+	ret = migrate_paths(t, &src, &dst, 0);
 
 	strbuf_release(&src);
 	strbuf_release(&dst);

-- 
2.48.1.538.gc4cfc42d60.dirty


^ permalink raw reply related	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 00/16] path: remove dependency on `the_repository`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2025-02-07 11:03   ` [PATCH v2 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
@ 2025-02-07 11:44   ` Karthik Nayak
  2025-02-08 15:31   ` shejialuo
  17 siblings, 0 replies; 64+ messages in thread
From: Karthik Nayak @ 2025-02-07 11:44 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: shejialuo

[-- Attachment #1: Type: text/plain, Size: 920 bytes --]

Patrick Steinhardt <ps@pks.im> writes:

> Hi,
>
> this patch series removes the dependency on `the_repository` from the
> "path" subsystem. The series is structured as follows:
>
>   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
>     repository paths (gitdir, commondir, workdir and submodule paths)
>     and adapts callers accodringly.
>
>   - Patches 6 to 12 drop repository path functions that had an implicit
>     dependency on `the_repository`.
>
>   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
>     on `the_repository`.
>
> Changes in v2:
>   - Rename variable to `wt_gitdir` for the sake of consistency.
>   - Improve several commit messages.
>   - Link to v1: https://lore.kernel.org/r/20250206-b4-pks-path-drop-the-repository-v1-0-4e77f0313206@pks.im
>

Changes look good, I had a look at the range-diff and everything looks
great to me now :)

Thanks

[snip]

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 00/16] path: remove dependency on `the_repository`
  2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2025-02-07 11:44   ` [PATCH v2 00/16] path: remove dependency on `the_repository` Karthik Nayak
@ 2025-02-08 15:31   ` shejialuo
  2025-02-10 18:32     ` Junio C Hamano
  17 siblings, 1 reply; 64+ messages in thread
From: shejialuo @ 2025-02-08 15:31 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Karthik Nayak

On Fri, Feb 07, 2025 at 12:03:25PM +0100, Patrick Steinhardt wrote:
> Hi,
> 
> this patch series removes the dependency on `the_repository` from the
> "path" subsystem. The series is structured as follows:
> 
>   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
>     repository paths (gitdir, commondir, workdir and submodule paths)
>     and adapts callers accodringly.
> 
>   - Patches 6 to 12 drop repository path functions that had an implicit
>     dependency on `the_repository`.
> 
>   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
>     on `the_repository`.
> 
> Changes in v2:
>   - Rename variable to `wt_gitdir` for the sake of consistency.
>   - Improve several commit messages.
>   - Link to v1: https://lore.kernel.org/r/20250206-b4-pks-path-drop-the-repository-v1-0-4e77f0313206@pks.im
> 

My questions for v1 are all answered. Thanks for your explanation. The
range-diff looks good to me.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 00/16] path: remove dependency on `the_repository`
  2025-02-08 15:31   ` shejialuo
@ 2025-02-10 18:32     ` Junio C Hamano
  2025-02-11 10:03       ` shejialuo
  0 siblings, 1 reply; 64+ messages in thread
From: Junio C Hamano @ 2025-02-10 18:32 UTC (permalink / raw)
  To: shejialuo; +Cc: Patrick Steinhardt, git, Karthik Nayak

shejialuo <shejialuo@gmail.com> writes:

> On Fri, Feb 07, 2025 at 12:03:25PM +0100, Patrick Steinhardt wrote:
>> Hi,
>> 
>> this patch series removes the dependency on `the_repository` from the
>> "path" subsystem. The series is structured as follows:
>> 
>>   - Patches 1 to 5 unifyf the interfaces that can be used to retrieve
>>     repository paths (gitdir, commondir, workdir and submodule paths)
>>     and adapts callers accodringly.
>> 
>>   - Patches 6 to 12 drop repository path functions that had an implicit
>>     dependency on `the_repository`.
>> 
>>   - Patches 13 to 16 refactor "path.c"-internal code to stop depending
>>     on `the_repository`.
>> 
>> Changes in v2:
>>   - Rename variable to `wt_gitdir` for the sake of consistency.
>>   - Improve several commit messages.
>>   - Link to v1: https://lore.kernel.org/r/20250206-b4-pks-path-drop-the-repository-v1-0-4e77f0313206@pks.im
>> 
>
> My questions for v1 are all answered. Thanks for your explanation. The
> range-diff looks good to me.

Thanks, all.

By the way, shejialuo, this may textually conflict with your
get_worktrees_without_reading_head() thing, but the resolution
should be obvious.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 00/16] path: remove dependency on `the_repository`
  2025-02-10 18:32     ` Junio C Hamano
@ 2025-02-11 10:03       ` shejialuo
  0 siblings, 0 replies; 64+ messages in thread
From: shejialuo @ 2025-02-11 10:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, git, Karthik Nayak

On Mon, Feb 10, 2025 at 10:32:16AM -0800, Junio C Hamano wrote:
> Thanks, all.
> 
> By the way, shejialuo, this may textually conflict with your
> get_worktrees_without_reading_head() thing, but the resolution
> should be obvious.

Thanks for reminding me to notice this. I will take care.

Thanks,
Jialuo

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-07 11:03   ` [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
@ 2025-02-22  7:20     ` Jeff King
  2025-02-24 16:14       ` Junio C Hamano
  0 siblings, 1 reply; 64+ messages in thread
From: Jeff King @ 2025-02-22  7:20 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Karthik Nayak, shejialuo

On Fri, Feb 07, 2025 at 12:03:36PM +0100, Patrick Steinhardt wrote:

>  static timestamp_t rerere_last_used_at(struct rerere_id *id)
>  {
> +	struct strbuf buf = STRBUF_INIT;
>  	struct stat st;
> +	int ret;
> +
> +	ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
>  
> -	return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
> +	strbuf_release(&buf);
> +	return ret;
>  }

This "int ret" should surely be a timestamp_t, no?

-Peff

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-22  7:20     ` Jeff King
@ 2025-02-24 16:14       ` Junio C Hamano
  2025-02-24 22:19         ` Jeff King
  0 siblings, 1 reply; 64+ messages in thread
From: Junio C Hamano @ 2025-02-24 16:14 UTC (permalink / raw)
  To: Jeff King; +Cc: Patrick Steinhardt, git, Karthik Nayak, shejialuo

Jeff King <peff@peff.net> writes:

> On Fri, Feb 07, 2025 at 12:03:36PM +0100, Patrick Steinhardt wrote:
>
>>  static timestamp_t rerere_last_used_at(struct rerere_id *id)
>>  {
>> +	struct strbuf buf = STRBUF_INIT;
>>  	struct stat st;
>> +	int ret;
>> +
>> +	ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
>>  
>> -	return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
>> +	strbuf_release(&buf);
>> +	return ret;
>>  }
>
> This "int ret" should surely be a timestamp_t, no?
>
> -Peff

Indeeeeeed.  Thanks for a careful reading.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-24 16:14       ` Junio C Hamano
@ 2025-02-24 22:19         ` Jeff King
  2025-02-24 22:50           ` Junio C Hamano
  0 siblings, 1 reply; 64+ messages in thread
From: Jeff King @ 2025-02-24 22:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, git, Karthik Nayak, shejialuo

On Mon, Feb 24, 2025 at 08:14:43AM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > On Fri, Feb 07, 2025 at 12:03:36PM +0100, Patrick Steinhardt wrote:
> >
> >>  static timestamp_t rerere_last_used_at(struct rerere_id *id)
> >>  {
> >> +	struct strbuf buf = STRBUF_INIT;
> >>  	struct stat st;
> >> +	int ret;
> >> +
> >> +	ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
> >>  
> >> -	return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
> >> +	strbuf_release(&buf);
> >> +	return ret;
> >>  }
> >
> > This "int ret" should surely be a timestamp_t, no?
> >
> > -Peff
> 
> Indeeeeeed.  Thanks for a careful reading.

Less reading, and more merge resolution. ;) (I had a similar series to
Patrick's that was half-done from a few months ago).

I see you added a fixup! to the topic. Note that rerere_created_at()
needs the same treatment, too.

One side note: using timestamp_t here should get us the same behavior
that the original had before this patch. But I'm not sure the original
was entirely correct. st_mtime is a time_t, so we are assuming the
implicit cast is OK. Our timestamp_t tries to be at least as long as
time_t, so I think we are OK for the future. For very old timestamps, it
is probably wrong (since time_t is usually signed, and timestamp_t is
not yet).

It's mostly academic, though, unless your filesystem has rerere files
before 1970. So I think we can probably just ignore it (and I do still
hope eventually to support negative values with timestamp_t).

-Peff

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-24 22:19         ` Jeff King
@ 2025-02-24 22:50           ` Junio C Hamano
  2025-02-24 23:10             ` Jeff King
  0 siblings, 1 reply; 64+ messages in thread
From: Junio C Hamano @ 2025-02-24 22:50 UTC (permalink / raw)
  To: Jeff King; +Cc: Patrick Steinhardt, git, Karthik Nayak, shejialuo

Jeff King <peff@peff.net> writes:

> One side note: using timestamp_t here should get us the same behavior
> that the original had before this patch. But I'm not sure the original
> was entirely correct. st_mtime is a time_t, so we are assuming the
> implicit cast is OK. Our timestamp_t tries to be at least as long as
> time_t, so I think we are OK for the future. For very old timestamps, it
> is probably wrong (since time_t is usually signed, and timestamp_t is
> not yet).
>
> It's mostly academic, though, unless your filesystem has rerere files
> before 1970. So I think we can probably just ignore it (and I do still
> hope eventually to support negative values with timestamp_t).

True.

I do not think timestamp_t is appropriate for rerere records,
actually.  The reason why we have timestamp_t is for things like the
author dates that can be arbitrarily and deliberately set to any
historical times, e.g. long before the committer was born.

Unlike that, the timestamps we are dealing with with rerere records
are the times on the filesystem when these rerere records were
created and/or used so whatever stat() gives us for st_mtime
(i.e. time_t) is a lot more appropriate.

I'd probably leave a #leftoverbits here; we should vet our use of
timestamp_t to see if we are not overusing the type.  Roughly, the
timestamps we may record in the commit and the tag objects should be
timestamp_t, but the time we get from the filesystem and only
compared with another timestamp the same way should use appropriate
system-defined type, which is likely to be time_t, as not everybody
may have struct timespec, and file expiration should not need
nanoseconds precision.

Thanks.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-24 22:50           ` Junio C Hamano
@ 2025-02-24 23:10             ` Jeff King
  2025-02-24 23:14               ` Junio C Hamano
  0 siblings, 1 reply; 64+ messages in thread
From: Jeff King @ 2025-02-24 23:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, git, Karthik Nayak, shejialuo

On Mon, Feb 24, 2025 at 02:50:32PM -0800, Junio C Hamano wrote:

> I do not think timestamp_t is appropriate for rerere records,
> actually.  The reason why we have timestamp_t is for things like the
> author dates that can be arbitrarily and deliberately set to any
> historical times, e.g. long before the committer was born.
> 
> Unlike that, the timestamps we are dealing with with rerere records
> are the times on the filesystem when these rerere records were
> created and/or used so whatever stat() gives us for st_mtime
> (i.e. time_t) is a lot more appropriate.
> 
> I'd probably leave a #leftoverbits here; we should vet our use of
> timestamp_t to see if we are not overusing the type.  Roughly, the
> timestamps we may record in the commit and the tag objects should be
> timestamp_t, but the time we get from the filesystem and only
> compared with another timestamp the same way should use appropriate
> system-defined type, which is likely to be time_t, as not everybody
> may have struct timespec, and file expiration should not need
> nanoseconds precision.

Yes, I was going to suggest using time_t (or even timespec) consistently
here, but...I think it is a little trickier than that.

We will compare these mtimes not just with themselves, but with a cutoff
time that we get from the user, via repo_config_get_expiry_in_days().
And that's parsing lots of formats into a timestamp_t. So either we need
a parallel universe of functions that operate on time_t (to use for
things we expect to only interact with filesystem times), or at some
point we have to interact between time_t and timestamp_t.

I hope you'll agree that that the "parallel universe" direction is too
gross to contemplate. ;)

So the only question is how and when to convert from time_t to
timestamp_t. I think our general strategy for when has been "as soon as
possible" which makes sense to me (most of the code only has to deal
with our one type). So I think doing it right after the stat() here
makes sense.

The "how" should probably not be "do an implicit cast". We have checks
for overflow, etc, in gm_time_t(), though I'm not sure what the error
handling should be for a case like this (that function just calls die(),
which might not be ideal).

All of which is to say it might be a bit more than a #leftoverbits.

-Peff

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-24 23:10             ` Jeff King
@ 2025-02-24 23:14               ` Junio C Hamano
  2025-02-25  6:24                 ` Patrick Steinhardt
  0 siblings, 1 reply; 64+ messages in thread
From: Junio C Hamano @ 2025-02-24 23:14 UTC (permalink / raw)
  To: Jeff King; +Cc: Patrick Steinhardt, git, Karthik Nayak, shejialuo

Jeff King <peff@peff.net> writes:

> So the only question is how and when to convert from time_t to
> timestamp_t. I think our general strategy for when has been "as soon as
> possible" which makes sense to me (most of the code only has to deal
> with our one type). So I think doing it right after the stat() here
> makes sense.

OK.

> All of which is to say it might be a bit more than a #leftoverbits.

OK.

Thanks for a dose of sanity.

^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer
  2025-02-24 23:14               ` Junio C Hamano
@ 2025-02-25  6:24                 ` Patrick Steinhardt
  0 siblings, 0 replies; 64+ messages in thread
From: Patrick Steinhardt @ 2025-02-25  6:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git, Karthik Nayak, shejialuo

On Mon, Feb 24, 2025 at 03:14:34PM -0800, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
> 
> > So the only question is how and when to convert from time_t to
> > timestamp_t. I think our general strategy for when has been "as soon as
> > possible" which makes sense to me (most of the code only has to deal
> > with our one type). So I think doing it right after the stat() here
> > makes sense.
> 
> OK.
> 
> > All of which is to say it might be a bit more than a #leftoverbits.
> 
> OK.
> 
> Thanks for a dose of sanity.

Indeed, thanks for catching this and the discussion! I'll include the
fixup commit and resend a v3.

Patrick

^ permalink raw reply	[flat|nested] 64+ messages in thread

end of thread, other threads:[~2025-02-25  6:24 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
2025-02-06 11:17   ` Karthik Nayak
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06 14:21   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
2025-02-06 11:53   ` Karthik Nayak
2025-02-07  6:15     ` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
2025-02-06 12:05   ` Karthik Nayak
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-07  7:04       ` Karthik Nayak
2025-02-06 15:03   ` shejialuo
2025-02-06  7:58 ` [PATCH 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
2025-02-07  7:15   ` Karthik Nayak
2025-02-07 10:49     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
2025-02-06 15:54   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-06 16:01   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
2025-02-06 16:14 ` [PATCH 00/16] path: remove dependency on `the_repository` shejialuo
2025-02-07  6:16   ` Patrick Steinhardt
2025-02-07  8:17 ` Karthik Nayak
2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
2025-02-22  7:20     ` Jeff King
2025-02-24 16:14       ` Junio C Hamano
2025-02-24 22:19         ` Jeff King
2025-02-24 22:50           ` Junio C Hamano
2025-02-24 23:10             ` Jeff King
2025-02-24 23:14               ` Junio C Hamano
2025-02-25  6:24                 ` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
2025-02-07 11:44   ` [PATCH v2 00/16] path: remove dependency on `the_repository` Karthik Nayak
2025-02-08 15:31   ` shejialuo
2025-02-10 18:32     ` Junio C Hamano
2025-02-11 10:03       ` shejialuo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).