* Re: [PATCH v1] repository: move fetch_if_missing into struct repository
From: Patrick Steinhardt @ 2026-07-15 6:35 UTC (permalink / raw)
To: Tian Yuchen
Cc: git, five231003, hariom18599, Christian Couder, Ayush Chandekar,
Olamide Caleb Bello
In-Reply-To: <20260715011850.3181131-1-cat@malon.dev>
On Wed, Jul 15, 2026 at 09:18:50AM +0800, Tian Yuchen wrote:
> The global variable 'fetch_if_missing' controls whether a missing
> object check should prompt a lazy fetch from a promisor remote.
> In order to continue the libification effort, move it into
> 'struct repository' and initialize it to 1 by default to keep the
> previous behavior.
Right. I was also thinking about moving this into a non-global scope
multiple times. I was approaching this a bit differently though: it's
ultimately a property of the object database whether or not we want to
accept missing objects, so I moved it in there instead.
I don't really think there's a downside with your version, though. Quite
on the contrary: we can really only perform the backfill fetches with a
whole repository at hand anyway. So conceptually your version might even
be more sensible.
> Subsystems that already pass around a repository pointer, are
> updated to read this flag directly from their respective 'repo'
> instances. For the rest, we access 'the_repository'.
>
> Note that in builtin/fsck.c and builtin/index-pack.c, when running
> related commands with the '-h' parameter, the 'repo' pointer is not
> passed in. To prevent null pointer dereferences, we defer
> operations on the repo in until after parameter parsing is complete.
s/on the repo in/on the repo/
> diff --git a/builtin/index-pack.c b/builtin/index-pack.c
> index 0793dc595c..721d576938 100644
> --- a/builtin/index-pack.c
> +++ b/builtin/index-pack.c
> @@ -1898,15 +1898,16 @@ int cmd_index_pack(int argc,
> int report_end_of_input = 0;
> int hash_algo = 0;
>
> + show_usage_if_asked(argc, argv, index_pack_usage);
> +
> /*
> * index-pack never needs to fetch missing objects except when
> * REF_DELTA bases are missing (which are explicitly handled). It only
> * accesses the repo to do hash collision checks and to check which
> * REF_DELTA bases need to be fetched.
> */
> - fetch_if_missing = 0;
> -
> - show_usage_if_asked(argc, argv, index_pack_usage);
> + if (repo)
> + repo->fetch_if_missing = 0;
>
> disable_replace_refs();
>
Okay. This command can run without a repository, in which case we'll end
up just indexing the pack. My assumption is that we'll probably end up
using `the_repository` if so, as we still use `the_repository` in this
file. So could this here cause a change in behaviour?
If the answer is "maybe" I'd propose that we simply continue to use
`the_repository` here.
> diff --git a/revision.c b/revision.c
> index e91d7e1f11..bb645654c3 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2714,7 +2714,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
> revs->ignore_missing = 1;
> } else if (opt && opt->allow_exclude_promisor_objects &&
> !strcmp(arg, "--exclude-promisor-objects")) {
> - if (fetch_if_missing)
> + if (revs->repo->fetch_if_missing)
> BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
> revs->exclude_promisor_objects = 1;
> } else {
This one here also makes me wonder whether it could cause weird
interactions in case a caller passes a repository other than
`the_repository`. It ideally _shouldn't_, but it's hard to tell because
we still use `the_repository` in lots of places here.
Thanks!
Patrick
^ permalink raw reply
* Re: [PATCH] remote-curl: simplify passing of push specs
From: Patrick Steinhardt @ 2026-07-15 6:41 UTC (permalink / raw)
To: René Scharfe; +Cc: Git List
In-Reply-To: <935883f3-3be4-4c51-9711-5208b9ef9ca1@web.de>
On Wed, Jul 15, 2026 at 06:41:17AM +0200, René Scharfe wrote:
> diff --git a/remote-curl.c b/remote-curl.c
> index 9e614c5567..2c35dd5240 100644
> --- a/remote-curl.c
> +++ b/remote-curl.c
> @@ -1340,10 +1340,9 @@ static void parse_get(const char *arg)
> fflush(stdout);
> }
>
> -static int push_dav(int nr_spec, const char **specs)
> +static int push_dav(const char **specs)
> {
> struct child_process child = CHILD_PROCESS_INIT;
> - size_t i;
>
> child.git_cmd = 1;
> strvec_push(&child.args, "http-push");
I wonder whether the interface would be even better if we simply passed
around a `const struct strvec *` directly. That makes it explicit what
kind of guarantees we have, and all transitive callers already have one
available anyway.
> @@ -1353,15 +1352,14 @@ static int push_dav(int nr_spec, const char **specs)
> if (options.verbosity > 1)
> strvec_push(&child.args, "--verbose");
> strvec_push(&child.args, url.buf);
> - for (i = 0; i < nr_spec; i++)
> - strvec_push(&child.args, specs[i]);
> + strvec_pushv(&child.args, specs);
I thought that we had something like `strvec_pushvec()` that knew to
also optimize for this case so that we don't have to reallocate the
vector multiple times. And if we had that function it would even be more
efficient to pass it down the stack. But we seemingly don't have it, so
that argument is kind of moot.
Other than those nits the patch looks good to me, thanks!
Patrick
^ permalink raw reply
* Re: [PATCH v4] show-branch: convert per-branch flags to commit-slab
From: Patrick Steinhardt @ 2026-07-15 6:47 UTC (permalink / raw)
To: Gatla Vishweshwar Reddy; +Cc: gitster, git
In-Reply-To: <20260715041856.51526-1-gatlavishweshwarreddy26@gmail.com>
On Wed, Jul 15, 2026 at 09:48:56AM +0530, Gatla Vishweshwar Reddy wrote:
> Changes in v4:
> - Fix show_independent() to use has_only_rev_flag_bit() instead of
> test_rev_flag_bit(), preserving the original semantics: a commit is
> independent only if reachable from exactly one tip, not merely if
> the i-th bit is set.
Please note that it's considered good etiquette on our mailing list to
not only post new versions of a patch series, but to also reply to at
least some of the review comments you got [1]. This makes the reviewer
feel like they're not only talking to a code producing entity (read: AI
prompt), but rather to a human on the other side of the internet.
Thanks!
Patrick
[1]: https://git-scm.com/docs/MyFirstContribution#reviewing
^ permalink raw reply
* Re: [PATCH 02/11] config: propagate launch_editor() failure in show_editor()
From: Patrick Steinhardt @ 2026-07-15 6:58 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <0692704d45060a62579b50dd7a2f07da04f435c8.1784069325.git.gitgitgadget@gmail.com>
On Tue, Jul 14, 2026 at 10:48:35PM +0000, Johannes Schindelin via GitGitGadget wrote:
> diff --git a/builtin/config.c b/builtin/config.c
> index 8d8ec0beea..1307fdb0d6 100644
> --- a/builtin/config.c
> +++ b/builtin/config.c
> @@ -1313,7 +1313,10 @@ static int show_editor(struct config_location_options *opts)
> else if (errno != EEXIST)
> die_errno(_("cannot create configuration file %s"), config_file);
> }
> - launch_editor(config_file, NULL, NULL);
> + if (launch_editor(config_file, NULL, NULL)) {
> + free(config_file);
> + return -1;
> + }
All error paths in `launch_editor()` already print an error message, so
we indeed don't have to do anything but bubble up the error here.
Patrick
^ permalink raw reply
* Re: [PATCH 06/11] compat/pread: check initial lseek for errors
From: Patrick Steinhardt @ 2026-07-15 6:58 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <b31e0326e7c4f97753c80077c8f0927504f40370.1784069325.git.gitgitgadget@gmail.com>
On Tue, Jul 14, 2026 at 10:48:39PM +0000, Johannes Schindelin via GitGitGadget wrote:
> diff --git a/compat/pread.c b/compat/pread.c
> index 484e6d4c71..ac7d058cb8 100644
> --- a/compat/pread.c
> +++ b/compat/pread.c
> @@ -7,6 +7,8 @@ ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
> ssize_t rc;
>
> current_offset = lseek(fd, 0, SEEK_CUR);
> + if (current_offset < 0)
> + return -1;
>
> if (lseek(fd, offset, SEEK_SET) < 0)
> return -1;
Heh, funny. I wanted to complain about misindentation here, but your new
code is actually indented correctly. It's everything else in this file
that is indented with spaces.
Patrick
^ permalink raw reply
* Re: [PATCH 07/11] transport-helper: check dup() return in get_exporter
From: Patrick Steinhardt @ 2026-07-15 6:58 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <1792042098cd50ba164b90e5ce62430037661343.1784069325.git.gitgitgadget@gmail.com>
On Tue, Jul 14, 2026 at 10:48:40PM +0000, Johannes Schindelin via GitGitGadget wrote:
> diff --git a/transport-helper.c b/transport-helper.c
> index 80f90eb7ba..31883b244e 100644
> --- a/transport-helper.c
> +++ b/transport-helper.c
> @@ -487,6 +487,8 @@ static int get_exporter(struct transport *transport,
> /* we need to duplicate helper->in because we want to use it after
> * fastexport is done with it. */
> fastexport->out = dup(helper->in);
> + if (fastexport->out < 0)
> + return error_errno(_("could not dup helper output fd"));
> strvec_push(&fastexport->args, "fast-export");
> strvec_push(&fastexport->args, "--use-done-feature");
> strvec_push(&fastexport->args, data->signed_tags ?
Makes sense. The only caller already knows to die in case it sees a
non-zero return value.
Patrick
^ permalink raw reply
* Re: [PATCH 10/11] bisect: check get_terms return at all call sites
From: Patrick Steinhardt @ 2026-07-15 6:58 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <c0827a79476d02f2b09ded919b44860e3743fbe0.1784069325.git.gitgitgadget@gmail.com>
On Tue, Jul 14, 2026 at 10:48:43PM +0000, Johannes Schindelin via GitGitGadget wrote:
> From: Johannes Schindelin <johannes.schindelin@gmx.de>
>
> Six callers of get_terms() silently discard its return value. When
> get_terms fails (missing or truncated BISECT_TERMS file), the term
> strings remain NULL or empty, causing confusing downstream
> behavior: commands like "bisect next" or "bisect run" proceed with
> empty term strings, producing nonsensical ref names (refs/bisect/
> with no suffix) and misleading error messages.
>
> Add checks at each call site so that a failed get_terms produces a
> clear "no terms defined" error, matching the pattern already used
> in bisect_terms() at line 512. The check tests the term pointers
> rather than the return value because some callers (bisect skip,
> legacy bad/good) call set_terms before get_terms, and the
> set_terms values should survive a get_terms failure.
Hm. Are there any callers that accept the case where either `term->bad`
or `term->good` are `NULL`? If not, should we maybe adapt the function
itself to return an error if so and then have all callers only ever
check for the return value of `get_term()` instead of also having to
check the result? That might also allow us to deduplicate the error
messages.
Patrick
^ permalink raw reply
* Re: [PATCH 11/11] bisect: handle dup() failure when redirecting stdout
From: Patrick Steinhardt @ 2026-07-15 6:58 UTC (permalink / raw)
To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <2da452e39cbe1bd53da9d76fa7f7615c1a453634.1784069325.git.gitgitgadget@gmail.com>
On Tue, Jul 14, 2026 at 10:48:44PM +0000, Johannes Schindelin via GitGitGadget wrote:
> diff --git a/builtin/bisect.c b/builtin/bisect.c
> index 15a2a30f89..801daf8c78 100644
> --- a/builtin/bisect.c
> +++ b/builtin/bisect.c
> @@ -1308,6 +1308,11 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
>
> fflush(stdout);
> saved_stdout = dup(1);
> + if (saved_stdout < 0) {
> + res = error_errno(_("could not duplicate stdout"));
> + close(temporary_stdout_fd);
> + break;
> + }
> dup2(temporary_stdout_fd, 1);
Shouldn't we also verify the return value of `dup2()` while at it?
Patrick
^ permalink raw reply
* Re: What's cooking in git.git (Jul 2026, #06)
From: Patrick Steinhardt @ 2026-07-15 7:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqtsq1qfzq.fsf@gitster.g>
On Tue, Jul 14, 2026 at 05:00:09PM -0700, Junio C Hamano wrote:
> * ps/odb-for-each-object-filter (2026-07-13) 10 commits
> - builtin/cat-file: filter objects via object database
> - odb: introduce object filters to `odb_for_each_object()`
> - pack-bitmap: introduce function to open bitmap for a single source
> - pack-bitmap: drop `_1` suffix from functions that open bitmaps
> - pack-bitmap: iterate object sources when opening bitmaps
> - pack-bitmap: allow aborting iteration of bitmapped objects
> - pack-objects: drop unused return value from add_object_entry()
> - pack-bitmap: mark object filter as `const`
> - odb/source-packed: improve lookup when enumerating objects
> - Merge branch 'ps/odb-drop-whence' into ps/odb-for-each-object-filter
> (this branch uses ps/odb-drop-whence.)
>
> The object database enumeration interface 'odb_for_each_object()'
> has been taught to accept object filters, allowing the underlying
> backends to optimize the traversal by using reachability bitmaps
> when available. 'git cat-file --batch-all-objects' has been updated
> to use this generic interface, simplifying its code and avoiding
> direct access to ODB backend internals.
>
> Will merge to 'next'?
> cf. <alW0KzSZuZnHmOZD@com-79390>
> source: <20260713-pks-odb-for-each-object-filter-v3-0-b3c65c641073@pks.im>
I've sent a tiny reroll that fixes two references to function names in
the commit messages. But other than that I think that the latest version
is ready to go.
> * cl/conditional-config-on-worktree-path (2026-07-09) 2 commits
> - config: add "worktree" and "worktree/i" includeIf conditions
> - config: refactor include_by_gitdir() into include_by_path()
>
> The '[includeIf "condition"]' conditional inclusion facility for
> configuration files has been taught to use the location of the
> worktree in its condition.
>
> Will merge to 'next'?
> cf. <alTJCTKR9jOWfgbk@pks.im>
> source: <20260710-includeif-worktree-v8-0-04686d8a616c@black-desk.cn>
I think this one is ready to go, too.
Thanks!
Patrick
^ permalink raw reply
* Re: [PATCH v3] show-branch: convert per-branch flags to commit-slab
From: Junio C Hamano @ 2026-07-15 7:20 UTC (permalink / raw)
To: Gatla Vishweshwar Reddy; +Cc: git
In-Reply-To: <20260715015158.48559-1-gatlavishweshwarreddy26@gmail.com>
Gatla Vishweshwar Reddy <gatlavishweshwarreddy26@gmail.com> writes:
> In response to Jeff King:
> - init_commit_rev_flags_with_stride() is used as foundation.
> Current stride=1 gives 64 branches. Dynamic stride for >64
> branches can be added as a follow-up.
If that is the case ...
> builtin/show-branch.c | 143 ++++++++++++++++++++++++------------------
> 1 file changed, 83 insertions(+), 60 deletions(-)
>
> diff --git a/builtin/show-branch.c b/builtin/show-branch.c
> index f02831b085..70436007ec 100644
> --- a/builtin/show-branch.c
> +++ b/builtin/show-branch.c
> @@ -34,16 +34,9 @@ static enum git_colorbool showbranch_use_color = GIT_COLOR_UNKNOWN;
>
> static struct strvec default_args = STRVEC_INIT;
>
> -/*
> - * TODO: convert this use of commit->object.flags to commit-slab
> - * instead to store a pointer to ref name directly. Then use the same
> - * UNINTERESTING definition from revision.h here.
> - */
> #define UNINTERESTING 01
... it is a bit premature to lose this TODO comment (which was
written, inspired by what I wrote ages ago, in [*1*]), until that
happens.
On the other hand, you can and should lose our own #define
UNINTERSTING here even with this "slab stores a single u64 word"
rewrite, and instead use the common one from <revision.h> header
file.
Thanks.
[Reference]
*1* https://lore.kernel.org/git/xmqq36yud9bp.fsf@gitster-ct.c.googlers.com/
^ permalink raw reply
* Re: [PATCH v7 3/3] replay: offer an option to linearize the commit topology
From: Elijah Newren @ 2026-07-15 7:34 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Toon Claes, git, Johannes Schindelin
In-Reply-To: <xmqqbjcawnhp.fsf@gitster.g>
On Mon, Jul 13, 2026 at 3:09 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> > For what it's worth, looking back at the v5 thread, it seems the `base
> > = last_commit` rule came in to fix the real bug Junio and Phillip
> > pointed out there -- that without it, only one side of a linearized
> > merge survived. That fix is clearly correct for the single-branch
> > case. My worry is only that applying it unconditionally reintroduces
> > the multiple-positive-refs ordering problem we deliberately avoid
> > elsewhere. Making `--linearize` reject multiple positive refs would
> > keep the merge-flattening fix while sidestepping this entirely.
> >
> >> A user
> >> who wants to linearize ranges independently is advised to use separate
> >> git-replay(1) invocations.
> >
> > Which, to me, is another argument for just disallowing multiple
> > positive refs under `--linearize`: if the recommended way to do it is
> > separate invocations anyway, we may as well require them.
>
> Hmph. To me, this is slightly different. It acts more like an
> escape hatch: "if you really do not want to mix unrelated things
> into a single linear history, you can do this other thing."
>
> Stepping back, the unpredictable order of multiple merged lines of
> history exists even without multiple positive refs. If you have
> independent lines of development that were merged and you linearize
> them, someone must choose which line comes first. If you let the
> machinery make that decision, the resulting commit order may not
> reflect your preferences.
>
> While I rarely perform octopus merges anymore, in situations where an
> octopus merge is appropriate (e.g., when you have N independent
> branches and their merge order does not matter), linearizing such
> a history into a random sequence of N segments, built on top of
> one another in an unspecified order, could actually be considered a
> feature. You do not have to make a decision about something that is
> inconsequential.
You're right that when flattening merges within a single branch, the
machinery must pick an order, and that's fine — unavoidable, even. My
objection isn't that; it's primarily the concatenation of distinct
branches named on the command line into one chain, and, as a secondary
point, the ignoring of the order of branches explicitly specified by
the user on the command line.
Concretely: I have three branches to rebase onto master; one of them
happens to contain a merge I'd like flattened. I add --linearize for
that one merge — and now all three branches are silently concatenated
into a single chain. That makes no sense to me, and I think won't to
most users.
Anyway, I think I must have explained my position rather poorly; your
response suggests I buried my main points, so let me try to restate
them:
TL;DR version; my problems with the current implementation of
`--linearize` are that it:
* Makes the rare usecase easy, and ignores the common usecase
* Makes it asymmetrically difficult to recover for those that wanted
the common usecase instead of the easy
* Makes `--linearize` mean something other than "remove non-linearity"
* Turns multiple branches into one, but updates several branches anyway
* Ignores order specified by the user on the command line
* Introduces an inconsistency within git-replay between `--advance`
and `--linearize --onto`
(The last three items being minor compared to the first three.)
Longer version:
Consider the following history
M1 M2 M3 M4 M5
*---*---*---*---* <- master
\ \
\ \ A1 A2 A3 A4
\ \-*---*---*---* <- branchA
\ \
\ -*---* <- branchC
\ C1 C2
\
\-*---*---* <- branchB
B1 B2 B3
git replay was designed to allow you to update all your branches at once.
For example, with this above history, running
git replay --onto master branchA branchB branchC
will rebase all three branches onto master (and handles the shared portion
of history between branchA and branchC in the obvious way):
M1 M2 M3 M4 M5
*---*---*---*---* <- master
|
| A1 A2 A3 A4
|--*---*---*---* <- branchA
| \
| -*---* <- branchC
| C1 C2
|
\-*---*---* <- branchB
B1 B2 B3
With the current implementation of --linearize, adding that flag, i.e.
git replay --linearize --onto master branchA branchB branchC
would instead give something like:
M1 M2 M3 M4 M5 B1 B2 B3 A1 A2 C1 C2 A3 A4
*---*---*---*---*---*---*---*---*---*---*---*---*---*
^ ^ ^ ^
| | | |
master branchB branchC branchA
This topology strikes me as something that users would very rarely ever
want. Further, it:
* Makes one question why branchB and branchC were kept instead of
deleted; if the whole point is to concatenate the branches, then
since whichever branch lands on top contains the other two, why not
just get rid of the others?
* Makes the command behave differently on *already linear* history
when --linearize is added, which makes no sense to me.
* (Minor point, but still confusing to me) Ignores the order of
branches the user employed on the command line
Of course, the above involves no merges, so let's introduce one; consider
the following alternate initial history:
M1 M2 M3 M4 M5
*---*---*---*---* <- master
| \
| \ A1 A2 A4 A6 A7 A8
| \-*---*---*---*---*---* <- branchA
\ \ / \
\ *---* -*---* <- branchC
\ A3 A5 C1 C2
\
\-*---* <- branchB
B1 B2
Replaying the three branches,
git replay --onto master branchA branchB branchC
we would expect the base of the branches to simply be updated to current
master:
M1 M2 M3 M4 M5
*---*---*---*---* <- master
|
| A1 A2 A4 A6 A7 A8
|---*---*---*---*---*---* <- branchA
| \ / \
| *---* -*---* <- branchC
| A3 A5 C1 C2
|
\-*---* <- branchB
B1 B2
If you were to add --linearize, i.e.
git replay --linearize --onto master branchA branchB branchC
I personally would expect:
M1 M2 M3 M4 M5
*---*---*---*---* <- master
|
| A1 A2 A4 A3 A5 A7 A8
|---*---*---*---*---*---*---* <- branchA
| \
| -*---* <- branchC
| C1 C2
|
\-*---* <- branchB
B1 B2
In other words, `--linearize` should remove the non-linearity in the graph.
Instead, the current implementation will return something like:
M1 M2 M3 M4 M5 A1 A2 A4 A3 A5 A7 C1 C2 B1 B2 A8
*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*
^ ^ ^ ^
| | | \
master branchC branchB branchA
I can only imagine this rarely being useful to the user.
But to make it worse, please consider the difficulty of someone who
wanted the bottom graph but got the top one, vs. the difficulty of
someone who wanted the top graph but got the bottom one:
* (wanted bottom, got top) Just rebase branchB and branchA again; easy
* (wanted top, got bottom) You need to meticulously figure out the common
points of history and which sets of commits belong to each branch in
order to sequentially rebase each branch into the expected result.
In particular, the need to meticulously track start and endpoints with
individual
rebases was one of the reasons that led to `git replay` rather than improvements
to `git rebase`; the latter was so focused on single branches, that it
wasn't really
possible to extend to multiple branches. It's thus rather
disappointing to see new
flags for `git replay` that make handling multiple branches more painful.
There's actually one more (admittedly minor) issue as well: it creates
an inconsistency within git-replay itself. The `--advance` flag has a
check to error out when multiple positive refs are specified solely
because I thought it was weird to override the order of branches the
user specified on the command line (and didn't want to implement
something that could force the ordering of the revision walk); the error
message even states "because the ordering would be ill-defined". For
consistency, either both should be fine with ignoring the order of
revisions specified by the user, or neither should be.
So, what to do?
Both paths I have in mind end at the same place; the only real question
is whether the desired behavior lands in this series or as follow-up.
The minimal move is to make --linearize reject multiple positive refs for
now (exactly as --advance and --revert already do), unblocking this series
so it can merge down nearly as-is, and leave per-branch linearization as
future work.
The complete move is to implement that desired behavior now, by tracking a
last_commit per command-line branch so each branch is linearized
independently.
The reason I am comfortable with erroring out as a stopgap: turning an
error into working behavior later never breaks anyone, whereas letting the
current concatenation semantics reach 'master' risks users coming to
depend on them, which would make switching to the better behavior a
compatibility break. Erroring now keeps our options open; merging as-is
quietly closes them. (git-replay is still EXPERIMENTAL, so this is not
fatal either way, but it seems better not to paint ourselves into a
corner.)
For this series I would be perfectly happy with just the error; the
per-branch last_commit tracking can come later.
^ permalink raw reply
* [PATCH v2 0/7] refs: remove use of `the_repository`
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260709-pks-refs-wo-the-repository-v1-0-1ad6f27529c9@pks.im>
Hi,
this patch series refactors the ref subsystem to drop uses of
`the_repository`. These patches were part of a discarded attempt to
make the initialization of the refdb eager. I guess they make sense by
themselves though, so here we go.
Note that these patches contain a slight tangent to also adapt
"worktree.c". This is one of the subsystems that caused problems with
eager refdb initialization because of `has_worktrees()`, so I refactored
this subsystem while at it.
The series is built on top of f85a7e6620 (Start Git 2.56 cycle,
2026-07-06) with ps/refs-writing-subcommands at 002fe677ca
(builtin/refs: add "rename" subcommand, 2026-07-06) merged into it.
Despite that, there's a small set of conflicts with "seen" that can be
merged like this:
diff --cc lib/setup.c
index 505e8d7bf2,d31808130b..0000000000
--- a/lib/setup.c
+++ b/lib/setup.c
@@@ -2822,15 -2847,16 +2848,16 @@@ 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);
+ separate_git_dir(repo, git_dir, original_git_dir);
- }
- else {
- set_git_dir(repo, git_dir, 1);
+ } else {
+ apply_and_export_relative_gitdir(repo, git_dir, 1);
git_dir = repo_get_git_dir(repo);
}
- startup_info->have_repository = 1;
+
+ if (worktree)
+ set_git_work_tree(repo, worktree);
/*
* Check to see if the repository version is right.
diff --git a/lib/refs/files-backend.c b/lib/refs/files-backend.c
index f672059333..3ba1b4eac4 100644
--- a/lib/refs/files-backend.c
+++ b/lib/refs/files-backend.c
@@ -859,7 +859,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
} else {
unable_to_lock_message(ref_file.buf, myerr, err);
if (myerr == EEXIST) {
- if (repo_ignore_case(the_repository) &&
+ if (repo_ignore_case(refs->base.repo) &&
transaction_has_case_conflicting_update(transaction, update)) {
/*
* In case-insensitive filesystems, ensure that conflicts within a
@@ -973,7 +973,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
* conflicts between 'foo' and 'Foo/bar'. So let's lowercase
* the refname.
*/
- if (repo_ignore_case(the_repository)) {
+ if (repo_ignore_case(refs->base.repo)) {
struct strbuf lower = STRBUF_INIT;
strbuf_addstr(&lower, refname);
Changes in v2:
- Fix default value for "core.packedRefsTimeout".
- Link to v1: https://patch.msgid.link/20260709-pks-refs-wo-the-repository-v1-0-1ad6f27529c9@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (7):
refs/packed: de-globalize handling of "core.packedRefsTimeout"
refs/packed: drop `USE_THE_REPOSITORY_VARIABLE`
refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
worktree: refactor code to use available repositories
worktree: pass repository to file-local functions
worktree: pass repository to public functions
refs: remove remaining uses of `the_repository`
branch.c | 6 +-
builtin/branch.c | 16 +++--
builtin/check-ref-format.c | 2 +-
builtin/checkout.c | 2 +-
builtin/config.c | 2 +-
builtin/fsck.c | 6 +-
builtin/gc.c | 2 +-
builtin/merge.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 2 +-
builtin/reflog.c | 4 +-
builtin/refs.c | 2 +-
builtin/worktree.c | 32 +++++----
reachable.c | 4 +-
ref-filter.c | 2 +-
refs.c | 23 +++----
refs.h | 5 +-
refs/files-backend.c | 31 +++++----
refs/packed-backend.c | 20 ++++--
revision.c | 6 +-
setup.c | 7 +-
submodule.c | 2 +-
t/helper/test-ref-store.c | 2 +-
worktree.c | 166 +++++++++++++++++++++++++--------------------
worktree.h | 27 +++++---
25 files changed, 206 insertions(+), 169 deletions(-)
Range-diff versus v1:
1: e2de4a7ae9 ! 1: 5c9df5ce44 refs/packed: de-globalize handling of "core.packedRefsTimeout"
@@ refs/packed-backend.c: int packed_refs_lock(struct ref_store *ref_store, int fla
- repo_config_get_int(the_repository, "core.packedrefstimeout", &timeout_value);
- timeout_configured = 1;
+ if (!refs->timeout_configured) {
-+ repo_config_get_int(ref_store->repo, "core.packedrefstimeout", &refs->timeout_value);
++ if (repo_config_get_int(ref_store->repo, "core.packedrefstimeout",
++ &refs->timeout_value))
++ refs->timeout_value = 1000;
+ refs->timeout_configured = true;
}
2: 3c96ed5d22 = 2: 39d91a88ad refs/packed: drop `USE_THE_REPOSITORY_VARIABLE`
3: a724abf676 = 3: 5d3d4c505f refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
4: ce64ae5edd = 4: cff8c31110 worktree: refactor code to use available repositories
5: fa5b6c95e2 = 5: b53b5f67f7 worktree: pass repository to file-local functions
6: 89b0263583 = 6: 0927572842 worktree: pass repository to public functions
7: e4ac64f7c2 = 7: bb3b3b1e80 refs: remove remaining uses of `the_repository`
---
base-commit: f035246f779167db3506394141b59472d544af65
change-id: 20260618-pks-refs-wo-the-repository-7e43e29371ac
^ permalink raw reply related
* [PATCH v2 1/7] refs/packed: de-globalize handling of "core.packedRefsTimeout"
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
When locking the "packed-refs" file we allow the user to configure a
timeout for how long we try taking the lock. This is configurable via
"core.packedRefsTimeout", which we parse in `packed_refs_lock()`.
The parsed value is stored in function-static variables though, which of
course has the effect that we'll only ever use the timeout configured in
the first packed reference store that we see. Consequently, if we ever
were to handle stores from different repositories, then we'd use the
same configuration for both stores even if they diverge.
This is of course a somewhat theoretical concern -- we don't typically
handle multiple packed stores, and even if we did it's very unlikely
that the user has configured different timeout values for each of them.
But still, this is a code smell, and an unnecessary one, too.
Fix the issue by moving the value into `struct packed_ref_store` so that
it can be parsed per store.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 499cb55dfa..14b27d24ec 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -162,6 +162,13 @@ struct packed_ref_store {
* `packed_ref_store`) must not be freed.
*/
struct tempfile *tempfile;
+
+ /*
+ * Timeout when taking the "packed-refs.lock" file. configurable via
+ * "core.packedRefsTimeout".
+ */
+ bool timeout_configured;
+ int timeout_value;
};
/*
@@ -1233,12 +1240,12 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
struct packed_ref_store *refs =
packed_downcast(ref_store, REF_STORE_WRITE | REF_STORE_MAIN,
"packed_refs_lock");
- static int timeout_configured = 0;
- static int timeout_value = 1000;
- if (!timeout_configured) {
- repo_config_get_int(the_repository, "core.packedrefstimeout", &timeout_value);
- timeout_configured = 1;
+ if (!refs->timeout_configured) {
+ if (repo_config_get_int(ref_store->repo, "core.packedrefstimeout",
+ &refs->timeout_value))
+ refs->timeout_value = 1000;
+ refs->timeout_configured = true;
}
/*
@@ -1249,7 +1256,7 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
if (hold_lock_file_for_update_timeout(
&refs->lock,
refs->path,
- flags, timeout_value) < 0) {
+ flags, refs->timeout_value) < 0) {
unable_to_lock_message(refs->path, errno, err);
return -1;
}
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 2/7] refs/packed: drop `USE_THE_REPOSITORY_VARIABLE`
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
There's a single user of `the_repository` in the "packed" reference
backend. Convert it to instead use the backend's repository and drop
`USE_THE_REPOSITORY_VARIABLE`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/packed-backend.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 14b27d24ec..c5d96793fa 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "../git-compat-util.h"
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 3/7] refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
We have a bunch of users of `the_repository` in the "files" backend, all
of which are trivial to convert to instead use the backend's own repo.
Do so.
There is one more dependency on global state though via `ignore_case`,
and thus we can't trivially remove `USE_THE_REPOSITORY_VARIABLE`. But
this is the only use of global state, and we want to ensure that we
don't unwittingly reintroduce a dependency on `the_repository` going
forward.
Add an extern declaration for `ignore_case` so that it becomes
accessible even without `USE_THE_REPOSITORY_VARIABLE` and drop the
define itself.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
refs/files-backend.c | 31 +++++++++++++++++--------------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 3df56c25c8..09e1be838a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "../git-compat-util.h"
@@ -29,6 +28,9 @@
#include "../revision.h"
#include <wildmatch.h>
+/* So that we can drop `USE_THE_REPOSITORY_VARIABLE`. */
+extern int ignore_case;
+
/*
* This backend uses the following flags in `ref_update::flags` for
* internal bookkeeping purposes. Their numerical values must not
@@ -788,7 +790,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
files_ref_path(refs, &ref_file, refname);
retry:
- switch (safe_create_leading_directories(the_repository, ref_file.buf)) {
+ switch (safe_create_leading_directories(refs->base.repo, ref_file.buf)) {
case SCLD_OK:
break; /* success */
case SCLD_EXISTS:
@@ -1164,7 +1166,8 @@ typedef int create_file_fn(const char *path, void *cb);
* recent call of fn. fn is always called at least once, and will be
* called more than once if it returns ENOENT or EISDIR.
*/
-static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
+static int raceproof_create_file(struct files_ref_store *refs,
+ const char *path, create_file_fn fn, void *cb)
{
/*
* The number of times we will try to remove empty directories
@@ -1220,7 +1223,7 @@ static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
strbuf_addstr(&path_copy, path);
do {
- scld_result = safe_create_leading_directories(the_repository, path_copy.buf);
+ scld_result = safe_create_leading_directories(refs->base.repo, path_copy.buf);
if (scld_result == SCLD_OK)
goto retry_fn;
} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
@@ -1289,7 +1292,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
cb_data.lk = &lock->lk;
cb_data.repo = refs->base.repo;
- if (raceproof_create_file(ref_file.buf, create_reflock, &cb_data)) {
+ if (raceproof_create_file(refs, ref_file.buf, create_reflock, &cb_data)) {
unable_to_lock_message(ref_file.buf, errno, err);
goto error_return;
}
@@ -1383,7 +1386,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
- null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL,
+ null_oid(refs->base.repo->hash_algo), &r->oid, NULL, NULL, NULL,
NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1629,7 +1632,7 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
files_reflog_path(refs, &path, newrefname);
files_reflog_path(refs, &tmp, TMP_RENAMED_LOG);
cb.tmp_renamed_log = tmp.buf;
- ret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);
+ ret = raceproof_create_file(refs, path.buf, rename_tmp_log_callback, &cb);
if (ret) {
if (errno == EISDIR)
error("directory not empty: %s", path.buf);
@@ -1916,13 +1919,13 @@ static int log_ref_setup(struct files_ref_store *refs,
char *logfile;
if (log_refs_cfg == LOG_REFS_UNSET)
- log_refs_cfg = is_bare_repository(the_repository) ? LOG_REFS_NONE : LOG_REFS_NORMAL;
+ log_refs_cfg = is_bare_repository(refs->base.repo) ? LOG_REFS_NONE : LOG_REFS_NORMAL;
files_reflog_path(refs, &logfile_sb, refname);
logfile = strbuf_detach(&logfile_sb, NULL);
if (force_create || should_autocreate_reflog(log_refs_cfg, refname)) {
- if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
+ if (raceproof_create_file(refs, logfile, open_or_create_logfile, logfd)) {
if (errno == ENOENT)
strbuf_addf(err, "unable to create directory for '%s': "
"%s", logfile, strerror(errno));
@@ -1955,7 +1958,7 @@ static int log_ref_setup(struct files_ref_store *refs,
}
if (*logfd >= 0)
- adjust_shared_perm(the_repository, logfile);
+ adjust_shared_perm(refs->base.repo, logfile);
free(logfile);
return 0;
@@ -3672,8 +3675,8 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
* they do not understand the reference format extension.
*/
strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
- safe_create_dir(the_repository, sb.buf, 1);
- adjust_shared_perm(the_repository, sb.buf);
+ safe_create_dir(refs->base.repo, sb.buf, 1);
+ adjust_shared_perm(refs->base.repo, sb.buf);
/*
* There is no need to create directories for common refs when creating
@@ -3685,11 +3688,11 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
*/
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/heads");
- safe_create_dir(the_repository, sb.buf, 1);
+ safe_create_dir(refs->base.repo, sb.buf, 1);
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/tags");
- safe_create_dir(the_repository, sb.buf, 1);
+ safe_create_dir(refs->base.repo, sb.buf, 1);
}
strbuf_release(&sb);
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 4/7] worktree: refactor code to use available repositories
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
In "worktree.c" we have lots of users of `the_repository` that already
have a repository available to them. Convert all of them to use that
repository instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
worktree.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/worktree.c b/worktree.c
index 30125827fd..8b10dea179 100644
--- a/worktree.c
+++ b/worktree.c
@@ -392,7 +392,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
if (!is_absolute_path(wt->path)) {
strbuf_addf_gently(errmsg,
_("'%s' file does not contain absolute path to the working tree location"),
- repo_common_path_replace(the_repository, &buf, "worktrees/%s/gitdir", wt->id));
+ repo_common_path_replace(wt->repo, &buf, "worktrees/%s/gitdir", wt->id));
goto done;
}
@@ -414,12 +414,12 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
goto done;
}
- strbuf_realpath(&realpath, repo_common_path_replace(the_repository, &buf, "worktrees/%s", wt->id), 1);
+ strbuf_realpath(&realpath, repo_common_path_replace(wt->repo, &buf, "worktrees/%s", wt->id), 1);
ret = fspathcmp(path, realpath.buf);
if (ret)
strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
- wt->path, repo_common_path_replace(the_repository, &buf,
+ wt->path, repo_common_path_replace(wt->repo, &buf,
"worktrees/%s", wt->id));
done:
free(path);
@@ -440,7 +440,7 @@ void update_worktree_location(struct worktree *wt, const char *path_,
if (is_main_worktree(wt))
BUG("can't relocate main worktree");
- wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+ wt_gitdir = repo_common_path(wt->repo, "worktrees/%s/gitdir", wt->id);
strbuf_realpath(&gitdir, wt_gitdir, 1);
strbuf_realpath(&path, path_, 1);
strbuf_addf(&dotgit, "%s/.git", path.buf);
@@ -658,7 +658,7 @@ static void repair_gitfile(struct worktree *wt,
goto done;
}
- path = repo_common_path(the_repository, "worktrees/%s", wt->id);
+ path = repo_common_path(wt->repo, "worktrees/%s", wt->id);
strbuf_realpath(&repo, path, 1);
strbuf_addf(&dotgit, "%s/.git", wt->path);
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
@@ -727,7 +727,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
if (is_main_worktree(wt))
goto done;
- path = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+ path = repo_common_path(wt->repo, "worktrees/%s/gitdir", wt->id);
strbuf_realpath(&gitdir, path, 1);
if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
@@ -1042,7 +1042,7 @@ int init_worktree_config(struct repository *r)
*/
if (r->repository_format_worktree_config)
return 0;
- if ((res = repo_config_set_gently(the_repository, "extensions.worktreeConfig", "true")))
+ if ((res = repo_config_set_gently(r, "extensions.worktreeConfig", "true")))
return error(_("failed to set extensions.worktreeConfig setting"));
common_config_file = xstrfmt("%s/config", r->commondir);
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 5/7] worktree: pass repository to file-local functions
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
We have a bunch of file-local functions that use `the_repository`.
Adapt them so that the repository is instead passed as a parameter so
that we can get rid of this dependency.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
worktree.c | 47 ++++++++++++++++++++++++++---------------------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/worktree.c b/worktree.c
index 8b10dea179..ebbf9e27e9 100644
--- a/worktree.c
+++ b/worktree.c
@@ -111,27 +111,28 @@ static int is_main_worktree_bare(struct repository *repo)
/**
* get the main worktree
*/
-static struct worktree *get_main_worktree(int skip_reading_head)
+static struct worktree *get_main_worktree(struct repository *repo,
+ int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf worktree_path = STRBUF_INIT;
- strbuf_add_real_path(&worktree_path, repo_get_common_dir(the_repository));
+ strbuf_add_real_path(&worktree_path, repo_get_common_dir(repo));
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
- worktree->repo = the_repository;
+ worktree->repo = repo;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->is_current = is_current_worktree(worktree);
- worktree->is_bare = (the_repository->bare_cfg == 1) ||
- is_bare_repository(the_repository) ||
+ worktree->is_bare = (repo->bare_cfg == 1) ||
+ is_bare_repository(repo) ||
/*
* When in a secondary worktree we have to also verify if the main
* worktree is bare in $commondir/config.worktree.
* This check is unnecessary if we're currently in the main worktree,
* as prior checks already consulted all configs of the current worktree.
*/
- (!worktree->is_current && is_main_worktree_bare(the_repository));
+ (!worktree->is_current && is_main_worktree_bare(repo));
if (!skip_reading_head)
add_head_info(worktree);
@@ -182,7 +183,8 @@ struct worktree *get_linked_worktree(const char *id,
* retrieving worktree metadata that could be used when the worktree is known
* to not be in a healthy state, e.g. when creating or repairing it.
*/
-static struct worktree **get_worktrees_internal(int skip_reading_head)
+static struct worktree **get_worktrees_internal(struct repository *repo,
+ int skip_reading_head)
{
struct worktree **list = NULL;
struct strbuf path = STRBUF_INIT;
@@ -192,9 +194,9 @@ static struct worktree **get_worktrees_internal(int skip_reading_head)
ALLOC_ARRAY(list, alloc);
- list[counter++] = get_main_worktree(skip_reading_head);
+ list[counter++] = get_main_worktree(repo, skip_reading_head);
- strbuf_addf(&path, "%s/worktrees", repo_get_common_dir(the_repository));
+ strbuf_addf(&path, "%s/worktrees", repo_get_common_dir(repo));
dir = opendir(path.buf);
strbuf_release(&path);
if (dir) {
@@ -216,12 +218,12 @@ static struct worktree **get_worktrees_internal(int skip_reading_head)
struct worktree **get_worktrees(void)
{
- return get_worktrees_internal(0);
+ return get_worktrees_internal(the_repository, 0);
}
struct worktree **get_worktrees_without_reading_head(void)
{
- return get_worktrees_internal(1);
+ return get_worktrees_internal(the_repository, 1);
}
char *get_worktree_git_dir(const struct worktree *wt)
@@ -707,7 +709,7 @@ static void repair_noop(int iserr UNUSED,
void repair_worktrees(worktree_repair_fn fn, void *cb_data, int use_relative_paths)
{
- struct worktree **worktrees = get_worktrees_internal(1);
+ struct worktree **worktrees = get_worktrees_internal(the_repository, 1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
if (!fn)
@@ -752,7 +754,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
void repair_worktrees_after_gitdir_move(const char *old_path)
{
- struct worktree **worktrees = get_worktrees_internal(1);
+ struct worktree **worktrees = get_worktrees_internal(the_repository, 1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
for (; *wt; wt++)
@@ -786,7 +788,9 @@ static int is_main_worktree_path(const char *path)
*
* Returns -1 on failure and strbuf.len on success.
*/
-static ssize_t infer_backlink(const char *gitfile, struct strbuf *inferred)
+static ssize_t infer_backlink(struct repository *repo,
+ const char *gitfile,
+ struct strbuf *inferred)
{
struct strbuf actual = STRBUF_INIT;
const char *id;
@@ -801,7 +805,7 @@ static ssize_t infer_backlink(const char *gitfile, struct strbuf *inferred)
id++; /* advance past '/' to point at <id> */
if (!*id)
goto error;
- repo_common_path_replace(the_repository, inferred, "worktrees/%s", id);
+ repo_common_path_replace(repo, inferred, "worktrees/%s", id);
if (!is_directory(inferred->buf))
goto error;
@@ -842,7 +846,7 @@ void repair_worktree_at_path(const char *path,
goto done;
}
- infer_backlink(dotgit.buf, &inferred_backlink);
+ infer_backlink(the_repository, dotgit.buf, &inferred_backlink);
strbuf_realpath_forgiving(&inferred_backlink, inferred_backlink.buf, 0);
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
if (dotgit_contents) {
@@ -1017,12 +1021,13 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
return rc;
}
-static int move_config_setting(const char *key, const char *value,
+static int move_config_setting(struct repository *repo,
+ const char *key, const char *value,
const char *from_file, const char *to_file)
{
- if (repo_config_set_in_file_gently(the_repository, to_file, key, NULL, value))
+ if (repo_config_set_in_file_gently(repo, to_file, key, NULL, value))
return error(_("unable to set %s in '%s'"), key, to_file);
- if (repo_config_set_in_file_gently(the_repository, from_file, key, NULL, NULL))
+ if (repo_config_set_in_file_gently(repo, from_file, key, NULL, NULL))
return error(_("unable to unset %s in '%s'"), key, from_file);
return 0;
}
@@ -1058,7 +1063,7 @@ int init_worktree_config(struct repository *r)
* _could_ be negating a global core.bare=true.
*/
if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) {
- if ((res = move_config_setting("core.bare", "true",
+ if ((res = move_config_setting(r, "core.bare", "true",
common_config_file,
main_worktree_file)))
goto cleanup;
@@ -1070,7 +1075,7 @@ int init_worktree_config(struct repository *r)
* upgrade to worktree config.
*/
if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
- if ((res = move_config_setting("core.worktree", core_worktree,
+ if ((res = move_config_setting(r, "core.worktree", core_worktree,
common_config_file,
main_worktree_file)))
goto cleanup;
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 6/7] worktree: pass repository to public functions
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
Refactor remaining public functions that still depend on
`the_repository` to instead receive a repository as parameter. This
allows us to get rid of `USE_THE_REPOSITORY_VARIABLE`.
Adapt callers accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
branch.c | 4 +-
builtin/branch.c | 2 +-
builtin/config.c | 2 +-
builtin/fsck.c | 6 +--
builtin/gc.c | 2 +-
builtin/notes.c | 2 +-
builtin/receive-pack.c | 2 +-
builtin/reflog.c | 4 +-
builtin/refs.c | 2 +-
builtin/worktree.c | 24 +++++-----
reachable.c | 4 +-
ref-filter.c | 2 +-
refs.c | 2 +-
revision.c | 6 +--
setup.c | 7 +--
submodule.c | 2 +-
t/helper/test-ref-store.c | 2 +-
worktree.c | 115 ++++++++++++++++++++++++++--------------------
worktree.h | 27 +++++++----
19 files changed, 120 insertions(+), 97 deletions(-)
diff --git a/branch.c b/branch.c
index 243db7d0fc..b2ac403b19 100644
--- a/branch.c
+++ b/branch.c
@@ -394,7 +394,7 @@ static void prepare_checked_out_branches(void)
return;
initialized_checked_out_branches = 1;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
while (worktrees[i]) {
char *old, *wt_gitdir;
@@ -846,7 +846,7 @@ void remove_branch_state(struct repository *r, int verbose)
void die_if_checked_out(const char *branch, int ignore_current_worktree)
{
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
for (int i = 0; worktrees[i]; i++) {
if (worktrees[i]->is_current && ignore_current_worktree)
diff --git a/builtin/branch.c b/builtin/branch.c
index 1572a4f9ef..c8fddf7f94 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -579,7 +579,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
const char *interpreted_oldname = NULL;
const char *interpreted_newname = NULL;
int recovery = 0, oldref_usage = 0;
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
if (check_branch_ref(&oldref, oldname)) {
/*
diff --git a/builtin/config.c b/builtin/config.c
index 8d8ec0beea..0882899c3f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -974,7 +974,7 @@ static void location_options_init(struct config_location_options *opts,
opts->source.file = opts->file_to_free = repo_git_path(the_repository, "config");
opts->source.scope = CONFIG_SCOPE_LOCAL;
} else if (opts->use_worktree_config) {
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
if (the_repository->repository_format_worktree_config)
opts->source.file = opts->file_to_free =
repo_git_path(the_repository, "config.worktree");
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 76b723f36d..a6c054e45b 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -632,7 +632,7 @@ static void snapshot_refs(struct repository *repo,
refs_for_each_ref_ext(get_main_ref_store(repo),
snapshot_ref, &data, &opts);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(repo);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct strbuf refname = STRBUF_INIT;
@@ -685,7 +685,7 @@ static void process_refs(struct repository *repo, struct snapshot *snap)
}
if (include_reflogs) {
- worktrees = get_worktrees();
+ worktrees = get_worktrees(repo);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
@@ -1121,7 +1121,7 @@ int cmd_fsck(int argc,
verify_index_checksum = 1;
verify_ce_order = 1;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(repo);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct index_state istate =
diff --git a/builtin/gc.c b/builtin/gc.c
index d32af422af..46999a99ab 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -412,7 +412,7 @@ static int worktree_prune_condition(struct gc_config *cfg)
while (limit && (d = readdir_skip_dot_and_dotdot(dir))) {
char *wtpath;
strbuf_reset(&buf);
- if (should_prune_worktree(d->d_name, &buf, &wtpath, expiry_date))
+ if (should_prune_worktree(the_repository, d->d_name, &buf, &wtpath, expiry_date))
limit--;
free(wtpath);
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 962df867c8..9f1f0ec840 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -989,7 +989,7 @@ static int merge(int argc, const char **argv, const char *prefix,
"NOTES_MERGE_PARTIAL", &result_oid, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
notes_ref);
if (wt)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 19eb6a1b61..b246c1ccae 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1503,7 +1503,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object_id *old_oid = &cmd->old_oid;
struct object_id *new_oid = &cmd->new_oid;
int do_update_worktree = 0;
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
const struct worktree *worktree =
find_shared_symref(worktrees, "HEAD", name);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index dcbfe89339..1211c58fa4 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -250,7 +250,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
struct string_list_item *item;
struct worktree **worktrees, **p;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
for (p = worktrees; *p; p++) {
if (single_worktree && !(*p)->is_current)
continue;
@@ -374,7 +374,7 @@ static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
struct string_list_item *item;
struct worktree **worktrees, **p;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
for (p = worktrees; *p; p++) {
if (single_worktree && !(*p)->is_current)
continue;
diff --git a/builtin/refs.c b/builtin/refs.c
index a9ca2058ee..5cd21c25fe 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -113,7 +113,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
repo_config(repo, git_fsck_config, &fsck_refs_options);
prepare_repo_settings(repo);
- worktrees = get_worktrees_without_reading_head();
+ worktrees = get_worktrees_without_reading_head(repo);
for (size_t i = 0; worktrees[i]; i++)
ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
&fsck_refs_options, worktrees[i]);
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d21c43fde3..0689b3d3e0 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -226,7 +226,7 @@ static void prune_worktrees(void)
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
char *path;
strbuf_reset(&reason);
- if (should_prune_worktree(d->d_name, &reason, &path, expire))
+ if (should_prune_worktree(the_repository, d->d_name, &reason, &path, expire))
prune_worktree(d->d_name, reason.buf);
else if (path)
string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
@@ -475,7 +475,7 @@ static int add_worktree(const char *path, const char *refname,
struct ref_store *wt_refs;
struct repo_config_values *cfg = repo_config_values(the_repository);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
check_candidate_path(path, opts->force, worktrees, "add");
free_worktrees(worktrees);
worktrees = NULL;
@@ -539,7 +539,8 @@ static int add_worktree(const char *path, const char *refname,
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
- write_worktree_linking_files(sb_git.buf, sb.buf, opts->relative_paths);
+ write_worktree_linking_files(the_repository, sb_git.buf,
+ sb.buf, opts->relative_paths);
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
@@ -547,7 +548,7 @@ static int add_worktree(const char *path, const char *refname,
/*
* Set up the ref store of the worktree and create the HEAD reference.
*/
- wt = get_linked_worktree(name, 1);
+ wt = get_linked_worktree(the_repository, name, 1);
if (!wt) {
ret = error(_("could not find created worktree '%s'"), name);
goto done;
@@ -1103,7 +1104,7 @@ static int list(int ac, const char **av, const char *prefix,
else if (!line_terminator && !porcelain)
die(_("the option '%s' requires '%s'"), "-z", "--porcelain");
else {
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
int path_maxwidth = 0, abbrev = DEFAULT_ABBREV, i;
struct worktree_display *display = NULL;
@@ -1146,7 +1147,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
if (ac != 1)
usage_with_options(git_worktree_lock_usage, options);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -1183,7 +1184,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
if (ac != 1)
usage_with_options(git_worktree_unlock_usage, options);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -1269,7 +1270,7 @@ static int move_worktree(int ac, const char **av, const char *prefix,
strbuf_addstr(&dst, path);
free(path);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -1394,7 +1395,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix,
if (ac != 1)
usage_with_options(git_worktree_remove_usage, options);
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -1456,8 +1457,9 @@ static int repair(int ac, const char **av, const char *prefix,
ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0);
p = ac > 0 ? av : self;
for (; *p; p++)
- repair_worktree_at_path(*p, report_repair, &rc, use_relative_paths);
- repair_worktrees(report_repair, &rc, use_relative_paths);
+ repair_worktree_at_path(the_repository, *p, report_repair,
+ &rc, use_relative_paths);
+ repair_worktrees(the_repository, report_repair, &rc, use_relative_paths);
return rc;
}
diff --git a/reachable.c b/reachable.c
index 101cfc2727..be87f487d8 100644
--- a/reachable.c
+++ b/reachable.c
@@ -62,7 +62,7 @@ static void add_rebase_files(struct rev_info *revs)
"rebase-merge/autostash",
"rebase-merge/orig-head",
};
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
for (struct worktree **wt = worktrees; *wt; wt++) {
char *wt_gitdir = get_worktree_git_dir(*wt);
@@ -319,7 +319,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
/* detached HEAD is not included in the list above */
refs_head_ref(get_main_ref_store(the_repository), add_one_ref, revs);
- other_head_refs(add_one_ref, revs);
+ other_head_refs(the_repository, add_one_ref, revs);
/* rebase autostash and orig-head */
add_rebase_files(revs);
diff --git a/ref-filter.c b/ref-filter.c
index 284796c49b..29aca08ce7 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2402,7 +2402,7 @@ static void lazy_init_worktree_map(void)
if (ref_to_worktree_map.worktrees)
return;
- ref_to_worktree_map.worktrees = get_worktrees();
+ ref_to_worktree_map.worktrees = get_worktrees(the_repository);
hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
}
diff --git a/refs.c b/refs.c
index 1d24637891..d9957a266c 100644
--- a/refs.c
+++ b/refs.c
@@ -3328,7 +3328,7 @@ static int move_files(const char *from_path, const char *to_path, struct strbuf
static int has_worktrees(void)
{
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees(the_repository);
int ret = 0;
size_t i;
diff --git a/revision.c b/revision.c
index 0c95edef59..7dd40a31d3 100644
--- a/revision.c
+++ b/revision.c
@@ -1711,7 +1711,7 @@ static void add_other_reflogs_to_pending(struct all_refs_cb *cb)
{
struct worktree **worktrees, **p;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
@@ -1837,7 +1837,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
if (revs->single_worktree)
return;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(the_repository);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct index_state istate = INDEX_STATE_INIT(revs->repo);
@@ -2813,7 +2813,7 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
- other_head_refs(handle_one_ref, &cb);
+ other_head_refs(the_repository, handle_one_ref, &cb);
}
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) {
diff --git a/setup.c b/setup.c
index 0de56a074f..505e8d7bf2 100644
--- a/setup.c
+++ b/setup.c
@@ -2650,7 +2650,8 @@ static void create_object_directory(struct repository *repo)
strbuf_release(&path);
}
-static void separate_git_dir(const char *git_dir, const char *git_link)
+static void separate_git_dir(struct repository *repo,
+ const char *git_dir, const char *git_link)
{
struct stat st;
@@ -2666,7 +2667,7 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
if (rename(src, git_dir))
die_errno(_("unable to move %s to %s"), src, git_dir);
- repair_worktrees_after_gitdir_move(src);
+ repair_worktrees_after_gitdir_move(repo, src);
}
write_file(git_link, "gitdir: %s", git_dir);
@@ -2823,7 +2824,7 @@ int init_db(struct repository *repo,
set_git_dir(repo, real_git_dir, 1);
git_dir = repo_get_git_dir(repo);
- separate_git_dir(git_dir, original_git_dir);
+ separate_git_dir(repo, git_dir, original_git_dir);
}
else {
set_git_dir(repo, git_dir, 1);
diff --git a/submodule.c b/submodule.c
index 93d0361072..c6dda4d156 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2494,7 +2494,7 @@ static void relocate_single_git_dir_into_superproject(const char *path,
if (validate_submodule_path(path) < 0)
exit(128);
- if (submodule_uses_worktrees(path))
+ if (submodule_uses_worktrees(the_repository, path))
die(_("relocate_gitdir for submodule '%s' with "
"more than one worktree not supported"), path);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 3866d0aca4..5a9a3053d9 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -84,7 +84,7 @@ static const char **get_store(const char **argv, struct ref_store **refs)
*refs = repo_get_submodule_ref_store(the_repository, gitdir);
} else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
- struct worktree **p, **worktrees = get_worktrees();
+ struct worktree **p, **worktrees = get_worktrees(the_repository);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
diff --git a/worktree.c b/worktree.c
index ebbf9e27e9..cbf95328a3 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -139,7 +138,8 @@ static struct worktree *get_main_worktree(struct repository *repo,
return worktree;
}
-struct worktree *get_linked_worktree(const char *id,
+struct worktree *get_linked_worktree(struct repository *repo,
+ const char *id,
int skip_reading_head)
{
struct worktree *worktree = NULL;
@@ -149,7 +149,7 @@ struct worktree *get_linked_worktree(const char *id,
if (!id)
die("Missing linked worktree name");
- repo_common_path_append(the_repository, &path, "worktrees/%s/gitdir", id);
+ repo_common_path_append(repo, &path, "worktrees/%s/gitdir", id);
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
/* invalid gitdir file */
goto done;
@@ -163,7 +163,7 @@ struct worktree *get_linked_worktree(const char *id,
}
CALLOC_ARRAY(worktree, 1);
- worktree->repo = the_repository;
+ worktree->repo = repo;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
worktree->is_current = is_current_worktree(worktree);
@@ -203,7 +203,7 @@ static struct worktree **get_worktrees_internal(struct repository *repo,
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
struct worktree *linked = NULL;
- if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
+ if ((linked = get_linked_worktree(repo, d->d_name, skip_reading_head))) {
ALLOC_GROW(list, counter + 1, alloc);
list[counter++] = linked;
}
@@ -216,14 +216,14 @@ static struct worktree **get_worktrees_internal(struct repository *repo,
return list;
}
-struct worktree **get_worktrees(void)
+struct worktree **get_worktrees(struct repository *repo)
{
- return get_worktrees_internal(the_repository, 0);
+ return get_worktrees_internal(repo, 0);
}
-struct worktree **get_worktrees_without_reading_head(void)
+struct worktree **get_worktrees_without_reading_head(struct repository *repo)
{
- return get_worktrees_internal(the_repository, 1);
+ return get_worktrees_internal(repo, 1);
}
char *get_worktree_git_dir(const struct worktree *wt)
@@ -336,7 +336,7 @@ const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
if (wt->prune_reason_valid)
return wt->prune_reason;
- if (should_prune_worktree(wt->id, &reason, &path, expire))
+ if (should_prune_worktree(wt->repo, wt->id, &reason, &path, expire))
wt->prune_reason = strbuf_detach(&reason, NULL);
wt->prune_reason_valid = 1;
@@ -447,7 +447,8 @@ void update_worktree_location(struct worktree *wt, const char *path_,
strbuf_realpath(&path, path_, 1);
strbuf_addf(&dotgit, "%s/.git", path.buf);
if (fspathcmp(wt->path, path.buf)) {
- write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths);
+ write_worktree_linking_files(wt->repo, dotgit.buf,
+ gitdir.buf, use_relative_paths);
free(wt->path);
wt->path = strbuf_detach(&path, NULL);
@@ -535,7 +536,8 @@ const struct worktree *find_shared_symref(struct worktree **worktrees,
return NULL;
}
-int submodule_uses_worktrees(const char *path)
+int submodule_uses_worktrees(struct repository *repo,
+ const char *path)
{
char *submodule_gitdir;
struct strbuf sb = STRBUF_INIT, err = STRBUF_INIT;
@@ -544,7 +546,7 @@ int submodule_uses_worktrees(const char *path)
int ret = 0;
struct repository_format format = REPOSITORY_FORMAT_INIT;
- submodule_gitdir = repo_submodule_path(the_repository,
+ submodule_gitdir = repo_submodule_path(repo,
path, "%s", "");
if (!submodule_gitdir)
return 0;
@@ -597,13 +599,14 @@ void strbuf_worktree_ref(const struct worktree *wt,
strbuf_addstr(sb, refname);
}
-int other_head_refs(refs_for_each_cb fn, void *cb_data)
+int other_head_refs(struct repository *repo,
+ refs_for_each_cb fn, void *cb_data)
{
struct worktree **worktrees, **p;
struct strbuf refname = STRBUF_INIT;
int ret = 0;
- worktrees = get_worktrees();
+ worktrees = get_worktrees(repo);
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct object_id oid;
@@ -614,7 +617,7 @@ int other_head_refs(refs_for_each_cb fn, void *cb_data)
strbuf_reset(&refname);
strbuf_worktree_ref(wt, &refname, "HEAD");
- if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ if (refs_resolve_ref_unsafe(get_main_ref_store(repo),
refname.buf,
RESOLVE_REF_READING,
&oid, &flag)) {
@@ -687,7 +690,8 @@ static void repair_gitfile(struct worktree *wt,
if (repair) {
fn(0, wt->path, repair, cb_data);
- write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths);
+ write_worktree_linking_files(wt->repo, dotgit.buf,
+ gitdir.buf, use_relative_paths);
}
done:
@@ -707,9 +711,10 @@ static void repair_noop(int iserr UNUSED,
/* nothing */
}
-void repair_worktrees(worktree_repair_fn fn, void *cb_data, int use_relative_paths)
+void repair_worktrees(struct repository *repo, worktree_repair_fn fn,
+ void *cb_data, int use_relative_paths)
{
- struct worktree **worktrees = get_worktrees_internal(the_repository, 1);
+ struct worktree **worktrees = get_worktrees_internal(repo, 1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
if (!fn)
@@ -745,16 +750,17 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
if (!file_exists(dotgit.buf))
goto done;
- write_worktree_linking_files(dotgit.buf, gitdir.buf, is_relative_path);
+ write_worktree_linking_files(wt->repo, dotgit.buf,
+ gitdir.buf, is_relative_path);
done:
strbuf_release(&gitdir);
strbuf_release(&dotgit);
free(path);
}
-void repair_worktrees_after_gitdir_move(const char *old_path)
+void repair_worktrees_after_gitdir_move(struct repository *repo, const char *old_path)
{
- struct worktree **worktrees = get_worktrees_internal(the_repository, 1);
+ struct worktree **worktrees = get_worktrees_internal(repo, 1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
for (; *wt; wt++)
@@ -762,7 +768,7 @@ void repair_worktrees_after_gitdir_move(const char *old_path)
free_worktrees(worktrees);
}
-static int is_main_worktree_path(const char *path)
+static int is_main_worktree_path(struct repository *repo, const char *path)
{
struct strbuf target = STRBUF_INIT;
struct strbuf maindir = STRBUF_INIT;
@@ -770,7 +776,7 @@ static int is_main_worktree_path(const char *path)
strbuf_add_real_path(&target, path);
strbuf_strip_suffix(&target, "/.git");
- strbuf_add_real_path(&maindir, repo_get_common_dir(the_repository));
+ strbuf_add_real_path(&maindir, repo_get_common_dir(repo));
strbuf_strip_suffix(&maindir, "/.git");
cmp = fspathcmp(maindir.buf, target.buf);
@@ -821,7 +827,8 @@ static ssize_t infer_backlink(struct repository *repo,
* Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
* the worktree's path.
*/
-void repair_worktree_at_path(const char *path,
+void repair_worktree_at_path(struct repository *repo,
+ const char *path,
worktree_repair_fn fn, void *cb_data,
int use_relative_paths)
{
@@ -837,7 +844,7 @@ void repair_worktree_at_path(const char *path,
if (!fn)
fn = repair_noop;
- if (is_main_worktree_path(path))
+ if (is_main_worktree_path(repo, path))
goto done;
strbuf_addf(&dotgit, "%s/.git", path);
@@ -846,7 +853,7 @@ void repair_worktree_at_path(const char *path,
goto done;
}
- infer_backlink(the_repository, dotgit.buf, &inferred_backlink);
+ infer_backlink(repo, dotgit.buf, &inferred_backlink);
strbuf_realpath_forgiving(&inferred_backlink, inferred_backlink.buf, 0);
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
if (dotgit_contents) {
@@ -919,7 +926,8 @@ void repair_worktree_at_path(const char *path,
if (repair) {
fn(0, gitdir.buf, repair, cb_data);
- write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths);
+ write_worktree_linking_files(repo, dotgit.buf,
+ gitdir.buf, use_relative_paths);
}
done:
free(dotgit_contents);
@@ -930,12 +938,16 @@ void repair_worktree_at_path(const char *path,
strbuf_release(&dotgit);
}
-int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
+int should_prune_worktree(struct repository *repo,
+ const char *id,
+ struct strbuf *reason,
+ char **wtpath,
+ timestamp_t expire)
{
struct stat st;
struct strbuf dotgit = STRBUF_INIT;
struct strbuf gitdir = STRBUF_INIT;
- struct strbuf repo = STRBUF_INIT;
+ struct strbuf repo_path = STRBUF_INIT;
struct strbuf file = STRBUF_INIT;
char *path = NULL;
int rc = 0;
@@ -945,17 +957,17 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
*wtpath = NULL;
- path = repo_common_path(the_repository, "worktrees/%s", id);
- strbuf_realpath(&repo, path, 1);
+ path = repo_common_path(repo, "worktrees/%s", id);
+ strbuf_realpath(&repo_path, path, 1);
FREE_AND_NULL(path);
- strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
- if (!is_directory(repo.buf)) {
+ strbuf_addf(&gitdir, "%s/gitdir", repo_path.buf);
+ if (!is_directory(repo_path.buf)) {
strbuf_addstr(reason, _("not a valid directory"));
rc = 1;
goto done;
}
- strbuf_addf(&file, "%s/locked", repo.buf);
+ strbuf_addf(&file, "%s/locked", repo_path.buf);
if (file_exists(file.buf)) {
goto done;
}
@@ -999,12 +1011,12 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
if (is_absolute_path(path)) {
strbuf_addstr(&dotgit, path);
} else {
- strbuf_addf(&dotgit, "%s/%s", repo.buf, path);
+ strbuf_addf(&dotgit, "%s/%s", repo_path.buf, path);
strbuf_realpath_forgiving(&dotgit, dotgit.buf, 0);
}
if (!file_exists(dotgit.buf)) {
strbuf_reset(&file);
- strbuf_addf(&file, "%s/index", repo.buf);
+ strbuf_addf(&file, "%s/index", repo_path.buf);
if (stat(file.buf, &st) || st.st_mtime <= expire) {
strbuf_addstr(reason, _("gitdir file points to non-existent location"));
rc = 1;
@@ -1016,7 +1028,7 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
free(path);
strbuf_release(&dotgit);
strbuf_release(&gitdir);
- strbuf_release(&repo);
+ strbuf_release(&repo_path);
strbuf_release(&file);
return rc;
}
@@ -1094,37 +1106,38 @@ int init_worktree_config(struct repository *r)
return res;
}
-void write_worktree_linking_files(const char *dotgit, const char *gitdir,
+void write_worktree_linking_files(struct repository *repo,
+ const char *dotgit, const char *gitdir,
int use_relative_paths)
{
struct strbuf path = STRBUF_INIT;
- struct strbuf repo = STRBUF_INIT;
+ struct strbuf repo_path = STRBUF_INIT;
struct strbuf tmp = STRBUF_INIT;
strbuf_addstr(&path, dotgit);
strbuf_strip_suffix(&path, "/.git");
strbuf_realpath(&path, path.buf, 1);
- strbuf_addstr(&repo, gitdir);
- strbuf_strip_suffix(&repo, "/gitdir");
- strbuf_realpath(&repo, repo.buf, 1);
+ strbuf_addstr(&repo_path, gitdir);
+ strbuf_strip_suffix(&repo_path, "/gitdir");
+ strbuf_realpath(&repo_path, repo_path.buf, 1);
- if (use_relative_paths && !the_repository->repository_format_relative_worktrees) {
- if (upgrade_repository_format(the_repository, 1) < 0)
+ if (use_relative_paths && !repo->repository_format_relative_worktrees) {
+ if (upgrade_repository_format(repo, 1) < 0)
die(_("unable to upgrade repository format to support relative worktrees"));
- if (repo_config_set_gently(the_repository, "extensions.relativeWorktrees", "true"))
+ if (repo_config_set_gently(repo, "extensions.relativeWorktrees", "true"))
die(_("unable to set extensions.relativeWorktrees setting"));
- the_repository->repository_format_relative_worktrees = 1;
+ repo->repository_format_relative_worktrees = 1;
}
if (use_relative_paths) {
- write_file(gitdir, "%s/.git", relative_path(path.buf, repo.buf, &tmp));
- write_file(dotgit, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
+ write_file(gitdir, "%s/.git", relative_path(path.buf, repo_path.buf, &tmp));
+ write_file(dotgit, "gitdir: %s", relative_path(repo_path.buf, path.buf, &tmp));
} else {
write_file(gitdir, "%s/.git", path.buf);
- write_file(dotgit, "gitdir: %s", repo.buf);
+ write_file(dotgit, "gitdir: %s", repo_path.buf);
}
strbuf_release(&path);
- strbuf_release(&repo);
+ strbuf_release(&repo_path);
strbuf_release(&tmp);
}
diff --git a/worktree.h b/worktree.h
index 1075409f9a..fbb2757f5b 100644
--- a/worktree.h
+++ b/worktree.h
@@ -28,7 +28,7 @@ struct worktree {
* The caller is responsible for freeing the memory from the returned
* worktrees by calling free_worktrees().
*/
-struct worktree **get_worktrees(void);
+struct worktree **get_worktrees(struct repository *repo);
/*
* Like `get_worktrees`, but does not read HEAD. Skip reading HEAD allows to
@@ -36,7 +36,7 @@ struct worktree **get_worktrees(void);
* the HEAD ref. This is useful in contexts where it is assumed that the
* refdb may not be in a consistent state.
*/
-struct worktree **get_worktrees_without_reading_head(void);
+struct worktree **get_worktrees_without_reading_head(struct repository *repo);
/*
* Construct a struct worktree corresponding to repo->gitdir and
@@ -47,7 +47,7 @@ struct worktree *get_current_worktree(struct repository *repo);
/*
* Returns 1 if linked worktrees exist, 0 otherwise.
*/
-int submodule_uses_worktrees(const char *path);
+int submodule_uses_worktrees(struct repository *repo, const char *path);
/*
* Return git dir of the worktree. Note that the path may be relative.
@@ -76,7 +76,8 @@ struct worktree *find_worktree(struct worktree **list,
* Look up the worktree corresponding to `id`, or NULL of no such worktree
* exists.
*/
-struct worktree *get_linked_worktree(const char *id,
+struct worktree *get_linked_worktree(struct repository *repo,
+ const char *id,
int skip_reading_head);
/*
@@ -112,7 +113,8 @@ const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire);
* `expire` defines a grace period to prune the worktree when its path
* does not exist.
*/
-int should_prune_worktree(const char *id,
+int should_prune_worktree(struct repository *repo,
+ const char *id,
struct strbuf *reason,
char **wtpath,
timestamp_t expire);
@@ -142,12 +144,14 @@ typedef void (* worktree_repair_fn)(int iserr, const char *path,
* function, if non-NULL, is called with the path of the worktree and a
* description of the repair or error, along with the callback user-data.
*/
-void repair_worktrees(worktree_repair_fn, void *cb_data, int use_relative_paths);
+void repair_worktrees(struct repository *repo, worktree_repair_fn,
+ void *cb_data, int use_relative_paths);
/*
* Repair the linked worktrees after the gitdir has been moved.
*/
-void repair_worktrees_after_gitdir_move(const char *old_path);
+void repair_worktrees_after_gitdir_move(struct repository *repo,
+ const char *old_path);
/*
* Repair the linked worktree after the gitdir has been moved.
@@ -164,7 +168,9 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
* worktree and a description of the repair or error, along with the callback
* user-data.
*/
-void repair_worktree_at_path(const char *, worktree_repair_fn,
+void repair_worktree_at_path(struct repository *repo,
+ const char *path,
+ worktree_repair_fn fn,
void *cb_data, int use_relative_paths);
/*
@@ -196,7 +202,7 @@ int is_shared_symref(const struct worktree *wt,
* Similar to head_ref() for all HEADs _except_ one from the current
* worktree, which is covered by head_ref().
*/
-int other_head_refs(refs_for_each_cb fn, void *cb_data);
+int other_head_refs(struct repository *repo, refs_for_each_cb fn, void *cb_data);
int is_worktree_being_rebased(const struct worktree *wt, const char *target);
int is_worktree_being_bisected(const struct worktree *wt, const char *target);
@@ -239,7 +245,8 @@ int init_worktree_config(struct repository *r);
* dotgit: "/path/to/foo/.git"
* gitdir: "/path/to/repo/worktrees/foo/gitdir"
*/
-void write_worktree_linking_files(const char *dotgit, const char *gitdir,
+void write_worktree_linking_files(struct repository *repo,
+ const char *dotgit, const char *gitdir,
int use_relative_paths);
#endif
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* [PATCH v2 7/7] refs: remove remaining uses of `the_repository`
From: Patrick Steinhardt @ 2026-07-15 7:39 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano
In-Reply-To: <20260715-pks-refs-wo-the-repository-v2-0-d00d364f5a3e@pks.im>
There are still a couple of callsites that use `the_repository`. Convert
these to instead use a repository injected by the caller. This allows us
to remove `USE_THE_REPOSITORY_VARIABLE`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
branch.c | 2 +-
builtin/branch.c | 14 +++++++++-----
builtin/check-ref-format.c | 2 +-
builtin/checkout.c | 2 +-
builtin/merge.c | 2 +-
builtin/worktree.c | 8 ++++----
refs.c | 23 +++++++++--------------
refs.h | 5 +++--
8 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/branch.c b/branch.c
index b2ac403b19..4f38905bad 100644
--- a/branch.c
+++ b/branch.c
@@ -372,7 +372,7 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
*/
int validate_branchname(const char *name, struct strbuf *ref)
{
- if (check_branch_ref(ref, name)) {
+ if (check_branch_ref(the_repository, ref, name)) {
int code = die_message(_("'%s' is not a valid branch name"), name);
advise_if_enabled(ADVICE_REF_SYNTAX,
_("See 'git help check-ref-format'"));
diff --git a/builtin/branch.c b/builtin/branch.c
index c8fddf7f94..be26ec0750 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -259,7 +259,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
char *target = NULL;
int flags = 0;
- copy_branchname(&bname, argv[i], allowed_interpret);
+ copy_branchname(the_repository, &bname,
+ argv[i], allowed_interpret);
free(name);
name = mkpathdup(fmt, bname.buf);
@@ -581,7 +582,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
int recovery = 0, oldref_usage = 0;
struct worktree **worktrees = get_worktrees(the_repository);
- if (check_branch_ref(&oldref, oldname)) {
+ if (check_branch_ref(the_repository, &oldref, oldname)) {
/*
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
@@ -898,7 +899,8 @@ int cmd_branch(int argc,
die(_("cannot give description to detached HEAD"));
branch_name = head;
} else if (argc == 1) {
- copy_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ copy_branchname(the_repository, &buf, argv[0],
+ INTERPRET_BRANCH_LOCAL);
branch_name = buf.buf;
} else {
die(_("cannot edit description of more than one branch"));
@@ -941,7 +943,8 @@ int cmd_branch(int argc,
if (!argc)
branch = branch_get(NULL);
else if (argc == 1) {
- copy_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ copy_branchname(the_repository, &buf, argv[0],
+ INTERPRET_BRANCH_LOCAL);
branch = branch_get(buf.buf);
} else
die(_("too many arguments to set new upstream"));
@@ -971,7 +974,8 @@ int cmd_branch(int argc,
if (!argc)
branch = branch_get(NULL);
else if (argc == 1) {
- copy_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
+ copy_branchname(the_repository, &buf, argv[0],
+ INTERPRET_BRANCH_LOCAL);
branch = branch_get(buf.buf);
} else
die(_("too many arguments to unset upstream"));
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index e42b0444ea..fd1c9c0e0c 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -45,7 +45,7 @@ static int check_ref_format_branch(const char *arg)
int nongit;
setup_git_directory_gently(the_repository, &nongit);
- if (check_branch_ref(&sb, arg) ||
+ if (check_branch_ref(the_repository, &sb, arg) ||
!skip_prefix(sb.buf, "refs/heads/", &name))
die("'%s' is not a valid branch name", arg);
printf("%s\n", name);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index aee84ca897..55e3a89a85 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -805,7 +805,7 @@ static void setup_branch_path(struct branch_info *branch)
&branch->oid, &branch->refname, 0))
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
- copy_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
+ copy_branchname(the_repository, &buf, branch->name, INTERPRET_BRANCH_LOCAL);
if (strcmp(buf.buf, branch->name)) {
free(branch->name);
branch->name = xstrdup(buf.buf);
diff --git a/builtin/merge.c b/builtin/merge.c
index 5b46a596f0..58d1b7bb07 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -553,7 +553,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
char *found_ref = NULL;
int len, early;
- copy_branchname(&bname, remote, 0);
+ copy_branchname(the_repository, &bname, remote, 0);
remote = bname.buf;
oidclr(&branch_head, the_repository->hash_algo);
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 0689b3d3e0..6397e149a8 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -481,7 +481,7 @@ static int add_worktree(const char *path, const char *refname,
worktrees = NULL;
/* is 'refname' a branch or commit? */
- if (!opts->detach && !check_branch_ref(&symref, refname) &&
+ if (!opts->detach && !check_branch_ref(the_repository, &symref, refname) &&
refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
is_branch = 1;
if (!opts->force)
@@ -650,7 +650,7 @@ static void print_preparing_worktree_line(int detach,
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
} else {
struct strbuf s = STRBUF_INIT;
- if (!detach && !check_branch_ref(&s, branch) &&
+ if (!detach && !check_branch_ref(the_repository, &s, branch) &&
refs_ref_exists(get_main_ref_store(the_repository), s.buf))
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
@@ -772,7 +772,7 @@ static char *dwim_branch(const char *path, char **new_branch)
char *branchname = xstrndup(s, n);
struct strbuf ref = STRBUF_INIT;
- branch_exists = !check_branch_ref(&ref, branchname) &&
+ branch_exists = !check_branch_ref(the_repository, &ref, branchname) &&
refs_ref_exists(get_main_ref_store(the_repository),
ref.buf);
strbuf_release(&ref);
@@ -869,7 +869,7 @@ static int add(int ac, const char **av, const char *prefix,
new_branch = new_branch_force;
if (!opts.force &&
- !check_branch_ref(&symref, new_branch) &&
+ !check_branch_ref(the_repository, &symref, new_branch) &&
refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
diff --git a/refs.c b/refs.c
index d9957a266c..92d5df5b71 100644
--- a/refs.c
+++ b/refs.c
@@ -2,8 +2,6 @@
* The backend-independent part of the reference module.
*/
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -744,14 +742,15 @@ static char *substitute_branch_name(struct repository *r,
return NULL;
}
-void copy_branchname(struct strbuf *sb, const char *name,
+void copy_branchname(struct repository *repo,
+ struct strbuf *sb, const char *name,
enum interpret_branch_kind allowed)
{
int len = strlen(name);
struct interpret_branch_name_options options = {
.allowed = allowed
};
- int used = repo_interpret_branch_name(the_repository, name, len, sb,
+ int used = repo_interpret_branch_name(repo, name, len, sb,
&options);
if (used < 0)
@@ -759,10 +758,10 @@ void copy_branchname(struct strbuf *sb, const char *name,
strbuf_add(sb, name + used, len - used);
}
-int check_branch_ref(struct strbuf *sb, const char *name)
+int check_branch_ref(struct repository *repo, struct strbuf *sb, const char *name)
{
if (startup_info->have_repository)
- copy_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
+ copy_branchname(repo, sb, name, INTERPRET_BRANCH_LOCAL);
else
strbuf_addstr(sb, name);
@@ -3326,9 +3325,9 @@ static int move_files(const char *from_path, const char *to_path, struct strbuf
return ret;
}
-static int has_worktrees(void)
+static int has_worktrees(struct repository *repo)
{
- struct worktree **worktrees = get_worktrees(the_repository);
+ struct worktree **worktrees = get_worktrees(repo);
int ret = 0;
size_t i;
@@ -3373,12 +3372,8 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* Worktrees complicate the migration because every worktree has a
* separate ref storage. While it should be feasible to implement, this
* is pushed out to a future iteration.
- *
- * TODO: we should really be passing the caller-provided repository to
- * `has_worktrees()`, but our worktree subsystem doesn't yet support
- * that.
*/
- if (has_worktrees()) {
+ if (has_worktrees(repo)) {
strbuf_addstr(errbuf, "migrating repositories with worktrees is not supported yet");
ret = -1;
goto done;
@@ -3503,7 +3498,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* repository format so that clients will use the new ref store.
* We also need to swap out the repository's main ref store.
*/
- initialize_repository_version(the_repository, hash_algo_by_ptr(repo->hash_algo), format, 1);
+ initialize_repository_version(repo, hash_algo_by_ptr(repo->hash_algo), format, 1);
/*
* Unset the old ref store and release it. `get_main_ref_store()` will
diff --git a/refs.h b/refs.h
index a381022c77..9979446d15 100644
--- a/refs.h
+++ b/refs.h
@@ -234,7 +234,8 @@ char *repo_default_branch_name(struct repository *r, int quiet);
* If "allowed" is non-zero, restrict the set of allowed expansions. See
* repo_interpret_branch_name() for details.
*/
-void copy_branchname(struct strbuf *sb, const char *name,
+void copy_branchname(struct repository *repo,
+ struct strbuf *sb, const char *name,
enum interpret_branch_kind allowed);
/*
@@ -243,7 +244,7 @@ void copy_branchname(struct strbuf *sb, const char *name,
*
* The return value is "0" if the result is valid, and "-1" otherwise.
*/
-int check_branch_ref(struct strbuf *sb, const char *name);
+int check_branch_ref(struct repository *repo, struct strbuf *sb, const char *name);
/*
* Similar for a tag name in refs/tags/.
--
2.55.0.313.g8d093f411d.dirty
^ permalink raw reply related
* Re: [PATCH GSoC v17 00/13] cat-file: add remote-object-info to batch-command
From: Pablo Sabater @ 2026-07-15 8:48 UTC (permalink / raw)
To: Junio C Hamano, Pablo Sabater
Cc: chandrapratap3519, chriscool, eric.peijian, git, jltobler,
karthik.188, peff, toon
In-Reply-To: <xmqqpl0pqdag.fsf@gitster.g>
On Wed Jul 15, 2026 at 2:58 AM CEST, Junio C Hamano wrote:
> "Pablo Sabater" <pabloosabaterr@gmail.com> writes:
>
>> You gave me feedback for v17 10th commit:
>>
>> https://lore.kernel.org/git/xmqqik6htpv4.fsf@gitster.g/
>>
>> Should I send a v18 or a new patch on top of 'next'?
>>
>> The fix is simple and I already have it on my local, I just want to do
>> whatever is better.
>
> I had v16 merged (prematurely) to 'next' and then saw v17, so I
> reverted the merge, which means 'next' no longer has your topic.
>
> And v17, as a brand new iteration, is not in, and will stay out of,
> 'next' until we are happy with it. If you have an updated v18,
> please send it as a whole replacement.
I'll send v18 very soon.
>
> Thanks. How close are we to the finish line, by the way?
There's one month left. Final evaluation ends on 17th August (more weeks
can be asked, if it seems too rushed, but I think it will fit just
right). I have the other series that supports objecttype but I wanted
this to be on 'next' before sending it.
I expect it to go faster because it's on top of all the
infrastructure built in this series and it's ~150 lines.
^ permalink raw reply
* Re: [PATCH v12 0/7] graph: indent visual roots in graph
From: Chandra Pratap @ 2026-07-15 8:59 UTC (permalink / raw)
To: Pablo Sabater
Cc: ayu.chandekar, christian.couder, git, gitster, jltobler,
karthik.188, krka, mroik, peff, phillip.wood, siddharthasthana31
In-Reply-To: <20260714-ps-pre-commit-indent-v12-0-d50938e006df@gmail.com>
On Tue, 14 Jul 2026 at 17:39, Pablo Sabater <pabloosabaterr@gmail.com> wrote:
>
> When rendering a graph, if the history contains multiple "visual roots",
> actual roots or commits that look like roots (i.e. have their parents
> filtered out) can end up being vertically adjacent to unrelated commits,
> falsely appearing to be related.
>
> A fix for this issue was already attempted [1] a while ago.
>
> This series adds indentation to the visual root commits, so they cannot be
> vertically adjacent anymore making it easier to identify them.
>
> Before indentation:
>
> * A
> * B1
> * B2
> * C1
> * C2
>
> After indentation:
>
> * A
> * B1
> \
> * B2
> * C1
> * C2
>
> Indents the visual root commits that have still commits to show after
> them, and if they have children it connects them with an edge at a new
> row.
>
> If there are multiple visual roots adjacent in history, the indentation
> starts with the second one, avoiding redundant indentation of the first
> one and cascades after the second.
>
> * A
> * B
> * C
> * D
> * E
> * F
> * G
> * H
> * I
> * J1
> * J2
>
> The indentation wraps after cascading columns and when wrapping back to
> the initial column if the next commit is a non-visual-root commit, force
> the indentation one extra level.
>
> Series explanation:
>
> 1. Cleanup to bring a common function from t4215 and t6016 that will be
> used in t4218.
>
> 2. Logic extraction of the chose of from where the commit source comes
> from.
>
> 3. Add a buffer for lookahead purposes.
>
> 4. Principal commit. Implement the logic to get the visual roots
> indented.
>
> 5. Make visual root cascading wrap after 4 columns
>
> 6. Add --[no-]graph-indent and log.graphIndent options.
>
> GitHub CI: https://github.com/pabloosabaterr/git/actions/runs/29331144667
>
> [1]: https://lore.kernel.org/git/xmqqwnwajbuj.fsf@gitster.c.googlers.com/
>
> V11 DIFF:
>
> - Changed the check that required graph, to not confuse because it is a
> boolean value.
>
> - Typos
>
> Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
> ---
> Pablo Sabater (7):
> lib-log-graph: move check_graph function
> revision: add next_commit_to_show()
> graph: add a 2 commit buffer for lookahead
> graph: indent visual root in graph
> graph: wrap cascading commits after 4 columns
> graph: move config reading into graph_read_config()
> graph: add --[no-]graph-indent and log.graphIndent
>
> Documentation/config/log.adoc | 4 +
> Documentation/rev-list-options.adoc | 8 +
> graph.c | 332 +++++++++++++++-
> graph.h | 17 +
> revision.c | 57 ++-
> revision.h | 2 +
> t/lib-log-graph.sh | 5 +
> t/meson.build | 1 +
> t/t4215-log-skewed-merges.sh | 33 +-
> t/t4218-log-graph-indentation.sh | 596 +++++++++++++++++++++++++++++
> t/t6016-rev-list-graph-simplify-history.sh | 25 +-
> 11 files changed, 1032 insertions(+), 48 deletions(-)
>
> Range-diff versus v11:
>
> 1: dd0bb0d215 = 1: d754392142 lib-log-graph: move check_graph function
> 2: 07e239533d = 2: c93c2c0771 revision: add next_commit_to_show()
> 3: 4d71f674a1 = 3: 70fe612ae1 graph: add a 2 commit buffer for lookahead
> 4: 48ad2562f0 = 4: e1ac06c4ea graph: indent visual root in graph
> 5: 45be69d11b = 5: ce52b41527 graph: wrap cascading commits after 4 columns
> 6: 8ce53ae21b = 6: 9b7bb2cebc graph: move config reading into graph_read_config()
> 7: c1fa81022e ! 7: 13e830725f graph: add --[no-]graph-indent and log.graphIndent
> @@ Documentation/rev-list-options.adoc: This implies the `--topo-order` option by d
> + When used with `--graph`, indent visual roots (commits with no parents
> + or whose parents are not shown) to differentiate them from commits that
> + are vertically adjacent but unrelated. Enabled by default. Use
> -+ `--no-graph-indent` to disable or set `graph.indent` to set a deafault
> -+ preference.
> ++ `--no-graph-indent` to disable or set `log.graphIndent` to set a
> ++ default preference.
> +
> ifdef::git-rev-list[]
> `--count`::
> @@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *re
> if (revs->graph_max_lanes > 0 && !revs->graph)
> die(_("the option '%s' requires '%s'"), "--graph-lane-limit", "--graph");
>
> -+ if (revs->graph_indent_set > 0 && !revs->graph)
> ++ if (revs->graph_indent_set && !revs->graph)
> + die(_("the option '%s' requires '%s'"), "--[no-]graph-indent", "--graph");
> +
> if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
> @@ t/t4218-log-graph-indentation.sh: test_expect_success 'visual root cascading get
> + EOF
> +'
> +
> -+# graph.indent true and no --option is the default state.
> ++# log.graphIndent unset and no --option (which activates graph indentation) is
> ++# the default state.
> +
> test_done
>
> ---
> base-commit: f60db8d575adb79761d363e026fb49bddf330c73
This version looks fine to me.
Thanks,
Chandra.
^ permalink raw reply
* [PATCH 0/2] gitweb: shorten commitdiff index hashes with file modes
From: Travor Liu @ 2026-07-15 9:11 UTC (permalink / raw)
To: git; +Cc: Travor Liu
From: Travor Liu <travor_lzh@outlook.com>
This series fixes gitweb rendering of commitdiff index lines that carry
a trailing file mode, such as:
index <old>..<new> 100644
gitweb currently recognizes the mode before matching the object IDs,
which appends the file-type annotation first. The later object-ID
matcher expects the ID range to end the line, so these common index
lines keep two full, unlinked object IDs.
Patch 1 moves the mode handling so the index object IDs can still be
shortened and linked.
Patch 2 adds a gitweb regression test for the common
"index <old>..<new> 100644" form.
Tested with:
make NO_RUST=YesPlease
cd t && prove -v t9502-gitweb-standalone-parse-output.sh
Travor Liu (2):
gitweb: shorten index hashes with trailing file modes
t9502: test gitweb index hash formatting with modes
gitweb/gitweb.perl | 18 +++++++++++++-----
t/t9502-gitweb-standalone-parse-output.sh | 14 ++++++++++++++
2 files changed, 27 insertions(+), 5 deletions(-)
--
2.52.0
^ permalink raw reply
* [PATCH 1/2] gitweb: shorten index hashes with trailing file modes
From: Travor Liu @ 2026-07-15 9:11 UTC (permalink / raw)
To: git; +Cc: Travor Liu
In-Reply-To: <SA1PR10MB997715AD62D7F2AF64EB1A9887F1F82@SA1PR10MB997715.namprd10.prod.outlook.com>
From: Travor Liu <travor_lzh@outlook.com>
Diff index lines have included a trailing file mode since ec1fcc16af
(Show original and resulting blob object info in diff output,
2005-10-07) when the old and new file modes match:
index <old>..<new> 100644
gitweb recognizes that trailing mode before it tries to shorten and
link the object IDs. This appends the file-type annotation first, but
the object-ID matcher requires the ID range to end the line. As a
result, this common form keeps both full object IDs as plain text.
That is inconsistent with other hash displays and makes commitdiff
output wider than necessary. Recent gitweb changes have fixed mobile
overflow in log, commit, blob and diff views; leaving two full object
IDs in this header preserves an avoidable long line in the diff header.
Remove the trailing mode before matching the index IDs, then append it
again after the IDs have been shortened and linked. This preserves the
mode display while letting ordinary and combined index lines use the
existing object-ID formatting paths.
Signed-off-by: Travor Liu <travor_lzh@outlook.com>
---
gitweb/gitweb.perl | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index fde8045..8c2d9b8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2339,12 +2339,14 @@ sub format_extended_diff_header_line {
$line .= $cgi->a({-href=>$to->{'href'}, -class=>"path"},
esc_path($to->{'file'}));
}
- # match single <mode>
- if ($line =~ m/\s(\d{6})$/) {
- $line .= '<span class="info"> (' .
- file_type_long($1) .
- ')</span>';
+
+ # Temporarily remove a trailing <mode> so an index line ends with its
+ # object IDs and can be shortened below.
+ my $mode;
+ if ($line =~ s/\s(\d{6})$//) {
+ $mode = $1;
}
+
# match <hash>
if ($line =~ oid_nlen_prefix_infix_regex($sha1_len, "index ", ",") |
$line =~ oid_nlen_prefix_infix_regex($sha256_len, "index ", ",")) {
@@ -2388,6 +2390,12 @@ sub format_extended_diff_header_line {
my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
$line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
}
+ if (defined $mode) {
+ $line .= " $mode" .
+ '<span class="info"> (' .
+ file_type_long($mode) .
+ ')</span>';
+ }
return $line . "<br/>\n";
}
--
2.52.0
^ permalink raw reply related
* [PATCH 2/2] t9502: test gitweb index hash formatting with modes
From: Travor Liu @ 2026-07-15 9:11 UTC (permalink / raw)
To: git; +Cc: Travor Liu
In-Reply-To: <SA1PR10MB997715AD62D7F2AF64EB1A9887F1F82@SA1PR10MB997715.namprd10.prod.outlook.com>
From: Travor Liu <travor_lzh@outlook.com>
gitweb should shorten and link the object IDs in commitdiff index lines
even when Git includes the trailing file mode:
index <old>..<new> 100644
Add coverage for that common form by rendering a commitdiff for a
regular file modification. Check that the visible index line contains
linked short blob IDs followed by the mode and file-type annotation,
and that the full unlinked form is not emitted.
Signed-off-by: Travor Liu <travor_lzh@outlook.com>
---
t/t9502-gitweb-standalone-parse-output.sh | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index 81d5625..7f37e26 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -115,6 +115,20 @@ test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
'
test_debug 'cat gitweb.headers'
+test_expect_success 'commitdiff: index line shortens hashes with mode' '
+ old_blob=$(git rev-parse HEAD:foo) &&
+ old_short=$(git rev-parse --short=7 HEAD:foo) &&
+ echo changed >foo &&
+ git commit -am "change foo" &&
+ new_blob=$(git rev-parse HEAD:foo) &&
+ new_short=$(git rev-parse --short=7 HEAD:foo) &&
+ gitweb_run "p=.git;a=commitdiff;h=HEAD" &&
+ grep ">${old_short}</a>\\.\\.<a [^>]*>${new_short}</a> 100644" \
+ gitweb.body >index_line &&
+ grep "<span class=\"info\"> (file)</span>" index_line &&
+ ! grep "index ${old_blob}\\.\\.${new_blob} 100644" gitweb.body
+'
+
# ----------------------------------------------------------------------
# forks of projects
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v2 02/10] sequencer: move definition of is_final_fixup()
From: Phillip Wood @ 2026-07-15 9:12 UTC (permalink / raw)
To: Andrei Rybak
Cc: farid.m.zakaria, git, gitster, oswald.buddenhagen, phillip.wood,
u.kleine-koenig
In-Reply-To: <20260714225056.2285055-1-rybak.a.v@gmail.com>
Hi Andrei
On 14/07/2026 23:50, Andrei Rybak wrote:
>> Move this function earlier in the file in preparation for adding a
>> new caller in a later commit.
>>
>> @@ -4925,6 +4910,21 @@ static int reread_todo_if_changed(struct repository *r,
>
> 4910 is greater than 4627, the function is_final_fixup() seems to have been
> moved _later_ in the file. But the commit message says "Move this function
> earlier in the file". Am I missing something?
Oh, thanks for the sanity check. I could have sworn I had to move this
function to get a later commit to compile at one point, but it clearly
doesn't need to move now. I'll drop this patch.
Thanks
Phillip
>
>> strbuf_release(&buf);
>>
>> return 0;
>> +}
>> +
>> +static int is_final_fixup(struct todo_list *todo_list)
>> +{
>> + int i = todo_list->current;
>> +
>> + if (!is_fixup(todo_list->items[i].command))
>> + return 0;
>> +
>> + while (++i < todo_list->nr)
>> + if (is_fixup(todo_list->items[i].command))
>> + return 0;
>> + else if (!is_noop(todo_list->items[i].command))
>> + break;
>> + return 1;
>> }
>>
>> static const char rescheduled_advice[] =
>> --
>> 2.54.0.200.gfd8d68259e3
^ permalink raw reply
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