* Re: [PATCH v2 00/13] setup: split up repository discovery and setup
From: Toon Claes @ 2026-07-08 9:42 UTC (permalink / raw)
To: Junio C Hamano, Justin Tobler; +Cc: Patrick Steinhardt, git
In-Reply-To: <xmqqldbm4r86.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
> Justin Tobler <jltobler@gmail.com> writes:
>
>> The changes in this version look good to me. Thanks.
>
> Thanks, both. These indeed look good.
It's nice to see the discovery being kept in this new struct that holds
accurate information. There is quite some code shuffled around through
this series, but the changes look sensible. So as far as I am concerned,
I agree this series looks good.
Cheers,
Toon
^ permalink raw reply
* Re: [PATCH v2 05/13] setup: introduce explicit repository discovery
From: Toon Claes @ 2026-07-08 9:32 UTC (permalink / raw)
To: Patrick Steinhardt, git; +Cc: Justin Tobler, Junio C Hamano
In-Reply-To: <20260707-pks-setup-split-discovery-and-setup-v2-5-aab372cd227c@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> When setting up the global repository we intermix repository discovery
> and repository configuration: we repeatedly call `set_git_work_tree()`
> and `apply_and_export_relative_gitdir()` until we're happy with the
> result. The result of this is then a partially-configured repository
> that we use for further setup.
>
> This process is quite hard to follow, as it's never quite clear which
> parts of the repository have been configured already and which haven't.
> Furthermore, it means that the repository configuration is distributed
> across many different places instead of having it neatly contained in a
> single location. Ultimately, this is the reason that we cannot use a
> central function like `repo_init()`.
>
> Refactor the logic so that we stop partially-configuring a repository
> and instead populate a new `struct repo_discovery`. This allow us to
> essentially split repository setup into two phases:
>
> - The first phase only figures out parameters required to configure
> the repository.
>
> - The second phase then takes these parameters and applies them to the
> repository.
>
> Like this, we'll never end up with a partially-configured repository and
> can eventually extend `repo_init()` to handle the full initialization
> for us.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> setup.c | 155 ++++++++++++++++++++++++++++++++++++++++------------------------
> 1 file changed, 98 insertions(+), 57 deletions(-)
>
> diff --git a/setup.c b/setup.c
> index 324a235dd1..f713d024f7 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -1090,14 +1090,47 @@ static void apply_and_export_relative_gitdir(struct repository *repo, const char
> strbuf_release(&realpath);
> }
>
> -static const char *setup_explicit_git_dir(struct repository *repo,
> - const char *gitdirenv,
> - struct strbuf *cwd,
> - struct repository_format *repo_fmt,
> - int *nongit_ok)
> +struct repo_discovery {
> + char *gitdir;
> + char *worktree;
> +};
I like where you're going with this. It's nicer to have a struct that
captures what we know and has accurate data, instead of having a
`repository` that's only partly initialized.
--
Cheers,
Toon
^ permalink raw reply
* [PATCH v3] t1410-reflog.sh: avoid suppressing git's exit code in pipelines
From: Gatla Vishweshwar Reddy @ 2026-07-08 9:20 UTC (permalink / raw)
To: git; +Cc: Gatla Vishweshwar Reddy
In-Reply-To: <xmqqechf8ryu.fsf@gitster.g>
Piping git commands directly to wc -l suppresses the exit code of
git, hiding potential failures from the test suite. Capture the
output to a temporary file first, then count the lines separately
to preserve the exit code. Where the expected count is known ahead
of time, use test_stdout_line_count instead.
Signed-off-by: Gatla Vishweshwar Reddy <gatlavishweshwarreddy26@gmail.com>
---
Changes in v3:
- Removed all variables (HEAD_entry_count, main_entry_count) entirely
- Removed all temporary file captures (reflog_output, reflog_main_output)
- Used test_stdout_line_count with hardcoded counts throughout
- The counts are known ahead of time: HEAD=5, main=5 initially,decreasing as entries are deleted
t/t1410-reflog.sh | 27 +++++++++------------------
1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index ce71f9a30a..3f2e36cf33 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -244,30 +244,22 @@ test_expect_success 'delete' '
test_tick &&
git commit -m tiger C &&
- HEAD_entry_count=$(git reflog | wc -l) &&
- main_entry_count=$(git reflog show main | wc -l) &&
-
- test $HEAD_entry_count = 5 &&
- test $main_entry_count = 5 &&
-
+ test_stdout_line_count = 5 git reflog &&
+ test_stdout_line_count = 5 git reflog show main &&
git reflog delete main@{1} &&
+ test_stdout_line_count = 4 git reflog show main &&
+ test_stdout_line_count = 5 git reflog &&
git reflog show main > output &&
- test_line_count = $(($main_entry_count - 1)) output &&
- test $HEAD_entry_count = $(git reflog | wc -l) &&
! grep ox < output &&
- main_entry_count=$(wc -l < output) &&
-
git reflog delete HEAD@{1} &&
- test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
- test $main_entry_count = $(git reflog show main | wc -l) &&
-
- HEAD_entry_count=$(git reflog | wc -l) &&
+ test_stdout_line_count = 4 git reflog &&
+ test_stdout_line_count = 4 git reflog show main &&
git reflog delete main@{07.04.2005.15:15:00.-0700} &&
+ test_stdout_line_count = 3 git reflog show main &&
git reflog show main > output &&
- test_line_count = $(($main_entry_count - 1)) output &&
! grep dragon < output
'
@@ -319,13 +311,12 @@ test_expect_success 'git reflog expire unknown reference' '
test_must_fail git reflog expire does-not-exist 2>stderr &&
test_grep "error: reflog could not be found: ${SQ}does-not-exist${SQ}" stderr
'
-
test_expect_success 'checkout should not delete log for packed ref' '
- test $(git reflog main | wc -l) = 4 &&
+ test_stdout_line_count = 4 git reflog main &&
git branch foo &&
git pack-refs --all &&
git checkout foo &&
- test $(git reflog main | wc -l) = 4
+ test_stdout_line_count = 4 git reflog main
'
test_expect_success 'stale dirs do not cause d/f conflicts (reflogs on)' '
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v2 0/7] git_hash_*() quality-of-life improvements
From: Patrick Steinhardt @ 2026-07-08 8:05 UTC (permalink / raw)
To: Jeff King; +Cc: git, brian m. carlson, Junio C Hamano
In-Reply-To: <20260708035235.GA41491@coredump.intra.peff.net>
On Tue, Jul 07, 2026 at 11:52:35PM -0400, Jeff King wrote:
> On Tue, Jul 07, 2026 at 12:55:57AM -0400, Jeff King wrote:
>
> > This implements the "idempotent git_hash_discard()" discussed in this
> > subthread:
> >
> > https://lore.kernel.org/git/20260702080707.GG2029434@coredump.intra.peff.net/
> >
> > with associated cleanups.
>
> Here's a v2 addressing the comments so far. Mostly minor changes:
>
> - fixed typos noticed by Patrick
>
> - dropped extra braces added by coccinelle
>
> - dropped a trailing blank line from patch 1 (this gets fixed in a
> later patch as we add more content after the blank line, but I
> noticed "git apply" complaining)
>
> - a bit more explanation in patch 7 about why we don't support
> idempotent final() calls
All of these changes look good to me, and the range-diff matches what
you describe here. So this series looks good to me, thanks!
Patrick
^ permalink raw reply
* Re: [PATCH 01/11] odb: run "pre-auto-gc" hook for all maintenance tasks
From: Patrick Steinhardt @ 2026-07-08 7:55 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqo6gi1sng.fsf@gitster.g>
On Tue, Jul 07, 2026 at 12:55:31PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> > diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
> > index d7f82e1bec..1212b306b6 100755
> > --- a/t/t7900-maintenance.sh
> > +++ b/t/t7900-maintenance.sh
> > @@ -740,6 +740,127 @@ test_expect_success 'geometric repacking honors configured split factor' '
> > )
> > '
> >
> > +test_expect_success 'pre-auto-gc hook runs exactly once' '
> > + test_when_finished "rm -rf repo" &&
> > + git init repo &&
> > + (
> > + cd repo &&
> > + write_script .git/hooks/pre-auto-gc <<-\EOF &&
> > + echo hook >>hook.log
> > + EOF
> > +
> > + # Satisfy the auto condition for multiple tasks, both in the
> > + # foreground and in the background phase.
> > + git config set maintenance.reflog-expire.auto -1 &&
> > + git config set maintenance.geometric-repack.auto -1 &&
> > + git config set maintenance.rerere-gc.auto -1 &&
> > +
> > + GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
> > + git maintenance run --auto 2>/dev/null &&
> > +
> > + # The successful hook does not inhibit any of the tasks...
> > + test_subcommand git reflog expire --all <trace2.txt &&
> > + test_subcommand_flex git repack <trace2.txt &&
> > + test_subcommand git rerere gc <trace2.txt &&
> > + # ... but it must only have been executed a single time.
> > + test_line_count = 1 hook.log
> > + )
> > +'
>
> Somehow I'd feel better if the hook used a full path to the append
> only log file, but it is reasonably clear that these three commands
> are unlikely to chdir around, so it may be OK.
>
> Obviously not in scope of this topic, but I wonder if we have a
> better way to test these three "housekeeping tasks" have run, than
> casting in stone the current implementation that spawns these three
> external command as subprocesses.
Yeah, this is awfully fragile indeed. Since 25914c4fde (maintenance: add
trace2 regions for task execution, 2020-09-17) we already have trace2
markers for each task that's running, so that may indeed be a much
better way to figure out whether the task is running or not.
I've made some local changes, but will hold back with sending a v2 until
there's more feedback.
Thanks!
Patrick
^ permalink raw reply
* Re: [PATCH v2] config: retry acquiring config.lock, configurable via core.configLockTimeout
From: Johannes Schindelin @ 2026-07-08 7:32 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Junio C Hamano, Joerg Thalheim, git
In-Reply-To: <agrIrGwSMFlKTx9x@pks.im>
Hi Patrick,
On Mon, 18 May 2026, Patrick Steinhardt wrote:
> On Mon, May 18, 2026 at 09:46:05AM +0900, Junio C Hamano wrote:
> > Joerg Thalheim <joerg@thalheim.io> writes:
> >
> > > +/*
> > > + * How long to retry acquiring config.lock when another process holds
> > > + * it. Default matches core.packedRefsTimeout; override via
> > > + * core.configLockTimeout.
> > > + */
> > > +static long config_lock_timeout_ms(struct repository *r)
> > > +{
> > > + static int configured;
> > > + static int timeout_ms = 1000;
> > > +
> > > + if (!configured) {
> > > + repo_config_get_int(r, "core.configlocktimeout", &timeout_ms);
> > > + configured = 1;
> > > + }
> > > +
> > > + return timeout_ms;
> > > +}
> >
> > The above design means whichever repository happens to be passed for
> > the first time as "r" to this call will fix the return value from
> > the function for the rest of the system, meaning that the lock timeout
> > is a per-process property and the repository parameter passed to the
> > function does not matter all that much.
> >
> > It may make sense to admit that this is not a per-repository
> > property (due to the use of local caching), have the function take
> > no parameter and use the_repository to the config_get call. That
> > would make the intention more clear.
> >
> > Of course the other end of the spectrum is to get rid of the
> > "configured" caching here, and ask the config system to make a
> > hashtable look-up every time the function is called. That will keep
> > the lock timeout per-repository, which is closer to what the current
> > function signature suggests.
> >
> > I dunno. My gut feeling is that there aren't valid reasons why you
> > would want to specifically set different timeout values per
> > repository, so the simplicity of using the_repository (i.e. the
> > primary repository instance this process deals with) sounds like a
> > better way to go.
>
> There probably is no reason to have different values per repo. But to
> me the question is whether there even are any use cases where we have to
> lock the config file so often in quick succession that the caching
> mechanism even matters. My gut feeling says no, also because parsing the
> value from the configuration is going to be drowned out by actually
> writing the lockfile and renaming it into place.
>
> So I'd rather lean towards dropping the cache and keeping the repository
> parameter.
While the question whether or not to spend 5-ish lines on caching sounds
like a topic that could be debated in splendor and at length over a couple
of beverages in a cozy bar, I am starting to grow a suspicion that I want
to doubt whether the cost of that cache was worth blocking this patch for
over a month. Lacking such a cozy setting and at this time also lacking
the leisure to enjoy said beverages, I'd rather go forward with the
proposed version and move on to more exciting things.
In other words: I consider this patch fine as-is, and in the event that I
would consider highly unlikely where the cache _really_ bothers anyone, it
will be an easy patch to remove it.
Ciao,
Johannes
^ permalink raw reply
* Re: [PATCH 1/2] t: add tests for ref tombstone scenarios
From: Kristofer Karlsson @ 2026-07-08 7:28 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Kristofer Karlsson via GitGitGadget, git
In-Reply-To: <ak3nWvyX4E9qB4T1@pks.im>
On Wed, 8 Jul 2026 at 08:00, Patrick Steinhardt <ps@pks.im> wrote:
>
> >
> > I could not find an existing test that covers the delete-then-recreate
> > flow (where tombstones are present when the new refs are created).
> > The existing tests cover creation and deletion separately but not the
> > interaction with tombstones.
> > (But perhaps such a test exists and I just can't find it.)
>
> In t1400 we definitely have some tests where we exercise this
> implicitly. In any case, if we want to retain this test I'd rather add
> it to t1400 itself, as the functionality that we're testing is itself
> not specific to the backend.
Thanks, you are right about the placement -- the contract is valid
regardless of backend.
You are also right about it already being tested this is implicitly
tested between multiple test runs since they have shared state
(the repo). Multiple tests delete the ref as clean up and
multiple tests also create a ref and verifies it.
So it is technically covered but it depends on multiple tests
being executed. I think this is simply exposing my personal
preference to have more self-contained and explicit tests,
but I am happy to drop the added tests -- it is perhaps more
important to avoid bloating the test code.
I will drop the added correctness test for the next iteration.
Thanks,
Kristofer
^ permalink raw reply
* Re: [PATCH 00/16] path: remove dependency on `the_repository`
From: krobchai wongkamw @ 2026-07-08 7:16 UTC (permalink / raw)
To: ps; +Cc: git
ส่งจาก iPhone ของฉัน
^ permalink raw reply
* Re: [PATCH v2 15/16] environment: move access to "core.sharedRepository" into repo settings
From: krobchai wongkamw @ 2026-07-08 7:11 UTC (permalink / raw)
To: ps; +Cc: git, karthik.188, shejialuo
ส่งจาก iPhone ของฉัน
^ permalink raw reply
* Re: [PATCH v2 00/11] receive-pack: use ODB transactions to stage object writes
From: Patrick Steinhardt @ 2026-07-08 6:42 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-1-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:01PM -0500, Justin Tobler wrote:
> Changes since V1:
>
> - Adapted other "file" ODB transaction helpers to be more consistent
> with current naming scheme.
> - Removed redundant NULL transaction handling from
> `odb_transaction_files_begin()`.
> - `odb_transaction_begin()` now returns an error if there is already
> an inflight transaction pending instead of setting the `out` pointer
> to NULL.
> - Updated `odb_transaction_env()` to return an error code and append
> environment variables to a strvec provided as an argument.
> - Removed redundant setting of tmpdir environment variables for child
> processes after tmpdir has been migrated.
> - Split changes adding ODB transaction flags into a separate commit.
> - Consistently wire the ODB transaction throughout git-receive-pack
> code instead of reading it from `the_repository`.
> - Updated user facing error message.
> - Updated some comments to better document functions/flags.
> - Clarified some commit messages.
> - Fixed typos.
I've got a couple smaller nits, but overall I'm quite happy with the
shape of this series now. Thanks!
Patrick
^ permalink raw reply
* Re: [PATCH v2 10/11] builtin/receive-pack: drop redundant tmpdir env
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-11-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:11PM -0500, Justin Tobler wrote:
> When performing the connectivity checks for a shallow ref in
> `update_shallow_ref()`, the child process environment variables are
> populated via `tmp_objdir_env()`. This is unnecessary though as
> `update_shallow_ref()` is only reached after `tmp_objdir_migrate()` has
> been performed which means there is no longer a temporary directory that
> needs to be shared with child processes.
Right. We call it transitively via either `execute_commands_atomic()` or
`execute_commands_not_atomic()`, both of which are called after
`tmp_objdir_migrate()`.
Patrick
^ permalink raw reply
* Re: [PATCH v2 09/11] odb/transaction: introduce ODB transaction flags
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-10-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:10PM -0500, Justin Tobler wrote:
> diff --git a/object-file.c b/object-file.c
> index 9b8ee6f36c..d95bdabba5 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -1710,6 +1712,27 @@ int odb_transaction_files_begin(struct odb_source *source,
> transaction->base.commit = odb_transaction_files_commit;
> transaction->base.write_object_stream = odb_transaction_files_write_object_stream;
> transaction->base.env = odb_transaction_files_env;
> +
> + transaction->prefix = "bulk-fsync";
> + if (flags & ODB_TRANSACTION_RECEIVE) {
> + /*
> + * ODB transactions for git-receive-pack(1) eagerly create a
> + * temporary directory and use a different temporary directory
> + * prefix.
> + *
> + * NEEDSWORK: This transaction flag is only used by the "files"
> + * backend to special case temporary directory set up and
> + * handling. Ideally transaction users should not have to care
> + * though. To avoid this, we could eagerly create the temporary
> + * directory and use the same prefix name for all transactions.
> + */
Yup, agreed, thanks for noting this here.
> diff --git a/odb/transaction.h b/odb/transaction.h
> index 1c6c97a53e..b19f180aee 100644
> --- a/odb/transaction.h
> +++ b/odb/transaction.h
> @@ -4,7 +4,6 @@
> #include "git-compat-util.h"
> #include "gettext.h"
> #include "odb.h"
> -#include "odb/source.h"
This is curious, and likely a result of you adding "odb/transaction.h"
to "odb/source.h".
> @@ -45,6 +44,12 @@ struct odb_transaction {
> int (*env)(struct odb_transaction *transaction, struct strvec *env);
> };
>
> +/* Flags used to configure an ODB transaction. */
> +enum odb_transaction_flags {
> + /* Configures the transaction for use with git-receive-pack(1). */
> + ODB_TRANSACTION_RECEIVE = (1 << 0),
> +};
> +
> /*
> * Starts an ODB transaction and returns it via `out`. Subsequent objects are
> * written to the transaction and not committed until odb_transaction_commit()
And this is the reason you have to add the include, so that the flags
are visible in both "odb/source.h" and in "odb/transaction.h".
This makes me wonder whether there's really much value in having this
header here be split out of "odb/source.h".
Patrick
^ permalink raw reply
* Re: [PATCH v2 07/11] odb/transaction: propagate commit errors
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-8-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:08PM -0500, Justin Tobler wrote:
> diff --git a/odb/transaction.c b/odb/transaction.c
> index df4275151b..51af2c9a61 100644
> --- a/odb/transaction.c
> +++ b/odb/transaction.c
> @@ -16,19 +16,26 @@ int odb_transaction_begin(struct object_database *odb,
> return ret;
> }
>
> -void odb_transaction_commit(struct odb_transaction *transaction)
> +int odb_transaction_commit(struct odb_transaction *transaction)
> {
> + int ret;
> +
> if (!transaction)
> - return;
> + return 0;
>
> /*
> * Ensure the transaction ending matches the pending transaction.
> */
> ASSERT(transaction == transaction->source->odb->transaction);
>
> - transaction->commit(transaction);
> + ret = transaction->commit(transaction);
> + if (ret)
> + return ret;
> +
> transaction->source->odb->transaction = NULL;
> free(transaction);
> +
> + return 0;
> }
Doesn't this cause a leak now?
I think this interface here is doing the same mistake that our reference
transactions did, where we automatically released the transaction on
commit. That caused multiple lifetime issues with references all over
the place.
This isn't an issue introduced by this patch series though, so it's fine
to ignore this for now.
Patrick
^ permalink raw reply
* Re: [PATCH v2 06/11] odb/transaction: propagate begin errors
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-7-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:07PM -0500, Justin Tobler wrote:
> When `odb_transaction_begin()` is invoked, the function returns the
> transaction pointer directly. There is no way for the backend to
> signal that it failed to set up its state, such as when creating the
> temporary object directory backing the transaction.
>
> In a subsequent commit, git-receive-pack(1) starts using ODB
> transactions and needs to be able to report such failures rather
> than silently ignore them. Refactor `odb_transaction_begin()` to
> return an int error code and write the resulting transaction into an
> out parameter. Also introduce `odb_transaction_begin_or_die()` as a
> convenience for callsites that do not need to handle errors
> explicitly.
>
> Note that `odb_transaction_begin()` now returns an error when the ODB
> already has an inflight transaction pending. ODB transaction call sites
> that may encounter an inflight transaction are updated to explicitly
> handle this case.
Yeah, this change is very much welcome and results in much saner
behaviour with less surprises. Thanks for making the change.
> diff --git a/cache-tree.c b/cache-tree.c
> index 184f7e2635..8eec1d4d52 100644
> --- a/cache-tree.c
> +++ b/cache-tree.c
> @@ -474,6 +474,7 @@ static int update_one(struct cache_tree *it,
>
> int cache_tree_update(struct index_state *istate, int flags)
> {
> + int inflight = !!the_repository->objects->transaction;
> struct odb_transaction *transaction;
> int skip, i;
>
> @@ -490,10 +491,12 @@ int cache_tree_update(struct index_state *istate, int flags)
>
> trace_performance_enter();
> trace2_region_enter("cache_tree", "update", istate->repo);
> - transaction = odb_transaction_begin(the_repository->objects);
> + if (!inflight)
> + odb_transaction_begin_or_die(the_repository->objects, &transaction);
> i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
> "", 0, &skip, flags);
> - odb_transaction_commit(transaction);
> + if (!inflight)
> + odb_transaction_commit(transaction);
> trace2_region_leave("cache_tree", "update", istate->repo);
> trace_performance_leave("cache_tree_update");
> if (i < 0)
Callsites like this really make me wonder why we even care to create
a transaction in the first place if we basically just commit it
immediately anyway. And while it's a bit sad that we have so many sites
where we don't really know whether we even have a transaction, I think
it's a good change that we have now annotated them clearly. A subsequent
patch series may then eventually refactor those sites so that we stop
depending on `odb->transaction` and inject the transaction via a
parameter.
> diff --git a/odb/transaction.h b/odb/transaction.h
> index d52f0533ce..36032a5365 100644
> --- a/odb/transaction.h
> +++ b/odb/transaction.h
> @@ -1,6 +1,8 @@
> #ifndef ODB_TRANSACTION_H
> #define ODB_TRANSACTION_H
>
> +#include "git-compat-util.h"
We typically don't include "git-compat-util.h" in header files.
> @@ -36,11 +38,21 @@ struct odb_transaction {
> };
>
> /*
> - * Starts an ODB transaction. Subsequent objects are written to the transaction
> - * and not committed until odb_transaction_commit() is invoked on the
> - * transaction. If the ODB already has a pending transaction, NULL is returned.
> + * Starts an ODB transaction and returns it via `out`. Subsequent objects are
> + * written to the transaction and not committed until odb_transaction_commit()
> + * is invoked on the transaction. Returns 0 on success and a negative value on
> + * error. Note that it is considered an error to start a new transaction if the
> + * ODB already has an inflight transaction pending.
> */
> -struct odb_transaction *odb_transaction_begin(struct object_database *odb);
> +int odb_transaction_begin(struct object_database *odb,
> + struct odb_transaction **out);
> +
> +static inline void odb_transaction_begin_or_die(struct object_database *odb,
> + struct odb_transaction **out)
> +{
> + if (odb_transaction_begin(odb, out))
> + die(_("failed to start ODB transaction"));
> +}
We could make it a bit simpler to use this function by continuing to
return the transaction directly. But on the other hand this results in a
more consistent interface.
Patrick
^ permalink raw reply
* Re: [PATCH v2 05/11] object-file: propagate files transaction errors
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-6-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:06PM -0500, Justin Tobler wrote:
> diff --git a/object-file.c b/object-file.c
> index e51389833a..64cb874fe7 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -511,11 +511,15 @@ static void odb_transaction_files_prepare(struct odb_transaction *base)
> * added at the time they call odb_transaction_files_begin.
> */
> if (!transaction || transaction->objdir)
> - return;
> + return 0;
>
> transaction->objdir = tmp_objdir_create(base->source->odb->repo, "bulk-fsync");
> - if (transaction->objdir)
> - tmp_objdir_replace_primary_odb(transaction->objdir, 0);
> + if (!transaction->objdir)
> + return -1;
As far as I can see we don't report any errors as part of
`tmp_objdir_create()`, so we should probably print an error here.
> @@ -1668,14 +1672,19 @@ static void odb_transaction_files_commit(struct odb_transaction *base)
> * Make the object files visible in the primary ODB after their data is
> * fully durable.
> */
> - tmp_objdir_migrate(transaction->objdir);
> + if (tmp_objdir_migrate(transaction->objdir))
> + return -1;
> +
Likewise.
Patrick
^ permalink raw reply
* Re: [PATCH v2 04/11] object-file: drop check for inflight transactions
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-5-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:05PM -0500, Justin Tobler wrote:
> ODB transactions are started via `odb_transaction_begin()` and contain
> validation to avoid starting multiple transactions at the same time. The
> "files" backend also has the same logic, but is redundant due to the
> generic layer already handling it. Drop this validation from the "files"
> backend accordingly.
Makes sense, and it fixes a layering violation: in the best case, a
source only has to care about itself and not about the owning object
database. Managing object-database-level state should be done by the
object database itself.
Patrick
^ permalink raw reply
* Re: [PATCH v2 03/11] object-file: embed transaction flush logic in commit function
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-4-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:04PM -0500, Justin Tobler wrote:
> When a "files" transaction is committed,
> `flush_loose_object_transaction()` is invoked to handle performing a
> hardware flush along with migrating the temporary object directory into
> the primary. In a subsequent commit, the temporary directory is also
> used to write packfiles.
>
> Instead of maintaining a separate helper function, embed the logic to
> flush and migrate the temporary directory directly into
> `odb_transaction_files_commit()`.
The change itself looks simple enough, but this makes me wonder why we
want to change this. Like, what subsequent step does this enable that
would otherwise be harder to do?
Maybe this will be answered by a subsequent commit.
Patrick
^ permalink raw reply
* Re: [PATCH v2 02/11] object-file: rename files transaction fsync function
From: Patrick Steinhardt @ 2026-07-08 6:41 UTC (permalink / raw)
To: Justin Tobler; +Cc: git, gitster
In-Reply-To: <20260708041412.1157499-3-jltobler@gmail.com>
On Tue, Jul 07, 2026 at 11:14:03PM -0500, Justin Tobler wrote:
> diff --git a/object-file.c b/object-file.c
> index a3eb8d71dd..d68824bb44 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -518,12 +518,17 @@ static void odb_transaction_files_prepare(struct odb_transaction *base)
> tmp_objdir_replace_primary_odb(transaction->objdir, 0);
> }
>
> -static void fsync_loose_object_transaction(struct odb_transaction *base,
> - int fd, const char *filename)
> +static void odb_transaction_files_fsync(struct odb_transaction *base,
> + int fd, const char *filename)
> {
> struct odb_transaction_files *transaction =
> container_of_or_null(base, struct odb_transaction_files, base);
>
> + if (!transaction || !transaction->objdir) {
> + fsync_or_die(fd, filename);
> + return;
> + }
The change results in a tiny bit of duplication, but I agree that it's
easier to reason about.
> @@ -531,8 +536,7 @@ static void fsync_loose_object_transaction(struct odb_transaction *base,
Somewhat funny that the diff renderer decided to put the hunk header
here instead of showing the single line that's now missing from the
diff.
> * before renaming the objects to their final names as part of
> * flush_batch_fsync.
> */
> - if (!transaction || !transaction->objdir ||
> - git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
> + if (git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) {
> if (errno == ENOSYS)
> warning(_("core.fsyncMethod = batch is unsupported on this platform"));
> fsync_or_die(fd, filename);
Thanks!
Patrick
^ permalink raw reply
* Re: [PATCH] SubmittingPatches: abandoning a series
From: Weijie Yuan @ 2026-07-08 6:21 UTC (permalink / raw)
To: Michael Montalbo; +Cc: Junio C Hamano, git
In-Reply-To: <CAC2QwmK2kFBNRT47i4k-zBK+b0PFezLFbx0eO8t7DqT7tTnKhw@mail.gmail.com>
On Tue, Jul 07, 2026 at 10:41:53PM -0700, Michael Montalbo wrote:
> I think my workflow for replying to threads I am not already a part of is
> flawed. I usually control-click the reply mailto: link on the message in
> lore.kernel.org/git. That launches Gmail then I manually craft a message
> and send from there. I will spend some time investigating my workflow
> more deeply.
I guess you are right. I suspect the Gmail web interface is the reason
why the In-Reply-To header was lost here.
> Thank you for the helpful note!
You´re welcome!
^ permalink raw reply
* Re: [PATCH 1/2] t: add tests for ref tombstone scenarios
From: Patrick Steinhardt @ 2026-07-08 5:59 UTC (permalink / raw)
To: Kristofer Karlsson; +Cc: Kristofer Karlsson via GitGitGadget, git
In-Reply-To: <CAL71e4ORdJXsz58SH71VjDNAWZ39T3+TrWN+gScAFx=Gt0CTkQ@mail.gmail.com>
On Tue, Jul 07, 2026 at 06:12:31PM +0200, Kristofer Karlsson wrote:
> On Tue, 7 Jul 2026 at 17:24, Patrick Steinhardt <ps@pks.im> wrote:
> > On Mon, Jul 06, 2026 at 01:35:55PM +0000, Kristofer Karlsson via GitGitGadget wrote:
> > > diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
> > > +test_expect_success 'delete and re-create refs with tombstones' '
> >
> > I wonder whether this test really adds any value. We probably have lots
> > of tests already that test creation/deletion of references.
>
> I could not find an existing test that covers the delete-then-recreate
> flow (where tombstones are present when the new refs are created).
> The existing tests cover creation and deletion separately but not the
> interaction with tombstones.
> (But perhaps such a test exists and I just can't find it.)
In t1400 we definitely have some tests where we exercise this
implicitly. In any case, if we want to retain this test I'd rather add
it to t1400 itself, as the functionality that we're testing is itself
not specific to the backend.
Patrick
^ permalink raw reply
* Re: [PATCH] SubmittingPatches: abandoning a series
From: Weijie Yuan @ 2026-07-08 5:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Michael Montalbo, git
In-Reply-To: <xmqqechew0ap.fsf@gitster.g>
On Tue, Jul 07, 2026 at 09:52:14PM -0700, Junio C Hamano wrote:
> Michael Montalbo <mmontalbo@gmail.com> writes:
> [...]
> > Finally, maybe a rule of thumb as to how old a topic a topic should get
> > before a "staleness" update is provided would be helpful, or maybe that
> > is too contextually dependent / would potentially introduce some
> > unwanted scheduling contract?
> >
> > Overall, I think the explicit guidance is helpful.
>
> I've been working on streamlining my workflow to manage the "What's
> cooking" report, and writing down guidelines with concrete numbers.
>
> * When the discussion seems to reach rough consensus that the
> latest round looks good for 'next', the topic is immediately
> marked as "Will merge to 'next'?" in my draft copy (note: I do
> not want to spam the list with "What's cooking" too often, but it
> is the document I work from, and it is updated multiple times a
> day).
>
> * After no negative opinions are seen on a topic in "Will merge to
> 'next'?" state for about 36 hours, the topic is marked as "Will
> merge to 'next'". I will merge such topics during the next
> integration cycle (note: I can only have up to two integration
> cycles per day due to time constraints).
>
> * Imagine that a topic was last updated more than 4 weeks ago. If
> there are review comments on the topic that are left unanswered
> by the author for more than a week, and if nothing happens in the
> discussion thread other than inquiry on the current status, the
> topic is marked as "stalled". I will try to notice and ping a
> stalled topic once or twice, but after that I may discard such a
> topic (which by the way I really hate having to decide to do so).
>
> * After a topic is merged to 'next', if nothing negative that needs
> fixing is discovered for 7 calendar days, the topic is merged to
> 'master'. I may shorten this depending on how complex the topic
> is.
>
> There may be more, but these are what I can think of offhand.
Integrating the above parts into the document seems like a good idea.
btw, do we need to synchronize MyFirstContribution simultaneously?
Quoting Patrick's words [1]:
Overall it's a bit on the annoying side that we have to always make sure
to update both SubmittingPatches and MyFirstContribution in tandem.
Makes me wonder whether they are mostly redundant and whether it would
make sense to eventually merge them.
Thanks,
Weijie
[1] https://lore.kernel.org/git/ai_7Wh7hrD8PZozg@pks.im/
^ permalink raw reply
* Re: [PATCH] SubmittingPatches: abandoning a series
From: Michael Montalbo @ 2026-07-08 5:41 UTC (permalink / raw)
To: wy; +Cc: Junio C Hamano, git
In-Reply-To: <ak3fr3avEmt-UJf7@wyuan.org>
On Tue, Jul 7, 2026 at 10:27 PM Weijie Yuan <wy@wyuan.org> wrote:
>
> Hi Michael,
>
> By the way, off-topic:
>
> I noticed that several times your replies didn't carry with a
> "In-Reply-To" header, which made me confused about the threads when
> surfing on the web interface of lore.kernel.org. So also, I cannot pull
> down the complete thread with b4. If possible, you may check your email
> clients, though all email clients suck ;-)
>
Argh, sorry about that :(
I think my workflow for replying to threads I am not already a part of is
flawed. I usually control-click the reply mailto: link on the message in
lore.kernel.org/git. That launches Gmail then I manually craft a message
and send from there. I will spend some time investigating my workflow
more deeply.
Thank you for the helpful note!
^ permalink raw reply
* Re: [PATCH] SubmittingPatches: abandoning a series
From: Weijie Yuan @ 2026-07-08 5:27 UTC (permalink / raw)
To: Michael Montalbo; +Cc: Junio C Hamano, git
In-Reply-To: <CAC2QwmJ8Z7hZHk9SofRsprvAR3B=UXUkeyy7i4uofRi-xEymow@mail.gmail.com>
Hi Michael,
By the way, off-topic:
I noticed that several times your replies didn't carry with a
"In-Reply-To" header, which made me confused about the threads when
surfing on the web interface of lore.kernel.org. So also, I cannot pull
down the complete thread with b4. If possible, you may check your email
clients, though all email clients suck ;-)
Thanks,
Weijie
^ permalink raw reply
* Re: [PATCH] SubmittingPatches: abandoning a series
From: Junio C Hamano @ 2026-07-08 4:52 UTC (permalink / raw)
To: Michael Montalbo; +Cc: git
In-Reply-To: <CAC2QwmJ8Z7hZHk9SofRsprvAR3B=UXUkeyy7i4uofRi-xEymow@mail.gmail.com>
Michael Montalbo <mmontalbo@gmail.com> writes:
> Junio C Hamano <gitster@pobox.com> writes:
>> + A discussion on the list might convince you that your changes are
>> + not such a good idea, in which case you are expected to explicitly
>> + retract the topic, to releave the maintainer from having to worry
>> + about it.
...
Thanks for improvements.
> Finally, maybe a rule of thumb as to how old a topic a topic should get
> before a "staleness" update is provided would be helpful, or maybe that
> is too contextually dependent / would potentially introduce some
> unwanted scheduling contract?
>
> Overall, I think the explicit guidance is helpful.
I've been working on streamlining my workflow to manage the "What's
cooking" report, and writing down guidelines with concrete numbers.
* When the discussion seems to reach rough consensus that the
latest round looks good for 'next', the topic is immediately
marked as "Will merge to 'next'?" in my draft copy (note: I do
not want to spam the list with "What's cooking" too often, but it
is the document I work from, and it is updated multiple times a
day).
* After no negative opinions are seen on a topic in "Will merge to
'next'?" state for about 36 hours, the topic is marked as "Will
merge to 'next'". I will merge such topics during the next
integration cycle (note: I can only have up to two integration
cycles per day due to time constraints).
* Imagine that a topic was last updated more than 4 weeks ago. If
there are review comments on the topic that are left unanswered
by the author for more than a week, and if nothing happens in the
discussion thread other than inquiry on the current status, the
topic is marked as "stalled". I will try to notice and ping a
stalled topic once or twice, but after that I may discard such a
topic (which by the way I really hate having to decide to do so).
* After a topic is merged to 'next', if nothing negative that needs
fixing is discovered for 7 calendar days, the topic is merged to
'master'. I may shorten this depending on how complex the topic
is.
THere may be more, but these are what I can think of offhand.
^ permalink raw reply
* [PATCH v2 11/11] builtin/receive-pack: stage incoming objects via ODB transactions
From: Justin Tobler @ 2026-07-08 4:14 UTC (permalink / raw)
To: git; +Cc: ps, gitster, Justin Tobler
In-Reply-To: <20260708041412.1157499-1-jltobler@gmail.com>
Objects received by git-receive-pack(1) are quarantined in a temporary
"incoming" directory and migrated into the object database prior to the
reference updates. The quarantine is currently managed through
`tmp_objdir` directly. In a pluggable ODB future, how exactly an object
gets written to a transaction may vary for a given ODB source. Refactor
git-receive-pack(1) to use the ODB transaction interfaces to manage the
object staging area in a more agnostic manner accordingly.
Note that the ODB transaction is now responsible for managing the
primary and alternate ODBs for the repository. One small change as a
result is that the temporary directory is now applied as the primary ODB
in the main process instead of an alternate. This does not change
anything for git-receive-pack(1) though because it only needs access to
the newly written objects and doesn't care how exactly it is set up.
Signed-off-by: Justin Tobler <jltobler@gmail.com>
---
builtin/receive-pack.c | 68 ++++++++++++++++++++++--------------------
1 file changed, 35 insertions(+), 33 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 50bc05c70c..8b8c20dc1a 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,7 +37,6 @@
#include "sigchain.h"
#include "string-list.h"
#include "strvec.h"
-#include "tmp-objdir.h"
#include "trace.h"
#include "trace2.h"
#include "version.h"
@@ -112,8 +111,6 @@ static enum {
} use_keepalive;
static int keepalive_in_sec = 5;
-static struct tmp_objdir *tmp_objdir;
-
static struct proc_receive_ref {
unsigned int want_add:1,
want_delete:1,
@@ -926,6 +923,7 @@ static void receive_hook_feed_state_free(void *data)
static int run_receive_hook(struct command *commands,
const char *hook_name,
int skip_broken,
+ struct odb_transaction *transaction,
const struct string_list *push_options)
{
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
@@ -959,8 +957,8 @@ static int run_receive_hook(struct command *commands,
strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
}
- if (tmp_objdir)
- strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
+ if (transaction)
+ odb_transaction_env(transaction, &opt.env);
prepare_push_cert_sha1(&opt);
@@ -1789,24 +1787,30 @@ static const struct object_id *command_singleton_iterator(void *cb_data)
}
static void set_connectivity_errors(struct command *commands,
- struct shallow_info *si)
+ struct shallow_info *si,
+ struct odb_transaction *transaction)
{
struct command *cmd;
for (cmd = commands; cmd; cmd = cmd->next) {
struct command *singleton = cmd;
struct check_connected_options opt = CHECK_CONNECTED_INIT;
+ struct strvec env = STRVEC_INIT;
if (shallow_update && si->shallow_ref[cmd->index])
/* to be checked in update_shallow_ref() */
continue;
- opt.env = tmp_objdir_env(tmp_objdir);
+ odb_transaction_env(transaction, &env);
+ opt.env = env.v;
+
if (!check_connected(command_singleton_iterator, &singleton,
&opt))
continue;
cmd->error_string = "missing necessary objects";
+
+ strvec_clear(&env);
}
}
@@ -2027,6 +2031,7 @@ static void execute_commands_atomic(struct command *commands,
static void execute_commands(struct command *commands,
const char *unpacker_error,
struct shallow_info *si,
+ struct odb_transaction *transaction,
const struct string_list *push_options)
{
struct check_connected_options opt = CHECK_CONNECTED_INIT;
@@ -2043,6 +2048,8 @@ static void execute_commands(struct command *commands,
}
if (!skip_connectivity_check) {
+ struct strvec env = STRVEC_INIT;
+
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
@@ -2056,14 +2063,17 @@ static void execute_commands(struct command *commands,
data.si = si;
opt.err_fd = err_fd;
opt.progress = err_fd && !quiet;
- opt.env = tmp_objdir_env(tmp_objdir);
+ odb_transaction_env(transaction, &env);
+ opt.env = env.v;
opt.exclude_hidden_refs_section = "receive";
if (check_connected(iterate_receive_command_list, &data, &opt))
- set_connectivity_errors(commands, si);
+ set_connectivity_errors(commands, si, transaction);
if (use_sideband)
finish_async(&muxer);
+
+ strvec_clear(&env);
}
reject_updates_to_hidden(commands);
@@ -2084,7 +2094,7 @@ static void execute_commands(struct command *commands,
}
}
- if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
+ if (run_receive_hook(commands, "pre-receive", 0, transaction, push_options)) {
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
cmd->error_string = "pre-receive hook declined";
@@ -2105,14 +2115,13 @@ static void execute_commands(struct command *commands,
* Now we'll start writing out refs, which means the objects need
* to be in their final positions so that other processes can see them.
*/
- if (tmp_objdir_migrate(tmp_objdir) < 0) {
+ if (odb_transaction_commit(transaction)) {
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
cmd->error_string = "unable to migrate objects to permanent storage";
}
return;
}
- tmp_objdir = NULL;
check_aliased_updates(commands);
@@ -2325,7 +2334,8 @@ static void push_header_arg(struct strvec *args, struct pack_header *hdr)
ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
}
-static const char *unpack(int err_fd, struct shallow_info *si)
+static const char *unpack(int err_fd, struct shallow_info *si,
+ struct odb_transaction *transaction)
{
struct pack_header hdr;
const char *hdr_err;
@@ -2350,20 +2360,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
strvec_push(&child.args, alt_shallow_file);
}
- tmp_objdir = tmp_objdir_create(the_repository, "incoming");
- if (!tmp_objdir) {
- if (err_fd > 0)
- close(err_fd);
- return "unable to create temporary object directory";
- }
- strvec_pushv(&child.env, tmp_objdir_env(tmp_objdir));
-
- /*
- * Normally we just pass the tmp_objdir environment to the child
- * processes that do the heavy lifting, but we may need to see these
- * objects ourselves to set up shallow information.
- */
- tmp_objdir_add_as_alternate(tmp_objdir);
+ odb_transaction_env(transaction, &child.env);
if (ntohl(hdr.hdr_entries) < unpack_limit) {
strvec_push(&child.args, "unpack-objects");
@@ -2430,13 +2427,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
return NULL;
}
-static const char *unpack_with_sideband(struct shallow_info *si)
+static const char *unpack_with_sideband(struct shallow_info *si,
+ struct odb_transaction *transaction)
{
struct async muxer;
const char *ret;
if (!use_sideband)
- return unpack(0, si);
+ return unpack(0, si, transaction);
use_keepalive = KEEPALIVE_AFTER_NUL;
memset(&muxer, 0, sizeof(muxer));
@@ -2445,7 +2443,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
if (start_async(&muxer))
return NULL;
- ret = unpack(muxer.in, si);
+ ret = unpack(muxer.in, si, transaction);
finish_async(&muxer);
return ret;
@@ -2622,6 +2620,7 @@ int cmd_receive_pack(int argc,
struct oid_array ref = OID_ARRAY_INIT;
struct shallow_info si;
struct packet_reader reader;
+ struct odb_transaction *transaction = NULL;
struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")),
@@ -2706,11 +2705,14 @@ int cmd_receive_pack(int argc,
if (!si.nr_ours && !si.nr_theirs)
shallow_update = 0;
if (!delete_only(commands)) {
- unpack_status = unpack_with_sideband(&si);
+ if (odb_transaction_begin(the_repository->objects, &transaction, ODB_TRANSACTION_RECEIVE))
+ unpack_status = "unable to start object transaction";
+ else
+ unpack_status = unpack_with_sideband(&si, transaction);
update_shallow_info(commands, &si, &ref);
}
use_keepalive = KEEPALIVE_ALWAYS;
- execute_commands(commands, unpack_status, &si,
+ execute_commands(commands, unpack_status, &si, transaction,
&push_options);
delete_tempfile(&pack_lockfile);
sigchain_push(SIGPIPE, SIG_IGN);
@@ -2719,7 +2721,7 @@ int cmd_receive_pack(int argc,
else if (report_status)
report(commands, unpack_status);
sigchain_pop(SIGPIPE);
- run_receive_hook(commands, "post-receive", 1,
+ run_receive_hook(commands, "post-receive", 1, NULL,
&push_options);
run_update_post_hook(commands);
free_commands(commands);
--
2.55.0.122.gf85a7e6620
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox