* Re: [PATCH v7 0/5] history: add squash subcommand to fold a range
From: Harald Nordgren @ 2026-07-07 7:51 UTC (permalink / raw)
To: phillip.wood
Cc: Harald Nordgren via GitGitGadget, git, Patrick Steinhardt,
Junio C Hamano, Matt Hunter
In-Reply-To: <5a5dbfae-4525-4b00-9e44-936be606ee85@gmail.com>
> There was some discussion [1] about making that the default and renaming
> it - was that overlooked? If not it would be helpful to comment on those
> discussions to explain why you don't think it is a good idea.
Not overlooked, but I side-stepped it because the discussion died
down, and yes I don't agree that it needs to be the default. I could
have mentioned my thinking in the cover letter.
> > now builds the same editor template git rebase -i shows
> > for a squash (a combination of N commits banner with each folded message
> > under its own header) and follows autosquash for markers: a fixup!
> > message falls out (commented under a will be skipped header), while a
> > squash! or amend! keeps its body with only the marker subject commented
> > so its remark can be reworded in. Only the message text is affected,
> > every commit's changes are always folded in.
>
> Rebase re-orders commits so that fixups immediately follow their target
> - do you do that here? I think that is very relevant because here we may
> be dealing with several different commits each being targeted by a set
> of fixups and presenting them mixed together will be confusing.
No, I'm not doing that now, but I can take a look at that.
> I think it should allow squashing a bunch of fixups together though. I
> thought there was a plan [3] to refuse to squash a fixup unless the
> range included its target.
I attempted this with reject_fixupish_oldest(), assuming only the
first commit needs to be checked as not being a fixup/squash/amend.
But now I realize that maybe we need to check all of the commits, and
also check if the target is in the range or not. It just makes the
logic a lot bigger.
> The range-diff does not show any input sanitization - what happens when
> the user passes "--reverse" for example? As I said in [4] we should copy
> what "git replay" does to sanity check the rev-list options, otherwise
> we've got no idea whether the parent of the first commit returned by
> get_revision() is the commit we want to use as the parent of the
> squashed commit.
Yeah, good point.
Harald
^ permalink raw reply
* [PATCH v2 13/13] setup: mark `set_git_work_tree()` as file-local
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
In the preceding commit we have removed the last callers of
`set_git_work_tree()` that is located outside of "setup.c". Remove its
declaration and mark the function as file-local.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 2 +-
setup.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/setup.c b/setup.c
index 683b8e65a2..b632c2bf8c 100644
--- a/setup.c
+++ b/setup.c
@@ -1904,7 +1904,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
* primarily to support git-clone to work in a new repository it just
* created, and is not meant to flip between different work trees.
*/
-void set_git_work_tree(struct repository *repo, const char *new_work_tree)
+static void set_git_work_tree(struct repository *repo, const char *new_work_tree)
{
if (repo->worktree_initialized) {
struct strbuf realpath = STRBUF_INIT;
diff --git a/setup.h b/setup.h
index bf3e3f3ea6..bb24ee8f0f 100644
--- a/setup.h
+++ b/setup.h
@@ -96,8 +96,6 @@ static inline int discover_git_directory(struct strbuf *commondir,
return 0;
}
-void set_git_work_tree(struct repository *repo, const char *tree);
-
/* Flags that can be passed to `enter_repo()`. */
enum {
/*
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 12/13] setup: pass worktree to `init_db()`
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
In the preceding commits we have refactored how we discover and set up
repositories so that we cannot end up with partially-configured repos.
Instead, we apply the gitdir, worktree and repository format in a single
location, only.
Initializing a new repository has the same antipattern though: while
most of the information for the new repository is passed via parameters,
the work tree is instead propagated by configuring the repository's work
tree.
Refactor the code so that we also pass the work tree as an explicit
parameter. Like this, configuration fo the repository happens in a
single spot, too, just as with repository discovery.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/clone.c | 8 ++++----
builtin/init-db.c | 34 ++++++++++------------------------
setup.c | 7 ++++++-
setup.h | 4 +++-
4 files changed, 23 insertions(+), 30 deletions(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index d60d1b60bc..9d08cd8722 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1116,7 +1116,6 @@ int cmd_clone(int argc,
die_errno(_("could not create work tree dir '%s'"),
work_tree);
junk_work_tree = work_tree;
- set_git_work_tree(the_repository, work_tree);
}
if (real_git_dir) {
@@ -1186,9 +1185,10 @@ int cmd_clone(int argc,
* repository, and reference backends may persist that information into
* their on-disk data structures.
*/
- init_db(the_repository, git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
- ref_storage_format, NULL,
- do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
+ init_db(the_repository, git_dir, real_git_dir, work_tree, option_template,
+ GIT_HASH_UNKNOWN, ref_storage_format, NULL,
+ do_not_override_repo_unix_permissions,
+ INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
if (real_git_dir) {
free((char *)git_dir);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 566732c9f4..e96b1283b7 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -231,39 +231,25 @@ int cmd_init_db(int argc,
if (!bare) {
const char *git_dir_parent = strrchr(git_dir, '/');
- if (work_tree) {
- set_git_work_tree(the_repository, work_tree);
- } else {
- char *work_tree_cfg = NULL;
-
+ if (!work_tree) {
if (git_dir_parent) {
char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
- work_tree_cfg = real_pathdup(rel, 1);
+ work_tree = real_pathdup(rel, 1);
free(rel);
+ } else {
+ work_tree = xgetcwd();
}
-
- if (!work_tree_cfg)
- work_tree_cfg = xgetcwd();
-
- set_git_work_tree(the_repository, work_tree_cfg);
-
- free(work_tree_cfg);
}
- if (access(repo_get_work_tree(the_repository), X_OK))
- die_errno (_("Cannot access work tree '%s'"),
- repo_get_work_tree(the_repository));
- }
- else {
- if (real_git_dir)
- die(_("--separate-git-dir incompatible with bare repository"));
- if (work_tree)
- set_git_work_tree(the_repository, work_tree);
+ if (access(work_tree, X_OK))
+ die_errno (_("Cannot access work tree '%s'"), work_tree);
+ } else if (real_git_dir) {
+ die(_("--separate-git-dir incompatible with bare repository"));
}
flags |= INIT_DB_EXIST_OK;
- ret = init_db(the_repository, git_dir, real_git_dir, template_dir, hash_algo,
- ref_storage_format, initial_branch,
+ ret = init_db(the_repository, git_dir, real_git_dir, work_tree,
+ template_dir, hash_algo, ref_storage_format, initial_branch,
init_shared_repository, flags);
free(template_dir_to_free);
diff --git a/setup.c b/setup.c
index 088e7b85f7..683b8e65a2 100644
--- a/setup.c
+++ b/setup.c
@@ -2823,7 +2823,9 @@ static void repository_format_configure(struct repository_format *repo_fmt,
}
int init_db(struct repository *repo,
- const char *git_dir, const char *real_git_dir,
+ const char *git_dir,
+ const char *real_git_dir,
+ const char *worktree,
const char *template_dir, int hash,
enum ref_storage_format ref_storage_format,
const char *initial_branch,
@@ -2852,6 +2854,9 @@ int init_db(struct repository *repo,
git_dir = repo_get_git_dir(repo);
}
+ if (worktree)
+ set_git_work_tree(repo, worktree);
+
/*
* Check to see if the repository version is right.
* Note that a newly created repository does not have
diff --git a/setup.h b/setup.h
index c01a244fe9..bf3e3f3ea6 100644
--- a/setup.h
+++ b/setup.h
@@ -263,7 +263,9 @@ const char *get_template_dir(const char *option_template);
#define INIT_DB_SKIP_REFDB (1 << 2)
int init_db(struct repository *repo,
- const char *git_dir, const char *real_git_dir,
+ const char *git_dir,
+ const char *real_git_dir,
+ const char *worktree,
const char *template_dir, int hash_algo,
enum ref_storage_format ref_storage_format,
const char *initial_branch, int init_shared_repository,
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 11/13] setup: drop redundant configuration of `startup_info->have_repository`
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
In `init_db()` we set `startup_info->have_repository` twice: once before
reading and applying the repository format and once after. This is
redundant though, as configuring the repository format does not rely on
this variable at all.
Remove the first such site. While at it, fix up formatting a bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/setup.c b/setup.c
index d4de8c2900..088e7b85f7 100644
--- a/setup.c
+++ b/setup.c
@@ -2847,12 +2847,10 @@ int init_db(struct repository *repo,
apply_and_export_relative_gitdir(repo, real_git_dir, 1);
git_dir = repo_get_git_dir(repo);
separate_git_dir(git_dir, original_git_dir);
- }
- else {
+ } else {
apply_and_export_relative_gitdir(repo, git_dir, 1);
git_dir = repo_get_git_dir(repo);
}
- startup_info->have_repository = 1;
/*
* Check to see if the repository version is right.
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 10/13] setup: make repository discovery self-contained
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
In the preceding commits we have introduced a separate repository
discovery phase and refactored the logic so that we have two clear
phases:
1. Repository discovery, which doesn't modify the repository itself at
all.
2. Repository configuration, which takes the information we have
discovered to set up the repository.
Extract the first phase into a new function `repo_discover()` to further
stress these two different phases.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 43 +++++++++++++++++++++++++------------------
1 file changed, 25 insertions(+), 18 deletions(-)
diff --git a/setup.c b/setup.c
index d1db0a4ca0..d4de8c2900 100644
--- a/setup.c
+++ b/setup.c
@@ -1922,20 +1922,10 @@ void set_git_work_tree(struct repository *repo, const char *new_work_tree)
repo_set_worktree(repo, new_work_tree);
}
-const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
+static void repo_discover(struct repo_discovery *discovery, int *nongit_ok)
{
struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
- struct repo_discovery discovery = REPO_DISCOVERY_INIT;
-
- /*
- * We may have read an incomplete configuration before
- * setting-up the git directory. If so, clear the cache so
- * that the next queries to the configuration reload complete
- * configuration (including the per-repo config file that we
- * ignored previously).
- */
- repo_config_clear(repo);
/*
* Let's assume that we are in a git repository.
@@ -1951,19 +1941,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {
case GIT_DIR_EXPLICIT:
- repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
+ repo_discover_explicit_gitdir(discovery, gitdir.buf, &cwd,
nongit_ok);
break;
case GIT_DIR_DISCOVERED:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
+ repo_discover_implicit_gitdir(discovery, gitdir.buf, &cwd, dir.len,
nongit_ok);
break;
case GIT_DIR_BARE:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- repo_discover_bare_gitdir(&discovery, &cwd, dir.len, nongit_ok);
+ repo_discover_bare_gitdir(discovery, &cwd, dir.len, nongit_ok);
break;
case GIT_DIR_HIT_CEILING:
if (!nongit_ok)
@@ -2013,6 +2003,27 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
BUG("unhandled repo_discovery_find_dir() result");
}
+ strbuf_release(&dir);
+ strbuf_release(&cwd);
+ strbuf_release(&gitdir);
+ strbuf_release(&report);
+}
+
+const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
+{
+ struct repo_discovery discovery = REPO_DISCOVERY_INIT;
+
+ /*
+ * We may have read an incomplete configuration before
+ * setting-up the git directory. If so, clear the cache so
+ * that the next queries to the configuration reload complete
+ * configuration (including the per-repo config file that we
+ * ignored previously).
+ */
+ repo_config_clear(repo);
+
+ repo_discover(&discovery, nongit_ok);
+
/*
* At this point, nongit_ok is stable. If it is non-NULL and points
* to a non-zero value, then this means that we haven't found a
@@ -2104,10 +2115,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
setup_original_cwd(repo);
repo_discovery_release(&discovery);
- strbuf_release(&dir);
- strbuf_release(&cwd);
- strbuf_release(&gitdir);
- strbuf_release(&report);
return repo->prefix;
}
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 09/13] setup: propagate prefix via repository discovery
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
In the preceding commits we have started to propagate all information
required for the configuration of the repository via a new `struct
repo_discovery`. The only exception is the repository's prefix, which we
still return via the return parameter.
This is conceptually fine, but somewhat inconsistent. Refactor this to
instead propagate the prefix via the repository discovery, too.
While at it, drop a static variable in `repo_discover_bare_gitdir()`.
We apply its value to the repository discovery anyway, so we don't have
to keep it around afterwards anymore.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 101 +++++++++++++++++++++++++++++-----------------------------------
1 file changed, 45 insertions(+), 56 deletions(-)
diff --git a/setup.c b/setup.c
index f8e4cf100b..d1db0a4ca0 100644
--- a/setup.c
+++ b/setup.c
@@ -1094,6 +1094,7 @@ struct repo_discovery {
struct repository_format format;
char *gitdir;
char *worktree;
+ char *prefix;
};
#define REPO_DISCOVERY_INIT { \
@@ -1105,6 +1106,7 @@ static void repo_discovery_release(struct repo_discovery *r)
clear_repository_format(&r->format);
free(r->gitdir);
free(r->worktree);
+ free(r->prefix);
}
static void repo_discovery_set_gitdir(struct repo_discovery *r,
@@ -1128,10 +1130,10 @@ static void repo_discovery_set_worktree(struct repo_discovery *r,
r->worktree = real_pathdup(worktree, 1);
}
-static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,
- const char *gitdirenv,
- struct strbuf *cwd,
- int *nongit_ok)
+static void repo_discover_explicit_gitdir(struct repo_discovery *discovery,
+ const char *gitdirenv,
+ struct strbuf *cwd,
+ int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
char *gitfile;
@@ -1149,16 +1151,13 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
if (!is_git_directory(gitdirenv)) {
if (nongit_ok) {
*nongit_ok = 1;
- free(gitfile);
- return NULL;
+ goto out;
}
die(_("not a git repository: '%s'"), gitdirenv);
}
- if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) {
- free(gitfile);
- return NULL;
- }
+ if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok))
+ goto out;
/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
if (work_tree_env) {
@@ -1173,8 +1172,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
} else if (discovery->format.is_bare > 0) {
/* #18, #26 */
repo_discovery_set_gitdir(discovery, gitdirenv, 0);
- free(gitfile);
- return NULL;
+ goto out;
} else if (discovery->format.work_tree) { /* #6, #14 */
if (is_absolute_path(discovery->format.work_tree)) {
repo_discovery_set_worktree(discovery, discovery->format.work_tree);
@@ -1193,8 +1191,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
/* #16d */
repo_discovery_set_gitdir(discovery, gitdirenv, 0);
- free(gitfile);
- return NULL;
+ goto out;
} else { /* #2, #10 */
repo_discovery_set_worktree(discovery, ".");
}
@@ -1202,8 +1199,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
/* both the worktree and cwd are already normalized */
if (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */
repo_discovery_set_gitdir(discovery, gitdirenv, 0);
- free(gitfile);
- return NULL;
+ goto out;
}
offset = dir_inside_of(cwd->buf, discovery->worktree);
@@ -1211,38 +1207,37 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
repo_discovery_set_gitdir(discovery, gitdirenv, 1);
if (chdir(discovery->worktree))
die_errno(_("cannot chdir to '%s'"), discovery->worktree);
- strbuf_addch(cwd, '/');
- free(gitfile);
- return cwd->buf + offset;
+ discovery->prefix = xstrfmt("%s/", cwd->buf + offset);
+ goto out;
}
/* cwd outside worktree */
repo_discovery_set_gitdir(discovery, gitdirenv, 0);
+
+out:
free(gitfile);
- return NULL;
}
-static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,
- const char *gitdir,
- struct strbuf *cwd, int offset,
- int *nongit_ok)
+static void repo_discover_implicit_gitdir(struct repo_discovery *discovery,
+ const char *gitdir,
+ struct strbuf *cwd, int offset,
+ int *nongit_ok)
{
if (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok))
- return NULL;
+ return;
/* --work-tree is set without --git-dir; use discovered one */
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
char *to_free = NULL;
- const char *ret;
if (offset != cwd->len && !is_absolute_path(gitdir))
gitdir = to_free = real_pathdup(gitdir, 1);
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- ret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,
- nongit_ok);
+ repo_discover_explicit_gitdir(discovery, gitdir, cwd,
+ nongit_ok);
free(to_free);
- return ret;
+ return;
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
@@ -1250,7 +1245,7 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
repo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- return NULL;
+ return;
}
/* #0, #1, #5, #8, #9, #12, #13 */
@@ -1258,37 +1253,34 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
repo_discovery_set_gitdir(discovery, gitdir, 0);
if (offset >= cwd->len)
- return NULL;
+ return;
/* Make "offset" point past the '/' (already the case for root dirs) */
if (offset != offset_1st_component(cwd->buf))
offset++;
- /* Add a '/' at the end */
- strbuf_addch(cwd, '/');
- return cwd->buf + offset;
+ discovery->prefix = xstrfmt("%s/", cwd->buf + offset);
}
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
-static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
- struct strbuf *cwd, int offset,
- int *nongit_ok)
+static void repo_discover_bare_gitdir(struct repo_discovery *discovery,
+ struct strbuf *cwd, int offset,
+ int *nongit_ok)
{
int root_len;
if (read_and_verify_repository_format(&discovery->format, ".", nongit_ok))
- return NULL;
+ return;
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
/* --work-tree is set without --git-dir; use discovered one */
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
- static const char *gitdir;
-
- gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
+ char *gitdir = offset == cwd->len ? xstrdup(".") : xmemdupz(cwd->buf, offset);
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- return repo_discover_explicit_gitdir(discovery, gitdir, cwd,
- nongit_ok);
+ repo_discover_explicit_gitdir(discovery, gitdir, cwd, nongit_ok);
+ free(gitdir);
+ return;
}
if (offset != cwd->len) {
@@ -1297,10 +1289,9 @@ static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
root_len = offset_1st_component(cwd->buf);
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
repo_discovery_set_gitdir(discovery, cwd->buf, 0);
- }
- else
+ } else {
repo_discovery_set_gitdir(discovery, ".", 0);
- return NULL;
+ }
}
static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
@@ -1936,7 +1927,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
struct repo_discovery discovery = REPO_DISCOVERY_INIT;
- const char *prefix = NULL;
/*
* We may have read an incomplete configuration before
@@ -1961,20 +1951,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {
case GIT_DIR_EXPLICIT:
- prefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
- nongit_ok);
+ repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
+ nongit_ok);
break;
case GIT_DIR_DISCOVERED:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- prefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
- nongit_ok);
+ repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
+ nongit_ok);
break;
case GIT_DIR_BARE:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- prefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,
- nongit_ok);
+ repo_discover_bare_gitdir(&discovery, &cwd, dir.len, nongit_ok);
break;
case GIT_DIR_HIT_CEILING:
if (!nongit_ok)
@@ -2103,10 +2092,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
* out where the repository is, i.e. a preparation
* for calling repo_config_get_bool().
*/
- if (prefix) {
- prefix = precompose_string_if_needed(prefix);
+ if (discovery.prefix) {
+ const char *prefix = precompose_string_if_needed(discovery.prefix);
repo->prefix = xstrdup(prefix);
- setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
+ setenv(GIT_PREFIX_ENVIRONMENT, repo->prefix, 1);
} else {
FREE_AND_NULL(repo->prefix);
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 07/13] setup: move prefix into repository
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
The repository prefix is currently stored in the startup info. This
feels somewhat awkward though, as it is inherently a property of a given
repository.
Move the prefix into the repository accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
builtin/repo.c | 8 ++++----
builtin/rev-parse.c | 5 +++--
builtin/update-index.c | 4 ++--
object-name.c | 4 ++--
repository.c | 1 +
repository.h | 8 ++++++++
setup.c | 6 +++---
setup.h | 1 -
trace.c | 4 ++--
9 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/builtin/repo.c b/builtin/repo.c
index 042d6de558..84e012f83f 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -84,7 +84,7 @@ static int get_path_commondir_absolute(struct repository *repo, struct strbuf *b
if (!common_dir)
return error(_("unable to get common directory"));
- format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+ format_path(buf, common_dir, repo->prefix, PATH_FORMAT_CANONICAL);
return 0;
}
@@ -95,7 +95,7 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
if (!common_dir)
return error(_("unable to get common directory"));
- format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+ format_path(buf, common_dir, repo->prefix, PATH_FORMAT_RELATIVE);
return 0;
}
@@ -106,7 +106,7 @@ static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
if (!git_dir)
return error(_("unable to get git directory"));
- format_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+ format_path(buf, git_dir, repo->prefix, PATH_FORMAT_CANONICAL);
return 0;
}
@@ -117,7 +117,7 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
if (!git_dir)
return error(_("unable to get git directory"));
- format_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+ format_path(buf, git_dir, repo->prefix, PATH_FORMAT_RELATIVE);
return 0;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 5e04b0e2bd..43693454d5 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -255,7 +255,7 @@ static int show_file(const char *arg, int output_prefix)
show_default();
if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
if (output_prefix) {
- const char *prefix = startup_info->prefix;
+ const char *prefix = the_repository->prefix;
char *fname = prefix_filename(prefix, arg);
show(fname);
free(fname);
@@ -832,7 +832,8 @@ int cmd_rev_parse(int argc,
prefix = argv[++i];
if (!prefix)
die(_("--prefix requires an argument"));
- startup_info->prefix = prefix;
+ FREE_AND_NULL(the_repository->prefix);
+ the_repository->prefix = xstrdup(prefix);
output_prefix = 1;
continue;
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 3d6646c318..f43d150eb3 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -875,7 +875,7 @@ static enum parse_opt_result unresolve_callback(
const char *arg, int unset)
{
int *has_errors = opt->value;
- const char *prefix = startup_info->prefix;
+ const char *prefix = the_repository->prefix;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
@@ -896,7 +896,7 @@ static enum parse_opt_result reupdate_callback(
const char *arg, int unset)
{
int *has_errors = opt->value;
- const char *prefix = startup_info->prefix;
+ const char *prefix = the_repository->prefix;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
diff --git a/object-name.c b/object-name.c
index 46159466ac..fc70acc9e0 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1708,8 +1708,8 @@ static char *resolve_relative_path(struct repository *r, const char *rel)
die(_("relative path syntax can't be used outside working tree"));
/* die() inside prefix_path() if resolved path is outside worktree */
- return prefix_path(the_repository, startup_info->prefix,
- startup_info->prefix ? strlen(startup_info->prefix) : 0,
+ return prefix_path(the_repository, the_repository->prefix,
+ the_repository->prefix ? strlen(the_repository->prefix) : 0,
rel);
}
diff --git a/repository.c b/repository.c
index 73d80bcffd..2ef0778846 100644
--- a/repository.c
+++ b/repository.c
@@ -376,6 +376,7 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->gitdir);
FREE_AND_NULL(repo->commondir);
+ FREE_AND_NULL(repo->prefix);
FREE_AND_NULL(repo->graft_file);
FREE_AND_NULL(repo->index_file);
FREE_AND_NULL(repo->worktree);
diff --git a/repository.h b/repository.h
index 7d649e32e7..b767307911 100644
--- a/repository.h
+++ b/repository.h
@@ -52,6 +52,14 @@ struct repository {
*/
char *commondir;
+ /*
+ * The "prefix", a path to the current working directory relative to
+ * the work tree root, or NULL, if the current working directory is not
+ * a strict subdirectory of the work tree root. The prefix always ends
+ * with a '/' character.
+ */
+ char *prefix;
+
/*
* Holds any information related to accessing the raw object content.
*/
diff --git a/setup.c b/setup.c
index b755693572..6cc9fa2de8 100644
--- a/setup.c
+++ b/setup.c
@@ -2030,7 +2030,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
* repository and that the caller expects startup_info to reflect
* this.
*
- * Regardless of the state of nongit_ok, startup_info->prefix and
+ * Regardless of the state of nongit_ok, the_repository->prefix and
* the GIT_PREFIX environment variable must always match. For details
* see Documentation/config/alias.adoc.
*/
@@ -2105,10 +2105,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
*/
if (prefix) {
prefix = precompose_string_if_needed(prefix);
- startup_info->prefix = prefix;
+ repo->prefix = xstrdup(prefix);
setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
} else {
- startup_info->prefix = NULL;
+ FREE_AND_NULL(repo->prefix);
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}
diff --git a/setup.h b/setup.h
index b9fd96bea6..c01a244fe9 100644
--- a/setup.h
+++ b/setup.h
@@ -299,7 +299,6 @@ struct startup_info {
bool force_bare_repository;
int have_repository;
- const char *prefix;
const char *original_cwd;
};
extern struct startup_info *startup_info;
diff --git a/trace.c b/trace.c
index 9b99460db8..515b99e7f5 100644
--- a/trace.c
+++ b/trace.c
@@ -299,7 +299,7 @@ static const char *quote_crnl(const char *path)
void trace_repo_setup(struct repository *r)
{
- const char *git_work_tree, *prefix = startup_info->prefix;
+ const char *git_work_tree, *prefix = r->prefix;
char *cwd;
if (!trace_want(&trace_setup_key))
@@ -310,7 +310,7 @@ void trace_repo_setup(struct repository *r)
if (!(git_work_tree = repo_get_work_tree(r)))
git_work_tree = "(null)";
- if (!startup_info->prefix)
+ if (!r->prefix)
prefix = "(null)";
trace_printf_key(&trace_setup_key, "setup: git_dir: %s\n", quote_crnl(repo_get_git_dir(r)));
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 08/13] setup: drop static `cwd` variable
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
The current working directory is stored as part of a static strbuf
variable. This variable had to have a lifetime longer than its
containing function because the value we return typically points into
that buffer.
In the preceding commit we have moved the prefix into the repository
though. Consequently, we can now return the repository's prefix instead
of the local one and thus properly manage the lifecycle of this local
variable.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/setup.c b/setup.c
index 6cc9fa2de8..f8e4cf100b 100644
--- a/setup.c
+++ b/setup.c
@@ -1933,7 +1933,7 @@ void set_git_work_tree(struct repository *repo, const char *new_work_tree)
const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
{
- static struct strbuf cwd = STRBUF_INIT;
+ struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
struct repo_discovery discovery = REPO_DISCOVERY_INIT;
const char *prefix = NULL;
@@ -2116,9 +2116,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
repo_discovery_release(&discovery);
strbuf_release(&dir);
+ strbuf_release(&cwd);
strbuf_release(&gitdir);
strbuf_release(&report);
- return prefix;
+ return repo->prefix;
}
int git_config_perm(const char *var, const char *value)
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 06/13] setup: embed repository format in discovery
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
All functions related to repository discovery receive both a `struct
repository_discovery` and `struct repository_format` as input, and the
expectation is that both will be populated. Refactor this so that the
repository format is part of the discovery result.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 60 +++++++++++++++++++++++++++++-------------------------------
1 file changed, 29 insertions(+), 31 deletions(-)
diff --git a/setup.c b/setup.c
index f713d024f7..b755693572 100644
--- a/setup.c
+++ b/setup.c
@@ -1091,14 +1091,18 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char
}
struct repo_discovery {
+ struct repository_format format;
char *gitdir;
char *worktree;
};
-#define REPO_DISCOVERY_INIT { 0 }
+#define REPO_DISCOVERY_INIT { \
+ .format = REPOSITORY_FORMAT_INIT, \
+}
static void repo_discovery_release(struct repo_discovery *r)
{
+ clear_repository_format(&r->format);
free(r->gitdir);
free(r->worktree);
}
@@ -1127,7 +1131,6 @@ static void repo_discovery_set_worktree(struct repo_discovery *r,
static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,
const char *gitdirenv,
struct strbuf *cwd,
- struct repository_format *repo_fmt,
int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
@@ -1152,7 +1155,7 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
die(_("not a git repository: '%s'"), gitdirenv);
}
- if (read_and_verify_repository_format(repo_fmt, gitdirenv, nongit_ok)) {
+ if (read_and_verify_repository_format(&discovery->format, gitdirenv, nongit_ok)) {
free(gitfile);
return NULL;
}
@@ -1165,22 +1168,22 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
* bogus where we have both "core.worktree" and "core.bare", so
* we have to explicitly unset the configuration.
*/
- FREE_AND_NULL(repo_fmt->work_tree);
+ FREE_AND_NULL(discovery->format.work_tree);
repo_discovery_set_worktree(discovery, work_tree_env);
- } else if (repo_fmt->is_bare > 0) {
+ } else if (discovery->format.is_bare > 0) {
/* #18, #26 */
repo_discovery_set_gitdir(discovery, gitdirenv, 0);
free(gitfile);
return NULL;
- } else if (repo_fmt->work_tree) { /* #6, #14 */
- if (is_absolute_path(repo_fmt->work_tree)) {
- repo_discovery_set_worktree(discovery, repo_fmt->work_tree);
+ } else if (discovery->format.work_tree) { /* #6, #14 */
+ if (is_absolute_path(discovery->format.work_tree)) {
+ repo_discovery_set_worktree(discovery, discovery->format.work_tree);
} else {
char *core_worktree;
if (chdir(gitdirenv))
die_errno(_("cannot chdir to '%s'"), gitdirenv);
- if (chdir(repo_fmt->work_tree))
- die_errno(_("cannot chdir to '%s'"), repo_fmt->work_tree);
+ if (chdir(discovery->format.work_tree))
+ die_errno(_("cannot chdir to '%s'"), discovery->format.work_tree);
core_worktree = xgetcwd();
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
@@ -1222,14 +1225,13 @@ static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,
const char *gitdir,
struct strbuf *cwd, int offset,
- struct repository_format *repo_fmt,
int *nongit_ok)
{
- if (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))
+ if (read_and_verify_repository_format(&discovery->format, gitdir, nongit_ok))
return NULL;
/* --work-tree is set without --git-dir; use discovered one */
- if (getenv(GIT_WORK_TREE_ENVIRONMENT) || repo_fmt->work_tree) {
+ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
char *to_free = NULL;
const char *ret;
@@ -1238,13 +1240,13 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
ret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,
- repo_fmt, nongit_ok);
+ nongit_ok);
free(to_free);
return ret;
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
- if (repo_fmt->is_bare > 0) {
+ if (discovery->format.is_bare > 0) {
repo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
@@ -1269,25 +1271,24 @@ static const char *repo_discover_implicit_gitdir(struct repo_discovery *discover
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
struct strbuf *cwd, int offset,
- struct repository_format *repo_fmt,
int *nongit_ok)
{
int root_len;
- if (read_and_verify_repository_format(repo_fmt, ".", nongit_ok))
+ if (read_and_verify_repository_format(&discovery->format, ".", nongit_ok))
return NULL;
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
/* --work-tree is set without --git-dir; use discovered one */
- if (getenv(GIT_WORK_TREE_ENVIRONMENT) || repo_fmt->work_tree) {
+ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || discovery->format.work_tree) {
static const char *gitdir;
gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
return repo_discover_explicit_gitdir(discovery, gitdir, cwd,
- repo_fmt, nongit_ok);
+ nongit_ok);
}
if (offset != cwd->len) {
@@ -1936,7 +1937,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
struct repo_discovery discovery = REPO_DISCOVERY_INIT;
const char *prefix = NULL;
- struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
/*
* We may have read an incomplete configuration before
@@ -1962,19 +1962,19 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {
case GIT_DIR_EXPLICIT:
prefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
- &repo_fmt, nongit_ok);
+ nongit_ok);
break;
case GIT_DIR_DISCOVERED:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
prefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
- &repo_fmt, nongit_ok);
+ nongit_ok);
break;
case GIT_DIR_BARE:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
prefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,
- &repo_fmt, nongit_ok);
+ nongit_ok);
break;
case GIT_DIR_HIT_CEILING:
if (!nongit_ok)
@@ -2078,21 +2078,21 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
if (ref_backend_uri) {
char *format;
- free(repo_fmt.ref_storage_payload);
+ free(discovery.format.ref_storage_payload);
- parse_reference_uri(ref_backend_uri, &format, &repo_fmt.ref_storage_payload);
- repo_fmt.ref_storage_format = ref_storage_format_by_name(format);
- if (repo_fmt.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+ parse_reference_uri(ref_backend_uri, &format, &discovery.format.ref_storage_payload);
+ discovery.format.ref_storage_format = ref_storage_format_by_name(format);
+ if (discovery.format.ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
die(_("unknown ref storage format: '%s'"), format);
free(format);
}
- if (apply_repository_format(repo, &repo_fmt,
+ if (apply_repository_format(repo, &discovery.format,
APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
- clear_repository_format(&repo_fmt);
+ clear_repository_format(&discovery.format);
strbuf_release(&err);
}
}
@@ -2118,8 +2118,6 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
strbuf_release(&dir);
strbuf_release(&gitdir);
strbuf_release(&report);
- clear_repository_format(&repo_fmt);
-
return prefix;
}
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 05/13] setup: introduce explicit repository discovery
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
When setting up the global repository we intermix repository discovery
and repository configuration: we repeatedly call `set_git_work_tree()`
and `apply_and_export_relative_gitdir()` until we're happy with the
result. The result of this is then a partially-configured repository
that we use for further setup.
This process is quite hard to follow, as it's never quite clear which
parts of the repository have been configured already and which haven't.
Furthermore, it means that the repository configuration is distributed
across many different places instead of having it neatly contained in a
single location. Ultimately, this is the reason that we cannot use a
central function like `repo_init()`.
Refactor the logic so that we stop partially-configuring a repository
and instead populate a new `struct repo_discovery`. This allow us to
essentially split repository setup into two phases:
- The first phase only figures out parameters required to configure
the repository.
- The second phase then takes these parameters and applies them to the
repository.
Like this, we'll never end up with a partially-configured repository and
can eventually extend `repo_init()` to handle the full initialization
for us.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 155 ++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 98 insertions(+), 57 deletions(-)
diff --git a/setup.c b/setup.c
index 324a235dd1..f713d024f7 100644
--- a/setup.c
+++ b/setup.c
@@ -1090,14 +1090,47 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char
strbuf_release(&realpath);
}
-static const char *setup_explicit_git_dir(struct repository *repo,
- const char *gitdirenv,
- struct strbuf *cwd,
- struct repository_format *repo_fmt,
- int *nongit_ok)
+struct repo_discovery {
+ char *gitdir;
+ char *worktree;
+};
+
+#define REPO_DISCOVERY_INIT { 0 }
+
+static void repo_discovery_release(struct repo_discovery *r)
+{
+ free(r->gitdir);
+ free(r->worktree);
+}
+
+static void repo_discovery_set_gitdir(struct repo_discovery *r,
+ const char *gitdir,
+ int make_realpath)
+{
+ free(r->gitdir);
+ if (make_realpath) {
+ struct strbuf realpath = STRBUF_INIT;
+ strbuf_realpath(&realpath, gitdir, 1);
+ r->gitdir = strbuf_detach(&realpath, NULL);
+ } else {
+ r->gitdir = xstrdup(gitdir);
+ }
+}
+
+static void repo_discovery_set_worktree(struct repo_discovery *r,
+ const char *worktree)
+{
+ free(r->worktree);
+ r->worktree = real_pathdup(worktree, 1);
+}
+
+static const char *repo_discover_explicit_gitdir(struct repo_discovery *discovery,
+ const char *gitdirenv,
+ struct strbuf *cwd,
+ struct repository_format *repo_fmt,
+ int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
- const char *worktree;
char *gitfile;
int offset;
@@ -1133,15 +1166,15 @@ static const char *setup_explicit_git_dir(struct repository *repo,
* we have to explicitly unset the configuration.
*/
FREE_AND_NULL(repo_fmt->work_tree);
- set_git_work_tree(repo, work_tree_env);
+ repo_discovery_set_worktree(discovery, work_tree_env);
} else if (repo_fmt->is_bare > 0) {
/* #18, #26 */
- apply_and_export_relative_gitdir(repo, gitdirenv, 0);
+ repo_discovery_set_gitdir(discovery, gitdirenv, 0);
free(gitfile);
return NULL;
} else if (repo_fmt->work_tree) { /* #6, #14 */
if (is_absolute_path(repo_fmt->work_tree)) {
- set_git_work_tree(repo, repo_fmt->work_tree);
+ repo_discovery_set_worktree(discovery, repo_fmt->work_tree);
} else {
char *core_worktree;
if (chdir(gitdirenv))
@@ -1151,49 +1184,46 @@ static const char *setup_explicit_git_dir(struct repository *repo,
core_worktree = xgetcwd();
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- set_git_work_tree(repo, core_worktree);
+ repo_discovery_set_worktree(discovery, core_worktree);
free(core_worktree);
}
} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
/* #16d */
- apply_and_export_relative_gitdir(repo, gitdirenv, 0);
+ repo_discovery_set_gitdir(discovery, gitdirenv, 0);
free(gitfile);
return NULL;
} else { /* #2, #10 */
- set_git_work_tree(repo, ".");
+ repo_discovery_set_worktree(discovery, ".");
}
- /* set_git_work_tree() must have been called by now */
- worktree = repo_get_work_tree(repo);
-
- /* both repo_get_work_tree() and cwd are already normalized */
- if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
- apply_and_export_relative_gitdir(repo, gitdirenv, 0);
+ /* both the worktree and cwd are already normalized */
+ if (!strcmp(cwd->buf, discovery->worktree)) { /* cwd == worktree */
+ repo_discovery_set_gitdir(discovery, gitdirenv, 0);
free(gitfile);
return NULL;
}
- offset = dir_inside_of(cwd->buf, worktree);
- if (offset >= 0) { /* cwd inside worktree? */
- apply_and_export_relative_gitdir(repo, gitdirenv, 1);
- if (chdir(worktree))
- die_errno(_("cannot chdir to '%s'"), worktree);
+ offset = dir_inside_of(cwd->buf, discovery->worktree);
+ if (offset >= 0) { /* cwd inside discovery->worktree? */
+ repo_discovery_set_gitdir(discovery, gitdirenv, 1);
+ if (chdir(discovery->worktree))
+ die_errno(_("cannot chdir to '%s'"), discovery->worktree);
strbuf_addch(cwd, '/');
free(gitfile);
return cwd->buf + offset;
}
/* cwd outside worktree */
- apply_and_export_relative_gitdir(repo, gitdirenv, 0);
+ repo_discovery_set_gitdir(discovery, gitdirenv, 0);
free(gitfile);
return NULL;
}
-static const char *setup_discovered_git_dir(struct repository *repo,
- const char *gitdir,
- struct strbuf *cwd, int offset,
- struct repository_format *repo_fmt,
- int *nongit_ok)
+static const char *repo_discover_implicit_gitdir(struct repo_discovery *discovery,
+ const char *gitdir,
+ struct strbuf *cwd, int offset,
+ struct repository_format *repo_fmt,
+ int *nongit_ok)
{
if (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))
return NULL;
@@ -1207,23 +1237,24 @@ static const char *setup_discovered_git_dir(struct repository *repo,
gitdir = to_free = real_pathdup(gitdir, 1);
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- ret = setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);
+ ret = repo_discover_explicit_gitdir(discovery, gitdir, cwd,
+ repo_fmt, nongit_ok);
free(to_free);
return ret;
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
if (repo_fmt->is_bare > 0) {
- apply_and_export_relative_gitdir(repo, gitdir, (offset != cwd->len));
+ repo_discovery_set_gitdir(discovery, gitdir, (offset != cwd->len));
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
return NULL;
}
/* #0, #1, #5, #8, #9, #12, #13 */
- set_git_work_tree(repo, ".");
+ repo_discovery_set_worktree(discovery, ".");
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
- apply_and_export_relative_gitdir(repo, gitdir, 0);
+ repo_discovery_set_gitdir(discovery, gitdir, 0);
if (offset >= cwd->len)
return NULL;
@@ -1236,10 +1267,10 @@ static const char *setup_discovered_git_dir(struct repository *repo,
}
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
-static const char *setup_bare_git_dir(struct repository *repo,
- struct strbuf *cwd, int offset,
- struct repository_format *repo_fmt,
- int *nongit_ok)
+static const char *repo_discover_bare_gitdir(struct repo_discovery *discovery,
+ struct strbuf *cwd, int offset,
+ struct repository_format *repo_fmt,
+ int *nongit_ok)
{
int root_len;
@@ -1255,7 +1286,8 @@ static const char *setup_bare_git_dir(struct repository *repo,
gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
- return setup_explicit_git_dir(repo, gitdir, cwd, repo_fmt, nongit_ok);
+ return repo_discover_explicit_gitdir(discovery, gitdir, cwd,
+ repo_fmt, nongit_ok);
}
if (offset != cwd->len) {
@@ -1263,10 +1295,10 @@ static const char *setup_bare_git_dir(struct repository *repo,
die_errno(_("cannot come back to cwd"));
root_len = offset_1st_component(cwd->buf);
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
- apply_and_export_relative_gitdir(repo, cwd->buf, 0);
+ repo_discovery_set_gitdir(discovery, cwd->buf, 0);
}
else
- apply_and_export_relative_gitdir(repo, ".", 0);
+ repo_discovery_set_gitdir(discovery, ".", 0);
return NULL;
}
@@ -1525,10 +1557,10 @@ static int is_implicit_bare_repo(const char *path)
* the discovered .git/ directory, if any. If `gitdir` is not absolute, it
* is relative to `dir` (i.e. *not* necessarily the cwd).
*/
-static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
- struct strbuf *gitdir,
- struct strbuf *report,
- int die_on_error)
+static enum discovery_result repo_discovery_find_dir(struct strbuf *dir,
+ struct strbuf *gitdir,
+ struct strbuf *report,
+ int die_on_error)
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
@@ -1695,7 +1727,7 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
return GIT_DIR_CWD_FAILURE;
cwd_len = dir.len;
- result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
+ result = repo_discovery_find_dir(&dir, gitdir, NULL, 0);
if (result <= 0) {
strbuf_release(&dir);
return result;
@@ -1902,6 +1934,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
{
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
+ struct repo_discovery discovery = REPO_DISCOVERY_INIT;
const char *prefix = NULL;
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
@@ -1926,20 +1959,22 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
die_errno(_("Unable to read current working directory"));
strbuf_addbuf(&dir, &cwd);
- switch (setup_git_directory_gently_1(&dir, &gitdir, &report, 1)) {
+ switch (repo_discovery_find_dir(&dir, &gitdir, &report, 1)) {
case GIT_DIR_EXPLICIT:
- prefix = setup_explicit_git_dir(repo, gitdir.buf, &cwd, &repo_fmt, nongit_ok);
+ prefix = repo_discover_explicit_gitdir(&discovery, gitdir.buf, &cwd,
+ &repo_fmt, nongit_ok);
break;
case GIT_DIR_DISCOVERED:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- prefix = setup_discovered_git_dir(repo, gitdir.buf, &cwd, dir.len,
- &repo_fmt, nongit_ok);
+ prefix = repo_discover_implicit_gitdir(&discovery, gitdir.buf, &cwd, dir.len,
+ &repo_fmt, nongit_ok);
break;
case GIT_DIR_BARE:
if (dir.len < cwd.len && chdir(dir.buf))
die(_("cannot change to '%s'"), dir.buf);
- prefix = setup_bare_git_dir(repo, &cwd, dir.len, &repo_fmt, nongit_ok);
+ prefix = repo_discover_bare_gitdir(&discovery, &cwd, dir.len,
+ &repo_fmt, nongit_ok);
break;
case GIT_DIR_HIT_CEILING:
if (!nongit_ok)
@@ -1980,13 +2015,13 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
case GIT_DIR_CWD_FAILURE:
case GIT_DIR_INVALID_FORMAT:
/*
- * As a safeguard against setup_git_directory_gently_1 returning
+ * As a safeguard against repo_discovery_find_dir returning
* these values, fallthrough to BUG. Otherwise it is possible to
* set startup_info->have_repository to 1 when we did nothing to
* find a repository.
*/
default:
- BUG("unhandled setup_git_directory_gently_1() result");
+ BUG("unhandled repo_discovery_find_dir() result");
}
/*
@@ -2005,10 +2040,10 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
startup_info->have_repository = 1;
/*
- * Not all paths through the setup code will call 'apply_and_export_relative_gitdir()' (which
- * directly sets up the environment) so in order to guarantee that the
- * environment is in a consistent state after setup, explicitly setup
- * the environment if we have a repository.
+ * Not all paths through the setup code will have recorded a gitdir
+ * above, so in order to guarantee that the environment is in a
+ * consistent state after setup, explicitly set up the gitdir and
+ * environment if we have a repository.
*
* NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some
* code paths so we also need to explicitly setup the environment if
@@ -2019,7 +2054,12 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
startup_info->have_repository ||
/* GIT_DIR_EXPLICIT */
getenv(GIT_DIR_ENVIRONMENT)) {
- if (!repo->gitdir) {
+ if (discovery.worktree)
+ set_git_work_tree(repo, discovery.worktree);
+
+ if (discovery.gitdir) {
+ apply_and_export_relative_gitdir(repo, discovery.gitdir, 0);
+ } else {
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdir)
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
@@ -2074,6 +2114,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
setup_original_cwd(repo);
+ repo_discovery_release(&discovery);
strbuf_release(&dir);
strbuf_release(&gitdir);
strbuf_release(&report);
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 04/13] setup: split up concerns of `setup_git_env_internal()`
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
The function `setup_git_env_internal()` does two completely unrelated
things:
- It configures the repository's gitdir and propagates environment
variables into it.
- It configures a couple of global parameters via environment
variables.
The function is called when we initialize the repository's path, but
it's also called via `chdir_notify_register()` whenever we change the
current working directory. While we indeed have to reconfigure the
gitdir in case it's a relative path, it doesn't make sense to reapply
the global environment variables.
Split up concerns of this function along the above delineation. Handling
of the global environment variables is moved into `init_git()`, as they
can be considered part of our setup procedure.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
common-init.c | 20 ++++++++++++++++
setup.c | 73 +++++++++++++++++++++++------------------------------------
2 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/common-init.c b/common-init.c
index 5cc73f058c..d26c9c1f20 100644
--- a/common-init.c
+++ b/common-init.c
@@ -5,7 +5,10 @@
#include "exec-cmd.h"
#include "gettext.h"
#include "attr.h"
+#include "odb.h"
+#include "parse.h"
#include "repository.h"
+#include "replace-object.h"
#include "setup.h"
#include "strbuf.h"
#include "trace2.h"
@@ -31,6 +34,22 @@ static void restore_sigpipe_to_default(void)
signal(SIGPIPE, SIG_DFL);
}
+static void setup_environment(void)
+{
+ char *git_replace_ref_base;
+ const char *replace_ref_base;
+
+ if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
+ disable_replace_refs();
+ replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
+ git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
+ : "refs/replace/");
+ update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
+
+ if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+ fetch_if_missing = 0;
+}
+
void init_git(const char **argv)
{
struct strbuf tmp = STRBUF_INIT;
@@ -51,6 +70,7 @@ void init_git(const char **argv)
git_setup_gettext();
initialize_repository(the_repository);
+ setup_environment();
attr_start();
diff --git a/setup.c b/setup.c
index 85fad0d77d..324a235dd1 100644
--- a/setup.c
+++ b/setup.c
@@ -10,7 +10,6 @@
#include "object-file.h"
#include "object-name.h"
#include "refs.h"
-#include "replace-object.h"
#include "repository.h"
#include "config.h"
#include "dir.h"
@@ -1042,38 +1041,19 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
return error_code ? NULL : path;
}
-static void setup_git_env_internal(struct repository *repo,
- const char *git_dir)
+static void apply_gitdir_and_environment(struct repository *repo, const char *path)
{
- char *git_replace_ref_base;
- const char *replace_ref_base;
- struct set_gitdir_args args = { NULL };
struct strvec to_free = STRVEC_INIT;
+ struct set_gitdir_args args = {
+ .commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT),
+ .graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT),
+ .index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT),
+ .disable_ref_updates = !!getenv(GIT_QUARANTINE_ENVIRONMENT),
+ };
- args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
- args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
- args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
- if (getenv(GIT_QUARANTINE_ENVIRONMENT))
- args.disable_ref_updates = true;
+ repo_set_gitdir(repo, path, &args);
- repo_set_gitdir(repo, git_dir, &args);
strvec_clear(&to_free);
-
- if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
- disable_replace_refs();
- replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
- git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
- : "refs/replace/");
- update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
-
- if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
- fetch_if_missing = 0;
-}
-
-static void set_git_dir_1(struct repository *repo, const char *path)
-{
- xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
- setup_git_env_internal(repo, path);
}
static void update_relative_gitdir(const char *name UNUSED,
@@ -1087,11 +1067,12 @@ static void update_relative_gitdir(const char *name UNUSED,
trace_printf_key(&trace_setup_key,
"setup: move $GIT_DIR to '%s'",
path);
- set_git_dir_1(repo, path);
+ apply_gitdir_and_environment(repo, path);
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
free(path);
}
-static void set_git_dir(struct repository *repo, const char *path, int make_realpath)
+static void apply_and_export_relative_gitdir(struct repository *repo, const char *path, int make_realpath)
{
struct strbuf realpath = STRBUF_INIT;
@@ -1100,7 +1081,9 @@ static void set_git_dir(struct repository *repo, const char *path, int make_real
path = realpath.buf;
}
- set_git_dir_1(repo, path);
+ apply_gitdir_and_environment(repo, path);
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
+
if (!is_absolute_path(path))
chdir_notify_register(NULL, update_relative_gitdir, repo);
@@ -1153,7 +1136,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
set_git_work_tree(repo, work_tree_env);
} else if (repo_fmt->is_bare > 0) {
/* #18, #26 */
- set_git_dir(repo, gitdirenv, 0);
+ apply_and_export_relative_gitdir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
} else if (repo_fmt->work_tree) { /* #6, #14 */
@@ -1173,7 +1156,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
}
} else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
/* #16d */
- set_git_dir(repo, gitdirenv, 0);
+ apply_and_export_relative_gitdir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
} else { /* #2, #10 */
@@ -1185,14 +1168,14 @@ static const char *setup_explicit_git_dir(struct repository *repo,
/* both repo_get_work_tree() and cwd are already normalized */
if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
- set_git_dir(repo, gitdirenv, 0);
+ apply_and_export_relative_gitdir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
}
offset = dir_inside_of(cwd->buf, worktree);
if (offset >= 0) { /* cwd inside worktree? */
- set_git_dir(repo, gitdirenv, 1);
+ apply_and_export_relative_gitdir(repo, gitdirenv, 1);
if (chdir(worktree))
die_errno(_("cannot chdir to '%s'"), worktree);
strbuf_addch(cwd, '/');
@@ -1201,7 +1184,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
}
/* cwd outside worktree */
- set_git_dir(repo, gitdirenv, 0);
+ apply_and_export_relative_gitdir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
}
@@ -1231,7 +1214,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
if (repo_fmt->is_bare > 0) {
- set_git_dir(repo, gitdir, (offset != cwd->len));
+ apply_and_export_relative_gitdir(repo, gitdir, (offset != cwd->len));
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
return NULL;
@@ -1240,7 +1223,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,
/* #0, #1, #5, #8, #9, #12, #13 */
set_git_work_tree(repo, ".");
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
- set_git_dir(repo, gitdir, 0);
+ apply_and_export_relative_gitdir(repo, gitdir, 0);
if (offset >= cwd->len)
return NULL;
@@ -1280,10 +1263,10 @@ static const char *setup_bare_git_dir(struct repository *repo,
die_errno(_("cannot come back to cwd"));
root_len = offset_1st_component(cwd->buf);
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
- set_git_dir(repo, cwd->buf, 0);
+ apply_and_export_relative_gitdir(repo, cwd->buf, 0);
}
else
- set_git_dir(repo, ".", 0);
+ apply_and_export_relative_gitdir(repo, ".", 0);
return NULL;
}
@@ -1878,7 +1861,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
struct repository_format fmt = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;
- set_git_dir(repo, ".", 0);
+ apply_and_export_relative_gitdir(repo, ".", 0);
read_and_verify_repository_format(&fmt, ".", NULL);
if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
@@ -2022,7 +2005,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
startup_info->have_repository = 1;
/*
- * Not all paths through the setup code will call 'set_git_dir()' (which
+ * Not all paths through the setup code will call 'apply_and_export_relative_gitdir()' (which
* directly sets up the environment) so in order to guarantee that the
* environment is in a consistent state after setup, explicitly setup
* the environment if we have a repository.
@@ -2040,7 +2023,7 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdir)
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
- setup_git_env_internal(repo, gitdir);
+ apply_gitdir_and_environment(repo, gitdir);
}
if (startup_info->have_repository) {
@@ -2825,12 +2808,12 @@ int init_db(struct repository *repo,
if (!exist_ok && !stat(real_git_dir, &st))
die(_("%s already exists"), real_git_dir);
- set_git_dir(repo, real_git_dir, 1);
+ apply_and_export_relative_gitdir(repo, real_git_dir, 1);
git_dir = repo_get_git_dir(repo);
separate_git_dir(git_dir, original_git_dir);
}
else {
- set_git_dir(repo, git_dir, 1);
+ apply_and_export_relative_gitdir(repo, git_dir, 1);
git_dir = repo_get_git_dir(repo);
}
startup_info->have_repository = 1;
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 03/13] setup: unify setup of shallow file
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
It is possible to configure an arbitrary "shallow" file via two
mechanisms, and the respective logic to handle these is split across two
locations:
- Via the "GIT_SHALLOW_FILE" environment variable, which is handled in
`setup_git_env_internal()`.
- Via the global "--shallow-file=" command line option, which is
handled in `handle_options()`.
We can rather easily unify this logic by not configuring the shallow
file in `handle_options()`, but instead overwriting the environment
variable. The environment variable itself is then handled inside of
`apply_repository_format()`, which is responsible for configuring a
discovered Git directory.
This new logic is similar in nature to how we handle the other global
options already, all of which end up setting an environment variable.
So for one this gives us more consistency. But more importantly, this
change means that `the_repository` will not contain any relevant state
anymore before we hit `apply_repository_format()` once we're at the end
of this patch series. Consequently, it will become possible for us to
completely discard `the_repository` and populate it anew.
Note that on first sight, this change looks like it might change the
precedence order. Before this change, we used to configure the shallow
file in the arguments handler first, and then it looks like we override
it via the environment variable. What's important to note though is the
last parameter to `set_alternate_shallow_file()`, which tells us whether
we want to overwrite a preexisting value, and when applying the value
from the environment we tell it not to overwrite preexisting values. So
in effect, the command line has precedence over the environment. After
this change, we now overwrite preexisting environment variables when we
see the argument, and consequently we keep the precedence order in tact.
With this change though we don't need the final parameter anymore that
tells `set_alternate_shallow_file()` whether or not to overwrite. We
only have a single callsite for this function now, and that function is
itself only ever called exactly once. Remove that parameter.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
git.c | 2 +-
setup.c | 10 +++++-----
shallow.c | 4 +---
shallow.h | 2 +-
4 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/git.c b/git.c
index 387eabe38c..e5f1811b6b 100644
--- a/git.c
+++ b/git.c
@@ -306,7 +306,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
} else if (!strcmp(cmd, "--shallow-file")) {
(*argv)++;
(*argc)--;
- set_alternate_shallow_file(the_repository, (*argv)[0], 1);
+ setenv(GIT_SHALLOW_FILE_ENVIRONMENT, (*argv)[0], 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "-C")) {
diff --git a/setup.c b/setup.c
index 1d8c193375..85fad0d77d 100644
--- a/setup.c
+++ b/setup.c
@@ -1046,7 +1046,6 @@ static void setup_git_env_internal(struct repository *repo,
const char *git_dir)
{
char *git_replace_ref_base;
- const char *shallow_file;
const char *replace_ref_base;
struct set_gitdir_args args = { NULL };
struct strvec to_free = STRVEC_INIT;
@@ -1067,10 +1066,6 @@ static void setup_git_env_internal(struct repository *repo,
: "refs/replace/");
update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
- shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
- if (shallow_file)
- set_alternate_shallow_file(repo, shallow_file, 0);
-
if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
fetch_if_missing = 0;
}
@@ -1774,8 +1769,13 @@ int apply_repository_format(struct repository *repo,
}
if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {
+ const char *shallow_file;
+
object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));
alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
+ shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
+ if (shallow_file)
+ set_alternate_shallow_file(repo, shallow_file);
}
repo->bare_cfg = format->is_bare;
diff --git a/shallow.c b/shallow.c
index 07cae44ae5..c063b3deaf 100644
--- a/shallow.c
+++ b/shallow.c
@@ -21,12 +21,10 @@
#include "statinfo.h"
#include "trace.h"
-void set_alternate_shallow_file(struct repository *r, const char *path, int override)
+void set_alternate_shallow_file(struct repository *r, const char *path)
{
if (r->parsed_objects->is_shallow != -1)
BUG("is_repository_shallow must not be called before set_alternate_shallow_file");
- if (r->parsed_objects->alternate_shallow_file && !override)
- return;
free(r->parsed_objects->alternate_shallow_file);
r->parsed_objects->alternate_shallow_file = xstrdup_or_null(path);
}
diff --git a/shallow.h b/shallow.h
index e20ca4c21b..6a64db42c9 100644
--- a/shallow.h
+++ b/shallow.h
@@ -10,7 +10,7 @@
struct oid_array;
struct strvec;
-void set_alternate_shallow_file(struct repository *r, const char *path, int override);
+void set_alternate_shallow_file(struct repository *r, const char *path);
int register_shallow(struct repository *r, const struct object_id *oid);
int unregister_shallow(const struct object_id *oid);
int is_repository_shallow(struct repository *r);
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 02/13] setup: mark bogus worktree in `apply_repository_format()`
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
When a repository is configured to have both "core.worktree" and
"core.bare" we emit a warning and mark the worktree configuration as
bogus so that the next call to `setup_work_tree()` will cause us to die.
This allows us to still use the misconfigured repository, at least as
long as we don't try to use its worktree.
This condition is handled in `setup_explicit_git_dir()`. In a subsequent
commit we'll refactor this function so that it doesn't receive a repo as
input anymore though, and consequently we cannot set the "bogus" bit
anymore.
Move the logic into `apply_repository_format()` instead to prepare for
this. While at it, fix up formatting a bit.
Note that this change requires us to also explicitly unset the value of
"core.worktree" in case we have the "GIT_WORK_TREE" environment variable
set. This is because the environment variable overrides the repository's
configuration, and we don't want to warn or die in case the work tree
has been configured explicitly regardless of whether or not "core.bare"
is set.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/setup.c b/setup.c
index 118416e350..1d8c193375 100644
--- a/setup.c
+++ b/setup.c
@@ -1147,24 +1147,24 @@ static const char *setup_explicit_git_dir(struct repository *repo,
}
/* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */
- if (work_tree_env)
+ if (work_tree_env) {
+ /*
+ * The environment variable overrides "core.worktree". This
+ * also has the consequence that we don't want to flag cases as
+ * bogus where we have both "core.worktree" and "core.bare", so
+ * we have to explicitly unset the configuration.
+ */
+ FREE_AND_NULL(repo_fmt->work_tree);
set_git_work_tree(repo, work_tree_env);
- else if (repo_fmt->is_bare > 0) {
- if (repo_fmt->work_tree) {
- /* #22.2, #30 */
- warning("core.bare and core.worktree do not make sense");
- repo->worktree_config_is_bogus = true;
- }
-
+ } else if (repo_fmt->is_bare > 0) {
/* #18, #26 */
set_git_dir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
- }
- else if (repo_fmt->work_tree) { /* #6, #14 */
- if (is_absolute_path(repo_fmt->work_tree))
+ } else if (repo_fmt->work_tree) { /* #6, #14 */
+ if (is_absolute_path(repo_fmt->work_tree)) {
set_git_work_tree(repo, repo_fmt->work_tree);
- else {
+ } else {
char *core_worktree;
if (chdir(gitdirenv))
die_errno(_("cannot chdir to '%s'"), gitdirenv);
@@ -1176,15 +1176,14 @@ static const char *setup_explicit_git_dir(struct repository *repo,
set_git_work_tree(repo, core_worktree);
free(core_worktree);
}
- }
- else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
+ } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
/* #16d */
set_git_dir(repo, gitdirenv, 0);
free(gitfile);
return NULL;
- }
- else /* #2, #10 */
+ } else { /* #2, #10 */
set_git_work_tree(repo, ".");
+ }
/* set_git_work_tree() must have been called by now */
worktree = repo_get_work_tree(repo);
@@ -1768,6 +1767,12 @@ int apply_repository_format(struct repository *repo,
if (verify_repository_format(format, err) < 0)
return -1;
+ if (format->is_bare > 0 && format->work_tree) {
+ /* #22.2, #30 */
+ warning("core.bare and core.worktree do not make sense");
+ repo->worktree_config_is_bogus = true;
+ }
+
if (flags & APPLY_REPOSITORY_FORMAT_HONOR_ENV) {
object_directory = xstrdup_or_null(getenv(DB_ENVIRONMENT));
alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* [PATCH v2 00/13] setup: split up repository discovery and setup
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im>
Hi,
this patch series is the next set of refactorings to simplify how we
configure repositories in "setup.c".
The setup of the repository is essentially happening in two phases:
1. We discover the location of the repository as well as its format.
2. We then use this information to configure the repository.
So far so sensible. In our code base though these two phases are quite
intertwined with one another, as we continue to repeatedly call
`set_git_dir()` and `set_work_tree()` on the repository as we discover
its locations. This makes it hard to follow the logic, and it basically
leaves us with a partially-configured repository.
This patch series splits this up into two proper phases that are
completely separate from one another. The first phase now populates a
`struct repo_discovery` structure, without even having access to any
repository. The second phase then takes that structure and configures
the repository accordingly.
Ultimately, the motivation of this whole exercise is that eventually we
can unify configuration of the repository into `repo_init()` instead of
having bits and pieces thereof distributed across "repository.c" and
"setup.c".
This series is built on top of v2.55.0 with the following three branches
merged into it:
- ps/refs-onbranch-fixes at d6522d01df (refs: protect against
chicken-and-egg recursion, 2026-06-25).
- ps/setup-drop-global-state at 1ceee7431b (treewide: drop
USE_THE_REPOSITORY_VARIABLE, 2026-06-11).
- jk/repo-info-path-keys at 3ac28d832a (repo: add path.gitdir with
absolute and relative suffix formatting, 2026-06-24).
Changes in v2:
- Expand commit message to talk about precedence order between
the "GIT_SHALLOW_FILE" environment variable and the "--shallow-file"
command line switch.
- Remove a now-unused parameter in `set_alternate_shallow_file()`.
- Fix a typo.
- Link to v1: https://patch.msgid.link/20260630-pks-setup-split-discovery-and-setup-v1-0-13864eb5a032@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (13):
setup: rename `check_repository_format_gently()`
setup: mark bogus worktree in `apply_repository_format()`
setup: unify setup of shallow file
setup: split up concerns of `setup_git_env_internal()`
setup: introduce explicit repository discovery
setup: embed repository format in discovery
setup: move prefix into repository
setup: drop static `cwd` variable
setup: propagate prefix via repository discovery
setup: make repository discovery self-contained
setup: drop redundant configuration of `startup_info->have_repository`
setup: pass worktree to `init_db()`
setup: mark `set_git_work_tree()` as file-local
builtin/clone.c | 8 +-
builtin/init-db.c | 34 ++--
builtin/repo.c | 8 +-
builtin/rev-parse.c | 5 +-
builtin/update-index.c | 4 +-
common-init.c | 20 +++
git.c | 2 +-
object-name.c | 4 +-
repository.c | 1 +
repository.h | 8 +
setup.c | 419 ++++++++++++++++++++++++++-----------------------
setup.h | 7 +-
shallow.c | 4 +-
shallow.h | 2 +-
trace.c | 4 +-
15 files changed, 285 insertions(+), 245 deletions(-)
Range-diff versus v1:
1: 19230b18cf = 1: f4db0a6a10 setup: rename `check_repository_format_gently()`
2: 246e8caf8f ! 2: 72f51e01ff setup: mark bogus worktree in `apply_repository_format()`
@@ setup.c: static const char *setup_explicit_git_dir(struct repository *repo,
+ * The environment variable overrides "core.worktree". This
+ * also has the consequence that we don't want to flag cases as
+ * bogus where we have both "core.worktree" and "core.bare", so
-+ * we have to exlicitly unset the configuration.
++ * we have to explicitly unset the configuration.
+ */
+ FREE_AND_NULL(repo_fmt->work_tree);
set_git_work_tree(repo, work_tree_env);
3: 06ea13242f ! 3: 1542e52523 setup: unify setup of shallow file
@@ Commit message
of this patch series. Consequently, it will become possible for us to
completely discard `the_repository` and populate it anew.
+ Note that on first sight, this change looks like it might change the
+ precedence order. Before this change, we used to configure the shallow
+ file in the arguments handler first, and then it looks like we override
+ it via the environment variable. What's important to note though is the
+ last parameter to `set_alternate_shallow_file()`, which tells us whether
+ we want to overwrite a preexisting value, and when applying the value
+ from the environment we tell it not to overwrite preexisting values. So
+ in effect, the command line has precedence over the environment. After
+ this change, we now overwrite preexisting environment variables when we
+ see the argument, and consequently we keep the precedence order in tact.
+
+ With this change though we don't need the final parameter anymore that
+ tells `set_alternate_shallow_file()` whether or not to overwrite. We
+ only have a single callsite for this function now, and that function is
+ itself only ever called exactly once. Remove that parameter.
+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
## git.c ##
@@ setup.c: int apply_repository_format(struct repository *repo,
alternate_object_directories = xstrdup_or_null(getenv(ALTERNATE_DB_ENVIRONMENT));
+ shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
+ if (shallow_file)
-+ set_alternate_shallow_file(repo, shallow_file, 0);
++ set_alternate_shallow_file(repo, shallow_file);
}
repo->bare_cfg = format->is_bare;
+
+ ## shallow.c ##
+@@
+ #include "statinfo.h"
+ #include "trace.h"
+
+-void set_alternate_shallow_file(struct repository *r, const char *path, int override)
++void set_alternate_shallow_file(struct repository *r, const char *path)
+ {
+ if (r->parsed_objects->is_shallow != -1)
+ BUG("is_repository_shallow must not be called before set_alternate_shallow_file");
+- if (r->parsed_objects->alternate_shallow_file && !override)
+- return;
+ free(r->parsed_objects->alternate_shallow_file);
+ r->parsed_objects->alternate_shallow_file = xstrdup_or_null(path);
+ }
+
+ ## shallow.h ##
+@@
+ struct oid_array;
+ struct strvec;
+
+-void set_alternate_shallow_file(struct repository *r, const char *path, int override);
++void set_alternate_shallow_file(struct repository *r, const char *path);
+ int register_shallow(struct repository *r, const struct object_id *oid);
+ int unregister_shallow(const struct object_id *oid);
+ int is_repository_shallow(struct repository *r);
4: add8007726 = 4: 5a2b4132a1 setup: split up concerns of `setup_git_env_internal()`
5: 4bc374b957 ! 5: 8190a24acf setup: introduce explicit repository discovery
@@ setup.c: static void apply_and_export_relative_gitdir(struct repository *repo, c
int offset;
@@ setup.c: static const char *setup_explicit_git_dir(struct repository *repo,
- * we have to exlicitly unset the configuration.
+ * we have to explicitly unset the configuration.
*/
FREE_AND_NULL(repo_fmt->work_tree);
- set_git_work_tree(repo, work_tree_env);
6: 687443fcac ! 6: 869b6cf7cb setup: embed repository format in discovery
@@ setup.c: static const char *repo_discover_explicit_gitdir(struct repo_discovery
}
@@ setup.c: static const char *repo_discover_explicit_gitdir(struct repo_discovery *discover
* bogus where we have both "core.worktree" and "core.bare", so
- * we have to exlicitly unset the configuration.
+ * we have to explicitly unset the configuration.
*/
- FREE_AND_NULL(repo_fmt->work_tree);
+ FREE_AND_NULL(discovery->format.work_tree);
7: 6e2e1caf30 = 7: af482fd82c setup: move prefix into repository
8: 9dda7f521b = 8: 8ad046bc79 setup: drop static `cwd` variable
9: 4fc0bbd6a2 = 9: 231c98255b setup: propagate prefix via repository discovery
10: 54ddf6a854 = 10: ee241b765d setup: make repository discovery self-contained
11: 46dbc1ac4c = 11: 4fa953a970 setup: drop redundant configuration of `startup_info->have_repository`
12: c42085145a = 12: 4299a4aadb setup: pass worktree to `init_db()`
13: 9a9a4dea01 = 13: 1add08fce7 setup: mark `set_git_work_tree()` as file-local
---
base-commit: b340fc4c4f3850656b726ff757b42d2020215378
change-id: 20260618-pks-setup-split-discovery-and-setup-d7f23831803c
^ permalink raw reply
* [PATCH v2 01/13] setup: rename `check_repository_format_gently()`
From: Patrick Steinhardt @ 2026-07-07 7:21 UTC (permalink / raw)
To: git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>
The function `check_repository_format_gently()` receives a format as
input. An unknowing reader may thus suspect that this function actually
checks the passed-in format for consistency. While the function indeed
checks the repository format, it actually serves two purposes:
- It reads the repository's format and populates the passed-in format
with that information.
- It then indeed checks whether the format is consistent.
Rename the function to `read_and_verify_repository_format()` to clarify
its functionality. While at it, reorder the parameters so that the
format comes first to better match other functions that pass around the
format.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
setup.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/setup.c b/setup.c
index 951ab9eedb..118416e350 100644
--- a/setup.c
+++ b/setup.c
@@ -749,9 +749,9 @@ static int check_repo_format(const char *var, const char *value,
return read_worktree_config(var, value, ctx, vdata);
}
-static int check_repository_format_gently(const char *gitdir,
- struct repository_format *candidate,
- int *nongit_ok)
+static int read_and_verify_repository_format(struct repository_format *format,
+ const char *gitdir,
+ int *nongit_ok)
{
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
@@ -759,7 +759,7 @@ static int check_repository_format_gently(const char *gitdir,
has_common = get_common_dir(&sb, gitdir);
strbuf_addstr(&sb, "/config");
- read_repository_format(candidate, sb.buf);
+ read_repository_format(format, sb.buf);
strbuf_release(&sb);
/*
@@ -767,10 +767,10 @@ static int check_repository_format_gently(const char *gitdir,
* we treat a missing config as a silent "ok", even when nongit_ok
* is unset.
*/
- if (candidate->version < 0)
+ if (format->version < 0)
return 0;
- if (verify_repository_format(candidate, &err) < 0) {
+ if (verify_repository_format(format, &err) < 0) {
if (nongit_ok) {
warning("%s", err.buf);
strbuf_release(&err);
@@ -780,37 +780,37 @@ static int check_repository_format_gently(const char *gitdir,
die("%s", err.buf);
}
- string_list_clear(&candidate->unknown_extensions, 0);
- string_list_clear(&candidate->v1_only_extensions, 0);
+ string_list_clear(&format->unknown_extensions, 0);
+ string_list_clear(&format->v1_only_extensions, 0);
- if (candidate->worktree_config) {
+ if (format->worktree_config) {
/*
* pick up core.bare and core.worktree from per-worktree
* config if present
*/
strbuf_addf(&sb, "%s/config.worktree", gitdir);
- git_config_from_file(read_worktree_config, sb.buf, candidate);
+ git_config_from_file(read_worktree_config, sb.buf, format);
strbuf_release(&sb);
has_common = 0;
}
if (startup_info->force_bare_repository) {
- candidate->is_bare = 1;
- FREE_AND_NULL(candidate->work_tree);
+ format->is_bare = 1;
+ FREE_AND_NULL(format->work_tree);
} else if (has_common) {
/*
* When sharing a common dir with another repository (e.g. a
* linked worktree), do not let this repository's config
* dictate bareness; it is inherited from the main worktree.
*/
- candidate->is_bare = -1;
+ format->is_bare = -1;
/*
* Furthermore, "core.worktree" is supposed to be ignored when
* we have a commondir configured, unless it comes from the
* per-worktree configuration.
*/
- FREE_AND_NULL(candidate->work_tree);
+ FREE_AND_NULL(format->work_tree);
}
return 0;
@@ -1141,7 +1141,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
die(_("not a git repository: '%s'"), gitdirenv);
}
- if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
+ if (read_and_verify_repository_format(repo_fmt, gitdirenv, nongit_ok)) {
free(gitfile);
return NULL;
}
@@ -1218,7 +1218,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,
struct repository_format *repo_fmt,
int *nongit_ok)
{
- if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
+ if (read_and_verify_repository_format(repo_fmt, gitdir, nongit_ok))
return NULL;
/* --work-tree is set without --git-dir; use discovered one */
@@ -1266,7 +1266,7 @@ static const char *setup_bare_git_dir(struct repository *repo,
{
int root_len;
- if (check_repository_format_gently(".", repo_fmt, nongit_ok))
+ if (read_and_verify_repository_format(repo_fmt, ".", nongit_ok))
return NULL;
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
@@ -1874,7 +1874,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
struct strbuf err = STRBUF_INIT;
set_git_dir(repo, ".", 0);
- check_repository_format_gently(".", &fmt, NULL);
+ read_and_verify_repository_format(&fmt, ".", NULL);
if (apply_repository_format(repo, &fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
startup_info->have_repository = 1;
@@ -2836,7 +2836,7 @@ int init_db(struct repository *repo,
* config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool.
*/
- check_repository_format_gently(repo_get_git_dir(repo), &repo_fmt, NULL);
+ read_and_verify_repository_format(&repo_fmt, repo_get_git_dir(repo), NULL);
repository_format_configure(&repo_fmt, hash, ref_storage_format);
if (apply_repository_format(repo, &repo_fmt, APPLY_REPOSITORY_FORMAT_HONOR_ENV, &err) < 0)
die("%s", err.buf);
--
2.55.0.141.g00534a21ce.dirty
^ permalink raw reply related
* Re: [PATCH v7 2/3] graph: add a 2 commit buffer for lookahead
From: Pablo Sabater @ 2026-07-07 6:31 UTC (permalink / raw)
To: Chandra Pratap
Cc: Kristofer Karlsson, git, ayu.chandekar, christian.couder, gitster,
jltobler, karthik.188, peff, phillip.wood, siddharthasthana31
In-Reply-To: <CA+J6zkSrcJVcKmm0duTQwWcLxrsZ6eZkVgL=hQUQHegKGsWsxg@mail.gmail.com>
El lun, 6 jul 2026 a las 17:33, Chandra Pratap
(<chandrapratap3519@gmail.com>) escribió:
>
> On Mon, 6 Jul 2026 at 19:15, Kristofer Karlsson <krka@spotify.com> wrote:
> >
> > The hardcoded size-2 lookahead buffer was my suggestion,
> > so I am responding inline with my thoughts although Pablo is
> > the right person for making further changes (if any).
> >
> > On Mon, 6 Jul 2026, Chandra Pratap <chandrapratap3519@gmail.com> wrote:
> > > Do we need to NULL out the retrieved buffer entries? If so, it is
> > > worthwhile asserting that the entire buffer is NULLed out in the
> > > !graph->lookahead_nr check above.
> >
> > You're right, it's not technically needed, and there are many places
> > in the repo where stale data remains in buffers, and it would be possible
> > to do that here too. I don't think it matters much in practice though,
> > and NULLing them out would perhaps prevent some accidental reuse on bugs
> > (NULL would crash instead).
It is not really needed to NULL because every time we access it (pop
or the graph_is_interesting()) we are limited by graph->lookahead_nr,
however I thought that it is better to have it NULL.
Imagine that somehow the lookahead_nr is 1 when it should be 0, having
NULL would segfault or if it doesn't at least we are sure that
graph_is_interesting() won't re-process as interesting a commit left
as stale on the buffer. Anyway, this is just speculation. I think it's
better to leave it like this.
> >
> > As for asserting: rather than checking that empty slots are NULL
> > (which just verifies our own cleanup), it might be more useful to
> > assert that a slot is non-NULL when lookahead_nr says it should be
> > populated, i.e. assert on read rather than on empty. But even that
> > may be overkill for a 2-element internal buffer.
>
> True. But since we're already going through the pains of initializing the
> buffer and NULLing it upon a pop, I'd much rather go the extra length
> and verify what we're trying to do, shouldn't be that complicated anyway.
>
> Whether that means checking for NULL here, on a push, or on a read
> is something I don't feel strongly about, either is fine with me.
About asserting, I think that the best is, because we are popping, to
check the first element only just in case we are in the imaginary
scenario that lookahead_nr is lying, but because we pop, we don't
really care about what's on the second entry.
>
> > > Not the best engineering practice, but I guess it is fine to constrain
> > > the logic to _only_ a 2-entry buffer since that's what we'll always
> > > deal with anyway.
> >
> > I did consider making it a proper ring buffer, but it felt like
> > overkill (and I could not find any other existing ring buffer to
> > piggy-back on in the repo), and the lookahead depth is
> > structurally tied to the algorithm - we only ever need two more
> > elements.
> >
> > It also helps that this is entirely internal to graph.c. If the
> > buffer were part of a broader API, a less hardcoded approach
> > would be more appropriate indeed.
>
> Agreed.
>
> > > We should use ARRAY_SIZE(graph->lookahead) instead of hardcoding
> > > the value 2.
> >
> > Agreed, that is a nice improvement. What do you think Pablo?
Yes, I'll do that on reroll.
> >
> > Thanks,
> > Kristofer
Not related with this feedback but worth saying:
re-reading what's done on revision.c there is this if line:
> if (!revs->max_count_stage && !revs->reverse_output_stage)
Graph is not compatible with --reverse, so the right-side will always be true.
About --max-count, I made a few tests and the lookahead behaves the
same regardless of the number of commits to be shown (even if capped).
So this whole if block can be dropped and we can try to populate the
lookahead buffer always.
Thanks both for the feedback and review,
Pablo.
^ permalink raw reply
* Re: [PATCH 05/13] setup: introduce explicit repository discovery
From: Patrick Steinhardt @ 2026-07-07 6:25 UTC (permalink / raw)
To: Justin Tobler; +Cc: git
In-Reply-To: <akwocdrzeu0xBLQZ@denethor>
On Mon, Jul 06, 2026 at 05:19:59PM -0500, Justin Tobler wrote:
> On 26/06/30 01:47PM, Patrick Steinhardt wrote:
> > Like this, we'll never end up with a partially-configured repository and
> > can eventually extend `repo_init()` to handle the full initialization
> > for us.
>
> So IIUC the expectation here would be for all configuration of the
> repository to happen prior to it being applied? Would it be a bug to
> attempt to apply configuration to a repository more than once?
I'd say that it should be treated as a bug, yes. I basically want us to
ensure that every repository is created once and exactly once via a
constructor that takes all required parameters as input. That's still
future music though.
Patrick
^ permalink raw reply
* Re: [PATCH 03/13] setup: unify setup of shallow file
From: Patrick Steinhardt @ 2026-07-07 6:25 UTC (permalink / raw)
To: Justin Tobler; +Cc: git
In-Reply-To: <akwkS45ZknejwhuO@denethor>
On Mon, Jul 06, 2026 at 05:02:08PM -0500, Justin Tobler wrote:
> On 26/06/30 01:47PM, Patrick Steinhardt wrote:
> > It is possible to configure an arbitrary "shallow" file via two
> > mechanisms, and the respective logic to handle these is split across two
> > locations:
> >
> > - Via the "GIT_SHALLOW_FILE" environment variable, which is handled in
> > `setup_git_env_internal()`.
> >
> > - Via the global "--shallow-file=" command line option, which is
> > handled in `handle_options()`.
>
> Ok.
>
> > We can rather easily unify this logic by not configuring the shallow
> > file in `handle_options()`, but instead overwriting the environment
> > variable. The environment variable itself is then handled inside of
> > `apply_repository_format()`, which is responsible for configuring a
> > discovered Git directory.
>
> What is supposed to be the correct order for processing shallow file
> configuration here? Does this mean that the `--shallow-file` option now
> overwrites the environment variable? Was this how it already was?
That's a good question. The command line switch does override the
environment variable, but it's not a change in behaviour: the last
parameter of `set_alternate_shallow_file()` controls whether or not we
want to override an already-configured shallow file. So even though we
used to call that function with the value of the environment variable at
a much later point in time, we had that parameter set to `0` there. So
if we've already configured the shallow file before via "--shallow-file"
it wouldn't have been overwritten.
We can remove this logic now though, as it's essentially unused after
this patch. And it certainly warrants a mention in the commit message.
> > This new logic is similar in nature to how we handle the other global
> > options already, all of which end up setting an environment variable.
> > So for one this gives us more consistency. But more importantly, this
> > change means that `the_repository` will not contain any relevant state
> > anymore before we hit `apply_repository_format()` once we're at the end
> > of this patch series. Consequently, it will become possible for us to
> > completely discard `the_repository` and populate it anew.
>
> I can't say that I'm a fan of using environment variables to store
> global state in this manner, but I guess if there is precdent and this
> is making us more consistent, it is probably fine. I guess the other
> option would be to store the read configuration is some intermediate
> structure to be applied later, but that may not be worth it here.
I agree, I'm not much of a fan of this either. I'd also love to
eventually refactor the argument handling in "git.c" to not rely on
global state anymore, but that's going to be a bigger refactoring (if
it's feasible at all).
Patrick
^ permalink raw reply
* Re: [PATCH 02/13] setup: mark bogus worktree in `apply_repository_format()`
From: Patrick Steinhardt @ 2026-07-07 6:25 UTC (permalink / raw)
To: Justin Tobler; +Cc: git
In-Reply-To: <akwfAmyeIVJYXj1h@denethor>
On Mon, Jul 06, 2026 at 04:49:41PM -0500, Justin Tobler wrote:
> On 26/06/30 01:47PM, Patrick Steinhardt wrote:
[snip]
> > Note that this change requires us to also explicitly unset the value of
> > "core.worktree" in case we have the "GIT_WORK_TREE" environment variable
> > set. This is because the environment variable overrides the repository's
> > configuration, and we don't want to warn or die in case the work tree
> > has been configured explicitly regardless of whether or not "core.bare"
> > is set.
>
> Hmmm, does this mean we now just silently ignore the misconfiguration if
> done via environment variable?
We do, but we also ignored those cases before. So the behaviour with and
without this change is (supposed) to be the exact same.
> > diff --git a/setup.c b/setup.c
> > index 118416e350..f54eac5e5a 100644
> > --- a/setup.c
> > +++ b/setup.c
> > @@ -1768,6 +1767,12 @@ int apply_repository_format(struct repository *repo,
> > if (verify_repository_format(format, err) < 0)
> > return -1;
> >
> > + if (format->is_bare > 0 && format->work_tree) {
> > + /* #22.2, #30 */
> > + warning("core.bare and core.worktree do not make sense");
> > + repo->worktree_config_is_bogus = true;
> > + }
>
> We now perform this validation in `apply_repository_format()`. Does
> deferring this check have any meaningful impact? Or is
> `apply_repository_format()` always called after
> `setup_explicit_git_dir()`?
No, it shouldn't have an impact on any user-visible behaviour. We always
call `apply_repository_format()` eventually, as that function is what
does the final setup of our repository.
Patrick
^ permalink raw reply
* Re: [PATCH RFC 2/2] builtin/history: print feedback after successful reword
From: Dominique Martinet @ 2026-07-07 5:09 UTC (permalink / raw)
To: Ben Knoble
Cc: Pablo Sabater, Junio C Hamano, git, Patrick Steinhardt,
Kaartic Sivaraam
In-Reply-To: <9C91B027-C24A-4D7B-A3BC-5CF3B04D990C@gmail.com>
[context: I just played with git history reword/fixup and dug through
archives for anything like this, so chiming in.
First, thanks for the new git history commands, they all look promising!]
Ben Knoble wrote on Mon, Jun 08, 2026 at 12:47:41PM -0400:
>>> Do other commands in "git history" (split is in 'master', drop and
>>> fixup are cooking) behave with similar verbosity? Consistency within
>>> the same "history" umbrella matters more than being similar with
>>> other commands that can be used for similar purposes.
I agree with the sentiment of needing consistency, but rather than say
"the other commands are not verbose" (as they are) I'd say they're new
enough we can afford to "make them all verbose" instead.
In particular, for git history reword there is an editor opening up, so
I didn't have much trouble assuming silence was success, but I was
disturbed by `git history fixup` which just returns immediately (much
faster than rebase) with no feedback at all.
>> They do not, they are thought with the rule of silence in mind.
>> However I think that this output is valuable information I might have
>> explained myself better at [1] but my thought is:
>>
>> git history reword aabb
>>
>> Now that I have my commit aabb rewritten I want to check it again just
>> to make sure I did what I wanted correctly,
>
> Some thoughts:
>
> - If the rewritten commit is an ancestor of HEAD, look at the log of HEAD@{1} or the log between HEAD and the aforementioned reflog entry. (git-range-diff may also be helpful there.)
> - Similarly, if the rewritten commit is reachable from some ref R, check R@{1} etc.
During my quick tests I was surprised with how git history reword/fixup
behave with commits that aren't ancestors of HEAD/any branch (that can
happen for example if you print `git log --oneline` once and refer to it
after editing.
This transcript is a bit ugly but should illustrate the issue:
```
$ git init
Initialized empty Git repository in ...test/.git/
$ echo a > aa
$ git add aa
$ git commit -m init
[master (root-commit) 62884dc4d43c] init
1 file changed, 1 insertion(+)
create mode 100644 aa
$ echo b > b
$ git add b
$ git commit -m b
[master 058294f87a36] b
1 file changed, 1 insertion(+)
create mode 100644 b
$ echo c > c
$ git add c
$ git commit -m c
[master 0c4ad0c9337c] c
1 file changed, 1 insertion(+)
create mode 100644 c
$ git log --oneline --graph
* 0c4ad0c9337c (HEAD -> master) c
* 058294f87a36 b
* 62884dc4d43c init
$ echo d > d
$ git add d
$ git history fixup HEAD^
$ echo e > e
$ git add e
$ git history fixup 058294f87a36
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: e
$ git history reword 058294f87a36
(editor showed up, commit message modified and saved)
$ git log --oneline --graph
* 5cc5551381a3 (HEAD -> master) c
* 0b7ab36bf167 b
* 62884dc4d43c init
```
-> fixup didn't show any message (and exited with 0), but didn't unstage
the hunk either and didn't do anything, so one cannot differentiate with
the fixup actually happening
-> reword showed up editor but didn't actually do anything visible
(probably did create a new commit somewhere that's unreachable?)
So I agree with Pablo's suggestion: printing old/new short hash on
success would help visualy confirming something worked.
... But it might be worth to ensure that the commit has any ref we can
handle (if --update-refs is set then the commit we edit is ancestor to
some branch, if not set then it must be an ancestor of HEAD)
What do you think?
--
Dominique Martinet | Asmadeus
^ permalink raw reply
* [PATCH 7/7] hash: check ctx->active flag in all wrapper functions
From: Jeff King @ 2026-07-07 5:09 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707045556.GA1288172@coredump.intra.peff.net>
It only makes sense to call git_hash_update(), etc, on a hash context
that has been initialized but not yet finalized or discarded. This is an
unlikely error to make, but it's easy for us to catch it and complain.
It's especially important because it would quietly "work" for many hash
backends (like sha1dc, which is just manipulating some bytes) but would
cause undefined behavior with others (like OpenSSL, which puts the
context onto the heap). Checking the flag lets us catch problems
consistently on every build.
Note that we can't do the same for git_init_hash(). Even though it would
cause a leak to call it twice (without an intervening final/discard),
the point of the function is that the contents of the struct are
undefined before the call. But calling it twice is an even less likely
error to make, so not covering it is OK.
Signed-off-by: Jeff King <peff@peff.net>
---
hash.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hash.c b/hash.c
index b1296f0018..82f7e24404 100644
--- a/hash.c
+++ b/hash.c
@@ -290,22 +290,32 @@ void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop)
void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
{
+ if (!src->active)
+ BUG("attempt to copy from an inactive hash context");
+ if (!dst->active)
+ BUG("attempt to copy to an inactive hash context");
src->algop->clone_fn(dst, src);
}
void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)
{
+ if (!ctx->active)
+ BUG("attempt to update an inactive hash context");
ctx->algop->update_fn(ctx, in, len);
}
void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)
{
+ if (!ctx->active)
+ BUG("attempt to finalize an inactive hash context");
ctx->algop->final_fn(hash, ctx);
ctx->active = false;
}
void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
{
+ if (!ctx->active)
+ BUG("attempt to finalize an inactive hash context");
ctx->algop->final_oid_fn(oid, ctx);
ctx->active = false;
}
--
2.55.0.459.g1b256877c9
^ permalink raw reply related
* [PATCH 6/7] http: use idempotent git_hash_discard()
From: Jeff King @ 2026-07-07 5:08 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707045556.GA1288172@coredump.intra.peff.net>
Now that it is OK to call git_hash_discard() even after finalizing the
hash, we no longer need the ctx_valid bool added by a2d8ea5a76 (http:
discard hash in dumb-http http_object_request, 2026-07-02).
Signed-off-by: Jeff King <peff@peff.net>
---
http.c | 5 +----
http.h | 1 -
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/http.c b/http.c
index 0341de5031..caccf2108e 100644
--- a/http.c
+++ b/http.c
@@ -2880,7 +2880,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
git_inflate_init(&freq->stream);
git_hash_init(&freq->c, the_hash_algo);
- freq->hash_ctx_valid = 1;
freq->url = get_remote_object_url(base_url, hex, 0);
@@ -2989,7 +2988,6 @@ int finish_http_object_request(struct http_object_request *freq)
}
git_hash_final_oid(&freq->real_oid, &freq->c);
- freq->hash_ctx_valid = 0;
if (freq->zret != Z_STREAM_END) {
unlink_or_warn(freq->tmpfile.buf);
return -1;
@@ -3030,8 +3028,7 @@ void release_http_object_request(struct http_object_request **freq_p)
curl_slist_free_all(freq->headers);
strbuf_release(&freq->tmpfile);
git_inflate_end(&freq->stream);
- if (freq->hash_ctx_valid)
- git_hash_discard(&freq->c);
+ git_hash_discard(&freq->c);
free(freq);
*freq_p = NULL;
diff --git a/http.h b/http.h
index 6b0639150f..729c51904d 100644
--- a/http.h
+++ b/http.h
@@ -255,7 +255,6 @@ struct http_object_request {
struct object_id oid;
struct object_id real_oid;
struct git_hash_ctx c;
- int hash_ctx_valid;
git_zstream stream;
int zret;
int rename;
--
2.55.0.459.g1b256877c9
^ permalink raw reply related
* [PATCH 5/7] csum-file: use idempotent git_hash_discard()
From: Jeff King @ 2026-07-07 5:07 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707045556.GA1288172@coredump.intra.peff.net>
Now that it is safe to call git_hash_discard() even after finalizing it,
we can simplify our cleanup logic a bit. This is mostly undoing a few
bits of 64337aecde (csum-file: always finalize or discard hash,
2026-07-02):
- We no longer need a separate free_hashfile_memory() function for
finalize_hashfile(). It can just call free_hashfile(), which will
now discard (or not) the hash as appropriate.
- When f->skip_hash is set, we don't need to discard; we can rely on
free_hashfile() to do it.
Signed-off-by: Jeff King <peff@peff.net>
---
csum-file.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
diff --git a/csum-file.c b/csum-file.c
index 7e81391524..fe18ee1de3 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -55,32 +55,25 @@ void hashflush(struct hashfile *f)
}
}
-static void free_hashfile_memory(struct hashfile *f)
+void free_hashfile(struct hashfile *f)
{
+ git_hash_discard(&f->ctx);
free(f->buffer);
free(f->check_buffer);
free(f);
}
-void free_hashfile(struct hashfile *f)
-{
- git_hash_discard(&f->ctx);
- free_hashfile_memory(f);
-}
-
int finalize_hashfile(struct hashfile *f, unsigned char *result,
enum fsync_component component, unsigned int flags)
{
int fd;
hashflush(f);
- if (f->skip_hash) {
- git_hash_discard(&f->ctx);
+ if (f->skip_hash)
hashclr(f->buffer, f->algop);
- } else {
+ else
git_hash_final(f->buffer, &f->ctx);
- }
if (result)
hashcpy(result, f->buffer, f->algop);
@@ -105,7 +98,7 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
if (close(f->check_fd))
die_errno("%s: sha1 file error on close", f->name);
}
- free_hashfile_memory(f);
+ free_hashfile(f);
return fd;
}
--
2.55.0.459.g1b256877c9
^ permalink raw reply related
* [PATCH 4/7] hash: make git_hash_discard() idempotent
From: Jeff King @ 2026-07-07 5:07 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707045556.GA1288172@coredump.intra.peff.net>
You must always either finalize or discard a hash context to release any
resources, but you must call only one such function. This creates extra
work for some callers, since their cleanup code paths need to know
whether they got there via their happy path (and the finalization
happened) or due to an error (in which case they need to discard).
Let's add an "active" flag that turns a redundant discard into a noop.
That lets you safely do this:
git_hash_init(&ctx, algo);
...
if (some_error)
goto out;
...
git_hash_final(result, &ctx);
out:
git_hash_discard(&ctx);
This should avoid future errors, and will also let us simplify a few
existing callers (in future patches).
Signed-off-by: Jeff King <peff@peff.net>
---
hash.c | 6 ++++++
hash.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/hash.c b/hash.c
index 55d1d41770..b1296f0018 100644
--- a/hash.c
+++ b/hash.c
@@ -285,6 +285,7 @@ void git_hash_free(struct git_hash_ctx *ctx)
void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop)
{
algop->init_fn(ctx);
+ ctx->active = true;
}
void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
@@ -300,16 +301,21 @@ void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)
void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)
{
ctx->algop->final_fn(hash, ctx);
+ ctx->active = false;
}
void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
{
ctx->algop->final_oid_fn(oid, ctx);
+ ctx->active = false;
}
void git_hash_discard(struct git_hash_ctx *ctx)
{
+ if (!ctx->active)
+ return;
ctx->algop->discard_fn(ctx);
+ ctx->active = false;
}
uint32_t hash_algo_by_name(const char *name)
diff --git a/hash.h b/hash.h
index 5686914b71..f97f7b9ff4 100644
--- a/hash.h
+++ b/hash.h
@@ -281,6 +281,7 @@ struct git_hash_ctx {
git_SHA_CTX_unsafe sha1_unsafe;
git_SHA256_CTX sha256;
} state;
+ bool active;
};
typedef void (*git_hash_init_fn)(struct git_hash_ctx *ctx);
--
2.55.0.459.g1b256877c9
^ permalink raw reply related
* [PATCH 3/7] hash: document function pointers and wrappers
From: Jeff King @ 2026-07-07 5:05 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, brian m. carlson
In-Reply-To: <20260707045556.GA1288172@coredump.intra.peff.net>
We want people to use the git_hash_*() wrappers rather than the bare
function pointers in the git_hash_algo struct. Let's document them
rather than the bare pointers, and warn people away from the pointers.
Coccinelle will eventually force the use of the wrappers, but it's
helpful to lead readers in the right direction from the start.
While we're here we can document a few other bits of wisdom I've turned
up while working in this area:
- You have to initialize the destination of a git_hash_clone(). This
is something we may eventually change for efficiency, but we should
definitely document the requirement for now.
- You must eventually finalize or discard a hash, since some backends
may allocate resources during initialization.
Signed-off-by: Jeff King <peff@peff.net>
---
hash.h | 43 ++++++++++++++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 11 deletions(-)
diff --git a/hash.h b/hash.h
index 0a23ef4dfd..5686914b71 100644
--- a/hash.h
+++ b/hash.h
@@ -309,22 +309,15 @@ struct git_hash_algo {
/* The block size of the hash. */
size_t blksz;
- /* The hash initialization function. */
+ /*
+ * Low-level implementation hooks. Callers should use the git_hash_*
+ * wrappers below rather than invoking these directly.
+ */
git_hash_init_fn init_fn;
-
- /* The hash context cloning function. */
git_hash_clone_fn clone_fn;
-
- /* The hash update function. */
git_hash_update_fn update_fn;
-
- /* The hash finalization function. */
git_hash_final_fn final_fn;
-
- /* The hash finalization function for object IDs. */
git_hash_final_oid_fn final_oid_fn;
-
- /* Discard an initialized hash without finalizing. */
git_hash_discard_fn discard_fn;
/* The OID of the empty tree. */
@@ -341,12 +334,40 @@ struct git_hash_algo {
};
extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+/*
+ * Prepare an uninitialized hash context for use. You must eventually release
+ * the context with with git_hash_final() (or final_oid()) or by calling
+ * git_hash_discard().
+ */
void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop);
+
+/*
+ * Clone the state of a hash. Both src and dst must have been initialized with
+ * git_hash_init().
+ */
void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src);
+
+/*
+ * Add more data to an initialized hash context.
+ */
void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len);
+
+/*
+ * Retrieve the final hash value from a context, releasing any resources.
+ */
void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx);
+
+/*
+ * Like git_hash_final(), but write the result into an object_id.
+ */
void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx);
+
+/*
+ * Discard a hash context without computing the final value, but still
+ * releasing any resources.
+ */
void git_hash_discard(struct git_hash_ctx *ctx);
+
const struct git_hash_algo *hash_algo_ptr_by_number(uint32_t algo);
struct git_hash_ctx *git_hash_alloc(void);
void git_hash_free(struct git_hash_ctx *ctx);
--
2.55.0.459.g1b256877c9
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox