git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* Re: [PATCH v6 1/7] refs: accept symref values in `ref_transaction_update()`
  2024-05-03 12:41  7%           ` [PATCH v6 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
@ 2024-05-04 15:18  0%             ` Phillip Wood
  0 siblings, 0 replies; 200+ results
From: Phillip Wood @ 2024-05-04 15:18 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: christian.couder, git, gitster, ps

Hi Karthik

On 03/05/2024 13:41, Karthik Nayak wrote:
> From: Karthik Nayak <karthik.188@gmail.com>
> 
> The function `ref_transaction_update()` obtains ref information and
> flags to create a `ref_update` and add them to the transaction at hand.
> 
> To extend symref support in transactions, we need to also accept the
> old and new ref targets and process it. This commit adds the required
> parameters to the function and modifies all call sites.
> 
> The two parameters added are `new_target` and `old_target`. The
> `new_target` is used to denote what the reference should point to when
> the transaction is applied. Some functions allow this parameter to be
> NULL, meaning that the reference is not changed.
> 
> The `old_target` denotes the value the reference must have before the
> update. Some functions allow this parameter to be NULL, meaning that the
> old value of the reference is not checked.
> 
> We also update the internal function `ref_transaction_add_update()`
> similarly to take the two new parameters.

The documentation for the new parameters looks good to me now - thanks 
for updating it. I'm confused about the assertions though as I mentioned 
in my other message [1].

Best Wishes

Phillip

[1] 
https://www.lore.kernel.org/git/7ca8c2c4-a9cc-4bec-b13c-95d7854b664b@gmail.com

> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
>   branch.c                |  2 +-
>   builtin/fast-import.c   |  5 +++--
>   builtin/fetch.c         |  2 +-
>   builtin/receive-pack.c  |  1 +
>   builtin/replace.c       |  2 +-
>   builtin/tag.c           |  1 +
>   builtin/update-ref.c    |  1 +
>   refs.c                  | 22 +++++++++++++++++-----
>   refs.h                  | 18 +++++++++++++++++-
>   refs/files-backend.c    | 12 ++++++------
>   refs/refs-internal.h    | 14 ++++++++++++++
>   refs/reftable-backend.c |  4 ++--
>   sequencer.c             |  9 +++++----
>   walker.c                |  2 +-
>   14 files changed, 71 insertions(+), 24 deletions(-)
> 
> diff --git a/branch.c b/branch.c
> index e4a738fc7b..48af4c3ceb 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -627,7 +627,7 @@ void create_branch(struct repository *r,
>   	if (!transaction ||
>   		ref_transaction_update(transaction, ref.buf,
>   					&oid, forcing ? NULL : null_oid(),
> -					0, msg, &err) ||
> +					NULL, NULL, 0, msg, &err) ||
>   		ref_transaction_commit(transaction, &err))
>   		die("%s", err.buf);
>   	ref_transaction_free(transaction);
> diff --git a/builtin/fast-import.c b/builtin/fast-import.c
> index dc5a9d32dd..297dfb91a1 100644
> --- a/builtin/fast-import.c
> +++ b/builtin/fast-import.c
> @@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
>   	transaction = ref_transaction_begin(&err);
>   	if (!transaction ||
>   	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
> -				   0, msg, &err) ||
> +				   NULL, NULL, 0, msg, &err) ||
>   	    ref_transaction_commit(transaction, &err)) {
>   		ref_transaction_free(transaction);
>   		error("%s", err.buf);
> @@ -1675,7 +1675,8 @@ static void dump_tags(void)
>   		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
>   
>   		if (ref_transaction_update(transaction, ref_name.buf,
> -					   &t->oid, NULL, 0, msg, &err)) {
> +					   &t->oid, NULL, NULL, NULL,
> +					   0, msg, &err)) {
>   			failure |= error("%s", err.buf);
>   			goto cleanup;
>   		}
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index 5857d860db..66840b7c5b 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
>   
>   	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
>   				     check_old ? &ref->old_oid : NULL,
> -				     0, msg, &err);
> +				     NULL, NULL, 0, msg, &err);
>   	if (ret) {
>   		ret = STORE_REF_ERROR_OTHER;
>   		goto out;
> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
> index e8d7df14b6..b150ef39a8 100644
> --- a/builtin/receive-pack.c
> +++ b/builtin/receive-pack.c
> @@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
>   		if (ref_transaction_update(transaction,
>   					   namespaced_name,
>   					   new_oid, old_oid,
> +					   NULL, NULL,
>   					   0, "push",
>   					   &err)) {
>   			rp_error("%s", err.buf);
> diff --git a/builtin/replace.c b/builtin/replace.c
> index da59600ad2..7690687b0e 100644
> --- a/builtin/replace.c
> +++ b/builtin/replace.c
> @@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
>   	transaction = ref_transaction_begin(&err);
>   	if (!transaction ||
>   	    ref_transaction_update(transaction, ref.buf, repl, &prev,
> -				   0, NULL, &err) ||
> +				   NULL, NULL, 0, NULL, &err) ||
>   	    ref_transaction_commit(transaction, &err))
>   		res = error("%s", err.buf);
>   
> diff --git a/builtin/tag.c b/builtin/tag.c
> index 9a33cb50b4..40a65fdebc 100644
> --- a/builtin/tag.c
> +++ b/builtin/tag.c
> @@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
>   	transaction = ref_transaction_begin(&err);
>   	if (!transaction ||
>   	    ref_transaction_update(transaction, ref.buf, &object, &prev,
> +				   NULL, NULL,
>   				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
>   				   reflog_msg.buf, &err) ||
>   	    ref_transaction_commit(transaction, &err)) {
> diff --git a/builtin/update-ref.c b/builtin/update-ref.c
> index e46afbc46d..21fdbf6ac8 100644
> --- a/builtin/update-ref.c
> +++ b/builtin/update-ref.c
> @@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
>   
>   	if (ref_transaction_update(transaction, refname,
>   				   &new_oid, have_old ? &old_oid : NULL,
> +				   NULL, NULL,
>   				   update_flags | create_reflog_flag,
>   				   msg, &err))
>   		die("%s", err.buf);
> diff --git a/refs.c b/refs.c
> index 55d2e0b2cb..47bc9dd103 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
>   		const char *refname, unsigned int flags,
>   		const struct object_id *new_oid,
>   		const struct object_id *old_oid,
> +		const char *new_target, const char *old_target,
>   		const char *msg)
>   {
>   	struct ref_update *update;
> @@ -1235,6 +1236,11 @@ struct ref_update *ref_transaction_add_update(
>   	if (transaction->state != REF_TRANSACTION_OPEN)
>   		BUG("update called for transaction that is not open");
>   
> +	if (old_oid && !is_null_oid(old_oid) && old_target)
> +		BUG("only one of old_oid and old_target should be non NULL");
> +	if (new_oid && !is_null_oid(new_oid) && new_target)
> +		BUG("only one of new_oid and new_target should be non NULL");
> +
>   	FLEX_ALLOC_STR(update, refname, refname);
>   	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
>   	transaction->updates[transaction->nr++] = update;
> @@ -1253,6 +1259,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
>   			   const char *refname,
>   			   const struct object_id *new_oid,
>   			   const struct object_id *old_oid,
> +			   const char *new_target,
> +			   const char *old_target,
>   			   unsigned int flags, const char *msg,
>   			   struct strbuf *err)
>   {
> @@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
>   	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
>   
>   	ref_transaction_add_update(transaction, refname, flags,
> -				   new_oid, old_oid, msg);
> +				   new_oid, old_oid, new_target,
> +				   old_target, msg);
>   	return 0;
>   }
>   
> @@ -1295,7 +1304,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
>   		return 1;
>   	}
>   	return ref_transaction_update(transaction, refname, new_oid,
> -				      null_oid(), flags, msg, err);
> +				      null_oid(), NULL, NULL, flags,
> +				      msg, err);
>   }
>   
>   int ref_transaction_delete(struct ref_transaction *transaction,
> @@ -1308,7 +1318,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
>   		BUG("delete called with old_oid set to zeros");
>   	return ref_transaction_update(transaction, refname,
>   				      null_oid(), old_oid,
> -				      flags, msg, err);
> +				      NULL, NULL, flags,
> +				      msg, err);
>   }
>   
>   int ref_transaction_verify(struct ref_transaction *transaction,
> @@ -1321,6 +1332,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
>   		BUG("verify called with old_oid set to NULL");
>   	return ref_transaction_update(transaction, refname,
>   				      NULL, old_oid,
> +				      NULL, NULL,
>   				      flags, NULL, err);
>   }
>   
> @@ -1335,8 +1347,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
>   
>   	t = ref_store_transaction_begin(refs, &err);
>   	if (!t ||
> -	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
> -				   &err) ||
> +	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
> +				   flags, msg, &err) ||
>   	    ref_transaction_commit(t, &err)) {
>   		ret = 1;
>   		ref_transaction_free(t);
> diff --git a/refs.h b/refs.h
> index d278775e08..c7851bf587 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -648,6 +648,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
>    *         before the update. A copy of this value is made in the
>    *         transaction.
>    *
> + *     new_target -- the target reference that the reference will be
> + *         updated to point to. If the reference is a regular reference,
> + *         it will be converted to a symbolic reference. Cannot be set
> + *         together with `new_oid`. A copy of this value is made in the
> + *         transaction.
> + *
> + *     old_target -- the reference that the reference must be pointing to.
> + *         Canont be set together with `old_oid`. A copy of this value is
> + *         made in the transaction.
> + *
>    *     flags -- flags affecting the update, passed to
>    *         update_ref_lock(). Possible flags: REF_NO_DEREF,
>    *         REF_FORCE_CREATE_REFLOG. See those constants for more
> @@ -713,7 +723,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
>    * beforehand. The old value is checked after the lock is taken to
>    * prevent races. If the old value doesn't agree with old_oid, the
>    * whole transaction fails. If old_oid is NULL, then the previous
> - * value is not checked.
> + * value is not checked. If `old_target` is not NULL, treat the reference
> + * as a symbolic ref and validate that its target before the update is
> + * `old_target`. If the `new_target` is not NULL, then the reference
> + * will be updated to a symbolic ref which targets `new_target`.
> + * Together, these allow us to update between regular refs and symrefs.
>    *
>    * See the above comment "Reference transaction updates" for more
>    * information.
> @@ -722,6 +736,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
>   			   const char *refname,
>   			   const struct object_id *new_oid,
>   			   const struct object_id *old_oid,
> +			   const char *new_target,
> +			   const char *old_target,
>   			   unsigned int flags, const char *msg,
>   			   struct strbuf *err);
>   
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index a098d14ea0..e4d0aa3d41 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -1198,7 +1198,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(), &r->oid, NULL);
> +			null_oid(), &r->oid, NULL, NULL, NULL);
>   	if (ref_transaction_commit(transaction, &err))
>   		goto cleanup;
>   
> @@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
>   		 * packed-refs transaction:
>   		 */
>   		if (ref_transaction_update(transaction, iter->refname,
> -					   iter->oid, NULL,
> +					   iter->oid, NULL, NULL, NULL,
>   					   REF_NO_DEREF, NULL, &err))
>   			die("failure preparing to create packed reference %s: %s",
>   			    iter->refname, err.buf);
> @@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
>   			transaction, "HEAD",
>   			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
>   			&update->new_oid, &update->old_oid,
> -			update->msg);
> +			NULL, NULL, update->msg);
>   
>   	/*
>   	 * Add "HEAD". This insertion is O(N) in the transaction
> @@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
>   	new_update = ref_transaction_add_update(
>   			transaction, referent, new_flags,
>   			&update->new_oid, &update->old_oid,
> -			update->msg);
> +			NULL, NULL, update->msg);
>   
>   	new_update->parent_update = update;
>   
> @@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
>   					packed_transaction, update->refname,
>   					REF_HAVE_NEW | REF_NO_DEREF,
>   					&update->new_oid, NULL,
> -					NULL);
> +					NULL, NULL, NULL);
>   		}
>   	}
>   
> @@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
>   		ref_transaction_add_update(packed_transaction, update->refname,
>   					   update->flags & ~REF_HAVE_OLD,
>   					   &update->new_oid, &update->old_oid,
> -					   NULL);
> +					   NULL, NULL, NULL);
>   	}
>   
>   	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index 56641aa57a..108f4ec419 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -124,6 +124,19 @@ struct ref_update {
>   	 */
>   	struct object_id old_oid;
>   
> +	/*
> +	 * If set, point the reference to this value. This can also be
> +	 * used to convert regular references to become symbolic refs.
> +	 * Cannot be set together with `new_oid`.
> +	 */
> +	const char *new_target;
> +
> +	/*
> +	 * If set, check that the reference previously pointed to this
> +	 * value. Cannot be set together with `old_oid`.
> +	 */
> +	const char *old_target;
> +
>   	/*
>   	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
>   	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
> @@ -173,6 +186,7 @@ struct ref_update *ref_transaction_add_update(
>   		const char *refname, unsigned int flags,
>   		const struct object_id *new_oid,
>   		const struct object_id *old_oid,
> +		const char *new_target, const char *old_target,
>   		const char *msg);
>   
>   /*
> diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
> index 1cda48c504..6104471199 100644
> --- a/refs/reftable-backend.c
> +++ b/refs/reftable-backend.c
> @@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
>   			new_update = ref_transaction_add_update(
>   					transaction, "HEAD",
>   					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
> -					&u->new_oid, &u->old_oid, u->msg);
> +					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
>   			string_list_insert(&affected_refnames, new_update->refname);
>   		}
>   
> @@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
>   				 */
>   				new_update = ref_transaction_add_update(
>   						transaction, referent.buf, new_flags,
> -						&u->new_oid, &u->old_oid, u->msg);
> +						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
>   				new_update->parent_update = u;
>   
>   				/*
> diff --git a/sequencer.c b/sequencer.c
> index 88de4dc20f..61e007d85f 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -665,7 +665,7 @@ static int fast_forward_to(struct repository *r,
>   	if (!transaction ||
>   	    ref_transaction_update(transaction, "HEAD",
>   				   to, unborn && !is_rebase_i(opts) ?
> -				   null_oid() : from,
> +				   null_oid() : from, NULL, NULL,
>   				   0, sb.buf, &err) ||
>   	    ref_transaction_commit(transaction, &err)) {
>   		ref_transaction_free(transaction);
> @@ -1298,7 +1298,7 @@ int update_head_with_reflog(const struct commit *old_head,
>   	if (!transaction ||
>   	    ref_transaction_update(transaction, "HEAD", new_head,
>   				   old_head ? &old_head->object.oid : null_oid(),
> -				   0, sb.buf, err) ||
> +				   NULL, NULL, 0, sb.buf, err) ||
>   	    ref_transaction_commit(transaction, err)) {
>   		ret = -1;
>   	}
> @@ -3832,8 +3832,9 @@ static int do_label(struct repository *r, const char *name, int len)
>   	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
>   		error(_("could not read HEAD"));
>   		ret = -1;
> -	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
> -					  NULL, 0, msg.buf, &err) < 0 ||
> +	} else if (ref_transaction_update(transaction, ref_name.buf,
> +					  &head_oid, NULL, NULL, NULL,
> +					  0, msg.buf, &err) < 0 ||
>   		   ref_transaction_commit(transaction, &err)) {
>   		error("%s", err.buf);
>   		ret = -1;
> diff --git a/walker.c b/walker.c
> index c0fd632d92..1b3df43906 100644
> --- a/walker.c
> +++ b/walker.c
> @@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
>   		strbuf_reset(&refname);
>   		strbuf_addf(&refname, "refs/%s", write_ref[i]);
>   		if (ref_transaction_update(transaction, refname.buf,
> -					   oids + i, NULL, 0,
> +					   oids + i, NULL, NULL, NULL, 0,
>   					   msg ? msg : "fetch (unknown)",
>   					   &err)) {
>   			error("%s", err.buf);


^ permalink raw reply	[relevance 0%]

* [PATCH] Bug fix: ensure P4 "err" is displayed when exception is raised.
@ 2024-05-04  5:57  3% Fahad Alrashed via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Fahad Alrashed via GitGitGadget @ 2024-05-04  5:57 UTC (permalink / raw)
  To: git; +Cc: Fahad Alrashed, Fahad Alrashed

From: Fahad Alrashed <fahad@keylock.net>

Bug fix: During "git p4 clone" if p4 process returns
an error from the server, it will store it in variable
"err". The it will send a text command "die-now" to
git-fast-import. However, git-fast-import raises an
exception: "fatal: Unsupported command: die-now"
and err is never displayed. This patch ensures that
err is dispayed using "finally:"

Signed-off-by: Fahad Alrashed <fahad@keylock.net>
---
    In git p4, git fast-import fails from die-now command and err (from
    Perforce) is not shown
    
    When importing from Perforce using git p4 clone <depot location>,
    cloning works fine until Perforce command p4 raises an error. This error
    message is stored in err variable then git-fast-import is sent a die-now
    command to kill it. An exception is raised fatal: Unsupported command:
    die-now.
    
    This patch forces python to call die() with the err message returned
    from Perforce.
    
    This commit fixes the root cause of a bug that took me hours to find.
    I'm sure many faced the cryptic error and declared that git-p4 is not
    working for them.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1668%2Falrashedf%2Fmaster-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1668/alrashedf/master-v1
Pull-Request: https://github.com/git/git/pull/1668

 git-p4.py | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/git-p4.py b/git-p4.py
index 28ab12c72b6..f1ab31d5403 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -3253,17 +3253,19 @@ def streamP4FilesCb(self, marshalled):
             if self.stream_have_file_info:
                 if "depotFile" in self.stream_file:
                     f = self.stream_file["depotFile"]
-            # force a failure in fast-import, else an empty
-            # commit will be made
-            self.gitStream.write("\n")
-            self.gitStream.write("die-now\n")
-            self.gitStream.close()
-            # ignore errors, but make sure it exits first
-            self.importProcess.wait()
-            if f:
-                die("Error from p4 print for %s: %s" % (f, err))
-            else:
-                die("Error from p4 print: %s" % err)
+            try:
+                # force a failure in fast-import, else an empty
+                # commit will be made
+                self.gitStream.write("\n")
+                self.gitStream.write("die-now\n")
+                self.gitStream.close()
+                # ignore errors, but make sure it exits first
+                self.importProcess.wait()
+            finally:
+                if f:
+                    die("Error from p4 print for %s: %s" % (f, err))
+                else:
+                    die("Error from p4 print: %s" % err)
 
         if 'depotFile' in marshalled and self.stream_have_file_info:
             # start of a new file - output the old one first

base-commit: 235986be822c9f8689be2e9a0b7804d0b1b6d821
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* [PATCH v3] refs: return conflict error when checking packed refs
  2024-05-03  4:50 19% ` [PATCH v2] " Ivan Tse via GitGitGadget
  2024-05-03  6:38  3%   ` Patrick Steinhardt
@ 2024-05-04  3:04 19%   ` Ivan Tse via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Ivan Tse via GitGitGadget @ 2024-05-04  3:04 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Ivan Tse, Ivan Tse

From: Ivan Tse <ivan.tse1@gmail.com>

The TRANSACTION_NAME_CONFLICT error code refers to a failure to create a
ref due to a name conflict with another ref. An example of this is a
directory/file conflict such as ref names A/B and A.

"git fetch" uses this error code to more accurately describe the error
by recommending to the user that they try running "git remote prune" to
remove any old refs that are deleted by the remote which would clear up
any directory/file conflicts.

This helpful error message is not displayed when the conflicted ref is
stored in packed refs. This change fixes this by ensuring error return
code consistency in `lock_raw_ref`.

Signed-off-by: Ivan Tse <ivan.tse1@gmail.com>
---
    refs: return conflict error when checking packed refs
    
    Changes against v2:
    
     * move test_grep to follow after their respective git fetch commands
     * Use "${SQ}" instead of '\''
    
    Thanks for the explanation and for the comments. I've updated the
    changes to incorporate your feedback!

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1716%2Fivantsepp%2Freturn_name_conflict_error_for_packed_refs-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1716/ivantsepp/return_name_conflict_error_for_packed_refs-v3
Pull-Request: https://github.com/git/git/pull/1716

Range-diff vs v2:

 1:  58b2cda5c18 ! 1:  943a8629b5f refs: return conflict error when checking packed refs
     @@ t/t5510-fetch.sh: test_expect_success 'branchname D/F conflict resolved by --pru
      +	(
      +		cd df-conflict-error &&
      +		git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
     -+		test_must_fail git fetch 2>../err &&
     ++		test_must_fail git fetch 2>err &&
     ++		test_grep "error: some local refs could not be updated; try running" err &&
     ++		test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err &&
      +		git pack-refs --all &&
     -+		test_must_fail git fetch 2>../err-packed
     -+	) &&
     -+	test_grep "error: some local refs could not be updated; try running" err &&
     -+	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err &&
     -+	test_grep "error: some local refs could not be updated; try running" err-packed &&
     -+	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err-packed
     ++		test_must_fail git fetch 2>err-packed &&
     ++		test_grep "error: some local refs could not be updated; try running" err-packed &&
     ++		test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err-packed
     ++	)
      +'
      +
       test_expect_success 'fetching a one-level ref works' '


 refs/files-backend.c |  4 +++-
 t/t5510-fetch.sh     | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea00..97473f377d1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -794,8 +794,10 @@ static int lock_raw_ref(struct files_ref_store *refs,
 		 */
 		if (refs_verify_refname_available(
 				    refs->packed_ref_store, refname,
-				    extras, NULL, err))
+				    extras, NULL, err)) {
+			ret = TRANSACTION_NAME_CONFLICT;
 			goto error_return;
+		}
 	}
 
 	ret = 0;
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 33d34d5ae9e..530369266fd 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1091,6 +1091,22 @@ test_expect_success 'branchname D/F conflict resolved by --prune' '
 	test_cmp expect actual
 '
 
+test_expect_success 'branchname D/F conflict rejected with targeted error message' '
+	git clone . df-conflict-error &&
+	git branch dir_conflict &&
+	(
+		cd df-conflict-error &&
+		git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
+		test_must_fail git fetch 2>err &&
+		test_grep "error: some local refs could not be updated; try running" err &&
+		test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err &&
+		git pack-refs --all &&
+		test_must_fail git fetch 2>err-packed &&
+		test_grep "error: some local refs could not be updated; try running" err-packed &&
+		test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err-packed
+	)
+'
+
 test_expect_success 'fetching a one-level ref works' '
 	test_commit extra &&
 	git reset --hard HEAD^ &&

base-commit: d4cc1ec35f3bcce816b69986ca41943f6ce21377
-- 
gitgitgadget


^ permalink raw reply related	[relevance 19%]

* Re: [PATCH 4/5] cocci: apply rules to rewrite callers of "refs" interfaces
  2024-05-03  6:28  6% ` [PATCH 4/5] cocci: apply rules to rewrite callers of "refs" interfaces Patrick Steinhardt
@ 2024-05-03 18:48  0%   ` Taylor Blau
  0 siblings, 0 replies; 200+ results
From: Taylor Blau @ 2024-05-03 18:48 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Fri, May 03, 2024 at 08:28:14AM +0200, Patrick Steinhardt wrote:
> Apply the rules that rewrite callers of "refs" interfaces to explicitly
> pass `struct ref_store`. The resulting patch has been applied with the
> `--whitespace=fix` option.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  add-interactive.c           | 17 +++++++----
>  bisect.c                    | 25 ++++++++++-----
>  blame.c                     |  4 +--
>  branch.c                    |  5 +--
>  builtin/am.c                | 38 ++++++++++++++---------
>  builtin/bisect.c            | 44 +++++++++++++++-----------
>  builtin/blame.c             |  4 +--
>  builtin/branch.c            | 49 ++++++++++++++++-------------
>  builtin/checkout.c          | 35 +++++++++++++--------
>  builtin/clone.c             | 36 +++++++++++++---------
>  builtin/describe.c          |  3 +-
>  builtin/fast-import.c       | 11 ++++---
>  builtin/fetch.c             | 20 ++++++++----
>  builtin/fsck.c              | 11 +++++--
>  builtin/gc.c                |  3 +-
>  builtin/log.c               |  6 ++--
>  builtin/merge.c             | 34 +++++++++++++--------
>  builtin/name-rev.c          |  5 +--
>  builtin/notes.c             | 26 +++++++++-------
>  builtin/pack-objects.c      | 10 ++++--
>  builtin/pull.c              |  2 +-
>  builtin/rebase.c            | 18 ++++++-----
>  builtin/receive-pack.c      | 15 ++++++---
>  builtin/reflog.c            | 25 ++++++++-------
>  builtin/remote.c            | 37 +++++++++++++---------
>  builtin/repack.c            |  7 +++--
>  builtin/replace.c           |  9 +++---
>  builtin/reset.c             | 13 +++++---
>  builtin/rev-parse.c         | 25 ++++++++++-----
>  builtin/show-branch.c       | 22 ++++++++-----
>  builtin/show-ref.c          | 19 ++++++++----
>  builtin/stash.c             | 23 ++++++++------
>  builtin/submodule--helper.c |  7 +++--
>  builtin/symbolic-ref.c      | 13 +++++---
>  builtin/tag.c               | 11 ++++---
>  builtin/update-index.c      |  2 +-
>  builtin/update-ref.c        | 21 ++++++++-----
>  builtin/worktree.c          | 19 +++++++-----
>  bundle-uri.c                | 12 +++++---
>  bundle.c                    |  2 +-
>  commit-graph.c              |  3 +-
>  commit.c                    |  3 +-
>  config.c                    |  3 +-
>  delta-islands.c             |  3 +-
>  fetch-pack.c                |  6 ++--
>  fmt-merge-msg.c             |  4 ++-
>  help.c                      |  5 +--
>  http-backend.c              | 13 +++++---
>  log-tree.c                  |  9 ++++--
>  ls-refs.c                   | 10 +++---
>  midx-write.c                |  3 +-
>  negotiator/default.c        |  3 +-
>  negotiator/skipping.c       |  3 +-
>  notes-cache.c               |  6 ++--
>  notes-merge.c               |  2 +-
>  notes-utils.c               |  7 +++--
>  notes.c                     |  5 +--
>  reachable.c                 |  5 +--
>  ref-filter.c                | 35 +++++++++++++++------
>  reflog-walk.c               | 27 +++++++++++-----
>  reflog.c                    | 20 +++++++-----
>  refs.c                      |  9 ++++--
>  remote.c                    | 38 ++++++++++++++---------
>  reset.c                     | 29 ++++++++++--------
>  revision.c                  | 27 ++++++++++------
>  sequencer.c                 | 61 ++++++++++++++++++++-----------------
>  server-info.c               |  3 +-
>  setup.c                     |  2 +-
>  shallow.c                   | 16 ++++++----
>  submodule.c                 |  6 ++--
>  transport-helper.c          | 29 ++++++++++--------
>  transport.c                 | 16 ++++++----
>  upload-pack.c               | 20 +++++++-----
>  walker.c                    |  6 ++--
>  wt-status.c                 | 22 +++++++------
>  75 files changed, 711 insertions(+), 436 deletions(-)
>
> diff --git a/add-interactive.c b/add-interactive.c
> index e17602b5e4..b5d6cd689a 100644
> --- a/add-interactive.c
> +++ b/add-interactive.c
> @@ -532,8 +532,9 @@ static int get_modified_files(struct repository *r,
>  			      size_t *binary_count)
>  {
>  	struct object_id head_oid;
> -	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
> -					     &head_oid, NULL);
> +	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
> +						  "HEAD", RESOLVE_REF_READING,
> +						  &head_oid, NULL);
>  	struct collection_status s = { 0 };
>  	int i;

OK, so this is the patch that applies the Coccinelle script from the
previous step.

This all makes sense, but I wonder if we should be more careful than
blindly replacing these functions with the same set of arguments
following a new

    get_main_ref_store(the_repository)

For instance, in this hunk, we have a 'struct repository *' in scope via
the 'r' parameter in add-interactive.c::get_modified_files().

I don't think it's wrong per-se to use the_repository here, but it does
create something to clean up in the future.

I haven't looked at other spots throughout this patch, just noticed
this in the first hunk and thought that I'd say something about it.
Otherwise the transformation seems obviously correct.

Thanks,
Taylor


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v4 2/3] doc: add spacing around paginate options
  2024-05-03 14:32  0%         ` Karthik Nayak
@ 2024-05-03 17:36  0%           ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-05-03 17:36 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: James Liu, git

Karthik Nayak <karthik.188@gmail.com> writes:

> James Liu <james@jamesliu.io> writes:
>
>> Make the documentation page consistent with the usage string printed by
>> git.c
>>
>
> Nit: missing full-stop here.

Yes, but "printed by git.c" is an unsatisfying way to say this.  We
can easily illustrate what the end user does to get it, for example:

    ... usage string given by "git help git".

It also makes it internally consistent with "[-v | --version]" that
exists in the same document, which is even better reason to do this
change.

Thanks, both.

>
>> Signed-off-by: James Liu <james@jamesliu.io>
>> ---
>>  Documentation/git.txt | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/Documentation/git.txt b/Documentation/git.txt
>> index 7fa75350b2..d11d3d0c86 100644
>> --- a/Documentation/git.txt
>> +++ b/Documentation/git.txt
>> @@ -11,7 +11,7 @@ SYNOPSIS
>>  [verse]
>>  'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
>>      [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
>> -    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--no-lazy-fetch]
>> +    [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
>>      [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
>>      [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
>>
>> --
>> 2.44.0


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts
  2024-05-03  7:17  6%       ` [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts James Liu
@ 2024-05-03 17:30  0%         ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-05-03 17:30 UTC (permalink / raw)
  To: James Liu; +Cc: git

James Liu <james@jamesliu.io> writes:

> We'll be adding another option to the --no-* class of options soon.
>
> Clean up the existing options by grouping them together in the OPTIONS
> section, and adding missing ones to the SYNOPSIS.

Nice.  

> diff --git a/Documentation/git.txt b/Documentation/git.txt
> index 7a1b112a3e..7fa75350b2 100644
> --- a/Documentation/git.txt
> +++ b/Documentation/git.txt
> @@ -11,9 +11,9 @@ SYNOPSIS
>  [verse]
>  'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
>      [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
> -    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
> -    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
> -    [--config-env=<name>=<envvar>] <command> [<args>]
> +    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--no-lazy-fetch]
> +    [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
> +    [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]

Looks sensible.

There still are a few options (like noglob-pathspecs) missing, but
cleaning them up from this part of the documentation is totally
outside the scope of this topic (#leftoverbits -- we either make
this exhaustive, or make it clear that this is not exhaustive).

Thanks.


^ permalink raw reply	[relevance 0%]

* Re* using tree as attribute source is slow, was Re: Help troubleshoot performance regression cloning with depth: git 2.44 vs git 2.42
  @ 2024-05-03 15:34  2%                 ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-05-03 15:34 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, Taylor Blau, Jeff King, John Cai,
	Dhruva Krishnamurthy

Dhruva Krishnamurthy <dhruvakm@gmail.com> writes:

> On Thu, May 2, 2024 at 2:08 PM Junio C Hamano <gitster@pobox.com> wrote:
>> We could drop [1/2] from the series in the meantime to make it a
>> GitLab installation specific issue where they explicitly use
>> attr.tree to point at HEAD ;-) That is not solving anything for
>> those who set attr.tree (in a sense, they are buying the feature
>> with overhead of reading attributes from the named tree), but at
>> least for most people who are used to seeing the bare repository
>> ignoring the attributes, it would be an improvement to drop the
>> "bare repositories the tree of the HEAD commit is used to look up
>> attributes files by default" half from the series.
>>
>
> A hack (without knowing side effects if any) is to use an empty tree
> for attr source:
> $ git config --add attr.tree $(git hash-object -t tree /dev/null)
>
> This gives me performance comparable to git 2.42

That is clever.  Instead of crawling a potentially large tree that
is at the HEAD of the main project payload to find ".gitattributes"
files that may be relevant (and often not), folks can set an empty
tree to attr.tree to the configuration until this gets corrected.

And for folks who had been happy with the pre 2.42 behaviour,
we could do something like the attached as the first step to a real fix.

----- >8 --------- >8 --------- >8 --------- >8 -----
Subject: [PATCH] stop using HEAD for attributes in bare repository by default

With 23865355 (attr: read attributes from HEAD when bare repo,
2023-10-13), we started to use the HEAD tree as the default
attribute source in a bare repository.  One argument for such a
behaviour is that it would make things like "git archive" run in
bare and non-bare repositories for the same commit consistent.
This changes was merged to Git 2.43 but without an explicit mention
in its release notes.

It turns out that this change destroys performance of shallowly
cloning from a bare repository.  As the "server" installations are
expected to be mostly bare, and "git pack-objects", which is the
core of driving the other side of "git clone" and "git fetch" wants
to see if a path is set not to delta with blobs from other paths via
the attribute system, the change forces the server side to traverse
the tree of the HEAD commit needlessly to find if each and every
paths the objects it sends out has the attribute that controls the
deltification.  Given that (1) most projects do not configure such
an attribute, and (2) it is dubious for the server side to honor
such an end-user supplied attribute anyway, this was a poor choice
of the default.

To mitigate the current situation, let's revert the change that uses
the tree of HEAD in a bare repository by default as the attribute
source.  This will help most people who have been happy with the
behaviour of Git 2.42 and before.

Two things to note:

 * If you are stuck with versions of Git 2.43 or newer, that is
   older than the release this fix appears in, you can explicitly
   set the attr.tree configuration variable to point at an empty
   tree object, i.e.

	$ git config attr.tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904

 * If you like the behaviour we are reverting, you can explicitly
   set the attr.tree configuration variable to HEAD, i.e.

	$ git config attr.tree HEAD

The right fix for this is to optimize the code paths that allow
accesses to attributes in tree objects, but that is a much more
involved change and is left as a longer-term project, outside the
scope of this "first step" fix.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 attr.c                  |  7 -------
 t/t0003-attributes.sh   | 10 ++++++++--
 t/t5001-archive-attr.sh |  3 ++-
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git c/attr.c w/attr.c
index 679e42258c..6af7151088 100644
--- c/attr.c
+++ w/attr.c
@@ -1223,13 +1223,6 @@ static void compute_default_attr_source(struct object_id *attr_source)
 		ignore_bad_attr_tree = 1;
 	}
 
-	if (!default_attr_source_tree_object_name &&
-	    startup_info->have_repository &&
-	    is_bare_repository()) {
-		default_attr_source_tree_object_name = "HEAD";
-		ignore_bad_attr_tree = 1;
-	}
-
 	if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
 		return;
 
diff --git c/t/t0003-attributes.sh w/t/t0003-attributes.sh
index 774b52c298..d755cc3c29 100755
--- c/t/t0003-attributes.sh
+++ w/t/t0003-attributes.sh
@@ -398,13 +398,19 @@ test_expect_success 'bad attr source defaults to reading .gitattributes file' '
 	)
 '
 
-test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
+test_expect_success 'bare repo no longer defaults to reading .gitattributes from HEAD' '
 	test_when_finished rm -rf test bare_with_gitattribute &&
 	git init test &&
 	test_commit -C test gitattributes .gitattributes "f/path test=val" &&
 	git clone --bare test bare_with_gitattribute &&
-	echo "f/path: test: val" >expect &&
+
+	echo "f/path: test: unspecified" >expect &&
 	git -C bare_with_gitattribute check-attr test -- f/path >actual &&
+	test_cmp expect actual &&
+
+	echo "f/path: test: val" >expect &&
+	git -C bare_with_gitattribute -c attr.tree=HEAD \
+		check-attr test -- f/path >actual &&
 	test_cmp expect actual
 '
 
diff --git c/t/t5001-archive-attr.sh w/t/t5001-archive-attr.sh
index eaf959d8f6..7310774af5 100755
--- c/t/t5001-archive-attr.sh
+++ w/t/t5001-archive-attr.sh
@@ -133,7 +133,8 @@ test_expect_success 'git archive vs. bare' '
 '
 
 test_expect_success 'git archive with worktree attributes, bare' '
-	(cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
+	(cd bare &&
+	git -c attr.tree=HEAD archive --worktree-attributes HEAD) >bare-worktree.tar &&
 	(mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
 '
 


^ permalink raw reply related	[relevance 2%]

* Re: [PATCH v4 2/3] doc: add spacing around paginate options
  2024-05-03  7:17 12%       ` [PATCH v4 2/3] doc: add spacing around paginate options James Liu
@ 2024-05-03 14:32  0%         ` Karthik Nayak
  2024-05-03 17:36  0%           ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Karthik Nayak @ 2024-05-03 14:32 UTC (permalink / raw)
  To: James Liu, git

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

James Liu <james@jamesliu.io> writes:

> Make the documentation page consistent with the usage string printed by
> git.c
>

Nit: missing full-stop here.

> Signed-off-by: James Liu <james@jamesliu.io>
> ---
>  Documentation/git.txt | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/git.txt b/Documentation/git.txt
> index 7fa75350b2..d11d3d0c86 100644
> --- a/Documentation/git.txt
> +++ b/Documentation/git.txt
> @@ -11,7 +11,7 @@ SYNOPSIS
>  [verse]
>  'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
>      [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
> -    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--no-lazy-fetch]
> +    [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
>      [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
>      [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
>
> --
> 2.44.0

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

^ permalink raw reply	[relevance 0%]

* [PATCH v6 1/7] refs: accept symref values in `ref_transaction_update()`
  2024-05-03 12:41  1%         ` [PATCH v6 0/7] refs: add support for transactional symref updates Karthik Nayak
@ 2024-05-03 12:41  7%           ` Karthik Nayak
  2024-05-04 15:18  0%             ` Phillip Wood
  0 siblings, 1 reply; 200+ results
From: Karthik Nayak @ 2024-05-03 12:41 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The function `ref_transaction_update()` obtains ref information and
flags to create a `ref_update` and add them to the transaction at hand.

To extend symref support in transactions, we need to also accept the
old and new ref targets and process it. This commit adds the required
parameters to the function and modifies all call sites.

The two parameters added are `new_target` and `old_target`. The
`new_target` is used to denote what the reference should point to when
the transaction is applied. Some functions allow this parameter to be
NULL, meaning that the reference is not changed.

The `old_target` denotes the value the reference must have before the
update. Some functions allow this parameter to be NULL, meaning that the
old value of the reference is not checked.

We also update the internal function `ref_transaction_add_update()`
similarly to take the two new parameters.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  5 +++--
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  1 +
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  1 +
 builtin/update-ref.c    |  1 +
 refs.c                  | 22 +++++++++++++++++-----
 refs.h                  | 18 +++++++++++++++++-
 refs/files-backend.c    | 12 ++++++------
 refs/refs-internal.h    | 14 ++++++++++++++
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  9 +++++----
 walker.c                |  2 +-
 14 files changed, 71 insertions(+), 24 deletions(-)

diff --git a/branch.c b/branch.c
index e4a738fc7b..48af4c3ceb 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..297dfb91a1 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1675,7 +1675,8 @@ static void dump_tags(void)
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..66840b7c5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..b150ef39a8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..7690687b0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..40a65fdebc 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..21fdbf6ac8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..47bc9dd103 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg)
 {
 	struct ref_update *update;
@@ -1235,6 +1236,11 @@ struct ref_update *ref_transaction_add_update(
 	if (transaction->state != REF_TRANSACTION_OPEN)
 		BUG("update called for transaction that is not open");
 
+	if (old_oid && !is_null_oid(old_oid) && old_target)
+		BUG("only one of old_oid and old_target should be non NULL");
+	if (new_oid && !is_null_oid(new_oid) && new_target)
+		BUG("only one of new_oid and new_target should be non NULL");
+
 	FLEX_ALLOC_STR(update, refname, refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
@@ -1253,6 +1259,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err)
 {
@@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, new_target,
+				   old_target, msg);
 	return 0;
 }
 
@@ -1295,7 +1304,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1318,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1332,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
 				      NULL, old_oid,
+				      NULL, NULL,
 				      flags, NULL, err);
 }
 
@@ -1335,8 +1347,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
-	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+				   flags, msg, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index d278775e08..c7851bf587 100644
--- a/refs.h
+++ b/refs.h
@@ -648,6 +648,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  *         before the update. A copy of this value is made in the
  *         transaction.
  *
+ *     new_target -- the target reference that the reference will be
+ *         updated to point to. If the reference is a regular reference,
+ *         it will be converted to a symbolic reference. Cannot be set
+ *         together with `new_oid`. A copy of this value is made in the
+ *         transaction.
+ *
+ *     old_target -- the reference that the reference must be pointing to.
+ *         Canont be set together with `old_oid`. A copy of this value is
+ *         made in the transaction.
+ *
  *     flags -- flags affecting the update, passed to
  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
  *         REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -713,7 +723,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  * beforehand. The old value is checked after the lock is taken to
  * prevent races. If the old value doesn't agree with old_oid, the
  * whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
  *
  * See the above comment "Reference transaction updates" for more
  * information.
@@ -722,6 +736,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea0..e4d0aa3d41 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
+					   iter->oid, NULL, NULL, NULL,
 					   REF_NO_DEREF, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
@@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL, NULL);
 		}
 	}
 
@@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..108f4ec419 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,19 @@ struct ref_update {
 	 */
 	struct object_id old_oid;
 
+	/*
+	 * If set, point the reference to this value. This can also be
+	 * used to convert regular references to become symbolic refs.
+	 * Cannot be set together with `new_oid`.
+	 */
+	const char *new_target;
+
+	/*
+	 * If set, check that the reference previously pointed to this
+	 * value. Cannot be set together with `old_oid`.
+	 */
+	const char *old_target;
+
 	/*
 	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
 	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
@@ -173,6 +186,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg);
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c504..6104471199 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 88de4dc20f..61e007d85f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -665,7 +665,7 @@ static int fast_forward_to(struct repository *r,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
-				   null_oid() : from,
+				   null_oid() : from, NULL, NULL,
 				   0, sb.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
@@ -1298,7 +1298,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   NULL, NULL, 0, sb.buf, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3832,8 +3832,9 @@ static int do_label(struct repository *r, const char *name, int len)
 	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
-	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+	} else if (ref_transaction_update(transaction, ref_name.buf,
+					  &head_oid, NULL, NULL, NULL,
+					  0, msg.buf, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index c0fd632d92..1b3df43906 100644
--- a/walker.c
+++ b/walker.c
@@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		strbuf_reset(&refname);
 		strbuf_addf(&refname, "refs/%s", write_ref[i]);
 		if (ref_transaction_update(transaction, refname.buf,
-					   oids + i, NULL, 0,
+					   oids + i, NULL, NULL, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
 					   &err)) {
 			error("%s", err.buf);
-- 
2.43.GIT



^ permalink raw reply related	[relevance 7%]

* [PATCH v6 0/7] refs: add support for transactional symref updates
  2024-05-01 20:22  1%       ` [PATCH v5 0/7] refs: add support for transactional symref updates Karthik Nayak
  2024-05-01 20:22  7%         ` [PATCH v5 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
@ 2024-05-03 12:41  1%         ` Karthik Nayak
  2024-05-03 12:41  7%           ` [PATCH v6 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
  1 sibling, 1 reply; 200+ results
From: Karthik Nayak @ 2024-05-03 12:41 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The patch series takes over from the existing patch series, wherein we
introduced symref-* commands to git-update-ref. Since there was a ton of
discussions on the UX of the patch series and its application, I thought it
would be best to shorten the series and split it into multiple smaller series.

This series adds transactional support for symrefs in the reference db. Then
we switch refs_create_symref() to start using transactions for symref updates.
This allows us to deprecate the create_symref code in the ref_storage_be
interface and remove all associated code which is no longer used.

The split was primarily done so we can merge the non-user facing parts of the
previous series. While pertaining the user facing components into another set
of patches wherein deeper discussion on the UX can be held without worrying
about the internal implementation. Also by using this new functionality in a
pre-existing command, we can leverage the existing tests to catch any
inconsistencies. One of which was how 'git-symbolic-ref' doesn't add reflog for
dangling symrefs, which I've modified my patch to do the same.

We also modify the reference transaction hook to support symrefs. For any symref
update the reference transaction hook will output the value with a 'ref:' prefix.

Previous versions:
V1: https://lore.kernel.org/git/20240330224623.579457-1-knayak@gitlab.com/
V2: https://lore.kernel.org/git/20240412095908.1134387-1-knayak@gitlab.com/
V3: https://lore.kernel.org/git/20240423212818.574123-1-knayak@gitlab.com/
V4: https://lore.kernel.org/r/20240426152449.228860-1-knayak@gitlab.com
V5: https://lore.kernel.org/r/20240501202229.2695774-1-knayak@gitlab.com

Changes over v5 are:
- More user friendly error messages.
- `create_symref_lock` now writes to an err buf, instead of directly to stderr.
- Refactor code to make it easier to read around logical operations.
- Cleanup commit message and fix typos.

Thanks to all reviewers!

Range diff:

1:  a354190905 = 1:  a354190905 refs: accept symref values in `ref_transaction_update()`
2:  7dff21dbef ! 2:  0d9c5b9804 files-backend: extract out `create_symref_lock()`
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     -				const char *target, const char *logmsg)
     +static int create_symref_lock(struct files_ref_store *refs,
     +			      struct ref_lock *lock, const char *refname,
    -+			      const char *target)
    ++			      const char *target, struct strbuf *err)
      {
    -+	if (!fdopen_lock_file(&lock->lk, "w"))
    -+		return error("unable to fdopen %s: %s",
    ++	if (!fdopen_lock_file(&lock->lk, "w")) {
    ++		strbuf_addf(err, "unable to fdopen %s: %s",
     +			     get_lock_file_path(&lock->lk), strerror(errno));
    ++		return -1;
    ++	}
     +
    -+	if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0)
    -+		return error("unable to fprintf %s: %s",
    ++	if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0) {
    ++		strbuf_addf(err, "unable to write to %s: %s",
     +			     get_lock_file_path(&lock->lk), strerror(errno));
    ++		return -1;
    ++	}
    ++
     +	return 0;
     +}
     +
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     +				    struct ref_lock *lock, const char *refname,
     +				    const char *target, const char *logmsg)
     +{
    ++	struct strbuf err = STRBUF_INIT;
     +	int ret;
     +
      	if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     -	if (!fdopen_lock_file(&lock->lk, "w"))
     -		return error("unable to fdopen %s: %s",
     -			     get_lock_file_path(&lock->lk), strerror(errno));
    -+	ret = create_symref_lock(refs, lock, refname, target);
    ++	ret = create_symref_lock(refs, lock, refname, target, &err);
     +	if (!ret) {
     +		update_symref_reflog(refs, lock, refname, target, logmsg);
      
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     +		if (commit_ref(lock) < 0)
     +			return error("unable to write symref for %s: %s", refname,
     +				     strerror(errno));
    ++	} else {
    ++		return error("%s", err.buf);
     +	}
      
     -	/* no error check; commit_ref will check ferror */
3:  901a586683 ! 3:  e0219ffd31 refs: support symrefs in 'reference-transaction' hook
    @@ refs.c: static int run_transaction_hook(struct ref_transaction *transaction,
     -			    oid_to_hex(&update->new_oid),
     -			    update->refname);
     +
    -+		if (update->flags & REF_HAVE_OLD && update->old_target)
    ++		if (!(update->flags & REF_HAVE_OLD))
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
    ++		else if (update->old_target)
     +			strbuf_addf(&buf, "ref:%s ", update->old_target);
     +		else
     +			strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
     +
    -+		if (update->flags & REF_HAVE_NEW && update->new_target)
    ++		if (!(update->flags & REF_HAVE_NEW))
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
    ++		else if (update->new_target)
     +			strbuf_addf(&buf, "ref:%s ", update->new_target);
     +		else
     +			strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
4:  6c97f6a660 ! 4:  b22c59c722 refs: add support for transactional symref updates
    @@ Commit message
         However, we never supported transactional updates of symrefs. Let's add
         support for symrefs in both the 'files' and the 'reftable' backend.
     
    -    Here, we add and use `ref_update_is_null_new_value()`, a helper function
    -    which is used to check if there is a new_value in a reference update.
    -    The new value could either be a symref target `new_target` or a OID
    -    `new_oid`.
    +    Here, we add and use `ref_update_has_null_new_value()`, a helper
    +    function which is used to check if there is a new_value in a reference
    +    update. The new value could either be a symref target `new_target` or a
    +    OID `new_oid`.
     
         With this, now transactional updates (verify, create, delete, update)
         can be used for:
    @@ Commit message
         This also allows us to expose this to users via new commands in
         'git-update-ref' in the future.
     
    -    We do not add reflog for dangling symref updates, because currently
    -    'git-symbolic-ref' doesn't add reflog for dangling symref updates and it
    -    would be best to keep this behavior consistent as we would move it to
    -    start using transaction based updates in the following commit.
    +    Note that a dangling symref update does not record a new reflog entry,
    +    which is unchanged before and after this commit.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs.c: struct ref_update *ref_transaction_add_update(
      	update->flags = flags;
      
     -	if (flags & REF_HAVE_NEW)
    -+	if (new_target)
    -+		update->new_target = xstrdup(new_target);
    -+	if (old_target)
    -+		update->old_target = xstrdup(old_target);
    -+	if (new_oid && flags & REF_HAVE_NEW)
    ++	update->new_target = xstrdup_or_null(new_target);
    ++	update->old_target = xstrdup_or_null(old_target);
    ++	if ((flags & REF_HAVE_NEW) && new_oid)
      		oidcpy(&update->new_oid, new_oid);
     -	if (flags & REF_HAVE_OLD)
    -+	if (old_oid && flags & REF_HAVE_OLD)
    ++	if ((flags & REF_HAVE_OLD) && old_oid)
      		oidcpy(&update->old_oid, old_oid);
     +
      	update->msg = normalize_reflog_message(msg);
    @@ refs.c: int copy_existing_ref(const char *oldref, const char *newref, const char
      	return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
      }
     +
    -+int ref_update_is_null_new_value(struct ref_update *update)
    ++int ref_update_has_null_new_value(struct ref_update *update)
     +{
     +	return !update->new_target && is_null_oid(&update->new_oid);
     +}
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
      	files_assert_main_repository(refs, "lock_ref_for_update");
      
     -	if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
    -+	if ((update->flags & REF_HAVE_NEW) && ref_update_is_null_new_value(update))
    ++	if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
      		update->flags |= REF_DELETING;
      
      	if (head_ref) {
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
     -	    !(update->flags & REF_DELETING) &&
     -	    !(update->flags & REF_LOG_ONLY)) {
     +	if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
    -+		if (create_symref_lock(refs, lock, update->refname, update->new_target)) {
    ++		if (create_symref_lock(refs, lock, update->refname, update->new_target, err)) {
     +			ret = TRANSACTION_GENERIC_ERROR;
     +			goto out;
     +		}
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
      		if (!(update->type & REF_ISSYMREF) &&
      		    oideq(&lock->old_oid, &update->new_oid)) {
      			/*
    +@@ refs/files-backend.c: static int files_transaction_prepare(struct ref_store *ref_store,
    + 	return ret;
    + }
    + 
    ++static int parse_and_write_reflog(struct files_ref_store *refs,
    ++				  struct ref_update *update,
    ++				  struct ref_lock *lock,
    ++				  struct strbuf *err)
    ++{
    ++	if (update->new_target) {
    ++		/*
    ++		 * We want to get the resolved OID for the target, to ensure
    ++		 * that the correct value is added to the reflog.
    ++		 */
    ++		if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
    ++					     RESOLVE_REF_READING,
    ++					     &update->new_oid, NULL)) {
    ++			/*
    ++			 * TODO: currently we skip creating reflogs for dangling
    ++			 * symref updates. It would be nice to capture this as
    ++			 * zero oid updates however.
    ++			 */
    ++			return 0;
    ++		}
    ++	}
    ++
    ++	if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
    ++				&update->new_oid, update->msg, update->flags, err)) {
    ++		char *old_msg = strbuf_detach(err, NULL);
    ++
    ++		strbuf_addf(err, "cannot update the ref '%s': %s",
    ++			    lock->ref_name, old_msg);
    ++		free(old_msg);
    ++		unlock_ref(lock);
    ++		update->backend_data = NULL;
    ++		return -1;
    ++	}
    ++
    ++	return 0;
    ++}
    ++
    + static int files_transaction_finish(struct ref_store *ref_store,
    + 				    struct ref_transaction *transaction,
    + 				    struct strbuf *err)
     @@ refs/files-backend.c: static int files_transaction_finish(struct ref_store *ref_store,
      
      		if (update->flags & REF_NEEDS_COMMIT ||
    @@ refs/files-backend.c: static int files_transaction_finish(struct ref_store *ref_
     -						&update->new_oid,
     -						update->msg, update->flags,
     -						err)) {
    -+			int create_reflog = 1;
    -+
    -+			if (update->new_target) {
    -+				/*
    -+				 * We want to get the resolved OID for the target, to ensure
    -+				 * that the correct value is added to the reflog.
    -+				 */
    -+				if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
    -+							     RESOLVE_REF_READING, &update->new_oid, NULL)) {
    -+					/* for dangling symrefs we skip creating a reflog entry. */
    -+					create_reflog = 0;
    -+				}
    -+			}
    -+
    -+			if (create_reflog && files_log_ref_write(refs,
    -+								 lock->ref_name,
    -+								 &lock->old_oid,
    -+								 &update->new_oid,
    -+								 update->msg, update->flags,
    -+								 err)) {
    - 				char *old_msg = strbuf_detach(err, NULL);
    - 
    - 				strbuf_addf(err, "cannot update the ref '%s': %s",
    -@@ refs/files-backend.c: static int files_transaction_finish(struct ref_store *ref_store,
    +-				char *old_msg = strbuf_detach(err, NULL);
    +-
    +-				strbuf_addf(err, "cannot update the ref '%s': %s",
    +-					    lock->ref_name, old_msg);
    +-				free(old_msg);
    +-				unlock_ref(lock);
    +-				update->backend_data = NULL;
    ++			if (parse_and_write_reflog(refs, update, lock, err)) {
    + 				ret = TRANSACTION_GENERIC_ERROR;
      				goto cleanup;
      			}
      		}
    @@ refs/refs-internal.h: void base_ref_store_init(struct ref_store *refs, struct re
     + * takes into consideration that the update could be a regular
     + * ref or a symbolic ref.
     + */
    -+int ref_update_is_null_new_value(struct ref_update *update);
    ++int ref_update_has_null_new_value(struct ref_update *update);
     +
      #endif /* REFS_REFS_INTERNAL_H */
     
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
      			 * when the reference in question doesn't exist.
      			 */
     -			 if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
    -+			 if (u->flags & REF_HAVE_NEW && !ref_update_is_null_new_value(u)) {
    ++			 if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
      				 ret = queue_transaction_update(refs, tx_data, u,
      								&current_oid, err);
      				 if (ret)
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
     +				ret = -1;
     +				goto done;
     +			}
    -+		} else if (u->flags & REF_HAVE_OLD && !oideq(&current_oid, &u->old_oid)) {
    ++		} else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
      			if (is_null_oid(&u->old_oid))
      				strbuf_addf(err, _("cannot lock ref '%s': "
      					    "reference already exists"),
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
      		 *   the given ref.
      		 */
     -		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
    -+		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && ref_update_is_null_new_value(u)) {
    ++		if ((u->flags & REF_HAVE_NEW) && !(u->type & REF_ISSYMREF) && ref_update_has_null_new_value(u)) {
      			struct reftable_log_record log = {0};
      			struct reftable_iterator it = {0};
      
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
      			    should_write_log(&arg->refs->base, u->refname))) {
      			struct reftable_log_record *log;
     +			int create_reflog = 1;
    ++
    ++			if (u->new_target) {
    ++				if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
    ++							     RESOLVE_REF_READING, &u->new_oid, NULL)) {
    ++					/*
    ++					 * TODO: currently we skip creating reflogs for dangling
    ++					 * symref updates. It would be nice to capture this as
    ++					 * zero oid updates however.
    ++					 */
    ++					create_reflog = 0;
    ++				}
    ++			}
      
     -			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
     -			log = &logs[logs_nr++];
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
     -			memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
     -			log->value.update.message =
     -				xstrndup(u->msg, arg->refs->write_options.block_size / 2);
    -+			if (u->new_target)
    -+				if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
    -+							     RESOLVE_REF_READING, &u->new_oid, NULL))
    -+					/* for dangling symrefs we skip creating reflog */
    -+					create_reflog = 0;
    -+
     +			if (create_reflog) {
     +				ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
     +				log = &logs[logs_nr++];
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
      			continue;
      
     -		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
    -+		if (u->flags & REF_HAVE_NEW && u->new_target) {
    ++		if (u->new_target) {
     +			struct reftable_ref_record ref = {
     +				.refname = (char *)u->refname,
     +				.value_type = REFTABLE_REF_SYMREF,
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
     +			ret = reftable_writer_add_ref(writer, &ref);
     +			if (ret < 0)
     +				goto done;
    -+		} else if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
    ++		} else if ((u->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(u)) {
      			struct reftable_ref_record ref = {
      				.refname = (char *)u->refname,
      				.update_index = ts,
5:  5b55406430 ! 5:  636bf5ce98 refs: use transaction in `refs_create_symref()`
    @@ Commit message
         target. To do this, it uses a ref-backend specific function
         `create_symref()`.
     
    -    In this previous commit, we introduce symref support in transactions.
    -    This means we can now use transactions to perform symref updates and not
    -    have to resort to `create_symref()`. Doing this allows us to remove and
    -    cleanup `create_symref()`, which we will do in the following commit.
    +    In the previous commits, we introduced symref support in transactions.
    +    This means we can now use transactions to perform symref updates and
    +    don't have to resort to `create_symref()`. Doing this allows us to
    +    remove and cleanup `create_symref()`, which we will do in the following
    +    commit.
     
         Modify the expected error message for a test in
    -    't/t0610-reftable-basics.sh', since the error is now thrown from the
    +    't/t0610-reftable-basics.sh', since the error is now thrown from
         'refs.c'. This is because in transactional updates, F/D conflicts are
         caught before we're in the reference backend.
     
    @@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls s
     +	git symbolic-ref refs/heads/symref refs/heads/main &&
     +
     +	cat >expect <<-EOF &&
    -+		prepared
    -+		$ZERO_OID ref:refs/heads/main refs/heads/symref
    -+		committed
    -+		$ZERO_OID ref:refs/heads/main refs/heads/symref
    ++	prepared
    ++	$ZERO_OID ref:refs/heads/main refs/heads/symref
    ++	committed
    ++	$ZERO_OID ref:refs/heads/main refs/heads/symref
     +	EOF
     +
     +	test_cmp expect actual
6:  9e25816e68 = 6:  07fb23374f refs: rename `refs_create_symref()` to `refs_update_symref()`
7:  3836e25932 ! 7:  5c05813bcc refs: remove `create_symref` and associated dead code
    @@ Commit message
         transactions to perform symref updates. Earlier `refs_create_symref()`
         used `create_symref()` to do the same.
     
    -    This means, we can now remove `create_symref()` and any code associated
    -    with it which is no longer used. We remove `create_symref()` code from
    -    all the reference backends and also remove it entirely from the
    -    `ref_storage_be` struct.
    +    We can now remove `create_symref()` and any code associated with it
    +    which is no longer used. We remove `create_symref()` code from all the
    +    reference backends and also remove it entirely from the `ref_storage_be`
    +    struct.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs/files-backend.c: static int create_ref_symlink(struct ref_lock *lock, const
     -
      static int create_symref_lock(struct files_ref_store *refs,
      			      struct ref_lock *lock, const char *refname,
    - 			      const char *target)
    + 			      const char *target, struct strbuf *err)
     @@ refs/files-backend.c: static int create_symref_lock(struct files_ref_store *refs,
      	return 0;
      }
    @@ refs/files-backend.c: static int create_symref_lock(struct files_ref_store *refs
     -				    struct ref_lock *lock, const char *refname,
     -				    const char *target, const char *logmsg)
     -{
    +-	struct strbuf err = STRBUF_INIT;
     -	int ret;
     -
     -	if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
    @@ refs/files-backend.c: static int create_symref_lock(struct files_ref_store *refs
     -		return 0;
     -	}
     -
    --	ret = create_symref_lock(refs, lock, refname, target);
    +-	ret = create_symref_lock(refs, lock, refname, target, &err);
     -	if (!ret) {
     -		update_symref_reflog(refs, lock, refname, target, logmsg);
     -
     -		if (commit_ref(lock) < 0)
     -			return error("unable to write symref for %s: %s", refname,
     -				     strerror(errno));
    +-	} else {
    +-		return error("%s", err.buf);
     -	}
     -
     -	return ret;


Karthik Nayak (7):
  refs: accept symref values in `ref_transaction_update()`
  files-backend: extract out `create_symref_lock()`
  refs: support symrefs in 'reference-transaction' hook
  refs: add support for transactional symref updates
  refs: use transaction in `refs_create_symref()`
  refs: rename `refs_create_symref()` to `refs_update_symref()`
  refs: remove `create_symref` and associated dead code

 Documentation/githooks.txt       |  14 +-
 branch.c                         |   2 +-
 builtin/branch.c                 |   2 +-
 builtin/fast-import.c            |   5 +-
 builtin/fetch.c                  |   2 +-
 builtin/receive-pack.c           |   1 +
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   1 +
 builtin/update-ref.c             |   1 +
 builtin/worktree.c               |   2 +-
 refs.c                           |  89 +++++++++----
 refs.h                           |  20 ++-
 refs/debug.c                     |  13 --
 refs/files-backend.c             | 213 +++++++++++++++++++------------
 refs/packed-backend.c            |   1 -
 refs/refs-internal.h             |  26 +++-
 refs/reftable-backend.c          | 165 +++++++++---------------
 sequencer.c                      |   9 +-
 t/helper/test-ref-store.c        |   2 +-
 t/t0610-reftable-basics.sh       |   2 +-
 t/t1416-ref-transaction-hooks.sh |  23 ++++
 walker.c                         |   2 +-
 22 files changed, 351 insertions(+), 246 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 1%]

* [PATCH v4 3/3] advice: add --no-advice global option
    2024-05-03  7:17  6%       ` [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts James Liu
  2024-05-03  7:17 12%       ` [PATCH v4 2/3] doc: add spacing around paginate options James Liu
@ 2024-05-03  7:17  5%       ` James Liu
  2 siblings, 0 replies; 200+ results
From: James Liu @ 2024-05-03  7:17 UTC (permalink / raw)
  To: git; +Cc: James Liu

Advice hints must be disabled individually by setting the relevant
advice.* variables to false in the Git configuration. For server-side
and scripted usages of Git where hints can be a hindrance, it can be
cumbersome to maintain configuration to ensure all advice hints are
disabled in perpetuity. This is a particular concern in tests, where
new or changed hints can result in failed assertions.

Add a --no-advice global option to disable all advice hints from being
displayed. This is independent of the toggles for individual advice
hints. Use an internal environment variable (GIT_ADVICE) to ensure this
configuration is propagated to the usage site, even if it executes in a
subprocess.

Signed-off-by: James Liu <james@jamesliu.io>
---
 Documentation/git.txt |  8 +++--
 advice.c              |  7 +++++
 environment.h         |  7 +++++
 git.c                 |  9 ++++--
 t/t0018-advice.sh     | 68 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index d11d3d0c86..a0c07f1db8 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -12,8 +12,9 @@ SYNOPSIS
 'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
     [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
-    [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
-    [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
+    [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
+    [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
+    <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -190,6 +191,9 @@ If you just want to run git as if it was started in `<path>` then use
 	Do not perform optional operations that require locks. This is
 	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
 
+--no-advice::
+	Disable all advice hints from being printed.
+
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
 	This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
diff --git a/advice.c b/advice.c
index 75111191ad..0a122c2020 100644
--- a/advice.c
+++ b/advice.c
@@ -2,6 +2,7 @@
 #include "advice.h"
 #include "config.h"
 #include "color.h"
+#include "environment.h"
 #include "gettext.h"
 #include "help.h"
 #include "string-list.h"
@@ -127,6 +128,12 @@ void advise(const char *advice, ...)
 int advice_enabled(enum advice_type type)
 {
 	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+	static int globally_enabled = -1;
+
+	if (globally_enabled < 0)
+		globally_enabled = git_env_bool(GIT_ADVICE_ENVIRONMENT, 1);
+	if (!globally_enabled)
+		return 0;
 
 	if (type == ADVICE_PUSH_UPDATE_REJECTED)
 		return enabled &&
diff --git a/environment.h b/environment.h
index 05fd94d7be..0b2d457f07 100644
--- a/environment.h
+++ b/environment.h
@@ -57,6 +57,13 @@ const char *getenv_safe(struct strvec *argv, const char *name);
 #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
 #define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
 
+/*
+ * Environment variable used to propagate the --no-advice global option to the
+ * advice_enabled() helper, even when run in a subprocess.
+ * This is an internal variable that should not be set by the user.
+ */
+#define GIT_ADVICE_ENVIRONMENT "GIT_ADVICE"
+
 /*
  * Environment variable used in handshaking the wire protocol.
  * Contains a colon ':' separated list of keys with optional values
diff --git a/git.c b/git.c
index 7654571b75..637c61ca9c 100644
--- a/git.c
+++ b/git.c
@@ -37,8 +37,9 @@ const char git_usage_string[] =
 	N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
 	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
-	   "           [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
-	   "           [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]");
+	   "           [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
+	   "           [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
+	   "           <command> [<args>]");
 
 const char git_more_info_string[] =
 	N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -337,6 +338,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 			setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--no-advice")) {
+			setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else {
 			fprintf(stderr, _("unknown option: %s\n"), cmd);
 			usage(git_usage_string);
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 0dcfb760a2..b02448ea16 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -29,4 +29,72 @@ test_expect_success 'advice should not be printed when config variable is set to
 	test_must_be_empty actual
 '
 
+test_expect_success 'advice should not be printed when --no-advice is used' '
+	q_to_tab >expect <<-\EOF &&
+On branch master
+
+No commits yet
+
+Untracked files:
+QREADME
+
+nothing added to commit but untracked files present
+EOF
+
+	test_when_finished "rm -fr advice-test" &&
+	git init advice-test &&
+	(
+		cd advice-test &&
+		>README &&
+		git --no-advice status
+	) >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'advice should not be printed when GIT_ADVICE is set to false' '
+	q_to_tab >expect <<-\EOF &&
+On branch master
+
+No commits yet
+
+Untracked files:
+QREADME
+
+nothing added to commit but untracked files present
+EOF
+
+	test_when_finished "rm -fr advice-test" &&
+	git init advice-test &&
+	(
+		cd advice-test &&
+		>README &&
+		GIT_ADVICE=false git status
+	) >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'advice should be printed when GIT_ADVICE is set to true' '
+	q_to_tab >expect <<-\EOF &&
+On branch master
+
+No commits yet
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+QREADME
+
+nothing added to commit but untracked files present (use "git add" to track)
+EOF
+
+	test_when_finished "rm -fr advice-test" &&
+	git init advice-test &&
+	(
+		cd advice-test &&
+		>README &&
+		GIT_ADVICE=true git status
+	) >actual &&
+	cat actual > /tmp/actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.44.0



^ permalink raw reply related	[relevance 5%]

* [PATCH v4 2/3] doc: add spacing around paginate options
    2024-05-03  7:17  6%       ` [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts James Liu
@ 2024-05-03  7:17 12%       ` James Liu
  2024-05-03 14:32  0%         ` Karthik Nayak
  2024-05-03  7:17  5%       ` [PATCH v4 3/3] advice: add --no-advice global option James Liu
  2 siblings, 1 reply; 200+ results
From: James Liu @ 2024-05-03  7:17 UTC (permalink / raw)
  To: git; +Cc: James Liu

Make the documentation page consistent with the usage string printed by
git.c

Signed-off-by: James Liu <james@jamesliu.io>
---
 Documentation/git.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index 7fa75350b2..d11d3d0c86 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
-    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--no-lazy-fetch]
+    [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
     [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
     [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
 
-- 
2.44.0



^ permalink raw reply related	[relevance 12%]

* [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts
  @ 2024-05-03  7:17  6%       ` James Liu
  2024-05-03 17:30  0%         ` Junio C Hamano
  2024-05-03  7:17 12%       ` [PATCH v4 2/3] doc: add spacing around paginate options James Liu
  2024-05-03  7:17  5%       ` [PATCH v4 3/3] advice: add --no-advice global option James Liu
  2 siblings, 1 reply; 200+ results
From: James Liu @ 2024-05-03  7:17 UTC (permalink / raw)
  To: git; +Cc: James Liu

We'll be adding another option to the --no-* class of options soon.

Clean up the existing options by grouping them together in the OPTIONS
section, and adding missing ones to the SYNOPSIS.

Signed-off-by: James Liu <james@jamesliu.io>
---
 Documentation/git.txt | 14 +++++++-------
 git.c                 |  6 +++---
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/Documentation/git.txt b/Documentation/git.txt
index 7a1b112a3e..7fa75350b2 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -11,9 +11,9 @@ SYNOPSIS
 [verse]
 'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
-    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
-    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
-    [--config-env=<name>=<envvar>] <command> [<args>]
+    [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--no-lazy-fetch]
+    [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]
+    [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -186,6 +186,10 @@ If you just want to run git as if it was started in `<path>` then use
 	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
 	environment variable to `1`.
 
+--no-optional-locks::
+	Do not perform optional operations that require locks. This is
+	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
+
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
 	This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
@@ -207,10 +211,6 @@ If you just want to run git as if it was started in `<path>` then use
 	Add "icase" magic to all pathspec. This is equivalent to setting
 	the `GIT_ICASE_PATHSPECS` environment variable to `1`.
 
---no-optional-locks::
-	Do not perform optional operations that require locks. This is
-	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
-
 --list-cmds=<group>[,<group>...]::
 	List commands by group. This is an internal/experimental
 	option and may change or be removed in the future. Supported
diff --git a/git.c b/git.c
index 654d615a18..7654571b75 100644
--- a/git.c
+++ b/git.c
@@ -36,9 +36,9 @@ struct cmd_struct {
 const char git_usage_string[] =
 	N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
 	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
-	   "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-	   "           [--config-env=<name>=<envvar>] <command> [<args>]");
+	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
+	   "           [--no-optional-locks] [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
+	   "           [--namespace=<name>] [--config-env=<name>=<envvar>] <command> [<args>]");
 
 const char git_more_info_string[] =
 	N_("'git help -a' and 'git help -g' list available subcommands and some\n"
-- 
2.44.0



^ permalink raw reply related	[relevance 6%]

* Re: [PATCH v2] refs: return conflict error when checking packed refs
  2024-05-03  4:50 19% ` [PATCH v2] " Ivan Tse via GitGitGadget
@ 2024-05-03  6:38  3%   ` Patrick Steinhardt
  2024-05-04  3:04 19%   ` [PATCH v3] " Ivan Tse via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-03  6:38 UTC (permalink / raw)
  To: Ivan Tse via GitGitGadget; +Cc: git, Ivan Tse

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

On Fri, May 03, 2024 at 04:50:29AM +0000, Ivan Tse via GitGitGadget wrote:
> From: Ivan Tse <ivan.tse1@gmail.com>
> 
> The TRANSACTION_NAME_CONFLICT error code refers to a failure to create a
> ref due to a name conflict with another ref. An example of this is a
> directory/file conflict such as ref names A/B and A.
> 
> "git fetch" uses this error code to more accurately describe the error
> by recommending to the user that they try running "git remote prune" to
> remove any old refs that are deleted by the remote which would clear up
> any directory/file conflicts.
> 
> This helpful error message is not displayed when the conflicted ref is
> stored in packed refs. This change fixes this by ensuring error return
> code consistency in `lock_raw_ref`.
> 
> Signed-off-by: Ivan Tse <ivan.tse1@gmail.com>
> ---
>     refs: return conflict error when checking packed refs
>     
>     Changes against v1:
>     
>      * added test for the error message during git fetch
>     
>     Thanks for reviewing! I've gone ahead and attempted to add tests for
>     this behavior. It tests that the error message is shown for both cases
>     when the ref is stored as loose vs packed-refs. How does this test look?
>     Also, should this test have a REFFILES prerequisite?

There is no need for the REFFILES prerequisite as you never access refs
on disk directly, but instead use our plumbing commands. Furthermore, we
do want to verify that both backends behave the same here. If the test
failed with the "reftable" backend I'd consider that to be a bug in the
backend.

[snip]
> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
> index 33d34d5ae9e..ae0828e26a1 100755
> --- a/t/t5510-fetch.sh
> +++ b/t/t5510-fetch.sh
> @@ -1091,6 +1091,22 @@ test_expect_success 'branchname D/F conflict resolved by --prune' '
>  	test_cmp expect actual
>  '
>  
> +test_expect_success 'branchname D/F conflict rejected with targeted error message' '
> +	git clone . df-conflict-error &&
> +	git branch dir_conflict &&
> +	(
> +		cd df-conflict-error &&
> +		git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
> +		test_must_fail git fetch 2>../err &&
> +		git pack-refs --all &&
> +		test_must_fail git fetch 2>../err-packed
> +	) &&
> +	test_grep "error: some local refs could not be updated; try running" err &&
> +	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err &&
> +	test_grep "error: some local refs could not be updated; try running" err-packed &&
> +	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err-packed

I would personally add those calls to `test_grep` right after the
respective fetches to make the test a bit easier to follow.

Also, instead of using '\''`, you can use the "${SQ}" variable to insert
single quotes. So, something like this:

    test_grep " ${SQ}git remote prune origin${SQ} to remove any old, conflicting branches" err &&

Other than that this patch looks good to me, thanks!

Patrick

> +'
> +
>  test_expect_success 'fetching a one-level ref works' '
>  	test_commit extra &&
>  	git reset --hard HEAD^ &&
> 
> base-commit: d4cc1ec35f3bcce816b69986ca41943f6ce21377
> -- 
> gitgitgadget
> 

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

^ permalink raw reply	[relevance 3%]

* [PATCH 4/5] cocci: apply rules to rewrite callers of "refs" interfaces
  2024-05-03  6:27  3% [PATCH 0/5] refs: remove functions without ref store Patrick Steinhardt
@ 2024-05-03  6:28  6% ` Patrick Steinhardt
  2024-05-03 18:48  0%   ` Taylor Blau
  0 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-05-03  6:28 UTC (permalink / raw)
  To: git

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

Apply the rules that rewrite callers of "refs" interfaces to explicitly
pass `struct ref_store`. The resulting patch has been applied with the
`--whitespace=fix` option.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 add-interactive.c           | 17 +++++++----
 bisect.c                    | 25 ++++++++++-----
 blame.c                     |  4 +--
 branch.c                    |  5 +--
 builtin/am.c                | 38 ++++++++++++++---------
 builtin/bisect.c            | 44 +++++++++++++++-----------
 builtin/blame.c             |  4 +--
 builtin/branch.c            | 49 ++++++++++++++++-------------
 builtin/checkout.c          | 35 +++++++++++++--------
 builtin/clone.c             | 36 +++++++++++++---------
 builtin/describe.c          |  3 +-
 builtin/fast-import.c       | 11 ++++---
 builtin/fetch.c             | 20 ++++++++----
 builtin/fsck.c              | 11 +++++--
 builtin/gc.c                |  3 +-
 builtin/log.c               |  6 ++--
 builtin/merge.c             | 34 +++++++++++++--------
 builtin/name-rev.c          |  5 +--
 builtin/notes.c             | 26 +++++++++-------
 builtin/pack-objects.c      | 10 ++++--
 builtin/pull.c              |  2 +-
 builtin/rebase.c            | 18 ++++++-----
 builtin/receive-pack.c      | 15 ++++++---
 builtin/reflog.c            | 25 ++++++++-------
 builtin/remote.c            | 37 +++++++++++++---------
 builtin/repack.c            |  7 +++--
 builtin/replace.c           |  9 +++---
 builtin/reset.c             | 13 +++++---
 builtin/rev-parse.c         | 25 ++++++++++-----
 builtin/show-branch.c       | 22 ++++++++-----
 builtin/show-ref.c          | 19 ++++++++----
 builtin/stash.c             | 23 ++++++++------
 builtin/submodule--helper.c |  7 +++--
 builtin/symbolic-ref.c      | 13 +++++---
 builtin/tag.c               | 11 ++++---
 builtin/update-index.c      |  2 +-
 builtin/update-ref.c        | 21 ++++++++-----
 builtin/worktree.c          | 19 +++++++-----
 bundle-uri.c                | 12 +++++---
 bundle.c                    |  2 +-
 commit-graph.c              |  3 +-
 commit.c                    |  3 +-
 config.c                    |  3 +-
 delta-islands.c             |  3 +-
 fetch-pack.c                |  6 ++--
 fmt-merge-msg.c             |  4 ++-
 help.c                      |  5 +--
 http-backend.c              | 13 +++++---
 log-tree.c                  |  9 ++++--
 ls-refs.c                   | 10 +++---
 midx-write.c                |  3 +-
 negotiator/default.c        |  3 +-
 negotiator/skipping.c       |  3 +-
 notes-cache.c               |  6 ++--
 notes-merge.c               |  2 +-
 notes-utils.c               |  7 +++--
 notes.c                     |  5 +--
 reachable.c                 |  5 +--
 ref-filter.c                | 35 +++++++++++++++------
 reflog-walk.c               | 27 +++++++++++-----
 reflog.c                    | 20 +++++++-----
 refs.c                      |  9 ++++--
 remote.c                    | 38 ++++++++++++++---------
 reset.c                     | 29 ++++++++++--------
 revision.c                  | 27 ++++++++++------
 sequencer.c                 | 61 ++++++++++++++++++++-----------------
 server-info.c               |  3 +-
 setup.c                     |  2 +-
 shallow.c                   | 16 ++++++----
 submodule.c                 |  6 ++--
 transport-helper.c          | 29 ++++++++++--------
 transport.c                 | 16 ++++++----
 upload-pack.c               | 20 +++++++-----
 walker.c                    |  6 ++--
 wt-status.c                 | 22 +++++++------
 75 files changed, 711 insertions(+), 436 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index e17602b5e4..b5d6cd689a 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -532,8 +532,9 @@ static int get_modified_files(struct repository *r,
 			      size_t *binary_count)
 {
 	struct object_id head_oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-					     &head_oid, NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &head_oid, NULL);
 	struct collection_status s = { 0 };
 	int i;
 
@@ -761,8 +762,10 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
 	size_t count, i, j;
 
 	struct object_id oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
-					     NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &oid,
+						  NULL);
 	struct lock_file index_lock;
 	const char **paths;
 	struct tree *tree;
@@ -990,8 +993,10 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
 	ssize_t count, i;
 
 	struct object_id oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
-					     NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &oid,
+						  NULL);
 	if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
 		return -1;
 
diff --git a/bisect.c b/bisect.c
index 29aae879b8..4ea703bec1 100644
--- a/bisect.c
+++ b/bisect.c
@@ -469,7 +469,8 @@ static int register_ref(const char *refname, const struct object_id *oid,
 
 static int read_bisect_refs(void)
 {
-	return for_each_ref_in("refs/bisect/", register_ref, NULL);
+	return refs_for_each_ref_in(get_main_ref_store(the_repository),
+				    "refs/bisect/", register_ref, NULL);
 }
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -709,7 +710,7 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
 static int is_expected_rev(const struct object_id *oid)
 {
 	struct object_id expected_oid;
-	if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected_oid))
 		return 0;
 	return oideq(oid, &expected_oid);
 }
@@ -721,11 +722,14 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
 	struct pretty_print_context pp = {0};
 	struct strbuf commit_msg = STRBUF_INIT;
 
-	update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), NULL,
+			"BISECT_EXPECTED_REV", bisect_rev, NULL, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (no_checkout) {
-		update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), NULL,
+				"BISECT_HEAD", bisect_rev, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else {
 		struct child_process cmd = CHILD_PROCESS_INIT;
 
@@ -1027,7 +1031,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	 * If no_checkout is non-zero, the bisection process does not
 	 * checkout the trial commit but instead simply updates BISECT_HEAD.
 	 */
-	int no_checkout = ref_exists("BISECT_HEAD");
+	int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+					  "BISECT_HEAD");
 	unsigned bisect_flags = 0;
 
 	read_bisect_terms(&term_bad, &term_good);
@@ -1178,10 +1183,14 @@ int bisect_clean_state(void)
 
 	/* There may be some refs packed during bisection */
 	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
-	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+	refs_for_each_ref_in(get_main_ref_store(the_repository),
+			     "refs/bisect", mark_for_removal,
+			     (void *) &refs_for_removal);
 	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
 	string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
-	result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
+	result = refs_delete_refs(get_main_ref_store(the_repository),
+				  "bisect: remove", &refs_for_removal,
+				  REF_NO_DEREF);
 	refs_for_removal.strdup_strings = 1;
 	string_list_clear(&refs_for_removal, 0);
 	unlink_or_warn(git_path_bisect_ancestors_ok());
diff --git a/blame.c b/blame.c
index 1a16d4eb6a..33586b9777 100644
--- a/blame.c
+++ b/blame.c
@@ -2700,7 +2700,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
 		return NULL;
 
 	/* Do we have HEAD? */
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+	if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
 		return NULL;
 	head_commit = lookup_commit_reference_gently(revs->repo,
 						     &head_oid, 1);
@@ -2803,7 +2803,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
 		if (sb->final) {
 			parent_oid = &sb->final->object.oid;
 		} else {
-			if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+			if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
 				die("no such ref: HEAD");
 			parent_oid = &head_oid;
 		}
diff --git a/branch.c b/branch.c
index e4a738fc7b..a83f7ecf89 100644
--- a/branch.c
+++ b/branch.c
@@ -377,7 +377,7 @@ int validate_branchname(const char *name, struct strbuf *ref)
 		exit(code);
 	}
 
-	return ref_exists(ref->buf);
+	return refs_ref_exists(get_main_ref_store(the_repository), ref->buf);
 }
 
 static int initialized_checked_out_branches;
@@ -623,7 +623,8 @@ void create_branch(struct repository *r,
 		msg = xstrfmt("branch: Reset to %s", start_name);
 	else
 		msg = xstrfmt("branch: Created from %s", start_name);
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2e8d..c62c5a6f71 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1001,7 +1001,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
 
 	if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
 		die_errno(_("failed to create directory '%s'"), state->dir);
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
 
 	if (split_mail(state, patch_format, paths, keep_cr) < 0) {
 		am_destroy(state);
@@ -1081,12 +1082,15 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
 	if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
 		write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
 		if (!state->rebasing)
-			update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
-				   UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					"am", "ORIG_HEAD", &curr_head, NULL,
+					0,
+					UPDATE_REFS_DIE_ON_ERR);
 	} else {
 		write_state_text(state, "abort-safety", "");
 		if (!state->rebasing)
-			delete_ref(NULL, "ORIG_HEAD", NULL, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, "ORIG_HEAD", NULL, 0);
 	}
 
 	/*
@@ -1119,7 +1123,8 @@ static void am_next(struct am_state *state)
 
 	oidclr(&state->orig_commit);
 	unlink(am_path(state, "original-commit"));
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
 
 	if (!repo_get_oid(the_repository, "HEAD", &head))
 		write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1466,8 +1471,9 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
 
 	oidcpy(&state->orig_commit, &commit_oid);
 	write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
-	update_ref("am", "REBASE_HEAD", &commit_oid,
-		   NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), "am",
+			"REBASE_HEAD", &commit_oid,
+			NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
 
 	return 0;
 }
@@ -1697,8 +1703,9 @@ static void do_commit(const struct am_state *state)
 	strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
 			state->msg);
 
-	update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
-		   UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), sb.buf, "HEAD",
+			&commit, old_oid, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (state->rebasing) {
 		FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -2175,7 +2182,8 @@ static void am_abort(struct am_state *state)
 
 	am_rerere_clear();
 
-	curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
+	curr_branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+					  "HEAD", 0, &curr_head, NULL);
 	has_curr_head = curr_branch && !is_null_oid(&curr_head);
 	if (!has_curr_head)
 		oidcpy(&curr_head, the_hash_algo->empty_tree);
@@ -2188,11 +2196,13 @@ static void am_abort(struct am_state *state)
 		die(_("failed to clean index"));
 
 	if (has_orig_head)
-		update_ref("am --abort", "HEAD", &orig_head,
-			   has_curr_head ? &curr_head : NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"am --abort", "HEAD", &orig_head,
+				has_curr_head ? &curr_head : NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	else if (curr_branch)
-		delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
+		refs_delete_ref(get_main_ref_store(the_repository), NULL,
+				curr_branch, NULL, REF_NO_DEREF);
 
 	free(curr_branch);
 	am_destroy(state);
diff --git a/builtin/bisect.c b/builtin/bisect.c
index f69c3f7e43..a58432b9d9 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -243,7 +243,7 @@ static int bisect_reset(const char *commit)
 		strbuf_addstr(&branch, commit);
 	}
 
-	if (branch.len && !ref_exists("BISECT_HEAD")) {
+	if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
 
 		cmd.git_cmd = 1;
@@ -302,8 +302,8 @@ static int bisect_write(const char *state, const char *rev,
 		goto finish;
 	}
 
-	if (update_ref(NULL, tag.buf, &oid, NULL, 0,
-		       UPDATE_REFS_MSG_ON_ERR)) {
+	if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0,
+			    UPDATE_REFS_MSG_ON_ERR)) {
 		res = -1;
 		goto finish;
 	}
@@ -416,11 +416,12 @@ static void bisect_status(struct bisect_state *state,
 	char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
 	char *good_glob = xstrfmt("%s-*", terms->term_good);
 
-	if (ref_exists(bad_ref))
+	if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref))
 		state->nr_bad = 1;
 
-	for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
-			     (void *) &state->nr_good);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr,
+				  good_glob, "refs/bisect/",
+				  (void *) &state->nr_good);
 
 	free(good_glob);
 	free(bad_ref);
@@ -574,9 +575,11 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
 	reset_revision_walk();
 	repo_init_revisions(the_repository, revs, NULL);
 	setup_revisions(0, NULL, revs, NULL);
-	for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  add_bisect_ref, bad, "refs/bisect/", &cb);
 	cb.object_flags = UNINTERESTING;
-	for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  add_bisect_ref, good, "refs/bisect/", &cb);
 	if (prepare_revision_walk(revs))
 		res = error(_("revision walk setup failed\n"));
 
@@ -636,7 +639,7 @@ static int bisect_successful(struct bisect_terms *terms)
 	char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
 	int res;
 
-	read_ref(bad_ref, &oid);
+	refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid);
 	commit = lookup_commit_reference_by_name(bad_ref);
 	repo_format_commit_message(the_repository, commit, "%s", &commit_name,
 				   &pp);
@@ -779,7 +782,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
 	/*
 	 * Verify HEAD
 	 */
-	head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
+	head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+				       "HEAD", 0, &head_oid, &flags);
 	if (!head)
 		if (repo_get_oid(the_repository, "HEAD", &head_oid))
 			return error(_("bad HEAD - I need a HEAD"));
@@ -838,8 +842,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
 			res = error(_("invalid ref: '%s'"), start_head.buf);
 			goto finish;
 		}
-		if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
-			       UPDATE_REFS_MSG_ON_ERR)) {
+		if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0,
+				    UPDATE_REFS_MSG_ON_ERR)) {
 			res = BISECT_FAILED;
 			goto finish;
 		}
@@ -972,7 +976,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
 		oid_array_append(&revs, &commit->object.oid);
 	}
 
-	if (read_ref("BISECT_EXPECTED_REV", &expected))
+	if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected))
 		verify_expected = 0; /* Ignore invalid file contents */
 
 	for (i = 0; i < revs.nr; i++) {
@@ -982,7 +986,9 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
 		}
 		if (verify_expected && !oideq(&revs.oid[i], &expected)) {
 			unlink_or_warn(git_path_bisect_ancestors_ok());
-			delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, "BISECT_EXPECTED_REV", NULL,
+					REF_NO_DEREF);
 			verify_expected = 0;
 		}
 	}
@@ -1179,13 +1185,15 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
 	struct object_id good_rev;
 	struct object_id current_rev;
 	char *good_glob = xstrfmt("%s-*", terms->term_good);
-	int no_checkout = ref_exists("BISECT_HEAD");
+	int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+					  "BISECT_HEAD");
 
-	for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
-			     &good_rev);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  get_first_good, good_glob, "refs/bisect/",
+				  &good_rev);
 	free(good_glob);
 
-	if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
+	if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
 		return -1;
 
 	res = bisect_checkout(&good_rev, no_checkout);
diff --git a/builtin/blame.c b/builtin/blame.c
index 9aa74680a3..6bc7aa6085 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1093,8 +1093,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		struct commit *head_commit;
 		struct object_id head_oid;
 
-		if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-					&head_oid, NULL) ||
+		if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING,
+					     &head_oid, NULL) ||
 		    !(head_commit = lookup_commit_reference_gently(revs.repo,
 							     &head_oid, 1)))
 			die("no such ref: HEAD");
diff --git a/builtin/branch.c b/builtin/branch.c
index dd3e3a7dc0..e32ccc4332 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -148,8 +148,8 @@ static int branch_merged(int kind, const char *name,
 
 		if (upstream &&
 		    (reference_name = reference_name_to_free =
-		     resolve_refdup(upstream, RESOLVE_REF_READING,
-				    &oid, NULL)) != NULL)
+		     refs_resolve_refdup(get_main_ref_store(the_repository), upstream, RESOLVE_REF_READING,
+					 &oid, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(the_repository,
 								&oid);
 	}
@@ -272,21 +272,24 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 			}
 		}
 
-		target = resolve_refdup(name,
-					RESOLVE_REF_READING
-					| RESOLVE_REF_NO_RECURSE
-					| RESOLVE_REF_ALLOW_BAD_NAME,
-					&oid, &flags);
+		target = refs_resolve_refdup(get_main_ref_store(the_repository),
+					     name,
+					     RESOLVE_REF_READING
+					     | RESOLVE_REF_NO_RECURSE
+					     | RESOLVE_REF_ALLOW_BAD_NAME,
+					     &oid, &flags);
 		if (!target) {
 			if (remote_branch) {
 				error(_("remote-tracking branch '%s' not found"), bname.buf);
 			} else {
 				char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
-				char *virtual_target = resolve_refdup(virtual_name,
-							RESOLVE_REF_READING
-							| RESOLVE_REF_NO_RECURSE
-							| RESOLVE_REF_ALLOW_BAD_NAME,
-							&oid, &flags);
+				char *virtual_target = refs_resolve_refdup(get_main_ref_store(the_repository),
+									   virtual_name,
+									   RESOLVE_REF_READING
+									   | RESOLVE_REF_NO_RECURSE
+									   | RESOLVE_REF_ALLOW_BAD_NAME,
+									   &oid,
+									   &flags);
 				FREE_AND_NULL(virtual_name);
 
 				if (virtual_target)
@@ -317,13 +320,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 		free(target);
 	}
 
-	if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+	if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
 		ret = 1;
 
 	for_each_string_list_item(item, &refs_to_delete) {
 		char *describe_ref = item->util;
 		char *name = item->string;
-		if (!ref_exists(name)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
 			char *refname = name + branch_name_pos;
 			if (!quiet)
 				printf(remote_branch
@@ -499,7 +502,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 static void print_current_branch_name(void)
 {
 	int flags;
-	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						      "HEAD", 0, NULL, &flags);
 	const char *shortname;
 	if (!refname)
 		die(_("could not resolve HEAD"));
@@ -580,7 +584,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
 		 * Bad name --- this could be an attempt to rename a
 		 * ref that we used to allow to be created by accident.
 		 */
-		if (ref_exists(oldref.buf))
+		if (refs_ref_exists(get_main_ref_store(the_repository), oldref.buf))
 			recovery = 1;
 		else {
 			int code = die_message(_("invalid branch name: '%s'"), oldname);
@@ -601,7 +605,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
 		}
 	}
 
-	if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+	if ((copy || !(oldref_usage & IS_HEAD)) && !refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) {
 		if (oldref_usage & IS_HEAD)
 			die(_("no commit on branch '%s' yet"), oldname);
 		else
@@ -632,9 +636,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
 			    oldref.buf, newref.buf);
 
 	if (!copy && !(oldref_usage & IS_ORPHAN) &&
-	    rename_ref(oldref.buf, newref.buf, logmsg.buf))
+	    refs_rename_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
 		die(_("branch rename failed"));
-	if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+	if (copy && refs_copy_existing_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
 		die(_("branch copy failed"));
 
 	if (recovery) {
@@ -786,7 +790,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	track = git_branch_track;
 
-	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   0, &head_oid, NULL);
 	if (!head)
 		die(_("failed to resolve HEAD as a valid ref"));
 	if (!strcmp(head, "HEAD"))
@@ -891,7 +896,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		}
 
 		strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
-		if (!ref_exists(branch_ref.buf))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch_ref.buf))
 			error((!argc || branch_checked_out(branch_ref.buf))
 			      ? _("no commit on branch '%s' yet")
 			      : _("no branch named '%s'"),
@@ -936,7 +941,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			die(_("no such branch '%s'"), argv[0]);
 		}
 
-		if (!ref_exists(branch->refname)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
 			if (!argc || branch_checked_out(branch->refname))
 				die(_("no commit on branch '%s' yet"), branch->name);
 			die(_("branch '%s' does not exist"), branch->name);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e6036aab..3944a9fcba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -646,7 +646,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		rollback_lock_file(&lock_file);
 	}
 
-	read_ref_full("HEAD", 0, &rev, NULL);
+	refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0,
+			   &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
 
 	errs |= post_checkout_hook(head, head, 0);
@@ -958,7 +959,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 				int ret;
 				struct strbuf err = STRBUF_INIT;
 
-				ret = safe_create_reflog(refname, &err);
+				ret = refs_create_reflog(get_main_ref_store(the_repository),
+							 refname, &err);
 				if (ret) {
 					fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
 						opts->new_orphan_branch, err.buf);
@@ -999,8 +1001,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 	if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
 		/* Nothing to do. */
 	} else if (opts->force_detach || !new_branch_info->path) {	/* No longer on any branch. */
-		update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
-			   REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"HEAD", &new_branch_info->commit->object.oid,
+				NULL,
+				REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old_branch_info->path &&
 			    advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach)
@@ -1008,7 +1012,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 			describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
 		}
 	} else if (new_branch_info->path) {	/* Switch branches. */
-		if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
+		if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
 			die(_("unable to update HEAD"));
 		if (!opts->quiet) {
 			if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
@@ -1029,8 +1033,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 			}
 		}
 		if (old_branch_info->path && old_branch_info->name) {
-			if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
-				delete_reflog(old_branch_info->path);
+			if (!refs_ref_exists(get_main_ref_store(the_repository), old_branch_info->path) && refs_reflog_exists(get_main_ref_store(the_repository), old_branch_info->path))
+				refs_delete_reflog(get_main_ref_store(the_repository),
+						   old_branch_info->path);
 		}
 	}
 	remove_branch_state(the_repository, !opts->quiet);
@@ -1129,7 +1134,8 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 	object->flags &= ~UNINTERESTING;
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
-	for_each_ref(add_pending_uninteresting_ref, &revs);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  add_pending_uninteresting_ref, &revs);
 	if (new_commit)
 		add_pending_oid(&revs, "HEAD",
 				&new_commit->object.oid,
@@ -1159,7 +1165,8 @@ static int switch_branches(const struct checkout_opts *opts,
 	trace2_cmd_mode("branch");
 
 	memset(&old_branch_info, 0, sizeof(old_branch_info));
-	old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
+	old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository),
+						   "HEAD", 0, &rev, &flag);
 	if (old_branch_info.path)
 		old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
 	if (!(flag & REF_ISSYMREF))
@@ -1247,7 +1254,7 @@ static void setup_new_branch_info_and_source_tree(
 	setup_branch_path(new_branch_info);
 
 	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
+	    !refs_read_ref(get_main_ref_store(the_repository), new_branch_info->path, &branch_rev))
 		oidcpy(rev, &branch_rev);
 	else
 		/* not an existing branch */
@@ -1466,7 +1473,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
 	if (!opts->new_branch)
 		die(_("You are on a branch yet to be born"));
 	strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
-	status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+	status = refs_create_symref(get_main_ref_store(the_repository),
+				    "HEAD", branch_ref.buf, "checkout -b");
 	strbuf_release(&branch_ref);
 	if (!opts->quiet)
 		fprintf(stderr, _("Switched to a new branch '%s'\n"),
@@ -1553,7 +1561,8 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
 
 	if (opts->ignore_other_worktrees)
 		return;
-	head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+	head_ref = refs_resolve_refdup(get_main_ref_store(the_repository),
+				       "HEAD", 0, NULL, &flags);
 	if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
 		die_if_checked_out(full_ref, 1);
 	free(head_ref);
@@ -1634,7 +1643,7 @@ static int checkout_branch(struct checkout_opts *opts,
 		struct object_id rev;
 		int flag;
 
-		if (!read_ref_full("HEAD", 0, &rev, &flag) &&
+		if (!refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &rev, &flag) &&
 		    (flag & REF_ISSYMREF) && is_null_oid(&rev))
 			return switch_unborn_to_new_branch(opts);
 	}
diff --git a/builtin/clone.c b/builtin/clone.c
index 74ec14542e..f279b84a84 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -539,7 +539,8 @@ static void write_remote_refs(const struct ref *local_refs)
 	struct ref_transaction *t;
 	struct strbuf err = STRBUF_INIT;
 
-	t = ref_transaction_begin(&err);
+	t = ref_store_transaction_begin(get_main_ref_store(the_repository),
+					&err);
 	if (!t)
 		die("%s", err.buf);
 
@@ -570,8 +571,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
 						     OBJECT_INFO_QUICK |
 						     OBJECT_INFO_SKIP_FETCH_OBJECT))
 			continue;
-		update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				ref->name, &ref->old_oid, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	}
 }
 
@@ -623,9 +625,9 @@ static void update_remote_refs(const struct ref *refs,
 		struct strbuf head_ref = STRBUF_INIT;
 		strbuf_addstr(&head_ref, branch_top);
 		strbuf_addstr(&head_ref, "HEAD");
-		if (create_symref(head_ref.buf,
-				  remote_head_points_at->peer_ref->name,
-				  msg) < 0)
+		if (refs_create_symref(get_main_ref_store(the_repository), head_ref.buf,
+				       remote_head_points_at->peer_ref->name,
+				       msg) < 0)
 			die(_("unable to update %s"), head_ref.buf);
 		strbuf_release(&head_ref);
 	}
@@ -637,33 +639,36 @@ static void update_head(const struct ref *our, const struct ref *remote,
 	const char *head;
 	if (our && skip_prefix(our->name, "refs/heads/", &head)) {
 		/* Local default branch link */
-		if (create_symref("HEAD", our->name, NULL) < 0)
+		if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
 			die(_("unable to update HEAD"));
 		if (!option_bare) {
-			update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
-				   UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					msg, "HEAD", &our->old_oid, NULL, 0,
+					UPDATE_REFS_DIE_ON_ERR);
 			install_branch_config(0, head, remote_name, our->name);
 		}
 	} else if (our) {
 		struct commit *c = lookup_commit_reference(the_repository,
 							   &our->old_oid);
 		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
-		update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				"HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
 		 * HEAD points to a branch but we don't know which one.
 		 * Detach HEAD in all these cases.
 		 */
-		update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				"HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
 		/*
 		 * Unborn head from remote; same as "our" case above except
 		 * that we have no ref to update.
 		 */
-		if (create_symref("HEAD", unborn, NULL) < 0)
+		if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
 			die(_("unable to update HEAD"));
 		if (!option_bare)
 			install_branch_config(0, head, remote_name, unborn);
@@ -704,7 +709,8 @@ static int checkout(int submodule_progress, int filter_submodules)
 	if (option_no_checkout)
 		return 0;
 
-	head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   RESOLVE_REF_READING, &oid, NULL);
 	if (!head) {
 		warning(_("remote HEAD refers to nonexistent ref, "
 			  "unable to checkout"));
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a714f..e63fa8d84e 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -638,7 +638,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 	}
 
 	hashmap_init(&names, commit_name_neq, NULL, 0);
-	for_each_rawref(get_name, NULL);
+	refs_for_each_rawref(get_main_ref_store(the_repository), get_name,
+			     NULL);
 	if (!hashmap_get_size(&names) && !always)
 		die(_("No names found, cannot describe anything."));
 
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..184cfa9f57 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1604,10 +1604,11 @@ static int update_branch(struct branch *b)
 
 	if (is_null_oid(&b->oid)) {
 		if (b->delete)
-			delete_ref(NULL, b->name, NULL, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, b->name, NULL, 0);
 		return 0;
 	}
-	if (read_ref(b->name, &old_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), b->name, &old_oid))
 		oidclr(&old_oid);
 	if (!force_update && !is_null_oid(&old_oid)) {
 		struct commit *old_cmit, *new_cmit;
@@ -1631,7 +1632,8 @@ static int update_branch(struct branch *b)
 			return -1;
 		}
 	}
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
 				   0, msg, &err) ||
@@ -1665,7 +1667,8 @@ static void dump_tags(void)
 	struct strbuf err = STRBUF_INIT;
 	struct ref_transaction *transaction;
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction) {
 		failure |= error("%s", err.buf);
 		goto cleanup;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..3829d66b40 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -340,7 +340,8 @@ static void find_non_local_tags(const struct ref *refs,
 	refname_hash_init(&remote_refs);
 	create_fetch_oidset(head, &fetch_oids);
 
-	for_each_ref(add_one_refname, &existing_refs);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_one_refname,
+			  &existing_refs);
 
 	/*
 	 * If we already have a transaction, then we need to filter out all
@@ -614,7 +615,9 @@ static struct ref *get_ref_map(struct remote *remote,
 
 			if (!existing_refs_populated) {
 				refname_hash_init(&existing_refs);
-				for_each_ref(add_one_refname, &existing_refs);
+				refs_for_each_ref(get_main_ref_store(the_repository),
+						  add_one_refname,
+						  &existing_refs);
 				existing_refs_populated = 1;
 			}
 
@@ -659,7 +662,8 @@ static int s_update_ref(const char *action,
 	 * lifecycle.
 	 */
 	if (!transaction) {
-		transaction = our_transaction = ref_transaction_begin(&err);
+		transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+									    &err);
 		if (!transaction) {
 			ret = STORE_REF_ERROR_OTHER;
 			goto out;
@@ -1393,7 +1397,9 @@ static int prune_refs(struct display_state *display_state,
 			for (ref = stale_refs; ref; ref = ref->next)
 				string_list_append(&refnames, ref->name);
 
-			result = delete_refs("fetch: prune", &refnames, 0);
+			result = refs_delete_refs(get_main_ref_store(the_repository),
+						  "fetch: prune", &refnames,
+						  0);
 			string_list_clear(&refnames, 0);
 		}
 	}
@@ -1479,7 +1485,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
 			continue;
 		}
 		old_nr = oids->nr;
-		for_each_glob_ref(add_oid, s, oids);
+		refs_for_each_glob_ref(get_main_ref_store(the_repository),
+				       add_oid, s, oids);
 		if (old_nr == oids->nr)
 			warning("ignoring --negotiation-tip=%s because it does not match any refs",
 				s);
@@ -1655,7 +1662,8 @@ static int do_fetch(struct transport *transport,
 			   config->display_format);
 
 	if (atomic_fetch) {
-		transaction = ref_transaction_begin(&err);
+		transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+							  &err);
 		if (!transaction) {
 			retcode = -1;
 			goto cleanup;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f892487c9b..d13a226c2e 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -514,7 +514,9 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
 	struct strbuf refname = STRBUF_INIT;
 
 	strbuf_worktree_ref(cb_data, &refname, logname);
-	for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+				 refname.buf, fsck_handle_reflog_ent,
+				 refname.buf);
 	strbuf_release(&refname);
 	return 0;
 }
@@ -563,7 +565,8 @@ static void get_default_heads(void)
 	const char *head_points_at;
 	struct object_id head_oid;
 
-	for_each_rawref(fsck_handle_ref, NULL);
+	refs_for_each_rawref(get_main_ref_store(the_repository),
+			     fsck_handle_ref, NULL);
 
 	worktrees = get_worktrees();
 	for (p = worktrees; *p; p++) {
@@ -712,7 +715,9 @@ static int fsck_head_link(const char *head_ref_name,
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
 
-	*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+	*head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  head_ref_name, 0, head_oid,
+						  NULL);
 	if (!*head_points_at) {
 		errors_found |= ERROR_REFS;
 		return error(_("invalid %s"), head_ref_name);
diff --git a/builtin/gc.c b/builtin/gc.c
index d3b5ca9bb1..054fca7835 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -907,7 +907,8 @@ static int should_write_commit_graph(void)
 	if (data.limit < 0)
 		return 1;
 
-	result = for_each_ref(dfs_on_ref, &data);
+	result = refs_for_each_ref(get_main_ref_store(the_repository),
+				   dfs_on_ref, &data);
 
 	repo_clear_commit_marks(the_repository, SEEN);
 
diff --git a/builtin/log.c b/builtin/log.c
index 4da7399905..b17dd8b40a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -2226,8 +2226,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
 		if (check_head) {
 			const char *ref, *v;
-			ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-						 NULL, NULL);
+			ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						      "HEAD",
+						      RESOLVE_REF_READING,
+						      NULL, NULL);
 			if (ref && skip_prefix(ref, "refs/heads/", &v))
 				branch_name = xstrdup(v);
 			else
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec87fc..c9af4cab6c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -449,8 +449,10 @@ static void finish(struct commit *head_commit,
 		if (verbosity >= 0 && !merge_msg.len)
 			printf(_("No merge message -- not updating HEAD\n"));
 		else {
-			update_ref(reflog_message.buf, "HEAD", new_head, head,
-				   0, UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					reflog_message.buf, "HEAD", new_head,
+					head,
+					0, UPDATE_REFS_DIE_ON_ERR);
 			/*
 			 * We ignore errors in 'gc --auto', since the
 			 * user should see them.
@@ -547,7 +549,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
 		struct strbuf truname = STRBUF_INIT;
 		strbuf_addf(&truname, "refs/heads/%s", remote);
 		strbuf_setlen(&truname, truname.len - len);
-		if (ref_exists(truname.buf)) {
+		if (refs_ref_exists(get_main_ref_store(the_repository), truname.buf)) {
 			strbuf_addf(msg,
 				    "%s\t\tbranch '%s'%s of .\n",
 				    oid_to_hex(&remote_head->object.oid),
@@ -1252,7 +1254,7 @@ static int merging_a_throwaway_tag(struct commit *commit)
 	 */
 	tag_ref = xstrfmt("refs/tags/%s",
 			  ((struct tag *)merge_remote_util(commit)->obj)->tag);
-	if (!read_ref(tag_ref, &oid) &&
+	if (!refs_read_ref(get_main_ref_store(the_repository), tag_ref, &oid) &&
 	    oideq(&oid, &merge_remote_util(commit)->obj->oid))
 		is_throwaway_tag = 0;
 	else
@@ -1284,7 +1286,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
 	 * current branch.
 	 */
-	branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	branch = branch_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+						      "HEAD", 0, &head_oid,
+						      NULL);
 	if (branch)
 		skip_prefix(branch, "refs/heads/", &branch);
 
@@ -1325,8 +1329,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
-		if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
-			delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
+		if (!refs_read_ref(get_main_ref_store(the_repository), "MERGE_AUTOSTASH", &stash_oid))
+			refs_delete_ref(get_main_ref_store(the_repository),
+					"", "MERGE_AUTOSTASH", &stash_oid,
+					REF_NO_DEREF);
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
@@ -1379,7 +1385,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		else
 			die(_("You have not concluded your merge (MERGE_HEAD exists)."));
 	}
-	if (ref_exists("CHERRY_PICK_HEAD")) {
+	if (refs_ref_exists(get_main_ref_store(the_repository), "CHERRY_PICK_HEAD")) {
 		if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 			    "Please, commit your changes before you merge."));
@@ -1450,8 +1456,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		remote_head_oid = &remoteheads->item->object.oid;
 		read_empty(remote_head_oid);
-		update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"initial pull", "HEAD", remote_head_oid, NULL,
+				0,
+				UPDATE_REFS_DIE_ON_ERR);
 		goto done;
 	}
 
@@ -1531,8 +1539,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		free(list);
 	}
 
-	update_ref("updating ORIG_HEAD", "ORIG_HEAD",
-		   &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository),
+			"updating ORIG_HEAD", "ORIG_HEAD",
+			&head_commit->object.oid, NULL, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (remoteheads && !common) {
 		/* No common ancestors found. */
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index ad9930c831..70e9ec4e47 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -296,7 +296,8 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
 	char *short_refname = NULL;
 
 	if (shorten_unambiguous)
-		short_refname = shorten_unambiguous_ref(refname, 0);
+		short_refname = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+							     refname, 0);
 	else if (skip_prefix(refname, "refs/heads/", &refname))
 		; /* refname already advanced */
 	else
@@ -647,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 
 	adjust_cutoff_timestamp_for_slop();
 
-	for_each_ref(name_ref, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository), name_ref, &data);
 	name_tips(&string_pool);
 
 	if (annotate_stdin) {
diff --git a/builtin/notes.c b/builtin/notes.c
index cb011303e6..a5ce90d9f9 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -794,9 +794,9 @@ static int merge_abort(struct notes_merge_options *o)
 	 * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
 	 */
 
-	if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
 		ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
-	if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
 		ret += error(_("failed to delete ref NOTES_MERGE_REF"));
 	if (notes_merge_abort(o))
 		ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -834,7 +834,8 @@ static int merge_commit(struct notes_merge_options *o)
 	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
 	o->local_ref = local_ref_to_free =
-		resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
+		refs_resolve_refdup(get_main_ref_store(the_repository),
+				    "NOTES_MERGE_REF", 0, &oid, NULL);
 	if (!o->local_ref)
 		die(_("failed to resolve NOTES_MERGE_REF"));
 
@@ -847,9 +848,10 @@ static int merge_commit(struct notes_merge_options *o)
 				   &pretty_ctx);
 	strbuf_trim(&msg);
 	strbuf_insertstr(&msg, 0, "notes: ");
-	update_ref(msg.buf, o->local_ref, &oid,
-		   is_null_oid(&parent_oid) ? NULL : &parent_oid,
-		   0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+			o->local_ref, &oid,
+			is_null_oid(&parent_oid) ? NULL : &parent_oid,
+			0, UPDATE_REFS_DIE_ON_ERR);
 
 	free_notes(t);
 	strbuf_release(&msg);
@@ -961,14 +963,16 @@ static int merge(int argc, const char **argv, const char *prefix)
 
 	if (result >= 0) /* Merge resulted (trivially) in result_oid */
 		/* Update default notes ref with new commit */
-		update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				default_notes_ref(), &result_oid, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
 		struct worktree **worktrees;
 		const struct worktree *wt;
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
-		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
-			   0, UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"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();
 		wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
@@ -977,7 +981,7 @@ static int merge(int argc, const char **argv, const char *prefix)
 			die(_("a notes merge into %s is already in-progress at %s"),
 			    default_notes_ref(), wt->path);
 		free_worktrees(worktrees);
-		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+		if (refs_create_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
 			die(_("failed to store link to current notes ref (%s)"),
 			    default_notes_ref());
 		fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index baf0090fc8..cd2396896d 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -939,7 +939,8 @@ static struct object_entry **compute_write_order(void)
 	/*
 	 * Mark objects that are at the tip of tags.
 	 */
-	for_each_tag_ref(mark_tagged, NULL);
+	refs_for_each_tag_ref(get_main_ref_store(the_repository), mark_tagged,
+			      NULL);
 
 	if (use_delta_islands) {
 		max_layers = compute_pack_layers(&to_pack);
@@ -4093,7 +4094,9 @@ static void mark_bitmap_preferred_tips(void)
 		return;
 
 	for_each_string_list_item(item, preferred_tips) {
-		for_each_ref_in(item->string, mark_bitmap_preferred_tip, NULL);
+		refs_for_each_ref_in(get_main_ref_store(the_repository),
+				     item->string, mark_bitmap_preferred_tip,
+				     NULL);
 	}
 }
 
@@ -4588,7 +4591,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
-		for_each_tag_ref(add_ref_tag, NULL);
+		refs_for_each_tag_ref(get_main_ref_store(the_repository),
+				      add_ref_tag, NULL);
 	stop_progress(&progress_state);
 	trace2_region_leave("pack-objects", "enumerate-objects",
 			    the_repository);
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76d52..26a0806969 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -611,7 +611,7 @@ static int pull_into_void(const struct object_id *merge_head,
 				  merge_head, 0))
 		return 1;
 
-	if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+	if (refs_update_ref(get_main_ref_store(the_repository), "initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
 		return 1;
 
 	return 0;
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 891f28468e..4399896d90 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -252,7 +252,7 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
 	if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
 		return error_errno(_("could not create temporary %s"), merge_dir());
 
-	delete_reflog("REBASE_HEAD");
+	refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD");
 
 	interactive = fopen(path_interactive(), "w");
 	if (!interactive)
@@ -514,8 +514,10 @@ static int finish_rebase(struct rebase_options *opts)
 	struct strbuf dir = STRBUF_INIT;
 	int ret = 0;
 
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"AUTO_MERGE", NULL, REF_NO_DEREF);
 	apply_autostash(state_dir_path("autostash", opts));
 	/*
 	 * We ignore errors in 'git maintenance run --auto', since the
@@ -1623,7 +1625,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		/* Is it a local branch? */
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "refs/heads/%s", branch_name);
-		if (!read_ref(buf.buf, &branch_oid)) {
+		if (!refs_read_ref(get_main_ref_store(the_repository), buf.buf, &branch_oid)) {
 			die_if_checked_out(buf.buf, 1);
 			options.head_name = xstrdup(buf.buf);
 			options.orig_head =
@@ -1640,8 +1642,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	} else if (argc == 0) {
 		/* Do not need to switch branches, we are already on it. */
 		options.head_name =
-			xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
-					 &flags));
+			xstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL,
+								&flags));
 		if (!options.head_name)
 			die(_("No such ref: %s"), "HEAD");
 		if (flags & REF_ISSYMREF) {
@@ -1735,7 +1737,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			if (!(options.flags & REBASE_NO_QUIET))
 				; /* be quiet */
 			else if (!strcmp(branch_name, "HEAD") &&
-				 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+				 refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
 				puts(_("HEAD is up to date."));
 			else
 				printf(_("Current branch %s is up to date.\n"),
@@ -1745,7 +1747,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		} else if (!(options.flags & REBASE_NO_QUIET))
 			; /* be quiet */
 		else if (!strcmp(branch_name, "HEAD") &&
-			 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+			 refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
 			puts(_("HEAD is up to date, rebase forced."));
 		else
 			printf(_("Current branch %s is up to date, rebase "
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..dd0e22d729 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1566,7 +1566,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		struct strbuf err = STRBUF_INIT;
 		if (!parse_object(the_repository, old_oid)) {
 			old_oid = NULL;
-			if (ref_exists(name)) {
+			if (refs_ref_exists(get_main_ref_store(the_repository), name)) {
 				rp_warning("allowing deletion of corrupt ref");
 			} else {
 				rp_warning("deleting a non-existent ref");
@@ -1693,7 +1693,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
 	int flag;
 
 	strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
-	dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
+	dst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					   buf.buf, 0, NULL, &flag);
 	check_aliased_update_internal(cmd, list, dst_name, flag);
 	strbuf_release(&buf);
 }
@@ -1829,7 +1830,8 @@ static void execute_commands_non_atomic(struct command *commands,
 		if (!should_process_cmd(cmd) || cmd->run_proc_receive)
 			continue;
 
-		transaction = ref_transaction_begin(&err);
+		transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+							  &err);
 		if (!transaction) {
 			rp_error("%s", err.buf);
 			strbuf_reset(&err);
@@ -1857,7 +1859,8 @@ static void execute_commands_atomic(struct command *commands,
 	struct strbuf err = STRBUF_INIT;
 	const char *reported_error = "atomic push failure";
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction) {
 		rp_error("%s", err.buf);
 		strbuf_reset(&err);
@@ -1983,7 +1986,9 @@ static void execute_commands(struct command *commands,
 	check_aliased_updates(commands);
 
 	free(head_name_to_free);
-	head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+	head_name = head_name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+							    "HEAD", 0, NULL,
+							    NULL);
 
 	if (run_proc_receive &&
 	    run_proc_receive_hook(commands, push_options))
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 060eb3377e..b4650cea16 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -364,11 +364,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 			};
 
 			set_reflog_expiry_param(&cb.cmd,  item->string);
-			status |= reflog_expire(item->string, flags,
-						reflog_expiry_prepare,
-						should_prune_fn,
-						reflog_expiry_cleanup,
-						&cb);
+			status |= refs_reflog_expire(get_main_ref_store(the_repository),
+						     item->string, flags,
+						     reflog_expiry_prepare,
+						     should_prune_fn,
+						     reflog_expiry_cleanup,
+						     &cb);
 		}
 		string_list_clear(&collected.reflogs, 0);
 	}
@@ -382,11 +383,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 			continue;
 		}
 		set_reflog_expiry_param(&cb.cmd, ref);
-		status |= reflog_expire(ref, flags,
-					reflog_expiry_prepare,
-					should_prune_fn,
-					reflog_expiry_cleanup,
-					&cb);
+		status |= refs_reflog_expire(get_main_ref_store(the_repository),
+					     ref, flags,
+					     reflog_expiry_prepare,
+					     should_prune_fn,
+					     reflog_expiry_cleanup,
+					     &cb);
 		free(ref);
 	}
 	return status;
@@ -437,7 +439,8 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
 	refname = argv[0];
 	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 		die(_("invalid ref format: %s"), refname);
-	return !reflog_exists(refname);
+	return !refs_reflog_exists(get_main_ref_store(the_repository),
+				   refname);
 }
 
 /*
diff --git a/builtin/remote.c b/builtin/remote.c
index 8412d12fa5..ff70d6835a 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -240,7 +240,7 @@ static int add(int argc, const char **argv, const char *prefix)
 		strbuf_reset(&buf2);
 		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
 
-		if (create_symref(buf.buf, buf2.buf, "remote add"))
+		if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
 			return error(_("Could not setup master '%s'"), master);
 	}
 
@@ -376,7 +376,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
 	for (ref = fetch_map; ref; ref = ref->next) {
 		if (omit_name_by_refspec(ref->name, &states->remote->fetch))
 			string_list_append(&states->skipped, abbrev_branch(ref->name));
-		else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+		else if (!ref->peer_ref || !refs_ref_exists(get_main_ref_store(the_repository), ref->peer_ref->name))
 			string_list_append(&states->new_refs, abbrev_branch(ref->name));
 		else
 			string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -598,8 +598,9 @@ static int read_remote_branches(const char *refname,
 	strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
 	if (starts_with(refname, buf.buf)) {
 		item = string_list_append(rename->remote_branches, refname);
-		symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
-					    NULL, &flag);
+		symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						 refname, RESOLVE_REF_READING,
+						 NULL, &flag);
 		if (symref && (flag & REF_ISSYMREF)) {
 			item->util = xstrdup(symref);
 			rename->symrefs_nr++;
@@ -789,7 +790,8 @@ static int mv(int argc, const char **argv, const char *prefix)
 	 * First remove symrefs, then rename the rest, finally create
 	 * the new symrefs.
 	 */
-	for_each_ref(read_remote_branches, &rename);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  read_remote_branches, &rename);
 	if (show_progress) {
 		/*
 		 * Count symrefs twice, since "renaming" them is done by
@@ -805,7 +807,7 @@ static int mv(int argc, const char **argv, const char *prefix)
 		if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
 					   &referent))
 			continue;
-		if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
+		if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF))
 			die(_("deleting '%s' failed"), item->string);
 
 		strbuf_release(&referent);
@@ -823,7 +825,7 @@ static int mv(int argc, const char **argv, const char *prefix)
 		strbuf_reset(&buf2);
 		strbuf_addf(&buf2, "remote: renamed %s to %s",
 				item->string, buf.buf);
-		if (rename_ref(item->string, buf.buf, buf2.buf))
+		if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf))
 			die(_("renaming '%s' failed"), item->string);
 		display_progress(progress, ++refs_renamed_nr);
 	}
@@ -843,7 +845,7 @@ static int mv(int argc, const char **argv, const char *prefix)
 		strbuf_reset(&buf3);
 		strbuf_addf(&buf3, "remote: renamed %s to %s",
 				item->string, buf.buf);
-		if (create_symref(buf.buf, buf2.buf, buf3.buf))
+		if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
 			die(_("creating '%s' failed"), buf.buf);
 		display_progress(progress, ++refs_renamed_nr);
 	}
@@ -917,11 +919,14 @@ static int rm(int argc, const char **argv, const char *prefix)
 	 * refs, which are invalidated when deleting a branch.
 	 */
 	cb_data.remote = remote;
-	result = for_each_ref(add_branch_for_removal, &cb_data);
+	result = refs_for_each_ref(get_main_ref_store(the_repository),
+				   add_branch_for_removal, &cb_data);
 	strbuf_release(&buf);
 
 	if (!result)
-		result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
+		result = refs_delete_refs(get_main_ref_store(the_repository),
+					  "remote: remove", &branches,
+					  REF_NO_DEREF);
 	string_list_clear(&branches, 0);
 
 	if (skipped.nr) {
@@ -1010,7 +1015,8 @@ static int get_remote_ref_states(const char *name,
 			get_push_ref_states(remote_refs, states);
 		transport_disconnect(transport);
 	} else {
-		for_each_ref(append_ref_to_tracked_list, states);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_ref_to_tracked_list, states);
 		string_list_sort(&states->tracked);
 		get_push_ref_states_noquery(states);
 	}
@@ -1407,7 +1413,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
 			head_name = xstrdup(states.heads.items[0].string);
 		free_remote_ref_states(&states);
 	} else if (opt_d && !opt_a && argc == 1) {
-		if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
+		if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
 			result |= error(_("Could not delete %s"), buf.buf);
 	} else
 		usage_with_options(builtin_remote_sethead_usage, options);
@@ -1415,9 +1421,9 @@ static int set_head(int argc, const char **argv, const char *prefix)
 	if (head_name) {
 		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
 		/* make sure it's valid */
-		if (!ref_exists(buf2.buf))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
 			result |= error(_("Not a valid ref: %s"), buf2.buf);
-		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+		else if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
 			result |= error(_("Could not setup %s"), buf.buf);
 		else if (opt_a)
 			printf("%s/HEAD set to %s\n", argv[0], head_name);
@@ -1457,7 +1463,8 @@ static int prune_remote(const char *remote, int dry_run)
 	string_list_sort(&refs_to_prune);
 
 	if (!dry_run)
-		result |= delete_refs("remote: prune", &refs_to_prune, 0);
+		result |= refs_delete_refs(get_main_ref_store(the_repository),
+					   "remote: prune", &refs_to_prune, 0);
 
 	for_each_string_list_item(item, &states.stale) {
 		const char *refname = item->util;
diff --git a/builtin/repack.c b/builtin/repack.c
index 15e4cccc45..43491a4cbf 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -706,11 +706,14 @@ static void midx_snapshot_refs(struct tempfile *f)
 
 		data.preferred = 1;
 		for_each_string_list_item(item, preferred)
-			for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+			refs_for_each_ref_in(get_main_ref_store(the_repository),
+					     item->string,
+					     midx_snapshot_ref_one, &data);
 		data.preferred = 0;
 	}
 
-	for_each_ref(midx_snapshot_ref_one, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  midx_snapshot_ref_one, &data);
 
 	if (close_tempfile_gently(f)) {
 		int save_errno = errno;
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..bc2a948c80 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -130,7 +130,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
 		strbuf_addstr(&ref, oid_to_hex(&oid));
 		full_hex = ref.buf + base_len;
 
-		if (read_ref(ref.buf, &oid)) {
+		if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
 			error(_("replace ref '%s' not found"), full_hex);
 			had_error = 1;
 			continue;
@@ -145,7 +145,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
 static int delete_replace_ref(const char *name, const char *ref,
 			      const struct object_id *oid)
 {
-	if (delete_ref(NULL, ref, oid, 0))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, ref, oid, 0))
 		return 1;
 	printf_ln(_("Deleted replace ref '%s'"), name);
 	return 0;
@@ -163,7 +163,7 @@ static int check_ref_valid(struct object_id *object,
 	if (check_refname_format(ref->buf, 0))
 		return error(_("'%s' is not a valid ref name"), ref->buf);
 
-	if (read_ref(ref->buf, prev))
+	if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev))
 		oidclr(prev);
 	else if (!force)
 		return error(_("replace ref '%s' already exists"), ref->buf);
@@ -198,7 +198,8 @@ static int replace_object_oid(const char *object_ref,
 		return -1;
 	}
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
 				   0, NULL, &err) ||
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6332..bf23fe78fa 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -307,13 +307,16 @@ static int reset_refs(const char *rev, const struct object_id *oid)
 	if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
 		orig = &oid_orig;
 		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
-			   UPDATE_REFS_MSG_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"ORIG_HEAD", orig, old_orig, 0,
+				UPDATE_REFS_MSG_ON_ERR);
 	} else if (old_orig)
-		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+		refs_delete_ref(get_main_ref_store(the_repository), NULL,
+				"ORIG_HEAD", old_orig, 0);
 	set_reflog_message(&msg, "updating HEAD", rev);
-	update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
-				       UPDATE_REFS_MSG_ON_ERR);
+	update_ref_status = refs_update_ref(get_main_ref_store(the_repository),
+					    msg.buf, "HEAD", oid, orig, 0,
+					    UPDATE_REFS_MSG_ON_ERR);
 	strbuf_release(&msg);
 	return update_ref_status;
 }
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 2b28d43939..5f79ec6338 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -160,8 +160,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
 			case 1: /* happy */
 				if (abbrev_ref) {
 					char *old = full;
-					full = shorten_unambiguous_ref(full,
-						abbrev_ref_strict);
+					full = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+									    full,
+									    abbrev_ref_strict);
 					free(old);
 				}
 				show_with_type(type, full);
@@ -599,9 +600,12 @@ static int opt_with_value(const char *arg, const char *opt, const char **value)
 static void handle_ref_opt(const char *pattern, const char *prefix)
 {
 	if (pattern)
-		for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+		refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+					  show_reference, pattern, prefix,
+					  NULL);
 	else
-		for_each_ref_in(prefix, show_reference, NULL);
+		refs_for_each_ref_in(get_main_ref_store(the_repository),
+				     prefix, show_reference, NULL);
 	clear_ref_exclusions(&ref_excludes);
 }
 
@@ -898,7 +902,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (!strcmp(arg, "--all")) {
-				for_each_ref(show_reference, NULL);
+				refs_for_each_ref(get_main_ref_store(the_repository),
+						  show_reference, NULL);
 				clear_ref_exclusions(&ref_excludes);
 				continue;
 			}
@@ -908,8 +913,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (!strcmp(arg, "--bisect")) {
-				for_each_fullref_in("refs/bisect/bad", NULL, show_reference, NULL);
-				for_each_fullref_in("refs/bisect/good", NULL, anti_reference, NULL);
+				refs_for_each_fullref_in(get_main_ref_store(the_repository),
+							 "refs/bisect/bad",
+							 NULL, show_reference,
+							 NULL);
+				refs_for_each_fullref_in(get_main_ref_store(the_repository),
+							 "refs/bisect/good",
+							 NULL, anti_reference,
+							 NULL);
 				continue;
 			}
 			if (opt_with_value(arg, "--branches", &arg)) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index b01ec761d2..d72f4cb98d 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -479,13 +479,15 @@ static void snarf_refs(int head, int remotes)
 	if (head) {
 		int orig_cnt = ref_name_cnt;
 
-		for_each_ref(append_head_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_head_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 	if (remotes) {
 		int orig_cnt = ref_name_cnt;
 
-		for_each_ref(append_remote_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_remote_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 }
@@ -549,7 +551,8 @@ static void append_one_rev(const char *av)
 
 		match_ref_pattern = av;
 		match_ref_slash = count_slashes(av);
-		for_each_ref(append_matching_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_matching_ref, NULL);
 		if (saved_matches == ref_name_cnt &&
 		    ref_name_cnt < MAX_REVS)
 			error(_("no matching refs with %s"), av);
@@ -740,9 +743,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 		if (ac == 0) {
 			static const char *fake_av[2];
 
-			fake_av[0] = resolve_refdup("HEAD",
-						    RESOLVE_REF_READING, &oid,
-						    NULL);
+			fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository),
+							 "HEAD",
+							 RESOLVE_REF_READING,
+							 &oid,
+							 NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
@@ -815,8 +820,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			snarf_refs(all_heads, all_remotes);
 	}
 
-	head = resolve_refdup("HEAD", RESOLVE_REF_READING,
-			      &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   RESOLVE_REF_READING,
+				   &head_oid, NULL);
 
 	if (with_current_branch && head) {
 		int has_head = 0;
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 3c521dbfd4..151ef35134 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -129,7 +129,8 @@ static int cmd_show_ref__exclude_existing(const struct exclude_existing_options
 	char buf[1024];
 	int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
 
-	for_each_ref(add_existing, &existing_refs);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_existing,
+			  &existing_refs);
 	while (fgets(buf, sizeof(buf), stdin)) {
 		char *ref;
 		int len = strlen(buf);
@@ -173,7 +174,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
 		struct object_id oid;
 
 		if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
-		    !read_ref(*refs, &oid)) {
+		    !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
 			show_one(show_one_opts, *refs, &oid);
 		}
 		else if (!show_one_opts->quiet)
@@ -205,14 +206,20 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts,
 		show_ref_data.patterns = patterns;
 
 	if (opts->show_head)
-		head_ref(show_ref, &show_ref_data);
+		refs_head_ref(get_main_ref_store(the_repository), show_ref,
+			      &show_ref_data);
 	if (opts->heads_only || opts->tags_only) {
 		if (opts->heads_only)
-			for_each_fullref_in("refs/heads/", NULL, show_ref, &show_ref_data);
+			refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						 "refs/heads/", NULL,
+						 show_ref, &show_ref_data);
 		if (opts->tags_only)
-			for_each_fullref_in("refs/tags/", NULL, show_ref, &show_ref_data);
+			refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						 "refs/tags/", NULL, show_ref,
+						 &show_ref_data);
 	} else {
-		for_each_ref(show_ref, &show_ref_data);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  show_ref, &show_ref_data);
 	}
 	if (!show_ref_data.found_match)
 		return 1;
diff --git a/builtin/stash.c b/builtin/stash.c
index 7751bca868..0a15ce287e 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -196,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		commit = argv[0];
 
 	if (!commit) {
-		if (!ref_exists(ref_stash)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) {
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -244,7 +244,8 @@ static int do_clear_stash(void)
 	if (repo_get_oid(the_repository, ref_stash, &obj))
 		return 0;
 
-	return delete_ref(NULL, ref_stash, &obj, 0);
+	return refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			       ref_stash, &obj, 0);
 }
 
 static int clear_stash(int argc, const char **argv, const char *prefix)
@@ -687,7 +688,8 @@ static int reject_reflog_ent(struct object_id *ooid UNUSED,
 
 static int reflog_is_empty(const char *refname)
 {
-	return !for_each_reflog_ent(refname, reject_reflog_ent, NULL);
+	return !refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+					 refname, reject_reflog_ent, NULL);
 }
 
 static int do_drop_stash(struct stash_info *info, int quiet)
@@ -824,7 +826,7 @@ static int list_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_list_usage,
 			     PARSE_OPT_KEEP_UNKNOWN_OPT);
 
-	if (!ref_exists(ref_stash))
+	if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash))
 		return 0;
 
 	cp.git_cmd = 1;
@@ -998,10 +1000,10 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms
 	if (!stash_msg)
 		stash_msg = "Created via \"git stash store\".";
 
-	if (update_ref(stash_msg, ref_stash, w_commit, NULL,
-		       REF_FORCE_CREATE_REFLOG,
-		       quiet ? UPDATE_REFS_QUIET_ON_ERR :
-		       UPDATE_REFS_MSG_ON_ERR)) {
+	if (refs_update_ref(get_main_ref_store(the_repository), stash_msg, ref_stash, w_commit, NULL,
+			    REF_FORCE_CREATE_REFLOG,
+			    quiet ? UPDATE_REFS_QUIET_ON_ERR :
+			    UPDATE_REFS_MSG_ON_ERR)) {
 		if (!quiet) {
 			fprintf_ln(stderr, _("Cannot update %s with %s"),
 				   ref_stash, oid_to_hex(w_commit));
@@ -1384,7 +1386,8 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 		goto done;
 	}
 
-	branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					     "HEAD", 0, NULL, &flags);
 	if (flags & REF_ISSYMREF)
 		skip_prefix(branch_ref, "refs/heads/", &branch_name);
 	head_short_sha1 = repo_find_unique_abbrev(the_repository,
@@ -1566,7 +1569,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 		goto done;
 	}
 
-	if (!reflog_exists(ref_stash) && do_clear_stash()) {
+	if (!refs_reflog_exists(get_main_ref_store(the_repository), ref_stash) && do_clear_stash()) {
 		ret = -1;
 		if (!quiet)
 			fprintf_ln(stderr, _("Cannot initialize stash"));
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e4e18adb57..4be79eab23 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2390,7 +2390,9 @@ static int remote_submodule_branch(const char *path, const char **branch)
 	}
 
 	if (!strcmp(*branch, ".")) {
-		const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+		const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+							      "HEAD", 0, NULL,
+							      NULL);
 
 		if (!refname)
 			return die_message(_("No such ref: %s"), "HEAD");
@@ -2796,7 +2798,8 @@ static int push_check(int argc, const char **argv, const char *prefix UNUSED)
 	argv++;
 	argc--;
 	/* Get the submodule's head ref and determine if it is detached */
-	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   0, &head_oid, NULL);
 	if (!head)
 		die(_("Failed to resolve HEAD as a valid ref."));
 	if (!strcmp(head, "HEAD"))
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index c9defe4d2e..b2b0a41fb6 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -18,7 +18,8 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
 	const char *refname;
 
 	resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
-	refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
+	refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					  HEAD, resolve_flags, NULL, &flag);
 
 	if (!refname)
 		die("No such ref: %s", HEAD);
@@ -31,7 +32,9 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i
 	if (print) {
 		char *to_free = NULL;
 		if (shorten)
-			refname = to_free = shorten_unambiguous_ref(refname, 0);
+			refname = to_free = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+									 refname,
+									 0);
 		puts(refname);
 		free(to_free);
 	}
@@ -66,7 +69,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
 			die("Cannot delete %s, not a symbolic ref", argv[0]);
 		if (!strcmp(argv[0], "HEAD"))
 			die("deleting '%s' is not allowed", argv[0]);
-		return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
+		return refs_delete_ref(get_main_ref_store(the_repository),
+				       NULL, argv[0], NULL, REF_NO_DEREF);
 	}
 
 	switch (argc) {
@@ -79,7 +83,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
 			die("Refusing to point HEAD outside of refs/");
 		if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
 			die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
-		ret = !!create_symref(argv[0], argv[1], msg);
+		ret = !!refs_create_symref(get_main_ref_store(the_repository),
+					   argv[0], argv[1], msg);
 		break;
 	default:
 		usage_with_options(git_symbolic_ref_usage, options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..424a03ad18 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -87,7 +87,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
 	for (p = argv; *p; p++) {
 		strbuf_reset(&ref);
 		strbuf_addf(&ref, "refs/tags/%s", *p);
-		if (read_ref(ref.buf, &oid)) {
+		if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
 			error(_("tag '%s' not found."), *p);
 			had_error = 1;
 			continue;
@@ -116,13 +116,13 @@ static int delete_tags(const char **argv)
 	struct string_list_item *item;
 
 	result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
-	if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+	if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF))
 		result = 1;
 
 	for_each_string_list_item(item, &refs_to_delete) {
 		const char *name = item->string;
 		struct object_id *oid = item->util;
-		if (!ref_exists(name))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), name))
 			printf(_("Deleted tag '%s' (was %s)\n"),
 				item->string + 10,
 				repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
@@ -630,7 +630,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	if (strbuf_check_tag_ref(&ref, tag))
 		die(_("'%s' is not a valid tag name."), tag);
 
-	if (read_ref(ref.buf, &prev))
+	if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev))
 		oidclr(&prev);
 	else if (!force)
 		die(_("tag '%s' already exists"), tag);
@@ -657,7 +657,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			   path);
 	}
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa1476c..9c9c24b831 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -682,7 +682,7 @@ static int do_reupdate(const char **paths,
 		       PATHSPEC_PREFER_CWD,
 		       prefix, paths);
 
-	if (read_ref("HEAD", &head_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &head_oid))
 		/* If there is no HEAD, that means it is an initial
 		 * commit.  Update everything in the index.
 		 */
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..896bac36ab 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -397,7 +397,8 @@ static void update_refs_stdin(void)
 	struct ref_transaction *transaction;
 	int i, j;
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction)
 		die("%s", err.buf);
 
@@ -464,7 +465,8 @@ static void update_refs_stdin(void)
 			 * get a "start".
 			 */
 			state = cmd->state;
-			transaction = ref_transaction_begin(&err);
+			transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+								  &err);
 			if (!transaction)
 				die("%s", err.buf);
 
@@ -571,11 +573,14 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 		 * For purposes of backwards compatibility, we treat
 		 * NULL_SHA1 as "don't care" here:
 		 */
-		return delete_ref(msg, refname,
-				  (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
-				  default_flags);
+		return refs_delete_ref(get_main_ref_store(the_repository),
+				       msg, refname,
+				       (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
+				       default_flags);
 	else
-		return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
-				  default_flags | create_reflog_flag,
-				  UPDATE_REFS_DIE_ON_ERR);
+		return refs_update_ref(get_main_ref_store(the_repository),
+				       msg, refname, &oid,
+				       oldval ? &oldoid : NULL,
+				       default_flags | create_reflog_flag,
+				       UPDATE_REFS_DIE_ON_ERR);
 }
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7c6c72536b..108cfa156a 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -433,7 +433,7 @@ static int add_worktree(const char *path, const char *refname,
 
 	/* is 'refname' a branch or commit? */
 	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
-	    ref_exists(symref.buf)) {
+	    refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
 		is_branch = 1;
 		if (!opts->force)
 			die_if_checked_out(symref.buf, 0);
@@ -605,7 +605,7 @@ static void print_preparing_worktree_line(int detach,
 	} else {
 		struct strbuf s = STRBUF_INIT;
 		if (!detach && !strbuf_check_branch_ref(&s, branch) &&
-		    ref_exists(s.buf))
+		    refs_ref_exists(get_main_ref_store(the_repository), s.buf))
 			fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
 				  branch);
 		else {
@@ -647,9 +647,9 @@ static int first_valid_ref(const char *refname UNUSED,
  */
 static int can_use_local_refs(const struct add_opts *opts)
 {
-	if (head_ref(first_valid_ref, NULL)) {
+	if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		return 1;
-	} else if (for_each_branch_ref(first_valid_ref, NULL)) {
+	} else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		if (!opts->quiet) {
 			struct strbuf path = STRBUF_INIT;
 			struct strbuf contents = STRBUF_INIT;
@@ -689,7 +689,7 @@ static int can_use_remote_refs(const struct add_opts *opts)
 {
 	if (!guess_remote) {
 		return 0;
-	} else if (for_each_remote_ref(first_valid_ref, NULL)) {
+	} else if (refs_for_each_remote_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		return 1;
 	} else if (!opts->force && remote_get(NULL)) {
 		die(_("No local or remote refs exist despite at least one remote\n"
@@ -747,7 +747,8 @@ static const char *dwim_branch(const char *path, const char **new_branch)
 	UNLEAK(branchname);
 
 	branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
-			ref_exists(ref.buf);
+			refs_ref_exists(get_main_ref_store(the_repository),
+					ref.buf);
 	strbuf_release(&ref);
 	if (branch_exists)
 		return branchname;
@@ -838,7 +839,7 @@ static int add(int ac, const char **av, const char *prefix)
 
 		if (!opts.force &&
 		    !strbuf_check_branch_ref(&symref, new_branch) &&
-		    ref_exists(symref.buf))
+		    refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
 			die_if_checked_out(symref.buf, 0);
 		strbuf_release(&symref);
 	}
@@ -974,7 +975,9 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
 		if (wt->is_detached)
 			strbuf_addstr(&sb, "(detached HEAD)");
 		else if (wt->head_ref) {
-			char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+			char *ref = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+								 wt->head_ref,
+								 0);
 			strbuf_addf(&sb, "[%s]", ref);
 			free(ref);
 		} else
diff --git a/bundle-uri.c b/bundle-uri.c
index ca32050a78..91b3319a5c 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -395,11 +395,13 @@ static int unbundle_from_file(struct repository *r, const char *file)
 		strbuf_setlen(&bundle_ref, bundle_prefix_len);
 		strbuf_addstr(&bundle_ref, branch_name);
 
-		has_old = !read_ref(bundle_ref.buf, &old_oid);
-		update_ref("fetched bundle", bundle_ref.buf, oid,
-			   has_old ? &old_oid : NULL,
-			   REF_SKIP_OID_VERIFICATION,
-			   UPDATE_REFS_MSG_ON_ERR);
+		has_old = !refs_read_ref(get_main_ref_store(the_repository),
+					 bundle_ref.buf, &old_oid);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"fetched bundle", bundle_ref.buf, oid,
+				has_old ? &old_oid : NULL,
+				REF_SKIP_OID_VERIFICATION,
+				UPDATE_REFS_MSG_ON_ERR);
 	}
 
 	bundle_header_release(&header);
diff --git a/bundle.c b/bundle.c
index a9744da255..95367c2d0a 100644
--- a/bundle.c
+++ b/bundle.c
@@ -389,7 +389,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 		if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
 				  &oid, &ref, 0) != 1)
 			goto skip_write_ref;
-		if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
+		if (refs_read_ref_full(get_main_ref_store(the_repository), e->name, RESOLVE_REF_READING, &oid, &flag))
 			flag = 0;
 		display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
diff --git a/commit-graph.c b/commit-graph.c
index 45417d7412..c4c156ff52 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1845,7 +1845,8 @@ int write_commit_graph_reachable(struct object_directory *odb,
 		data.progress = start_delayed_progress(
 			_("Collecting referenced commits"), 0);
 
-	for_each_ref(add_ref_to_set, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_ref_to_set,
+			  &data);
 
 	stop_progress(&data.progress);
 
diff --git a/commit.c b/commit.c
index 1a479a997c..1d08951007 100644
--- a/commit.c
+++ b/commit.c
@@ -1070,7 +1070,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
 
 	memset(&revs, 0, sizeof(revs));
 	revs.initial = 1;
-	for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+				 full_refname, collect_one_reflog_ent, &revs);
 
 	if (!revs.nr)
 		add_one_commit(&oid, &revs);
diff --git a/config.c b/config.c
index ae3652b08f..e8f30dfe83 100644
--- a/config.c
+++ b/config.c
@@ -303,7 +303,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
 	int ret;
 	struct strbuf pattern = STRBUF_INIT;
 	const char *refname = !the_repository->gitdir ?
-		NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+		NULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					       "HEAD", 0, NULL, &flags);
 	const char *shortname;
 
 	if (!refname || !(flags & REF_ISSYMREF)	||
diff --git a/delta-islands.c b/delta-islands.c
index f7e079425f..4ac3c10551 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -488,7 +488,8 @@ void load_delta_islands(struct repository *r, int progress)
 
 	git_config(island_config_callback, &ild);
 	ild.remote_islands = kh_init_str();
-	for_each_ref(find_island_for_ref, &ild);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  find_island_for_ref, &ild);
 	free_config_regexes(&ild);
 	deduplicate_islands(ild.remote_islands, r);
 	free_remote_islands(ild.remote_islands);
diff --git a/fetch-pack.c b/fetch-pack.c
index 091f9a80a9..8e8f3bba32 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -290,7 +290,8 @@ static void mark_tips(struct fetch_negotiator *negotiator,
 	int i;
 
 	if (!negotiation_tips) {
-		for_each_rawref(rev_list_insert_ref_oid, negotiator);
+		refs_for_each_rawref(get_main_ref_store(the_repository),
+				     rev_list_insert_ref_oid, negotiator);
 		return;
 	}
 
@@ -793,7 +794,8 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
 	 */
 	trace2_region_enter("fetch-pack", "mark_complete_local_refs", NULL);
 	if (!args->deepen) {
-		for_each_rawref(mark_complete_oid, NULL);
+		refs_for_each_rawref(get_main_ref_store(the_repository),
+				     mark_complete_oid, NULL);
 		for_each_cached_alternate(NULL, mark_alternate_complete);
 		commit_list_sort_by_date(&complete);
 		if (cutoff)
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index ae201e21db..7d144b803a 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -661,7 +661,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
 	/* learn the commit that we merge into and the current branch name */
 	current_branch = current_branch_to_free =
-		resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
+		refs_resolve_refdup(get_main_ref_store(the_repository),
+				    "HEAD", RESOLVE_REF_READING, &head_oid,
+				    NULL);
 	if (!current_branch)
 		die("No current branch");
 
diff --git a/help.c b/help.c
index 2dbe57b413..1d057aa607 100644
--- a/help.c
+++ b/help.c
@@ -800,7 +800,7 @@ static int append_similar_ref(const char *refname,
 	if (starts_with(refname, "refs/remotes/") &&
 	    !strcmp(branch, cb->base_ref))
 		string_list_append_nodup(cb->similar_refs,
-					 shorten_unambiguous_ref(refname, 1));
+					 refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), refname, 1));
 	return 0;
 }
 
@@ -811,7 +811,8 @@ static struct string_list guess_refs(const char *ref)
 
 	ref_cb.base_ref = ref;
 	ref_cb.similar_refs = &similar_refs;
-	for_each_ref(append_similar_ref, &ref_cb);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  append_similar_ref, &ref_cb);
 	return similar_refs;
 }
 
diff --git a/http-backend.c b/http-backend.c
index 1ed1e29d07..5b65287ac9 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -559,7 +559,8 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
 
 	} else {
 		select_getanyfile(hdr);
-		for_each_namespaced_ref(NULL, show_text_ref, &buf);
+		refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+					     NULL, show_text_ref, &buf);
 		send_strbuf(hdr, "text/plain", &buf);
 	}
 	strbuf_release(&buf);
@@ -571,9 +572,10 @@ static int show_head_ref(const char *refname, const struct object_id *oid,
 	struct strbuf *buf = cb_data;
 
 	if (flag & REF_ISSYMREF) {
-		const char *target = resolve_ref_unsafe(refname,
-							RESOLVE_REF_READING,
-							NULL, NULL);
+		const char *target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+							     refname,
+							     RESOLVE_REF_READING,
+							     NULL, NULL);
 
 		if (target)
 			strbuf_addf(buf, "ref: %s\n", strip_namespace(target));
@@ -589,7 +591,8 @@ static void get_head(struct strbuf *hdr, char *arg UNUSED)
 	struct strbuf buf = STRBUF_INIT;
 
 	select_getanyfile(hdr);
-	head_ref_namespaced(show_head_ref, &buf);
+	refs_head_ref_namespaced(get_main_ref_store(the_repository),
+				 show_head_ref, &buf);
 	send_strbuf(hdr, "text/plain", &buf);
 	strbuf_release(&buf);
 }
diff --git a/log-tree.c b/log-tree.c
index 16031b44e7..41416de4e3 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -232,8 +232,10 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
 		}
 		decoration_loaded = 1;
 		decoration_flags = flags;
-		for_each_ref(add_ref_decoration, filter);
-		head_ref(add_ref_decoration, filter);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  add_ref_decoration, filter);
+		refs_head_ref(get_main_ref_store(the_repository),
+			      add_ref_decoration, filter);
 		for_each_commit_graft(add_graft_decoration, filter);
 	}
 }
@@ -277,7 +279,8 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
 		return NULL;
 
 	/* Now resolve and find the matching current branch */
-	branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
+	branch_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					      "HEAD", 0, NULL, &rru_flags);
 	if (!branch_name || !(rru_flags & REF_ISSYMREF))
 		return NULL;
 
diff --git a/ls-refs.c b/ls-refs.c
index 819cbefee3..8e3ffff811 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -95,9 +95,11 @@ static int send_ref(const char *refname, const struct object_id *oid,
 		strbuf_addf(&data->buf, "unborn %s", refname_nons);
 	if (data->symrefs && flag & REF_ISSYMREF) {
 		struct object_id unused;
-		const char *symref_target = resolve_ref_unsafe(refname, 0,
-							       &unused,
-							       &flag);
+		const char *symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+								    refname,
+								    0,
+								    &unused,
+								    &flag);
 
 		if (!symref_target)
 			die("'%s' is a symref but it is not?", refname);
@@ -126,7 +128,7 @@ static void send_possibly_unborn_head(struct ls_refs_data *data)
 	int oid_is_null;
 
 	strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
-	if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+	if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), namespaced.buf, 0, &oid, &flag))
 		return; /* bad ref */
 	oid_is_null = is_null_oid(&oid);
 	if (!oid_is_null ||
diff --git a/midx-write.c b/midx-write.c
index 65e69d2de7..9d096d5a28 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -755,7 +755,8 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 		read_refs_snapshot(refs_snapshot, &revs);
 	} else {
 		setup_revisions(0, NULL, &revs, NULL);
-		for_each_ref(add_ref_to_pending, &revs);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  add_ref_to_pending, &revs);
 	}
 
 	/*
diff --git a/negotiator/default.c b/negotiator/default.c
index 9a5b696327..518b3c43b2 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -192,6 +192,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
 	ns->rev_list.compare = compare_commits_by_commit_date;
 
 	if (marked)
-		for_each_ref(clear_marks, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  clear_marks, NULL);
 	marked = 1;
 }
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 5b91520430..b7e008c2fd 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -261,6 +261,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
 	data->rev_list.compare = compare;
 
 	if (marked)
-		for_each_ref(clear_marks, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  clear_marks, NULL);
 	marked = 1;
 }
diff --git a/notes-cache.c b/notes-cache.c
index 0e1d5b1ac7..038db01ca0 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -17,7 +17,7 @@ static int notes_cache_match_validity(struct repository *r,
 	struct strbuf msg = STRBUF_INIT;
 	int ret;
 
-	if (read_ref(ref, &oid) < 0)
+	if (refs_read_ref(get_main_ref_store(the_repository), ref, &oid) < 0)
 		return 0;
 
 	commit = lookup_commit_reference_gently(r, &oid, 1);
@@ -66,8 +66,8 @@ int notes_cache_write(struct notes_cache *c)
 	if (commit_tree(c->validity, strlen(c->validity), &tree_oid, NULL,
 			&commit_oid, NULL, NULL) < 0)
 		return -1;
-	if (update_ref("update notes cache", c->tree.update_ref, &commit_oid,
-		       NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
+	if (refs_update_ref(get_main_ref_store(the_repository), "update notes cache", c->tree.update_ref, &commit_oid,
+			    NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
 		return -1;
 
 	return 0;
diff --git a/notes-merge.c b/notes-merge.c
index 51282934ae..6a9a139b12 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -562,7 +562,7 @@ int notes_merge(struct notes_merge_options *o,
 	       o->local_ref, o->remote_ref);
 
 	/* Dereference o->local_ref into local_sha1 */
-	if (read_ref_full(o->local_ref, 0, &local_oid, NULL))
+	if (refs_read_ref_full(get_main_ref_store(the_repository), o->local_ref, 0, &local_oid, NULL))
 		die("Failed to resolve local notes ref '%s'", o->local_ref);
 	else if (!check_refname_format(o->local_ref, 0) &&
 		is_null_oid(&local_oid))
diff --git a/notes-utils.c b/notes-utils.c
index 6197a5a455..e33aa86c4b 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -23,7 +23,7 @@ void create_notes_commit(struct repository *r,
 	if (!parents) {
 		/* Deduce parent commit from t->ref */
 		struct object_id parent_oid;
-		if (!read_ref(t->ref, &parent_oid)) {
+		if (!refs_read_ref(get_main_ref_store(the_repository), t->ref, &parent_oid)) {
 			struct commit *parent = lookup_commit(r, &parent_oid);
 			if (repo_parse_commit(r, parent))
 				die("Failed to find/parse commit %s", t->ref);
@@ -55,8 +55,9 @@ void commit_notes(struct repository *r, struct notes_tree *t, const char *msg)
 
 	create_notes_commit(r, t, NULL, buf.buf, buf.len, &commit_oid);
 	strbuf_insertstr(&buf, 0, "notes: ");
-	update_ref(buf.buf, t->update_ref, &commit_oid, NULL, 0,
-		   UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), buf.buf,
+			t->update_ref, &commit_oid, NULL, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	strbuf_release(&buf);
 }
diff --git a/notes.c b/notes.c
index fed1eda80c..53ca25c814 100644
--- a/notes.c
+++ b/notes.c
@@ -945,7 +945,8 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
 {
 	assert(list->strdup_strings);
 	if (has_glob_specials(glob)) {
-		for_each_glob_ref(string_list_add_one_ref, glob, list);
+		refs_for_each_glob_ref(get_main_ref_store(the_repository),
+				       string_list_add_one_ref, glob, list);
 	} else {
 		struct object_id oid;
 		if (repo_get_oid(the_repository, glob, &oid))
@@ -1029,7 +1030,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
 	if (flags & NOTES_INIT_EMPTY ||
 	    repo_get_oid_treeish(the_repository, notes_ref, &object_oid))
 		return;
-	if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
+	if (flags & NOTES_INIT_WRITABLE && refs_read_ref(get_main_ref_store(the_repository), notes_ref, &object_oid))
 		die("Cannot use notes ref %s", notes_ref);
 	if (get_tree_entry(the_repository, &object_oid, "", &oid, &mode))
 		die("Failed to read notes tree referenced by %s (%s)",
diff --git a/reachable.c b/reachable.c
index 3b85add243..1224b30008 100644
--- a/reachable.c
+++ b/reachable.c
@@ -363,10 +363,11 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 	add_index_objects_to_pending(revs, 0);
 
 	/* Add all external refs */
-	for_each_ref(add_one_ref, revs);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_one_ref,
+			  revs);
 
 	/* detached HEAD is not included in the list above */
-	head_ref(add_one_ref, revs);
+	refs_head_ref(get_main_ref_store(the_repository), add_one_ref, revs);
 	other_head_refs(add_one_ref, revs);
 
 	/* rebase autostash and orig-head */
diff --git a/ref-filter.c b/ref-filter.c
index eab4beba16..31cc096644 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -895,7 +895,9 @@ static int head_atom_parser(struct ref_format *format UNUSED,
 {
 	if (arg)
 		return err_no_arg(err, "HEAD");
-	atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+	atom->u.head = refs_resolve_refdup(get_main_ref_store(the_repository),
+					   "HEAD", RESOLVE_REF_READING, NULL,
+					   NULL);
 	return 0;
 }
 
@@ -2135,7 +2137,9 @@ static const char *rstrip_ref_components(const char *refname, int len)
 static const char *show_ref(struct refname_atom *atom, const char *refname)
 {
 	if (atom->option == R_SHORT)
-		return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+		return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+						    refname,
+						    warn_ambiguous_refs);
 	else if (atom->option == R_LSTRIP)
 		return lstrip_ref_components(refname, atom->lstrip);
 	else if (atom->option == R_RSTRIP)
@@ -2338,8 +2342,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
 
 	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
-		ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
-					     NULL, NULL);
+		ref->symref = refs_resolve_refdup(get_main_ref_store(the_repository),
+						  ref->refname,
+						  RESOLVE_REF_READING,
+						  NULL, NULL);
 		if (!ref->symref)
 			ref->symref = xstrdup("");
 	}
@@ -2640,7 +2646,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 		 * prefixes like "refs/heads/" etc. are stripped off,
 		 * so we have to look at everything:
 		 */
-		return for_each_fullref_in("", NULL, cb, cb_data);
+		return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						"", NULL, cb, cb_data);
 	}
 
 	if (filter->ignore_case) {
@@ -2649,7 +2656,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 		 * so just return everything and let the caller
 		 * sort it out.
 		 */
-		return for_each_fullref_in("", NULL, cb, cb_data);
+		return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						"", NULL, cb, cb_data);
 	}
 
 	if (!filter->name_patterns[0]) {
@@ -3060,11 +3068,17 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
 		 * of filter_ref_kind().
 		 */
 		if (filter->kind == FILTER_REFS_BRANCHES)
-			ret = for_each_fullref_in("refs/heads/", NULL, fn, cb_data);
+			ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						       "refs/heads/", NULL,
+						       fn, cb_data);
 		else if (filter->kind == FILTER_REFS_REMOTES)
-			ret = for_each_fullref_in("refs/remotes/", NULL, fn, cb_data);
+			ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						       "refs/remotes/", NULL,
+						       fn, cb_data);
 		else if (filter->kind == FILTER_REFS_TAGS)
-			ret = for_each_fullref_in("refs/tags/", NULL, fn, cb_data);
+			ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						       "refs/tags/", NULL, fn,
+						       cb_data);
 		else if (filter->kind & FILTER_REFS_REGULAR)
 			ret = for_each_fullref_in_pattern(filter, fn, cb_data);
 
@@ -3074,7 +3088,8 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
 		 */
 		if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) &&
 		    (filter->kind & FILTER_REFS_DETACHED_HEAD))
-			head_ref(fn, cb_data);
+			refs_head_ref(get_main_ref_store(the_repository), fn,
+				      cb_data);
 	}
 
 	clear_contains_cache(&filter->internal.contains_cache);
diff --git a/reflog-walk.c b/reflog-walk.c
index 66484f4f32..f11b97e889 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -67,24 +67,32 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 	struct complete_reflogs *reflogs =
 		xcalloc(1, sizeof(struct complete_reflogs));
 	reflogs->ref = xstrdup(ref);
-	for_each_reflog_ent(ref, read_one_reflog, reflogs);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository), ref,
+				 read_one_reflog, reflogs);
 	if (reflogs->nr == 0) {
 		const char *name;
 		void *name_to_free;
-		name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
-						     NULL, NULL);
+		name = name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+							  ref,
+							  RESOLVE_REF_READING,
+							  NULL, NULL);
 		if (name) {
-			for_each_reflog_ent(name, read_one_reflog, reflogs);
+			refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+						 name, read_one_reflog,
+						 reflogs);
 			free(name_to_free);
 		}
 	}
 	if (reflogs->nr == 0) {
 		char *refname = xstrfmt("refs/%s", ref);
-		for_each_reflog_ent(refname, read_one_reflog, reflogs);
+		refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+					 refname, read_one_reflog, reflogs);
 		if (reflogs->nr == 0) {
 			free(refname);
 			refname = xstrfmt("refs/heads/%s", ref);
-			for_each_reflog_ent(refname, read_one_reflog, reflogs);
+			refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+						 refname, read_one_reflog,
+						 reflogs);
 		}
 		free(refname);
 	}
@@ -174,7 +182,8 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
 	else {
 		if (*branch == '\0') {
 			free(branch);
-			branch = resolve_refdup("HEAD", 0, NULL, NULL);
+			branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+						     "HEAD", 0, NULL, NULL);
 			if (!branch)
 				die("no current branch");
 
@@ -236,7 +245,9 @@ void get_reflog_selector(struct strbuf *sb,
 	if (shorten) {
 		if (!commit_reflog->reflogs->short_ref)
 			commit_reflog->reflogs->short_ref
-				= shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0);
+				= refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+							       commit_reflog->reflogs->ref,
+							       0);
 		printed_ref = commit_reflog->reflogs->short_ref;
 	} else {
 		printed_ref = commit_reflog->reflogs->ref;
diff --git a/reflog.c b/reflog.c
index 647f3ca398..8861c2d606 100644
--- a/reflog.c
+++ b/reflog.c
@@ -343,7 +343,8 @@ void reflog_expiry_prepare(const char *refname,
 	case UE_ALWAYS:
 		return;
 	case UE_HEAD:
-		for_each_ref(push_tip_to_list, &cb->tips);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  push_tip_to_list, &cb->tips);
 		for (elem = cb->tips; elem; elem = elem->next)
 			commit_list_insert(elem->item, &cb->mark_list);
 		break;
@@ -416,19 +417,22 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
 	recno = strtoul(spec + 2, &ep, 10);
 	if (*ep == '}') {
 		cmd.recno = -recno;
-		for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+		refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+					 ref, count_reflog_ent, &cmd);
 	} else {
 		cmd.expire_total = approxidate(spec + 2);
-		for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+		refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+					 ref, count_reflog_ent, &cmd);
 		cmd.expire_total = 0;
 	}
 
 	cb.cmd = cmd;
-	status |= reflog_expire(ref, flags,
-				reflog_expiry_prepare,
-				should_prune_fn,
-				reflog_expiry_cleanup,
-				&cb);
+	status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
+				     flags,
+				     reflog_expiry_prepare,
+				     should_prune_fn,
+				     reflog_expiry_cleanup,
+				     &cb);
 
  cleanup:
 	free(ref);
diff --git a/refs.c b/refs.c
index 00bcc72719..d398731d74 100644
--- a/refs.c
+++ b/refs.c
@@ -487,7 +487,8 @@ static int warn_if_dangling_symref(const char *refname,
 	if (!(flags & REF_ISSYMREF))
 		return 0;
 
-	resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
+	resolves_to = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					      refname, 0, NULL, NULL);
 	if (!resolves_to
 	    || (d->refname
 		? strcmp(resolves_to, d->refname)
@@ -508,7 +509,8 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 	data.refname = refname;
 	data.refnames = NULL;
 	data.msg_fmt = msg_fmt;
-	for_each_rawref(warn_if_dangling_symref, &data);
+	refs_for_each_rawref(get_main_ref_store(the_repository),
+			     warn_if_dangling_symref, &data);
 }
 
 void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
@@ -519,7 +521,8 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li
 	data.refname = NULL;
 	data.refnames = refnames;
 	data.msg_fmt = msg_fmt;
-	for_each_rawref(warn_if_dangling_symref, &data);
+	refs_for_each_rawref(get_main_ref_store(the_repository),
+			     warn_if_dangling_symref, &data);
 }
 
 int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
diff --git a/remote.c b/remote.c
index 2b650b813b..ec8c158e60 100644
--- a/remote.c
+++ b/remote.c
@@ -1198,8 +1198,10 @@ static char *guess_ref(const char *name, struct ref *peer)
 {
 	struct strbuf buf = STRBUF_INIT;
 
-	const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
-					   NULL, NULL);
+	const char *r = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						peer->name,
+						RESOLVE_REF_READING,
+						NULL, NULL);
 	if (!r)
 		return NULL;
 
@@ -1316,9 +1318,10 @@ static int match_explicit(struct ref *src, struct ref *dst,
 	if (!dst_value) {
 		int flag;
 
-		dst_value = resolve_ref_unsafe(matched_src->name,
-					       RESOLVE_REF_READING,
-					       NULL, &flag);
+		dst_value = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						    matched_src->name,
+						    RESOLVE_REF_READING,
+						    NULL, &flag);
 		if (!dst_value ||
 		    ((flag & REF_ISSYMREF) &&
 		     !starts_with(dst_value, "refs/heads/")))
@@ -1882,7 +1885,7 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 		 * or because it is not a real branch, and get_branch
 		 * auto-vivified it?
 		 */
-		if (!ref_exists(branch->refname))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname))
 			return error_buf(err, _("no such branch: '%s'"),
 					 branch->name);
 		return error_buf(err,
@@ -2168,13 +2171,13 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	struct strvec argv = STRVEC_INIT;
 
 	/* Cannot stat if what we used to build on no longer exists */
-	if (read_ref(base, &oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), base, &oid))
 		return -1;
 	theirs = lookup_commit_reference(the_repository, &oid);
 	if (!theirs)
 		return -1;
 
-	if (read_ref(branch_name, &oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), branch_name, &oid))
 		return -1;
 	ours = lookup_commit_reference(the_repository, &oid);
 	if (!ours)
@@ -2278,7 +2281,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
 		upstream_is_gone = 1;
 	}
 
-	base = shorten_unambiguous_ref(full_base, 0);
+	base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+					    full_base, 0);
 	if (upstream_is_gone) {
 		strbuf_addf(sb,
 			_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2358,7 +2362,8 @@ struct ref *get_local_heads(void)
 {
 	struct ref *local_refs = NULL, **local_tail = &local_refs;
 
-	for_each_ref(one_local_ref, &local_tail);
+	refs_for_each_ref(get_main_ref_store(the_repository), one_local_ref,
+			  &local_tail);
 	return local_refs;
 }
 
@@ -2468,7 +2473,8 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)
 	for (ref = fetch_map; ref; ref = ref->next)
 		string_list_append(&ref_names, ref->name);
 	string_list_sort(&ref_names);
-	for_each_ref(get_stale_heads_cb, &info);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  get_stale_heads_cb, &info);
 	string_list_clear(&ref_names, 0);
 	return stale_refs;
 }
@@ -2553,7 +2559,7 @@ static int remote_tracking(struct remote *remote, const char *refname,
 	dst = apply_refspecs(&remote->fetch, refname);
 	if (!dst)
 		return -1; /* no tracking ref for refname at remote */
-	if (read_ref(dst, oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), dst, oid))
 		return -1; /* we know what the tracking ref is but we cannot read it */
 
 	*dst_refname = dst;
@@ -2659,12 +2665,16 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
 	 * Get the timestamp from the latest entry
 	 * of the remote-tracking ref's reflog.
 	 */
-	for_each_reflog_ent_reverse(remote->tracking_ref, peek_reflog, &date);
+	refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+					 remote->tracking_ref, peek_reflog,
+					 &date);
 
 	cb.remote_commit = commit;
 	cb.local_commits = &arr;
 	cb.remote_reflog_timestamp = date;
-	ret = for_each_reflog_ent_reverse(local, check_and_collect_until, &cb);
+	ret = refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
+					       local, check_and_collect_until,
+					       &cb);
 
 	/* We found an entry in the reflog. */
 	if (ret > 0)
diff --git a/reset.c b/reset.c
index d619cb7115..59ebb1f842 100644
--- a/reset.c
+++ b/reset.c
@@ -47,11 +47,13 @@ static int update_refs(const struct reset_head_opts *opts,
 				strbuf_addstr(&msg, "updating ORIG_HEAD");
 				reflog_orig_head = msg.buf;
 			}
-			update_ref(reflog_orig_head, "ORIG_HEAD",
-				   orig_head ? orig_head : head,
-				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					reflog_orig_head, "ORIG_HEAD",
+					orig_head ? orig_head : head,
+					old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
 		} else if (old_orig)
-			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, "ORIG_HEAD", old_orig, 0);
 	}
 
 	if (!reflog_head) {
@@ -60,16 +62,19 @@ static int update_refs(const struct reset_head_opts *opts,
 		reflog_head = msg.buf;
 	}
 	if (!switch_to_branch)
-		ret = update_ref(reflog_head, "HEAD", oid, head,
-				 detach_head ? REF_NO_DEREF : 0,
-				 UPDATE_REFS_MSG_ON_ERR);
+		ret = refs_update_ref(get_main_ref_store(the_repository),
+				      reflog_head, "HEAD", oid, head,
+				      detach_head ? REF_NO_DEREF : 0,
+				      UPDATE_REFS_MSG_ON_ERR);
 	else {
-		ret = update_ref(reflog_branch ? reflog_branch : reflog_head,
-				 switch_to_branch, oid, NULL, 0,
-				 UPDATE_REFS_MSG_ON_ERR);
+		ret = refs_update_ref(get_main_ref_store(the_repository),
+				      reflog_branch ? reflog_branch : reflog_head,
+				      switch_to_branch, oid, NULL, 0,
+				      UPDATE_REFS_MSG_ON_ERR);
 		if (!ret)
-			ret = create_symref("HEAD", switch_to_branch,
-					    reflog_head);
+			ret = refs_create_symref(get_main_ref_store(the_repository),
+						 "HEAD", switch_to_branch,
+						 reflog_head);
 	}
 	if (!ret && run_hook)
 		run_hooks_l("post-checkout",
diff --git a/revision.c b/revision.c
index 7e45f765d9..7ddf0f151a 100644
--- a/revision.c
+++ b/revision.c
@@ -1738,7 +1738,8 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 	cb.all_revs = revs;
 	cb.all_flags = flags;
 	cb.wt = NULL;
-	for_each_reflog(handle_one_reflog, &cb);
+	refs_for_each_reflog(get_main_ref_store(the_repository),
+			     handle_one_reflog, &cb);
 
 	if (!revs->single_worktree)
 		add_other_reflogs_to_pending(&cb);
@@ -1979,9 +1980,9 @@ static const char *lookup_other_head(struct object_id *oid)
 	};
 
 	for (i = 0; i < ARRAY_SIZE(other_head); i++)
-		if (!read_ref_full(other_head[i],
-				RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-				oid, NULL)) {
+		if (!refs_read_ref_full(get_main_ref_store(the_repository), other_head[i],
+					RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+					oid, NULL)) {
 			if (is_null_oid(oid))
 				die(_("%s exists but is a symbolic ref"), other_head[i]);
 			return other_head[i];
@@ -2789,7 +2790,8 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
 	} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
 		struct all_refs_cb cb;
 		init_all_refs_cb(&cb, revs, *flags);
-		for_each_glob_ref(handle_one_ref, optarg, &cb);
+		refs_for_each_glob_ref(get_main_ref_store(the_repository),
+				       handle_one_ref, optarg, &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 		return argcount;
 	} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
@@ -2804,7 +2806,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
 			return error(_("options '%s' and '%s' cannot be used together"),
 				     "--exclude-hidden", "--branches");
 		init_all_refs_cb(&cb, revs, *flags);
-		for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
+		refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+					  handle_one_ref, optarg,
+					  "refs/heads/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (skip_prefix(arg, "--tags=", &optarg)) {
 		struct all_refs_cb cb;
@@ -2812,7 +2816,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
 			return error(_("options '%s' and '%s' cannot be used together"),
 				     "--exclude-hidden", "--tags");
 		init_all_refs_cb(&cb, revs, *flags);
-		for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
+		refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+					  handle_one_ref, optarg,
+					  "refs/tags/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (skip_prefix(arg, "--remotes=", &optarg)) {
 		struct all_refs_cb cb;
@@ -2820,7 +2826,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
 			return error(_("options '%s' and '%s' cannot be used together"),
 				     "--exclude-hidden", "--remotes");
 		init_all_refs_cb(&cb, revs, *flags);
-		for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
+		refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+					  handle_one_ref, optarg,
+					  "refs/remotes/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (!strcmp(arg, "--reflog")) {
 		add_reflogs_to_pending(revs, *flags);
@@ -2911,7 +2919,8 @@ static void NORETURN diagnose_missing_default(const char *def)
 	int flags;
 	const char *refname;
 
-	refname = resolve_ref_unsafe(def, 0, NULL, &flags);
+	refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					  def, 0, NULL, &flags);
 	if (!refname || !(flags & REF_ISSYMREF) || (flags & REF_ISBROKEN))
 		die(_("your current branch appears to be broken"));
 
diff --git a/sequencer.c b/sequencer.c
index 88de4dc20f..19421cbdb8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -266,7 +266,7 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
 	oidcpy(&rec->after, null_oid());
 
 	/* This may fail, but that's fine, we will keep the null OID. */
-	read_ref(ref, &rec->before);
+	refs_read_ref(get_main_ref_store(the_repository), ref, &rec->before);
 
 	return rec;
 }
@@ -440,7 +440,7 @@ int sequencer_remove_state(struct replay_opts *opts)
 			char *eol = strchr(p, '\n');
 			if (eol)
 				*eol = '\0';
-			if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
+			if (refs_delete_ref(get_main_ref_store(the_repository), "(rebase) cleanup", p, NULL, 0) < 0) {
 				warning(_("could not delete '%s'"), p);
 				ret = -1;
 			}
@@ -661,7 +661,8 @@ static int fast_forward_to(struct repository *r,
 
 	strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
@@ -841,11 +842,12 @@ static int is_index_unchanged(struct repository *r)
 	struct index_state *istate = r->index;
 	const char *head_name;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+	if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
 		/* Check to see if this is an unborn branch */
-		head_name = resolve_ref_unsafe("HEAD",
-			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-			&head_oid, NULL);
+		head_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						    "HEAD",
+						    RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+						    &head_oid, NULL);
 		if (!head_name ||
 			!starts_with(head_name, "refs/heads/") ||
 			!is_null_oid(&head_oid))
@@ -1294,7 +1296,8 @@ int update_head_with_reflog(const struct commit *old_head,
 		strbuf_addch(&sb, '\n');
 	}
 
-	transaction = ref_transaction_begin(err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
@@ -1720,8 +1723,8 @@ static int try_to_commit(struct repository *r,
 
 static int write_rebase_head(struct object_id *oid)
 {
-	if (update_ref("rebase", "REBASE_HEAD", oid,
-		       NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+	if (refs_update_ref(get_main_ref_store(the_repository), "rebase", "REBASE_HEAD", oid,
+			    NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
 		return error(_("could not update %s"), "REBASE_HEAD");
 
 	return 0;
@@ -2455,12 +2458,12 @@ static int do_pick_commit(struct repository *r,
 	if ((command == TODO_PICK || command == TODO_REWORD ||
 	     command == TODO_EDIT) && !opts->no_commit &&
 	    (res == 0 || res == 1) &&
-	    update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
-		       REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+	    refs_update_ref(get_main_ref_store(the_repository), NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
+			    REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
 		res = -1;
 	if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
-	    update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
-		       REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+	    refs_update_ref(get_main_ref_store(the_repository), NULL, "REVERT_HEAD", &commit->object.oid, NULL,
+			    REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
 		res = -1;
 
 	if (res) {
@@ -3364,7 +3367,7 @@ static int rollback_single_pick(struct repository *r)
 	if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
 	    !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
 		return error(_("no cherry-pick or revert in progress"));
-	if (read_ref_full("HEAD", 0, &head_oid, NULL))
+	if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, NULL))
 		return error(_("cannot resolve HEAD"));
 	if (is_null_oid(&head_oid))
 		return error(_("cannot abort from a branch yet to be born"));
@@ -3375,7 +3378,7 @@ static int skip_single_pick(void)
 {
 	struct object_id head;
 
-	if (read_ref_full("HEAD", 0, &head, NULL))
+	if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head, NULL))
 		return error(_("cannot resolve HEAD"));
 	return reset_merge(&head);
 }
@@ -3891,7 +3894,7 @@ static struct commit *lookup_label(struct repository *r, const char *label,
 
 	strbuf_reset(buf);
 	strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
-	if (!read_ref(buf->buf, &oid)) {
+	if (!refs_read_ref(get_main_ref_store(the_repository), buf->buf, &oid)) {
 		commit = lookup_commit_object(r, &oid);
 	} else {
 		/* fall back to non-rewritten ref or commit */
@@ -3987,9 +3990,10 @@ static int do_reset(struct repository *r,
 		ret = error(_("could not write index"));
 
 	if (!ret)
-		ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
-						len, name), "HEAD", &oid,
-				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		ret = refs_update_ref(get_main_ref_store(the_repository), reflog_message(opts, "reset", "'%.*s'",
+											 len, name),
+				      "HEAD", &oid,
+				      NULL, 0, UPDATE_REFS_MSG_ON_ERR);
 cleanup:
 	free((void *)desc.buffer);
 	if (ret < 0)
@@ -4471,7 +4475,7 @@ static int do_update_ref(struct repository *r, const char *refname)
 	for_each_string_list_item(item, &list) {
 		if (!strcmp(item->string, refname)) {
 			struct update_ref_record *rec = item->util;
-			if (read_ref("HEAD", &rec->after))
+			if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &rec->after))
 				return -1;
 			break;
 		}
@@ -5031,15 +5035,15 @@ static int pick_commits(struct repository *r,
 			}
 			msg = reflog_message(opts, "finish", "%s onto %s",
 				head_ref.buf, buf.buf);
-			if (update_ref(msg, head_ref.buf, &head, &orig,
-				       REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+			if (refs_update_ref(get_main_ref_store(the_repository), msg, head_ref.buf, &head, &orig,
+					    REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
 				res = error(_("could not update %s"),
 					head_ref.buf);
 				goto cleanup_head_ref;
 			}
 			msg = reflog_message(opts, "finish", "returning to %s",
 				head_ref.buf);
-			if (create_symref("HEAD", head_ref.buf, msg)) {
+			if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
 				res = error(_("could not update HEAD to %s"),
 					head_ref.buf);
 				goto cleanup_head_ref;
@@ -6209,10 +6213,11 @@ static int add_decorations_to_list(const struct commit *commit,
 				   struct todo_add_branch_context *ctx)
 {
 	const struct name_decoration *decoration = get_name_decoration(&commit->object);
-	const char *head_ref = resolve_ref_unsafe("HEAD",
-						  RESOLVE_REF_READING,
-						  NULL,
-						  NULL);
+	const char *head_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						       "HEAD",
+						       RESOLVE_REF_READING,
+						       NULL,
+						       NULL);
 
 	while (decoration) {
 		struct todo_item *item;
diff --git a/server-info.c b/server-info.c
index e2fe0f9143..6feaa457c5 100644
--- a/server-info.c
+++ b/server-info.c
@@ -175,7 +175,8 @@ static int add_info_ref(const char *path, const struct object_id *oid,
 
 static int generate_info_refs(struct update_info_ctx *uic)
 {
-	return for_each_ref(add_info_ref, uic);
+	return refs_for_each_ref(get_main_ref_store(the_repository),
+				 add_info_ref, uic);
 }
 
 static int update_info_refs(int force)
diff --git a/setup.c b/setup.c
index f4b32f76e3..b0ed643b55 100644
--- a/setup.c
+++ b/setup.c
@@ -2001,7 +2001,7 @@ void create_reference_database(unsigned int ref_storage_format,
 			die(_("invalid initial branch name: '%s'"),
 			    initial_branch);
 
-		if (create_symref("HEAD", ref, NULL) < 0)
+		if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
 			exit(1);
 		free(ref);
 	}
diff --git a/shallow.c b/shallow.c
index 7ff50dd0da..a0b181ba8a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -678,8 +678,10 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
 	 * connect to old refs. If not (e.g. force ref updates) it'll
 	 * have to go down to the current shallow commits.
 	 */
-	head_ref(mark_uninteresting, NULL);
-	for_each_ref(mark_uninteresting, NULL);
+	refs_head_ref(get_main_ref_store(the_repository), mark_uninteresting,
+		      NULL);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  mark_uninteresting, NULL);
 
 	/* Mark potential bottoms so we won't go out of bound */
 	for (i = 0; i < nr_shallow; i++) {
@@ -782,8 +784,8 @@ static void post_assign_shallow(struct shallow_info *info,
 	info->nr_theirs = dst;
 
 	memset(&ca, 0, sizeof(ca));
-	head_ref(add_ref, &ca);
-	for_each_ref(add_ref, &ca);
+	refs_head_ref(get_main_ref_store(the_repository), add_ref, &ca);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_ref, &ca);
 
 	/* Remove unreachable shallow commits from "ours" */
 	for (i = dst = 0; i < info->nr_ours; i++) {
@@ -822,8 +824,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
 			struct commit_array ca;
 
 			memset(&ca, 0, sizeof(ca));
-			head_ref(add_ref, &ca);
-			for_each_ref(add_ref, &ca);
+			refs_head_ref(get_main_ref_store(the_repository),
+				      add_ref, &ca);
+			refs_for_each_ref(get_main_ref_store(the_repository),
+					  add_ref, &ca);
 			si->commits = ca.commits;
 			si->nr_commits = ca.nr;
 		}
diff --git a/submodule.c b/submodule.c
index ce2d032521..bdd75faa44 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1233,7 +1233,8 @@ int push_unpushed_submodules(struct repository *r,
 		char *head;
 		struct object_id head_oid;
 
-		head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+		head = refs_resolve_refdup(get_main_ref_store(the_repository),
+					   "HEAD", 0, &head_oid, NULL);
 		if (!head)
 			die(_("Failed to resolve HEAD as a valid ref."));
 
@@ -1271,7 +1272,8 @@ static int append_oid_to_array(const char *ref UNUSED,
 void check_for_new_submodule_commits(struct object_id *oid)
 {
 	if (!initialized_fetch_ref_tips) {
-		for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_oid_to_array, &ref_tips_before_fetch);
 		initialized_fetch_ref_tips = 1;
 	}
 
diff --git a/transport-helper.c b/transport-helper.c
index 8d284b24d5..780fcaf529 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -551,7 +551,7 @@ static int fetch_with_import(struct transport *transport,
 		else
 			private = xstrdup(name);
 		if (private) {
-			if (read_ref(private, &posn->old_oid) < 0)
+			if (refs_read_ref(get_main_ref_store(the_repository), private, &posn->old_oid) < 0)
 				die(_("could not read ref %s"), private);
 			free(private);
 		}
@@ -923,8 +923,10 @@ static int push_update_refs_status(struct helper_data *data,
 			private = apply_refspecs(&data->rs, ref->name);
 			if (!private)
 				continue;
-			update_ref("update by helper", private, &(ref->new_oid),
-				   NULL, 0, 0);
+			refs_update_ref(get_main_ref_store(the_repository),
+					"update by helper", private,
+					&(ref->new_oid),
+					NULL, 0, 0);
 			free(private);
 		} else {
 			for (report = ref->report; report; report = report->next) {
@@ -934,11 +936,12 @@ static int push_update_refs_status(struct helper_data *data,
 							 : ref->name);
 				if (!private)
 					continue;
-				update_ref("update by helper", private,
-					   report->new_oid
-					   ? report->new_oid
-					   : &(ref->new_oid),
-					   NULL, 0, 0);
+				refs_update_ref(get_main_ref_store(the_repository),
+						"update by helper", private,
+						report->new_oid
+						? report->new_oid
+						: &(ref->new_oid),
+						NULL, 0, 0);
 				free(private);
 			}
 		}
@@ -1105,9 +1108,11 @@ static int push_refs_with_export(struct transport *transport,
 					int flag;
 
 					/* Follow symbolic refs (mainly for HEAD). */
-					name = resolve_ref_unsafe(ref->peer_ref->name,
-								  RESOLVE_REF_READING,
-								  &oid, &flag);
+					name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+								       ref->peer_ref->name,
+								       RESOLVE_REF_READING,
+								       &oid,
+								       &flag);
 					if (!name || !(flag & REF_ISSYMREF))
 						name = ref->peer_ref->name;
 
@@ -1252,7 +1257,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
 		if (eon) {
 			if (has_attribute(eon + 1, "unchanged")) {
 				(*tail)->status |= REF_STATUS_UPTODATE;
-				if (read_ref((*tail)->name, &(*tail)->old_oid) < 0)
+				if (refs_read_ref(get_main_ref_store(the_repository), (*tail)->name, &(*tail)->old_oid) < 0)
 					die(_("could not read ref %s"),
 					    (*tail)->name);
 			}
diff --git a/transport.c b/transport.c
index df518ead70..0ad04b77fd 100644
--- a/transport.c
+++ b/transport.c
@@ -100,8 +100,9 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 		/* Follow symbolic refs (mainly for HEAD). */
 		localname = ref->peer_ref->name;
 		remotename = ref->name;
-		tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
-					 NULL, &flag);
+		tmp = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					      localname, RESOLVE_REF_READING,
+					      NULL, &flag);
 		if (tmp && flag & REF_ISSYMREF &&
 			starts_with(tmp, "refs/heads/"))
 			localname = tmp;
@@ -543,10 +544,12 @@ static void update_one_tracking_ref(struct remote *remote, char *refname,
 		if (verbose)
 			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
 		if (deletion)
-			delete_ref(NULL, rs.dst, NULL, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, rs.dst, NULL, 0);
 		else
-			update_ref("update by push", rs.dst, new_oid,
-				   NULL, 0, 0);
+			refs_update_ref(get_main_ref_store(the_repository),
+					"update by push", rs.dst, new_oid,
+					NULL, 0, 0);
 		free(rs.dst);
 	}
 }
@@ -814,7 +817,8 @@ void transport_print_push_status(const char *dest, struct ref *refs,
 	if (transport_color_config() < 0)
 		warning(_("could not parse transport.color.* config"));
 
-	head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   RESOLVE_REF_READING, NULL, NULL);
 
 	if (verbose) {
 		for (ref = refs; ref; ref = ref->next)
diff --git a/upload-pack.c b/upload-pack.c
index 902144b9d3..8fbd138515 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -618,7 +618,8 @@ static void for_each_namespaced_ref_1(each_ref_fn fn,
 	if (allow_hidden_refs(data->allow_uor))
 		excludes = hidden_refs_to_excludes(&data->hidden_refs);
 
-	for_each_namespaced_ref(excludes, fn, data);
+	refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
+				     excludes, fn, data);
 }
 
 
@@ -873,7 +874,8 @@ static void deepen(struct upload_pack_data *data, int depth)
 		 * Checking for reachable shallows requires that our refs be
 		 * marked with OUR_REF.
 		 */
-		head_ref_namespaced(check_ref, data);
+		refs_head_ref_namespaced(get_main_ref_store(the_repository),
+					 check_ref, data);
 		for_each_namespaced_ref_1(check_ref, data);
 
 		get_reachable_list(data, &reachable_shallows);
@@ -1288,7 +1290,8 @@ static int find_symref(const char *refname,
 
 	if ((flag & REF_ISSYMREF) == 0)
 		return 0;
-	symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
+	symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						refname, 0, NULL, &flag);
 	if (!symref_target || (flag & REF_ISSYMREF) == 0)
 		die("'%s' is a symref but it is not?", refname);
 	item = string_list_append(cb_data, strip_namespace(refname));
@@ -1413,13 +1416,15 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
 	if (data.timeout)
 		data.daemon_mode = 1;
 
-	head_ref_namespaced(find_symref, &data.symref);
+	refs_head_ref_namespaced(get_main_ref_store(the_repository),
+				 find_symref, &data.symref);
 
 	if (advertise_refs || !data.stateless_rpc) {
 		reset_timeout(data.timeout);
 		if (advertise_refs)
 			data.no_done = 1;
-		head_ref_namespaced(send_ref, &data);
+		refs_head_ref_namespaced(get_main_ref_store(the_repository),
+					 send_ref, &data);
 		for_each_namespaced_ref_1(send_ref, &data);
 		if (!data.sent_capabilities) {
 			const char *refname = "capabilities^{}";
@@ -1433,7 +1438,8 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
 		advertise_shallow_grafts(1);
 		packet_flush(1);
 	} else {
-		head_ref_namespaced(check_ref, &data);
+		refs_head_ref_namespaced(get_main_ref_store(the_repository),
+					 check_ref, &data);
 		for_each_namespaced_ref_1(check_ref, &data);
 	}
 
@@ -1511,7 +1517,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
 
 		strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
 		if (ref_is_hidden(refname_nons, refname.buf, hidden_refs) ||
-		    read_ref(refname.buf, &oid)) {
+		    refs_read_ref(get_main_ref_store(the_repository), refname.buf, &oid)) {
 			packet_writer_error(writer, "unknown ref %s", refname_nons);
 			die("unknown ref %s", refname_nons);
 		}
diff --git a/walker.c b/walker.c
index c0fd632d92..74bc049d4c 100644
--- a/walker.c
+++ b/walker.c
@@ -286,7 +286,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 	ALLOC_ARRAY(oids, targets);
 
 	if (write_ref) {
-		transaction = ref_transaction_begin(&err);
+		transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+							  &err);
 		if (!transaction) {
 			error("%s", err.buf);
 			goto done;
@@ -294,7 +295,8 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 	}
 
 	if (!walker->get_recover) {
-		for_each_ref(mark_complete, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  mark_complete, NULL);
 		commit_list_sort_by_date(&complete);
 	}
 
diff --git a/wt-status.c b/wt-status.c
index bdfc23e2ae..ff4be071ca 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -145,7 +145,8 @@ void wt_status_prepare(struct repository *r, struct wt_status *s)
 	s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 	s->use_color = -1;
 	s->relative_paths = 1;
-	s->branch = resolve_refdup("HEAD", 0, NULL, NULL);
+	s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+					"HEAD", 0, NULL, NULL);
 	s->reference = "HEAD";
 	s->fp = stdout;
 	s->index_file = get_index_file();
@@ -976,7 +977,8 @@ static int stash_count_refs(struct object_id *ooid UNUSED,
 static int count_stash_entries(void)
 {
 	int n = 0;
-	for_each_reflog_ent("refs/stash", stash_count_refs, &n);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+				 "refs/stash", stash_count_refs, &n);
 	return n;
 }
 
@@ -1304,10 +1306,10 @@ static int split_commit_in_progress(struct wt_status *s)
 	    !s->branch || strcmp(s->branch, "HEAD"))
 		return 0;
 
-	if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-			  &head_oid, &head_flags) ||
-	    read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-			  &orig_head_oid, &orig_head_flags))
+	if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			       &head_oid, &head_flags) ||
+	    refs_read_ref_full(get_main_ref_store(the_repository), "ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			       &orig_head_oid, &orig_head_flags))
 		return 0;
 	if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
 		return 0;
@@ -1679,7 +1681,7 @@ static void wt_status_get_detached_from(struct repository *r,
 	char *ref = NULL;
 
 	strbuf_init(&cb.buf, 0);
-	if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
+	if (refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), "HEAD", grab_1st_switch, &cb) <= 0) {
 		strbuf_release(&cb.buf);
 		return;
 	}
@@ -2087,7 +2089,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 		upstream_is_gone = 1;
 	}
 
-	short_base = shorten_unambiguous_ref(base, 0);
+	short_base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+						  base, 0);
 	color_fprintf(s->fp, header_color, "...");
 	color_fprintf(s->fp, branch_color_remote, "%s", short_base);
 	free(short_base);
@@ -2220,7 +2223,8 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
 		ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
 					     &base, 0, s->ahead_behind_flags);
 		if (base) {
-			base = shorten_unambiguous_ref(base, 0);
+			base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+							    base, 0);
 			fprintf(s->fp, "# branch.upstream %s%c", base, eol);
 			free((char *)base);
 
-- 
2.45.0


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

^ permalink raw reply related	[relevance 6%]

* [PATCH 0/5] refs: remove functions without ref store
@ 2024-05-03  6:27  3% Patrick Steinhardt
  2024-05-03  6:28  6% ` [PATCH 4/5] cocci: apply rules to rewrite callers of "refs" interfaces Patrick Steinhardt
  0 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-05-03  6:27 UTC (permalink / raw)
  To: git

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

Hi,

this patch series aims to convert the ref subsystem to rely less on
`the_repository`. The focus of it is to remove those cases where we have
two variants of the same function: one with a `struct ref_store`, and
one without. There are still other cases in "refs.c" where we implicitly
assume `the_repository`, but those require a bit more thought and will
thus be handled in a subsequent patch series.

The biggest part of this patch is a set of new Coccinelle rules added by
patch 3. Those rules are applied in patch 4 and the now-unused functions
that do not take a `struct ref_store` are then removed in patch 5. This
of course results in quite a lot of churn, but given that it is fully
automated via Coccinelle I don't think it is particularly bad.

It is quite likely that this patch series will impact in-flight patch
series. I'd be quite happy to drop the last patch that removes the old
interfaces to make this a bit less painful.

Patrick

Patrick Steinhardt (5):
  refs: introduce missing functions that accept a `struct ref_store`
  refs: add `exclude_patterns` parameter to `for_each_fullref_in()`
  cocci: introduce rules to transform "refs" to pass ref store
  cocci: apply rules to rewrite callers of "refs" interfaces
  refs: remove functions without ref store

 add-interactive.c             |  17 ++-
 bisect.c                      |  25 +++--
 blame.c                       |   4 +-
 branch.c                      |   5 +-
 builtin/am.c                  |  38 ++++---
 builtin/bisect.c              |  44 +++++---
 builtin/blame.c               |   4 +-
 builtin/branch.c              |  49 +++++----
 builtin/checkout.c            |  35 +++---
 builtin/clone.c               |  36 +++---
 builtin/describe.c            |   3 +-
 builtin/fast-import.c         |  11 +-
 builtin/fetch.c               |  20 +++-
 builtin/fsck.c                |  11 +-
 builtin/gc.c                  |   3 +-
 builtin/log.c                 |   6 +-
 builtin/merge.c               |  34 ++++--
 builtin/name-rev.c            |   5 +-
 builtin/notes.c               |  26 +++--
 builtin/pack-objects.c        |  10 +-
 builtin/pull.c                |   2 +-
 builtin/rebase.c              |  18 +--
 builtin/receive-pack.c        |  15 ++-
 builtin/reflog.c              |  25 +++--
 builtin/remote.c              |  37 ++++---
 builtin/repack.c              |   7 +-
 builtin/replace.c             |   9 +-
 builtin/reset.c               |  13 ++-
 builtin/rev-parse.c           |  25 +++--
 builtin/show-branch.c         |  22 ++--
 builtin/show-ref.c            |  19 +++-
 builtin/stash.c               |  23 ++--
 builtin/submodule--helper.c   |   7 +-
 builtin/symbolic-ref.c        |  13 ++-
 builtin/tag.c                 |  11 +-
 builtin/update-index.c        |   2 +-
 builtin/update-ref.c          |  21 ++--
 builtin/worktree.c            |  19 ++--
 bundle-uri.c                  |  12 +-
 bundle.c                      |   2 +-
 commit-graph.c                |   3 +-
 commit.c                      |   3 +-
 config.c                      |   3 +-
 contrib/coccinelle/refs.cocci | 103 +++++++++++++++++
 delta-islands.c               |   3 +-
 fetch-pack.c                  |   6 +-
 fmt-merge-msg.c               |   4 +-
 help.c                        |   5 +-
 http-backend.c                |  13 ++-
 log-tree.c                    |   9 +-
 ls-refs.c                     |  10 +-
 midx-write.c                  |   3 +-
 negotiator/default.c          |   3 +-
 negotiator/skipping.c         |   3 +-
 notes-cache.c                 |   6 +-
 notes-merge.c                 |   2 +-
 notes-utils.c                 |   7 +-
 notes.c                       |   5 +-
 reachable.c                   |   5 +-
 ref-filter.c                  |  35 ++++--
 reflog-walk.c                 |  27 +++--
 reflog.c                      |  20 ++--
 refs.c                        | 200 ++++------------------------------
 refs.h                        |  84 +++-----------
 remote.c                      |  38 ++++---
 reset.c                       |  29 +++--
 revision.c                    |  27 +++--
 sequencer.c                   |  61 ++++++-----
 server-info.c                 |   3 +-
 setup.c                       |   2 +-
 shallow.c                     |  16 ++-
 submodule.c                   |   6 +-
 transport-helper.c            |  29 +++--
 transport.c                   |  16 ++-
 upload-pack.c                 |  20 ++--
 walker.c                      |   6 +-
 wt-status.c                   |  22 ++--
 77 files changed, 845 insertions(+), 680 deletions(-)
 create mode 100644 contrib/coccinelle/refs.cocci

-- 
2.45.0


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

^ permalink raw reply	[relevance 3%]

* [PATCH v2] refs: return conflict error when checking packed refs
  2024-04-30 14:50  4% [PATCH] refs: return conflict error when checking packed refs Ivan Tse via GitGitGadget
  2024-05-02 12:38  0% ` Patrick Steinhardt
@ 2024-05-03  4:50 19% ` Ivan Tse via GitGitGadget
  2024-05-03  6:38  3%   ` Patrick Steinhardt
  2024-05-04  3:04 19%   ` [PATCH v3] " Ivan Tse via GitGitGadget
  1 sibling, 2 replies; 200+ results
From: Ivan Tse via GitGitGadget @ 2024-05-03  4:50 UTC (permalink / raw)
  To: git; +Cc: Ivan Tse, Ivan Tse

From: Ivan Tse <ivan.tse1@gmail.com>

The TRANSACTION_NAME_CONFLICT error code refers to a failure to create a
ref due to a name conflict with another ref. An example of this is a
directory/file conflict such as ref names A/B and A.

"git fetch" uses this error code to more accurately describe the error
by recommending to the user that they try running "git remote prune" to
remove any old refs that are deleted by the remote which would clear up
any directory/file conflicts.

This helpful error message is not displayed when the conflicted ref is
stored in packed refs. This change fixes this by ensuring error return
code consistency in `lock_raw_ref`.

Signed-off-by: Ivan Tse <ivan.tse1@gmail.com>
---
    refs: return conflict error when checking packed refs
    
    Changes against v1:
    
     * added test for the error message during git fetch
    
    Thanks for reviewing! I've gone ahead and attempted to add tests for
    this behavior. It tests that the error message is shown for both cases
    when the ref is stored as loose vs packed-refs. How does this test look?
    Also, should this test have a REFFILES prerequisite?

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1716%2Fivantsepp%2Freturn_name_conflict_error_for_packed_refs-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1716/ivantsepp/return_name_conflict_error_for_packed_refs-v2
Pull-Request: https://github.com/git/git/pull/1716

Range-diff vs v1:

 1:  a87ba267e44 ! 1:  58b2cda5c18 refs: return conflict error when checking packed refs
     @@ refs/files-backend.c: static int lock_raw_ref(struct files_ref_store *refs,
       	}
       
       	ret = 0;
     +
     + ## t/t5510-fetch.sh ##
     +@@ t/t5510-fetch.sh: test_expect_success 'branchname D/F conflict resolved by --prune' '
     + 	test_cmp expect actual
     + '
     + 
     ++test_expect_success 'branchname D/F conflict rejected with targeted error message' '
     ++	git clone . df-conflict-error &&
     ++	git branch dir_conflict &&
     ++	(
     ++		cd df-conflict-error &&
     ++		git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
     ++		test_must_fail git fetch 2>../err &&
     ++		git pack-refs --all &&
     ++		test_must_fail git fetch 2>../err-packed
     ++	) &&
     ++	test_grep "error: some local refs could not be updated; try running" err &&
     ++	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err &&
     ++	test_grep "error: some local refs could not be updated; try running" err-packed &&
     ++	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err-packed
     ++'
     ++
     + test_expect_success 'fetching a one-level ref works' '
     + 	test_commit extra &&
     + 	git reset --hard HEAD^ &&


 refs/files-backend.c |  4 +++-
 t/t5510-fetch.sh     | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea00..97473f377d1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -794,8 +794,10 @@ static int lock_raw_ref(struct files_ref_store *refs,
 		 */
 		if (refs_verify_refname_available(
 				    refs->packed_ref_store, refname,
-				    extras, NULL, err))
+				    extras, NULL, err)) {
+			ret = TRANSACTION_NAME_CONFLICT;
 			goto error_return;
+		}
 	}
 
 	ret = 0;
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 33d34d5ae9e..ae0828e26a1 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1091,6 +1091,22 @@ test_expect_success 'branchname D/F conflict resolved by --prune' '
 	test_cmp expect actual
 '
 
+test_expect_success 'branchname D/F conflict rejected with targeted error message' '
+	git clone . df-conflict-error &&
+	git branch dir_conflict &&
+	(
+		cd df-conflict-error &&
+		git update-ref refs/remotes/origin/dir_conflict/file HEAD &&
+		test_must_fail git fetch 2>../err &&
+		git pack-refs --all &&
+		test_must_fail git fetch 2>../err-packed
+	) &&
+	test_grep "error: some local refs could not be updated; try running" err &&
+	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err &&
+	test_grep "error: some local refs could not be updated; try running" err-packed &&
+	test_grep " '\''git remote prune origin'\'' to remove any old, conflicting branches" err-packed
+'
+
 test_expect_success 'fetching a one-level ref works' '
 	test_commit extra &&
 	git reset --hard HEAD^ &&

base-commit: d4cc1ec35f3bcce816b69986ca41943f6ce21377
-- 
gitgitgadget


^ permalink raw reply related	[relevance 19%]

* Re: t4216-log-bloom.sh broken ?
  @ 2024-05-02 20:05  3%           ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-05-02 20:05 UTC (permalink / raw)
  To: SZEDER Gábor; +Cc: Torsten Bögershausen, git

Junio C Hamano <gitster@pobox.com> writes:

> ... it is a very unsatisfactory workaround, compared to a world where we
> do not have to worry about such a broken mv (perhaps by noticing a
> broken macOS /bin/mv and automatically doing mv () { mv -f "$@" }
> for them).
>
> I am curious what other differences Torsten will find out between
> good macs and bad ones.  Perhaps we can narrow down the bad apples?

In any case, while we are waiting, I did a few "grep":

    $ git grep 'mv \(.*\)\.tmp \1' t
    t/lib-t6000.sh:	mv sed.script.tmp sed.script
    t/t7508-status.sh:	rm "$1" && mv "$1".tmp "$1"
    t/t8011-blame-split-file.sh:	mv one.tmp one &&
    t/t8011-blame-split-file.sh:	mv two.tmp two &&
    t/t9400-git-cvsserver-server.sh:	mv merge.tmp merge &&
    t/t9400-git-cvsserver-server.sh:	mv merge.tmp merge &&
    t/t9802-git-p4-filetype.sh:		mv empty-symlink,v.tmp empty-symlink,v

    $ git grep 'mv "\(.*\)\.tmp" "\1"' t
    t/lib-chunk.sh:	mv "$fn.tmp" "$fn"
    t/t3404-rebase-interactive.sh:	mv "$1.tmp" "$1"
    t/t5515-fetch-merge-logic.sh:	mv "$file.tmp" "$file"
    t/t7600-merge.sh:) >"$1.tmp" && mv "$1.tmp" "$1"
    t/t9001-send-email.sh:	mv "$1.tmp" "$1"
    t/t9001-send-email.sh:	mv "$1.tmp" "$1"

    $ git grep 'mv \(.*\)+ \1' t
    t/t4025-hunk-header.sh:	mv file+ file
    t/t6402-merge-rename.sh:	mv A+ A &&
    t/t6402-merge-rename.sh:	mv A+ A &&
    t/t6432-merge-recursive-space-options.sh:	mv text.txt+ text.txt &&
    t/t6432-merge-recursive-space-options.sh:	mv text.txt+ text.txt &&

There is nothing other than the one we found in lib-chunk.sh that
work on files under .git/objects/ that are made read-only, so we
should be OK with a workaround like the attached.

 t/lib-chunk.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git c/t/lib-chunk.sh w/t/lib-chunk.sh
index a7cd9c3c6d..9f01df190b 100644
--- c/t/lib-chunk.sh
+++ w/t/lib-chunk.sh
@@ -13,5 +13,6 @@ corrupt_chunk_file () {
 	fn=$1; shift
 	perl "$TEST_DIRECTORY"/lib-chunk/corrupt-chunk-file.pl \
 		"$@" <"$fn" >"$fn.tmp" &&
-	mv "$fn.tmp" "$fn"
+	# some vintages of macOS 'mv' fails to overwrite a read-only file.
+	mv -f "$fn.tmp" "$fn"
 }


^ permalink raw reply related	[relevance 3%]

* Re: [PATCH] refs: return conflict error when checking packed refs
  2024-04-30 14:50  4% [PATCH] refs: return conflict error when checking packed refs Ivan Tse via GitGitGadget
@ 2024-05-02 12:38  0% ` Patrick Steinhardt
  2024-05-03  4:50 19% ` [PATCH v2] " Ivan Tse via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-02 12:38 UTC (permalink / raw)
  To: Ivan Tse via GitGitGadget; +Cc: git, Ivan Tse

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

On Tue, Apr 30, 2024 at 02:50:47PM +0000, Ivan Tse via GitGitGadget wrote:
> From: Ivan Tse <ivan.tse1@gmail.com>
> 
> The TRANSACTION_NAME_CONFLICT error code refers to a failure to create a
> ref due to a name conflict with another ref. An example of this is a
> directory/file conflict such as ref names A/B and A.
> 
> "git fetch" uses this error code to more accurately describe the error
> by recommending to the user that they try running "git remote prune" to
> remove any old refs that are deleted by the remote which would clear up
> any directory/file conflicts.
> 
> This helpful error message is not displayed when the conflicted ref is
> stored in packed refs. This change fixes this by ensuring error return
> code consistency in `lock_raw_ref`.

The change itself makes sense to me. But I'd like to see a test that
demonstrates the new behaviour so that we don't regress this in the
future.

Thanks!

Patrick

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

^ permalink raw reply	[relevance 0%]

* [PATCH v3 10/10] refs: refuse to write pseudorefs
  2024-05-02  8:17  3% ` [PATCH v3 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
  2024-05-02  8:17 18%   ` [PATCH v3 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
@ 2024-05-02  8:17 28%   ` Patrick Steinhardt
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-02  8:17 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler, Kristoffer Haugsbakk

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

Pseudorefs are not stored in the ref database as by definition, they
carry additional metadata that essentially makes them not a ref. As
such, writing pseudorefs via the ref backend does not make any sense
whatsoever as the ref backend wouldn't know how exactly to store the
data.

Restrict writing pseudorefs via the ref backend.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs.c           | 7 +++++++
 t/t5510-fetch.sh | 6 +++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/refs.c b/refs.c
index 50d679b7e7..7c3c7465a4 100644
--- a/refs.c
+++ b/refs.c
@@ -1307,6 +1307,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
 		return -1;
 	}
 
+	if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
+	    is_pseudo_ref(refname)) {
+		strbuf_addf(err, _("refusing to update pseudoref '%s'"),
+			    refname);
+		return -1;
+	}
+
 	if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
 		BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
 
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 33d34d5ae9..4eb569f4df 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -518,7 +518,7 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
 	one_head=$(cd one && git rev-parse HEAD) &&
 	this_head=$(git rev-parse HEAD) &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -530,7 +530,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 	one_ref=$(cd one && git symbolic-ref HEAD) &&
 	git config branch.main.remote blub &&
 	git config branch.main.merge "$one_ref" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -540,7 +540,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 # the merge spec does not match the branch the remote HEAD points to
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
 	git config branch.main.merge "${one_ref}_not" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
-- 
2.45.0


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

^ permalink raw reply related	[relevance 28%]

* [PATCH v3 01/10] Documentation/glossary: redefine pseudorefs as special refs
  2024-05-02  8:17  3% ` [PATCH v3 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
@ 2024-05-02  8:17 18%   ` Patrick Steinhardt
  2024-05-02  8:17 28%   ` [PATCH v3 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-02  8:17 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler, Kristoffer Haugsbakk

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

Nowadays, Git knows about three different kinds of refs. As defined in
gitglossary(7):

  - Regular refs that start with "refs/", like "refs/heads/main".

  - Pseudorefs, which live in the root directory. These must have
    all-caps names and must be a file that start with an object hash.
    Consequently, symbolic refs are not pseudorefs because they do not
    start with an object hash.

  - Special refs, of which we only have "FETCH_HEAD" and "MERGE_HEAD".

This state is extremely confusing, and I would claim that most folks
don't fully understand what is what here. The current definitions also
have several problems:

  - Where does "HEAD" fit in? It's not a pseudoref because it can be
    a symbolic ref. It's not a regular ref because it does not start
    with "refs/". And it's not a special ref, either.

  - There is a strong overlap between pseudorefs and special refs. The
    pseudoref section for example mentions "MERGE_HEAD", even though it
    is a special ref. Is it thus both a pseudoref and a special ref?

  - Why do we even need to distinguish refs that live in the root from
    other refs when they behave just like a regular ref anyway?

In other words, the current state is quite a mess and leads to wild
inconsistencies without much of a good reason.

The original reason why pseudorefs were introduced is that there are
some refs that sometimes behave like a ref, even though they aren't a
ref. And we really only have two of these nowadays, namely "MERGE_HEAD"
and "FETCH_HEAD". Those files are never written via the ref backends,
but are instead written by git-fetch(1), git-pull(1) and git-merge(1).
They contain additional metadata that highlights where a ref has been
fetched from or the list of commits that have been merged.

This original intent in fact matches the definition of special refs that
we have recently introduced in 8df4c5d205 (Documentation: add "special
refs" to the glossary, 2024-01-19). Due to the introduction of the new
reftable backend we were forced to distinguish those refs more clearly
such that we don't ever try to read or write them via the reftable
backend. In the same series, we also addressed all the other cases where
we used to write those special refs via the filesystem directly, thus
circumventing the ref backend, to instead write them via the backends.
Consequently, there are no other refs left anymore which are special.

Let's address this mess and return the pseudoref terminology back to its
original intent: a ref that sometimes behave like a ref, but which isn't
really a ref because it gets written to the filesystem directly. Or in
other words, let's redefine pseudorefs to match the current definition
of special refs. As special refs and pseudorefs are now the same per
definition, we can drop the "special refs" term again. It's not exposed
to our users and thus they wouldn't ever encounter that term anyway.

Refs that live in the root of the ref hierarchy but which are not
pseudorefs will be further defined in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/glossary-content.txt | 40 +++++++++---------------------
 1 file changed, 12 insertions(+), 28 deletions(-)

diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index d71b199955..ca04768e3b 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -497,20 +497,18 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	Pseudorefs are a class of files under `$GIT_DIR` which behave
-	like refs for the purposes of rev-parse, but which are treated
-	specially by git.  Pseudorefs both have names that are all-caps,
-	and always start with a line consisting of a
-	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
-	pseudoref, because it is sometimes a symbolic ref.  They might
-	optionally contain some additional data.  `MERGE_HEAD` and
-	`CHERRY_PICK_HEAD` are examples.  Unlike
-	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
-	be symbolic refs, and never have reflogs.  They also cannot be
-	updated through the normal ref update machinery.  Instead,
-	they are updated by directly writing to the files.  However,
-	they can be read as if they were refs, so `git rev-parse
-	MERGE_HEAD` will work.
+	A ref that has different semantics than normal refs. These refs can be
+	accessed via normal Git commands but may not behave the same as a
+	normal ref in some cases.
++
+The following pseudorefs are known to Git:
+
+ - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+   may refer to multiple object IDs. Each object ID is annotated with metadata
+   indicating where it was fetched from and its fetch status.
+
+ - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
+   conflicts. It contains all commit IDs which are being merged.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -638,20 +636,6 @@ The most notable example is `HEAD`.
 	An <<def_object,object>> used to temporarily store the contents of a
 	<<def_dirty,dirty>> working directory and the index for future reuse.
 
-[[def_special_ref]]special ref::
-	A ref that has different semantics than normal refs. These refs can be
-	accessed via normal Git commands but may not behave the same as a
-	normal ref in some cases.
-+
-The following special refs are known to Git:
-
- - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
-   may refer to multiple object IDs. Each object ID is annotated with metadata
-   indicating where it was fetched from and its fetch status.
-
- - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
-   conflicts. It contains all commit IDs which are being merged.
-
 [[def_submodule]]submodule::
 	A <<def_repository,repository>> that holds the history of a
 	separate project inside another repository (the latter of
-- 
2.45.0


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

^ permalink raw reply related	[relevance 18%]

* [PATCH v3 00/10] Clarify pseudo-ref terminology
    2024-04-29 13:41  6% ` [PATCH 2/3] refs: do not label special refs as pseudo refs Patrick Steinhardt
  2024-04-30 12:26  2% ` [PATCH v2 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
@ 2024-05-02  8:17  3% ` Patrick Steinhardt
  2024-05-02  8:17 18%   ` [PATCH v3 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
  2024-05-02  8:17 28%   ` [PATCH v3 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
  2 siblings, 2 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-02  8:17 UTC (permalink / raw)
  To: git
  Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler, Kristoffer Haugsbakk

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

Hi,

this is the third version of my patch series that aims to clarify the
pseudo-ref terminology.

Changes compared to v2:

    - Various typo fixes.

    - Added a note in the first commit that we're about to clearly
      define rules around "root refs" in a subsequent commit. While this
      patch series will make root refs act like "just a normal ref", we
      will still have strict limits around the naming policy for them.

Thanks!

Patrick

Patrick Steinhardt (10):
  Documentation/glossary: redefine pseudorefs as special refs
  Documentation/glossary: clarify limitations of pseudorefs
  Documentation/glossary: define root refs as refs
  refs: rename `is_pseudoref()` to `is_root_ref()`
  refs: refname `is_special_ref()` to `is_pseudo_ref()`
  refs: classify HEAD as a root ref
  refs: root refs can be symbolic refs
  refs: pseudorefs are no refs
  ref-filter: properly distinuish pseudo and root refs
  refs: refuse to write pseudorefs

 Documentation/glossary-content.txt |  72 ++++++++---------
 builtin/for-each-ref.c             |   2 +-
 ref-filter.c                       |  16 ++--
 ref-filter.h                       |   4 +-
 refs.c                             | 120 ++++++++++++++++-------------
 refs.h                             |  50 +++++++++++-
 refs/files-backend.c               |   3 +-
 refs/reftable-backend.c            |   3 +-
 t/t5510-fetch.sh                   |   6 +-
 t/t6302-for-each-ref-filter.sh     |  34 ++++++++
 10 files changed, 205 insertions(+), 105 deletions(-)

Range-diff against v2:
 1:  2489bb5585 !  1:  e651bae690 Documentation/glossary: redefine pseudorefs as special refs
    @@ Commit message
     
         The original reason why pseudorefs were introduced is that there are
         some refs that sometimes behave like a ref, even though they aren't a
    -    ref. And we really only have two of these nowadads, namely "MERGE_HEAD"
    +    ref. And we really only have two of these nowadays, namely "MERGE_HEAD"
         and "FETCH_HEAD". Those files are never written via the ref backends,
         but are instead written by git-fetch(1), git-pull(1) and git-merge(1).
    -    They contain additional metadata that hihlights where a ref has been
    +    They contain additional metadata that highlights where a ref has been
         fetched from or the list of commits that have been merged.
     
         This original intent in fact matches the definition of special refs that
    @@ Commit message
         definition, we can drop the "special refs" term again. It's not exposed
         to our users and thus they wouldn't ever encounter that term anyway.
     
    +    Refs that live in the root of the ref hierarchy but which are not
    +    pseudorefs will be further defined in a subsequent commit.
    +
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## Documentation/glossary-content.txt ##
     @@ Documentation/glossary-content.txt: exclude;;
    - 	that start with `refs/bisect/`, but might later include other
      	unusual refs.
      
    --[[def_pseudoref]]pseudoref::
    + [[def_pseudoref]]pseudoref::
     -	Pseudorefs are a class of files under `$GIT_DIR` which behave
     -	like refs for the purposes of rev-parse, but which are treated
     -	specially by git.  Pseudorefs both have names that are all-caps,
    @@ Documentation/glossary-content.txt: exclude;;
     -	they are updated by directly writing to the files.  However,
     -	they can be read as if they were refs, so `git rev-parse
     -	MERGE_HEAD` will work.
    -+[[def_pseudoref]]pseudoref ref::
     +	A ref that has different semantics than normal refs. These refs can be
     +	accessed via normal Git commands but may not behave the same as a
     +	normal ref in some cases.
 2:  1f2f8cf3f2 !  2:  66ac046132 Documentation/glossary: clarify limitations of pseudorefs
    @@ Commit message
           - They can be read via git-rev-parse(1) and similar tools.
     
           - They are not surfaced when iterating through refs, like when using
    -        git-for-each-ref(1). They are no ref, so iterating through refs
    +        git-for-each-ref(1). They are not refs, so iterating through refs
             should not surface them.
     
           - They cannot be written via git-update-ref(1) and related commands.
    @@ Commit message
      ## Documentation/glossary-content.txt ##
     @@ Documentation/glossary-content.txt: exclude;;
      
    - [[def_pseudoref]]pseudoref ref::
    + [[def_pseudoref]]pseudoref::
      	A ref that has different semantics than normal refs. These refs can be
     -	accessed via normal Git commands but may not behave the same as a
     -	normal ref in some cases.
 3:  9659d7da3f !  3:  243d616101 Documentation/glossary: define root refs as refs
    @@ Documentation/glossary-content.txt: The following pseudorefs are known to Git:
     -Different subhierarchies are used for different purposes (e.g. the
     -`refs/heads/` hierarchy is used to represent local branches).
     +Ref names must either start with `refs/` or be located in the root of
    -+the hierarchy. In that case, their name must conform to the following
    -+rules:
    ++the hierarchy. For the latter, their name must follow these rules:
      +
     -There are a few special-purpose refs that do not begin with `refs/`.
     -The most notable example is `HEAD`.
 4:  3d7ea70417 =  4:  0a116f9d11 refs: rename `is_pseudoref()` to `is_root_ref()`
 5:  e6b6db972d !  5:  484a0856bc refs: refname `is_special_ref()` to `is_pseudo_ref()`
    @@ Commit message
         defined terminology in our gitglossary(7). Note that in the preceding
         commit we have just renamed `is_pseudoref()` to `is_root_ref()`, where
         there may be confusion for in-flight patch series that add new calls to
    -    `is_pseudoref()`. In order to intentionall break such patch series we
    +    `is_pseudoref()`. In order to intentionally break such patch series we
         have thus picked `is_pseudo_ref()` instead of `is_pseudoref()` as the
         new name.
     
 6:  44f72a7baf =  6:  c196fe3c45 refs: classify HEAD as a root ref
 7:  e90b2f8aa9 !  7:  92a71222e1 refs: root refs can be symbolic refs
    @@ Commit message
     
         Last but not least, the current behaviour can actually lead to a
         segfault when calling `is_root_ref()` with a reference that either does
    -    not exist or that is a symbolic ref because we never intialized `oid`.
    +    not exist or that is a symbolic ref because we never initialized `oid`.
     
         Let's loosen the restrictions in accordance to the new definition of
         root refs, which are simply plain refs that may as well be a symbolic
 8:  bc82d7ae65 =  8:  8bd52e5363 refs: pseudorefs are no refs
 9:  95d7547b2e =  9:  cd6d745a01 ref-filter: properly distinuish pseudo and root refs
10:  b2029612dd = 10:  6956fccced refs: refuse to write pseudorefs
-- 
2.45.0


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

^ permalink raw reply	[relevance 3%]

* [PATCH 09/11] refs/reftable: allow disabling writing the object index
  @ 2024-05-02  6:52  2% ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-05-02  6:52 UTC (permalink / raw)
  To: git

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

Besides the expected "ref" and "log" records, the reftable library also
writes "obj" records. These are basically a reverse mapping of object
IDs to their respective ref records so that it becomes efficient to
figure out which references point to a specific object. The motivation
for this data structure is the "uploadpack.allowTipSHA1InWant" config,
which allows a client to fetch any object by its hash that has a ref
pointing to it.

This reverse index is not used by Git at all though, and the expectation
is that most hosters nowadays use "uploadpack.allowAnySHA1InWant". It
may thus be preferable for many users to disable writing these optional
object indices altogether to safe some precious disk space.

Add a new config "reftable.indexObjects" that allows the user to disable
the object index altogether.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/config/reftable.txt |  6 +++
 refs/reftable-backend.c           |  3 ++
 t/t0613-reftable-write-options.sh | 69 +++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+)

diff --git a/Documentation/config/reftable.txt b/Documentation/config/reftable.txt
index 16b915c75e..6e4466f3c5 100644
--- a/Documentation/config/reftable.txt
+++ b/Documentation/config/reftable.txt
@@ -31,3 +31,9 @@ A maximum of `65535` restart points per block is supported.
 +
 The default value is to create restart points every 16 records. A value of `0`
 will use the default value.
+
+reftable.indexObjects::
+	Whether the reftable backend shall write object blocks. Object blocks
+	are a reverse mapping of object ID to the references pointing to them.
++
+The default value is `true`.
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index a786143de2..5298fcef6e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -249,6 +249,9 @@ static int reftable_be_config(const char *var, const char *value,
 			die("reftable block size cannot exceed %u", (unsigned)UINT16_MAX);
 		opts->restart_interval = restart_interval;
 		return 0;
+	} else if (!strcmp(var, "reftable.indexobjects")) {
+		opts->skip_index_objects = !git_config_bool(var, value);
+		return 0;
 	}
 
 	return 0;
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh
index e0a5b26f58..e2708e11d5 100755
--- a/t/t0613-reftable-write-options.sh
+++ b/t/t0613-reftable-write-options.sh
@@ -214,4 +214,73 @@ test_expect_success 'restart interval exceeding maximum supported interval' '
 	)
 '
 
+test_expect_success 'object index gets written by default with ref index' '
+	test_config_global core.logAllRefUpdates false &&
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		for i in $(test_seq 5)
+		do
+			printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+			return 1
+		done >input &&
+		git update-ref --stdin <input &&
+		git -c reftable.blockSize=100 pack-refs &&
+
+		cat >expect <<-EOF &&
+		header:
+		  block_size: 100
+		ref:
+		  - length: 53
+		    restarts: 1
+		  - length: 95
+		    restarts: 1
+		  - length: 71
+		    restarts: 1
+		  - length: 80
+		    restarts: 1
+		obj:
+		  - length: 11
+		    restarts: 1
+		EOF
+		test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'object index can be disabled' '
+	test_config_global core.logAllRefUpdates false &&
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		for i in $(test_seq 5)
+		do
+			printf "update refs/heads/branch-%d HEAD\n" "$i" ||
+			return 1
+		done >input &&
+		git update-ref --stdin <input &&
+		git -c reftable.blockSize=100 -c reftable.indexObjects=false pack-refs &&
+
+		cat >expect <<-EOF &&
+		header:
+		  block_size: 100
+		ref:
+		  - length: 53
+		    restarts: 1
+		  - length: 95
+		    restarts: 1
+		  - length: 71
+		    restarts: 1
+		  - length: 80
+		    restarts: 1
+		EOF
+		test-tool dump-reftable -b .git/reftable/*.ref >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.45.0


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

^ permalink raw reply related	[relevance 2%]

* [PATCH v4 00/10] Make trailer_info struct private (plus sequencer cleanup)
  2024-04-26  0:26  2%   ` [PATCH v3 00/10] " Linus Arver via GitGitGadget
@ 2024-05-02  4:54  2%     ` Linus Arver via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Linus Arver via GitGitGadget @ 2024-05-02  4:54 UTC (permalink / raw)
  To: git
  Cc: Christian Couder, Junio C Hamano, Emily Shaffer, Josh Steadmon,
	Randall S. Becker, Christian Couder, Kristoffer Haugsbakk,
	Linus Arver, Linus Arver, Linus Arver

NOTE: This series is based on the la/format-trailer-info topic branch (see
its discussion at [1]).

This series is based on the initial series [2], notably the v4 version of
patches 17-20 as suggested by Christian [3]. This version addresses the
review comments for those patches, namely the splitting up of Patch 19 there
into 3 separate patches [4] (as Patches 05-07 here) .

The central idea is to make the trailer_info struct private (that is, move
its definition from trailer.h to trailer.c) --- aka the "pimpl" idiom. See
the detailed commit message for Patch 07 for the motivation behind the
change.

Patch 04 makes sequencer.c a well-behaved trailer API consumer, by making
use of the trailer iterator. Patch 03 prepares us for Patch 04. Patch 08
slightly reduces the weight of the API by removing (from the API surface) an
unused function.


Notable changes in v4
=====================

 * Drop "25%" language in Patch 03
 * Rename some variables
 * Update patch emails to personal (linus@ucla.edu) email


Notable changes in v3
=====================

 * (NEW Patch 10) Expand test coverage to check the contents of each
   iteration (raw, key, val fields), not just the total number of iterations
 * (NEW Patch 09) Add documentation in <trailer.h> for using
   parse_trailers()
 * (unrelated) I will lose access to my linusa@google.com email address
   tomorrow (I'm switching jobs!) and so future emails from me will come
   from linus@ucla.edu [5]. I've added the latter email to the CC list here
   so things should just work. Cheers


Notable changes in v2
=====================

 * Add unit tests at the beginning of the series (Patches 01 and 02) and use
   it to verify that the other edge cases remain unchanged when we add the
   "raw" member (Patch 03)

[1]
https://lore.kernel.org/git/pull.1694.git.1710485706.gitgitgadget@gmail.com/
[2]
https://lore.kernel.org/git/pull.1632.v4.git.1707196348.gitgitgadget@gmail.com/
[3]
https://lore.kernel.org/git/CAP8UFD08F0V13X0+CJ1uhMPzPWVMs2okGVMJch0DkQg5M3BWLA@mail.gmail.com/
[4]
https://lore.kernel.org/git/CAP8UFD1twELGKvvesxgCrZrypKZpgSt04ira3mvurG1UbpDfxQ@mail.gmail.com/
[5]
https://lore.kernel.org/git/pull.1720.git.1713309711217.gitgitgadget@gmail.com/

Linus Arver (10):
  Makefile: sort UNIT_TEST_PROGRAMS
  trailer: add unit tests for trailer iterator
  trailer: teach iterator about non-trailer lines
  sequencer: use the trailer iterator
  interpret-trailers: access trailer_info with new helpers
  trailer: make parse_trailers() return trailer_info pointer
  trailer: make trailer_info struct private
  trailer: retire trailer_info_get() from API
  trailer: document parse_trailers() usage
  trailer unit tests: inspect iterator contents

 Makefile                     |   5 +-
 builtin/interpret-trailers.c |  12 +-
 sequencer.c                  |  27 ++-
 t/unit-tests/t-trailer.c     | 315 +++++++++++++++++++++++++++++++++++
 trailer.c                    | 167 ++++++++++++-------
 trailer.h                    |  94 +++++++----
 6 files changed, 506 insertions(+), 114 deletions(-)
 create mode 100644 t/unit-tests/t-trailer.c


base-commit: 3452d173241c8b87ecdd67f91f594cb14327e394
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1696%2Flistx%2Ftrailer-api-part-3-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1696/listx/trailer-api-part-3-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1696

Range-diff vs v3:

  1:  b6a1304f8ae !  1:  8a9f71442d8 Makefile: sort UNIT_TEST_PROGRAMS
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          Makefile: sort UNIT_TEST_PROGRAMS
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## Makefile ##
      @@ Makefile: THIRD_PARTY_SOURCES += sha1collisiondetection/%
  2:  4ad0fbbb33c !  2:  b503b539c6f trailer: add unit tests for trailer iterator
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: add unit tests for trailer iterator
     @@ Commit message
          implementation details which are, unlike the API, subject to drastic
          changes).
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## Makefile ##
      @@ Makefile: UNIT_TEST_PROGRAMS += t-ctype
  3:  9077d5a315d !  3:  4aeb48050b1 trailer: teach iterator about non-trailer lines
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: teach iterator about non-trailer lines
     @@ Commit message
          for non-trailer lines, making the comparison still work even with this
          commit).
      
     -    Rename "num_expected_trailers" to "num_expected_objects" in
     +    Rename "num_expected_trailers" to "num_expected" in
          t/unit-tests/t-trailer.c because the items we iterate over now include
          non-trailer lines.
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## t/unit-tests/t-trailer.c ##
      @@
     @@ t/unit-tests/t-trailer.c
       #include "trailer.h"
       
      -static void t_trailer_iterator(const char *msg, size_t num_expected_trailers)
     -+static void t_trailer_iterator(const char *msg, size_t num_expected_objects)
     ++static void t_trailer_iterator(const char *msg, size_t num_expected)
       {
       	struct trailer_iterator iter;
       	size_t i = 0;
     @@ t/unit-tests/t-trailer.c: static void t_trailer_iterator(const char *msg, size_t
       	trailer_iterator_release(&iter);
       
      -	check_uint(i, ==, num_expected_trailers);
     -+	check_uint(i, ==, num_expected_objects);
     ++	check_uint(i, ==, num_expected);
       }
       
       static void run_t_trailer_iterator(void)
     @@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
       		const char *name;
       		const char *msg;
      -		size_t num_expected_trailers;
     -+		size_t num_expected_objects;
     ++		size_t num_expected;
       	} tc[] = {
       		{
       			"empty input",
     @@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
       	for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
       		TEST(t_trailer_iterator(tc[i].msg,
      -					tc[i].num_expected_trailers),
     -+					tc[i].num_expected_objects),
     ++					tc[i].num_expected),
       		     "%s", tc[i].name);
       	}
       }
     @@ trailer.h: void format_trailers_from_commit(const struct process_trailer_options
       struct trailer_iterator {
      +	/*
      +	 * Raw line (e.g., "foo: bar baz") before being parsed as a trailer
     -+	 * key/val pair as part of a trailer block. A trailer block can be
     -+	 * either 100% trailer lines, or mixed in with non-trailer lines (in
     -+	 * which case at least 25% must be trailer lines).
     ++	 * key/val pair as part of a trailer block (as the "key" and "val"
     ++	 * fields below). If a line fails to parse as a trailer, then the "key"
     ++	 * will be the entire line and "val" will be the empty string.
      +	 */
      +	const char *raw;
     -+
       	struct strbuf key;
       	struct strbuf val;
       
  4:  4a1d18da574 !  4:  a3d080d4d6c sequencer: use the trailer iterator
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          sequencer: use the trailer iterator
     @@ Commit message
          before when we iterated over the unparsed string array (char **trailers)
          in trailer_info.
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## sequencer.c ##
      @@ sequencer.c: static const char *get_todo_path(const struct replay_opts *opts)
  5:  460979ba964 !  5:  44df42ca503 interpret-trailers: access trailer_info with new helpers
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          interpret-trailers: access trailer_info with new helpers
     @@ Commit message
          implementation (and thus hidden from the API).
      
          Helped-by: Christian Couder <chriscool@tuxfamily.org>
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## builtin/interpret-trailers.c ##
      @@ builtin/interpret-trailers.c: static void interpret_trailers(const struct process_trailer_options *opts,
  6:  d217858c637 !  6:  9ed7cef9d29 trailer: make parse_trailers() return trailer_info pointer
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: make parse_trailers() return trailer_info pointer
     @@ Commit message
          format_trailers_from_commit() and trailer_iterator_init() accordingly.
      
          Helped-by: Christian Couder <chriscool@tuxfamily.org>
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## builtin/interpret-trailers.c ##
      @@ builtin/interpret-trailers.c: static void interpret_trailers(const struct process_trailer_options *opts,
  7:  49c66c48cc1 !  7:  246ac9a5d07 trailer: make trailer_info struct private
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: make trailer_info struct private
     @@ Commit message
      
          Helped-by: Junio C Hamano <gitster@pobox.com>
          Helped-by: Christian Couder <chriscool@tuxfamily.org>
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## trailer.c ##
      @@
  8:  56e1cca4b7b !  8:  ca6f0c4208c trailer: retire trailer_info_get() from API
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: retire trailer_info_get() from API
     @@ Commit message
          We have to also reposition it to be above parse_trailers(), which
          depends on it.
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## trailer.c ##
      @@ trailer.c: static struct trailer_info *trailer_info_new(void)
  9:  35304837e08 !  9:  c1a0f1bed04 trailer: document parse_trailers() usage
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer: document parse_trailers() usage
     @@ Commit message
          comments a bit easier to read (because "head" itself doesn't really have
          any domain-specific meaning here).
      
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## trailer.c ##
      @@ trailer.c: static struct trailer_info *trailer_info_get(const struct process_trailer_option
 10:  4d53707f836 ! 10:  310b632ddfd trailer unit tests: inspect iterator contents
     @@
       ## Metadata ##
     -Author: Linus Arver <linusa@google.com>
     +Author: Linus Arver <linus@ucla.edu>
      
       ## Commit message ##
          trailer unit tests: inspect iterator contents
     @@ Commit message
          iteration.
      
          Helped-by: Junio C Hamano <gitster@pobox.com>
     -    Signed-off-by: Linus Arver <linusa@google.com>
     +    Signed-off-by: Linus Arver <linus@ucla.edu>
      
       ## t/unit-tests/t-trailer.c ##
      @@
       #include "test-lib.h"
       #include "trailer.h"
       
     --static void t_trailer_iterator(const char *msg, size_t num_expected_objects)
     -+struct trailer_assertions {
     +-static void t_trailer_iterator(const char *msg, size_t num_expected)
     ++struct contents {
      +	const char *raw;
      +	const char *key;
      +	const char *val;
      +};
      +
     -+static void t_trailer_iterator(const char *msg, size_t num_expected_objects,
     -+			       struct trailer_assertions *trailer_assertions)
     ++static void t_trailer_iterator(const char *msg, size_t num_expected,
     ++			       struct contents *contents)
       {
       	struct trailer_iterator iter;
       	size_t i = 0;
     @@ t/unit-tests/t-trailer.c
       	trailer_iterator_init(&iter, msg);
      -	while (trailer_iterator_advance(&iter))
      +	while (trailer_iterator_advance(&iter)) {
     -+		if (num_expected_objects) {
     -+			check_str(iter.raw, trailer_assertions[i].raw);
     -+			check_str(iter.key.buf, trailer_assertions[i].key);
     -+			check_str(iter.val.buf, trailer_assertions[i].val);
     ++		if (num_expected) {
     ++			check_str(iter.raw, contents[i].raw);
     ++			check_str(iter.key.buf, contents[i].key);
     ++			check_str(iter.val.buf, contents[i].val);
      +		}
       		i++;
      +	}
       	trailer_iterator_release(&iter);
       
     - 	check_uint(i, ==, num_expected_objects);
     -@@ t/unit-tests/t-trailer.c: static void t_trailer_iterator(const char *msg, size_t num_expected_objects)
     + 	check_uint(i, ==, num_expected);
     +@@ t/unit-tests/t-trailer.c: static void t_trailer_iterator(const char *msg, size_t num_expected)
       
       static void run_t_trailer_iterator(void)
       {
     @@ t/unit-tests/t-trailer.c: static void t_trailer_iterator(const char *msg, size_t
       	static struct test_cases {
       		const char *name;
       		const char *msg;
     - 		size_t num_expected_objects;
     -+		struct trailer_assertions trailer_assertions[10];
     + 		size_t num_expected;
     ++		struct contents contents[10];
       	} tc[] = {
       		{
       			"empty input",
     @@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
       
       	for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
       		TEST(t_trailer_iterator(tc[i].msg,
     --					tc[i].num_expected_objects),
     -+					tc[i].num_expected_objects,
     -+					tc[i].trailer_assertions),
     +-					tc[i].num_expected),
     ++					tc[i].num_expected,
     ++					tc[i].contents),
       		     "%s", tc[i].name);
       	}
       }

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* [PATCH v5 1/7] refs: accept symref values in `ref_transaction_update()`
  2024-05-01 20:22  1%       ` [PATCH v5 0/7] refs: add support for transactional symref updates Karthik Nayak
@ 2024-05-01 20:22  7%         ` Karthik Nayak
  2024-05-03 12:41  1%         ` [PATCH v6 0/7] refs: add support for transactional symref updates Karthik Nayak
  1 sibling, 0 replies; 200+ results
From: Karthik Nayak @ 2024-05-01 20:22 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The function `ref_transaction_update()` obtains ref information and
flags to create a `ref_update` and add them to the transaction at hand.

To extend symref support in transactions, we need to also accept the
old and new ref targets and process it. This commit adds the required
parameters to the function and modifies all call sites.

The two parameters added are `new_target` and `old_target`. The
`new_target` is used to denote what the reference should point to when
the transaction is applied. Some functions allow this parameter to be
NULL, meaning that the reference is not changed.

The `old_target` denotes the value the reference must have before the
update. Some functions allow this parameter to be NULL, meaning that the
old value of the reference is not checked.

We also update the internal function `ref_transaction_add_update()`
similarly to take the two new parameters.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  5 +++--
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  1 +
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  1 +
 builtin/update-ref.c    |  1 +
 refs.c                  | 22 +++++++++++++++++-----
 refs.h                  | 18 +++++++++++++++++-
 refs/files-backend.c    | 12 ++++++------
 refs/refs-internal.h    | 14 ++++++++++++++
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  9 +++++----
 walker.c                |  2 +-
 14 files changed, 71 insertions(+), 24 deletions(-)

diff --git a/branch.c b/branch.c
index e4a738fc7b..48af4c3ceb 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..297dfb91a1 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1675,7 +1675,8 @@ static void dump_tags(void)
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..66840b7c5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..b150ef39a8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..7690687b0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..40a65fdebc 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..21fdbf6ac8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..47bc9dd103 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg)
 {
 	struct ref_update *update;
@@ -1235,6 +1236,11 @@ struct ref_update *ref_transaction_add_update(
 	if (transaction->state != REF_TRANSACTION_OPEN)
 		BUG("update called for transaction that is not open");
 
+	if (old_oid && !is_null_oid(old_oid) && old_target)
+		BUG("only one of old_oid and old_target should be non NULL");
+	if (new_oid && !is_null_oid(new_oid) && new_target)
+		BUG("only one of new_oid and new_target should be non NULL");
+
 	FLEX_ALLOC_STR(update, refname, refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
@@ -1253,6 +1259,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err)
 {
@@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, new_target,
+				   old_target, msg);
 	return 0;
 }
 
@@ -1295,7 +1304,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1318,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1332,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
 				      NULL, old_oid,
+				      NULL, NULL,
 				      flags, NULL, err);
 }
 
@@ -1335,8 +1347,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
-	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+				   flags, msg, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index d278775e08..c7851bf587 100644
--- a/refs.h
+++ b/refs.h
@@ -648,6 +648,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  *         before the update. A copy of this value is made in the
  *         transaction.
  *
+ *     new_target -- the target reference that the reference will be
+ *         updated to point to. If the reference is a regular reference,
+ *         it will be converted to a symbolic reference. Cannot be set
+ *         together with `new_oid`. A copy of this value is made in the
+ *         transaction.
+ *
+ *     old_target -- the reference that the reference must be pointing to.
+ *         Canont be set together with `old_oid`. A copy of this value is
+ *         made in the transaction.
+ *
  *     flags -- flags affecting the update, passed to
  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
  *         REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -713,7 +723,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  * beforehand. The old value is checked after the lock is taken to
  * prevent races. If the old value doesn't agree with old_oid, the
  * whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
  *
  * See the above comment "Reference transaction updates" for more
  * information.
@@ -722,6 +736,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea0..e4d0aa3d41 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
+					   iter->oid, NULL, NULL, NULL,
 					   REF_NO_DEREF, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
@@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL, NULL);
 		}
 	}
 
@@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..108f4ec419 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,19 @@ struct ref_update {
 	 */
 	struct object_id old_oid;
 
+	/*
+	 * If set, point the reference to this value. This can also be
+	 * used to convert regular references to become symbolic refs.
+	 * Cannot be set together with `new_oid`.
+	 */
+	const char *new_target;
+
+	/*
+	 * If set, check that the reference previously pointed to this
+	 * value. Cannot be set together with `old_oid`.
+	 */
+	const char *old_target;
+
 	/*
 	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
 	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
@@ -173,6 +186,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg);
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c504..6104471199 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 88de4dc20f..61e007d85f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -665,7 +665,7 @@ static int fast_forward_to(struct repository *r,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
-				   null_oid() : from,
+				   null_oid() : from, NULL, NULL,
 				   0, sb.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
@@ -1298,7 +1298,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   NULL, NULL, 0, sb.buf, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3832,8 +3832,9 @@ static int do_label(struct repository *r, const char *name, int len)
 	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
-	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+	} else if (ref_transaction_update(transaction, ref_name.buf,
+					  &head_oid, NULL, NULL, NULL,
+					  0, msg.buf, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index c0fd632d92..1b3df43906 100644
--- a/walker.c
+++ b/walker.c
@@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		strbuf_reset(&refname);
 		strbuf_addf(&refname, "refs/%s", write_ref[i]);
 		if (ref_transaction_update(transaction, refname.buf,
-					   oids + i, NULL, 0,
+					   oids + i, NULL, NULL, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
 					   &err)) {
 			error("%s", err.buf);
-- 
2.43.GIT



^ permalink raw reply related	[relevance 7%]

* [PATCH v5 0/7] refs: add support for transactional symref updates
  2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
  2024-04-26 15:24  7%       ` [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
  2024-04-26 15:24 11%       ` [PATCH v4 4/7] update-ref: add support for 'symref-delete' command Karthik Nayak
@ 2024-05-01 20:22  1%       ` Karthik Nayak
  2024-05-01 20:22  7%         ` [PATCH v5 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
  2024-05-03 12:41  1%         ` [PATCH v6 0/7] refs: add support for transactional symref updates Karthik Nayak
  2 siblings, 2 replies; 200+ results
From: Karthik Nayak @ 2024-05-01 20:22 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The patch series takes over from the existing patch series, wherein we
introduced symref-* commands to git-update-ref. Since there was a ton of
discussions on the UX of the patch series and its application, I thought it
would be best to shorten the series and split it into multiple smaller series.

This series adds transactional support for symrefs in the reference db. Then
we switch refs_create_symref() to start using transactions for symref updates.
This allows us to deprecate the create_symref code in the ref_storage_be
interface and remove all associated code which is no longer used.

The split was primarily done so we can merge the non-user facing parts of the
previous series. While pertaining the user facing components into another set
of patches wherein deeper discussion on the UX can be held without worrying
about the internal implementation. Also by using this new functionality in a
pre-existing command, we can leverage the existing tests to catch any
inconsistencies. One of which was how 'git-symbolic-ref' doesn't add reflog for
dangling symrefs, which I've modified my patch to do the same.

We also modify the reference transaction hook to support symrefs. For any symref
update the reference transaction hook will output the value with a 'ref:' prefix.

Previous versions:
V1: https://lore.kernel.org/git/20240330224623.579457-1-knayak@gitlab.com/
V2: https://lore.kernel.org/git/20240412095908.1134387-1-knayak@gitlab.com/
V3: https://lore.kernel.org/git/20240423212818.574123-1-knayak@gitlab.com/
V4: https://lore.kernel.org/r/20240426152449.228860-1-knayak@gitlab.com

Changes over v4 are:
- Dropped the patches for adding support in git-update-ref.
- Added changes to use transaction in 'refs_create_symref()' and
  deprecate 'create_symref'.
- Cleaned up the commit messages, documentation and comments.
- Added better handling, like if 'old_target' is set, the value of the
  reference is checked, irrelevant of its type.

Range diff:

1:  4a56e3ede4 ! 1:  a354190905 refs: accept symref values in `ref_transaction[_add]_update`
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    refs: accept symref values in `ref_transaction[_add]_update`
    +    refs: accept symref values in `ref_transaction_update()`
     
    -    The `ref_transaction[_add]_update` functions obtain ref information and
    -    flags to create a `ref_update` and add it to the transaction at hand.
    +    The function `ref_transaction_update()` obtains ref information and
    +    flags to create a `ref_update` and add them to the transaction at hand.
     
         To extend symref support in transactions, we need to also accept the
    -    old and new ref targets and process it. In this commit, let's add the
    -    required parameters to the function and modify all call sites.
    +    old and new ref targets and process it. This commit adds the required
    +    parameters to the function and modifies all call sites.
     
         The two parameters added are `new_target` and `old_target`. The
         `new_target` is used to denote what the reference should point to when
    -    the transaction is applied.
    +    the transaction is applied. Some functions allow this parameter to be
    +    NULL, meaning that the reference is not changed.
     
         The `old_target` denotes the value the reference must have before the
         update. Some functions allow this parameter to be NULL, meaning that the
         old value of the reference is not checked.
     
    -    The handling logic of these parameters will be added in consequent
    -    commits as we add symref commands to the '--stdin' mode of
    -    'git-update-ref'.
    +    We also update the internal function `ref_transaction_add_update()`
    +    similarly to take the two new parameters.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs.c: struct ref_update *ref_transaction_add_update(
      		BUG("update called for transaction that is not open");
      
     +	if (old_oid && !is_null_oid(old_oid) && old_target)
    -+		BUG("Only one of old_oid and old_target should be non NULL");
    ++		BUG("only one of old_oid and old_target should be non NULL");
     +	if (new_oid && !is_null_oid(new_oid) && new_target)
    -+		BUG("Only one of new_oid and new_target should be non NULL");
    ++		BUG("only one of new_oid and new_target should be non NULL");
     +
      	FLEX_ALLOC_STR(update, refname, refname);
      	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
    @@ refs.h: struct ref_transaction *ref_transaction_begin(struct strbuf *err);
       *         transaction.
       *
     + *     new_target -- the target reference that the reference will be
    -+ *         update to point to. This takes precedence over new_oid when
    -+ *         set. If the reference is a regular reference, it will be
    -+ *         converted to a symbolic reference.
    ++ *         updated to point to. If the reference is a regular reference,
    ++ *         it will be converted to a symbolic reference. Cannot be set
    ++ *         together with `new_oid`. A copy of this value is made in the
    ++ *         transaction.
     + *
     + *     old_target -- the reference that the reference must be pointing to.
    -+ *         Will only be taken into account when the reference is a symbolic
    -+ *         reference.
    ++ *         Canont be set together with `old_oid`. A copy of this value is
    ++ *         made in the transaction.
     + *
       *     flags -- flags affecting the update, passed to
       *         update_ref_lock(). Possible flags: REF_NO_DEREF,
    @@ refs/refs-internal.h: struct ref_update {
     +	/*
     +	 * If set, point the reference to this value. This can also be
     +	 * used to convert regular references to become symbolic refs.
    ++	 * Cannot be set together with `new_oid`.
     +	 */
     +	const char *new_target;
     +
     +	/*
    -+	 * If set and the reference is a symbolic ref, check that the
    -+	 * reference previously pointed to this value.
    ++	 * If set, check that the reference previously pointed to this
    ++	 * value. Cannot be set together with `old_oid`.
     +	 */
     +	const char *old_target;
     +
2:  496bf14f28 ! 2:  7dff21dbef files-backend: extract out `create_symref_lock`
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    files-backend: extract out `create_symref_lock`
    +    files-backend: extract out `create_symref_lock()`
     
    -    The function `create_symref_locked` creates a symref by creating a
    +    The function `create_symref_locked()` creates a symref by creating a
         '<symref>.lock' file and then committing the symref lock, which creates
         the final symref.
     
    -    Split this into two individual functions `create_and_commit_symref` and
    -    `create_symref_locked`. This way we can create the symref lock and
    -    commit it at different times. This will be used to provide symref
    -    support in `git-update-ref(1)`.
    +    Extract the early half of `create_symref_locked()` into a new helper
    +    function `create_symref_lock()`. Because the name of the new function is
    +    too similar to the original, rename the original to
    +    `create_and_commit_symref()` to avoid confusion.
    +
    +    The new function `create_symref_locked()` can be used to create the
    +    symref lock in a separate step from that of committing it. This allows
    +    to add transactional support for symrefs, where the lock would be
    +    created in the preparation step and the lock would be committed in the
    +    finish step.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     +		return error("unable to fdopen %s: %s",
     +			     get_lock_file_path(&lock->lk), strerror(errno));
     +
    -+	/* no error check; commit_ref will check ferror */
    -+	fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
    ++	if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0)
    ++		return error("unable to fprintf %s: %s",
    ++			     get_lock_file_path(&lock->lk), strerror(errno));
     +	return 0;
     +}
     +
    @@ refs/files-backend.c: static void update_symref_reflog(struct files_ref_store *r
     -	if (commit_ref(lock) < 0)
     -		return error("unable to write symref for %s: %s", refname,
     -			     strerror(errno));
    - 	return 0;
    +-	return 0;
    ++	return ret;
      }
      
    + static int files_create_symref(struct ref_store *ref_store,
     @@ refs/files-backend.c: static int files_create_symref(struct ref_store *ref_store,
      		return -1;
      	}
3:  6337859cbb < -:  ---------- update-ref: add support for 'symref-verify' command
4:  e611cb5a8c < -:  ---------- update-ref: add support for 'symref-delete' command
5:  37f8be2f3f < -:  ---------- update-ref: add support for 'symref-create' command
6:  b385f4d0d7 < -:  ---------- update-ref: add support for 'symref-update' command
7:  ef335e47d1 < -:  ---------- ref: support symrefs in 'reference-transaction' hook
-:  ---------- > 3:  901a586683 refs: support symrefs in 'reference-transaction' hook
-:  ---------- > 4:  6c97f6a660 refs: add support for transactional symref updates
-:  ---------- > 5:  5b55406430 refs: use transaction in `refs_create_symref()`
-:  ---------- > 6:  9e25816e68 refs: rename `refs_create_symref()` to `refs_update_symref()`
-:  ---------- > 7:  3836e25932 refs: remove `create_symref` and associated dead code


Karthik Nayak (7):
  refs: accept symref values in `ref_transaction_update()`
  files-backend: extract out `create_symref_lock()`
  refs: support symrefs in 'reference-transaction' hook
  refs: add support for transactional symref updates
  refs: use transaction in `refs_create_symref()`
  refs: rename `refs_create_symref()` to `refs_update_symref()`
  refs: remove `create_symref` and associated dead code

 Documentation/githooks.txt       |  14 ++-
 branch.c                         |   2 +-
 builtin/branch.c                 |   2 +-
 builtin/fast-import.c            |   5 +-
 builtin/fetch.c                  |   2 +-
 builtin/receive-pack.c           |   1 +
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   1 +
 builtin/update-ref.c             |   1 +
 builtin/worktree.c               |   2 +-
 refs.c                           |  87 +++++++++++----
 refs.h                           |  20 +++-
 refs/debug.c                     |  13 ---
 refs/files-backend.c             | 185 +++++++++++++++++++------------
 refs/packed-backend.c            |   1 -
 refs/refs-internal.h             |  26 ++++-
 refs/reftable-backend.c          | 159 +++++++++-----------------
 sequencer.c                      |   9 +-
 t/helper/test-ref-store.c        |   2 +-
 t/t0610-reftable-basics.sh       |   2 +-
 t/t1416-ref-transaction-hooks.sh |  23 ++++
 walker.c                         |   2 +-
 22 files changed, 321 insertions(+), 240 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 1%]

* [PATCH] completion: zsh: stop leaking local cache variable
@ 2024-04-30 21:53  3% D. Ben Knoble via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: D. Ben Knoble via GitGitGadget @ 2024-04-30 21:53 UTC (permalink / raw)
  To: git; +Cc: Felipe Contreras, D. Ben Knoble, D. Ben Knoble

From: "D. Ben Knoble" <ben.knoble+github@gmail.com>

Completing commands like "git rebase" in one repository will leak the
local __git_repo_path into the shell's environment so that completing
commands after changing to a different repository will give the old
repository's references (or none at all).

The bug report on the mailing list [1] suggests one simple way to observe
this yourself:

Enter the following commands from some directory:
  mkdir a b b/c
  for d (a b); git -C $d init && git -C $d commit --allow-empty -m init
  cd a
  git branch foo
  pushd ../b/c
  git branch bar

Now type these:
  git rebase <TAB>… # completion for bar available; C-c to abort
  declare -p __git_repo_path # outputs /path/to/b/.git
  popd
  git branch # outputs foo, main
  git rebase <TAB>… # completion candidates are bar, main!

Ideally, the last typed <TAB> should be yielding foo, main.

Commit beb6ee7163 (completion: extract repository discovery from
__gitdir(), 2017-02-03) anticipated this problem by marking
__git_repo_path as local in __git_main and __gitk_main for Bash
completion but did not give the same mark to _git for Zsh completion.
Thus make __git_repo_path local for Zsh completion, too.

[1]: https://lore.kernel.org/git/CALnO6CBv3+e2WL6n6Mh7ZZHCX2Ni8GpvM4a-bQYxNqjmgZdwdg@mail.gmail.com/

Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
---
    completion: zsh: stop leaking local cache variable
    
    Prevent leaking a local variable used to cache the repo path, which
    breaks future completions in different repositories using the shell,
    when using contributed Zsh completion.
    
    ------------------------------------------------------------------------
    
    I made a few attempts at starting a test script for this based on
    https://unix.stackexchange.com/a/668827/301073, but that code doesn't
    work and it was all becoming precariously complicated (sh starting zsh
    to start zsh in a pty which would receive keystrokes and check specific
    outputs: I couldn't make certain pieces work in a normal way locally,
    let alone as part of Git's test suite). Suffice to say I have tested
    this myself?

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1725%2Fbenknoble%2Ffix-zsh-completion-repo-cache-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1725/benknoble/fix-zsh-completion-repo-cache-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1725

 contrib/completion/git-completion.zsh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index cac6f618817..f5877bd7a1e 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -272,6 +272,7 @@ _git ()
 {
 	local _ret=1
 	local cur cword prev
+	local __git_repo_path
 
 	cur=${words[CURRENT]}
 	prev=${words[CURRENT-1]}

base-commit: 786a3e4b8d754d2b14b1208b98eeb0a554ef19a8
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* [PATCH] scalar: avoid segfault in reconfigure --all
@ 2024-04-30 16:58  3% Derrick Stolee via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Derrick Stolee via GitGitGadget @ 2024-04-30 16:58 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Derrick Stolee

From: Derrick Stolee <stolee@gmail.com>

During the latest v2.45.0 update, 'scalar reconfigure --all' started to
segfault on my machine. Breaking it down via the debugger, it was
faulting on a NULL reference to the_hash_algo, which is a macro pointing
to the_repository->hash_algo.

This NULL reference appears to be due to the way the loop is abusing the
the_repository pointer, pointing it to a local repository struct after
discovering that the current directory is a valid Git repository. This
repo-swapping bit was in the original implementation from 4582676075
(scalar: teach 'reconfigure' to optionally handle all registered
enlistments, 2021-12-03), but only recently started segfaulting while
trying to parse the HEAD reference. This also only happens on the
_second_ repository in the list, so does not reproduce if there is only
one registered repo.

My first inclination was to try to refactor cmd_reconfigure() to execute
'git for-each-repo' instead of this loop. In addition to the difficulty
of executing 'scalar reconfigure' within 'git for-each-repo', it would
be difficult to perform the clean-up logic for non-existent repos if we
relied on that child process.

Instead, I chose to move the temporary repo to be within the loop and
reinstate the_repository to its old value after we are done performing
logic on the current array item.

Add a test to t9210-scalar.sh to test 'scalar reconfigure --all' with
multiple registered repos, as a precaution. Unfortunately, I was unable
to reproduce the segfault using this test, so there is some coverage
left to be desired. What exactly causes my setup to hit this bug but not
this test structure? Unclear.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
    scalar: avoid segfault in reconfigure --all
    
    I noticed this while validating the v2.45.0 release (specifically the
    microsoft/git fork, but this applies to the core project).
    
    I couldn't figure out if or why this changed during this cycle. There
    were some changes in setup.c (30b7c4bdca (setup: notice more types of
    implicit bare repositories, 2024-03-09) and 45bb916248 (setup: allow
    cwd=.git w/ bareRepository=explicit, 2024-01-20)) but they seemed
    innocuous and were not in the stack trace of the segfault.
    
    After asking around, it seems like this kind of issue has been happening
    in older versions, but users were ignoring the error during the
    installer or was hidden by a silent installer.
    
    -Stolee

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1724%2Fderrickstolee%2Fscalar-reconfigure-repo-handle-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1724/derrickstolee/scalar-reconfigure-repo-handle-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1724

 scalar.c          | 10 +++++++---
 t/t9210-scalar.sh | 17 +++++++++++++++++
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/scalar.c b/scalar.c
index fb2940c2a00..7234049a1b8 100644
--- a/scalar.c
+++ b/scalar.c
@@ -645,7 +645,6 @@ static int cmd_reconfigure(int argc, const char **argv)
 	};
 	struct string_list scalar_repos = STRING_LIST_INIT_DUP;
 	int i, res = 0;
-	struct repository r = { NULL };
 	struct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;
 
 	argc = parse_options(argc, argv, NULL, options,
@@ -665,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv)
 
 	for (i = 0; i < scalar_repos.nr; i++) {
 		int succeeded = 0;
+		struct repository *old_repo, r = { NULL };
 		const char *dir = scalar_repos.items[i].string;
 
 		strbuf_reset(&commondir);
@@ -712,13 +712,17 @@ static int cmd_reconfigure(int argc, const char **argv)
 
 		git_config_clear();
 
+		if (repo_init(&r, gitdir.buf, commondir.buf))
+			goto loop_end;
+
+		old_repo = the_repository;
 		the_repository = &r;
-		r.commondir = commondir.buf;
-		r.gitdir = gitdir.buf;
 
 		if (set_recommended_config(1) >= 0)
 			succeeded = 1;
 
+		the_repository = old_repo;
+
 loop_end:
 		if (!succeeded) {
 			res = -1;
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 428339e3427..a696337b055 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -180,6 +180,23 @@ test_expect_success 'scalar reconfigure' '
 	test true = "$(git -C one/src config core.preloadIndex)"
 '
 
+test_expect_success 'scalar reconfigure --all' '
+	repos="two three four" &&
+	for num in $repos
+	do
+		git init $num/src &&
+		scalar register $num/src &&
+		git -C $num/src config core.preloadIndex false || return 1
+	done &&
+
+	scalar reconfigure --all &&
+
+	for num in $repos
+	do
+		test true = "$(git -C $num/src config core.preloadIndex)" || return 1
+	done
+'
+
 test_expect_success '`reconfigure -a` removes stale config entries' '
 	git init stale/src &&
 	scalar register stale &&

base-commit: e326e520101dcf43a0499c3adc2df7eca30add2d
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* Re: [PATCH v3 1/1] advice: add --no-advice global option
  @ 2024-04-30 16:29  2%       ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-30 16:29 UTC (permalink / raw)
  To: James Liu; +Cc: git

James Liu <james@jamesliu.io> writes:

> Advice hints must be disabled individually by setting the relevant
> advice.* variables to false in the Git configuration. For server-side
> and scripted usages of Git where hints aren't necessary, it can be
> cumbersome to maintain configuration to ensure all advice hints are

It is not that scripted use decline hints because they are not
"necessary"; it is they find the hints harmful for whatever reason.

> diff --git a/Documentation/git.txt b/Documentation/git.txt
> index 7a1b112a3e..ef1d9dce5d 100644
> --- a/Documentation/git.txt
> +++ b/Documentation/git.txt
> @@ -13,7 +13,7 @@ SYNOPSIS
>      [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
>      [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
>      [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
> -    [--config-env=<name>=<envvar>] <command> [<args>]
> +    [--config-env=<name>=<envvar>] [--no-advice] <command> [<args>]

Move the new one near [--no-replace-objects] to group the --no-*
options together.

This is not a fault of this patch, but I notice "--no-lazy-fetch"
and "--no-optional-locks" are not listed here (there may be others,
I didn't check too deeply).  It might make sense to have a separate
clean-up step to move the --no-* "ordinarily we would never want to
disable these wholesale, but for very special needs here are the
knobs to toggle them off" options together in the DESCRIPTION
section and add missing ones to the SYNOPSIS section.

> diff --git a/advice.c b/advice.c
> index 75111191ad..abad9ccaa2 100644
> --- a/advice.c
> +++ b/advice.c
> @@ -2,6 +2,7 @@
>  #include "advice.h"
>  #include "config.h"
>  #include "color.h"
> +#include "environment.h"
>  #include "gettext.h"
>  #include "help.h"
>  #include "string-list.h"
> @@ -126,7 +127,12 @@ void advise(const char *advice, ...)
>  
>  int advice_enabled(enum advice_type type)
>  {
> -	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
> +	int enabled;
> +
> +	if (!git_env_bool(GIT_ADVICE, 1))
> +		return 0;

How expensive is it to check git_env_bool() every time without
caching the parsing of the value given to the environment variable?
Everybody pays this price but for most users who do not set the
environment variable it is not a price we want them to pay.

One way to solve that might be to just insert these

	static int advice_enabled = -1;

	if (advice_enabled < 0)
		advice_enabled = git_env_bool(GIT_ADVICE_ENVIRONMENT, 1)
	if (!advice_enabled)
		return 0;

and leave everything else intact.  We do not even need to split the
declalation+initialization into a ...

> +	enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;

... separate assignment.

> +/*
> + * Environment variable used to propagate the --no-advice global option to the
> + * advice_enabled() helper, even when run in a subprocess.
> + * This is an internal variable that should not be set by the user.
> + */
> +#define GIT_ADVICE "GIT_ADVICE"

As Patrick pointed out, this should be GIT_ADVICE_ENVIRONMENT or
something.

> diff --git a/git.c b/git.c
> index 654d615a18..6283d126e5 100644
> --- a/git.c
> +++ b/git.c
> @@ -38,7 +38,7 @@ const char git_usage_string[] =
>  	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
>  	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
>  	   "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
> -	   "           [--config-env=<name>=<envvar>] <command> [<args>]");
> +	   "           [--config-env=<name>=<envvar>] [--no-advice] <command> [<args>]");

Ditto.

> diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
> index 0dcfb760a2..2ce2d4668a 100755
> --- a/t/t0018-advice.sh
> +++ b/t/t0018-advice.sh
> @@ -29,4 +29,24 @@ test_expect_success 'advice should not be printed when config variable is set to
>  	test_must_be_empty actual
>  '
>  
> +test_expect_success 'advice should not be printed when --no-advice is used' '
> +	cat << EOF > expect &&

Style.  Between the redirection operator and the file that the
operator operates on, there should be no SP.

> +On branch master
> +
> +No commits yet
> +
> +Untracked files:
> +	README

Something like

	q_to_tab >expect <<\-EOF
	On branch master
	...
	Untracked files:
	QREADME
	
	nothing added to ...
	EOF

or

	sed -e "s/^|//" >expect <<\-EOF
	On branch master
	...
	Untracked files:
	|	README
	
	nothing added to ...
	EOF

would work better.

> +	git init advice-test &&
> +  test_when_finished "rm -fr advice-test" &&

Funny indentation.  Also if "git init" fails in the middle, after
creating the directory but before completing, your when-finished
handler fails to register.

> +  cd advice-test &&

Do not chdir around without being inside a subshell.  If we have to
add new tests later to this script, you do not want them to start in
the directory that you are going to remove at the end of this test.

> +  touch README &&

When you do not care about the timestamp, avoid using "touch".  Writing

	>README &&

instead would make it clear that you ONLY care about the presense of
the file, not even its contents.

> +  git --no-advice status > ../actual &&
> +  test_cmp ../expect ../actual

So, taken together:

	(
		cd advice-test &&
		>README &&
		git --no-advice status
	) >actual &&
	test_cmp expect actual

> +'
> +
>  test_done

Thanks.


^ permalink raw reply	[relevance 2%]

* [PATCH] refs: return conflict error when checking packed refs
@ 2024-04-30 14:50  4% Ivan Tse via GitGitGadget
  2024-05-02 12:38  0% ` Patrick Steinhardt
  2024-05-03  4:50 19% ` [PATCH v2] " Ivan Tse via GitGitGadget
  0 siblings, 2 replies; 200+ results
From: Ivan Tse via GitGitGadget @ 2024-04-30 14:50 UTC (permalink / raw)
  To: git; +Cc: Ivan Tse, Ivan Tse

From: Ivan Tse <ivan.tse1@gmail.com>

The TRANSACTION_NAME_CONFLICT error code refers to a failure to create a
ref due to a name conflict with another ref. An example of this is a
directory/file conflict such as ref names A/B and A.

"git fetch" uses this error code to more accurately describe the error
by recommending to the user that they try running "git remote prune" to
remove any old refs that are deleted by the remote which would clear up
any directory/file conflicts.

This helpful error message is not displayed when the conflicted ref is
stored in packed refs. This change fixes this by ensuring error return
code consistency in `lock_raw_ref`.

Signed-off-by: Ivan Tse <ivan.tse1@gmail.com>
---
    refs: return conflict error when checking packed refs
    
    I wanted to provide extra context around the error message I'm referring
    to so it's clear what I'm trying to achieve.
    
    First, let's get into a directory/file conflict situation:
    
    git branch dir_file_conflict
    git push origin dir_file_conflict
    git update-ref -d refs/remotes/origin/dir_file_conflict
    git update-ref -d refs/heads/dir_file_conflict
    git update-ref refs/remotes/origin/dir_file_conflict/file master
    
    
    From above, the remote origin has a dir_file_conflict ref name and the
    local repository has a dir_file_conflict/file ref name for the remote.
    This situation can occur if dir_file_conflict/file used to exist and but
    was later deleted in the remote.
    
    Then when we git fetch:
    
    yolo10[master] $ git fetch
    error: cannot lock ref 'refs/remotes/origin/dir_file_conflict': 'refs/remotes/origin/dir_file_conflict/file' exists; cannot create 'refs/remotes/origin/dir_file_conflict'
    From github.com:ivantsepp/yolo10
     ! [new branch]      dir_file_conflict -> origin/dir_file_conflict  (unable to update local ref)
    error: some local refs could not be updated; try running
     'git remote prune origin' to remove any old, conflicting branches
    
    
    We get the helpful error message to run git remote prune origin to
    remove old, conflicting branches.
    
    However, when the ref is stored in packed refs:
    
    yolo10[master] $ git pack-refs --all
    yolo10[master] $ git fetch
    error: cannot lock ref 'refs/remotes/origin/dir_file_conflict': 'refs/remotes/origin/dir_file_conflict/file' exists; cannot create 'refs/remotes/origin/dir_file_conflict'
    From github.com:ivantsepp/yolo10
     ! [new branch]      dir_file_conflict -> origin/dir_file_conflict  (unable to update local ref)
    
    
    The helpful message is not there! I believe this error message should
    show up regardless of how the ref is stored (loose vs packed-refs). I
    attempted to track down the necessary change to make this happen and it
    seems like a straightforward change. I hope I didn't overlook anything!

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1716%2Fivantsepp%2Freturn_name_conflict_error_for_packed_refs-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1716/ivantsepp/return_name_conflict_error_for_packed_refs-v1
Pull-Request: https://github.com/git/git/pull/1716

 refs/files-backend.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea00..97473f377d1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -794,8 +794,10 @@ static int lock_raw_ref(struct files_ref_store *refs,
 		 */
 		if (refs_verify_refname_available(
 				    refs->packed_ref_store, refname,
-				    extras, NULL, err))
+				    extras, NULL, err)) {
+			ret = TRANSACTION_NAME_CONFLICT;
 			goto error_return;
+		}
 	}
 
 	ret = 0;

base-commit: 786a3e4b8d754d2b14b1208b98eeb0a554ef19a8
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [PATCH v4 0/3] builtin/tag.c: add --trailer option
  2024-04-29 18:54  3%   ` [PATCH v3 0/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
@ 2024-04-30 14:41  3%     ` John Passaro via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: John Passaro via GitGitGadget @ 2024-04-30 14:41 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Patrick Steinhardt, John Passaro, John Passaro

4th follow-up patch taking welcome feedback from Patrick and JCH. Net new
changes include separating from a 2-patch series to 3.

Since git-tag --list --format="%(trailers)" can interpret trailers from
annotated tag messages, it seems natural to support --trailer when writing a
new tag message.

git-commit accomplishes this by taking --trailer arguments and passing them
to git-interpret-trailer. This patch series refactors that logic and uses it
to implement --trailer on git-tag.

John Passaro (3):
  builtin/commit.c: remove bespoke option callback
  builtin/commit.c: refactor --trailer logic
  builtin/tag.c: add --trailer option

 Documentation/git-tag.txt |  18 +++++-
 builtin/commit.c          |  20 +------
 builtin/tag.c             |  41 +++++++++++---
 t/t7004-tag.sh            | 114 ++++++++++++++++++++++++++++++++++++++
 trailer.c                 |  11 ++++
 trailer.h                 |   9 +++
 6 files changed, 185 insertions(+), 28 deletions(-)


base-commit: 786a3e4b8d754d2b14b1208b98eeb0a554ef19a8
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1723%2Fjpassaro%2Fjp%2Ftag-trailer-arg-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1723/jpassaro/jp/tag-trailer-arg-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1723

Range-diff vs v3:

 -:  ----------- > 1:  ce047c58aa8 builtin/commit.c: remove bespoke option callback
 1:  0c9517f434a ! 2:  8f53a54bbfe builtin/commit.c: refactor --trailer logic
     @@ Commit message
          Let's move this logic from git-commit to a new function in the trailer
          API, so that it can be re-used in other commands.
      
     -    Additionally, replace git-commit's bespoke callback for --trailer with
     -    the standard OPT_PASSTHRU_ARGV macro. This bespoke callback was only
     -    adding its values to a strvec and sanity-checking that `unset` is always
     -    false; both of these are already implemented in the parse-option API.
     -
     -    Signed-off-by: John Passaro <john.a.passaro@gmail.com>
          Helped-by: Patrick Steinhardt <ps@pks.im>
          Helped-by: Junio C Hamano <gitster@pobox.com>
     +    Signed-off-by: John Passaro <john.a.passaro@gmail.com>
      
       ## builtin/commit.c ##
      @@
     @@ builtin/commit.c
       
       static const char * const builtin_commit_usage[] = {
       	N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
     -@@ builtin/commit.c: static struct strbuf message = STRBUF_INIT;
     - 
     - static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
     - 
     --static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
     --{
     --	BUG_ON_OPT_NEG(unset);
     --
     --	strvec_pushl(opt->value, "--trailer", arg, NULL);
     --	return 0;
     --}
     --
     - static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
     - {
     - 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
      @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
       	fclose(s->fp);
       
     @@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const cha
       			die(_("unable to pass trailers to --trailers"));
       		strvec_clear(&trailer_args);
       	}
     -@@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
     - 		OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
     - 		OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
     - 		OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
     --		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
     -+		OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
     - 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
     - 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
     - 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
      
       ## trailer.c ##
     -@@
     - #include "commit.h"
     - #include "trailer.h"
     - #include "list.h"
     -+#include "run-command.h"
     - /*
     -  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
     -  */
      @@ trailer.c: void trailer_iterator_release(struct trailer_iterator *iter)
       	strbuf_release(&iter->val);
       	strbuf_release(&iter->key);
       }
      +
     -+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
     ++int amend_file_with_trailers(const char *path, const struct strvec *trailer_args) {
      +	struct child_process run_trailer = CHILD_PROCESS_INIT;
      +
      +	run_trailer.git_cmd = 1;
     @@ trailer.c: void trailer_iterator_release(struct trailer_iterator *iter)
      
       ## trailer.h ##
      @@
     - 
       #include "list.h"
       #include "strbuf.h"
     -+#include "strvec.h"
       
     ++struct strvec;
     ++
       enum trailer_where {
       	WHERE_DEFAULT,
     + 	WHERE_END,
      @@ trailer.h: int trailer_iterator_advance(struct trailer_iterator *iter);
        */
       void trailer_iterator_release(struct trailer_iterator *iter);
     @@ trailer.h: int trailer_iterator_advance(struct trailer_iterator *iter);
      + * This calls run_command() and its return value is the same (i.e. 0 for
      + * success, various non-zero for other errors). See run-command.h.
      + */
     -+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
     ++int amend_file_with_trailers(const char *path, const struct strvec *trailer_args);
      +
       #endif /* TRAILER_H */
 2:  5b6239167b8 ! 3:  f1d68337eda builtin/tag.c: add --trailer arg
     @@ Metadata
      Author: John Passaro <john.a.passaro@gmail.com>
      
       ## Commit message ##
     -    builtin/tag.c: add --trailer arg
     +    builtin/tag.c: add --trailer option
      
          git-tag currently supports interpreting trailers from an annotated tag
     -    message, using --list --format="%(trailers)". There is no ergonomic way
     -    to add trailers to an annotated tag message.
     +    message, using --list --format="%(trailers)". However, to add a trailer
     +    to a tag message, you must do so using `-F` or by editing the message.
      
     -    In a previous patch, we refactored git-commit's implementation of its
     -    --trailer arg to the trailer.h API. Let's use that new function to teach
     -    git-tag the same --trailer argument, emulating as much of git-commit's
     -    behavior as much as possible.
     +    In a previous patch, we moved git-commit's implementation of its
     +    --trailer option to the trailer.h API. Let's use that new function to
     +    teach git-tag the same --trailer option, emulating as much of
     +    git-commit's behavior as much as possible.
      
     -    Signed-off-by: John Passaro <john.a.passaro@gmail.com>
          Helped-by: Patrick Steinhardt <ps@pks.im>
     +    Signed-off-by: John Passaro <john.a.passaro@gmail.com>
      
       ## Documentation/git-tag.txt ##
      @@ Documentation/git-tag.txt: SYNOPSIS
 3:  d5335e30b0b < -:  ----------- po: update git-tag translations

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs
  2024-04-30 12:26 19%   ` [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
@ 2024-04-30 12:49  0%     ` Karthik Nayak
  0 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-30 12:49 UTC (permalink / raw)
  To: Patrick Steinhardt, git
  Cc: Jeff King, Phillip Wood, Junio C Hamano, Justin Tobler

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

Patrick Steinhardt <ps@pks.im> writes:

> Nowadays, Git knows about three different kinds of refs. As defined in
> gitglossary(7):
>
>   - Regular refs that start with "refs/", like "refs/heads/main".
>
>   - Pseudorefs, which live in the root directory. These must have
>     all-caps names and must be a file that start with an object hash.
>     Consequently, symbolic refs are not pseudorefs because they do not
>     start with an object hash.
>
>   - Special refs, of which we only have "FETCH_HEAD" and "MERGE_HEAD".
>

Nit: but since you go into explaining what the _old_ pseudoref is,
perhaps you should also add a line about why "FETCH_HEAD" and
"MERGE_HEAD" were called special refs.

> This state is extremely confusing, and I would claim that most folks
> don't fully understand what is what here. The current definitions also
> have several problems:
>
>   - Where does "HEAD" fit in? It's not a pseudoref because it can be
>     a symbolic ref. It's not a regular ref because it does not start
>     with "refs/". And it's not a special ref, either.
>
>   - There is a strong overlap between pseudorefs and special refs. The
>     pseudoref section for example mentions "MERGE_HEAD", even though it
>     is a special ref. Is it thus both a pseudoref and a special ref?
>
>   - Why do we even need to distinguish refs that live in the root from
>     other refs when they behave just like a regular ref anyway?
>
> In other words, the current state is quite a mess and leads to wild
> inconsistencies without much of a good reason.
>
> The original reason why pseudorefs were introduced is that there are
> some refs that sometimes behave like a ref, even though they aren't a
> ref. And we really only have two of these nowadads, namely "MERGE_HEAD"
> and "FETCH_HEAD". Those files are never written via the ref backends,
> but are instead written by git-fetch(1), git-pull(1) and git-merge(1).
> They contain additional metadata that hihlights where a ref has been

s/hihlights/highlights

> fetched from or the list of commits that have been merged.

This is good detail and I guess you can skip my earlier suggestion.

> This original intent in fact matches the definition of special refs that
> we have recently introduced in 8df4c5d205 (Documentation: add "special
> refs" to the glossary, 2024-01-19). Due to the introduction of the new
> reftable backend we were forced to distinguish those refs more clearly
> such that we don't ever try to read or write them via the reftable
> backend. In the same series, we also addressed all the other cases where
> we used to write those special refs via the filesystem directly, thus
> circumventing the ref backend, to instead write them via the backends.
> Consequently, there are no other refs left anymore which are special.
>
> Let's address this mess and return the pseudoref terminology back to its
> original intent: a ref that sometimes behave like a ref, but which isn't
> really a ref because it gets written to the filesystem directly. Or in
> other words, let's redefine pseudorefs to match the current definition
> of special refs. As special refs and pseudorefs are now the same per
> definition, we can drop the "special refs" term again. It's not exposed
> to our users and thus they wouldn't ever encounter that term anyway.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  Documentation/glossary-content.txt | 42 +++++++++---------------------
>  1 file changed, 13 insertions(+), 29 deletions(-)
>
> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index d71b199955..f5c0f49150 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -496,21 +496,19 @@ exclude;;
>  	that start with `refs/bisect/`, but might later include other
>  	unusual refs.
>
> -[[def_pseudoref]]pseudoref::
> -	Pseudorefs are a class of files under `$GIT_DIR` which behave
> -	like refs for the purposes of rev-parse, but which are treated
> -	specially by git.  Pseudorefs both have names that are all-caps,
> -	and always start with a line consisting of a
> -	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
> -	pseudoref, because it is sometimes a symbolic ref.  They might
> -	optionally contain some additional data.  `MERGE_HEAD` and
> -	`CHERRY_PICK_HEAD` are examples.  Unlike
> -	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
> -	be symbolic refs, and never have reflogs.  They also cannot be
> -	updated through the normal ref update machinery.  Instead,
> -	they are updated by directly writing to the files.  However,
> -	they can be read as if they were refs, so `git rev-parse
> -	MERGE_HEAD` will work.
> +[[def_pseudoref]]pseudoref ref::

shouldn't this just be 'pseudoref'?

> +	A ref that has different semantics than normal refs. These refs can be
> +	accessed via normal Git commands but may not behave the same as a
> +	normal ref in some cases.
> ++
> +The following pseudorefs are known to Git:
> +
> + - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
> +   may refer to multiple object IDs. Each object ID is annotated with metadata
> +   indicating where it was fetched from and its fetch status.
> +
> + - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
> +   conflicts. It contains all commit IDs which are being merged.
>
>  [[def_pull]]pull::
>  	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
> @@ -638,20 +636,6 @@ The most notable example is `HEAD`.
>  	An <<def_object,object>> used to temporarily store the contents of a
>  	<<def_dirty,dirty>> working directory and the index for future reuse.
>
> -[[def_special_ref]]special ref::
> -	A ref that has different semantics than normal refs. These refs can be
> -	accessed via normal Git commands but may not behave the same as a
> -	normal ref in some cases.
> -+
> -The following special refs are known to Git:
> -
> - - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
> -   may refer to multiple object IDs. Each object ID is annotated with metadata
> -   indicating where it was fetched from and its fetch status.
> -
> - - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
> -   conflicts. It contains all commit IDs which are being merged.
> -
>  [[def_submodule]]submodule::
>  	A <<def_repository,repository>> that holds the history of a
>  	separate project inside another repository (the latter of
> --
> 2.45.0

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

^ permalink raw reply	[relevance 0%]

* [PATCH v2 10/10] refs: refuse to write pseudorefs
  2024-04-30 12:26  2% ` [PATCH v2 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
  2024-04-30 12:26 19%   ` [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
@ 2024-04-30 12:27 28%   ` Patrick Steinhardt
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-30 12:27 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler

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

Pseudorefs are not stored in the ref database as by definition, they
carry additional metadata that essentially makes them not a ref. As
such, writing pseudorefs via the ref backend does not make any sense
whatsoever as the ref backend wouldn't know how exactly to store the
data.

Restrict writing pseudorefs via the ref backend.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs.c           | 7 +++++++
 t/t5510-fetch.sh | 6 +++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/refs.c b/refs.c
index 50d679b7e7..7c3c7465a4 100644
--- a/refs.c
+++ b/refs.c
@@ -1307,6 +1307,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
 		return -1;
 	}
 
+	if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
+	    is_pseudo_ref(refname)) {
+		strbuf_addf(err, _("refusing to update pseudoref '%s'"),
+			    refname);
+		return -1;
+	}
+
 	if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
 		BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
 
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 33d34d5ae9..4eb569f4df 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -518,7 +518,7 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' '
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [1]' '
 	one_head=$(cd one && git rev-parse HEAD) &&
 	this_head=$(git rev-parse HEAD) &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -530,7 +530,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 	one_ref=$(cd one && git symbolic-ref HEAD) &&
 	git config branch.main.remote blub &&
 	git config branch.main.merge "$one_ref" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
@@ -540,7 +540,7 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 # the merge spec does not match the branch the remote HEAD points to
 test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge [3]' '
 	git config branch.main.merge "${one_ref}_not" &&
-	git update-ref -d FETCH_HEAD &&
+	rm .git/FETCH_HEAD &&
 	git fetch one &&
 	test $one_head = "$(git rev-parse --verify FETCH_HEAD)" &&
 	test $this_head = "$(git rev-parse --verify HEAD)"
-- 
2.45.0


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

^ permalink raw reply related	[relevance 28%]

* [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs
  2024-04-30 12:26  2% ` [PATCH v2 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
@ 2024-04-30 12:26 19%   ` Patrick Steinhardt
  2024-04-30 12:49  0%     ` Karthik Nayak
  2024-04-30 12:27 28%   ` [PATCH v2 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
  1 sibling, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-30 12:26 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler

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

Nowadays, Git knows about three different kinds of refs. As defined in
gitglossary(7):

  - Regular refs that start with "refs/", like "refs/heads/main".

  - Pseudorefs, which live in the root directory. These must have
    all-caps names and must be a file that start with an object hash.
    Consequently, symbolic refs are not pseudorefs because they do not
    start with an object hash.

  - Special refs, of which we only have "FETCH_HEAD" and "MERGE_HEAD".

This state is extremely confusing, and I would claim that most folks
don't fully understand what is what here. The current definitions also
have several problems:

  - Where does "HEAD" fit in? It's not a pseudoref because it can be
    a symbolic ref. It's not a regular ref because it does not start
    with "refs/". And it's not a special ref, either.

  - There is a strong overlap between pseudorefs and special refs. The
    pseudoref section for example mentions "MERGE_HEAD", even though it
    is a special ref. Is it thus both a pseudoref and a special ref?

  - Why do we even need to distinguish refs that live in the root from
    other refs when they behave just like a regular ref anyway?

In other words, the current state is quite a mess and leads to wild
inconsistencies without much of a good reason.

The original reason why pseudorefs were introduced is that there are
some refs that sometimes behave like a ref, even though they aren't a
ref. And we really only have two of these nowadads, namely "MERGE_HEAD"
and "FETCH_HEAD". Those files are never written via the ref backends,
but are instead written by git-fetch(1), git-pull(1) and git-merge(1).
They contain additional metadata that hihlights where a ref has been
fetched from or the list of commits that have been merged.

This original intent in fact matches the definition of special refs that
we have recently introduced in 8df4c5d205 (Documentation: add "special
refs" to the glossary, 2024-01-19). Due to the introduction of the new
reftable backend we were forced to distinguish those refs more clearly
such that we don't ever try to read or write them via the reftable
backend. In the same series, we also addressed all the other cases where
we used to write those special refs via the filesystem directly, thus
circumventing the ref backend, to instead write them via the backends.
Consequently, there are no other refs left anymore which are special.

Let's address this mess and return the pseudoref terminology back to its
original intent: a ref that sometimes behave like a ref, but which isn't
really a ref because it gets written to the filesystem directly. Or in
other words, let's redefine pseudorefs to match the current definition
of special refs. As special refs and pseudorefs are now the same per
definition, we can drop the "special refs" term again. It's not exposed
to our users and thus they wouldn't ever encounter that term anyway.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/glossary-content.txt | 42 +++++++++---------------------
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index d71b199955..f5c0f49150 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -496,21 +496,19 @@ exclude;;
 	that start with `refs/bisect/`, but might later include other
 	unusual refs.
 
-[[def_pseudoref]]pseudoref::
-	Pseudorefs are a class of files under `$GIT_DIR` which behave
-	like refs for the purposes of rev-parse, but which are treated
-	specially by git.  Pseudorefs both have names that are all-caps,
-	and always start with a line consisting of a
-	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
-	pseudoref, because it is sometimes a symbolic ref.  They might
-	optionally contain some additional data.  `MERGE_HEAD` and
-	`CHERRY_PICK_HEAD` are examples.  Unlike
-	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
-	be symbolic refs, and never have reflogs.  They also cannot be
-	updated through the normal ref update machinery.  Instead,
-	they are updated by directly writing to the files.  However,
-	they can be read as if they were refs, so `git rev-parse
-	MERGE_HEAD` will work.
+[[def_pseudoref]]pseudoref ref::
+	A ref that has different semantics than normal refs. These refs can be
+	accessed via normal Git commands but may not behave the same as a
+	normal ref in some cases.
++
+The following pseudorefs are known to Git:
+
+ - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+   may refer to multiple object IDs. Each object ID is annotated with metadata
+   indicating where it was fetched from and its fetch status.
+
+ - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
+   conflicts. It contains all commit IDs which are being merged.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -638,20 +636,6 @@ The most notable example is `HEAD`.
 	An <<def_object,object>> used to temporarily store the contents of a
 	<<def_dirty,dirty>> working directory and the index for future reuse.
 
-[[def_special_ref]]special ref::
-	A ref that has different semantics than normal refs. These refs can be
-	accessed via normal Git commands but may not behave the same as a
-	normal ref in some cases.
-+
-The following special refs are known to Git:
-
- - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
-   may refer to multiple object IDs. Each object ID is annotated with metadata
-   indicating where it was fetched from and its fetch status.
-
- - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
-   conflicts. It contains all commit IDs which are being merged.
-
 [[def_submodule]]submodule::
 	A <<def_repository,repository>> that holds the history of a
 	separate project inside another repository (the latter of
-- 
2.45.0


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

^ permalink raw reply related	[relevance 19%]

* [PATCH v2 00/10] Clarify pseudo-ref terminology
    2024-04-29 13:41  6% ` [PATCH 2/3] refs: do not label special refs as pseudo refs Patrick Steinhardt
@ 2024-04-30 12:26  2% ` Patrick Steinhardt
  2024-04-30 12:26 19%   ` [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
  2024-04-30 12:27 28%   ` [PATCH v2 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
  2024-05-02  8:17  3% ` [PATCH v3 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
  2 siblings, 2 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-30 12:26 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Karthik Nayak, Phillip Wood, Junio C Hamano,
	Justin Tobler

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

Hi,

this is the second version of my patch series that tries to clarify the
pseudoref terminology.

As I have alluded to in my first version of this patch series, I'd
really like to return the pseudoref terminology back to its original
roots. Namely, a pseudoref is not a ref and does not conform to the
format of refs as they may contain additional metadata. With the new
definition, we really only have two pseudorefs: FETCH_HEAD and
MERGE_HEAD.

This has multiple consequences:

  - Pseudorefs are never stored via the ref backend.

  - Pseudorefs can be read via tools like git-rev-parse(1).

  - Pseudorefs are not surfaced by tools like git-for-each-ref(1). They
    are not refs, so a tool that goes through all refs should not
    surface them.

  - Pseudorefs cannot be written via tools like git-update-ref(1). They
    are always written by the respective subsystems that create them via
    the filesystem directly.

  - All other refs in the root hierarchy are just plain refs. They are
    not special. They can be symbolic or regular refs. The only thing of
    notice here is a bunch of restrictions that they have in place
    regarding their naming.

  - Special refs are no more. Or rather, special refs are the new
    pseudorefs.

Overall, this significantly simplifies our whole terminology around
refs that most people didn't really understand in the first place,
including myself. Furthermore, it makes it so that the ref backends
don't need to know about any policy except for what is a proper ref
name. Whether refs should be symbolic or direct refs is higher-level
logic that belongs in the respective subsystems, and the ref backends
should not stand in the way as generic vessels for refs.

This patch series makes the necessary changes to out glossary as well as
the code.

Patrick

Patrick Steinhardt (10):
  Documentation/glossary: redefine pseudorefs as special refs
  Documentation/glossary: clarify limitations of pseudorefs
  Documentation/glossary: define root refs as refs
  refs: rename `is_pseudoref()` to `is_root_ref()`
  refs: refname `is_special_ref()` to `is_pseudo_ref()`
  refs: classify HEAD as a root ref
  refs: root refs can be symbolic refs
  refs: pseudorefs are no refs
  ref-filter: properly distinuish pseudo and root refs
  refs: refuse to write pseudorefs

 Documentation/glossary-content.txt |  75 +++++++++---------
 builtin/for-each-ref.c             |   2 +-
 ref-filter.c                       |  16 ++--
 ref-filter.h                       |   4 +-
 refs.c                             | 120 ++++++++++++++++-------------
 refs.h                             |  50 +++++++++++-
 refs/files-backend.c               |   3 +-
 refs/reftable-backend.c            |   3 +-
 t/t5510-fetch.sh                   |   6 +-
 t/t6302-for-each-ref-filter.sh     |  34 ++++++++
 10 files changed, 207 insertions(+), 106 deletions(-)

-- 
2.45.0


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

^ permalink raw reply	[relevance 2%]

* Re: [PATCH 2/3] refs: do not label special refs as pseudo refs
  2024-04-29 22:52  0%   ` Justin Tobler
@ 2024-04-30  7:29  0%     ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-30  7:29 UTC (permalink / raw)
  To: git, Jeff King, Karthik Nayak

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

On Mon, Apr 29, 2024 at 05:52:41PM -0500, Justin Tobler wrote:
> On 24/04/29 03:41PM, Patrick Steinhardt wrote:
> > diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> > index d71b199955..4275918fa0 100644
> > --- a/Documentation/glossary-content.txt
> > +++ b/Documentation/glossary-content.txt
> > @@ -497,20 +497,28 @@ exclude;;
> >  	unusual refs.
> >  
> >  [[def_pseudoref]]pseudoref::
> > -	Pseudorefs are a class of files under `$GIT_DIR` which behave
> > -	like refs for the purposes of rev-parse, but which are treated
> > -	specially by git.  Pseudorefs both have names that are all-caps,
> > -	and always start with a line consisting of a
> > -	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
> > -	pseudoref, because it is sometimes a symbolic ref.  They might
> 
> We remove the example here about HEAD not being a pseudoref. This
> example seems helpful to indicate that a pseudoref cannot be a symbolic
> ref. Is this no longer the case and the change intended?

I just don't see why we would want to have this restriction. Honestly,
the more I think about this whole topic the more I want to go into the
direction I've hinted at in the cover letter: drop "special refs" and
define pseudo refs as either FETCH_HEAD or MERGE_HEAD. Everything else
is just a normal ref, even though some of those may live in the root
directory if they conform to a set of strict rules:

  - All upppercase characters plus underscores.

  - Must end with "_HEAD", except a list of known irregular root refs.

I feel like the world would be better like this.

> > -	optionally contain some additional data.  `MERGE_HEAD` and
> > -	`CHERRY_PICK_HEAD` are examples.  Unlike
> > -	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
> > -	be symbolic refs, and never have reflogs.  They also cannot be
> > -	updated through the normal ref update machinery.  Instead,
> > -	they are updated by directly writing to the files.  However,
> > -	they can be read as if they were refs, so `git rev-parse
> > -	MERGE_HEAD` will work.
> > +	Pseudorefs are references that live in the root of the reference
> > +	hierarchy, outside of the usual "refs/" hierarchy. Pseudorefs have an
> > +	all-uppercase name and must end with a "_HEAD" suffix, for example
> > +	"`BISECT_HEAD`". Other than that, pseudorefs behave the exact same as
> > +	any other reference and can be both read and written via regular Git
> > +	tooling.
> 
> Pseudorefs behaving the same and using the same tooling seems to
> contridict the previous documentation. I assume the previous information
> was out-of-date, but it might be nice to explain this in the message.

Yes, and I actually want to change this. We never enforced restrictions
for pseudorefs anyway, they can be symrefs just fine. And neither would
I see any reason why that should be the case in the first place.

> > ++
> > +<<def_special_ref>,Special refs>> are not pseudorefs.
> > ++
> > +Due to historic reasons, Git has several irregular pseudo refs that do not
> > +follow above rules. The following list of irregular pseudo refs is exhaustive
> 
> We seem to be inconsistent between using "pseudoref" and "pseudo ref".
> Not sure it we want to be consistent here. 

Makes sense.

Patrick

> -Justin
> 
> > +and shall not be extended in the future:
> > +
> > + - "`AUTO_MERGE`"
> > +
> > + - "`BISECT_EXPECTED_REV`"
> > +
> > + - "`NOTES_MERGE_PARTIAL`"
> > +
> > + - "`NOTES_MERGE_REF`"
> > +
> > + - "`MERGE_AUTOSTASH`"
> >  
> >  [[def_pull]]pull::
> >  	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
> > diff --git a/refs.c b/refs.c
> > index c64f66bff9..567c6fc6ff 100644
> > --- a/refs.c
> > +++ b/refs.c
> > @@ -905,6 +905,8 @@ int is_pseudoref(struct ref_store *refs, const char *refname)
> >  
> >  	if (!is_pseudoref_syntax(refname))
> >  		return 0;
> > +	if (is_special_ref(refname))
> > +		return 0;
> >  
> >  	if (ends_with(refname, "_HEAD")) {
> >  		refs_resolve_ref_unsafe(refs, refname,
> > diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
> > index 948f1bb5f4..8c92fbde79 100755
> > --- a/t/t6302-for-each-ref-filter.sh
> > +++ b/t/t6302-for-each-ref-filter.sh
> > @@ -52,6 +52,23 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
> >  	test_cmp expect actual
> >  '
> >  
> > +test_expect_success '--include-root-refs pattern does not print special refs' '
> > +	test_when_finished "rm -rf repo" &&
> > +	git init repo &&
> > +	(
> > +		cd repo &&
> > +		test_commit initial &&
> > +		git rev-parse HEAD >.git/MERGE_HEAD &&
> > +		git for-each-ref --format="%(refname)" --include-root-refs >actual &&
> > +		cat >expect <<-EOF &&
> > +		HEAD
> > +		$(git symbolic-ref HEAD)
> > +		refs/tags/initial
> > +		EOF
> > +		test_cmp expect actual
> > +	)
> > +'
> > +
> >  test_expect_success '--include-root-refs with other patterns' '
> >  	cat >expect <<-\EOF &&
> >  	HEAD
> > -- 
> > 2.45.0-rc1
> > 
> 
> 

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

^ permalink raw reply	[relevance 0%]

* Re: Reply to community feedback
  2024-04-29 22:04  0%   ` Reply to community feedback Matheus Afonso Martins Moreira
@ 2024-04-30  6:51  0%     ` Torsten Bögershausen
  0 siblings, 0 replies; 200+ results
From: Torsten Bögershausen @ 2024-04-30  6:51 UTC (permalink / raw)
  To: Matheus Afonso Martins Moreira; +Cc: git

On Mon, Apr 29, 2024 at 07:04:40PM -0300, Matheus Afonso Martins Moreira wrote:

> Thank you for your feedback.
>
> > are there any plans to integrate the parser into connect.c and fetch ?
>
> Yes.
>
> That was my intention but I was not confident enough to touch connect.c
> before getting feedback from the community, since it's critical code
> and it is my first contribution.

Welcome to the Git community.

I wasn't aware of t0110 as a test case...

>
> I do want to merge all URL parsing in git into this one function though,
> thereby creating a "single point of truth". This is so that if the algorithm
> is modified the changes are visible to the URL parser builtin as well.
>

That is a good thing to do. Be prepared for a longer journey, since we have
this legacy stuff to deal with. But I am happy to help with reviews, even
if that may take some days,

[]

> When adding test cases, I looked at the possibilities enumerated in urls.txt
> and generated test cases based on those. I also looked at the urlmatch.h
> test cases. However...
>
> > Some work can be seen in t5601-clone.sh
>
> ... I did not think to check those.
>
> > Especially, when dealing with literal IPv6 addresses,
> > the ones with [] and the simplified ssh syntax 'myhost:src'
> > are interesting to test.
>
> You're right about that. I shall prepare an updated v2 patchset
> with more test cases, and also any other changes/improvements
> requested by maintainers.
>
> > And some features using the [] syntax to embedd a port number
> > inside the simplified ssh syntax had not been documented,
> > but used in practise, and are now part of the test suite.
> > See "[myhost:123]:src" in t5601
>
> Indeed, I did not read anything of the sort when I checked it.
> Would you like me to commit a note to this effect to urls.txt ?

On short: please not.
This kind of syntax was never ment to be used.
The official "ssh://myhost:123/src" is recommended.
When IPv6 parsing was added, people discovered that it could be
used to "protect" the ':' from being a seperator between the hostname
and the path, and can be used to seperate the hostname from the port.
Once that was used in real live, it was too late to change it.
If we now get a better debug tool, it could mention that this is
a legacy feature, and recommend the longer "ssh://" syntax.

>
> > Or is this new tool just a helper, to verify "good" URL's,
> > and not accepting our legacy parser quirks ?
>
> It is my intention that this builtin be able to accept, parse
> and decompose all types of URLs that git itself can accept.
>
> > Then we still should see some IPv6 tests ?
>
> I will add them!
>
> > Or may be not, as we prefer hostnames these days ?
>
> I would have to defer that choice to someone more experienced
> with the codebase. Please advise on how to proceed.

Re-reading this email conversation,
I think that we should support (in the future),
what we support today.
Having a new parser tool means, that there is a chance to reject
those URLs with the note/hint, that they are depracted, and should
be replaced by a proper one.
From my point of view this means that all existing test case should pass
even with the new parser, as a general approach.
Deprecating things is hard, may take years, and may be done in a seperate
task/patch series. Or may be part of this one, in seperate commits.

>
> > The RFC 1738 uses the term "scheme" here, and using the very generic
> > term "protocol" may lead to name clashes later.
> > Would something like "git_scheme" or so be better ?
>
> Scheme does seem like a better word if it's the terminology used by RFCs.
> I can change that in a new version if necessary.
> That code is based on the existing connect.c parsing code though.
>
> > I think that the "///" version is superflous, it should already
> > be covered by the "//" version
>
> I thought it was a good idea because of existing precedent:
> my first approach to creating the test cases was to copy the
> ones from t0110-urlmatch-normalization.sh which did have many
> cases such as those. Then as I developed the code I came to
> believe that it was not necessary: I call url_normalize
> in the url_parse function and url_normalize is already being
> tested. I think I just forgot to delete those lines.
>
> Reading that file over once again, it does have IPv6 address
> test cases. So I should probably go over it again.
>
> Thanks again for the feedback,
>
>   Matheus
>


^ permalink raw reply	[relevance 0%]

* [PATCH v3 0/1] advice: add --no-advice global option
    @ 2024-04-30  1:47  2%   ` James Liu
      1 sibling, 2 replies; 200+ results
From: James Liu @ 2024-04-30  1:47 UTC (permalink / raw)
  To: git; +Cc: James Liu

Hi,

This is v3 of the patch to add a global --no-advice option for silencing
all advice hints. The environment variable has been renamed to
GIT_ADVICE and marked for internal use only, and the conditional in the
advice_enabled() helper has been adjusted to use git_env_bool().

I explored the idea of adding another test to ensure the configuration
is propagated to subprocesses correctly. This would use `git fetch --all`
as the trigger, however it appears that advice is only printed when
`fetch_one()` is invoked, which I don't think spawns any child
processes. With that said, since this is a common pattern, I believe
the existing additional test case is sufficient.

Cheers,
James

James Liu (1):
  advice: add --no-advice global option

 Documentation/git.txt |  5 ++++-
 advice.c              |  8 +++++++-
 environment.h         |  7 +++++++
 git.c                 |  6 +++++-
 t/t0018-advice.sh     | 20 ++++++++++++++++++++
 5 files changed, 43 insertions(+), 3 deletions(-)

Range-diff against v2:
1:  0f2ecb7862 ! 1:  55d5559586 advice: add --no-advice global option
    @@ Commit message
     
         Add a --no-advice global option to disable all advice hints from being
         displayed. This is independent of the toggles for individual advice
    -    hints.
    +    hints. Use an internal environment variable (GIT_ADVICE) to ensure this
    +    configuration is propagated to the usage site, even if it executes in a
    +    subprocess.
     
         Signed-off-by: James Liu <james@jamesliu.io>
     
    @@ advice.c: void advise(const char *advice, ...)
     -	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
     +	int enabled;
     +
    -+	if (getenv(GIT_NO_ADVICE))
    ++	if (!git_env_bool(GIT_ADVICE, 1))
     +		return 0;
     +
     +	enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
    @@ advice.c: void advise(const char *advice, ...)
     
      ## environment.h ##
     @@ environment.h: const char *getenv_safe(struct strvec *argv, const char *name);
    - #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
      #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
      #define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
    -+#define GIT_NO_ADVICE "GIT_NO_ADVICE"
      
    ++/*
    ++ * Environment variable used to propagate the --no-advice global option to the
    ++ * advice_enabled() helper, even when run in a subprocess.
    ++ * This is an internal variable that should not be set by the user.
    ++ */
    ++#define GIT_ADVICE "GIT_ADVICE"
    ++
      /*
       * Environment variable used in handshaking the wire protocol.
    +  * Contains a colon ':' separated list of keys with optional values
     
      ## git.c ##
     @@ git.c: const char git_usage_string[] =
    @@ git.c: static int handle_options(const char ***argv, int *argc, int *envchanged)
      			if (envchanged)
      				*envchanged = 1;
     +		} else if (!strcmp(cmd, "--no-advice")) {
    -+			setenv(GIT_NO_ADVICE, "1", 1);
    ++			setenv(GIT_ADVICE, "0", 1);
     +			if (envchanged)
     +				*envchanged = 1;
      		} else {
-- 
2.44.0



^ permalink raw reply	[relevance 2%]

* Re: [PATCH v2 1/1] advice: add --no-advice global option
  2024-04-29  6:40  4%         ` Jeff King
  2024-04-29  6:55  0%           ` Dragan Simic
@ 2024-04-30  0:56  0%           ` James Liu
  1 sibling, 0 replies; 200+ results
From: James Liu @ 2024-04-30  0:56 UTC (permalink / raw)
  To: Jeff King; +Cc: Dragan Simic, git

On Mon Apr 29, 2024 at 4:40 PM AEST, Jeff King wrote:
> You need an environment variable if you want the command-line option to
> work consistently across commands that spawn external processes. E.g.:
>
>   git --no-advice fetch --all
>
> is going to spawn fetch sub-processes under the hood. You'd want them to
> respect --no-advice, too, so we either have to propagate the
> command-line option or use the environment. And when you consider an
> external script like git-foo that runs a bunch of underlying Git
> commands, then propagating becomes too cumbersome and error-prone.

Thanks for the explanation Jeff! Makes sense why the pattern is so
prevalent.

> You should use git_env_bool() to avoid the confusing behavior that
> GIT_NO_ADVICE=false still turns off advice. ;)
>
> You can also drop the "NO", which helps avoid awkward double negation.
> For example, if you do:
>
>   if (git_env_bool("GIT_ADVICE", 1))
> 	return 0;
>
> then leaving that variable unset will act as if it is set to "1", but
> you can still do GIT_ADVICE=0 to suppress it.

Awesome. I'll apply this suggestion in the next version of this patch.

Cheers,
James


^ permalink raw reply	[relevance 0%]

* Re: [PATCH 2/3] refs: do not label special refs as pseudo refs
  2024-04-29 13:41  6% ` [PATCH 2/3] refs: do not label special refs as pseudo refs Patrick Steinhardt
  2024-04-29 15:12  0%   ` Phillip Wood
@ 2024-04-29 22:52  0%   ` Justin Tobler
  2024-04-30  7:29  0%     ` Patrick Steinhardt
  1 sibling, 1 reply; 200+ results
From: Justin Tobler @ 2024-04-29 22:52 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Karthik Nayak

On 24/04/29 03:41PM, Patrick Steinhardt wrote:
> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index d71b199955..4275918fa0 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -497,20 +497,28 @@ exclude;;
>  	unusual refs.
>  
>  [[def_pseudoref]]pseudoref::
> -	Pseudorefs are a class of files under `$GIT_DIR` which behave
> -	like refs for the purposes of rev-parse, but which are treated
> -	specially by git.  Pseudorefs both have names that are all-caps,
> -	and always start with a line consisting of a
> -	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
> -	pseudoref, because it is sometimes a symbolic ref.  They might

We remove the example here about HEAD not being a pseudoref. This
example seems helpful to indicate that a pseudoref cannot be a symbolic
ref. Is this no longer the case and the change intended?

> -	optionally contain some additional data.  `MERGE_HEAD` and
> -	`CHERRY_PICK_HEAD` are examples.  Unlike
> -	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
> -	be symbolic refs, and never have reflogs.  They also cannot be
> -	updated through the normal ref update machinery.  Instead,
> -	they are updated by directly writing to the files.  However,
> -	they can be read as if they were refs, so `git rev-parse
> -	MERGE_HEAD` will work.
> +	Pseudorefs are references that live in the root of the reference
> +	hierarchy, outside of the usual "refs/" hierarchy. Pseudorefs have an
> +	all-uppercase name and must end with a "_HEAD" suffix, for example
> +	"`BISECT_HEAD`". Other than that, pseudorefs behave the exact same as
> +	any other reference and can be both read and written via regular Git
> +	tooling.

Pseudorefs behaving the same and using the same tooling seems to
contridict the previous documentation. I assume the previous information
was out-of-date, but it might be nice to explain this in the message.

> ++
> +<<def_special_ref>,Special refs>> are not pseudorefs.
> ++
> +Due to historic reasons, Git has several irregular pseudo refs that do not
> +follow above rules. The following list of irregular pseudo refs is exhaustive

We seem to be inconsistent between using "pseudoref" and "pseudo ref".
Not sure it we want to be consistent here. 

-Justin

> +and shall not be extended in the future:
> +
> + - "`AUTO_MERGE`"
> +
> + - "`BISECT_EXPECTED_REV`"
> +
> + - "`NOTES_MERGE_PARTIAL`"
> +
> + - "`NOTES_MERGE_REF`"
> +
> + - "`MERGE_AUTOSTASH`"
>  
>  [[def_pull]]pull::
>  	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
> diff --git a/refs.c b/refs.c
> index c64f66bff9..567c6fc6ff 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -905,6 +905,8 @@ int is_pseudoref(struct ref_store *refs, const char *refname)
>  
>  	if (!is_pseudoref_syntax(refname))
>  		return 0;
> +	if (is_special_ref(refname))
> +		return 0;
>  
>  	if (ends_with(refname, "_HEAD")) {
>  		refs_resolve_ref_unsafe(refs, refname,
> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
> index 948f1bb5f4..8c92fbde79 100755
> --- a/t/t6302-for-each-ref-filter.sh
> +++ b/t/t6302-for-each-ref-filter.sh
> @@ -52,6 +52,23 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
>  	test_cmp expect actual
>  '
>  
> +test_expect_success '--include-root-refs pattern does not print special refs' '
> +	test_when_finished "rm -rf repo" &&
> +	git init repo &&
> +	(
> +		cd repo &&
> +		test_commit initial &&
> +		git rev-parse HEAD >.git/MERGE_HEAD &&
> +		git for-each-ref --format="%(refname)" --include-root-refs >actual &&
> +		cat >expect <<-EOF &&
> +		HEAD
> +		$(git symbolic-ref HEAD)
> +		refs/tags/initial
> +		EOF
> +		test_cmp expect actual
> +	)
> +'
> +
>  test_expect_success '--include-root-refs with other patterns' '
>  	cat >expect <<-\EOF &&
>  	HEAD
> -- 
> 2.45.0-rc1
> 




^ permalink raw reply	[relevance 0%]

* Reply to community feedback
  2024-04-29 20:53  3% ` Torsten Bögershausen
@ 2024-04-29 22:04  0%   ` Matheus Afonso Martins Moreira
  2024-04-30  6:51  0%     ` Torsten Bögershausen
  0 siblings, 1 reply; 200+ results
From: Matheus Afonso Martins Moreira @ 2024-04-29 22:04 UTC (permalink / raw)
  To: tboegi; +Cc: git

Thank you for your feedback.

> are there any plans to integrate the parser into connect.c and fetch ?

Yes.

That was my intention but I was not confident enough to touch connect.c
before getting feedback from the community, since it's critical code
and it is my first contribution.

I do want to merge all URL parsing in git into this one function though,
thereby creating a "single point of truth". This is so that if the algorithm
is modified the changes are visible to the URL parser builtin as well.

> Speaking as a person, who manage to break the parsing of URLs once,
> with the good intention to improve things, I need to learn that
> test cases are important.

Absolutely agree.

When adding test cases, I looked at the possibilities enumerated in urls.txt
and generated test cases based on those. I also looked at the urlmatch.h
test cases. However...

> Some work can be seen in t5601-clone.sh

... I did not think to check those.

> Especially, when dealing with literal IPv6 addresses,
> the ones with [] and the simplified ssh syntax 'myhost:src'
> are interesting to test.

You're right about that. I shall prepare an updated v2 patchset
with more test cases, and also any other changes/improvements
requested by maintainers.

> And some features using the [] syntax to embedd a port number
> inside the simplified ssh syntax had not been documented,
> but used in practise, and are now part of the test suite.
> See "[myhost:123]:src" in t5601

Indeed, I did not read anything of the sort when I checked it.
Would you like me to commit a note to this effect to urls.txt ?

> Or is this new tool just a helper, to verify "good" URL's,
> and not accepting our legacy parser quirks ?

It is my intention that this builtin be able to accept, parse
and decompose all types of URLs that git itself can accept.

> Then we still should see some IPv6 tests ?

I will add them!

> Or may be not, as we prefer hostnames these days ?

I would have to defer that choice to someone more experienced
with the codebase. Please advise on how to proceed.

> The RFC 1738 uses the term "scheme" here, and using the very generic
> term "protocol" may lead to name clashes later.
> Would something like "git_scheme" or so be better ?

Scheme does seem like a better word if it's the terminology used by RFCs.
I can change that in a new version if necessary.
That code is based on the existing connect.c parsing code though.

> I think that the "///" version is superflous, it should already
> be covered by the "//" version

I thought it was a good idea because of existing precedent:
my first approach to creating the test cases was to copy the
ones from t0110-urlmatch-normalization.sh which did have many
cases such as those. Then as I developed the code I came to
believe that it was not necessary: I call url_normalize
in the url_parse function and url_normalize is already being
tested. I think I just forgot to delete those lines.

Reading that file over once again, it does have IPv6 address
test cases. So I should probably go over it again.

Thanks again for the feedback,

  Matheus


^ permalink raw reply	[relevance 0%]

* Re: [PATCH 00/13] builtin: implement, document and test url-parse
  2024-04-28 22:30  3% [PATCH 00/13] builtin: implement, document and test url-parse Matheus Moreira via GitGitGadget
@ 2024-04-29 20:53  3% ` Torsten Bögershausen
  2024-04-29 22:04  0%   ` Reply to community feedback Matheus Afonso Martins Moreira
  0 siblings, 1 reply; 200+ results
From: Torsten Bögershausen @ 2024-04-29 20:53 UTC (permalink / raw)
  To: Matheus Moreira via GitGitGadget; +Cc: git, Matheus Moreira

On Sun, Apr 28, 2024 at 10:30:48PM +0000, Matheus Moreira via GitGitGadget wrote:
> Git commands accept a wide variety of URLs syntaxes, not just standard URLs.
> This can make parsing git URLs difficult since standard URL parsers cannot
> be used. Even if an external parser were implemented, it would have to track
> git's development closely in case support for any new URL schemes are added.
>
> These patches introduce a new url-parse builtin command that exposes git's
> native URL parsing algorithms as a plumbing command, allowing other programs
> to then call upon git itself to parse the git URLs and their components.
>
> This should be quite useful for scripts. For example, a script might want to
> add remotes to repositories, naming them according to the domain name where
> the repository is hosted. This new builtin allows it to parse the git URL
> and extract its host name which can then be used as input for other
> operations. This would be difficult to implement otherwise due to git's
> support for scp style URLs.
>

All in all, having a URL parser as such is a good thing, thanks for working
on that.

There are, however, some notes and questions, up for discussion:

- are there any plans to integrate the parser into connect.c and fetch ?
  Speaking as a person, who manage to break the parsing of URLs once,
  with the good intention to improve things, I need to learn that
  test cases are important.
  Some work can be seen in t5601-clone.sh
  Especially, when dealing with literal IPv6 addresses, the ones with []
  and the simplified ssh syntax 'myhost:src' are interesting to test.
  Git itself strives to be RFC compliant when parsing URLs, but
  we do not fully guarantee to be "fully certified".
  And some features using the [] syntax to embedd a port number
  inside the simplified ssh syntax had not been documented,
  but used in practise, and are now part of the test suite.
  See "[myhost:123]:src" in t5601

- Or is this new tool just a helper, to verify "good" URL's,
  and not accepting our legacy parser quirks ?
  Then we still should see some IPv6 tests ?
  Or may be not, as we prefer hostnames these days ?

- One minor comment:
  in 02/13 we read:
        +enum protocol {
        +       PROTO_UNKNOWN = 0,
        +       PROTO_LOCAL,
        +       PROTO_FILE,
        +       PROTO_SSH,
        +       PROTO_GIT,
  The RFC 1738 uses the term "scheme" here, and using the very generic
  term "protocol" may lead to name clashes later.
  Would something like "git_scheme" or so be better ?

- One minor comment:
   In 13/13 we read:
        +       git url-parse "file:///" &&
        +       git url-parse "file://"

  I think that the "///" version is superflous, it should already
  be covered by the "//" version



^ permalink raw reply	[relevance 3%]

* [PATCH v3 0/3] builtin/tag.c: add --trailer option
  2024-04-29 16:53  1% ` [PATCH v2] " John Passaro via GitGitGadget
@ 2024-04-29 18:54  3%   ` John Passaro via GitGitGadget
  2024-04-30 14:41  3%     ` [PATCH v4 " John Passaro via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: John Passaro via GitGitGadget @ 2024-04-29 18:54 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Patrick Steinhardt, John Passaro, John Passaro

Follow-up patch taking welcome feedback from Patrick and JCH.

Since git-tag --list --format="%(trailers)" can interpret trailers from
annotated tag messages, it seems natural to support --trailer when writing a
new tag message.

git-commit accomplishes this by taking --trailer arguments and passing them
to git interpret-trailer. This patch series refactors that logic and uses it
to implement --trailer on git-tag.

Also Included are updates to the i18n files, since the git-tag patch changes
some strings that are subject to translation. If I am out of my lane here,
please feel free to separate or leave out the i18n patch.

John Passaro (3):
  builtin/commit.c: refactor --trailer logic
  builtin/tag.c: add --trailer arg
  po: update git-tag translations

 Documentation/git-tag.txt |  18 +++++-
 builtin/commit.c          |  20 +------
 builtin/tag.c             |  41 +++++++++++---
 po/bg.po                  |   2 +
 po/ca.po                  |   4 +-
 po/de.po                  |   2 +
 po/el.po                  |   9 ++-
 po/es.po                  |  14 +++--
 po/fr.po                  |   2 +
 po/id.po                  |   2 +
 po/it.po                  |   6 +-
 po/ko.po                  |  10 ++--
 po/pl.po                  |   6 +-
 po/ru.po                  |   1 +
 po/sv.po                  |   2 +
 po/tr.po                  |   2 +
 po/uk.po                  |   2 +
 po/vi.po                  |   2 +
 po/zh_CN.po               |   2 +
 po/zh_TW.po               |   2 +
 t/t7004-tag.sh            | 114 ++++++++++++++++++++++++++++++++++++++
 trailer.c                 |  12 ++++
 trailer.h                 |   8 +++
 23 files changed, 237 insertions(+), 46 deletions(-)


base-commit: 786a3e4b8d754d2b14b1208b98eeb0a554ef19a8
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1723%2Fjpassaro%2Fjp%2Ftag-trailer-arg-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1723/jpassaro/jp/tag-trailer-arg-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1723

Range-diff vs v2:

 -:  ----------- > 1:  0c9517f434a builtin/commit.c: refactor --trailer logic
 1:  d4beb7cd67e ! 2:  5b6239167b8 builtin/tag.c: add --trailer arg
     @@ Metadata
       ## Commit message ##
          builtin/tag.c: add --trailer arg
      
     -    Teach git-tag to accept --trailer option to add trailers to annotated
     -    tag messages, like git-commit. Move the code that git-commit uses for
     -    trailers to the trailer.h API, so it can be re-used for git-tag.
     +    git-tag currently supports interpreting trailers from an annotated tag
     +    message, using --list --format="%(trailers)". There is no ergonomic way
     +    to add trailers to an annotated tag message.
     +
     +    In a previous patch, we refactored git-commit's implementation of its
     +    --trailer arg to the trailer.h API. Let's use that new function to teach
     +    git-tag the same --trailer argument, emulating as much of git-commit's
     +    behavior as much as possible.
      
          Signed-off-by: John Passaro <john.a.passaro@gmail.com>
     +    Helped-by: Patrick Steinhardt <ps@pks.im>
      
       ## Documentation/git-tag.txt ##
      @@ Documentation/git-tag.txt: SYNOPSIS
     @@ Documentation/git-tag.txt: This option is only applicable when listing tags with
       --edit::
       	The message taken from file with `-F` and command line with
      
     - ## builtin/commit.c ##
     -@@
     - #include "commit-reach.h"
     - #include "commit-graph.h"
     - #include "pretty.h"
     -+#include "trailer.h"
     - 
     - static const char * const builtin_commit_usage[] = {
     - 	N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
     -@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
     - 	fclose(s->fp);
     - 
     - 	if (trailer_args.nr) {
     --		struct child_process run_trailer = CHILD_PROCESS_INIT;
     --
     --		strvec_pushl(&run_trailer.args, "interpret-trailers",
     --			     "--in-place", "--no-divider",
     --			     git_path_commit_editmsg(), NULL);
     --		strvec_pushv(&run_trailer.args, trailer_args.v);
     --		run_trailer.git_cmd = 1;
     --		if (run_command(&run_trailer))
     -+		if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
     - 			die(_("unable to pass trailers to --trailers"));
     - 		strvec_clear(&trailer_args);
     - 	}
     -
       ## builtin/tag.c ##
      @@
       #include "date.h"
     @@ builtin/tag.c: static void create_tag(const struct object_id *object, const char
       		}
       	}
       
     -@@ builtin/tag.c: struct msg_arg {
     - 	struct strbuf buf;
     - };
     - 
     -+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
     -+{
     -+	BUG_ON_OPT_NEG(unset);
     -+
     -+	strvec_pushl(opt->value, "--trailer", arg, NULL);
     -+	return 0;
     -+}
     -+
     - static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
     - {
     - 	struct msg_arg *msg = opt->value;
      @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
       	struct ref_sorting *sorting;
       	struct string_list sorting_options = STRING_LIST_INIT_DUP;
     @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
       		OPT_CALLBACK_F('m', "message", &msg, N_("message"),
       			       N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
       		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
     -+		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"),
     -+				PARSE_OPT_NONEG, opt_pass_trailer),
     ++		OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
     ++				  N_("add custom trailer(s)"), PARSE_OPT_NONEG),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
       		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
       		OPT_CLEANUP(&cleanup_arg),
     @@ t/t7004-tag.sh: test_expect_success 'git tag --format with ahead-behind' '
       	refs/tags/tag-zero-lines 0 1 !
       	EOF
       	git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
     -
     - ## trailer.c ##
     -@@
     - #include "commit.h"
     - #include "trailer.h"
     - #include "list.h"
     -+#include "run-command.h"
     - /*
     -  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
     -  */
     -@@ trailer.c: void trailer_iterator_release(struct trailer_iterator *iter)
     - 	strbuf_release(&iter->val);
     - 	strbuf_release(&iter->key);
     - }
     -+
     -+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
     -+	struct child_process run_trailer = CHILD_PROCESS_INIT;
     -+
     -+	run_trailer.git_cmd = 1;
     -+	strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+		     "--in-place", "--no-divider",
     -+		     path, NULL);
     -+	strvec_pushv(&run_trailer.args, trailer_args->v);
     -+	return run_command(&run_trailer);
     -+}
     -
     - ## trailer.h ##
     -@@
     - 
     - #include "list.h"
     - #include "strbuf.h"
     -+#include "strvec.h"
     - 
     - enum trailer_where {
     - 	WHERE_DEFAULT,
     -@@ trailer.h: int trailer_iterator_advance(struct trailer_iterator *iter);
     -  */
     - void trailer_iterator_release(struct trailer_iterator *iter);
     - 
     -+/*
     -+ * Augment a file to add trailers to it by running git-interpret-trailers.
     -+ * This calls run_command() and its return value is the same (i.e. 0 for
     -+ * success, various non-zero for other errors). See run-command.h.
     -+ */
     -+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
     -+
     - #endif /* TRAILER_H */
 -:  ----------- > 3:  d5335e30b0b po: update git-tag translations

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [ANNOUNCE] Git v2.45.0
@ 2024-04-29 17:12  2% Junio C Hamano
    0 siblings, 1 reply; 200+ results
From: Junio C Hamano @ 2024-04-29 17:12 UTC (permalink / raw)
  To: git; +Cc: Linux Kernel, git-packagers

The latest feature release Git v2.45.0 is now available at the
usual places.  It is comprised of 540 non-merge commits since
v2.44.0, contributed by 96 people, 38 of which are new faces [*].

The tarballs are found at:

    https://www.kernel.org/pub/software/scm/git/

The following public repositories all have a copy of the 'v2.45.0'
tag and the 'master' branch that the tag points at:

  url = https://git.kernel.org/pub/scm/git/git
  url = https://kernel.googlesource.com/pub/scm/git/git
  url = git://repo.or.cz/alt-git.git
  url = https://github.com/gitster/git

New contributors whose contributions weren't in v2.44.0 are as follows.
Welcome to the Git development community!

  Ahelenia Ziemiańska, Angelo Dureghello, Aryan Gupta, Benjamin
  Flesch, Bo Anderson, Brian C Tracy, Brian Lyles, Bruno Haible,
  Chuck Lever, Dario Gjorgjevski, Dirk Gouders, Eugenio Gigante,
  Florian Schmidt, Haritha D, Harmen Stoppels, Jean-Rémy
  Falleri, Jiamu Sun, Jonas Wunderlich, Jonathan Davies, Julio
  Bacellari, Kipras Melnikovas, Kisaragi Hiu, Marcel Röthke,
  Matthew Rollings, Max Gautier, mirth hickford, Paweł Dominiak,
  Pi Fisher, Ralph Seichter, Richard Macklin, shejialuo, Steven
  Jeuris, Thalia Archibald, Tiago Pascoal, Vincenzo Mezzela,
  Vũ Tiến Hưng, Xing Xin, and Yehezkel Bernat.

Returning contributors who helped this release are as follows.
Thanks for your continued support.

  Alexander Shopov, Arkadii Yakovets, Bagas Sanjaya, Beat Bolli,
  brian m. carlson, Chandra Pratap, Christian Couder, Derrick
  Stolee, Đoàn Trần Công Danh, Dragan Simic, Elijah Newren,
  Emir SARI, Eric Sunshine, Eric W. Biederman, Ghanshyam Thakkar,
  Han Young, Jakub Wilk, Jean-Noël Avila, Jeff Hostetler, Jeff
  King, Jiang Xin, Johannes Schindelin, Johannes Sixt, John Cai,
  Josh Steadmon, Josh Triplett, Junio C Hamano, Justin Tobler,
  Karthik Nayak, Kate Golovanova, Kristoffer Haugsbakk, Kyle
  Lippincott, Kyle Meyer, Linus Arver, Lumynous, Manlio Perillo,
  Matthias Aßhauer, Matthias Rüster, M Hickford, Michael Lohmann,
  Michael Osipov, Mike Hommey, Orgad Shaneh, Patrick Steinhardt,
  Peter Hutterer, Peter Krefting, Philippe Blain, Phillip Wood,
  Ralf Thielow, René Scharfe, Rubén Justo, Sergey Organov,
  SZEDER Gábor, Taylor Blau, Teng Long, Ville Skyttä, Yasushi
  SHOJI, and Yi-Jyun Pan.

[*] We are counting not just the authorship contribution but issue
    reporting, mentoring, helping and reviewing that are recorded in
    the commit trailers.

----------------------------------------------------------------

Git v2.45 Release Notes
=======================

Backward Compatibility Notes

UI, Workflows & Features

 * Integrate the reftable code into the refs framework as a backend.
   With "git init --ref-format=reftable", hopefully it would be a lot
   more efficient to manage a repository with many references.

 * "git checkout -p" and friends learned that that "@" is a synonym
   for "HEAD".

 * Variants of vimdiff learned to honor mergetool.<variant>.layout
   settings.

 * "git reflog" learned a "list" subcommand that enumerates known reflogs.

 * When a merge conflicted at a submodule, merge-ort backend used to
   unconditionally give a lengthy message to suggest how to resolve
   it.  Now the message can be squelched as an advice message.

 * "git for-each-ref" learned "--include-root-refs" option to show
   even the stuff outside the 'refs/' hierarchy.

 * "git rev-list --missing=print" has learned to optionally take
   "--allow-missing-tips", which allows the objects at the starting
   points to be missing.

 * "git merge-tree" has learned that the three trees involved in the
   3-way merge only need to be trees, not necessarily commits.

 * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
   other kinds of *_HEAD pseudorefs.

 * Platform specific tweaks for OS/390 has been added to
   config.mak.uname.

 * Users with safe.bareRepository=explicit can still work from within
   $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
   of the primary worktree without explicitly specifying the $GIT_DIR
   environment variable or the --git-dir=<path> option.

 * The output format for dates "iso-strict" has been tweaked to show
   a time in the Zulu timezone with "Z" suffix, instead of "+00:00".

 * "git diff" and friends learned two extra configuration variables,
   diff.srcPrefix and diff.dstPrefix.

 * The status.showUntrackedFiles configuration variable had a name
   that tempts users to set a Boolean value expressed in our usual
   "false", "off", and "0", but it only took "no".  This has been
   corrected so "true" and its synonyms are taken as "normal", while
   "false" and its synonyms are taken as "no".

 * Remove an ancient and not well maintained Hg-to-git migration
   script from contrib/.

 * Hints that suggest what to do after resolving conflicts can now be
   squelched by disabling advice.mergeConflict.

 * Allow git-cherry-pick(1) to automatically drop redundant commits via
   a new `--empty` option, similar to the `--empty` options for
   git-rebase(1) and git-am(1). Includes a soft deprecation of
   `--keep-redundant-commits` as well as some related docs changes and
   sequencer code cleanup.

 * "git config" learned "--comment=<message>" option to leave a
   comment immediately after the "variable = value" on the same line
   in the configuration file.

 * core.commentChar used to be limited to a single byte, but has been
   updated to allow an arbitrary multi-byte sequence.

 * "git add -p" and other "interactive hunk selection" UI has learned to
   skip showing the hunk immediately after it has already been shown, and
   an additional action to explicitly ask to reshow the current hunk.

 * "git pack-refs" learned the "--auto" option, which defers the decision of
   whether and how to pack to the ref backend. This is used by the reftable
   backend to avoid repacking of an already-optimal ref database. The new mode
   is triggered from "git gc --auto".

 * "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
   diagnose a pathspec element that did not match any files in certain
   situations, unlike "git add <pathspec>" did.

 * The userdiff patterns for C# has been updated.

 * Git writes a "waiting for your editor" message on an incomplete
   line after launching an editor, and then append another error
   message on the same line if the editor errors out.  It now clears
   the "waiting for..." line before giving the error message.

 * The filename used for rejected hunks "git apply --reject" creates
   was limited to PATH_MAX, which has been lifted.

 * When "git bisect" reports the commit it determined to be the
   culprit, we used to show it in a format that does not honor common
   UI tweaks, like log.date and log.decorate.  The code has been
   taught to use "git show" to follow more customizations.


Performance, Internal Implementation, Development Support etc.

 * The code to iterate over refs with the reftable backend has seen
   some optimization.

 * More tests that are marked as "ref-files only" have been updated to
   improve test coverage of reftable backend.

 * Some parts of command line completion script (in contrib/) have
   been micro-optimized.

 * The way placeholders are to be marked-up in documentation have been
   specified; use "_<placeholder>_" to typeset the word inside a pair
   of <angle-brackets> emphasized.

 * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
   fetching of objects from the promisor remote, which may be handy
   for debugging.

 * The implementation in "git clean" that makes "-n" and "-i" ignore
   clean.requireForce has been simplified, together with the
   documentation.

 * Uses of xwrite() helper have been audited and updated for better
   error checking and simpler code.

 * Some trace2 events that lacked def_param have learned to show it,
   enriching the output.

 * The parse-options code that deals with abbreviated long option
   names have been cleaned up.

 * The code in reftable backend that creates new table files works
   better with the tempfile framework to avoid leaving cruft after a
   failure.

 * The reftable code has its own custom binary search function whose
   comparison callback has an unusual interface, which caused the
   binary search to degenerate into a linear search, which has been
   corrected.

 * The code to iterate over reflogs in the reftable has been optimized
   to reduce memory allocation and deallocation.

 * Work to support a repository that work with both SHA-1 and SHA-256
   hash algorithms has started.

 * A new fuzz target that exercises config parsing code has been
   added.

 * Fix the way recently added tests interpolate variables defined
   outside them, and document the best practice to help future
   developers.

 * Introduce an experimental protocol for contributors to propose the
   topic description to be used in the "What's cooking" report, the
   merge commit message for the topic, and in the release notes and
   document it in the SubmittingPatches document.

 * The t/README file now gives a hint on running individual tests in
   the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
   (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).

 * The "hint:" messages given by the advice mechanism, when given a
   message with a blank line, left a line with trailing whitespace,
   which has been cleansed.

 * Documentation rules has been explicitly described how to mark-up
   literal parts and a few manual pages have been updated as examples.

 * The .editorconfig file has been taught that a Makefile uses HT
   indentation.

 * t-prio-queue test has been cleaned up by using C99 compound
   literals; this is meant to also serve as a weather-balloon to smoke
   out folks with compilers who have trouble compiling code that uses
   the feature.

 * Windows binary used to decide the use of unix-domain socket at
   build time, but it learned to make the decision at runtime instead.

 * The "shared repository" test in the t0610 reftable test failed
   under restrictive umask setting (e.g. 007), which has been
   corrected.

 * Document and apply workaround for a buggy version of dash that
   mishandles "local var=val" construct.

 * The codepaths that reach date_mode_from_type() have been updated to
   pass "struct date_mode" by value to make them thread safe.

 * The strategy to compact multiple tables of reftables after many
   operations accumulate many entries has been improved to avoid
   accumulating too many tables uncollected.

 * The code to iterate over reftable blocks has seen some optimization
   to reduce memory allocation and deallocation.

 * The way "git fast-import" handles paths described in its input has
   been tightened up and more clearly documented.

 * The cvsimport tests required that the platform understands
   traditional timezone notations like CST6CDT, which has been
   updated to work on those systems as long as they understand
   POSIX notation with explicit tz transition dates.

 * The code to format trailers have been cleaned up.


Fixes since v2.44
-----------------

 * "git apply" on a filesystem without filemode support have learned
   to take a hint from what is in the index for the path, even when
   not working with the "--index" or "--cached" option, when checking
   the executable bit match what is required by the preimage in the
   patch.
   (merge 45b625142d cp/apply-core-filemode later to maint).

 * "git column" has been taught to reject negative padding value, as
   it would lead to nonsense behaviour including division by zero.
   (merge 76fb807faa kh/column-reject-negative-padding later to maint).

 * "git am --help" now tells readers what actions are available in
   "git am --whitespace=<action>", in addition to saying that the
   option is passed through to the underlying "git apply".
   (merge a171dac734 jc/am-whitespace-doc later to maint).

 * "git tag --column" failed to check the exit status of its "git
   column" invocation, which has been corrected.
   (merge 92e66478fc rj/tag-column-fix later to maint).

 * Credential helper based on libsecret (in contrib/) has been updated
   to handle an empty password correctly.
   (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).

 * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
   option; it used to always exit with 0 and signalled success.
   (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).

 * The code incorrectly attempted to use textconv cache when asked,
   even when we are not running in a repository, which has been
   corrected.
   (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).

 * Remove an empty file that shouldn't have been added in the first
   place.
   (merge 4f66942215 js/remove-cruft-files later to maint).

 * The logic to access reflog entries by date and number had ugly
   corner cases at the boundaries, which have been cleaned up.
   (merge 5edd126720 jk/reflog-special-cases-fix later to maint).

 * An error message from "git upload-pack", which responds to "git
   fetch" requests, had a trailing NUL in it, which has been
   corrected.
   (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).

 * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
   to be the first header file.
   (merge 4e89f0e07c jc/doc-compat-util later to maint).

 * "git commit -v --cleanup=scissors" used to add the scissors line
   twice in the log message buffer, which has been corrected.
   (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).

 * A custom remote helper no longer cannot access the newly created
   repository during "git clone", which is a regression in Git 2.44.
   This has been corrected.
   (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).

 * Various parts of upload-pack have been updated to bound the resource
   consumption relative to the size of the repository to protect from
   abusive clients.
   (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).

 * The upload-pack program, when talking over v2, accepted the
   packfile-uris protocol extension from the client, even if it did
   not advertise the capability, which has been corrected.
   (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).

 * Make sure failure return from merge_bases_many() is properly caught.
   (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).

 * FSMonitor client code was confused when FSEvents were given in a
   different case on a case-insensitive filesystem, which has been
   corrected.
   (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).

 * The "core.commentChar" configuration variable only allows an ASCII
   character, which was not clearly documented, which has been
   corrected.
   (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).

 * With release 2.44 we got rid of all uses of test_i18ngrep and there
   is no in-flight topic that adds a new use of it.  Make a call to
   test_i18ngrep a hard failure, so that we can remove it at the end
   of this release cycle.
   (merge 381a83dfa3 jc/test-i18ngrep later to maint).

 * The command line completion script (in contrib/) learned to
   complete "git reflog" better.
   (merge 1284f9cc11 rj/complete-reflog later to maint).

 * The logic to complete the command line arguments to "git worktree"
   subcommand (in contrib/) has been updated to correctly honor things
   like "git -C dir" etc.
   (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).

 * When git refuses to create a branch because the proposed branch
   name is not a valid refname, an advice message is given to refer
   the user to exact naming rules.
   (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).

 * Code simplification by getting rid of code that sets an environment
   variable that is no longer used.
   (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).

 * The code to find the effective end of log messages can fall into an
   endless loop, which has been corrected.
   (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).

 * Mark-up used in the documentation has been improved for
   consistency.
   (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).

 * The status.showUntrackedFiles configuration variable was
   incorrectly documented to accept "false", which has been corrected.

 * Leaks from "git restore" have been plugged.
   (merge 2f64da0790 rj/restore-plug-leaks later to maint).

 * "git bugreport --no-suffix" was not supported and instead
   segfaulted, which has been corrected.
   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).

 * The documentation for "%(trailers[:options])" placeholder in the
   "--pretty" option of commands in the "git log" family has been
   updated.
   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).

 * "git checkout --conflict=bad" reported a bad conflictStyle as if it
   were given to a configuration variable; it has been corrected to
   report that the command line option is bad.
   (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).

 * Code clean-up in the "git log" machinery that implements custom log
   message formatting.
   (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).

 * "git config" corrupted literal HT characters written in the
   configuration file as part of a value, which has been corrected.
   (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).

 * A unit test for reftable code tried to enumerate all files in a
   directory after reftable operations and expected to see nothing but
   the files it wanted to leave there, but was fooled by .nfs* cruft
   files left, which has been corrected.
   (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).

 * The implementation and documentation of "object-format" option
   exchange between the Git itself and its remote helpers did not
   quite match, which has been corrected.

 * The "--pretty=<shortHand>" option of the commands in the "git log"
   family, defined as "[pretty] shortHand = <expansion>" should have
   been looked up case insensitively, but was not, which has been
   corrected.
   (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).

 * "git apply" failed to extract the filename the patch applied to,
   when the change was about an empty file created in or deleted from
   a directory whose name ends with a SP, which has been corrected.
   (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).

 * Update a more recent tutorial doc.
   (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).

 * The test script had an incomplete and ineffective attempt to avoid
   clobbering the testing user's real crontab (and its equivalents),
   which has been completed.
   (merge 73cb87773b es/test-cron-safety later to maint).

 * Use advice_if_enabled() API to rewrite a simple pattern to
   call advise() after checking advice_enabled().
   (merge 6412d01527 rj/use-adv-if-enabled later to maint).

 * Another "set -u" fix for the bash prompt (in contrib/) script.
   (merge d7805bc743 vs/complete-with-set-u-fix later to maint).

 * "git checkout/switch --detach foo", after switching to the detached
   HEAD state, gave the tracking information for the 'foo' branch,
   which was pointless.

 * "git apply" has been updated to lift the hardcoded pathname length
   limit, which in turn allowed a mksnpath() function that is no
   longer used.
   (merge 708f7e0590 rs/apply-lift-path-length-limit later to maint).

 * A file descriptor leak in an error codepath, used when "git apply
   --reject" fails to create the *.rej file, has been corrected.
   (merge 2b1f456adf rs/apply-reject-fd-leakfix later to maint).

 * A config parser callback function fell through instead of returning
   after recognising and processing a variable, wasting cycles, which
   has been corrected.
   (merge a816ccd642 ds/fetch-config-parse-microfix later to maint).

 * Fix was added to work around a regression in libcURL 8.7.0 (which has
   already been fixed in their tip of the tree).
   (merge 92a209bf24 jk/libcurl-8.7-regression-workaround later to maint).

 * The variable that holds the value read from the core.excludefile
   configuration variable used to leak, which has been corrected.
   (merge 0e0fefb29f jc/unleak-core-excludesfile later to maint).

 * vreportf(), which is used by error() and friends, has been taught
   to give the error message printf-format string when its vsnprintf()
   call fails, instead of showing nothing useful to identify the
   nature of the error.
   (merge c63adab961 rs/usage-fallback-to-show-message-format later to maint).

 * Adjust to an upcoming changes to GNU make that breaks our Makefiles.
   (merge 227b8fd902 tb/make-indent-conditional-with-non-spaces later to maint).

 * Git 2.44 introduced a regression that makes the updated code to
   barf in repositories with multi-pack index written by older
   versions of Git, which has been corrected.

 * When .git/rr-cache/ rerere database gets corrupted or rerere is fed to
   work on a file with conflicted hunks resolved incompletely, the rerere
   machinery got confused and segfaulted, which has been corrected.
   (merge 167395bb47 mr/rerere-crash-fix later to maint).

 * The "receive-pack" program (which responds to "git push") was not
   converted to run "git maintenance --auto" when other codepaths that
   used to run "git gc --auto" were updated, which has been corrected.
   (merge 7bf3057d9c ps/run-auto-maintenance-in-receive-pack later to maint).

 * Other code cleanup, docfix, build fix, etc.
   (merge f0e578c69c rs/use-xstrncmpz later to maint).
   (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
   (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
   (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
   (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
   (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
   (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
   (merge 244001aa20 hs/rebase-not-in-progress later to maint).
   (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
   (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
   (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
   (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
   (merge 40b8076462 ak/rebase-autosquash later to maint).
   (merge 3223204456 eg/add-uflags later to maint).
   (merge 5f78d52dce es/config-doc-sort-sections later to maint).
   (merge 781fb7b4c2 as/option-names-in-messages later to maint).
   (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
   (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
   (merge 67471bc704 ja/doc-formatting-fix later to maint).
   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
   (merge 0d527842b7 az/grep-group-error-message-update later to maint).
   (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
   (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
   (merge 39bb692152 rs/imap-send-use-xsnprintf later to maint).
   (merge 8d320cec60 jc/t2104-style-fixes later to maint).
   (merge b4454d5a7b pw/t3428-cleanup later to maint).
   (merge 84a7c33a4b pf/commitish-committish later to maint).
   (merge 8882ee9d68 la/mailmap-entry later to maint).
   (merge 44bdba2fa6 rs/no-openssl-compilation-fix-on-macos later to maint).
   (merge f412d72c19 yb/replay-doc-linkfix later to maint).
   (merge 5da40be8d7 xx/rfc2822-date-format-in-doc later to maint).

----------------------------------------------------------------

Changes since v2.44.0 are as follows:

Ahelenia Ziemiańska (1):
      grep: improve errors for unmatched ( and )

Alexander Shopov (5):
      transport-helper.c: trivial fix of error message
      builtin/remote.c: trivial fix of error message
      builtin/clone.c: trivial fix of message
      revision.c: trivial fix to message
      l10n: bg.po: Updated Bulgarian translation (5652t)

Arkadii Yakovets (1):
      l10n: uk: v2.45 update

Aryan Gupta (1):
      tests: modernize the test script t0010-racy-git.sh

Bagas Sanjaya (1):
      l10n: po-id for 2.45

Beat Bolli (25):
      completion: use awk for filtering the config entries
      date: make "iso-strict" conforming for the UTC timezone
      t0006: add more tests with a negative TZ offset
      doc: avoid redundant use of cat
      contrib/subtree/t: avoid redundant use of cat
      t/lib-cvs.sh: avoid redundant use of cat
      t/annotate-tests.sh: avoid redundant use of cat
      t/perf: avoid redundant use of cat
      t/t0*: avoid redundant uses of cat
      t/t1*: avoid redundant uses of cat
      t/t3*: avoid redundant uses of cat
      t/t4*: avoid redundant uses of cat
      t/t5*: avoid redundant uses of cat
      t/t6*: avoid redundant uses of cat
      t/t7*: avoid redundant use of cat
      t/t8*: avoid redundant use of cat
      t/t9*: avoid redundant uses of cat
      t/t1*: merge a "grep | sed" pipeline
      t/t3*: merge a "grep | awk" pipeline
      t/t4*: merge a "grep | sed" pipeline
      t/t5*: merge a "grep | sed" pipeline
      t/t8*: merge "grep | sed" pipelines
      t/t9*: merge "grep | sed" pipelines
      contrib/coverage-diff: avoid redundant pipelines
      git-quiltimport: avoid an unnecessary subshell

Bo Anderson (5):
      t/lib-credential: clean additional credential
      osxkeychain: replace deprecated SecKeychain API
      osxkeychain: erase all matching credentials
      osxkeychain: erase matching passwords only
      osxkeychain: store new attributes

Brian C Tracy (1):
      fuzz: add fuzzer for config parsing

Brian Lyles (13):
      docs: clarify file options in git-config `--edit`
      docs: fix typo in git-config `--default`
      docs: correct trailer `key_value_separator` description
      docs: adjust trailer `separator` and `key_value_separator` language
      pretty: update tests to use `test_config`
      pretty: find pretty formats case-insensitively
      docs: address inaccurate `--empty` default with `--exec`
      docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
      rebase: update `--empty=ask` to `--empty=stop`
      sequencer: handle unborn branch with `--allow-empty`
      sequencer: do not require `allow_empty` for redundant commit options
      cherry-pick: enforce `--keep-redundant-commits` incompatibility
      cherry-pick: add `--empty` for more robust redundant commit handling

Chandra Pratap (2):
      apply: ignore working tree filemode when !core.filemode
      t9146: replace test -d/-e/-f with appropriate test_path_is_* function

Christian Couder (5):
      revision: clarify a 'return NULL' in get_reference()
      oidset: refactor oidset_insert_from_set()
      t6022: fix 'test' style and 'even though' typo
      rev-list: allow missing tips with --missing=[print|allow*]
      revision: fix --missing=[print|allow*] for annotated tags

Derrick Stolee (1):
      fetch: return when parsing submodule.recurse

Dirk Gouders (6):
      Documentation/user-manual.txt: example for generating object hashes
      MyFirstObjectWalk: use additional arg in config_fn_t
      MyFirstObjectWalk: fix misspelled "builtins/"
      MyFirstObjectWalk: fix filtered object walk
      MyFirstObjectWalk: fix description for counting omitted objects
      MyFirstObjectWalk: add stderr to pipe processing

Dragan Simic (8):
      documentation: send-email: use camel case consistently
      config: minor addition of whitespace
      config: really keep value-internal whitespace verbatim
      t1300: add more tests for whitespace and inline comments
      config.txt: describe handling of whitespace further
      grep docs: describe --recurse-submodules further and improve formatting a bit
      grep docs: describe --no-index further and improve formatting a bit
      config: fix some small capitalization issues, as spotted

Emir SARI (1):
      l10n: tr: Update Turkish translations

Eric Sunshine (2):
      docs: sort configuration variable groupings alphabetically
      test-lib: fix non-functioning GIT_TEST_MAINT_SCHEDULER fallback

Eric W. Biederman (23):
      object-file-convert: stubs for converting from one object format to another
      oid-array: teach oid-array to handle multiple kinds of oids
      object-names: support input of oids in any supported hash
      repository: add a compatibility hash algorithm
      loose: compatibilty short name support
      object-file: update the loose object map when writing loose objects
      object-file: add a compat_oid_in parameter to write_object_file_flags
      commit: convert mergetag before computing the signature of a commit
      commit: export add_header_signature to support handling signatures on tags
      tag: sign both hashes
      object: factor out parse_mode out of fast-import and tree-walk into in object.h
      object-file-convert: don't leak when converting tag objects
      object-file-convert: convert commits that embed signed tags
      object-file: update object_info_extended to reencode objects
      rev-parse: add an --output-object-format parameter
      builtin/cat-file: let the oid determine the output algorithm
      tree-walk: init_tree_desc take an oid to get the hash algorithm
      object-file: handle compat objects in check_object_signature
      builtin/ls-tree: let the oid determine the output algorithm
      test-lib: compute the compatibility hash so tests may use it
      t1006: rename sha1 to oid
      t1006: test oid compatibility with cat-file
      t1016-compatObjectFormat: add tests to verify the conversion between objects

Eugenio Gigante (1):
      add: use unsigned type for collection of bits

Florian Schmidt (1):
      wt-status: don't find scissors line beyond buf len

Ghanshyam Thakkar (5):
      add-patch: classify '@' as a synonym for 'HEAD'
      add -p tests: remove PERL prerequisites
      setup: remove unnecessary variable
      builtin/commit: error out when passing untracked path with -i
      builtin/add: error out when passing untracked path with -u

Haritha D (1):
      build: support z/OS (OS/390).

Harmen Stoppels (1):
      rebase: make warning less passive aggressive

Jakub Wilk (1):
      git-remote.txt: fix typo

Jean-Noël Avila (18):
      doc: git-rev-parse: enforce command-line description syntax
      doc: close unclosed angle-bracket of a placeholder in git-clone doc
      doc: end sentences with full-stop
      doc: clarify the format of placeholders
      doc: git-init: format verbatim parts
      doc: git-init: format placeholders
      doc: git-init: rework definition lists
      doc: git-init: rework config item init.templateDir
      doc: git-clone: format verbatim words
      doc: git-clone: format placeholders
      doc: format alternatives in synopsis
      doc: fix some placeholders formating
      doc: rework CodingGuidelines with new formatting rules
      doc: allow literal and emphasis format in doc vs help tests
      doc: git-init: apply new documentation formatting guidelines
      doc: git-clone: apply new documentation formatting guidelines
      doc: git-clone: do not autoreference the manpage in itself
      l10n: fr: v2.45.0

Jeff Hostetler (17):
      name-hash: add index_dir_find()
      t7527: add case-insensitve test for FSMonitor
      fsmonitor: refactor refresh callback on directory events
      fsmonitor: clarify handling of directory events in callback helper
      fsmonitor: refactor refresh callback for non-directory events
      dir: create untracked_cache_invalidate_trimmed_path()
      fsmonitor: refactor untracked-cache invalidation
      fsmonitor: move untracked-cache invalidation into helper functions
      fsmonitor: return invalidated cache-entry count on directory event
      fsmonitor: remove custom loop from non-directory path handler
      fsmonitor: return invalidated cache-entry count on non-directory event
      fsmonitor: trace the new invalidated cache-entry count
      fsmonitor: refactor bit invalidation in refresh callback
      fsmonitor: support case-insensitive events
      t0211: demonstrate missing 'def_param' events for certain commands
      trace2: avoid emitting 'def_param' set more than once
      trace2: emit 'def_param' set with 'cmd_name' event

Jeff King (51):
      t0303: check that helper_test_clean removes all credentials
      userdiff: skip textconv caching when not in a repository
      Revert "refs: allow @{n} to work with n-sized reflog"
      get_oid_basic(): special-case ref@{n} for oldest reflog entry
      read_ref_at(): special-case ref@{0} for an empty reflog
      upload-pack: drop separate v2 "haves" array
      upload-pack: switch deepen-not list to an oid_array
      upload-pack: use oidset for deepen_not list
      upload-pack: use a strmap for want-ref lines
      upload-pack: accept only a single packfile-uri line
      upload-pack: always turn off save_commit_buffer
      upload-pack: use PARSE_OBJECT_SKIP_HASH_CHECK in more places
      upload-pack: free tree buffers after parsing
      upload-pack: use repository struct to get config
      upload-pack: centralize setup of sideband-all config
      upload-pack: use existing config mechanism for advertisement
      upload-pack: only accept packfile-uris if we advertised it
      doc/gitremote-helpers: fix missing single-quote
      config: forbid newline as core.commentChar
      strbuf: simplify comment-handling in add_lines() helper
      strbuf: avoid static variables in strbuf_add_commented_lines()
      commit: refactor base-case of adjust_comment_line_char()
      strbuf: avoid shadowing global comment_line_char name
      environment: store comment_line_char as a string
      strbuf: accept a comment string for strbuf_stripspace()
      strbuf: accept a comment string for strbuf_commented_addf()
      strbuf: accept a comment string for strbuf_add_commented_lines()
      prefer comment_line_str to comment_line_char for printing
      find multi-byte comment chars in NUL-terminated strings
      find multi-byte comment chars in unterminated buffers
      sequencer: handle multi-byte comment characters when writing todo list
      wt-status: drop custom comment-char stringification
      environment: drop comment_line_char compatibility macro
      config: allow multi-byte core.commentChar
      shortlog: stop setting pp.print_email_subject
      pretty: split oneline and email subject printing
      pretty: drop print_email_subject flag
      log: do not set up extra_headers for non-email formats
      format-patch: return an allocated string from log_write_email_headers()
      format-patch: simplify after-subject MIME header handling
      doc/gitremote-helpers: fix more missing single-quotes
      transport-helper: use write helpers more consistently
      transport-helper: drop "object-format <algo>" option
      transport-helper: send "true" value for object-format option
      contrib: drop hg-to-git script
      format-patch: fix leak of empty header string
      rebase: use child_process_clear() to clean
      config: add core.commentString
      http: reset POSTFIELDSIZE when clearing curl handle
      INSTALL: bump libcurl version to 7.21.3
      remote-curl: add Transfer-Encoding header only for older curl

Jiamu Sun (1):
      bugreport.c: fix a crash in `git bugreport` with `--no-suffix` option

Jiang Xin (1):
      l10n: TEAMS: retire l10n teams no update in 1 year

Johannes Schindelin (22):
      merge-tree: accept 3 trees as arguments
      merge-tree: fail with a non-zero exit code on missing tree objects
      merge-ort: do check `parse_tree()`'s return value
      t4301: verify that merge-tree fails on missing blob objects
      Always check `parse_tree*()`'s return value
      cache-tree: avoid an unnecessary check
      fill_tree_descriptor(): mark error message for translation
      neue: remove a bogus empty file
      commit-reach(paint_down_to_common): plug two memory leaks
      commit-reach(repo_in_merge_bases_many): optionally expect missing commits
      commit-reach(repo_in_merge_bases_many): report missing commits
      commit-reach(paint_down_to_common): prepare for handling shallow commits
      commit-reach(paint_down_to_common): start reporting errors
      commit-reach(merge_bases_many): pass on "missing commits" errors
      commit-reach(get_merge_bases_many_0): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases): pass on "missing commits" errors
      commit-reach(get_octopus_merge_bases): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many_dirty): pass on errors
      merge-recursive: prepare for `merge_submodule()` to report errors
      merge-ort/merge-recursive: do report errors in `merge_submodule()`
      merge-tree: fix argument type of the `--merge-base` option

John Cai (1):
      t5300: fix test_with_bad_commit()

Jonas Wunderlich (1):
      doc: status.showUntrackedFiles does not take "false"

Josh Triplett (2):
      commit: avoid redundant scissor line with --cleanup=scissors -v
      commit: unify logic to avoid multiple scissors lines when merging

Julio Bacellari (1):
      doc: remove outdated information about interactive.singleKey

Junio C Hamano (64):
      apply: correctly reverse patch's pre- and post-image mode bits
      apply: code simplification
      t9210: do not rely on lazy fetching to fail
      git: --no-lazy-fetch option
      doc: add shortcut to "am --whitespace=<action>"
      doc: apply the new placeholder rules to git-add documentation
      compat: drop inclusion of <git-compat-util.h>
      Start the 2.45 cycle
      git: document GIT_NO_REPLACE_OBJECTS environment variable
      doc: clarify the wording on <git-compat-util.h> requirement
      git: extend --no-lazy-fetch to work across subprocesses
      The second batch
      The third batch
      test_i18ngrep: hard deprecate and forbid its use
      unpack: replace xwrite() loop with write_in_full()
      sideband: avoid short write(2)
      repack: check error writing to pack-objects subprocess
      clean: further clean-up of implementation around "--force"
      The fourth batch
      The fifth batch
      setup: notice more types of implicit bare repositories
      The sixth batch
      status: unify parsing of --untracked= and status.showUntrackedFiles
      status: allow --untracked=false and friends
      The seventh batch
      The eighth batch
      config: fix --comment formatting
      config: allow tweaking whitespace between value and comment
      diff.*Prefix: use camelCase in the doc and test titles
      The ninth batch
      apply: parse names out of "diff --git" more carefully
      The tenth batch
      The eleventh batch
      SubmittingPatches: release-notes entry experiment
      The twelfth batch
      t4126: make sure a directory with SP at the end is usable
      t4126: fix "funny directory name" test on Windows (again)
      advice: omit trailing whitespace
      checkout: omit "tracking" information on a detached HEAD
      The thirteenth batch
      t2104: style fixes
      The fourteenth batch
      revision: optionally record matches with pathspec elements
      The fifteenth batch
      CodingGuidelines: describe "export VAR=VAL" rule
      CodingGuidelines: quote assigned value in 'local var=$val'
      t: local VAR="VAL" (quote positional parameters)
      t: local VAR="VAL" (quote command substitution)
      t: local VAR="VAL" (quote ${magic-reference})
      t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
      t0610: local VAR="VAL" fix
      t1016: local VAR="VAL" fix
      config: do not leak excludes_file
      Makefile(s): do not enforce "all indents must be done with tab"
      The sixteenth batch
      t2104: style fixes
      The seventeenth batch
      The eighteenth batch
      The ninteenth batch
      The twentieth batch
      Git 2.45-rc0
      A bit more topics before -rc1
      Git 2.45-rc1
      Git 2.45

Justin Tobler (3):
      reftable/stack: expose option to disable auto-compaction
      reftable/stack: add env to disable autocompaction
      reftable/stack: use geometric table compaction

Karthik Nayak (7):
      refs: introduce `is_pseudoref()` and `is_headref()`
      refs: extract out `loose_fill_ref_dir_regular_file()`
      refs: introduce `refs_for_each_include_root_refs()`
      ref-filter: rename 'FILTER_REFS_ALL' to 'FILTER_REFS_REGULAR'
      for-each-ref: add new option to include root refs
      update-ref: use {old,new}-oid instead of {old,new}value
      githooks: use {old,new}-oid instead of {old,new}-value

Kipras Melnikovas (1):
      mergetools: vimdiff: use correct tool's name when reading mergetool config

Kristoffer Haugsbakk (9):
      column: disallow negative padding
      column: guard against negative padding
      gitcli: drop mention of “non-dashed form”
      config: document `core.commentChar` as ASCII-only
      t3200: improve test style
      advice: make all entries stylistically consistent
      advice: use backticks for verbatim
      advice: use double quotes for regular quoting
      branch: advise about ref syntax rules

Linus Arver (15):
      trailer: free trailer_info _after_ all related usage
      shortlog: add test for de-duplicating folded trailers
      trailer: rename functions to use 'trailer'
      trailer: reorder format_trailers_from_commit() parameters
      trailer: move interpret_trailers() to interpret-trailers.c
      trailer_info_get(): reorder parameters
      format_trailers(): use strbuf instead of FILE
      format_trailer_info(): move "fast path" to caller
      format_trailers_from_commit(): indirectly call trailer_info_get()
      format_trailer_info(): use trailer_item objects
      format_trailer_info(): drop redundant unfold_value()
      format_trailer_info(): append newline for non-trailer lines
      trailer: begin formatting unification
      trailer: finish formatting unification
      mailmap: change primary address for Linus Arver

M Hickford (1):
      libsecret: retrieve empty password

Marcel Röthke (1):
      rerere: fix crashes due to unmatched opening conflict markers

Matthias Aßhauer (1):
      Win32: detect unix socket support at runtime

Max Gautier (1):
      editorconfig: add Makefiles to "text files"

Michael Lohmann (2):
      revision: ensure MERGE_HEAD is a ref in prepare_show_merge
      revision: implement `git log --merge` also for rebase/cherry-pick/revert

Orgad Shaneh (1):
      docs: remove duplicate entry and fix typo in 2.45 changelog

Patrick Steinhardt (99):
      refs: introduce reftable backend
      ci: add jobs to test with the reftable backend
      refs/reftable: fix leak when copying reflog fails
      reftable/record: introduce function to compare records by key
      reftable/merged: allocation-less dropping of shadowed records
      reftable/merged: skip comparison for records of the same subiter
      reftable/pq: allocation-less comparison of entry keys
      reftable/block: swap buffers instead of copying
      reftable/record: don't try to reallocate ref record name
      reftable/reader: add comments to `table_iter_next()`
      t: move tests exercising the "files" backend
      t0410: convert tests to use DEFAULT_REPO_FORMAT prereq
      t1400: exercise reflog with gaps with reftable backend
      t1404: make D/F conflict tests compatible with reftable backend
      t1405: remove unneeded cleanup step
      t2011: exercise D/F conflicts with HEAD with the reftable backend
      t7003: ensure filter-branch prunes reflogs with the reftable backend
      git-difftool--helper: honor `--trust-exit-code` with `--dir-diff`
      dir-iterator: pass name to `prepare_next_entry_data()` directly
      dir-iterator: support iteration in sorted order
      refs/files: sort reflogs returned by the reflog iterator
      refs/files: sort merged worktree and common reflogs
      refs: always treat iterators as ordered
      refs: drop unused params from the reflog iterator callback
      refs: stop resolving ref corresponding to reflogs
      builtin/reflog: introduce subcommand to list reflogs
      builtin/clone: allow remote helpers to detect repo
      refs/reftable: don't fail empty transactions in repo without HEAD
      reftable/pq: use `size_t` to track iterator index
      reftable/merged: make `merged_iter` structure private
      reftable/merged: advance subiter on subsequent iteration
      reftable/merged: make subiters own their records
      reftable/merged: remove unnecessary null check for subiters
      reftable/merged: handle subiter cleanup on close only
      reftable/merged: circumvent pqueue with single subiter
      reftable/merged: avoid duplicate pqueue emptiness check
      reftable/record: reuse refname when decoding
      reftable/record: reuse refname when copying
      reftable/record: decode keys in place
      reftable: allow inlining of a few functions
      refs/reftable: precompute prefix length
      refs/reftable: reload correct stack when creating reflog iter
      reftable/record: convert old and new object IDs to arrays
      reftable/record: avoid copying author info
      reftable/record: reuse refnames when decoding log records
      reftable/record: reuse message when decoding log records
      reftable/record: use scratch buffer when decoding records
      refs/reftable: track last log record name via strbuf
      t0610: remove unused variable assignment
      lockfile: report when rollback fails
      reftable/stack: register new tables as tempfiles
      reftable/stack: register lockfiles during compaction
      reftable/stack: register compacted tables as tempfiles
      reftable/record: fix memory leak when decoding object records
      reftable/block: fix binary search over restart counter
      t5601: exercise clones with "includeIf.*.onbranch"
      reftable: fix tests being broken by NFS' delete-after-close semantics
      t7800: improve test descriptions with empty arguments
      t7800: use single quotes for test bodies
      t/README: document how to loop around test cases
      reftable/stack: fix error handling in `reftable_stack_init_addition()`
      reftable/error: discern locked/outdated errors
      reftable/stack: use error codes when locking fails during compaction
      reftable/stack: gracefully handle failed auto-compaction due to locks
      refs/reftable: print errors on compaction failure
      t/helper: drop pack-refs wrapper
      refs: move `struct pack_refs_opts` to where it's used
      refs: remove `PACK_REFS_ALL` flag
      refs/reftable: expose auto compaction via new flag
      builtin/pack-refs: release allocated memory
      builtin/pack-refs: introduce new "--auto" flag
      builtin/gc: move `struct maintenance_run_opts`
      t6500: extract objects with "17" prefix
      builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
      builtin/gc: pack refs when using `git maintenance run --auto`
      reftable/basics: fix return type of `binsearch()` to be `size_t`
      reftable/basics: improve `binsearch()` test
      reftable/refname: refactor binary search over refnames
      reftable/block: refactor binary search over restart points
      reftable/block: fix error handling when searching restart points
      reftable/record: extract function to decode key lengths
      reftable/block: avoid decoding keys when searching restart points
      t0610: make `--shared=` tests reusable
      t0610: execute git-pack-refs(1) with specified umask
      reftable/block: rename `block_reader_start()`
      reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
      reftable/block: better grouping of functions
      reftable/block: introduce `block_reader_release()`
      reftable/block: move ownership of block reader into `struct table_iter`
      reftable/reader: iterate to next block in place
      reftable/block: reuse uncompressed blocks
      reftable/block: open-code call to `uncompress2()`
      reftable/block: reuse `zstream` state on inflation
      reftable/block: avoid copying block iterators on seek
      pack-bitmap: gracefully handle missing BTMP chunks
      run-command: introduce function to prepare auto-maintenance process
      builtin/receive-pack: convert to use git-maintenance(1)
      docs: improve changelog entry for `git pack-refs --auto`
      docs: address typos in Git v2.45 changelog

Peter Hutterer (1):
      diff: add diff.srcPrefix and diff.dstPrefix configuration variables

Peter Krefting (2):
      bisect: report the found commit with "show"
      l10n: sv.po: Update Swedish translation

Philippe Blain (5):
      merge-ort: turn submodule conflict suggestions into an advice
      ci(github): make Windows test artifacts name unique
      sequencer: allow disabling conflict advice
      builtin/am: allow disabling conflict advice
      t/README: mention test files are make targets

Phillip Wood (9):
      rebase -i: stop setting GIT_CHERRY_PICK_HELP
      xdiff-interface: refactor parsing of merge.conflictstyle
      merge-ll: introduce LL_MERGE_OPTIONS_INIT
      merge options: add a conflict style member
      checkout: cleanup --conflict=<style> parsing
      checkout: fix interaction between --conflict and --merge
      t3428: modernize test setup
      t3428: use test_commit_message
      t3428: restore coverage for "apply" backend

Pi Fisher (1):
      typo: replace 'commitish' with 'committish'

Ralf Thielow (1):
      l10n: Update German translation

Ralph Seichter (1):
      config: add --comment option to add a comment

René Scharfe (32):
      use xstrncmpz()
      fetch: convert strncmp() with strlen() to starts_with()
      mem-pool: add mem_pool_strfmt()
      name-rev: use mem_pool_strfmt()
      submodule: use strvec_pushf() for --submodule-prefix
      t-ctype: allow NUL anywhere in the specification string
      t-ctype: simplify EOF check
      t-ctype: align output of i
      t-ctype: avoid duplicating class names
      parse-options: recognize abbreviated negated option with arg
      parse-options: set arg of abbreviated option lazily
      parse-options: factor out register_abbrev() and struct parsed_option
      parse-options: detect ambiguous self-negation
      parse-options: normalize arg and long_name before comparison
      parse-options: rearrange long_name matching code
      t-prio-queue: shorten array index message
      t-prio-queue: check result array bounds
      factor out strbuf_expand_bad_format()
      cat-file: use strbuf_expand_bad_format()
      midx: use strvec_pushf() for pack-objects base name
      mem-pool: use st_add() in mem_pool_strvfmt()
      imap-send: use xsnprintf to format command
      t-prio-queue: simplify using compound literals
      apply: avoid fixed-size buffer in create_one_file()
      path: remove mksnpath()
      apply: don't leak fd on fdopen() error
      usage: report vsnprintf(3) failure
      date: make DATE_MODE thread-safe
      git-compat-util: fix NO_OPENSSL on current macOS
      imap-send: increase command size limit
      apply: avoid using fixed-size buffer in write_out_one_reject()
      don't report vsnprintf(3) error as bug

Richard Macklin (1):
      rebase: fix typo in autosquash documentation

Rubén Justo (18):
      tag: error when git-column fails
      completion: fix __git_complete_worktree_paths
      completion: reflog with implicit "show"
      completion: reflog show <log-options>
      completion: introduce __git_find_subcommand
      completion: factor out __git_resolve_builtins
      completion: reflog subcommands and options
      checkout: plug some leaks in git-restore
      add-patch: introduce 'p' in interactive-patch
      add-patch: do not print hunks repeatedly
      add: use advise_if_enabled for ADVICE_ADD_IGNORED_FILE
      add: use advise_if_enabled for ADVICE_ADD_EMPTY_PATHSPEC
      add: use advise_if_enabled for ADVICE_ADD_EMBEDDED_REPO
      launch_editor: waiting message on error
      apply: plug a leak in apply_data
      add-interactive: plug a leak in get_untracked_files
      add-patch: plug a leak handling the '/' command
      add: plug a leak on interactive_add

SZEDER Gábor (1):
      upload-pack: don't send null character in abort message to the client

Sergey Organov (1):
      clean: improve -n and -f implementation and documentation

Steven Jeuris (1):
      userdiff: better method/property matching for C#

Taylor Blau (9):
      Documentation/config/pack.txt: fix broken AsciiDoc mark-up
      upload-pack: disallow object-info capability by default
      midx-write: move writing-related functions from midx.c
      midx-write.c: factor out common want_included_pack() routine
      midx-write.c: check count of packs to repack after grouping
      midx-write.c: use `--stdin-packs` when repacking
      t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `
      Makefile(s): avoid recipe prefix in conditional statements
      Documentation/RelNotes/2.45.0.txt: fix typo

Teng Long (1):
      l10n: zh_CN: for git 2.45 rounds

Thalia Archibald (8):
      fast-import: tighten path unquoting
      fast-import: directly use strbufs for paths
      fast-import: allow unquoted empty path for root
      fast-import: remove dead strbuf
      fast-import: improve documentation for path quoting
      fast-import: document C-style escapes for paths
      fast-import: forbid escaped NUL in paths
      fast-import: make comments more precise

Ville Skyttä (2):
      completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode
      completion: protect prompt against unset SHOWUPSTREAM in nounset mode

Vincenzo Mezzela (1):
      t7301: use test_path_is_(missing|file)

Vũ Tiến Hưng (2):
      l10n: Update Vietnamese team contact
      l10n: vi: Updated translation for 2.45

Xing Xin (1):
      Documentation: fix typos describing date format

Yehezkel Bernat (1):
      Documentation: fix linkgit reference

Yi-Jyun Pan (1):
      l10n: zh-TW: Git 2.45

brian m. carlson (7):
      loose: add a mapping between SHA-1 and SHA-256 for loose objects
      commit: write commits for both hashes
      cache: add a function to read an OID of a specific algorithm
      object-file-convert: add a function to convert trees between algorithms
      object-file-convert: convert tag objects when writing
      object-file-convert: convert commit objects when writing
      repository: implement extensions.compatObjectFormat

shejialuo (1):
      t9117: prefer test_path_* helper functions

Đoàn Trần Công Danh (1):
      t9604: Fix test for musl libc and new Debian



^ permalink raw reply	[relevance 2%]

* [PATCH v2] builtin/tag.c: add --trailer arg
  2024-04-29  4:31  2% [PATCH] builtin/tag.c: add --trailer arg John Passaro via GitGitGadget
@ 2024-04-29 16:53  1% ` John Passaro via GitGitGadget
  2024-04-29 18:54  3%   ` [PATCH v3 0/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: John Passaro via GitGitGadget @ 2024-04-29 16:53 UTC (permalink / raw)
  To: git; +Cc: John Passaro, John Passaro

From: John Passaro <john.a.passaro@gmail.com>

Teach git-tag to accept --trailer option to add trailers to annotated
tag messages, like git-commit. Move the code that git-commit uses for
trailers to the trailer.h API, so it can be re-used for git-tag.

Signed-off-by: John Passaro <john.a.passaro@gmail.com>
---
    builtin/tag.c: add --trailer arg
    
    cc: Patrick Steinhardt ps@pks.im cc: John Passaro
    john.a.passaro@gmail.com

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1723%2Fjpassaro%2Fjp%2Ftag-trailer-arg-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1723/jpassaro/jp/tag-trailer-arg-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1723

Range-diff vs v1:

 1:  02d7a0f035e ! 1:  d4beb7cd67e builtin/tag.c: add --trailer arg
     @@ Commit message
          builtin/tag.c: add --trailer arg
      
          Teach git-tag to accept --trailer option to add trailers to annotated
     -    tag messages, like git-commit.
     +    tag messages, like git-commit. Move the code that git-commit uses for
     +    trailers to the trailer.h API, so it can be re-used for git-tag.
      
          Signed-off-by: John Passaro <john.a.passaro@gmail.com>
      
     @@ Documentation/git-tag.txt: This option is only applicable when listing tags with
       --edit::
       	The message taken from file with `-F` and command line with
      
     + ## builtin/commit.c ##
     +@@
     + #include "commit-reach.h"
     + #include "commit-graph.h"
     + #include "pretty.h"
     ++#include "trailer.h"
     + 
     + static const char * const builtin_commit_usage[] = {
     + 	N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
     +@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
     + 	fclose(s->fp);
     + 
     + 	if (trailer_args.nr) {
     +-		struct child_process run_trailer = CHILD_PROCESS_INIT;
     +-
     +-		strvec_pushl(&run_trailer.args, "interpret-trailers",
     +-			     "--in-place", "--no-divider",
     +-			     git_path_commit_editmsg(), NULL);
     +-		strvec_pushv(&run_trailer.args, trailer_args.v);
     +-		run_trailer.git_cmd = 1;
     +-		if (run_command(&run_trailer))
     ++		if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
     + 			die(_("unable to pass trailers to --trailers"));
     + 		strvec_clear(&trailer_args);
     + 	}
     +
       ## builtin/tag.c ##
      @@
       #include "date.h"
       #include "write-or-die.h"
       #include "object-file-convert.h"
     -+#include "run-command.h"
     ++#include "trailer.h"
       
       static const char * const git_tag_usage[] = {
       	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
     @@ builtin/tag.c: static const char message_advice_nested_tag[] =
       		       const char *tag,
       		       struct strbuf *buf, struct create_tag_options *opt,
      -		       struct object_id *prev, struct object_id *result, char *path)
     -+		       struct object_id *prev, struct object_id *result, struct strvec *trailer_args, char *path)
     ++		       struct object_id *prev, struct object_id *result,
     ++		       struct strvec *trailer_args, char *path)
       {
       	enum object_type type;
       	struct strbuf header = STRBUF_INIT;
     @@ builtin/tag.c: static void create_tag(const struct object_id *object, const char
       
      -		if (opt->message_given) {
      +		if (opt->message_given && buf->len) {
     ++			strbuf_complete(buf, '\n');
       			write_or_die(fd, buf->buf, buf->len);
     -+			if (trailer_args->nr && buf->buf[buf->len-1] != '\n') {
     -+				write_or_die(fd, "\n", 1);
     -+			}
       			strbuf_reset(buf);
       		} else if (!is_null_oid(prev)) {
     - 			write_tag_body(fd, prev);
      @@ builtin/tag.c: static void create_tag(const struct object_id *object, const char *object_ref,
       		}
       		close(fd);
     @@ builtin/tag.c: static void create_tag(const struct object_id *object, const char
      -			fprintf(stderr,
      -			_("Please supply the message using either -m or -F option.\n"));
      -			exit(1);
     -+		if (trailer_args->nr) {
     -+			struct child_process run_trailer = CHILD_PROCESS_INIT;
     -+
     -+			strvec_pushl(&run_trailer.args, "interpret-trailers",
     -+				     "--in-place", "--no-divider",
     -+				     path, NULL);
     -+			strvec_pushv(&run_trailer.args, trailer_args->v);
     -+			run_trailer.git_cmd = 1;
     -+			if (run_command(&run_trailer))
     -+				die(_("unable to pass trailers to --trailers"));
     -+		}
     ++		if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
     ++			die(_("unable to pass trailers to --trailers"));
      +
      +		if (should_edit) {
      +			if (launch_editor(path, buf, NULL)) {
      +				fprintf(stderr,
     -+				_("Please supply the message using either -m or -F option.\n"));
     ++					_("Please supply the message using either -m or -F option.\n"));
      +				exit(1);
      +			}
      +		} else if (trailer_args->nr) {
      +			strbuf_reset(buf);
      +			if (strbuf_read_file(buf, path, 0) < 0) {
      +				fprintf(stderr,
     -+						_("Please supply the message using either -m or -F option.\n"));
     ++					_("Please supply the message using either -m or -F option.\n"));
      +				exit(1);
      +			}
       		}
     @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
       		OPT_CALLBACK_F('m', "message", &msg, N_("message"),
       			       N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
       		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
     -+		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
     ++		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"),
     ++				PARSE_OPT_NONEG, opt_pass_trailer),
       		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
       		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
       		OPT_CLEANUP(&cleanup_arg),
     @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
       		set_signing_key(keyid);
       	}
      -	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
     -+	create_tag_object = (opt.sign || annotate || msg.given || msgfile || edit_flag || trailer_args.nr);
     ++	create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
     ++			     edit_flag || trailer_args.nr);
       
       	if ((create_tag_object || force) && (cmdmode != 0))
       		usage_with_options(git_tag_usage, options);
      @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
     - 	else if (!force)
     - 		die(_("tag '%s' already exists"), tag);
     - 
     --	opt.message_given = msg.given || msgfile;
     -+	opt.message_given = msg.given || (msgfile != NULL);
     - 	opt.use_editor = edit_flag;
     - 
     - 	if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
     -@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
     - 		if (force_sign_annotate && !annotate)
       			opt.sign = 1;
       		path = git_pathdup("TAG_EDITMSG");
     --		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
     -+		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object, &trailer_args,
     - 			   path);
     + 		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
     +-			   path);
     ++			   &trailer_args, path);
       	}
       
     + 	transaction = ref_transaction_begin(&err);
      @@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
       	strbuf_release(&reflog_msg);
       	strbuf_release(&msg.buf);
     @@ t/t7004-tag.sh: test_expect_success \
       
      +# trailers
      +
     -+get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect
     -+cat >>expect <<EOF
     -+create tag with trailers
     -+
     -+my-trailer: here
     -+alt-trailer: there
     -+EOF
      +test_expect_success 'create tag with -m and --trailer' '
     -+	git tag -m "create tag with trailers"  --trailer my-trailer=here --trailer alt-trailer=there tag-with-inline-message-and-trailers &&
     ++	get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
     ++	cat >>expect <<-\EOF &&
     ++	create tag with trailers
     ++
     ++	my-trailer: here
     ++	alt-trailer: there
     ++	EOF
     ++	git tag -m "create tag with trailers" \
     ++		--trailer my-trailer=here \
     ++		--trailer alt-trailer=there \
     ++		tag-with-inline-message-and-trailers &&
      +	get_tag_msg tag-with-inline-message-and-trailers >actual &&
      +	test_cmp expect actual
      +'
     @@ t/t7004-tag.sh: test_expect_success \
      +	test_cmp expect actual
      +'
      +
     -+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
     -+get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect
     -+cat >>expect <<EOF
     -+create tag from message file using --trailer
     -+
     -+my-trailer: here
     -+alt-trailer: there
     -+EOF
      +test_expect_success 'create tag with -F and --trailer' '
     -+	git tag -F messagefilewithnotrailers  --trailer my-trailer=here --trailer alt-trailer=there tag-with-file-message-and-trailers &&
     ++	echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
     ++	get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
     ++	cat >>expect <<-\EOF &&
     ++	create tag from message file using --trailer
     ++
     ++	my-trailer: here
     ++	alt-trailer: there
     ++	EOF
     ++	git tag -F messagefilewithnotrailers \
     ++		--trailer my-trailer=here \
     ++		--trailer alt-trailer=there \
     ++		tag-with-file-message-and-trailers &&
      +	get_tag_msg tag-with-file-message-and-trailers >actual &&
      +	test_cmp expect actual
      +'
      +
     -+test_expect_success 'set up editor' '
     -+	write_script fakeeditor <<-\EOF
     ++test_expect_success 'create tag with -m and --trailer and --edit' '
     ++	write_script fakeeditor <<-\EOF &&
      +	sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
      +	mv "$1-" "$1"
      +	EOF
     -+'
     -+
     -+get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect
     -+cat >>expect <<EOF
     -+EDITED: create tag with trailers
     ++	get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
     ++	cat >>expect <<-\EOF &&
     ++	EDITED: create tag with trailers
      +
     -+my-trailer: here
     -+alt-trailer: there
     -+EOF
     -+test_expect_success 'create tag with -m and --trailer and --edit' '
     -+	GIT_EDITOR=./fakeeditor git tag --edit -m "create tag with trailers"  --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-inline-message-and-trailers &&
     ++	my-trailer: here
     ++	alt-trailer: there
     ++	EOF
     ++	GIT_EDITOR=./fakeeditor git tag --edit \
     ++		-m "create tag with trailers" \
     ++		--trailer my-trailer=here \
     ++		--trailer alt-trailer=there \
     ++		tag-with-edited-inline-message-and-trailers &&
      +	get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
      +	test_cmp expect actual
      +'
      +
     -+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
     -+get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect
     -+cat >>expect <<EOF
     -+EDITED: create tag from message file using --trailer
     -+
     -+my-trailer: here
     -+alt-trailer: there
     -+EOF
      +test_expect_success 'create tag with -F and --trailer and --edit' '
     -+	GIT_EDITOR=./fakeeditor git tag --edit -F messagefilewithnotrailers  --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-file-message-and-trailers &&
     ++	echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
     ++	get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
     ++	cat >>expect <<-\EOF &&
     ++	EDITED: create tag from message file using --trailer
     ++
     ++	my-trailer: here
     ++	alt-trailer: there
     ++	EOF
     ++	GIT_EDITOR=./fakeeditor git tag --edit \
     ++		-F messagefilewithnotrailers \
     ++		--trailer my-trailer=here \
     ++		--trailer alt-trailer=there \
     ++		tag-with-edited-file-message-and-trailers &&
      +	get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
      +	test_cmp expect actual
      +'
      +
     -+test_expect_success 'set up editor' '
     -+	write_script fakeeditor <<-\EOF
     ++test_expect_success 'create annotated tag and force editor when only --trailer is given' '
     ++	write_script fakeeditor <<-\EOF &&
      +	echo "add a line" >"$1-"
     -+	echo >>"$1-"
      +	cat <"$1" >>"$1-"
      +	mv "$1-" "$1"
      +	EOF
     -+'
     -+
     -+get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect
     -+cat >>expect <<EOF
     -+add a line
     ++	get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
     ++	cat >>expect <<-\EOF &&
     ++	add a line
      +
     -+my-trailer: here
     -+alt-trailer: there
     -+EOF
     -+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
     -+	GIT_EDITOR=./fakeeditor git tag --trailer my-trailer=here --trailer alt-trailer=there tag-with-trailers-and-no-message &&
     ++	my-trailer: here
     ++	alt-trailer: there
     ++	EOF
     ++	GIT_EDITOR=./fakeeditor git tag \
     ++		--trailer my-trailer=here \
     ++		--trailer alt-trailer=there \
     ++		tag-with-trailers-and-no-message &&
      +	get_tag_msg tag-with-trailers-and-no-message >actual &&
      +	test_cmp expect actual
      +'
     @@ t/t7004-tag.sh: test_expect_success 'git tag --format with ahead-behind' '
       	refs/tags/tag-zero-lines 0 1 !
       	EOF
       	git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
     +
     + ## trailer.c ##
     +@@
     + #include "commit.h"
     + #include "trailer.h"
     + #include "list.h"
     ++#include "run-command.h"
     + /*
     +  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
     +  */
     +@@ trailer.c: void trailer_iterator_release(struct trailer_iterator *iter)
     + 	strbuf_release(&iter->val);
     + 	strbuf_release(&iter->key);
     + }
     ++
     ++int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
     ++	struct child_process run_trailer = CHILD_PROCESS_INIT;
     ++
     ++	run_trailer.git_cmd = 1;
     ++	strvec_pushl(&run_trailer.args, "interpret-trailers",
     ++		     "--in-place", "--no-divider",
     ++		     path, NULL);
     ++	strvec_pushv(&run_trailer.args, trailer_args->v);
     ++	return run_command(&run_trailer);
     ++}
     +
     + ## trailer.h ##
     +@@
     + 
     + #include "list.h"
     + #include "strbuf.h"
     ++#include "strvec.h"
     + 
     + enum trailer_where {
     + 	WHERE_DEFAULT,
     +@@ trailer.h: int trailer_iterator_advance(struct trailer_iterator *iter);
     +  */
     + void trailer_iterator_release(struct trailer_iterator *iter);
     + 
     ++/*
     ++ * Augment a file to add trailers to it by running git-interpret-trailers.
     ++ * This calls run_command() and its return value is the same (i.e. 0 for
     ++ * success, various non-zero for other errors). See run-command.h.
     ++ */
     ++int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
     ++
     + #endif /* TRAILER_H */


 Documentation/git-tag.txt |  18 +++++-
 builtin/commit.c          |  10 +---
 builtin/tag.c             |  49 +++++++++++++---
 t/t7004-tag.sh            | 114 ++++++++++++++++++++++++++++++++++++++
 trailer.c                 |  12 ++++
 trailer.h                 |   8 +++
 6 files changed, 192 insertions(+), 19 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c31ec..79b0a7e9644 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+	[(--trailer <token>[(=|:)<value>])...]
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@ creates a 'tag' object, and requires a tag message.  Unless
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
 
 Otherwise, a tag reference that points directly at the given object
 (i.e., a lightweight tag) is created.
@@ -178,6 +179,19 @@ This option is only applicable when listing tags without annotation lines.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
 	is given.
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git tag --trailer "Signed-off-by:T A Ger \
+	<tagger@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer to the tag message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+	The trailers can be seen in `git tag --list` using
+	`--format="%(trailers)"` placeholder.
+
 -e::
 --edit::
 	The message taken from file with `-F` and command line with
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e1484446b0..a1cbc128429 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -38,6 +38,7 @@
 #include "commit-reach.h"
 #include "commit-graph.h"
 #include "pretty.h"
+#include "trailer.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
@@ -1038,14 +1039,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	fclose(s->fp);
 
 	if (trailer_args.nr) {
-		struct child_process run_trailer = CHILD_PROCESS_INIT;
-
-		strvec_pushl(&run_trailer.args, "interpret-trailers",
-			     "--in-place", "--no-divider",
-			     git_path_commit_editmsg(), NULL);
-		strvec_pushv(&run_trailer.args, trailer_args.v);
-		run_trailer.git_cmd = 1;
-		if (run_command(&run_trailer))
+		if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
 			die(_("unable to pass trailers to --trailers"));
 		strvec_clear(&trailer_args);
 	}
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b45..0a029fb8c30 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
 #include "date.h"
 #include "write-or-die.h"
 #include "object-file-convert.h"
+#include "trailer.h"
 
 static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+	   "        [(--trailer <token>[(=|:)<value>])...]\n"
 	   "        <tagname> [<commit> | <object>]"),
 	N_("git tag -d <tagname>..."),
 	N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -290,10 +292,12 @@ static const char message_advice_nested_tag[] =
 static void create_tag(const struct object_id *object, const char *object_ref,
 		       const char *tag,
 		       struct strbuf *buf, struct create_tag_options *opt,
-		       struct object_id *prev, struct object_id *result, char *path)
+		       struct object_id *prev, struct object_id *result,
+		       struct strvec *trailer_args, char *path)
 {
 	enum object_type type;
 	struct strbuf header = STRBUF_INIT;
+	int should_edit;
 
 	type = oid_object_info(the_repository, object, NULL);
 	if (type <= OBJ_NONE)
@@ -313,13 +317,15 @@ static void create_tag(const struct object_id *object, const char *object_ref,
 		    tag,
 		    git_committer_info(IDENT_STRICT));
 
-	if (!opt->message_given || opt->use_editor) {
+	should_edit = opt->use_editor || !opt->message_given;
+	if (should_edit || trailer_args->nr) {
 		int fd;
 
 		/* write the template message before editing: */
 		fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
-		if (opt->message_given) {
+		if (opt->message_given && buf->len) {
+			strbuf_complete(buf, '\n');
 			write_or_die(fd, buf->buf, buf->len);
 			strbuf_reset(buf);
 		} else if (!is_null_oid(prev)) {
@@ -338,10 +344,22 @@ static void create_tag(const struct object_id *object, const char *object_ref,
 		}
 		close(fd);
 
-		if (launch_editor(path, buf, NULL)) {
-			fprintf(stderr,
-			_("Please supply the message using either -m or -F option.\n"));
-			exit(1);
+		if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
+			die(_("unable to pass trailers to --trailers"));
+
+		if (should_edit) {
+			if (launch_editor(path, buf, NULL)) {
+				fprintf(stderr,
+					_("Please supply the message using either -m or -F option.\n"));
+				exit(1);
+			}
+		} else if (trailer_args->nr) {
+			strbuf_reset(buf);
+			if (strbuf_read_file(buf, path, 0) < 0) {
+				fprintf(stderr,
+					_("Please supply the message using either -m or -F option.\n"));
+				exit(1);
+			}
 		}
 	}
 
@@ -416,6 +434,14 @@ struct msg_arg {
 	struct strbuf buf;
 };
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(opt->value, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 {
 	struct msg_arg *msg = opt->value;
@@ -463,6 +489,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
+	struct strvec trailer_args = STRVEC_INIT;
 	int icase = 0;
 	int edit_flag = 0;
 	struct option options[] = {
@@ -479,6 +506,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_CALLBACK_F('m', "message", &msg, N_("message"),
 			       N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
 		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"),
+				PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
 		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
 		OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +577,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		opt.sign = 1;
 		set_signing_key(keyid);
 	}
-	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+	create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
+			     edit_flag || trailer_args.nr);
 
 	if ((create_tag_object || force) && (cmdmode != 0))
 		usage_with_options(git_tag_usage, options);
@@ -654,7 +684,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			opt.sign = 1;
 		path = git_pathdup("TAG_EDITMSG");
 		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
-			   path);
+			   &trailer_args, path);
 	}
 
 	transaction = ref_transaction_begin(&err);
@@ -686,6 +716,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	strbuf_release(&reflog_msg);
 	strbuf_release(&msg.buf);
 	strbuf_release(&err);
+	strvec_clear(&trailer_args);
 	free(msgfile);
 	return ret;
 }
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 696866d7794..fa6336edf98 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -668,6 +668,115 @@ test_expect_success \
 	test_cmp expect actual
 '
 
+# trailers
+
+test_expect_success 'create tag with -m and --trailer' '
+	get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
+	cat >>expect <<-\EOF &&
+	create tag with trailers
+
+	my-trailer: here
+	alt-trailer: there
+	EOF
+	git tag -m "create tag with trailers" \
+		--trailer my-trailer=here \
+		--trailer alt-trailer=there \
+		tag-with-inline-message-and-trailers &&
+	get_tag_msg tag-with-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'list tag extracting trailers' '
+	cat >expect <<-\EOF &&
+	my-trailer: here
+	alt-trailer: there
+
+	EOF
+	git tag --list --format="%(trailers)" tag-with-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer' '
+	echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+	get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
+	cat >>expect <<-\EOF &&
+	create tag from message file using --trailer
+
+	my-trailer: here
+	alt-trailer: there
+	EOF
+	git tag -F messagefilewithnotrailers \
+		--trailer my-trailer=here \
+		--trailer alt-trailer=there \
+		tag-with-file-message-and-trailers &&
+	get_tag_msg tag-with-file-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create tag with -m and --trailer and --edit' '
+	write_script fakeeditor <<-\EOF &&
+	sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+	mv "$1-" "$1"
+	EOF
+	get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
+	cat >>expect <<-\EOF &&
+	EDITED: create tag with trailers
+
+	my-trailer: here
+	alt-trailer: there
+	EOF
+	GIT_EDITOR=./fakeeditor git tag --edit \
+		-m "create tag with trailers" \
+		--trailer my-trailer=here \
+		--trailer alt-trailer=there \
+		tag-with-edited-inline-message-and-trailers &&
+	get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer and --edit' '
+	echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+	get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
+	cat >>expect <<-\EOF &&
+	EDITED: create tag from message file using --trailer
+
+	my-trailer: here
+	alt-trailer: there
+	EOF
+	GIT_EDITOR=./fakeeditor git tag --edit \
+		-F messagefilewithnotrailers \
+		--trailer my-trailer=here \
+		--trailer alt-trailer=there \
+		tag-with-edited-file-message-and-trailers &&
+	get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
+	write_script fakeeditor <<-\EOF &&
+	echo "add a line" >"$1-"
+	cat <"$1" >>"$1-"
+	mv "$1-" "$1"
+	EOF
+	get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
+	cat >>expect <<-\EOF &&
+	add a line
+
+	my-trailer: here
+	alt-trailer: there
+	EOF
+	GIT_EDITOR=./fakeeditor git tag \
+		--trailer my-trailer=here \
+		--trailer alt-trailer=there \
+		tag-with-trailers-and-no-message &&
+	get_tag_msg tag-with-trailers-and-no-message >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'bad editor causes panic when only --trailer is given' '
+	test_must_fail env GIT_EDITOR=false git tag --trailer my-trailer=here tag-will-not-exist
+'
+
 # listing messages for annotated non-signed tags:
 
 test_expect_success \
@@ -810,6 +919,11 @@ test_expect_success 'git tag --format with ahead-behind' '
 	refs/tags/tag-lines 0 1 !
 	refs/tags/tag-one-line 0 1 !
 	refs/tags/tag-right 0 0 !
+	refs/tags/tag-with-edited-file-message-and-trailers 0 1 !
+	refs/tags/tag-with-edited-inline-message-and-trailers 0 1 !
+	refs/tags/tag-with-file-message-and-trailers 0 1 !
+	refs/tags/tag-with-inline-message-and-trailers 0 1 !
+	refs/tags/tag-with-trailers-and-no-message 0 1 !
 	refs/tags/tag-zero-lines 0 1 !
 	EOF
 	git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
diff --git a/trailer.c b/trailer.c
index c72ae687099..843c378199e 100644
--- a/trailer.c
+++ b/trailer.c
@@ -7,6 +7,7 @@
 #include "commit.h"
 #include "trailer.h"
 #include "list.h"
+#include "run-command.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
  */
@@ -1170,3 +1171,14 @@ void trailer_iterator_release(struct trailer_iterator *iter)
 	strbuf_release(&iter->val);
 	strbuf_release(&iter->key);
 }
+
+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
+	struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+	run_trailer.git_cmd = 1;
+	strvec_pushl(&run_trailer.args, "interpret-trailers",
+		     "--in-place", "--no-divider",
+		     path, NULL);
+	strvec_pushv(&run_trailer.args, trailer_args->v);
+	return run_command(&run_trailer);
+}
diff --git a/trailer.h b/trailer.h
index 9f42aa75994..55f85b008ee 100644
--- a/trailer.h
+++ b/trailer.h
@@ -3,6 +3,7 @@
 
 #include "list.h"
 #include "strbuf.h"
+#include "strvec.h"
 
 enum trailer_where {
 	WHERE_DEFAULT,
@@ -158,4 +159,11 @@ int trailer_iterator_advance(struct trailer_iterator *iter);
  */
 void trailer_iterator_release(struct trailer_iterator *iter);
 
+/*
+ * Augment a file to add trailers to it by running git-interpret-trailers.
+ * This calls run_command() and its return value is the same (i.e. 0 for
+ * success, various non-zero for other errors). See run-command.h.
+ */
+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
+
 #endif /* TRAILER_H */

base-commit: e326e520101dcf43a0499c3adc2df7eca30add2d
-- 
gitgitgadget


^ permalink raw reply related	[relevance 1%]

* Re: [PATCH 2/3] refs: do not label special refs as pseudo refs
  2024-04-29 13:41  6% ` [PATCH 2/3] refs: do not label special refs as pseudo refs Patrick Steinhardt
@ 2024-04-29 15:12  0%   ` Phillip Wood
  2024-04-29 22:52  0%   ` Justin Tobler
  1 sibling, 0 replies; 200+ results
From: Phillip Wood @ 2024-04-29 15:12 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: Jeff King, Karthik Nayak

Hi Patrick

On 29/04/2024 14:41, Patrick Steinhardt wrote:
> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index d71b199955..4275918fa0 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -497,20 +497,28 @@ exclude;;
>   	unusual refs.
>   
>   [[def_pseudoref]]pseudoref::
> -	Pseudorefs are a class of files under `$GIT_DIR` which behave
> -	like refs for the purposes of rev-parse, but which are treated
> -	specially by git.  Pseudorefs both have names that are all-caps,
> -	and always start with a line consisting of a
> -	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
> -	pseudoref, because it is sometimes a symbolic ref.  They might
> -	optionally contain some additional data.  `MERGE_HEAD` and
> -	`CHERRY_PICK_HEAD` are examples.  Unlike
> -	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
> -	be symbolic refs, and never have reflogs.  They also cannot be
> -	updated through the normal ref update machinery.  Instead,
> -	they are updated by directly writing to the files.  However,
> -	they can be read as if they were refs, so `git rev-parse
> -	MERGE_HEAD` will work.
> +	Pseudorefs are references that live in the root of the reference
> +	hierarchy, outside of the usual "refs/" hierarchy. Pseudorefs have an
> +	all-uppercase name and must end with a "_HEAD" suffix, for example
> +	"`BISECT_HEAD`". Other than that, pseudorefs behave the exact same as
> +	any other reference and can be both read and written via regular Git
> +	tooling.

This changes the definition to allow pseudorefs to by symbolic refs. 
When is_pseudoref() was introduced Junio and I had a brief discussion 
about this restriction and he was not in favor of allowing pseudorefs to 
be symbolic refs [1].

Are there any practical implications of the changes in this patch for 
users running commands like "git log FETCH_HEAD" (I can't think of any 
off the top of my head but it would be good to have some reassurance on 
that point in the commit message)

Best Wishes

Phillip

[1] https://lore.kernel.org/git/xmqq34u2q3zs.fsf@gitster.g/

> +<<def_special_ref>,Special refs>> are not pseudorefs.
> ++
> +Due to historic reasons, Git has several irregular pseudo refs that do not
> +follow above rules. The following list of irregular pseudo refs is exhaustive
> +and shall not be extended in the future:
> +
> + - "`AUTO_MERGE`"
> +
> + - "`BISECT_EXPECTED_REV`"
> +
> + - "`NOTES_MERGE_PARTIAL`"
> +
> + - "`NOTES_MERGE_REF`"
> +
> + - "`MERGE_AUTOSTASH`"
>   
>   [[def_pull]]pull::
>   	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
> diff --git a/refs.c b/refs.c
> index c64f66bff9..567c6fc6ff 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -905,6 +905,8 @@ int is_pseudoref(struct ref_store *refs, const char *refname)
>   
>   	if (!is_pseudoref_syntax(refname))
>   		return 0;
> +	if (is_special_ref(refname))
> +		return 0;
>   
>   	if (ends_with(refname, "_HEAD")) {
>   		refs_resolve_ref_unsafe(refs, refname,
> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
> index 948f1bb5f4..8c92fbde79 100755
> --- a/t/t6302-for-each-ref-filter.sh
> +++ b/t/t6302-for-each-ref-filter.sh
> @@ -52,6 +52,23 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
>   	test_cmp expect actual
>   '
>   
> +test_expect_success '--include-root-refs pattern does not print special refs' '
> +	test_when_finished "rm -rf repo" &&
> +	git init repo &&
> +	(
> +		cd repo &&
> +		test_commit initial &&
> +		git rev-parse HEAD >.git/MERGE_HEAD &&
> +		git for-each-ref --format="%(refname)" --include-root-refs >actual &&
> +		cat >expect <<-EOF &&
> +		HEAD
> +		$(git symbolic-ref HEAD)
> +		refs/tags/initial
> +		EOF
> +		test_cmp expect actual
> +	)
> +'
> +
>   test_expect_success '--include-root-refs with other patterns' '
>   	cat >expect <<-\EOF &&
>   	HEAD


^ permalink raw reply	[relevance 0%]

* Re: [GIT PULL] l10n updates for 2.45.0
  2024-04-29  7:41  2% [GIT PULL] l10n updates for 2.45.0 Jiang Xin
@ 2024-04-29 14:37  0% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-29 14:37 UTC (permalink / raw)
  To: Jiang Xin
  Cc: Git l10n discussion group, Git List, Alexander Shopov,
	Arkadii Yakovets, Bagas Sanjaya, Dimitriy Ryazantcev, Emir SARI,
	Emir SARI, Jean-Noël Avila, Jordi Mas, Peter Krefting,
	Ralf Thielow, Teng Long, Vũ Tiến Hưng,
	Yi-Jyun Pan

Jiang Xin <worldhello.net@gmail.com> writes:

> Hi Junio,
>
> Please pull the following l10n updates for Git 2.45.0.
>
> The following changes since commit e326e520101dcf43a0499c3adc2df7eca30add2d:
>
>   Merge branch 'rj/add-i-leak-fix' (2024-04-25 10:34:24 -0700)
>
> are available in the Git repository at:
>
>   git@github.com:git-l10n/git-po.git tags/l10n-2.45.0-rnd1
>
> for you to fetch changes up to 2cf631412d8c0213151c38c15e2e7e46fb881bdd:
>
>   Merge branch 'master' of github.com:alshopov/git-po (2024-04-29 14:50:23 +0800)

Thanks, all!  Pulled.


^ permalink raw reply	[relevance 0%]

* [PATCH 2/3] refs: do not label special refs as pseudo refs
  @ 2024-04-29 13:41  6% ` Patrick Steinhardt
  2024-04-29 15:12  0%   ` Phillip Wood
  2024-04-29 22:52  0%   ` Justin Tobler
  2024-04-30 12:26  2% ` [PATCH v2 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
  2024-05-02  8:17  3% ` [PATCH v3 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
  2 siblings, 2 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-29 13:41 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Karthik Nayak

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

We have two refs which almost behave like a ref in many contexts, but
aren't really:

  - MERGE_HEAD contains the list of parents during a merge.

  - FETCH_HEAD contains the list of fetched references after
    git-fetch(1) with some annotations.

These references have been declared "special refs" in 8df4c5d205
(Documentation: add "special refs" to the glossary, 2024-01-19).

Due to their "_HEAD" suffix, those special refs also almost look like a
pseudo ref, even though they aren't. But because `is_pseudoref()` labels
anything as a pseudo ref that ends with the `_HEAD` suffix, it will also
happily label both of the above special refs as pseudo refs.

This mis-labeling creates some weirdness and inconsistent behaviour
across ref backends. As special refs are never stored via a ref backend,
they theoretically speaking cannot know about special refs. But with the
recent introduction of the `--include-root-refs` flag this isn't quite
true anymore: the "files" backend will yield all refs that look like a
pseudo ref or "HEAD" stored in the root directory. And given that both
of the above look like pseudo refs, the "files" backend will list those,
too. The "reftable" backend naturally cannot know about those, and
teaching it to parse and yield these special refs very much feels like
the wrong way to go. So, arguably, the better direction to go is to mark
the "files" behaviour as a bug and stop yielding special refs there.

Conceptually, this feels like the right thing to do, too. Special refs
really aren't refs, they are a different file format that for some part
may behave like a ref. If we were designing these special refs from
scratch, we would have likely never named it anything like a "ref" at
all.

So let's double down on the path that the mentioned commit has started,
which is to cleanly distinguish special refs and pseudo refs.

Ideally, the proper way would be to return to the original meaning that
pseudo refs really had: a ref that behaves like a ref for most of the
part, but isn't really a ref. We would essentially replace the current
"pseudoref" term with the "special ref" term. The consequence is that
all refs except for FETCH_HEAD and MERGE_HEAD would be normal refs,
regardless of whether they live in the root hierarchy or not. The way
that pseudorefs are enforced now would then change to be a naming policy
for refs, only. It's unclear though how sensible it would be to do such
a large change to terminology now, which is why this commit does the
next best thing.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/glossary-content.txt | 36 ++++++++++++++++++------------
 refs.c                             |  2 ++
 t/t6302-for-each-ref-filter.sh     | 17 ++++++++++++++
 3 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index d71b199955..4275918fa0 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -497,20 +497,28 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	Pseudorefs are a class of files under `$GIT_DIR` which behave
-	like refs for the purposes of rev-parse, but which are treated
-	specially by git.  Pseudorefs both have names that are all-caps,
-	and always start with a line consisting of a
-	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
-	pseudoref, because it is sometimes a symbolic ref.  They might
-	optionally contain some additional data.  `MERGE_HEAD` and
-	`CHERRY_PICK_HEAD` are examples.  Unlike
-	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
-	be symbolic refs, and never have reflogs.  They also cannot be
-	updated through the normal ref update machinery.  Instead,
-	they are updated by directly writing to the files.  However,
-	they can be read as if they were refs, so `git rev-parse
-	MERGE_HEAD` will work.
+	Pseudorefs are references that live in the root of the reference
+	hierarchy, outside of the usual "refs/" hierarchy. Pseudorefs have an
+	all-uppercase name and must end with a "_HEAD" suffix, for example
+	"`BISECT_HEAD`". Other than that, pseudorefs behave the exact same as
+	any other reference and can be both read and written via regular Git
+	tooling.
++
+<<def_special_ref>,Special refs>> are not pseudorefs.
++
+Due to historic reasons, Git has several irregular pseudo refs that do not
+follow above rules. The following list of irregular pseudo refs is exhaustive
+and shall not be extended in the future:
+
+ - "`AUTO_MERGE`"
+
+ - "`BISECT_EXPECTED_REV`"
+
+ - "`NOTES_MERGE_PARTIAL`"
+
+ - "`NOTES_MERGE_REF`"
+
+ - "`MERGE_AUTOSTASH`"
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
diff --git a/refs.c b/refs.c
index c64f66bff9..567c6fc6ff 100644
--- a/refs.c
+++ b/refs.c
@@ -905,6 +905,8 @@ int is_pseudoref(struct ref_store *refs, const char *refname)
 
 	if (!is_pseudoref_syntax(refname))
 		return 0;
+	if (is_special_ref(refname))
+		return 0;
 
 	if (ends_with(refname, "_HEAD")) {
 		refs_resolve_ref_unsafe(refs, refname,
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 948f1bb5f4..8c92fbde79 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -52,6 +52,23 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
 	test_cmp expect actual
 '
 
+test_expect_success '--include-root-refs pattern does not print special refs' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		git rev-parse HEAD >.git/MERGE_HEAD &&
+		git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+		cat >expect <<-EOF &&
+		HEAD
+		$(git symbolic-ref HEAD)
+		refs/tags/initial
+		EOF
+		test_cmp expect actual
+	)
+'
+
 test_expect_success '--include-root-refs with other patterns' '
 	cat >expect <<-\EOF &&
 	HEAD
-- 
2.45.0-rc1


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

^ permalink raw reply related	[relevance 6%]

* [GIT PULL] l10n updates for 2.45.0
@ 2024-04-29  7:41  2% Jiang Xin
  2024-04-29 14:37  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Jiang Xin @ 2024-04-29  7:41 UTC (permalink / raw)
  To: Junio C Hamano, Git l10n discussion group
  Cc: Jiang Xin, Git List, Alexander Shopov, Arkadii Yakovets,
	Bagas Sanjaya, Dimitriy Ryazantcev, Emir SARI, Emir SARI,
	Jean-Noël Avila, Jordi Mas, Peter Krefting, Ralf Thielow,
	Teng Long, Vũ Tiến Hưng, Yi-Jyun Pan

Hi Junio,

Please pull the following l10n updates for Git 2.45.0.

The following changes since commit e326e520101dcf43a0499c3adc2df7eca30add2d:

  Merge branch 'rj/add-i-leak-fix' (2024-04-25 10:34:24 -0700)

are available in the Git repository at:

  git@github.com:git-l10n/git-po.git tags/l10n-2.45.0-rnd1

for you to fetch changes up to 2cf631412d8c0213151c38c15e2e7e46fb881bdd:

  Merge branch 'master' of github.com:alshopov/git-po (2024-04-29 14:50:23 +0800)

----------------------------------------------------------------
l10n-2.45.0-rnd1
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEE37vMEzKDqYvVxs51k24VDd1FMtUFAmYvSbkACgkQk24VDd1F
MtUkgg/+ONzez0C+y1+xddvhkBtTYmcLDtB4mXNnt65RSNIXWJRhgDb9KFSfl3ki
9FxaHtFiRyNAfLTuv5EwfV9B+t4Prdh6tuz7dCcz7jy0HEIpwStH5fA+x12GEU2+
C/QexPQQ1tZFPFAlfrexhT4hBKQ/irbZLLb7dl8hp3NtjdCkwMllH4N9lrHGfUKi
w0AKr+9Bj73AOWhQqxeh0KlyVk5y8+DOgHvN/Dp46igN3+rZfhGDq7D7CNQBGncx
iJsiyXTUEdHGqvCgJCB290cz1kmNnt5v2n4Zmr6HsagzRrT6M7KeUrfmO+S+jAy/
gmiF2qyW6+u3yVIeE+WqDIWqbkgrVWiGcfqlj+IxCFYY6ZoQy+5HjnOb8JMtXe0A
C/Y1yn/pGaDXAx4dncKMIdXnXtpzQJM6107nr5NqtQ/U2OqFYM5PPxMYxtWNDlMn
Hxp1nkYPbdd5nOUj+Xh+i8uOpA0D/FG2pBLHXVABjtf409shQ1ykkLHcmnX6Cv6p
gI8J/TIxwJAzKl+CHusDhStWutnsKajAapE1va/YaNyeysD63a6gySOAac+ovgir
+LSVuAA/o/uSv28btHO9aKNO54tKVvzro0zspWyaeT2lzaolZXfi+iHzDlfiWgOj
TG9fjXfMPOrAL2HOYLw8wmJhBdeGemSRdwl5uWimBAeQiMj1lAw=
=0564
-----END PGP SIGNATURE-----

----------------------------------------------------------------
Alexander Shopov (1):
      l10n: bg.po: Updated Bulgarian translation (5652t)

Arkadii Yakovets (1):
      l10n: uk: v2.45 update

Bagas Sanjaya (1):
      l10n: po-id for 2.45

Emir SARI (1):
      l10n: tr: Update Turkish translations

Jean-Noël Avila (1):
      l10n: fr: v2.45.0

Jiang Xin (9):
      Merge branch 'po-id' of github.com:bagasme/git-po
      Merge branch 'l10n-de-2.45' of github.com:ralfth/git
      Merge branch 'l10n/uk/2.45-uk-update'
      l10n: TEAMS: retire l10n teams no update in 1 year
      Merge branch 'update-teams' of https://github.com/Nekosha/git-po
      Merge branch 'tl/zh_CN_2.45.0_rnd' of github.com:dyrone/git
      Merge branch 'l10n/zh-TW/240428' of github.com:l10n-tw/git-po
      Merge branch 'fr_v2.45.0' of github.com:jnavila/git
      Merge branch 'master' of github.com:alshopov/git-po

Peter Krefting (1):
      l10n: sv.po: Update Swedish translation

Ralf Thielow (1):
      l10n: Update German translation

Teng Long (1):
      l10n: zh_CN: for git 2.45 rounds

Vũ Tiến Hưng (2):
      l10n: Update Vietnamese team contact
      l10n: vi: Updated translation for 2.45

Yi-Jyun Pan (1):
      l10n: zh-TW: Git 2.45

 po/TEAMS    |   23 +-
 po/bg.po    |  731 +++--
 po/de.po    |  670 ++--
 po/fr.po    |  681 ++--
 po/id.po    |  812 +++--
 po/sv.po    |  941 +++---
 po/tr.po    |  661 ++--
 po/uk.po    |  663 ++--
 po/vi.po    | 9973 ++++++++++++++++++++++++++++++++---------------------------
 po/zh_CN.po |  798 +++--
 po/zh_TW.po |  910 ++++--
 11 files changed, 9642 insertions(+), 7221 deletions(-)

--
Jiang Xin


^ permalink raw reply	[relevance 2%]

* Re: [PATCH v2 1/1] advice: add --no-advice global option
  2024-04-29  6:40  4%         ` Jeff King
@ 2024-04-29  6:55  0%           ` Dragan Simic
  2024-04-30  0:56  0%           ` James Liu
  1 sibling, 0 replies; 200+ results
From: Dragan Simic @ 2024-04-29  6:55 UTC (permalink / raw)
  To: Jeff King; +Cc: James Liu, git

Hello Jeff,

On 2024-04-29 08:40, Jeff King wrote:
> On Mon, Apr 29, 2024 at 03:01:55PM +1000, James Liu wrote:
> 
>> > >  int advice_enabled(enum advice_type type)
>> > >  {
>> > > -	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
>> > > +	int enabled;
>> > > +
>> > > +	if (getenv(GIT_NO_ADVICE))
>> > > +		return 0;
>> >
>> > Huh, I was under impression that having an environment
>> > variable to control this behavior was frowned upon by
>> > Junio? [1]  To me, supporting such a variable would be
>> > a somewhat acceptable risk, [2] but of course it's the
>> > maintainer's opinion that matters most.
>> >
>> > [1] https://lore.kernel.org/git/xmqqfrva3k9j.fsf@gitster.g/
>> > [2] https://lore.kernel.org/git/462de4ec1fb1896fa7f26b3515deca57@manjaro.org/
>> 
>> You're correct. I saw this pattern for a few of the other global CLI
>> options and followed it. I'm unsure what the best alternative for
>> passing this configuration down to the `advice_enabled()` function is.
>> I'd appreciate some guidance here.
> 
> You need an environment variable if you want the command-line option to
> work consistently across commands that spawn external processes. E.g.:
> 
>   git --no-advice fetch --all
> 
> is going to spawn fetch sub-processes under the hood. You'd want them 
> to
> respect --no-advice, too, so we either have to propagate the
> command-line option or use the environment. And when you consider an
> external script like git-foo that runs a bunch of underlying Git
> commands, then propagating becomes too cumbersome and error-prone.

Well described, thanks.  Though, I'm afraid Junio isn't going
to like this new environment variable, but we'll see.

> You should use git_env_bool() to avoid the confusing behavior that
> GIT_NO_ADVICE=false still turns off advice. ;)
> 
> You can also drop the "NO", which helps avoid awkward double negation.
> For example, if you do:
> 
>   if (git_env_bool("GIT_ADVICE", 1))
> 	return 0;
> 
> then leaving that variable unset will act as if it is set to "1", but
> you can still do GIT_ADVICE=0 to suppress it.
> 
> There are some older variables (e.g., GIT_NO_REPLACE_OBJECTS) that made
> this mistake and we are stuck with, but I think we should avoid it for
> newer ones.

Makes sense to me.


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v2 1/1] advice: add --no-advice global option
  @ 2024-04-29  6:40  4%         ` Jeff King
  2024-04-29  6:55  0%           ` Dragan Simic
  2024-04-30  0:56  0%           ` James Liu
  0 siblings, 2 replies; 200+ results
From: Jeff King @ 2024-04-29  6:40 UTC (permalink / raw)
  To: James Liu; +Cc: Dragan Simic, git

On Mon, Apr 29, 2024 at 03:01:55PM +1000, James Liu wrote:

> > >  int advice_enabled(enum advice_type type)
> > >  {
> > > -	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
> > > +	int enabled;
> > > +
> > > +	if (getenv(GIT_NO_ADVICE))
> > > +		return 0;
> >
> > Huh, I was under impression that having an environment
> > variable to control this behavior was frowned upon by
> > Junio? [1]  To me, supporting such a variable would be
> > a somewhat acceptable risk, [2] but of course it's the
> > maintainer's opinion that matters most.
> >
> > [1] https://lore.kernel.org/git/xmqqfrva3k9j.fsf@gitster.g/
> > [2] 
> > https://lore.kernel.org/git/462de4ec1fb1896fa7f26b3515deca57@manjaro.org/
> 
> You're correct. I saw this pattern for a few of the other global CLI
> options and followed it. I'm unsure what the best alternative for
> passing this configuration down to the `advice_enabled()` function is.
> I'd appreciate some guidance here.

You need an environment variable if you want the command-line option to
work consistently across commands that spawn external processes. E.g.:

  git --no-advice fetch --all

is going to spawn fetch sub-processes under the hood. You'd want them to
respect --no-advice, too, so we either have to propagate the
command-line option or use the environment. And when you consider an
external script like git-foo that runs a bunch of underlying Git
commands, then propagating becomes too cumbersome and error-prone.

You should use git_env_bool() to avoid the confusing behavior that
GIT_NO_ADVICE=false still turns off advice. ;)

You can also drop the "NO", which helps avoid awkward double negation.
For example, if you do:

  if (git_env_bool("GIT_ADVICE", 1))
	return 0;

then leaving that variable unset will act as if it is set to "1", but
you can still do GIT_ADVICE=0 to suppress it.

There are some older variables (e.g., GIT_NO_REPLACE_OBJECTS) that made
this mistake and we are stuck with, but I think we should avoid it for
newer ones.

-Peff


^ permalink raw reply	[relevance 4%]

* [PATCH v3 06/13] remote-curl: fix parsing of detached SHA256 heads
  2024-04-29  6:34  2% ` [PATCH v3 00/13] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
@ 2024-04-29  6:34 10%   ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-29  6:34 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, brian m. carlson, Justin Tobler

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

The dumb HTTP transport tries to read the remote HEAD reference by
downloading the "HEAD" file and then parsing it via `http_fetch_ref()`.
This function will either parse the file as an object ID in case it is
exactly `the_hash_algo->hexsz` long, or otherwise it will check whether
the reference starts with "ref :" and parse it as a symbolic ref.

This is broken when parsing detached HEADs of a remote SHA256 repository
because we never update `the_hash_algo` to the discovered remote object
hash. Consequently, `the_hash_algo` will always be the fallback SHA1
hash algorithm, which will cause us to fail parsing HEAD altogteher when
it contains a SHA256 object ID.

Fix this issue by setting up `the_hash_algo` via `repo_set_hash_algo()`.
While at it, let's make the expected SHA1 fallback explicit in our code,
which also addresses an upcoming issue where we are going to remove the
SHA1 fallback for `the_hash_algo`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c              | 19 ++++++++++++++++++-
 t/t5550-http-fetch-dumb.sh | 15 +++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/remote-curl.c b/remote-curl.c
index 0b6d7815fd..004b707fdf 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -266,12 +266,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 	return list;
 }
 
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
 static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
 {
 	const char *p = memchr(heads->buf, '\t', heads->len);
 	int algo;
+
+	/*
+	 * In case the remote has no refs we have no way to reliably determine
+	 * the object hash used by that repository. In that case we simply fall
+	 * back to SHA1, which may or may not be correct.
+	 */
 	if (!p)
-		return the_hash_algo;
+		return &hash_algos[GIT_HASH_SHA1];
 
 	algo = hash_algo_by_length((p - heads->buf) / 2);
 	if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +306,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
 		    "is this a git repository?",
 		    transport_anonymize_url(url.buf));
 
+	/*
+	 * Set the repository's hash algo to whatever we have just detected.
+	 * This ensures that we can correctly parse the remote references.
+	 */
+	repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
 	data = heads->buf;
 	start = NULL;
 	mid = data;
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 4c3b32785d..5f16cbc58d 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -55,6 +55,21 @@ test_expect_success 'list refs from outside any repository' '
 	test_cmp expect actual
 '
 
+
+test_expect_success 'list detached HEAD from outside any repository' '
+	git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+		"$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+		update-ref --no-deref HEAD refs/heads/main &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+	cat >expect <<-EOF &&
+	$(git rev-parse main)	HEAD
+	$(git rev-parse main)	refs/heads/main
+	EOF
+	nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'create password-protected repository' '
 	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
 	cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-- 
2.45.0-rc1


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

^ permalink raw reply related	[relevance 10%]

* [PATCH v3 00/13] Stop relying on SHA1 fallback for `the_hash_algo`
  2024-04-19  9:51  2% [PATCH 00/11] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
  2024-04-19  9:51 10% ` [PATCH 05/11] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
  2024-04-23  5:07  2% ` [PATCH v2 00/12] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
@ 2024-04-29  6:34  2% ` Patrick Steinhardt
  2024-04-29  6:34 10%   ` [PATCH v3 06/13] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
  2 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-29  6:34 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, brian m. carlson, Justin Tobler

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

Hi,

this is the third version of my patch series that stops relying on the
SHA1 fallback configured for `the_hash_algo`.

There's only a single change compared to v2, which is a new patch that
fixes a segfault in the commit-graph fuzzer.

Thanks!

Patrick

Patrick Steinhardt (13):
  path: harden validation of HEAD with non-standard hashes
  path: move `validate_headref()` to its only user
  parse-options-cb: only abbreviate hashes when hash algo is known
  attr: don't recompute default attribute source
  attr: fix BUG() when parsing attrs outside of repo
  remote-curl: fix parsing of detached SHA256 heads
  builtin/rev-parse: allow shortening to more than 40 hex characters
  builtin/blame: don't access potentially unitialized `the_hash_algo`
  builtin/bundle: abort "verify" early when there is no repository
  builtin/diff: explicitly set hash algo when there is no repo
  builtin/shortlog: don't set up revisions without repo
  oss-fuzz/commit-graph: set up hash algorithm
  repository: stop setting SHA1 as the default object hash

 attr.c                       | 31 +++++++++++++++------
 builtin/blame.c              |  5 ++--
 builtin/bundle.c             |  5 ++++
 builtin/diff.c               |  9 ++++++
 builtin/rev-parse.c          |  5 ++--
 builtin/shortlog.c           |  2 +-
 oss-fuzz/fuzz-commit-graph.c |  1 +
 parse-options-cb.c           |  3 +-
 path.c                       | 53 ------------------------------------
 path.h                       |  1 -
 remote-curl.c                | 19 ++++++++++++-
 repository.c                 |  2 --
 setup.c                      | 53 ++++++++++++++++++++++++++++++++++++
 t/t0003-attributes.sh        | 15 ++++++++++
 t/t0040-parse-options.sh     | 17 ++++++++++++
 t/t1500-rev-parse.sh         |  6 ++++
 t/t5550-http-fetch-dumb.sh   | 15 ++++++++++
 17 files changed, 168 insertions(+), 74 deletions(-)

Range-diff against v2:
 1:  a986b464d3 =  1:  5134f35cda path: harden validation of HEAD with non-standard hashes
 2:  a347c7e6ca =  2:  589b6a99ef path: move `validate_headref()` to its only user
 3:  c0a15b2fa6 =  3:  9a63c445d2 parse-options-cb: only abbreviate hashes when hash algo is known
 4:  1b5f904eed =  4:  929bacbfce attr: don't recompute default attribute source
 5:  26909daca4 =  5:  8f20aec1ee attr: fix BUG() when parsing attrs outside of repo
 6:  0b99184f50 =  6:  53439067a1 remote-curl: fix parsing of detached SHA256 heads
 7:  ccfda3c2d2 =  7:  1f74960760 builtin/rev-parse: allow shortening to more than 40 hex characters
 8:  1813e7eb5c =  8:  2d985abca1 builtin/blame: don't access potentially unitialized `the_hash_algo`
 9:  31182a1fc6 =  9:  f3b23d28aa builtin/bundle: abort "verify" early when there is no repository
10:  78e19d0a1b = 10:  7577b6b96c builtin/diff: explicitly set hash algo when there is no repo
11:  51bcddbc31 = 11:  509c79d1d3 builtin/shortlog: don't set up revisions without repo
 -:  ---------- > 12:  660f976129 oss-fuzz/commit-graph: set up hash algorithm
12:  e8126371e1 = 13:  95909c2da5 repository: stop setting SHA1 as the default object hash
-- 
2.45.0-rc1


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

^ permalink raw reply	[relevance 2%]

* [PATCH] builtin/tag.c: add --trailer arg
@ 2024-04-29  4:31  2% John Passaro via GitGitGadget
  2024-04-29 16:53  1% ` [PATCH v2] " John Passaro via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: John Passaro via GitGitGadget @ 2024-04-29  4:31 UTC (permalink / raw)
  To: git; +Cc: John Passaro, John Passaro

From: John Passaro <john.a.passaro@gmail.com>

Teach git-tag to accept --trailer option to add trailers to annotated
tag messages, like git-commit.

Signed-off-by: John Passaro <john.a.passaro@gmail.com>
---
    builtin/tag.c: add --trailer arg

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1723%2Fjpassaro%2Fjp%2Ftag-trailer-arg-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1723/jpassaro/jp/tag-trailer-arg-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1723

 Documentation/git-tag.txt |  18 ++++++-
 builtin/tag.c             |  59 +++++++++++++++++----
 t/t7004-tag.sh            | 104 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 169 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c31ec..79b0a7e9644 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+	[(--trailer <token>[(=|:)<value>])...]
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@ creates a 'tag' object, and requires a tag message.  Unless
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
 
 Otherwise, a tag reference that points directly at the given object
 (i.e., a lightweight tag) is created.
@@ -178,6 +179,19 @@ This option is only applicable when listing tags without annotation lines.
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
 	is given.
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git tag --trailer "Signed-off-by:T A Ger \
+	<tagger@example.com>" --trailer "Helped-by:C O Mitter \
+	<committer@example.com>"` will add the "Signed-off-by" trailer
+	and the "Helped-by" trailer to the tag message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+	The trailers can be seen in `git tag --list` using
+	`--format="%(trailers)"` placeholder.
+
 -e::
 --edit::
 	The message taken from file with `-F` and command line with
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b45..0334a5d15ec 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
 #include "date.h"
 #include "write-or-die.h"
 #include "object-file-convert.h"
+#include "run-command.h"
 
 static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+	   "        [(--trailer <token>[(=|:)<value>])...]\n"
 	   "        <tagname> [<commit> | <object>]"),
 	N_("git tag -d <tagname>..."),
 	N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -290,10 +292,11 @@ static const char message_advice_nested_tag[] =
 static void create_tag(const struct object_id *object, const char *object_ref,
 		       const char *tag,
 		       struct strbuf *buf, struct create_tag_options *opt,
-		       struct object_id *prev, struct object_id *result, char *path)
+		       struct object_id *prev, struct object_id *result, struct strvec *trailer_args, char *path)
 {
 	enum object_type type;
 	struct strbuf header = STRBUF_INIT;
+	int should_edit;
 
 	type = oid_object_info(the_repository, object, NULL);
 	if (type <= OBJ_NONE)
@@ -313,14 +316,18 @@ static void create_tag(const struct object_id *object, const char *object_ref,
 		    tag,
 		    git_committer_info(IDENT_STRICT));
 
-	if (!opt->message_given || opt->use_editor) {
+	should_edit = opt->use_editor || !opt->message_given;
+	if (should_edit || trailer_args->nr) {
 		int fd;
 
 		/* write the template message before editing: */
 		fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
-		if (opt->message_given) {
+		if (opt->message_given && buf->len) {
 			write_or_die(fd, buf->buf, buf->len);
+			if (trailer_args->nr && buf->buf[buf->len-1] != '\n') {
+				write_or_die(fd, "\n", 1);
+			}
 			strbuf_reset(buf);
 		} else if (!is_null_oid(prev)) {
 			write_tag_body(fd, prev);
@@ -338,10 +345,31 @@ static void create_tag(const struct object_id *object, const char *object_ref,
 		}
 		close(fd);
 
-		if (launch_editor(path, buf, NULL)) {
-			fprintf(stderr,
-			_("Please supply the message using either -m or -F option.\n"));
-			exit(1);
+		if (trailer_args->nr) {
+			struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+			strvec_pushl(&run_trailer.args, "interpret-trailers",
+				     "--in-place", "--no-divider",
+				     path, NULL);
+			strvec_pushv(&run_trailer.args, trailer_args->v);
+			run_trailer.git_cmd = 1;
+			if (run_command(&run_trailer))
+				die(_("unable to pass trailers to --trailers"));
+		}
+
+		if (should_edit) {
+			if (launch_editor(path, buf, NULL)) {
+				fprintf(stderr,
+				_("Please supply the message using either -m or -F option.\n"));
+				exit(1);
+			}
+		} else if (trailer_args->nr) {
+			strbuf_reset(buf);
+			if (strbuf_read_file(buf, path, 0) < 0) {
+				fprintf(stderr,
+						_("Please supply the message using either -m or -F option.\n"));
+				exit(1);
+			}
 		}
 	}
 
@@ -416,6 +444,14 @@ struct msg_arg {
 	struct strbuf buf;
 };
 
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+	BUG_ON_OPT_NEG(unset);
+
+	strvec_pushl(opt->value, "--trailer", arg, NULL);
+	return 0;
+}
+
 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 {
 	struct msg_arg *msg = opt->value;
@@ -463,6 +499,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
+	struct strvec trailer_args = STRVEC_INIT;
 	int icase = 0;
 	int edit_flag = 0;
 	struct option options[] = {
@@ -479,6 +516,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_CALLBACK_F('m', "message", &msg, N_("message"),
 			       N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
 		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
 		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
 		OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +586,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		opt.sign = 1;
 		set_signing_key(keyid);
 	}
-	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+	create_tag_object = (opt.sign || annotate || msg.given || msgfile || edit_flag || trailer_args.nr);
 
 	if ((create_tag_object || force) && (cmdmode != 0))
 		usage_with_options(git_tag_usage, options);
@@ -635,7 +673,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	else if (!force)
 		die(_("tag '%s' already exists"), tag);
 
-	opt.message_given = msg.given || msgfile;
+	opt.message_given = msg.given || (msgfile != NULL);
 	opt.use_editor = edit_flag;
 
 	if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
@@ -653,7 +691,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		if (force_sign_annotate && !annotate)
 			opt.sign = 1;
 		path = git_pathdup("TAG_EDITMSG");
-		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object, &trailer_args,
 			   path);
 	}
 
@@ -686,6 +724,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	strbuf_release(&reflog_msg);
 	strbuf_release(&msg.buf);
 	strbuf_release(&err);
+	strvec_clear(&trailer_args);
 	free(msgfile);
 	return ret;
 }
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 696866d7794..364db2b4685 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -668,6 +668,105 @@ test_expect_success \
 	test_cmp expect actual
 '
 
+# trailers
+
+get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect
+cat >>expect <<EOF
+create tag with trailers
+
+my-trailer: here
+alt-trailer: there
+EOF
+test_expect_success 'create tag with -m and --trailer' '
+	git tag -m "create tag with trailers"  --trailer my-trailer=here --trailer alt-trailer=there tag-with-inline-message-and-trailers &&
+	get_tag_msg tag-with-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'list tag extracting trailers' '
+	cat >expect <<-\EOF &&
+	my-trailer: here
+	alt-trailer: there
+
+	EOF
+	git tag --list --format="%(trailers)" tag-with-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
+get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect
+cat >>expect <<EOF
+create tag from message file using --trailer
+
+my-trailer: here
+alt-trailer: there
+EOF
+test_expect_success 'create tag with -F and --trailer' '
+	git tag -F messagefilewithnotrailers  --trailer my-trailer=here --trailer alt-trailer=there tag-with-file-message-and-trailers &&
+	get_tag_msg tag-with-file-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'set up editor' '
+	write_script fakeeditor <<-\EOF
+	sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+	mv "$1-" "$1"
+	EOF
+'
+
+get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect
+cat >>expect <<EOF
+EDITED: create tag with trailers
+
+my-trailer: here
+alt-trailer: there
+EOF
+test_expect_success 'create tag with -m and --trailer and --edit' '
+	GIT_EDITOR=./fakeeditor git tag --edit -m "create tag with trailers"  --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-inline-message-and-trailers &&
+	get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
+get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect
+cat >>expect <<EOF
+EDITED: create tag from message file using --trailer
+
+my-trailer: here
+alt-trailer: there
+EOF
+test_expect_success 'create tag with -F and --trailer and --edit' '
+	GIT_EDITOR=./fakeeditor git tag --edit -F messagefilewithnotrailers  --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-file-message-and-trailers &&
+	get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'set up editor' '
+	write_script fakeeditor <<-\EOF
+	echo "add a line" >"$1-"
+	echo >>"$1-"
+	cat <"$1" >>"$1-"
+	mv "$1-" "$1"
+	EOF
+'
+
+get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect
+cat >>expect <<EOF
+add a line
+
+my-trailer: here
+alt-trailer: there
+EOF
+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
+	GIT_EDITOR=./fakeeditor git tag --trailer my-trailer=here --trailer alt-trailer=there tag-with-trailers-and-no-message &&
+	get_tag_msg tag-with-trailers-and-no-message >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'bad editor causes panic when only --trailer is given' '
+	test_must_fail env GIT_EDITOR=false git tag --trailer my-trailer=here tag-will-not-exist
+'
+
 # listing messages for annotated non-signed tags:
 
 test_expect_success \
@@ -810,6 +909,11 @@ test_expect_success 'git tag --format with ahead-behind' '
 	refs/tags/tag-lines 0 1 !
 	refs/tags/tag-one-line 0 1 !
 	refs/tags/tag-right 0 0 !
+	refs/tags/tag-with-edited-file-message-and-trailers 0 1 !
+	refs/tags/tag-with-edited-inline-message-and-trailers 0 1 !
+	refs/tags/tag-with-file-message-and-trailers 0 1 !
+	refs/tags/tag-with-inline-message-and-trailers 0 1 !
+	refs/tags/tag-with-trailers-and-no-message 0 1 !
 	refs/tags/tag-zero-lines 0 1 !
 	EOF
 	git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&

base-commit: e326e520101dcf43a0499c3adc2df7eca30add2d
-- 
gitgitgadget


^ permalink raw reply related	[relevance 2%]

* [PATCH 00/13] builtin: implement, document and test url-parse
@ 2024-04-28 22:30  3% Matheus Moreira via GitGitGadget
  2024-04-29 20:53  3% ` Torsten Bögershausen
  0 siblings, 1 reply; 200+ results
From: Matheus Moreira via GitGitGadget @ 2024-04-28 22:30 UTC (permalink / raw)
  To: git; +Cc: Matheus Moreira

Git commands accept a wide variety of URLs syntaxes, not just standard URLs.
This can make parsing git URLs difficult since standard URL parsers cannot
be used. Even if an external parser were implemented, it would have to track
git's development closely in case support for any new URL schemes are added.

These patches introduce a new url-parse builtin command that exposes git's
native URL parsing algorithms as a plumbing command, allowing other programs
to then call upon git itself to parse the git URLs and their components.

This should be quite useful for scripts. For example, a script might want to
add remotes to repositories, naming them according to the domain name where
the repository is hosted. This new builtin allows it to parse the git URL
and extract its host name which can then be used as input for other
operations. This would be difficult to implement otherwise due to git's
support for scp style URLs.

Signed-off-by: Matheus Afonso Martins Moreira matheus@matheusmoreira.com

Matheus Afonso Martins Moreira (13):
  url: move helper function to URL header and source
  urlmatch: define url_parse function
  builtin: create url-parse command
  url-parse: add URL parsing helper function
  url-parse: enumerate possible URL components
  url-parse: define component extraction helper fn
  url-parse: define string to component converter fn
  url-parse: define usage and options
  url-parse: parse options given on the command line
  url-parse: validate all given git URLs
  url-parse: output URL components selected by user
  Documentation: describe the url-parse builtin
  tests: add tests for the new url-parse builtin

 .gitignore                      |   1 +
 Documentation/git-url-parse.txt |  59 ++++++++++
 Makefile                        |   1 +
 builtin.h                       |   1 +
 builtin/url-parse.c             | 132 ++++++++++++++++++++++
 command-list.txt                |   1 +
 connect.c                       |   8 --
 connect.h                       |   1 -
 git.c                           |   1 +
 remote.c                        |   1 +
 t/t9904-url-parse.sh            | 194 ++++++++++++++++++++++++++++++++
 url.c                           |   8 ++
 url.h                           |   2 +
 urlmatch.c                      |  90 +++++++++++++++
 urlmatch.h                      |   1 +
 15 files changed, 492 insertions(+), 9 deletions(-)
 create mode 100644 Documentation/git-url-parse.txt
 create mode 100644 builtin/url-parse.c
 create mode 100755 t/t9904-url-parse.sh


base-commit: e326e520101dcf43a0499c3adc2df7eca30add2d
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1715%2Fmatheusmoreira%2Furl-parse-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1715/matheusmoreira/url-parse-v1
Pull-Request: https://github.com/git/git/pull/1715
-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: Use of Git with local folders
  @ 2024-04-27 16:08  3%     ` Beat Bolli
  0 siblings, 0 replies; 200+ results
From: Beat Bolli @ 2024-04-27 16:08 UTC (permalink / raw)
  To: Felipe Bustamante, git; +Cc: 'Karthik Nayak'

Hi Felipe

[please don't top-post]

On 26.04.2024 17:56, Felipe Bustamante wrote:
> Hi Karthik,
> 
> Thanks for the reply.
> 
> What you indicate allows me to create a relationship between a new
> branch and a new empty local directory, but what I need is to relate
> a branch with an existing local directory since the latter contains
> the changes made to the source code and I need to generate change
> control.
> 
> I am going to clarify the working conditions to explain why we have
> worked in this way.
> 
> The computer for development is in a completely isolated work
> environment, without connection to any data network, all data ports
> are disabled (network, USB, Bluetooth, etc.) so there is no
> possibility of copying the code. source towards a more open
> environment.
> 
> This computer only has Visual Studio 2022 installed, no other type of
> software has been installed, which is why it is impossible to create
> branches from the master since VS 2022 does not allow branches
> connected to local directories.
> 
> The only way that could be used to isolate daily changes was to
> create a directory each day, by means of a copy from Windows File
> Explorer, containing the changes made during the last and previous
> days.
> 
> The structure of the project would be as follows:
> 
> 1. CS_2024-04-10 directory, this is the original directory containing
> the original source code. This directory was converted to a GIT
> repository using VS 2022 which allows us to have the master branch.
> Because of the above, this directory contains the hidden .git
> directory. 2. The next day, a copy of the original directory was
> made, and this copy was named CS_2024-04-11. Because of this, this
> directory contains the hidden .git directory of the original. We
> worked on this copy by opening the project in VS 2022 and making
> various changes and then saving the results.
> 
> 3. The previous process has been carried out for two weeks, so there
> are 10 source code directories which include the changes made during
> each workday plus the changes from the respective previous day, for
> example, the directory CS_2024-04- 16 contains all the changes made
> from day 10 to day 15 plus the changes made during day 16.
> 
> Now, what we need is to know if it is possible to convert all these
> sequential directories into a GIT structure with change control such
> as the first directory with the master branch and then branches that
> can be associated with each subsequent directory.
> 
> I have installed, to support the idea, the GIT program. For this
> reason, now the development computer only contains two software for
> development, Visual Studio 2022, and GIT.
> 
> How would it be possible to create branches from the master branch,
> using the original directory CS_2024-04-10 and associate these
> branches with the sequential directories created?
> 
> Thank you very much for the help.
> 
> Felipe Bustamante Sverige
> 
> -----Original Message----- From: Karthik Nayak
> <karthik.188@gmail.com> Sent: den 26 april 2024 12:16 To: Felipe
> Bustamante <fisadmaster@gmail.com>; git@vger.kernel.org Subject: Re:
> Use of Git with local folders
> 
> Hello Felipe,
> 
> "Felipe Bustamante" <fisadmaster@gmail.com> writes:
>> Hi,
>> 
>> I would like to know if it is possible to combine the contents of
>> several directories with copies of the same source code but with
>> different changes?
>> 
>> The stage is: 1. There is a directory with the original source
>> code, without changes. 2. There are several directories, ordered by
>> the date of creation, which are a copy of the original source code,
>> copies made every day after generating changes, that is, the
>> original source code resides in the DIR1 directory, a copy is made
>> with name dir2, and changes are made to the source code of the
>> project. The next day, a copy of the directory of name DIR2 is made
>> and renamed DIR3, we work with this directory making changes to the
>> source code. The same process is carried out for two weeks.
>> 
>> The important question would be, is it possible to combine these
>> directories in a repository with a Master branch (the first
>> original directory (DIR1) and transform the other directory into
>> branches of the master?
>> 
>> It would be useful, if copies were made that were made on an
>> original directory (dir1) that was already becoming a git
>> repository, with the use of Visual Studio 2022?
>> 
>> As an observation, the work is done on an isolated computer,
>> without internet connection or to backup devices, the computer is
>> fully isolated and it is necessary to generate the version control,
>> in the case that it is possible.
>> 
>> Any orientation in this regard would be very useful.

What would work in my opinion is this, if I understand you correctly:

1. In the Git repo of day 1, remove all files except for the .git 
folder. This is to make sure that any files that are no longer present 
on the next day are really no longer in the repo.

2. Copy the content of day 2 except the .git folder over into day 1. git 
status, git diff etc should show the differences between days 1 and 2.

3. Create a new branch for this day, and commit all changes.

4. Continue from step 1 for each remaining day.

This will result in one repository that has one commit per day. Going 
forward, you'd probably want to commit more often so that your commits 
comprise a meaningful unit of work instead of arbitrary day boundaries.

It does not matter that your system is air-gapped. Git works perfectly 
locally without ever fetching or pushing.

By the way, I don't think it's necessary to create a new branch each 
day; you can inspect the history just as well by only committing.


HTH

-- 
Cheers, Beat



^ permalink raw reply	[relevance 3%]

* [PATCH v4 4/7] update-ref: add support for 'symref-delete' command
  2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
  2024-04-26 15:24  7%       ` [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
@ 2024-04-26 15:24 11%       ` Karthik Nayak
  2024-05-01 20:22  1%       ` [PATCH v5 0/7] refs: add support for transactional symref updates Karthik Nayak
  2 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-26 15:24 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

Add a new command 'symref-delete' to allow deletions of symbolic refs in
a transaction via the '--stdin' mode of the 'git-update-ref' command.
The 'symref-delete' command can, when given an <old-target>, delete the
provided <ref> only when it points to <old-target>. This will only work
when used with the 'no-deref' mode as it doesn't make sense to deref a
symref during deletion.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-update-ref.txt | 11 ++++--
 builtin/fetch.c                  |  2 +-
 builtin/receive-pack.c           |  3 +-
 builtin/update-ref.c             | 33 ++++++++++++++++-
 refs.c                           | 12 ++++---
 refs.h                           |  4 ++-
 refs/files-backend.c             |  2 +-
 refs/reftable-backend.c          |  2 +-
 t/t1400-update-ref.sh            | 61 +++++++++++++++++++++++++++++++-
 9 files changed, 117 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 9fe78b3501..2924b9437e 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -65,6 +65,7 @@ performs all modifications together.  Specify commands of the form:
 	create SP <ref> SP <new-oid> LF
 	delete SP <ref> [SP <old-oid>] LF
 	verify SP <ref> [SP <old-oid>] LF
+	symref-delete SP <ref> [SP <old-target>] LF
 	symref-verify SP <ref> [SP <old-target>] LF
 	option SP <opt> LF
 	start LF
@@ -87,6 +88,7 @@ quoting:
 	create SP <ref> NUL <new-oid> NUL
 	delete SP <ref> NUL [<old-oid>] NUL
 	verify SP <ref> NUL [<old-oid>] NUL
+	symref-delete SP <ref> [NUL <old-target>] NUL
 	symref-verify SP <ref> [NUL <old-target>] NUL
 	option SP <opt> NUL
 	start NUL
@@ -112,13 +114,18 @@ create::
 	exist.  The given <new-oid> may not be zero.
 
 delete::
-	Delete <ref> after verifying it exists with <old-oid>, if
-	given.  If given, <old-oid> may not be zero.
+	Delete <ref> after verifying it exists with <old-oid>, if given.
+	If given, <old-oid> may not be zero.  If instead, ref:<old-target>
+	is provided, verify that the symbolic ref <ref> targets
+	<old-target> before deleting it.
 
 verify::
 	Verify <ref> against <old-oid> but do not change it.  If
 	<old-oid> is zero or missing, the ref must not exist.
 
+symref-delete::
+	Delete <ref> after verifying it exists with <old-target>, if given.
+
 symref-verify::
 	Verify symbolic <ref> against <old-target> but do not change it.
 	If <old-target> is missing, the ref must not exist.  Can only be
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 66840b7c5b..d02592efca 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1383,7 +1383,7 @@ static int prune_refs(struct display_state *display_state,
 		if (transaction) {
 			for (ref = stale_refs; ref; ref = ref->next) {
 				result = ref_transaction_delete(transaction, ref->name, NULL, 0,
-								"fetch: prune", &err);
+								NULL, "fetch: prune", &err);
 				if (result)
 					goto cleanup;
 			}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b150ef39a8..9a4667d57d 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_delete(transaction,
 					   namespaced_name,
 					   old_oid,
-					   0, "push", &err)) {
+					   0, NULL,
+					   "push", &err)) {
 			rp_error("%s", err.buf);
 			ret = "failed to delete";
 		} else {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 419b28169b..8fef3aed0a 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -293,7 +293,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 
 	if (ref_transaction_delete(transaction, refname,
 				   have_old ? &old_oid : NULL,
-				   update_flags, msg, &err))
+				   update_flags, NULL, msg, &err))
 		die("%s", err.buf);
 
 	update_flags = default_flags;
@@ -301,6 +301,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 	strbuf_release(&err);
 }
 
+
+static void parse_cmd_symref_delete(struct ref_transaction *transaction,
+				    const char *next, const char *end)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname, *old_target;
+
+	if (!(update_flags & REF_NO_DEREF))
+		die("symref-delete: cannot operate with deref mode");
+
+	refname = parse_refname(&next);
+	if (!refname)
+		die("symref-delete: missing <ref>");
+
+	old_target = parse_next_refname(&next);
+
+	if (*next != line_termination)
+		die("symref-delete %s: extra input: %s", refname, next);
+
+	if (ref_transaction_delete(transaction, refname, NULL,
+				   update_flags, old_target, msg, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	free(old_target);
+	strbuf_release(&err);
+}
+
+
 static void parse_cmd_verify(struct ref_transaction *transaction,
 			     const char *next, const char *end)
 {
@@ -443,6 +473,7 @@ static const struct parse_cmd {
 	{ "create",        parse_cmd_create,        2, UPDATE_REFS_OPEN },
 	{ "delete",        parse_cmd_delete,        2, UPDATE_REFS_OPEN },
 	{ "verify",        parse_cmd_verify,        2, UPDATE_REFS_OPEN },
+	{ "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
 	{ "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
 	{ "option",        parse_cmd_option,        1, UPDATE_REFS_OPEN },
 	{ "start",         parse_cmd_start,         0, UPDATE_REFS_STARTED },
diff --git a/refs.c b/refs.c
index 0e1013b5ab..6b7c46bfd8 100644
--- a/refs.c
+++ b/refs.c
@@ -979,7 +979,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
 	transaction = ref_store_transaction_begin(refs, &err);
 	if (!transaction ||
 	    ref_transaction_delete(transaction, refname, old_oid,
-				   flags, msg, &err) ||
+				   flags, NULL, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ref_transaction_free(transaction);
@@ -1318,14 +1318,18 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_target,
+			   const char *msg,
 			   struct strbuf *err)
 {
 	if (old_oid && is_null_oid(old_oid))
 		BUG("delete called with old_oid set to zeros");
+	if (old_target && !(flags & REF_NO_DEREF))
+		BUG("delete cannot operate on symrefs with deref mode");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      NULL, NULL, flags,
+				      NULL, old_target, flags,
 				      msg, err);
 }
 
@@ -2752,7 +2756,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
 
 	for_each_string_list_item(item, refnames) {
 		ret = ref_transaction_delete(transaction, item->string,
-					     NULL, flags, msg, &err);
+					     NULL, flags, NULL, msg, &err);
 		if (ret) {
 			warning(_("could not delete reference %s: %s"),
 				item->string, err.buf);
diff --git a/refs.h b/refs.h
index 27b9aeaf54..4be4930f04 100644
--- a/refs.h
+++ b/refs.h
@@ -766,7 +766,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_target,
+			   const char *msg,
 			   struct strbuf *err);
 
 /*
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 53197fa3af..fc5037fe5a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2516,7 +2516,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
 	files_assert_main_repository(refs, "lock_ref_for_update");
 
-	if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
+	if ((update->flags & REF_HAVE_NEW) && ref_update_is_null_new_value(update))
 		update->flags |= REF_DELETING;
 
 	if (head_ref) {
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index a2474245aa..2b2cbca8c0 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1120,7 +1120,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
 		if (u->flags & REF_LOG_ONLY)
 			continue;
 
-		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+		if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
 			struct reftable_ref_record ref = {
 				.refname = (char *)u->refname,
 				.update_index = ts,
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 34b29eeac8..8efddac013 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1689,7 +1689,7 @@ do
 		test_cmp before after
 	'
 
-	test_expect_success "stdin ${type} symref-verify no value is treated as zero value" '
+	test_expect_success "stdin ${type} symref-verify fails with no value" '
 		git symbolic-ref refs/heads/symref >expect &&
 		create_stdin_buf ${type} "symref-verify refs/heads/symref" "" &&
 		test_must_fail git update-ref --stdin ${type} --no-deref <stdin
@@ -1728,6 +1728,65 @@ do
 		test_cmp expect actual
 	'
 
+	test_expect_success "stdin ${type} symref-delete fails without --no-deref" '
+		git symbolic-ref refs/heads/symref $a &&
+		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
+		test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
+		grep "fatal: symref-delete: cannot operate with deref mode" err
+	'
+
+	test_expect_success "stdin ${type} symref-delete fails with no ref" '
+		create_stdin_buf ${type} "symref-delete " &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		grep "fatal: symref-delete: missing <ref>" err
+	'
+
+	test_expect_success "stdin ${type} symref-delete fails with too many arguments" '
+		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" "$a" &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		if test "$type" = "-z"
+		then
+			grep "fatal: unknown command: $a" err
+		else
+			grep "fatal: symref-delete refs/heads/symref: extra input:  $a" err
+		fi
+	'
+
+	test_expect_success "stdin ${type} symref-delete fails with wrong old value" '
+		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$m" &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		if test_have_prereq REFTABLE
+		then
+			grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err
+		else
+			grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}" err
+		fi &&
+		git symbolic-ref refs/heads/symref >expect &&
+		echo $a >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "stdin ${type} symref-delete works with right old value" '
+		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
+		git update-ref --stdin ${type} --no-deref <stdin &&
+		test_must_fail git rev-parse --verify -q refs/heads/symref
+	'
+
+	test_expect_success "stdin ${type} symref-delete works with empty old value" '
+		git symbolic-ref refs/heads/symref $a &&
+		create_stdin_buf ${type} "symref-delete refs/heads/symref" "" &&
+		git update-ref --stdin ${type} --no-deref <stdin &&
+		test_must_fail git rev-parse --verify -q $b
+	'
+
+	test_expect_success "stdin ${type} symref-delete succeeds for dangling reference" '
+		test_must_fail git symbolic-ref refs/heads/nonexistent &&
+		git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
+		create_stdin_buf ${type} "symref-delete refs/heads/symref2" "refs/heads/nonexistent" &&
+		git update-ref --stdin ${type} --no-deref <stdin &&
+		test_must_fail git symbolic-ref -d refs/heads/symref2
+	'
+
 done
 
 test_done
-- 
2.43.GIT



^ permalink raw reply related	[relevance 11%]

* [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin'
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
                       ` (2 preceding siblings ...)
  2024-04-23 22:03  2%     ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Jeff King
@ 2024-04-26 15:24  1%     ` Karthik Nayak
  2024-04-26 15:24  7%       ` [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
                         ` (2 more replies)
  3 siblings, 3 replies; 200+ results
From: Karthik Nayak @ 2024-04-26 15:24 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The 'git-update-ref(1)' command allows transactional reference updates.
But currently only supports regular reference updates. Meaning, if one
wants to update HEAD (symbolic ref) in a transaction, there is no tool
to do so.

One option to obtain transactional updates for the HEAD ref is to
manually create the HEAD.lock file and commit. This is intrusive, where
the user needs to mimic internal git behavior. Also, this only works
when using the files backend.

At GitLab, we've been using the manual process till date, to allow users
to set and change their default branch. But with the introduction of
reftables as a reference backend, this becomes a necessity to be solved
within git.

This patch series goes about introducing a set of commands
symref-{create,verify,delete,update} to work with symrefs complimenting
the existing commands for the regular refs in the '--stdin' mode of
'git-update-ref'.

The 'symref-verify' command can be used to verify if a symref exists and
its existing value.

The 'symref-create' command can be used to create a new symref.

The 'symref-delete' command can be used to delete an existing symref while
optionally checking its existing value.

The 'symref-update' command can be used to update a symref, create a symref,
delete a symref or even convert an existing regular ref to a symref. Wherein
like the regular 'update' command, the zero OID can be used to create/delete
a symref.

While this series adds the commands and the required ground work, it only
is accessile within the '--stdin' mode of 'git-update-ref'. However, it makes
it easy to extend it further to the command line too, which will be present
in a follow up series.

Previous versions:
V1: https://lore.kernel.org/git/20240330224623.579457-1-knayak@gitlab.com/
V2: https://lore.kernel.org/git/20240412095908.1134387-1-knayak@gitlab.com/
V3: https://lore.kernel.org/git/20240423212818.574123-1-knayak@gitlab.com/

V3 took a different approach of incorporating changes into the existing commands, 
of 'git-update-ref --stdin' but we realized that there was some ambiguity in how
these commands are parsed [1]. In that sense it makes more sense to compare this
version with v2 instead.

Changes over v2 are:

- Rename (old|new)_ref to (old|new)_target, to avoid confusion.
- Better assertions around the input data.
- Removing the REF_SYMREF_UPDATE flag.
- Filled in missing/new documentation.
- For symref-update, realized that there was some ambiguity on how the
  old_oid and old_target was parsed, and now the command requires the
  user to explicitly input the data type.
- Support dangling refs in all operations.
- More test cases around empty values.
- Removed unecessary header includes.
- Fixed whitespace issues with the previous series.
- Other review comments.


[1]: https://lore.kernel.org/git/20240423220308.GC1172807@coredump.intra.peff.net/

Range diff (against v2):

1:  3269d0e91e ! 1:  4a56e3ede4 refs: accept symref values in `ref_transaction[_add]_update`
    @@ Commit message
         flags to create a `ref_update` and add it to the transaction at hand.
     
         To extend symref support in transactions, we need to also accept the
    -    old and new ref values and process it. In this commit, let's add the
    -    required paramaters to the function and modify all call sites.
    +    old and new ref targets and process it. In this commit, let's add the
    +    required parameters to the function and modify all call sites.
     
    -    The two paramaters added are `new_ref` and `old_ref`. The `new_ref` is
    -    used to denote what the reference should point to when the transaction
    -    is applied. Some functions allow this parameter to be NULL, meaning that
    -    the reference is not changed, or `""`, meaning that the reference should
    -    be deleted.
    +    The two parameters added are `new_target` and `old_target`. The
    +    `new_target` is used to denote what the reference should point to when
    +    the transaction is applied.
     
    -    The `old_ref` denotes the value of that the reference must have before
    -    the update. Some functions allow this parameter to be NULL, meaning that
    -    the old value of the reference is not checked, or `""`, meaning that the
    -    reference must not exist before the update. A copy of this value is made
    -    in the transaction.
    +    The `old_target` denotes the value the reference must have before the
    +    update. Some functions allow this parameter to be NULL, meaning that the
    +    old value of the reference is not checked.
     
         The handling logic of these parameters will be added in consequent
    -    commits as we implement symref-{create, update, delete, verify}.
    +    commits as we add symref commands to the '--stdin' mode of
    +    'git-update-ref'.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs.c: struct ref_update *ref_transaction_add_update(
      		const char *refname, unsigned int flags,
      		const struct object_id *new_oid,
      		const struct object_id *old_oid,
    -+		const char *new_ref, const char *old_ref,
    ++		const char *new_target, const char *old_target,
      		const char *msg)
      {
      	struct ref_update *update;
    +@@ refs.c: struct ref_update *ref_transaction_add_update(
    + 	if (transaction->state != REF_TRANSACTION_OPEN)
    + 		BUG("update called for transaction that is not open");
    + 
    ++	if (old_oid && !is_null_oid(old_oid) && old_target)
    ++		BUG("Only one of old_oid and old_target should be non NULL");
    ++	if (new_oid && !is_null_oid(new_oid) && new_target)
    ++		BUG("Only one of new_oid and new_target should be non NULL");
    ++
    + 	FLEX_ALLOC_STR(update, refname, refname);
    + 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
    + 	transaction->updates[transaction->nr++] = update;
     @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
      			   const struct object_id *old_oid,
    -+			   const char *new_ref, const char *old_ref,
    ++			   const char *new_target,
    ++			   const char *old_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err)
      {
    @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      
      	ref_transaction_add_update(transaction, refname, flags,
     -				   new_oid, old_oid, msg);
    -+				   new_oid, old_oid, new_ref, old_ref, msg);
    ++				   new_oid, old_oid, new_target,
    ++				   old_target, msg);
      	return 0;
      }
      
    @@ refs.c: int refs_update_ref(struct ref_store *refs, const char *msg,
     
      ## refs.h ##
     @@ refs.h: struct ref_transaction *ref_transaction_begin(struct strbuf *err);
    -  */
    - #define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
    - 
    -+/*
    -+ * The reference update is considered to be done on a symbolic reference. This
    -+ * ensures that we verify, delete, create and update the ref correspondingly.
    -+ */
    -+#define REF_SYMREF_UPDATE (1 << 12)
    -+
    - /*
    -  * Bitmask of all of the flags that are allowed to be passed in to
    -  * ref_transaction_update() and friends:
    -  */
    - #define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS                                  \
    - 	(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
    --	 REF_SKIP_REFNAME_VERIFICATION)
    -+	 REF_SKIP_REFNAME_VERIFICATION | REF_SYMREF_UPDATE )
    - 
    - /*
    -  * Add a reference update to transaction. `new_oid` is the value that
    +  *         before the update. A copy of this value is made in the
    +  *         transaction.
    +  *
    ++ *     new_target -- the target reference that the reference will be
    ++ *         update to point to. This takes precedence over new_oid when
    ++ *         set. If the reference is a regular reference, it will be
    ++ *         converted to a symbolic reference.
    ++ *
    ++ *     old_target -- the reference that the reference must be pointing to.
    ++ *         Will only be taken into account when the reference is a symbolic
    ++ *         reference.
    ++ *
    +  *     flags -- flags affecting the update, passed to
    +  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
    +  *         REF_FORCE_CREATE_REFLOG. See those constants for more
    +@@ refs.h: struct ref_transaction *ref_transaction_begin(struct strbuf *err);
    +  * beforehand. The old value is checked after the lock is taken to
    +  * prevent races. If the old value doesn't agree with old_oid, the
    +  * whole transaction fails. If old_oid is NULL, then the previous
    +- * value is not checked.
    ++ * value is not checked. If `old_target` is not NULL, treat the reference
    ++ * as a symbolic ref and validate that its target before the update is
    ++ * `old_target`. If the `new_target` is not NULL, then the reference
    ++ * will be updated to a symbolic ref which targets `new_target`.
    ++ * Together, these allow us to update between regular refs and symrefs.
    +  *
    +  * See the above comment "Reference transaction updates" for more
    +  * information.
     @@ refs.h: int ref_transaction_update(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
      			   const struct object_id *old_oid,
    -+			   const char *new_ref, const char *old_ref,
    ++			   const char *new_target,
    ++			   const char *old_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err);
      
    @@ refs/refs-internal.h: struct ref_update {
      	struct object_id old_oid;
      
     +	/*
    -+	 * If (flags & REF_SYMREF_UPDATE), set the reference to this
    -+	 * value (or delete it, if `new_ref` is an empty string).
    ++	 * If set, point the reference to this value. This can also be
    ++	 * used to convert regular references to become symbolic refs.
     +	 */
    -+	const char *new_ref;
    ++	const char *new_target;
     +
     +	/*
    -+	 * If (type & REF_SYMREF_UPDATE), check that the reference
    -+	 * previously had this value (or didn't previously exist,
    -+	 * if `old_ref` is an empty string).
    ++	 * If set and the reference is a symbolic ref, check that the
    ++	 * reference previously pointed to this value.
     +	 */
    -+	const char *old_ref;
    ++	const char *old_target;
     +
      	/*
      	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
    @@ refs/refs-internal.h: struct ref_update *ref_transaction_add_update(
      		const char *refname, unsigned int flags,
      		const struct object_id *new_oid,
      		const struct object_id *old_oid,
    -+		const char *new_ref, const char *old_ref,
    ++		const char *new_target, const char *old_target,
      		const char *msg);
      
      /*
4:  53fdb408ef = 2:  496bf14f28 files-backend: extract out `create_symref_lock`
2:  a8cb0e0a1d ! 3:  6337859cbb update-ref: add support for symref-verify
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    update-ref: add support for symref-verify
    +    update-ref: add support for 'symref-verify' command
     
    -    In the previous commit, we added the required base for adding symref
    -    support in transactions provided by the 'git-update-ref(1)'. This commit
    -    introduces the 'symref-verify' command which is similar to the existing
    -    'verify' command for regular refs.
    +    In the previous commits, we added the required base for adding symref
    +    commands to the '--stdin' mode provided by 'git-update-ref(1)'. Using
    +    them, add a new 'symref-verify' command to verify symrefs.
     
         The 'symref-verify' command allows users to verify if a provided <ref>
    -    contains the provided <old-ref> without changing the <ref>. If <old-ref>
    -    is not provided, the command will verify that the <ref> doesn't exist.
    -    Since we're checking for symbolic refs, this command will only work with
    -    the 'no-deref' mode. This is because any dereferenced symbolic ref will
    -    point to an object and not a ref and the regular 'verify' command can be
    -    used in such situations.
    +    contains the provided <old-target> without changing the <ref>. If
    +    <old-target> is not provided, the command will verify that the <ref>
    +    doesn't exist. Since we're checking for symbolic refs, this command will
    +    only work with the 'no-deref' mode. This is because any dereferenced
    +    symbolic ref will point to an object and not a ref and the regular
    +    'verify' command can be used in such situations.
     
    -    This commit adds all required helper functions required to also
    -    introduce the other symref commands, namely create, delete, and update.
    +    Add and use `ref_update_is_null_new_value`, a helper function which is
    +    used to check if there is a new_value in a reference update. The new
    +    value could either be a symref target `new_target` or a OID `new_oid`.
         We also add tests to test the command in both the regular stdin mode and
         also with the '-z' flag.
     
    -    When the user doesn't provide a <old-ref> we need to check that the
    -    provided <ref> doesn't exist. And to do this, we take over the existing
    -    understanding that <old-oid> when set to its zero value, it refers to
    -    the ref not existing. While this seems like a mix of contexts between
    -    using <*-ref> and <*-oid>, this actually works really well, especially
    -    considering the fact that we want to eventually also introduce
    -
    -        symref-update SP <ref> SP <new-ref> [SP (<old-oid> | <old-rev>)] LF
    -
    -    and here, we'd allow the user to update a regular <ref> to a symref and
    -    use <old-oid> to check the <ref>'s oid. This can be extrapolated to the
    -    user using this to create a symref when provided a zero <old-oid>. Which
    -    will work given how we're setting it up.
    -
         We also disable the reference-transaction hook for symref-updates which
         will be tackled in its own commit.
     
    -    Add required tests for 'symref-verify' while also adding reflog checks for
    -    the pre-existing 'verify' tests.
    +    Add required tests for symref support in 'verify' while also adding
    +    reflog checks for the pre-existing 'verify' tests.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ Documentation/git-update-ref.txt: performs all modifications together.  Specify
      	create SP <ref> SP <new-oid> LF
      	delete SP <ref> [SP <old-oid>] LF
      	verify SP <ref> [SP <old-oid>] LF
    -+	symref-verify SP <ref> [SP <old-ref>] LF
    ++	symref-verify SP <ref> [SP <old-target>] LF
      	option SP <opt> LF
      	start LF
      	prepare LF
    @@ Documentation/git-update-ref.txt: quoting:
      	create SP <ref> NUL <new-oid> NUL
      	delete SP <ref> NUL [<old-oid>] NUL
      	verify SP <ref> NUL [<old-oid>] NUL
    -+	symref-verify SP <ref> [NUL <old-ref>] NUL
    ++	symref-verify SP <ref> [NUL <old-target>] NUL
      	option SP <opt> NUL
      	start NUL
      	prepare NUL
    @@ Documentation/git-update-ref.txt: verify::
      	<old-oid> is zero or missing, the ref must not exist.
      
     +symref-verify::
    -+	Verify symbolic <ref> against <old-ref> but do not change it.
    -+	If <old-ref> is missing, the ref must not exist.  Can only be
    ++	Verify symbolic <ref> against <old-target> but do not change it.
    ++	If <old-target> is missing, the ref must not exist.  Can only be
     +	used in `no-deref` mode.
     +
      option::
    @@ builtin/update-ref.c: static char *parse_refname(const char **next)
      	return strbuf_detach(&ref, NULL);
      }
      
    -+
    -+
     +/*
     + * Wrapper around parse_refname which skips the next delimiter.
     + */
     +static char *parse_next_refname(const char **next)
     +{
    -+        if (line_termination) {
    -+                /* Without -z, consume SP and use next argument */
    -+                if (!**next || **next == line_termination)
    -+                        return NULL;
    -+                if (**next != ' ')
    -+                        die("expected SP but got: %s", *next);
    -+        } else {
    -+                /* With -z, read the next NUL-terminated line */
    -+                if (**next)
    -+                        return NULL;
    -+        }
    -+        /* Skip the delimiter */
    -+        (*next)++;
    ++	if (line_termination) {
    ++		/* Without -z, consume SP and use next argument */
    ++		if (!**next || **next == line_termination)
    ++			return NULL;
    ++		if (**next != ' ')
    ++			die("expected SP but got: %s", *next);
    ++	} else {
    ++		/* With -z, read the next NUL-terminated line */
    ++		if (**next)
    ++			return NULL;
    ++	}
    ++	/* Skip the delimiter */
    ++	(*next)++;
     +
    -+        return parse_refname(next);
    ++	return parse_refname(next);
     +}
    ++
     +
      /*
       * The value being parsed is <old-oid> (as opposed to <new-oid>; the
    @@ builtin/update-ref.c: static void parse_cmd_verify(struct ref_transaction *trans
     +}
     +
     +static void parse_cmd_symref_verify(struct ref_transaction *transaction,
    -+                                    const char *next, const char *end)
    ++				    const char *next, const char *end)
     +{
     +	struct strbuf err = STRBUF_INIT;
     +	struct object_id old_oid;
    -+	char *refname, *old_ref;
    ++	char *refname, *old_target;
     +
     +	if (!(update_flags & REF_NO_DEREF))
     +		die("symref-verify: cannot operate with deref mode");
    @@ builtin/update-ref.c: static void parse_cmd_verify(struct ref_transaction *trans
     +	 * old_ref is optional, but we want to differentiate between
     +	 * a NULL and zero value.
     +	 */
    -+	old_ref = parse_next_refname(&next);
    -+	if (!old_ref)
    ++	old_target = parse_next_refname(&next);
    ++	if (!old_target)
     +		old_oid = *null_oid();
    -+	else if (read_ref(old_ref, NULL))
    -+		die("symref-verify %s: invalid <old-ref>", refname);
     +
     +	if (*next != line_termination)
     +		die("symref-verify %s: extra input: %s", refname, next);
     +
    -+	if (ref_transaction_verify(transaction, refname, old_ref ? NULL : &old_oid,
    -+				   old_ref, update_flags | REF_SYMREF_UPDATE, &err))
    ++	if (ref_transaction_verify(transaction, refname,
    ++				   old_target ? NULL : &old_oid,
    ++				   old_target, update_flags, &err))
      		die("%s", err.buf);
      
      	update_flags = default_flags;
      	free(refname);
    -+	free(old_ref);
    ++	free(old_target);
      	strbuf_release(&err);
      }
      
    @@ builtin/update-ref.c: static const struct parse_cmd {
      static void update_refs_stdin(void)
     
      ## refs.c ##
    -@@
    - #include "object-store-ll.h"
    - #include "object.h"
    - #include "path.h"
    -+#include "string.h"
    - #include "tag.h"
    - #include "submodule.h"
    - #include "worktree.h"
    -@@
    - #include "date.h"
    - #include "commit.h"
    - #include "wildmatch.h"
    -+#include "wrapper.h"
    - 
    - /*
    -  * List of all available backends
     @@ refs.c: void ref_transaction_free(struct ref_transaction *transaction)
      
      	for (i = 0; i < transaction->nr; i++) {
      		free(transaction->updates[i]->msg);
    -+		free((void *)transaction->updates[i]->old_ref);
    ++		free((void *)transaction->updates[i]->old_target);
    ++		free((void *)transaction->updates[i]->new_target);
      		free(transaction->updates[i]);
      	}
      	free(transaction->updates);
    @@ refs.c: struct ref_update *ref_transaction_add_update(
      	update->flags = flags;
      
     -	if (flags & REF_HAVE_NEW)
    --		oidcpy(&update->new_oid, new_oid);
    ++	if (new_target)
    ++		update->new_target = xstrdup(new_target);
    ++	if (old_target)
    ++		update->old_target = xstrdup(old_target);
    ++	if (new_oid && flags & REF_HAVE_NEW)
    + 		oidcpy(&update->new_oid, new_oid);
     -	if (flags & REF_HAVE_OLD)
    --		oidcpy(&update->old_oid, old_oid);
    -+	/*
    -+	 * The ref values are to be considered over the oid values when we're
    -+	 * doing symref operations.
    -+	 */
    -+	if (update->flags & REF_SYMREF_UPDATE) {
    -+		if (old_ref)
    -+			update->old_ref = xstrdup(old_ref);
    -+	} else {
    -+		if (flags & REF_HAVE_NEW)
    -+			oidcpy(&update->new_oid, new_oid);
    -+		if (flags & REF_HAVE_OLD)
    -+			oidcpy(&update->old_oid, old_oid);
    -+	}
    ++	if (old_oid && flags & REF_HAVE_OLD)
    + 		oidcpy(&update->old_oid, old_oid);
      	update->msg = normalize_reflog_message(msg);
      	return update;
    - }
     @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      	flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
      
      	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
    -+	flags |= (new_ref ? REF_HAVE_NEW : 0) | (old_ref ? REF_HAVE_OLD : 0);
    ++	flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
      
      	ref_transaction_add_update(transaction, refname, flags,
    - 				   new_oid, old_oid, new_ref, old_ref, msg);
    + 				   new_oid, old_oid, new_target,
     @@ refs.c: int ref_transaction_delete(struct ref_transaction *transaction,
      int ref_transaction_verify(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *old_oid,
    -+			   const char *old_ref,
    ++			   const char *old_target,
      			   unsigned int flags,
      			   struct strbuf *err)
      {
     -	if (!old_oid)
    -+	if (flags & REF_SYMREF_UPDATE && !old_ref && !old_oid)
    -+		BUG("verify called with old_ref set to NULL");
    -+	if (!(flags & REF_SYMREF_UPDATE) && !old_oid)
    - 		BUG("verify called with old_oid set to NULL");
    +-		BUG("verify called with old_oid set to NULL");
    ++	if (!old_target && !old_oid)
    ++		BUG("verify called with old_oid and old_target set to NULL");
    ++	if (old_target && !(flags & REF_NO_DEREF))
    ++		BUG("verify cannot operate on symrefs with deref mode");
      	return ref_transaction_update(transaction, refname,
      				      NULL, old_oid,
     -				      NULL, NULL,
    -+				      NULL, old_ref,
    ++				      NULL, old_target,
      				      flags, NULL, err);
      }
      
    @@ refs.c: static int run_transaction_hook(struct ref_transaction *transaction,
      	for (i = 0; i < transaction->nr; i++) {
      		struct ref_update *update = transaction->updates[i];
      
    -+		if (update->flags & REF_SYMREF_UPDATE)
    ++		/*
    ++		 * Skip reference transaction for symbolic refs.
    ++		 */
    ++		if (update->new_target || update->old_target)
     +			continue;
     +
      		strbuf_reset(&buf);
    @@ refs.c: int copy_existing_ref(const char *oldref, const char *newref, const char
      	return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
      }
     +
    -+int null_new_value(struct ref_update *update) {
    -+	if (update->flags & REF_SYMREF_UPDATE && update->new_ref)
    -+		return 0;
    -+	return is_null_oid(&update->new_oid);
    ++int ref_update_is_null_new_value(struct ref_update *update) {
    ++	return !update->new_target && is_null_oid(&update->new_oid);
     +}
     
      ## refs.h ##
    @@ refs.h: int ref_transaction_delete(struct ref_transaction *transaction,
      int ref_transaction_verify(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *old_oid,
    -+			   const char *old_ref,
    ++			   const char *old_target,
      			   unsigned int flags,
      			   struct strbuf *err);
      
    @@ refs/files-backend.c: static const char *original_update_refname(struct ref_upda
      }
      
     +/*
    -+ * Check whether the REF_HAVE_OLD and old_ref values stored in update
    -+ * are consistent with ref, which is the symbolic reference's current
    -+ * value. If everything is OK, return 0; otherwise, write an error
    -+ * message to err and return -1.
    ++ * Check whether the REF_HAVE_OLD and old_target values stored in
    ++ * update are consistent with ref, which is the symbolic reference's
    ++ * current value. If everything is OK, return 0; otherwise, write an
    ++ * error message to err and return -1.
     + */
    -+static int check_old_ref(struct ref_update *update, char *ref,
    -+			 struct strbuf *err)
    ++static int check_old_target(struct ref_update *update, char *ref,
    ++			    struct strbuf *err)
     +{
     +	if (!(update->flags & REF_HAVE_OLD) ||
    -+	    !strcmp(update->old_ref, ref))
    ++	    !strcmp(update->old_target, ref))
     +		return 0;
     +
    -+	if (!strcmp(update->old_ref, ""))
    ++	if (!strcmp(update->old_target, ""))
     +		strbuf_addf(err, "cannot lock ref '%s': "
     +			    "reference already exists",
     +			    original_update_refname(update));
    @@ refs/files-backend.c: static const char *original_update_refname(struct ref_upda
     +		strbuf_addf(err, "cannot lock ref '%s': "
     +			    "reference is missing but expected %s",
     +			    original_update_refname(update),
    -+			    update->old_ref);
    ++			    update->old_target);
     +	else
     +		strbuf_addf(err, "cannot lock ref '%s': "
     +			    "is at %s but expected %s",
     +			    original_update_refname(update),
    -+			    ref, update->old_ref);
    ++			    ref, update->old_target);
     +
     +	return -1;
     +}
    @@ refs/files-backend.c: static const char *original_update_refname(struct ref_upda
      /*
       * Check whether the REF_HAVE_OLD and old_oid values stored in update
       * are consistent with oid, which is the reference's current value. If
    -@@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *refs,
    - 			       struct strbuf *err)
    - {
    - 	struct strbuf referent = STRBUF_INIT;
    --	int mustexist = (update->flags & REF_HAVE_OLD) &&
    --		!is_null_oid(&update->old_oid);
    -+	int mustexist = (update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid);
    - 	int ret = 0;
    - 	struct ref_lock *lock;
    - 
     @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *refs,
      					ret = TRANSACTION_GENERIC_ERROR;
      					goto out;
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
     +			}
     +
     +			/*
    -+			 * For symref verification, we need to check the referent value
    ++			 * For symref verification, we need to check the reference value
     +			 * rather than the oid. If we're dealing with regular refs or we're
     +			 * verifying a dereferenced symref, we then check the oid.
     +			 */
    -+			if (update->flags & REF_SYMREF_UPDATE && update->old_ref) {
    -+				if (check_old_ref(update, referent.buf, err)) {
    ++			if (update->old_target) {
    ++				if (check_old_target(update, referent.buf, err)) {
     +					ret = TRANSACTION_GENERIC_ERROR;
     +					goto out;
     +				}
    @@ refs/refs-internal.h: void base_ref_store_init(struct ref_store *refs, struct re
     + * takes into consideration that the update could be a regular
     + * ref or a symbolic ref.
     + */
    -+int null_new_value(struct ref_update *update);
    ++int ref_update_is_null_new_value(struct ref_update *update);
     +
      #endif /* REFS_REFS_INTERNAL_H */
     
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
      		 * backend returns, which keeps our tests happy.
      		 */
     -		if (u->flags & REF_HAVE_OLD && !oideq(&current_oid, &u->old_oid)) {
    -+		if ((u->flags & REF_HAVE_OLD) &&
    -+		    (u->flags & REF_SYMREF_UPDATE) &&
    -+		    u->old_ref) {
    -+			if   (strcmp(referent.buf, u->old_ref)) {
    -+				if (!strcmp(u->old_ref, ""))
    -+					strbuf_addf(err, "cannot lock ref '%s': "
    -+						    "reference already exists",
    ++		if ((u->flags & REF_HAVE_OLD) && u->old_target) {
    ++			if (strcmp(referent.buf, u->old_target)) {
    ++				if (!strcmp(u->old_target, ""))
    ++					strbuf_addf(err, "verifying symref target: '%s': "
    ++						    "provided target is empty",
     +						    original_update_refname(u));
     +				else if (!strcmp(referent.buf, ""))
    -+					strbuf_addf(err, "cannot lock ref '%s': "
    ++					strbuf_addf(err, "verifying symref target: '%s': "
     +						    "reference is missing but expected %s",
     +						    original_update_refname(u),
    -+						    u->old_ref);
    ++						    u->old_target);
     +				else
    -+					strbuf_addf(err, "cannot lock ref '%s': "
    ++					strbuf_addf(err, "verifying symref target: '%s': "
     +						    "is at %s but expected %s",
     +						    original_update_refname(u),
    -+						    referent.buf, u->old_ref);
    ++						    referent.buf, u->old_target);
     +				ret = -1;
     +				goto done;
     +			}
    @@ t/t1400-update-ref.sh: test_expect_success PIPE 'transaction flushes status upda
      	test_cmp expected actual
      '
      
    -+create_stdin_buf ()
    -+{
    ++create_stdin_buf () {
     +	if test "$1" = "-z"
     +	then
     +		shift
    @@ t/t1400-update-ref.sh: test_expect_success PIPE 'transaction flushes status upda
     +for type in "" "-z"
     +do
     +
    -+test_expect_success "stdin ${type} symref-verify fails without --no-deref" '
    -+	git symbolic-ref refs/heads/symref $a &&
    -+	create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" &&
    -+	test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    -+	grep "fatal: symref-verify: cannot operate with deref mode" err
    -+'
    ++	test_expect_success "stdin ${type} symref-verify fails without --no-deref" '
    ++		git symbolic-ref refs/heads/symref $a &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" &&
    ++		test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    ++		grep "fatal: symref-verify: cannot operate with deref mode" err
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-verify fails with too many arguments" '
    -+	create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" "$a" &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err  &&
    -+	if test "$type" = "-z"
    -+	then
    -+		grep "fatal: unknown command: $a" err
    -+	else
    -+		grep "fatal: symref-verify refs/heads/symref: extra input:  $a" err
    -+	fi
    -+'
    ++	test_expect_success "stdin ${type} symref-verify fails with too many arguments" '
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" "$a" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err  &&
    ++		if test "$type" = "-z"
    ++		then
    ++			grep "fatal: unknown command: $a" err
    ++		else
    ++			grep "fatal: symref-verify refs/heads/symref: extra input:  $a" err
    ++		fi
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-verify succeeds for correct value" '
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
    -+	create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" &&
    -+	git update-ref --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/heads/symref >actual &&
    -+	test_cmp expect actual &&
    -+	test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
    -+	test_cmp before after
    -+'
    ++	test_expect_success "stdin ${type} symref-verify succeeds for correct value" '
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >actual &&
    ++		test_cmp expect actual &&
    ++		test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
    ++		test_cmp before after
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-verify succeeds for missing reference" '
    -+	test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
    -+	create_stdin_buf ${type} "symref-verify refs/heads/missing" &&
    -+	git update-ref --stdin ${type} --no-deref <stdin &&
    -+	test_must_fail git rev-parse --verify -q refs/heads/missing &&
    -+	test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
    -+	test_cmp before after
    -+'
    ++	test_expect_success "stdin ${type} symref-verify no value is treated as zero value" '
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin
    ++	'
    ++
    ++	test_expect_success "stdin ${type} symref-verify succeeds for dangling reference" '
    ++		test_when_finished "git symbolic-ref -d refs/heads/symref2" &&
    ++		test_must_fail git symbolic-ref refs/heads/nonexistent &&
    ++		git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref2" "refs/heads/nonexistent" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin
    ++	'
    ++
    ++	test_expect_success "stdin ${type} symref-verify succeeds for missing reference" '
    ++		test-tool ref-store main for-each-reflog-ent refs/heads/symref >before &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/missing" "$Z" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		test_must_fail git rev-parse --verify -q refs/heads/missing &&
    ++		test-tool ref-store main for-each-reflog-ent refs/heads/symref >after &&
    ++		test_cmp before after
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-verify fails for wrong value" '
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	create_stdin_buf ${type} "symref-verify refs/heads/symref" "$b" &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/heads/symref >actual &&
    -+	test_cmp expect actual
    -+'
    ++	test_expect_success "stdin ${type} symref-verify fails for wrong value" '
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "$b" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >actual &&
    ++		test_cmp expect actual
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-verify fails for mistaken null value" '
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	create_stdin_buf ${type} "symref-verify refs/heads/symref" &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/heads/symref >actual &&
    -+	test_cmp expect actual
    -+'
    ++	test_expect_success "stdin ${type} symref-verify fails for mistaken null value" '
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		create_stdin_buf ${type} "symref-verify refs/heads/symref" "$Z" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >actual &&
    ++		test_cmp expect actual
    ++	'
     +
     +done
     +
3:  37c3e006da ! 4:  e611cb5a8c update-ref: add support for symref-delete
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    update-ref: add support for symref-delete
    +    update-ref: add support for 'symref-delete' command
     
    -    Similar to the previous commit, add 'symref-delete' to allow deletions
    -    of symbolic refs in a transaction via the 'git-update-ref' command. The
    -    'symref-delete' command can when given with an <old-ref>, deletes the
    -    provided <ref> only when it points to <old-ref>.
    +    Add a new command 'symref-delete' to allow deletions of symbolic refs in
    +    a transaction via the '--stdin' mode of the 'git-update-ref' command.
    +    The 'symref-delete' command can, when given an <old-target>, delete the
    +    provided <ref> only when it points to <old-target>. This will only work
    +    when used with the 'no-deref' mode as it doesn't make sense to deref a
    +    symref during deletion.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ Documentation/git-update-ref.txt: performs all modifications together.  Specify
      	create SP <ref> SP <new-oid> LF
      	delete SP <ref> [SP <old-oid>] LF
      	verify SP <ref> [SP <old-oid>] LF
    -+	symref-delete SP <ref> [SP <old-ref>] LF
    - 	symref-verify SP <ref> [SP <old-ref>] LF
    ++	symref-delete SP <ref> [SP <old-target>] LF
    + 	symref-verify SP <ref> [SP <old-target>] LF
      	option SP <opt> LF
      	start LF
     @@ Documentation/git-update-ref.txt: quoting:
      	create SP <ref> NUL <new-oid> NUL
      	delete SP <ref> NUL [<old-oid>] NUL
      	verify SP <ref> NUL [<old-oid>] NUL
    -+	symref-delete SP <ref> [NUL <old-ref>] NUL
    - 	symref-verify SP <ref> [NUL <old-ref>] NUL
    ++	symref-delete SP <ref> [NUL <old-target>] NUL
    + 	symref-verify SP <ref> [NUL <old-target>] NUL
      	option SP <opt> NUL
      	start NUL
    -@@ Documentation/git-update-ref.txt: verify::
    +@@ Documentation/git-update-ref.txt: create::
    + 	exist.  The given <new-oid> may not be zero.
    + 
    + delete::
    +-	Delete <ref> after verifying it exists with <old-oid>, if
    +-	given.  If given, <old-oid> may not be zero.
    ++	Delete <ref> after verifying it exists with <old-oid>, if given.
    ++	If given, <old-oid> may not be zero.  If instead, ref:<old-target>
    ++	is provided, verify that the symbolic ref <ref> targets
    ++	<old-target> before deleting it.
    + 
    + verify::
      	Verify <ref> against <old-oid> but do not change it.  If
      	<old-oid> is zero or missing, the ref must not exist.
      
     +symref-delete::
    -+	Delete <ref> after verifying it exists with <old-ref>, if
    -+	given.
    ++	Delete <ref> after verifying it exists with <old-target>, if given.
     +
      symref-verify::
    - 	Verify symbolic <ref> against <old-ref> but do not change it.
    - 	If <old-ref> is missing, the ref must not exist.  Can only be
    + 	Verify symbolic <ref> against <old-target> but do not change it.
    + 	If <old-target> is missing, the ref must not exist.  Can only be
     
      ## builtin/fetch.c ##
     @@ builtin/fetch.c: static int prune_refs(struct display_state *display_state,
    @@ builtin/update-ref.c: static void parse_cmd_delete(struct ref_transaction *trans
      	strbuf_release(&err);
      }
      
    ++
     +static void parse_cmd_symref_delete(struct ref_transaction *transaction,
     +				    const char *next, const char *end)
     +{
     +	struct strbuf err = STRBUF_INIT;
    -+	char *refname, *old_ref;
    ++	char *refname, *old_target;
     +
     +	if (!(update_flags & REF_NO_DEREF))
    -+                die("symref-delete: cannot operate with deref mode");
    ++		die("symref-delete: cannot operate with deref mode");
     +
     +	refname = parse_refname(&next);
     +	if (!refname)
     +		die("symref-delete: missing <ref>");
     +
    -+        old_ref = parse_next_refname(&next);
    -+	if (old_ref && read_ref(old_ref, NULL))
    -+		die("symref-delete %s: invalid <old-ref>", refname);
    ++	old_target = parse_next_refname(&next);
     +
     +	if (*next != line_termination)
     +		die("symref-delete %s: extra input: %s", refname, next);
     +
     +	if (ref_transaction_delete(transaction, refname, NULL,
    -+				   update_flags | REF_SYMREF_UPDATE,
    -+				   old_ref, msg, &err))
    ++				   update_flags, old_target, msg, &err))
     +		die("%s", err.buf);
     +
     +	update_flags = default_flags;
     +	free(refname);
    -+	free(old_ref);
    ++	free(old_target);
     +	strbuf_release(&err);
     +}
    ++
     +
      static void parse_cmd_verify(struct ref_transaction *transaction,
      			     const char *next, const char *end)
    @@ refs.c: int refs_delete_ref(struct ref_store *refs, const char *msg,
      	    ref_transaction_commit(transaction, &err)) {
      		error("%s", err.buf);
      		ref_transaction_free(transaction);
    -@@ refs.c: void ref_transaction_free(struct ref_transaction *transaction)
    - 	for (i = 0; i < transaction->nr; i++) {
    - 		free(transaction->updates[i]->msg);
    - 		free((void *)transaction->updates[i]->old_ref);
    -+		free((void *)transaction->updates[i]->new_ref);
    - 		free(transaction->updates[i]);
    - 	}
    - 	free(transaction->updates);
    -@@ refs.c: struct ref_update *ref_transaction_add_update(
    - 	if (update->flags & REF_SYMREF_UPDATE) {
    - 		if (old_ref)
    - 			update->old_ref = xstrdup(old_ref);
    -+		if (new_ref)
    -+			update->new_ref = xstrdup(new_ref);
    - 	} else {
    - 		if (flags & REF_HAVE_NEW)
    - 			oidcpy(&update->new_oid, new_oid);
     @@ refs.c: int ref_transaction_create(struct ref_transaction *transaction,
      int ref_transaction_delete(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *old_oid,
     -			   unsigned int flags, const char *msg,
     +			   unsigned int flags,
    -+			   const char *old_ref,
    ++			   const char *old_target,
     +			   const char *msg,
      			   struct strbuf *err)
      {
    --	if (old_oid && is_null_oid(old_oid))
    -+	if (!(flags & REF_SYMREF_UPDATE) && old_oid &&
    -+	    is_null_oid(old_oid))
    + 	if (old_oid && is_null_oid(old_oid))
      		BUG("delete called with old_oid set to zeros");
    ++	if (old_target && !(flags & REF_NO_DEREF))
    ++		BUG("delete cannot operate on symrefs with deref mode");
      	return ref_transaction_update(transaction, refname,
      				      null_oid(), old_oid,
     -				      NULL, NULL, flags,
    -+				      NULL, old_ref, flags,
    ++				      NULL, old_target, flags,
      				      msg, err);
      }
      
    @@ refs.c: int refs_delete_refs(struct ref_store *refs, const char *logmsg,
      	for_each_string_list_item(item, refnames) {
      		ret = ref_transaction_delete(transaction, item->string,
     -					     NULL, flags, msg, &err);
    -+					     NULL, flags, 0, msg, &err);
    ++					     NULL, flags, NULL, msg, &err);
      		if (ret) {
      			warning(_("could not delete reference %s: %s"),
      				item->string, err.buf);
    @@ refs.h: int ref_transaction_create(struct ref_transaction *transaction,
      			   const struct object_id *old_oid,
     -			   unsigned int flags, const char *msg,
     +			   unsigned int flags,
    -+			   const char *old_ref,
    ++			   const char *old_target,
     +			   const char *msg,
      			   struct strbuf *err);
      
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
      	files_assert_main_repository(refs, "lock_ref_for_update");
      
     -	if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
    -+	if ((update->flags & REF_HAVE_NEW) && null_new_value(update))
    ++	if ((update->flags & REF_HAVE_NEW) && ref_update_is_null_new_value(update))
      		update->flags |= REF_DELETING;
      
      	if (head_ref) {
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
      			continue;
      
     -		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
    -+		if (u->flags & REF_HAVE_NEW && null_new_value(u)) {
    ++		if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
      			struct reftable_ref_record ref = {
      				.refname = (char *)u->refname,
      				.update_index = ts,
     
      ## t/t1400-update-ref.sh ##
    -@@ t/t1400-update-ref.sh: test_expect_success "stdin ${type} symref-verify fails for mistaken null value"
    - 	test_cmp expect actual
    - '
    +@@ t/t1400-update-ref.sh: do
    + 		test_cmp before after
    + 	'
      
    -+test_expect_success "stdin ${type} symref-delete fails without --no-deref" '
    -+	git symbolic-ref refs/heads/symref $a &&
    -+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
    -+	test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    -+	grep "fatal: symref-delete: cannot operate with deref mode" err
    -+'
    +-	test_expect_success "stdin ${type} symref-verify no value is treated as zero value" '
    ++	test_expect_success "stdin ${type} symref-verify fails with no value" '
    + 		git symbolic-ref refs/heads/symref >expect &&
    + 		create_stdin_buf ${type} "symref-verify refs/heads/symref" "" &&
    + 		test_must_fail git update-ref --stdin ${type} --no-deref <stdin
    +@@ t/t1400-update-ref.sh: do
    + 		test_cmp expect actual
    + 	'
    + 
    ++	test_expect_success "stdin ${type} symref-delete fails without --no-deref" '
    ++		git symbolic-ref refs/heads/symref $a &&
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
    ++		test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    ++		grep "fatal: symref-delete: cannot operate with deref mode" err
    ++	'
    ++
    ++	test_expect_success "stdin ${type} symref-delete fails with no ref" '
    ++		create_stdin_buf ${type} "symref-delete " &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    ++		grep "fatal: symref-delete: missing <ref>" err
    ++	'
    ++
    ++	test_expect_success "stdin ${type} symref-delete fails with too many arguments" '
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" "$a" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    ++		if test "$type" = "-z"
    ++		then
    ++			grep "fatal: unknown command: $a" err
    ++		else
    ++			grep "fatal: symref-delete refs/heads/symref: extra input:  $a" err
    ++		fi
    ++	'
     +
    -+test_expect_success "stdin ${type} fails symref-delete with no ref" '
    -+	create_stdin_buf ${type} "symref-delete " &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	grep "fatal: symref-delete: missing <ref>" err
    -+'
    ++	test_expect_success "stdin ${type} symref-delete fails with wrong old value" '
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$m" &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    ++		if test_have_prereq REFTABLE
    ++		then
    ++			grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err
    ++		else
    ++			grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}" err
    ++		fi &&
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		echo $a >actual &&
    ++		test_cmp expect actual
    ++	'
     +
    -+test_expect_success "stdin ${type} fails symref-delete with too many arguments" '
    -+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" "$a" &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	if test "$type" = "-z"
    -+	then
    -+		grep "fatal: unknown command: $a" err
    -+	else
    -+		grep "fatal: symref-delete refs/heads/symref: extra input:  $a" err
    -+	fi
    -+'
    ++	test_expect_success "stdin ${type} symref-delete works with right old value" '
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		test_must_fail git rev-parse --verify -q refs/heads/symref
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-delete ref fails with wrong old value" '
    -+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$m" &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	grep "fatal: cannot lock ref '"'"'refs/heads/symref'"'"'" err &&
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	echo $a >actual &&
    -+	test_cmp expect actual
    -+'
    ++	test_expect_success "stdin ${type} symref-delete works with empty old value" '
    ++		git symbolic-ref refs/heads/symref $a &&
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref" "" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		test_must_fail git rev-parse --verify -q $b
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-delete ref works with right old value" '
    -+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
    -+	git update-ref --stdin ${type} --no-deref <stdin &&
    -+	test_must_fail git rev-parse --verify -q $b
    -+'
    ++	test_expect_success "stdin ${type} symref-delete succeeds for dangling reference" '
    ++		test_must_fail git symbolic-ref refs/heads/nonexistent &&
    ++		git symbolic-ref refs/heads/symref2 refs/heads/nonexistent &&
    ++		create_stdin_buf ${type} "symref-delete refs/heads/symref2" "refs/heads/nonexistent" &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		test_must_fail git symbolic-ref -d refs/heads/symref2
    ++	'
     +
      done
      
5:  8fa0151f94 ! 5:  37f8be2f3f update-ref: add support for symref-create
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    update-ref: add support for symref-create
    +    update-ref: add support for 'symref-create' command
     
    -    Add 'symref-create' to allow creation of symbolic refs in a transaction
    -    via the 'git-update-ref' command. The 'symref-create' command takes in a
    -    <new-ref>, which the created <ref> will point to.
    +    Add 'symref-create' command to the '--stdin' mode 'git-update-ref' to
    +    allow creation of symbolic refs in a transaction. The 'symref-create'
    +    command takes in a <new-target>, which the created <ref> will point to.
     
    -    We also support the 'core.prefersymlinkrefs', wherein if the flag is set
    -    and the filesystem supports symlinks, we create the symbolic ref as a
    -    symlink.
    +    Also, support the 'core.prefersymlinkrefs' config, wherein if the config
    +    is set and the filesystem supports symlinks, we create the symbolic ref
    +    as a symlink. We fallback to creating a regular symref if creating the
    +    symlink is unsuccessful.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ Documentation/git-update-ref.txt: performs all modifications together.  Specify
      	create SP <ref> SP <new-oid> LF
      	delete SP <ref> [SP <old-oid>] LF
      	verify SP <ref> [SP <old-oid>] LF
    -+	symref-create SP <ref> SP <new-ref> LF
    - 	symref-delete SP <ref> [SP <old-ref>] LF
    - 	symref-verify SP <ref> [SP <old-ref>] LF
    ++	symref-create SP <ref> SP <new-target> LF
    + 	symref-delete SP <ref> [SP <old-target>] LF
    + 	symref-verify SP <ref> [SP <old-target>] LF
      	option SP <opt> LF
     @@ Documentation/git-update-ref.txt: quoting:
      	create SP <ref> NUL <new-oid> NUL
      	delete SP <ref> NUL [<old-oid>] NUL
      	verify SP <ref> NUL [<old-oid>] NUL
    -+	symref-create SP <ref> NUL <new-ref> NUL
    - 	symref-delete SP <ref> [NUL <old-ref>] NUL
    - 	symref-verify SP <ref> [NUL <old-ref>] NUL
    ++	symref-create SP <ref> NUL <new-target> NUL
    + 	symref-delete SP <ref> [NUL <old-target>] NUL
    + 	symref-verify SP <ref> [NUL <old-target>] NUL
      	option SP <opt> NUL
    +@@ Documentation/git-update-ref.txt: update::
    + 
    + create::
    + 	Create <ref> with <new-oid> after verifying it does not
    +-	exist.  The given <new-oid> may not be zero.
    ++	exist.  The given <new-oid> may not be zero.  If instead
    ++	ref:<new-target> is provided, a symbolic ref is created
    ++	which targets <new-target>.
    + 
    + delete::
    + 	Delete <ref> after verifying it exists with <old-oid>, if given.
     @@ Documentation/git-update-ref.txt: verify::
      	Verify <ref> against <old-oid> but do not change it.  If
      	<old-oid> is zero or missing, the ref must not exist.
      
     +symref-create::
    -+	Create symbolic ref <ref> with <new-ref> after verifying
    ++	Create symbolic ref <ref> with <new-target> after verifying
     +	it does not exist.  Can only be used in `no-deref` mode.
     +
      symref-delete::
    - 	Delete <ref> after verifying it exists with <old-ref>, if
    - 	given.
    + 	Delete <ref> after verifying it exists with <old-target>, if given.
    + 
     
      ## builtin/clone.c ##
     @@ builtin/clone.c: static void write_remote_refs(const struct ref *local_refs)
    @@ builtin/update-ref.c: static void parse_cmd_create(struct ref_transaction *trans
      	strbuf_release(&err);
      }
      
    ++
     +static void parse_cmd_symref_create(struct ref_transaction *transaction,
     +				    const char *next, const char *end)
     +{
     +	struct strbuf err = STRBUF_INIT;
    -+	char *refname, *new_ref;
    ++	char *refname, *new_target;
     +
     +	if (!(update_flags & REF_NO_DEREF))
    -+                die("symref-create: cannot operate with deref mode");
    ++		die("symref-create: cannot operate with deref mode");
     +
     +	refname = parse_refname(&next);
     +	if (!refname)
     +		die("symref-create: missing <ref>");
     +
    -+	new_ref = parse_next_refname(&next);
    -+	if (!new_ref)
    -+		die("symref-create %s: missing <new-ref>", refname);
    -+	if (read_ref(new_ref, NULL))
    -+		die("symref-create %s: invalid <new-ref>", refname);
    ++	new_target = parse_next_refname(&next);
    ++	if (!new_target)
    ++		die("symref-create %s: missing <new-target>", refname);
     +
     +	if (*next != line_termination)
     +		die("symref-create %s: extra input: %s", refname, next);
     +
    -+	if (ref_transaction_create(transaction, refname, NULL, new_ref,
    -+				   update_flags | create_reflog_flag |
    -+				   REF_SYMREF_UPDATE, msg, &err))
    ++	if (ref_transaction_create(transaction, refname, NULL, new_target,
    ++				   update_flags | create_reflog_flag,
    ++				   msg, &err))
     +		die("%s", err.buf);
     +
     +	update_flags = default_flags;
     +	free(refname);
    -+	free(new_ref);
    ++	free(new_target);
     +	strbuf_release(&err);
     +}
     +
    @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      int ref_transaction_create(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
    -+			   const char *new_ref,
    ++			   const char *new_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err)
      {
     -	if (!new_oid || is_null_oid(new_oid)) {
    -+	if ((flags & REF_SYMREF_UPDATE) && !new_ref) {
    -+		strbuf_addf(err, "'%s' has a no new ref", refname);
    -+		return 1;
    -+	}
    -+	if (!(flags & REF_SYMREF_UPDATE) && (!new_oid || is_null_oid(new_oid))) {
    - 		strbuf_addf(err, "'%s' has a null OID", refname);
    +-		strbuf_addf(err, "'%s' has a null OID", refname);
    ++	if ((!new_oid || is_null_oid(new_oid)) && !new_target) {
    ++		strbuf_addf(err, "'%s' has a null OID or no new target", refname);
      		return 1;
      	}
    ++	if (new_target && !(flags & REF_NO_DEREF))
    ++		BUG("create cannot operate on symrefs with deref mode");
      	return ref_transaction_update(transaction, refname, new_oid,
     -				      null_oid(), NULL, NULL, flags,
    -+				      null_oid(), new_ref, NULL, flags,
    ++				      null_oid(), new_target, NULL, flags,
      				      msg, err);
      }
      
    @@ refs.h: int ref_transaction_update(struct ref_transaction *transaction,
      int ref_transaction_create(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
    -+			   const char *new_ref,
    ++			   const char *new_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err);
      
    @@ refs/files-backend.c: static int lock_ref_for_update(struct files_ref_store *ref
      		}
      	}
      
    -+	if (update->flags & REF_SYMREF_UPDATE && update->new_ref) {
    -+		if (create_symref_lock(refs, lock, update->refname, update->new_ref)) {
    ++	if (update->new_target) {
    ++		if (create_symref_lock(refs, lock, update->refname, update->new_target)) {
     +			ret = TRANSACTION_GENERIC_ERROR;
     +			goto out;
     +		}
    @@ refs/files-backend.c: static int files_transaction_finish(struct ref_store *ref_
      
      		if (update->flags & REF_NEEDS_COMMIT ||
      		    update->flags & REF_LOG_ONLY) {
    -+			if (update->flags & REF_SYMREF_UPDATE && update->new_ref) {
    -+				/* for dangling symrefs we gracefully set the oid to zero */
    -+				if (!refs_resolve_ref_unsafe(&refs->base, update->new_ref,
    ++			if (update->new_target) {
    ++				/*
    ++				 * We want to get the resolved OID for the target, to ensure
    ++				 * that the correct value is added to the reflog.
    ++				 */
    ++				if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
     +							     RESOLVE_REF_READING, &update->new_oid, NULL)) {
    ++					/* for dangling symrefs we gracefully set the oid to zero */
     +					update->new_oid = *null_oid();
     +				}
     +			}
    @@ refs/files-backend.c: static int files_transaction_finish(struct ref_store *ref_
     +		 * We try creating a symlink, if that succeeds we continue to the
     +		 * next updated. If not, we try and create a regular symref.
     +		 */
    -+		if (update->flags & REF_SYMREF_UPDATE && prefer_symlink_refs)
    -+			if (!create_ref_symlink(lock, update->new_ref))
    ++		if (update->new_target && prefer_symlink_refs)
    ++			if (!create_ref_symlink(lock, update->new_target))
     +				continue;
     +
      		if (update->flags & REF_NEEDS_COMMIT) {
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
      			 * when the reference in question doesn't exist.
      			 */
     -			 if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
    -+			 if (u->flags & REF_HAVE_NEW && !null_new_value(u)) {
    ++			 if (u->flags & REF_HAVE_NEW && !ref_update_is_null_new_value(u)) {
      				 ret = queue_transaction_update(refs, tx_data, u,
      								&current_oid, err);
      				 if (ret)
    @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writ
      		 *   the given ref.
      		 */
     -		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
    -+		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && null_new_value(u)) {
    ++		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && ref_update_is_null_new_value(u)) {
      			struct reftable_log_record log = {0};
      			struct reftable_iterator it = {0};
      
    +@@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writer *writer, void *cb_data
    + 			    should_write_log(&arg->refs->base, u->refname))) {
    + 			struct reftable_log_record *log;
    + 
    ++			if (u->new_target)
    ++				if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
    ++							     RESOLVE_REF_READING, &u->new_oid, NULL))
    ++					/* for dangling symrefs we gracefully set the oid to zero */
    ++					u->new_oid = *null_oid();
    ++
    + 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
    + 			log = &logs[logs_nr++];
    + 			memset(log, 0, sizeof(*log));
     @@ refs/reftable-backend.c: static int write_transaction_table(struct reftable_writer *writer, void *cb_data
      		if (u->flags & REF_LOG_ONLY)
      			continue;
      
    --		if (u->flags & REF_HAVE_NEW && null_new_value(u)) {
    -+		if (u->flags & REF_SYMREF_UPDATE &&
    -+		    u->flags & REF_HAVE_NEW &&
    -+		    !null_new_value(u)) {
    +-		if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
    ++		if (u->flags & REF_HAVE_NEW && u->new_target) {
     +			struct reftable_ref_record ref = {
     +				.refname = (char *)u->refname,
     +				.value_type = REFTABLE_REF_SYMREF,
    -+				.value.symref = (char *)u->new_ref,
    ++				.value.symref = (char *)u->new_target,
     +				.update_index = ts,
     +			};
     +
     +			ret = reftable_writer_add_ref(writer, &ref);
     +			if (ret < 0)
     +				goto done;
    -+		} else if (u->flags & REF_HAVE_NEW && null_new_value(u)) {
    ++		} else if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
      			struct reftable_ref_record ref = {
      				.refname = (char *)u->refname,
      				.update_index = ts,
    @@ t/t0600-reffiles-backend.sh: test_expect_success POSIXPERM 'git reflog expire ho
      test_done
     
      ## t/t1400-update-ref.sh ##
    -@@ t/t1400-update-ref.sh: test_expect_success "stdin ${type} symref-delete ref works with right old value"
    - 	test_must_fail git rev-parse --verify -q $b
    - '
    +@@ t/t1400-update-ref.sh: do
    + 		test_must_fail git symbolic-ref -d refs/heads/symref2
    + 	'
      
    -+test_expect_success "stdin ${type} symref-create fails without --no-deref" '
    -+	create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" &&
    -+	test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    -+	grep "fatal: symref-create: cannot operate with deref mode" err
    -+'
    ++	test_expect_success "stdin ${type} symref-create fails without --no-deref" '
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" &&
    ++		test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
    ++		grep "fatal: symref-create: cannot operate with deref mode" err
    ++	'
     +
    -+test_expect_success "stdin ${type} fails symref-create with no ref" '
    -+	create_stdin_buf ${type} "symref-create " >stdin &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	grep "fatal: symref-create: missing <ref>" err
    -+'
    ++	test_expect_success "stdin ${type} symref-create fails with too many arguments" '
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" "$a" >stdin &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    ++		if test "$type" = "-z"
    ++		then
    ++			grep "fatal: unknown command: $a" err
    ++		else
    ++			grep "fatal: symref-create refs/heads/symref: extra input:  $a" err
    ++		fi
    ++	'
     +
    -+test_expect_success "stdin ${type} fails symref-create with no new value" '
    -+	create_stdin_buf ${type} "symref-create refs/heads/symref" >stdin &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	grep "fatal: symref-create refs/heads/symref: missing <new-ref>" err
    -+'
    ++	test_expect_success "stdin ${type} symref-create fails with no target" '
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" >stdin &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin
    ++	'
     +
    -+test_expect_success "stdin ${type} fails symref-create with too many arguments" '
    -+	create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" "$a" >stdin &&
    -+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
    -+	if test "$type" = "-z"
    -+	then
    -+		grep "fatal: unknown command: $a" err
    -+	else
    -+		grep "fatal: symref-create refs/heads/symref: extra input:  $a" err
    -+	fi
    -+'
    ++	test_expect_success "stdin ${type} symref-create fails with empty target" '
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "" >stdin &&
    ++		test_must_fail git update-ref --stdin ${type} --no-deref <stdin
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-create ref works" '
    -+	test_when_finished "git symbolic-ref -d refs/heads/symref" &&
    -+	create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" >stdin &&
    -+	git update-ref --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	echo $a >actual &&
    -+	test_cmp expect actual
    -+'
    ++	test_expect_success "stdin ${type} symref-create works" '
    ++		test_when_finished "git symbolic-ref -d refs/heads/symref" &&
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" >stdin &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		echo $a >actual &&
    ++		test_cmp expect actual
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-create does not create reflogs by default" '
    -+	test_when_finished "git symbolic-ref -d refs/symref" &&
    -+	create_stdin_buf ${type} "symref-create refs/symref" "$a" >stdin &&
    -+	git update-ref --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/symref >expect &&
    -+	echo $a >actual &&
    -+	test_cmp expect actual &&
    -+	test_must_fail git reflog exists refs/symref
    -+'
    ++	test_expect_success "stdin ${type} create dangling symref ref works" '
    ++		test_when_finished "git symbolic-ref -d refs/heads/symref" &&
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "refs/heads/unkown" >stdin &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		echo refs/heads/unkown >actual &&
    ++		test_cmp expect actual
    ++	'
     +
    -+test_expect_success "stdin ${type} symref-create reflogs with --create-reflog" '
    -+	test_when_finished "git symbolic-ref -d refs/heads/symref" &&
    -+	create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" >stdin &&
    -+	git update-ref --create-reflog --stdin ${type} --no-deref <stdin &&
    -+	git symbolic-ref refs/heads/symref >expect &&
    -+	echo $a >actual &&
    -+	test_cmp expect actual &&
    -+	git reflog exists refs/heads/symref
    -+'
    ++	test_expect_success "stdin ${type} symref-create does not create reflogs by default" '
    ++		test_when_finished "git symbolic-ref -d refs/symref" &&
    ++		create_stdin_buf ${type} "symref-create refs/symref" "$a" >stdin &&
    ++		git update-ref --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/symref >expect &&
    ++		echo $a >actual &&
    ++		test_cmp expect actual &&
    ++		test_must_fail git reflog exists refs/symref
    ++	'
    ++
    ++	test_expect_success "stdin ${type} symref-create reflogs with --create-reflog" '
    ++		test_when_finished "git symbolic-ref -d refs/heads/symref" &&
    ++		create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" >stdin &&
    ++		git update-ref --create-reflog --stdin ${type} --no-deref <stdin &&
    ++		git symbolic-ref refs/heads/symref >expect &&
    ++		echo $a >actual &&
    ++		test_cmp expect actual &&
    ++		git reflog exists refs/heads/symref
    ++	'
     +
      done
      
6:  714492ede3 < -:  ---------- update-ref: add support for symref-update
-:  ---------- > 6:  b385f4d0d7 update-ref: add support for 'symref-update' command
7:  c483104562 ! 7:  ef335e47d1 refs: support symrefs in 'reference-transaction' hook
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    refs: support symrefs in 'reference-transaction' hook
    +    ref: support symrefs in 'reference-transaction' hook
     
         The 'reference-transaction' hook runs whenever a reference update is
    -    made to the system. In the previous commits, we added support for
    -    various symref commands in `git-update-ref`. While it allowed us to now
    +    made to the system. In the previous commits, we added symref support for
    +    various commands in `git-update-ref`. While it allowed us to now
         manipulate symbolic refs via `git-update-ref`, it didn't activate the
         'reference-transaction' hook.
     
    @@ Commit message
         new format described for this and we stick to the existing format of:
             <old-value> SP <new-value> SP <ref-name> LF
         but now, <old-value> and <new-value> could also denote references
    -    instead of objects.
    +    instead of objects, where the format is similar to that in
    +    'git-update-ref', i.e. 'ref:<ref-target>'.
     
         While this seems to be backward incompatible, it is okay, since the only
         way the `reference-transaction` hook has refs in its output is when
    -    `git-update-ref` is used with `update-symref` command. Also the
    -    documentation for reference-transaction hook always stated that support
    -    for symbolic references may be added in the future.
    +    `git-update-ref` is used to manipulate symrefs. Also the documentation
    +    for reference-transaction hook always stated that support for symbolic
    +    references may be added in the future.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ Documentation/githooks.txt: given reference transaction is in:
      `<ref-name>` via `git rev-parse`.
      
     +For symbolic reference updates the `<old_value>` and `<new-value>`
    -+fields could denote references instead of objects.
    ++fields could denote references instead of objects, denoted via the
    ++`ref:<ref-target>` format.
     +
      The exit status of the hook is ignored for any state except for the
      "prepared" state. In the "prepared" state, a non-zero exit status will
    @@ refs.c: static int run_transaction_hook(struct ref_transaction *transaction,
      
      	for (i = 0; i < transaction->nr; i++) {
      		struct ref_update *update = transaction->updates[i];
    -+		const char *new_value, *old_value;
    ++		strbuf_reset(&buf);
      
    --		if (update->flags & REF_SYMREF_UPDATE)
    +-		/*
    +-		 * Skip reference transaction for symbolic refs.
    +-		 */
    +-		if (update->new_target || update->old_target)
     -			continue;
    -+		new_value = oid_to_hex(&update->new_oid);
    -+		old_value = oid_to_hex(&update->old_oid);
    -+
    -+		if (update->flags & REF_SYMREF_UPDATE) {
    -+			if (update->flags & REF_HAVE_NEW && !null_new_value(update))
    -+				new_value = update->new_ref;
    -+			if (update->flags & REF_HAVE_OLD && update->old_ref)
    -+				old_value = update->old_ref;
    -+		}
    ++		if (update->flags & REF_HAVE_OLD && update->old_target)
    ++			strbuf_addf(&buf, "ref:%s ", update->old_target);
    ++		else
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
      
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		strbuf_addf(&buf, "%s %s %s\n",
     -			    oid_to_hex(&update->old_oid),
     -			    oid_to_hex(&update->new_oid),
     -			    update->refname);
    -+		strbuf_addf(&buf, "%s %s %s\n", old_value, new_value, update->refname);
    ++		if (update->flags & REF_HAVE_NEW && update->new_target)
    ++			strbuf_addf(&buf, "ref:%s ", update->new_target);
    ++		else
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
    ++
    ++		strbuf_addf(&buf, "%s\n", update->refname);
      
      		if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
      			if (errno != EPIPE) {
     
      ## t/t1416-ref-transaction-hooks.sh ##
    -@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'hook gets all queued updates in aborted state' '
    - 	test_cmp expect actual
    +@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls succeed' '
    + 	test_cmp expect target-repo.git/actual
      '
      
    -+# This test doesn't add a check for 'symref-delete' since there is a
    ++# This test doesn't add a check for symref 'delete' since there is a
     +# variation between the ref backends WRT 'delete'. In the files backend,
     +# 'delete' also triggers an additional transaction update on the
     +# packed-refs backend, which constitutes additional reflog entries.
    - test_expect_success 'interleaving hook calls succeed' '
    - 	test_when_finished "rm -r target-repo.git" &&
    - 
    -@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls succeed' '
    - 	test_cmp expect target-repo.git/actual
    - '
    - 
     +test_expect_success 'hook gets all queued symref updates' '
     +	test_when_finished "rm actual" &&
     +
    @@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls s
     +
     +	cat >expect <<-EOF &&
     +		prepared
    -+		refs/heads/main $ZERO_OID refs/heads/symref
    -+		$ZERO_OID refs/heads/main refs/heads/symrefc
    -+		refs/heads/main refs/heads/branch refs/heads/symrefu
    ++		ref:refs/heads/main $ZERO_OID refs/heads/symref
    ++		$ZERO_OID ref:refs/heads/main refs/heads/symrefc
    ++		ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
     +		committed
    -+		refs/heads/main $ZERO_OID refs/heads/symref
    -+		$ZERO_OID refs/heads/main refs/heads/symrefc
    -+		refs/heads/main refs/heads/branch refs/heads/symrefu
    ++		ref:refs/heads/main $ZERO_OID refs/heads/symref
    ++		$ZERO_OID ref:refs/heads/main refs/heads/symrefc
    ++		ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
     +	EOF
     +
     +	git update-ref --no-deref --stdin <<-EOF &&
     +		start
     +		symref-verify refs/heads/symref refs/heads/main
     +		symref-create refs/heads/symrefc refs/heads/main
    -+		symref-update refs/heads/symrefu refs/heads/branch refs/heads/main
    ++		symref-update refs/heads/symrefu refs/heads/branch ref refs/heads/main
     +		prepare
     +		commit
     +	EOF


Karthik Nayak (7):
  refs: accept symref values in `ref_transaction[_add]_update`
  files-backend: extract out `create_symref_lock`
  update-ref: add support for 'symref-verify' command
  update-ref: add support for 'symref-delete' command
  update-ref: add support for 'symref-create' command
  update-ref: add support for 'symref-update' command
  ref: support symrefs in 'reference-transaction' hook

 Documentation/git-update-ref.txt |  34 ++-
 Documentation/githooks.txt       |  14 +-
 branch.c                         |   2 +-
 builtin/clone.c                  |   2 +-
 builtin/fast-import.c            |   5 +-
 builtin/fetch.c                  |   4 +-
 builtin/receive-pack.c           |   4 +-
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   1 +
 builtin/update-ref.c             | 240 ++++++++++++++++--
 refs.c                           |  78 ++++--
 refs.h                           |  23 +-
 refs/files-backend.c             | 141 +++++++++--
 refs/refs-internal.h             |  20 ++
 refs/reftable-backend.c          |  49 +++-
 sequencer.c                      |   9 +-
 t/t0600-reffiles-backend.sh      |  32 +++
 t/t1400-update-ref.sh            | 413 ++++++++++++++++++++++++++++++-
 t/t1416-ref-transaction-hooks.sh |  41 +++
 walker.c                         |   2 +-
 20 files changed, 1030 insertions(+), 86 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 1%]

* [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update`
  2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
@ 2024-04-26 15:24  7%       ` Karthik Nayak
  2024-04-26 15:24 11%       ` [PATCH v4 4/7] update-ref: add support for 'symref-delete' command Karthik Nayak
  2024-05-01 20:22  1%       ` [PATCH v5 0/7] refs: add support for transactional symref updates Karthik Nayak
  2 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-26 15:24 UTC (permalink / raw)
  To: karthik.188; +Cc: christian.couder, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The `ref_transaction[_add]_update` functions obtain ref information and
flags to create a `ref_update` and add it to the transaction at hand.

To extend symref support in transactions, we need to also accept the
old and new ref targets and process it. In this commit, let's add the
required parameters to the function and modify all call sites.

The two parameters added are `new_target` and `old_target`. The
`new_target` is used to denote what the reference should point to when
the transaction is applied.

The `old_target` denotes the value the reference must have before the
update. Some functions allow this parameter to be NULL, meaning that the
old value of the reference is not checked.

The handling logic of these parameters will be added in consequent
commits as we add symref commands to the '--stdin' mode of
'git-update-ref'.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  5 +++--
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  1 +
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  1 +
 builtin/update-ref.c    |  1 +
 refs.c                  | 22 +++++++++++++++++-----
 refs.h                  | 17 ++++++++++++++++-
 refs/files-backend.c    | 12 ++++++------
 refs/refs-internal.h    | 13 +++++++++++++
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  9 +++++----
 walker.c                |  2 +-
 14 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/branch.c b/branch.c
index e4a738fc7b..48af4c3ceb 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..297dfb91a1 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1675,7 +1675,8 @@ static void dump_tags(void)
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..66840b7c5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..b150ef39a8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..7690687b0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..40a65fdebc 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..21fdbf6ac8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..060a31616d 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg)
 {
 	struct ref_update *update;
@@ -1235,6 +1236,11 @@ struct ref_update *ref_transaction_add_update(
 	if (transaction->state != REF_TRANSACTION_OPEN)
 		BUG("update called for transaction that is not open");
 
+	if (old_oid && !is_null_oid(old_oid) && old_target)
+		BUG("Only one of old_oid and old_target should be non NULL");
+	if (new_oid && !is_null_oid(new_oid) && new_target)
+		BUG("Only one of new_oid and new_target should be non NULL");
+
 	FLEX_ALLOC_STR(update, refname, refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
@@ -1253,6 +1259,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err)
 {
@@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, new_target,
+				   old_target, msg);
 	return 0;
 }
 
@@ -1295,7 +1304,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1318,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1332,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
 				      NULL, old_oid,
+				      NULL, NULL,
 				      flags, NULL, err);
 }
 
@@ -1335,8 +1347,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
-	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+				   flags, msg, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index d278775e08..c792e13a64 100644
--- a/refs.h
+++ b/refs.h
@@ -648,6 +648,15 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  *         before the update. A copy of this value is made in the
  *         transaction.
  *
+ *     new_target -- the target reference that the reference will be
+ *         update to point to. This takes precedence over new_oid when
+ *         set. If the reference is a regular reference, it will be
+ *         converted to a symbolic reference.
+ *
+ *     old_target -- the reference that the reference must be pointing to.
+ *         Will only be taken into account when the reference is a symbolic
+ *         reference.
+ *
  *     flags -- flags affecting the update, passed to
  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
  *         REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -713,7 +722,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  * beforehand. The old value is checked after the lock is taken to
  * prevent races. If the old value doesn't agree with old_oid, the
  * whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
  *
  * See the above comment "Reference transaction updates" for more
  * information.
@@ -722,6 +735,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea0..e4d0aa3d41 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
+					   iter->oid, NULL, NULL, NULL,
 					   REF_NO_DEREF, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
@@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL, NULL);
 		}
 	}
 
@@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..3040d4797c 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,18 @@ struct ref_update {
 	 */
 	struct object_id old_oid;
 
+	/*
+	 * If set, point the reference to this value. This can also be
+	 * used to convert regular references to become symbolic refs.
+	 */
+	const char *new_target;
+
+	/*
+	 * If set and the reference is a symbolic ref, check that the
+	 * reference previously pointed to this value.
+	 */
+	const char *old_target;
+
 	/*
 	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
 	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
@@ -173,6 +185,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg);
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c504..6104471199 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 2c19846385..af1b25692b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -616,7 +616,7 @@ static int fast_forward_to(struct repository *r,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
-				   null_oid() : from,
+				   null_oid() : from, NULL, NULL,
 				   0, sb.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
@@ -1248,7 +1248,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   NULL, NULL, 0, sb.buf, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3764,8 +3764,9 @@ static int do_label(struct repository *r, const char *name, int len)
 	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
-	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+	} else if (ref_transaction_update(transaction, ref_name.buf,
+					  &head_oid, NULL, NULL, NULL,
+					  0, msg.buf, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index c0fd632d92..1b3df43906 100644
--- a/walker.c
+++ b/walker.c
@@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		strbuf_reset(&refname);
 		strbuf_addf(&refname, "refs/%s", write_ref[i]);
 		if (ref_transaction_update(transaction, refname.buf,
-					   oids + i, NULL, 0,
+					   oids + i, NULL, NULL, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
 					   &err)) {
 			error("%s", err.buf);
-- 
2.43.GIT



^ permalink raw reply related	[relevance 7%]

* [PATCH v3 00/10] Make trailer_info struct private (plus sequencer cleanup)
  2024-04-19  5:22  3% ` [PATCH v2 0/8] " Linus Arver via GitGitGadget
@ 2024-04-26  0:26  2%   ` Linus Arver via GitGitGadget
  2024-05-02  4:54  2%     ` [PATCH v4 " Linus Arver via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-26  0:26 UTC (permalink / raw)
  To: git
  Cc: Christian Couder, Junio C Hamano, Emily Shaffer, Josh Steadmon,
	Randall S. Becker, Christian Couder, Kristoffer Haugsbakk,
	Linus Arver, Linus Arver, Linus Arver

NOTE: This series is based on the la/format-trailer-info topic branch (see
its discussion at [1]).

This series is based on the initial series [2], notably the v4 version of
patches 17-20 as suggested by Christian [3]. This version addresses the
review comments for those patches, namely the splitting up of Patch 19 there
into 3 separate patches [4] (as Patches 05-07 here) .

The central idea is to make the trailer_info struct private (that is, move
its definition from trailer.h to trailer.c) --- aka the "pimpl" idiom. See
the detailed commit message for Patch 07 for the motivation behind the
change.

Patch 04 makes sequencer.c a well-behaved trailer API consumer, by making
use of the trailer iterator. Patch 03 prepares us for Patch 04. Patch 08
slightly reduces the weight of the API by removing (from the API surface) an
unused function.


Notable changes in v3
=====================

 * (NEW Patch 10) Expand test coverage to check the contents of each
   iteration (raw, key, val fields), not just the total number of iterations
 * (NEW Patch 09) Add documentation in <trailer.h> for using
   parse_trailers()
 * (unrelated) I will lose access to my linusa@google.com email address
   tomorrow (I'm switching jobs!) and so future emails from me will come
   from linus@ucla.edu [5]. I've added the latter email to the CC list here
   so things should just work. Cheers


Notable changes in v2
=====================

 * Add unit tests at the beginning of the series (Patches 01 and 02) and use
   it to verify that the other edge cases remain unchanged when we add the
   "raw" member (Patch 03)

[1]
https://lore.kernel.org/git/pull.1694.git.1710485706.gitgitgadget@gmail.com/
[2]
https://lore.kernel.org/git/pull.1632.v4.git.1707196348.gitgitgadget@gmail.com/
[3]
https://lore.kernel.org/git/CAP8UFD08F0V13X0+CJ1uhMPzPWVMs2okGVMJch0DkQg5M3BWLA@mail.gmail.com/
[4]
https://lore.kernel.org/git/CAP8UFD1twELGKvvesxgCrZrypKZpgSt04ira3mvurG1UbpDfxQ@mail.gmail.com/
[5]
https://lore.kernel.org/git/pull.1720.git.1713309711217.gitgitgadget@gmail.com/

Linus Arver (10):
  Makefile: sort UNIT_TEST_PROGRAMS
  trailer: add unit tests for trailer iterator
  trailer: teach iterator about non-trailer lines
  sequencer: use the trailer iterator
  interpret-trailers: access trailer_info with new helpers
  trailer: make parse_trailers() return trailer_info pointer
  trailer: make trailer_info struct private
  trailer: retire trailer_info_get() from API
  trailer: document parse_trailers() usage
  trailer unit tests: inspect iterator contents

 Makefile                     |   5 +-
 builtin/interpret-trailers.c |  12 +-
 sequencer.c                  |  27 ++-
 t/unit-tests/t-trailer.c     | 315 +++++++++++++++++++++++++++++++++++
 trailer.c                    | 167 ++++++++++++-------
 trailer.h                    |  95 +++++++----
 6 files changed, 507 insertions(+), 114 deletions(-)
 create mode 100644 t/unit-tests/t-trailer.c


base-commit: 3452d173241c8b87ecdd67f91f594cb14327e394
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1696%2Flistx%2Ftrailer-api-part-3-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1696/listx/trailer-api-part-3-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1696

Range-diff vs v2:

  1:  b6a1304f8ae =  1:  b6a1304f8ae Makefile: sort UNIT_TEST_PROGRAMS
  2:  e1fa05143ac !  2:  4ad0fbbb33c trailer: add unit tests for trailer iterator
     @@ t/unit-tests/t-trailer.c (new)
      +	size_t i = 0;
      +
      +	trailer_iterator_init(&iter, msg);
     -+	while (trailer_iterator_advance(&iter)) {
     ++	while (trailer_iterator_advance(&iter))
      +		i++;
     -+	}
      +	trailer_iterator_release(&iter);
      +
      +	check_uint(i, ==, num_expected_trailers);
     @@ t/unit-tests/t-trailer.c (new)
      +			/*
      +			 * Even though this trailer block has a non-trailer line
      +			 * in it, it's still a valid trailer block because it's
     -+			 * at least 25% trailers and is Git-generated.
     ++			 * at least 25% trailers and is Git-generated (see
     ++			 * git_generated_prefixes[] in trailer.c).
      +			 */
      +			"not a trailer line\n"
      +			"not a trailer line\n"
     @@ t/unit-tests/t-trailer.c (new)
      +			"\n"
      +			/*
      +			 * This block has only 1 non-trailer out of 10 (IOW, 90%
     -+			 * trailers) but is not considered a trailer because the
     -+			 * 25% threshold only applies to cases where there was a
     -+			 * Git-generated trailer (see git_generated_prefixes[]
     -+			 * in trailer.c).
     ++			 * trailers) but is not considered a trailer block
     ++			 * because the 25% threshold only applies to cases where
     ++			 * there was a Git-generated trailer.
      +			 */
      +			"Reviewed-by: x\n"
      +			"Reviewed-by: x\n"
  3:  5520a98e296 !  3:  9077d5a315d trailer: teach iterator about non-trailer lines
     @@ Commit message
          for non-trailer lines, making the comparison still work even with this
          commit).
      
     +    Rename "num_expected_trailers" to "num_expected_objects" in
     +    t/unit-tests/t-trailer.c because the items we iterate over now include
     +    non-trailer lines.
     +
          Signed-off-by: Linus Arver <linusa@google.com>
      
       ## t/unit-tests/t-trailer.c ##
     +@@
     + #include "test-lib.h"
     + #include "trailer.h"
     + 
     +-static void t_trailer_iterator(const char *msg, size_t num_expected_trailers)
     ++static void t_trailer_iterator(const char *msg, size_t num_expected_objects)
     + {
     + 	struct trailer_iterator iter;
     + 	size_t i = 0;
     +@@ t/unit-tests/t-trailer.c: static void t_trailer_iterator(const char *msg, size_t num_expected_trailers)
     + 		i++;
     + 	trailer_iterator_release(&iter);
     + 
     +-	check_uint(i, ==, num_expected_trailers);
     ++	check_uint(i, ==, num_expected_objects);
     + }
     + 
     + static void run_t_trailer_iterator(void)
     +@@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
     + 	static struct test_cases {
     + 		const char *name;
     + 		const char *msg;
     +-		size_t num_expected_trailers;
     ++		size_t num_expected_objects;
     + 	} tc[] = {
     + 		{
     + 			"empty input",
      @@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
       			"not a trailer line\n"
       			"not a trailer line\n"
     @@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
       		},
       		{
       			"with non-trailer lines (one too many) in trailer block",
     +@@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
     + 
     + 	for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
     + 		TEST(t_trailer_iterator(tc[i].msg,
     +-					tc[i].num_expected_trailers),
     ++					tc[i].num_expected_objects),
     + 		     "%s", tc[i].name);
     + 	}
     + }
      
       ## trailer.c ##
      @@ trailer.c: void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
  4:  84897cf5c83 =  4:  4a1d18da574 sequencer: use the trailer iterator
  5:  e961d49cd40 =  5:  460979ba964 interpret-trailers: access trailer_info with new helpers
  6:  093f68f3658 =  6:  d217858c637 trailer: make parse_trailers() return trailer_info pointer
  7:  0e9ae049b88 !  7:  49c66c48cc1 trailer: make trailer_info struct private
     @@ Commit message
            (2) external API users are unable to peer inside this struct (because
                it is only ever exposed as an opaque pointer).
      
     -    There are a couple disadvantages:
     +    There are a few disadvantages:
      
            (A) every time the member of the struct is accessed an extra pointer
                dereference must be done, and
     @@ Commit message
            (B) for users of trailer_info outside trailer.c, this struct can no
                longer be allocated on the stack and may only be allocated on the
                heap (because its definition is hidden away in trailer.c) and
     -          appropriately deallocated by the user.
     +          appropriately deallocated by the user, and
     +
     +      (C) without good documentation on the API, the opaque struct is
     +          hostile to programmers by going opposite to the "Show me your
     +          data structures, and I won't usually need your code; it'll
     +          be obvious." mantra [2].
      
          (The disadvantages have already been observed in the two preparatory
          commits that precede this one.) This commit believes that the benefits
     @@ Commit message
          [1] Hanson, David R. "C Interfaces and Implementations: Techniques for
              Creating Reusable Software". Addison Wesley, 1997. p. 22
      
     +    [2] Raymond, Eric S. "The Cathedral and the Bazaar: Musings on Linux and
     +        Open Source by an Accidental Revolutionary". O'Reilly, 1999.
     +
     +    Helped-by: Junio C Hamano <gitster@pobox.com>
          Helped-by: Christian Couder <chriscool@tuxfamily.org>
          Signed-off-by: Linus Arver <linusa@google.com>
      
  8:  eca77a1a462 =  8:  56e1cca4b7b trailer: retire trailer_info_get() from API
  -:  ----------- >  9:  35304837e08 trailer: document parse_trailers() usage
  -:  ----------- > 10:  4d53707f836 trailer unit tests: inspect iterator contents

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* [PATCH] completion: fix zsh parsing $GIT_PS1_SHOWUPSTREAM
@ 2024-04-25 18:59  4% Thomas via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Thomas via GitGitGadget @ 2024-04-25 18:59 UTC (permalink / raw)
  To: git; +Cc: Thomas, Thomas Queiroz

From: Thomas Queiroz <thomasqueirozb@gmail.com>

Since GIT_PS1_SHOWUPSTREAM is a variable with space separated values and
zsh for loops do no split by space by default, parsing of the options
wasn't actually being done. The `-d' '` is a hacky solution that works
in both bash and zsh. The correct way to do that in zsh would be do use
read -rA and loop over the resulting array but -A isn't defined in bash.

Signed-off-by: Thomas Queiroz <thomasqueirozb@gmail.com>
---
    completion: Fix zsh parsing $GIT_PS1_SHOWUPSTREAM

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1710%2Fthomasqueirozb%2Fzsh-completion-fix-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1710/thomasqueirozb/zsh-completion-fix-v1
Pull-Request: https://github.com/git/git/pull/1710

 contrib/completion/git-prompt.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 5330e769a72..9c25ec1e965 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -141,14 +141,14 @@ __git_ps1_show_upstream ()
 
 	# parse configuration values
 	local option
-	for option in ${GIT_PS1_SHOWUPSTREAM-}; do
+	while read -r -d' ' option; do
 		case "$option" in
 		git|svn) upstream_type="$option" ;;
 		verbose) verbose=1 ;;
 		legacy)  legacy=1  ;;
 		name)    name=1 ;;
 		esac
-	done
+	done <<< "${GIT_PS1_SHOWUPSTREAM-} "
 
 	# Find our upstream type
 	case "$upstream_type" in

base-commit: 21306a098c3f174ad4c2a5cddb9069ee27a548b0
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [ANNOUNCE] Git v2.45.0-rc1
@ 2024-04-24 17:07  2% Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-24 17:07 UTC (permalink / raw)
  To: git; +Cc: Linux Kernel, git-packagers

A release candidate Git v2.45.0-rc1 is now available for testing at
the usual places.  It is comprised of 521 non-merge commits since
v2.44.0, contributed by 84 people, 36 of which are new faces [*].

The tarballs are found at:

    https://www.kernel.org/pub/software/scm/git/testing/

The following public repositories all have a copy of the
'v2.45.0-rc1' tag and the 'master' branch that the tag points at:

  url = https://git.kernel.org/pub/scm/git/git
  url = https://kernel.googlesource.com/pub/scm/git/git
  url = git://repo.or.cz/alt-git.git
  url = https://github.com/gitster/git

New contributors whose contributions weren't in v2.44.0 are as follows.
Welcome to the Git development community!

  Ahelenia Ziemiańska, Angelo Dureghello, Aryan Gupta, Benjamin
  Flesch, Bo Anderson, Brian C Tracy, Brian Lyles, Bruno Haible,
  Chuck Lever, Dario Gjorgjevski, Dirk Gouders, Eugenio Gigante,
  Florian Schmidt, Haritha D, Harmen Stoppels, Jean-Rémy Falleri,
  Jiamu Sun, Jonas Wunderlich, Jonathan Davies, Julio Bacellari,
  Kipras Melnikovas, Marcel Röthke, Matthew Rollings, Max Gautier,
  mirth hickford, Paweł Dominiak, Pi Fisher, Ralph Seichter,
  Richard Macklin, shejialuo, Steven Jeuris, Thalia Archibald,
  Tiago Pascoal, Vincenzo Mezzela, Xing Xin, and Yehezkel Bernat.

Returning contributors who helped this release are as follows.
Thanks for your continued support.

  Alexander Shopov, Beat Bolli, brian m. carlson, Chandra Pratap,
  Christian Couder, Derrick Stolee, Đoàn Trần Công Danh,
  Dragan Simic, Elijah Newren, Eric Sunshine, Eric W. Biederman,
  Ghanshyam Thakkar, Han Young, Jakub Wilk, Jean-Noël Avila,
  Jeff Hostetler, Jeff King, Johannes Schindelin, Johannes Sixt,
  John Cai, Josh Steadmon, Josh Triplett, Junio C Hamano, Justin
  Tobler, Karthik Nayak, Kristoffer Haugsbakk, Kyle Lippincott,
  Kyle Meyer, Linus Arver, Manlio Perillo, Matthias Aßhauer, M
  Hickford, Michael Lohmann, Michael Osipov, Mike Hommey, Orgad
  Shaneh, Patrick Steinhardt, Peter Hutterer, Peter Krefting,
  Philippe Blain, Phillip Wood, René Scharfe, Rubén Justo,
  Sergey Organov, SZEDER Gábor, Taylor Blau, Ville Skyttä,
  and Yasushi SHOJI.

[*] We are counting not just the authorship contribution but issue
    reporting, mentoring, helping and reviewing that are recorded in
    the commit trailers.

----------------------------------------------------------------

Git v2.45 Release Notes (draft)
===============================

Backward Compatibility Notes

UI, Workflows & Features

 * Integrate the reftable code into the refs framework as a backend.
   With "git init --ref-format=reftable", hopefully it would be a lot
   more efficient to manage a repository with many references.

 * "git checkout -p" and friends learned that that "@" is a synonym
   for "HEAD".

 * Variants of vimdiff learned to honor mergetool.<variant>.layout
   settings.

 * "git reflog" learned a "list" subcommand that enumerates known reflogs.

 * When a merge conflicted at a submodule, merge-ort backend used to
   unconditionally give a lengthy message to suggest how to resolve
   it.  Now the message can be squelched as an advice message.

 * "git for-each-ref" learned "--include-root-refs" option to show
   even the stuff outside the 'refs/' hierarchy.

 * "git rev-list --missing=print" has learned to optionally take
   "--allow-missing-tips", which allows the objects at the starting
   points to be missing.

 * "git merge-tree" has learned that the three trees involved in the
   3-way merge only need to be trees, not necessarily commits.

 * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
   other kinds of *_HEAD pseudorefs.

 * Platform specific tweaks for OS/390 has been added to
   config.mak.uname.

 * Users with safe.bareRepository=explicit can still work from within
   $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
   of the primary worktree without explicitly specifying the $GIT_DIR
   environment variable or the --git-dir=<path> option.

 * The output format for dates "iso-strict" has been tweaked to show
   a time in the Zulu timezone with "Z" suffix, instead of "+00:00".

 * "git diff" and friends learned two extra configuration variables,
   diff.srcPrefix and diff.dstPrefix.

 * The status.showUntrackedFiles configuration variable had a name
   that tempts users to set a Boolean value expressed in our usual
   "false", "off", and "0", but it only took "no".  This has been
   corrected so "true" and its synonyms are taken as "normal", while
   "false" and its synonyms are taken as "no".

 * Remove an ancient and not well maintained Hg-to-git migration
   script from contrib/.

 * Hints that suggest what to do after resolving conflicts can now be
   squelched by disabling advice.mergeConflict.

 * Allow git-cherry-pick(1) to automatically drop redundant commits via
   a new `--empty` option, similar to the `--empty` options for
   git-rebase(1) and git-am(1). Includes a soft deprecation of
   `--keep-redundant-commits` as well as some related docs changes and
   sequencer code cleanup.

 * "git config" learned "--comment=<message>" option to leave a
   comment immediately after the "variable = value" on the same line
   in the configuration file.

 * core.commentChar used to be limited to a single byte, but has been
   updated to allow an arbitrary multi-byte sequence.

 * "git add -p" and other "interactive hunk selection" UI has learned to
   skip showing the hunk immediately after it has already been shown, and
   an additional action to explicitly ask to reshow the current hunk.

 * "git pack-refs" learned the "--auto" option, which defers the decision of
   whether and how to pack to the ref backend. This is used by the reftable
   backend to avoid repacking of an already-optimal ref database. The new mode
   is triggered from "git gc --auto".

 * "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
   diagnose a pathspec element that did not match any files in certain
   situations, unlike "git add <pathspec>" did.

 * The userdiff patterns for C# has been updated.

 * Git writes a "waiting for your editor" message on an incomplete
   line after launching an editor, and then append another error
   message on the same line if the editor errors out.  It now clears
   the "waiting for..." line before giving the error message.

 * The filename used for rejected hunks "git apply --reject" creates
   was limited to PATH_MAX, which has been lifted.

 * When "git bisect" reports the commit it determined to be the
   culprit, we used to show it in a format that does not honor common
   UI tweaks, like log.date and log.decorate.  The code has been
   taught to use "git show" to follow more customizations.


Performance, Internal Implementation, Development Support etc.

 * The code to iterate over refs with the reftable backend has seen
   some optimization.

 * More tests that are marked as "ref-files only" have been updated to
   improve test coverage of reftable backend.

 * Some parts of command line completion script (in contrib/) have
   been micro-optimized.

 * The way placeholders are to be marked-up in documentation have been
   specified; use "_<placeholder>_" to typeset the word inside a pair
   of <angle-brackets> emphasized.

 * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
   fetching of objects from the promisor remote, which may be handy
   for debugging.

 * The implementation in "git clean" that makes "-n" and "-i" ignore
   clean.requireForce has been simplified, together with the
   documentation.

 * Uses of xwrite() helper have been audited and updated for better
   error checking and simpler code.

 * Some trace2 events that lacked def_param have learned to show it,
   enriching the output.

 * The parse-options code that deals with abbreviated long option
   names have been cleaned up.

 * The code in reftable backend that creates new table files works
   better with the tempfile framework to avoid leaving cruft after a
   failure.

 * The reftable code has its own custom binary search function whose
   comparison callback has an unusual interface, which caused the
   binary search to degenerate into a linear search, which has been
   corrected.

 * The code to iterate over reflogs in the reftable has been optimized
   to reduce memory allocation and deallocation.

 * Work to support a repository that work with both SHA-1 and SHA-256
   hash algorithms has started.

 * A new fuzz target that exercises config parsing code has been
   added.

 * Fix the way recently added tests interpolate variables defined
   outside them, and document the best practice to help future
   developers.

 * Introduce an experimental protocol for contributors to propose the
   topic description to be used in the "What's cooking" report, the
   merge commit message for the topic, and in the release notes and
   document it in the SubmittingPatches document.

 * The t/README file now gives a hint on running individual tests in
   the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
   (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).

 * The "hint:" messages given by the advice mechanism, when given a
   message with a blank line, left a line with trailing whitespace,
   which has been cleansed.

 * Documentation rules has been explicitly described how to mark-up
   literal parts and a few manual pages have been updated as examples.

 * The .editorconfig file has been taught that a Makefile uses HT
   indentation.

 * t-prio-queue test has been cleaned up by using C99 compound
   literals; this is meant to also serve as a weather-balloon to smoke
   out folks with compilers who have trouble compiling code that uses
   the feature.

 * Windows binary used to decide the use of unix-domain socket at
   build time, but it learned to make the decision at runtime instead.

 * The "shared repository" test in the t0610 reftable test failed
   under restrictive umask setting (e.g. 007), which has been
   corrected.

 * Document and apply workaround for a buggy version of dash that
   mishandles "local var=val" construct.

 * The codepaths that reach date_mode_from_type() have been updated to
   pass "struct date_mode" by value to make them thread safe.

 * The strategy to compact multiple tables of reftables after many
   operations accumulate many entries has been improved to avoid
   accumulating too many tables uncollected.

 * The code to iterate over reftable blocks has seen some optimization
   to reduce memory allocation and deallocation.

 * The way "git fast-import" handles paths described in its input has
   been tightened up and more clearly documented.

 * The cvsimport tests required that the platform understands
   traditional timezone notations like CST6CDT, which has been
   updated to work on those systems as long as they understand
   POSIX notation with explicit tz transition dates.

 * The code to format trailers have been cleaned up.


Fixes since v2.44
-----------------

 * "git apply" on a filesystem without filemode support have learned
   to take a hint from what is in the index for the path, even when
   not working with the "--index" or "--cached" option, when checking
   the executable bit match what is required by the preimage in the
   patch.
   (merge 45b625142d cp/apply-core-filemode later to maint).

 * "git column" has been taught to reject negative padding value, as
   it would lead to nonsense behaviour including division by zero.
   (merge 76fb807faa kh/column-reject-negative-padding later to maint).

 * "git am --help" now tells readers what actions are available in
   "git am --whitespace=<action>", in addition to saying that the
   option is passed through to the underlying "git apply".
   (merge a171dac734 jc/am-whitespace-doc later to maint).

 * "git tag --column" failed to check the exit status of its "git
   column" invocation, which has been corrected.
   (merge 92e66478fc rj/tag-column-fix later to maint).

 * Credential helper based on libsecret (in contrib/) has been updated
   to handle an empty password correctly.
   (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).

 * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
   option; it used to always exit with 0 and signalled success.
   (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).

 * The code incorrectly attempted to use textconv cache when asked,
   even when we are not running in a repository, which has been
   corrected.
   (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).

 * Remove an empty file that shouldn't have been added in the first
   place.
   (merge 4f66942215 js/remove-cruft-files later to maint).

 * The logic to access reflog entries by date and number had ugly
   corner cases at the boundaries, which have been cleaned up.
   (merge 5edd126720 jk/reflog-special-cases-fix later to maint).

 * An error message from "git upload-pack", which responds to "git
   fetch" requests, had a trialing NUL in it, which has been
   corrected.
   (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).

 * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
   to be the first header file.
   (merge 4e89f0e07c jc/doc-compat-util later to maint).

 * "git commit -v --cleanup=scissors" used to add the scissors line
   twice in the log message buffer, which has been corrected.
   (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).

 * A custom remote helper no longer cannot access the newly created
   repository during "git clone", which is a regression in Git 2.44.
   This has been corrected.
   (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).

 * Various parts of upload-pack have been updated to bound the resource
   consumption relative to the size of the repository to protect from
   abusive clients.
   (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).

 * The upload-pack program, when talking over v2, accepted the
   packfile-uris protocol extension from the client, even if it did
   not advertise the capability, which has been corrected.
   (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).

 * Make sure failure return from merge_bases_many() is properly caught.
   (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).

 * FSMonitor client code was confused when FSEvents were given in a
   different case on a case-insensitive filesystem, which has been
   corrected.
   (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).

 * The "core.commentChar" configuration variable only allows an ASCII
   character, which was not clearly documented, which has been
   corrected.
   (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).

 * With release 2.44 we got rid of all uses of test_i18ngrep and there
   is no in-flight topic that adds a new use of it.  Make a call to
   test_i18ngrep a hard failure, so that we can remove it at the end
   of this release cycle.
   (merge 381a83dfa3 jc/test-i18ngrep later to maint).

 * The command line completion script (in contrib/) learned to
   complete "git reflog" better.
   (merge 1284f9cc11 rj/complete-reflog later to maint).

 * The logic to complete the command line arguments to "git worktree"
   subcommand (in contrib/) has been updated to correctly honor things
   like "git -C dir" etc.
   (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).

 * When git refuses to create a branch because the proposed branch
   name is not a valid refname, an advice message is given to refer
   the user to exact naming rules.
   (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).

 * Code simplification by getting rid of code that sets an environment
   variable that is no longer used.
   (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).

 * The code to find the effective end of log messages can fall into an
   endless loop, which has been corrected.
   (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).

 * Mark-up used in the documentation has been improved for
   consistency.
   (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).

 * The status.showUntrackedFiles configuration variable was
   incorrectly documented to accept "false", which has been corrected.

 * Leaks from "git restore" have been plugged.
   (merge 2f64da0790 rj/restore-plug-leaks later to maint).

 * "git bugreport --no-suffix" was not supported and instead
   segfaulted, which has been corrected.
   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).

 * The documentation for "%(trailers[:options])" placeholder in the
   "--pretty" option of commands in the "git log" family has been
   updated.
   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).

 * "git checkout --conflict=bad" reported a bad conflictStyle as if it
   were given to a configuration variable; it has been corrected to
   report that the command line option is bad.
   (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).

 * Code clean-up in the "git log" machinery that implements custom log
   message formatting.
   (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).

 * "git config" corrupted literal HT characters written in the
   configuration file as part of a value, which has been corrected.
   (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).

 * A unit test for reftable code tried to enumerate all files in a
   directory after reftable operations and expected to see nothing but
   the files it wanted to leave there, but was fooled by .nfs* cruft
   files left, which has been corrected.
   (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).

 * The implementation and documentation of "object-format" option
   exchange between the Git itself and its remote helpers did not
   quite match, which has been corrected.

 * The "--pretty=<shortHand>" option of the commands in the "git log"
   family, defined as "[pretty] shortHand = <expansion>" should have
   been looked up case insensitively, but was not, which has been
   corrected.
   (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).

 * "git apply" failed to extract the filename the patch applied to,
   when the change was about an empty file created in or deleted from
   a directory whose name ends with a SP, which has been corrected.
   (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).

 * Update a more recent tutorial doc.
   (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).

 * The test script had an incomplete and ineffective attempt to avoid
   clobbering the testing user's real crontab (and its equivalents),
   which has been completed.
   (merge 73cb87773b es/test-cron-safety later to maint).

 * Use advice_if_enabled() API to rewrite a simple pattern to
   call advise() after checking advice_enabled().
   (merge 6412d01527 rj/use-adv-if-enabled later to maint).

 * Another "set -u" fix for the bash prompt (in contrib/) script.
   (merge d7805bc743 vs/complete-with-set-u-fix later to maint).

 * "git checkout/switch --detach foo", after switching to the detached
   HEAD state, gave the tracking information for the 'foo' branch,
   which was pointless.

 * "git apply" has been updated to lift the hardcoded pathname length
   limit, which in turn allowed a mksnpath() function that is no
   longer used.
   (merge 708f7e0590 rs/apply-lift-path-length-limit later to maint).

 * A file descriptor leak in an error codepath, used when "git apply
   --reject" fails to create the *.rej file, has been corrected.
   (merge 2b1f456adf rs/apply-reject-fd-leakfix later to maint).

 * A config parser callback function fell through instead of returning
   after recognising and processing a variable, wasting cycles, which
   has been corrected.
   (merge a816ccd642 ds/fetch-config-parse-microfix later to maint).

 * Fix was added to work around a regression in libcURL 8.7.0 (which has
   already been fixed in their tip of the tree).
   (merge 92a209bf24 jk/libcurl-8.7-regression-workaround later to maint).

 * The variable that holds the value read from the core.excludefile
   configuration variable used to leak, which has been corrected.
   (merge 0e0fefb29f jc/unleak-core-excludesfile later to maint).

 * vreportf(), which is used by error() and friends, has been taught
   to give the error message printf-format string when its vsnprintf()
   call fails, instead of showing nothing useful to identify the
   nature of the error.
   (merge c63adab961 rs/usage-fallback-to-show-message-format later to maint).

 * Adjust to an upcoming changes to GNU make that breaks our Makefiles.
   (merge 227b8fd902 tb/make-indent-conditional-with-non-spaces later to maint).

 * Git 2.44 introduced a regression that makes the updated code to
   barf in repositories with multi-pack index written by older
   versions of Git, which has been corrected.

 * When .git/rr-cache/ rerere database gets corrupted or rerere is fed to
   work on a file with conflicted hunks resolved incompletely, the rerere
   machinery got confused and segfaulted, which has been corrected.
   (merge 167395bb47 mr/rerere-crash-fix later to maint).

 * The "receive-pack" program (which responds to "git push") was not
   converted to run "git maintenance --auto" when other codepaths that
   used to run "git gc --auto" were updated, which has been corrected.
   (merge 7bf3057d9c ps/run-auto-maintenance-in-receive-pack later to maint).

 * Other code cleanup, docfix, build fix, etc.
   (merge f0e578c69c rs/use-xstrncmpz later to maint).
   (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
   (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
   (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
   (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
   (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
   (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
   (merge 244001aa20 hs/rebase-not-in-progress later to maint).
   (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
   (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
   (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
   (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
   (merge 40b8076462 ak/rebase-autosquash later to maint).
   (merge 3223204456 eg/add-uflags later to maint).
   (merge 5f78d52dce es/config-doc-sort-sections later to maint).
   (merge 781fb7b4c2 as/option-names-in-messages later to maint).
   (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
   (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
   (merge 67471bc704 ja/doc-formatting-fix later to maint).
   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
   (merge 0d527842b7 az/grep-group-error-message-update later to maint).
   (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
   (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
   (merge 39bb692152 rs/imap-send-use-xsnprintf later to maint).
   (merge 8d320cec60 jc/t2104-style-fixes later to maint).
   (merge b4454d5a7b pw/t3428-cleanup later to maint).
   (merge 84a7c33a4b pf/commitish-committish later to maint).
   (merge 8882ee9d68 la/mailmap-entry later to maint).
   (merge 44bdba2fa6 rs/no-openssl-compilation-fix-on-macos later to maint).
   (merge f412d72c19 yb/replay-doc-linkfix later to maint).
   (merge 5da40be8d7 xx/rfc2822-date-format-in-doc later to maint).

----------------------------------------------------------------

Changes since v2.44.0 are as follows:

Ahelenia Ziemiańska (1):
      grep: improve errors for unmatched ( and )

Alexander Shopov (4):
      transport-helper.c: trivial fix of error message
      builtin/remote.c: trivial fix of error message
      builtin/clone.c: trivial fix of message
      revision.c: trivial fix to message

Aryan Gupta (1):
      tests: modernize the test script t0010-racy-git.sh

Beat Bolli (25):
      completion: use awk for filtering the config entries
      date: make "iso-strict" conforming for the UTC timezone
      t0006: add more tests with a negative TZ offset
      doc: avoid redundant use of cat
      contrib/subtree/t: avoid redundant use of cat
      t/lib-cvs.sh: avoid redundant use of cat
      t/annotate-tests.sh: avoid redundant use of cat
      t/perf: avoid redundant use of cat
      t/t0*: avoid redundant uses of cat
      t/t1*: avoid redundant uses of cat
      t/t3*: avoid redundant uses of cat
      t/t4*: avoid redundant uses of cat
      t/t5*: avoid redundant uses of cat
      t/t6*: avoid redundant uses of cat
      t/t7*: avoid redundant use of cat
      t/t8*: avoid redundant use of cat
      t/t9*: avoid redundant uses of cat
      t/t1*: merge a "grep | sed" pipeline
      t/t3*: merge a "grep | awk" pipeline
      t/t4*: merge a "grep | sed" pipeline
      t/t5*: merge a "grep | sed" pipeline
      t/t8*: merge "grep | sed" pipelines
      t/t9*: merge "grep | sed" pipelines
      contrib/coverage-diff: avoid redundant pipelines
      git-quiltimport: avoid an unnecessary subshell

Bo Anderson (5):
      t/lib-credential: clean additional credential
      osxkeychain: replace deprecated SecKeychain API
      osxkeychain: erase all matching credentials
      osxkeychain: erase matching passwords only
      osxkeychain: store new attributes

Brian C Tracy (1):
      fuzz: add fuzzer for config parsing

Brian Lyles (13):
      docs: clarify file options in git-config `--edit`
      docs: fix typo in git-config `--default`
      docs: correct trailer `key_value_separator` description
      docs: adjust trailer `separator` and `key_value_separator` language
      pretty: update tests to use `test_config`
      pretty: find pretty formats case-insensitively
      docs: address inaccurate `--empty` default with `--exec`
      docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
      rebase: update `--empty=ask` to `--empty=stop`
      sequencer: handle unborn branch with `--allow-empty`
      sequencer: do not require `allow_empty` for redundant commit options
      cherry-pick: enforce `--keep-redundant-commits` incompatibility
      cherry-pick: add `--empty` for more robust redundant commit handling

Chandra Pratap (2):
      apply: ignore working tree filemode when !core.filemode
      t9146: replace test -d/-e/-f with appropriate test_path_is_* function

Christian Couder (5):
      revision: clarify a 'return NULL' in get_reference()
      oidset: refactor oidset_insert_from_set()
      t6022: fix 'test' style and 'even though' typo
      rev-list: allow missing tips with --missing=[print|allow*]
      revision: fix --missing=[print|allow*] for annotated tags

Derrick Stolee (1):
      fetch: return when parsing submodule.recurse

Dirk Gouders (6):
      Documentation/user-manual.txt: example for generating object hashes
      MyFirstObjectWalk: use additional arg in config_fn_t
      MyFirstObjectWalk: fix misspelled "builtins/"
      MyFirstObjectWalk: fix filtered object walk
      MyFirstObjectWalk: fix description for counting omitted objects
      MyFirstObjectWalk: add stderr to pipe processing

Dragan Simic (8):
      documentation: send-email: use camel case consistently
      config: minor addition of whitespace
      config: really keep value-internal whitespace verbatim
      t1300: add more tests for whitespace and inline comments
      config.txt: describe handling of whitespace further
      grep docs: describe --recurse-submodules further and improve formatting a bit
      grep docs: describe --no-index further and improve formatting a bit
      config: fix some small capitalization issues, as spotted

Eric Sunshine (2):
      docs: sort configuration variable groupings alphabetically
      test-lib: fix non-functioning GIT_TEST_MAINT_SCHEDULER fallback

Eric W. Biederman (23):
      object-file-convert: stubs for converting from one object format to another
      oid-array: teach oid-array to handle multiple kinds of oids
      object-names: support input of oids in any supported hash
      repository: add a compatibility hash algorithm
      loose: compatibilty short name support
      object-file: update the loose object map when writing loose objects
      object-file: add a compat_oid_in parameter to write_object_file_flags
      commit: convert mergetag before computing the signature of a commit
      commit: export add_header_signature to support handling signatures on tags
      tag: sign both hashes
      object: factor out parse_mode out of fast-import and tree-walk into in object.h
      object-file-convert: don't leak when converting tag objects
      object-file-convert: convert commits that embed signed tags
      object-file: update object_info_extended to reencode objects
      rev-parse: add an --output-object-format parameter
      builtin/cat-file: let the oid determine the output algorithm
      tree-walk: init_tree_desc take an oid to get the hash algorithm
      object-file: handle compat objects in check_object_signature
      builtin/ls-tree: let the oid determine the output algorithm
      test-lib: compute the compatibility hash so tests may use it
      t1006: rename sha1 to oid
      t1006: test oid compatibility with cat-file
      t1016-compatObjectFormat: add tests to verify the conversion between objects

Eugenio Gigante (1):
      add: use unsigned type for collection of bits

Florian Schmidt (1):
      wt-status: don't find scissors line beyond buf len

Ghanshyam Thakkar (5):
      add-patch: classify '@' as a synonym for 'HEAD'
      add -p tests: remove PERL prerequisites
      setup: remove unnecessary variable
      builtin/commit: error out when passing untracked path with -i
      builtin/add: error out when passing untracked path with -u

Haritha D (1):
      build: support z/OS (OS/390).

Harmen Stoppels (1):
      rebase: make warning less passive aggressive

Jakub Wilk (1):
      git-remote.txt: fix typo

Jean-Noël Avila (17):
      doc: git-rev-parse: enforce command-line description syntax
      doc: close unclosed angle-bracket of a placeholder in git-clone doc
      doc: end sentences with full-stop
      doc: clarify the format of placeholders
      doc: git-init: format verbatim parts
      doc: git-init: format placeholders
      doc: git-init: rework definition lists
      doc: git-init: rework config item init.templateDir
      doc: git-clone: format verbatim words
      doc: git-clone: format placeholders
      doc: format alternatives in synopsis
      doc: fix some placeholders formating
      doc: rework CodingGuidelines with new formatting rules
      doc: allow literal and emphasis format in doc vs help tests
      doc: git-init: apply new documentation formatting guidelines
      doc: git-clone: apply new documentation formatting guidelines
      doc: git-clone: do not autoreference the manpage in itself

Jeff Hostetler (17):
      name-hash: add index_dir_find()
      t7527: add case-insensitve test for FSMonitor
      fsmonitor: refactor refresh callback on directory events
      fsmonitor: clarify handling of directory events in callback helper
      fsmonitor: refactor refresh callback for non-directory events
      dir: create untracked_cache_invalidate_trimmed_path()
      fsmonitor: refactor untracked-cache invalidation
      fsmonitor: move untracked-cache invalidation into helper functions
      fsmonitor: return invalidated cache-entry count on directory event
      fsmonitor: remove custom loop from non-directory path handler
      fsmonitor: return invalidated cache-entry count on non-directory event
      fsmonitor: trace the new invalidated cache-entry count
      fsmonitor: refactor bit invalidation in refresh callback
      fsmonitor: support case-insensitive events
      t0211: demonstrate missing 'def_param' events for certain commands
      trace2: avoid emitting 'def_param' set more than once
      trace2: emit 'def_param' set with 'cmd_name' event

Jeff King (51):
      t0303: check that helper_test_clean removes all credentials
      userdiff: skip textconv caching when not in a repository
      Revert "refs: allow @{n} to work with n-sized reflog"
      get_oid_basic(): special-case ref@{n} for oldest reflog entry
      read_ref_at(): special-case ref@{0} for an empty reflog
      upload-pack: drop separate v2 "haves" array
      upload-pack: switch deepen-not list to an oid_array
      upload-pack: use oidset for deepen_not list
      upload-pack: use a strmap for want-ref lines
      upload-pack: accept only a single packfile-uri line
      upload-pack: always turn off save_commit_buffer
      upload-pack: use PARSE_OBJECT_SKIP_HASH_CHECK in more places
      upload-pack: free tree buffers after parsing
      upload-pack: use repository struct to get config
      upload-pack: centralize setup of sideband-all config
      upload-pack: use existing config mechanism for advertisement
      upload-pack: only accept packfile-uris if we advertised it
      doc/gitremote-helpers: fix missing single-quote
      config: forbid newline as core.commentChar
      strbuf: simplify comment-handling in add_lines() helper
      strbuf: avoid static variables in strbuf_add_commented_lines()
      commit: refactor base-case of adjust_comment_line_char()
      strbuf: avoid shadowing global comment_line_char name
      environment: store comment_line_char as a string
      strbuf: accept a comment string for strbuf_stripspace()
      strbuf: accept a comment string for strbuf_commented_addf()
      strbuf: accept a comment string for strbuf_add_commented_lines()
      prefer comment_line_str to comment_line_char for printing
      find multi-byte comment chars in NUL-terminated strings
      find multi-byte comment chars in unterminated buffers
      sequencer: handle multi-byte comment characters when writing todo list
      wt-status: drop custom comment-char stringification
      environment: drop comment_line_char compatibility macro
      config: allow multi-byte core.commentChar
      shortlog: stop setting pp.print_email_subject
      pretty: split oneline and email subject printing
      pretty: drop print_email_subject flag
      log: do not set up extra_headers for non-email formats
      format-patch: return an allocated string from log_write_email_headers()
      format-patch: simplify after-subject MIME header handling
      doc/gitremote-helpers: fix more missing single-quotes
      transport-helper: use write helpers more consistently
      transport-helper: drop "object-format <algo>" option
      transport-helper: send "true" value for object-format option
      contrib: drop hg-to-git script
      format-patch: fix leak of empty header string
      rebase: use child_process_clear() to clean
      config: add core.commentString
      http: reset POSTFIELDSIZE when clearing curl handle
      INSTALL: bump libcurl version to 7.21.3
      remote-curl: add Transfer-Encoding header only for older curl

Jiamu Sun (1):
      bugreport.c: fix a crash in `git bugreport` with `--no-suffix` option

Johannes Schindelin (22):
      merge-tree: accept 3 trees as arguments
      merge-tree: fail with a non-zero exit code on missing tree objects
      merge-ort: do check `parse_tree()`'s return value
      t4301: verify that merge-tree fails on missing blob objects
      Always check `parse_tree*()`'s return value
      cache-tree: avoid an unnecessary check
      fill_tree_descriptor(): mark error message for translation
      neue: remove a bogus empty file
      commit-reach(paint_down_to_common): plug two memory leaks
      commit-reach(repo_in_merge_bases_many): optionally expect missing commits
      commit-reach(repo_in_merge_bases_many): report missing commits
      commit-reach(paint_down_to_common): prepare for handling shallow commits
      commit-reach(paint_down_to_common): start reporting errors
      commit-reach(merge_bases_many): pass on "missing commits" errors
      commit-reach(get_merge_bases_many_0): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases): pass on "missing commits" errors
      commit-reach(get_octopus_merge_bases): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many_dirty): pass on errors
      merge-recursive: prepare for `merge_submodule()` to report errors
      merge-ort/merge-recursive: do report errors in `merge_submodule()`
      merge-tree: fix argument type of the `--merge-base` option

John Cai (1):
      t5300: fix test_with_bad_commit()

Jonas Wunderlich (1):
      doc: status.showUntrackedFiles does not take "false"

Josh Triplett (2):
      commit: avoid redundant scissor line with --cleanup=scissors -v
      commit: unify logic to avoid multiple scissors lines when merging

Julio Bacellari (1):
      doc: remove outdated information about interactive.singleKey

Junio C Hamano (63):
      apply: correctly reverse patch's pre- and post-image mode bits
      apply: code simplification
      t9210: do not rely on lazy fetching to fail
      git: --no-lazy-fetch option
      doc: add shortcut to "am --whitespace=<action>"
      doc: apply the new placeholder rules to git-add documentation
      compat: drop inclusion of <git-compat-util.h>
      Start the 2.45 cycle
      git: document GIT_NO_REPLACE_OBJECTS environment variable
      doc: clarify the wording on <git-compat-util.h> requirement
      git: extend --no-lazy-fetch to work across subprocesses
      The second batch
      The third batch
      test_i18ngrep: hard deprecate and forbid its use
      unpack: replace xwrite() loop with write_in_full()
      sideband: avoid short write(2)
      repack: check error writing to pack-objects subprocess
      clean: further clean-up of implementation around "--force"
      The fourth batch
      The fifth batch
      setup: notice more types of implicit bare repositories
      The sixth batch
      status: unify parsing of --untracked= and status.showUntrackedFiles
      status: allow --untracked=false and friends
      The seventh batch
      The eighth batch
      config: fix --comment formatting
      config: allow tweaking whitespace between value and comment
      diff.*Prefix: use camelCase in the doc and test titles
      The ninth batch
      apply: parse names out of "diff --git" more carefully
      The tenth batch
      The eleventh batch
      SubmittingPatches: release-notes entry experiment
      The twelfth batch
      t4126: make sure a directory with SP at the end is usable
      t4126: fix "funny directory name" test on Windows (again)
      advice: omit trailing whitespace
      checkout: omit "tracking" information on a detached HEAD
      The thirteenth batch
      t2104: style fixes
      The fourteenth batch
      revision: optionally record matches with pathspec elements
      The fifteenth batch
      CodingGuidelines: describe "export VAR=VAL" rule
      CodingGuidelines: quote assigned value in 'local var=$val'
      t: local VAR="VAL" (quote positional parameters)
      t: local VAR="VAL" (quote command substitution)
      t: local VAR="VAL" (quote ${magic-reference})
      t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
      t0610: local VAR="VAL" fix
      t1016: local VAR="VAL" fix
      config: do not leak excludes_file
      Makefile(s): do not enforce "all indents must be done with tab"
      The sixteenth batch
      t2104: style fixes
      The seventeenth batch
      The eighteenth batch
      The ninteenth batch
      The twentieth batch
      Git 2.45-rc0
      A bit more topics before -rc1
      Git 2.45-rc1

Justin Tobler (3):
      reftable/stack: expose option to disable auto-compaction
      reftable/stack: add env to disable autocompaction
      reftable/stack: use geometric table compaction

Karthik Nayak (7):
      refs: introduce `is_pseudoref()` and `is_headref()`
      refs: extract out `loose_fill_ref_dir_regular_file()`
      refs: introduce `refs_for_each_include_root_refs()`
      ref-filter: rename 'FILTER_REFS_ALL' to 'FILTER_REFS_REGULAR'
      for-each-ref: add new option to include root refs
      update-ref: use {old,new}-oid instead of {old,new}value
      githooks: use {old,new}-oid instead of {old,new}-value

Kipras Melnikovas (1):
      mergetools: vimdiff: use correct tool's name when reading mergetool config

Kristoffer Haugsbakk (9):
      column: disallow negative padding
      column: guard against negative padding
      gitcli: drop mention of “non-dashed form”
      config: document `core.commentChar` as ASCII-only
      t3200: improve test style
      advice: make all entries stylistically consistent
      advice: use backticks for verbatim
      advice: use double quotes for regular quoting
      branch: advise about ref syntax rules

Linus Arver (15):
      trailer: free trailer_info _after_ all related usage
      shortlog: add test for de-duplicating folded trailers
      trailer: rename functions to use 'trailer'
      trailer: reorder format_trailers_from_commit() parameters
      trailer: move interpret_trailers() to interpret-trailers.c
      trailer_info_get(): reorder parameters
      format_trailers(): use strbuf instead of FILE
      format_trailer_info(): move "fast path" to caller
      format_trailers_from_commit(): indirectly call trailer_info_get()
      format_trailer_info(): use trailer_item objects
      format_trailer_info(): drop redundant unfold_value()
      format_trailer_info(): append newline for non-trailer lines
      trailer: begin formatting unification
      trailer: finish formatting unification
      mailmap: change primary address for Linus Arver

M Hickford (1):
      libsecret: retrieve empty password

Marcel Röthke (1):
      rerere: fix crashes due to unmatched opening conflict markers

Matthias Aßhauer (1):
      Win32: detect unix socket support at runtime

Max Gautier (1):
      editorconfig: add Makefiles to "text files"

Michael Lohmann (2):
      revision: ensure MERGE_HEAD is a ref in prepare_show_merge
      revision: implement `git log --merge` also for rebase/cherry-pick/revert

Orgad Shaneh (1):
      docs: remove duplicate entry and fix typo in 2.45 changelog

Patrick Steinhardt (99):
      refs: introduce reftable backend
      ci: add jobs to test with the reftable backend
      refs/reftable: fix leak when copying reflog fails
      reftable/record: introduce function to compare records by key
      reftable/merged: allocation-less dropping of shadowed records
      reftable/merged: skip comparison for records of the same subiter
      reftable/pq: allocation-less comparison of entry keys
      reftable/block: swap buffers instead of copying
      reftable/record: don't try to reallocate ref record name
      reftable/reader: add comments to `table_iter_next()`
      t: move tests exercising the "files" backend
      t0410: convert tests to use DEFAULT_REPO_FORMAT prereq
      t1400: exercise reflog with gaps with reftable backend
      t1404: make D/F conflict tests compatible with reftable backend
      t1405: remove unneeded cleanup step
      t2011: exercise D/F conflicts with HEAD with the reftable backend
      t7003: ensure filter-branch prunes reflogs with the reftable backend
      git-difftool--helper: honor `--trust-exit-code` with `--dir-diff`
      dir-iterator: pass name to `prepare_next_entry_data()` directly
      dir-iterator: support iteration in sorted order
      refs/files: sort reflogs returned by the reflog iterator
      refs/files: sort merged worktree and common reflogs
      refs: always treat iterators as ordered
      refs: drop unused params from the reflog iterator callback
      refs: stop resolving ref corresponding to reflogs
      builtin/reflog: introduce subcommand to list reflogs
      builtin/clone: allow remote helpers to detect repo
      refs/reftable: don't fail empty transactions in repo without HEAD
      reftable/pq: use `size_t` to track iterator index
      reftable/merged: make `merged_iter` structure private
      reftable/merged: advance subiter on subsequent iteration
      reftable/merged: make subiters own their records
      reftable/merged: remove unnecessary null check for subiters
      reftable/merged: handle subiter cleanup on close only
      reftable/merged: circumvent pqueue with single subiter
      reftable/merged: avoid duplicate pqueue emptiness check
      reftable/record: reuse refname when decoding
      reftable/record: reuse refname when copying
      reftable/record: decode keys in place
      reftable: allow inlining of a few functions
      refs/reftable: precompute prefix length
      refs/reftable: reload correct stack when creating reflog iter
      reftable/record: convert old and new object IDs to arrays
      reftable/record: avoid copying author info
      reftable/record: reuse refnames when decoding log records
      reftable/record: reuse message when decoding log records
      reftable/record: use scratch buffer when decoding records
      refs/reftable: track last log record name via strbuf
      t0610: remove unused variable assignment
      lockfile: report when rollback fails
      reftable/stack: register new tables as tempfiles
      reftable/stack: register lockfiles during compaction
      reftable/stack: register compacted tables as tempfiles
      reftable/record: fix memory leak when decoding object records
      reftable/block: fix binary search over restart counter
      t5601: exercise clones with "includeIf.*.onbranch"
      reftable: fix tests being broken by NFS' delete-after-close semantics
      t7800: improve test descriptions with empty arguments
      t7800: use single quotes for test bodies
      t/README: document how to loop around test cases
      reftable/stack: fix error handling in `reftable_stack_init_addition()`
      reftable/error: discern locked/outdated errors
      reftable/stack: use error codes when locking fails during compaction
      reftable/stack: gracefully handle failed auto-compaction due to locks
      refs/reftable: print errors on compaction failure
      t/helper: drop pack-refs wrapper
      refs: move `struct pack_refs_opts` to where it's used
      refs: remove `PACK_REFS_ALL` flag
      refs/reftable: expose auto compaction via new flag
      builtin/pack-refs: release allocated memory
      builtin/pack-refs: introduce new "--auto" flag
      builtin/gc: move `struct maintenance_run_opts`
      t6500: extract objects with "17" prefix
      builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
      builtin/gc: pack refs when using `git maintenance run --auto`
      reftable/basics: fix return type of `binsearch()` to be `size_t`
      reftable/basics: improve `binsearch()` test
      reftable/refname: refactor binary search over refnames
      reftable/block: refactor binary search over restart points
      reftable/block: fix error handling when searching restart points
      reftable/record: extract function to decode key lengths
      reftable/block: avoid decoding keys when searching restart points
      t0610: make `--shared=` tests reusable
      t0610: execute git-pack-refs(1) with specified umask
      reftable/block: rename `block_reader_start()`
      reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
      reftable/block: better grouping of functions
      reftable/block: introduce `block_reader_release()`
      reftable/block: move ownership of block reader into `struct table_iter`
      reftable/reader: iterate to next block in place
      reftable/block: reuse uncompressed blocks
      reftable/block: open-code call to `uncompress2()`
      reftable/block: reuse `zstream` state on inflation
      reftable/block: avoid copying block iterators on seek
      pack-bitmap: gracefully handle missing BTMP chunks
      run-command: introduce function to prepare auto-maintenance process
      builtin/receive-pack: convert to use git-maintenance(1)
      docs: improve changelog entry for `git pack-refs --auto`
      docs: address typos in Git v2.45 changelog

Peter Hutterer (1):
      diff: add diff.srcPrefix and diff.dstPrefix configuration variables

Peter Krefting (1):
      bisect: report the found commit with "show"

Philippe Blain (5):
      merge-ort: turn submodule conflict suggestions into an advice
      ci(github): make Windows test artifacts name unique
      sequencer: allow disabling conflict advice
      builtin/am: allow disabling conflict advice
      t/README: mention test files are make targets

Phillip Wood (9):
      rebase -i: stop setting GIT_CHERRY_PICK_HELP
      xdiff-interface: refactor parsing of merge.conflictstyle
      merge-ll: introduce LL_MERGE_OPTIONS_INIT
      merge options: add a conflict style member
      checkout: cleanup --conflict=<style> parsing
      checkout: fix interaction between --conflict and --merge
      t3428: modernize test setup
      t3428: use test_commit_message
      t3428: restore coverage for "apply" backend

Pi Fisher (1):
      typo: replace 'commitish' with 'committish'

Ralph Seichter (1):
      config: add --comment option to add a comment

René Scharfe (31):
      use xstrncmpz()
      fetch: convert strncmp() with strlen() to starts_with()
      mem-pool: add mem_pool_strfmt()
      name-rev: use mem_pool_strfmt()
      submodule: use strvec_pushf() for --submodule-prefix
      t-ctype: allow NUL anywhere in the specification string
      t-ctype: simplify EOF check
      t-ctype: align output of i
      t-ctype: avoid duplicating class names
      parse-options: recognize abbreviated negated option with arg
      parse-options: set arg of abbreviated option lazily
      parse-options: factor out register_abbrev() and struct parsed_option
      parse-options: detect ambiguous self-negation
      parse-options: normalize arg and long_name before comparison
      parse-options: rearrange long_name matching code
      t-prio-queue: shorten array index message
      t-prio-queue: check result array bounds
      factor out strbuf_expand_bad_format()
      cat-file: use strbuf_expand_bad_format()
      midx: use strvec_pushf() for pack-objects base name
      mem-pool: use st_add() in mem_pool_strvfmt()
      imap-send: use xsnprintf to format command
      t-prio-queue: simplify using compound literals
      apply: avoid fixed-size buffer in create_one_file()
      path: remove mksnpath()
      apply: don't leak fd on fdopen() error
      usage: report vsnprintf(3) failure
      date: make DATE_MODE thread-safe
      git-compat-util: fix NO_OPENSSL on current macOS
      imap-send: increase command size limit
      apply: avoid using fixed-size buffer in write_out_one_reject()

Richard Macklin (1):
      rebase: fix typo in autosquash documentation

Rubén Justo (14):
      tag: error when git-column fails
      completion: fix __git_complete_worktree_paths
      completion: reflog with implicit "show"
      completion: reflog show <log-options>
      completion: introduce __git_find_subcommand
      completion: factor out __git_resolve_builtins
      completion: reflog subcommands and options
      checkout: plug some leaks in git-restore
      add-patch: introduce 'p' in interactive-patch
      add-patch: do not print hunks repeatedly
      add: use advise_if_enabled for ADVICE_ADD_IGNORED_FILE
      add: use advise_if_enabled for ADVICE_ADD_EMPTY_PATHSPEC
      add: use advise_if_enabled for ADVICE_ADD_EMBEDDED_REPO
      launch_editor: waiting message on error

SZEDER Gábor (1):
      upload-pack: don't send null character in abort message to the client

Sergey Organov (1):
      clean: improve -n and -f implementation and documentation

Steven Jeuris (1):
      userdiff: better method/property matching for C#

Taylor Blau (8):
      Documentation/config/pack.txt: fix broken AsciiDoc mark-up
      upload-pack: disallow object-info capability by default
      midx-write: move writing-related functions from midx.c
      midx-write.c: factor out common want_included_pack() routine
      midx-write.c: check count of packs to repack after grouping
      midx-write.c: use `--stdin-packs` when repacking
      t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `
      Makefile(s): avoid recipe prefix in conditional statements

Thalia Archibald (8):
      fast-import: tighten path unquoting
      fast-import: directly use strbufs for paths
      fast-import: allow unquoted empty path for root
      fast-import: remove dead strbuf
      fast-import: improve documentation for path quoting
      fast-import: document C-style escapes for paths
      fast-import: forbid escaped NUL in paths
      fast-import: make comments more precise

Ville Skyttä (2):
      completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode
      completion: protect prompt against unset SHOWUPSTREAM in nounset mode

Vincenzo Mezzela (1):
      t7301: use test_path_is_(missing|file)

Xing Xin (1):
      Documentation: fix typos describing date format

Yehezkel Bernat (1):
      Documentation: fix linkgit reference

brian m. carlson (7):
      loose: add a mapping between SHA-1 and SHA-256 for loose objects
      commit: write commits for both hashes
      cache: add a function to read an OID of a specific algorithm
      object-file-convert: add a function to convert trees between algorithms
      object-file-convert: convert tag objects when writing
      object-file-convert: convert commit objects when writing
      repository: implement extensions.compatObjectFormat

shejialuo (1):
      t9117: prefer test_path_* helper functions

Đoàn Trần Công Danh (1):
      t9604: Fix test for musl libc and new Debian



^ permalink raw reply	[relevance 2%]

* [PATCH] Documentation/RelNotes/2.45.0.txt: fix typo
@ 2024-04-24 16:27 12% Taylor Blau
  0 siblings, 0 replies; 200+ results
From: Taylor Blau @ 2024-04-24 16:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 Documentation/RelNotes/2.45.0.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/RelNotes/2.45.0.txt b/Documentation/RelNotes/2.45.0.txt
index 96629b44f9..e46bf6da07 100644
--- a/Documentation/RelNotes/2.45.0.txt
+++ b/Documentation/RelNotes/2.45.0.txt
@@ -251,7 +251,7 @@ Fixes since v2.44
    (merge 5edd126720 jk/reflog-special-cases-fix later to maint).
 
  * An error message from "git upload-pack", which responds to "git
-   fetch" requests, had a trialing NUL in it, which has been
+   fetch" requests, had a trailing NUL in it, which has been
    corrected.
    (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).
 

base-commit: 10f1281498467654abdb13c6c7c7b23af4b97aeb
-- 
2.44.0.704.g10f12814984


^ permalink raw reply related	[relevance 12%]

* Re: [PATCH v3 0/8] refs: add symref support to 'git-update-ref'
  2024-04-23 22:03  2%     ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Jeff King
@ 2024-04-24 16:25  0%       ` Karthik Nayak
  0 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-24 16:25 UTC (permalink / raw)
  To: Jeff King; +Cc: chris.torek, git, gitster, ps

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

Jeff King <peff@peff.net> writes:

> On Tue, Apr 23, 2024 at 11:28:10PM +0200, Karthik Nayak wrote:
>
>> Changes from v2:
>> 1. We no longer have separate commands for symrefs, instead the regular
>> commands learn to parse 'ref:<target>' as symref targets. This reduces
>> the code in this series. Thanks Patrick for the suggestion.
>
> Hmm. I can see how this makes things a lot simpler, but it introduces an
> ambiguity, since you can pass full ref expressions to "update-ref" (like
> "ref:foo" to find the "foo" entry in ref^{tree}). I see that you only
> kick in the symref "ref:" handling if the regular oid lookup failed, so
> there's no backwards-compatibility issue (anything that used to work
> will still take precedence, and the new code only runs when the old code
> would have reported an error).
>
> But I wonder if it would let somebody cause mischief in a repository
> they can push to, but which may get updates from other sources. For
> example, imagine a forge like GitLab runs the equivalent of:
>
>   echo "create refs/whatever ref:refs/heads/main" |
>   git update-ref --stdin
>
> as part of some system process. Now if I push up "refs/heads/ref" that
> contains the path "refs/heads/main" in its tree, that will take
> precedence, causing the system process to do something it did not
> expect.
>
> I think you'd have to pile on a lot of assumptions to get any kind of
> security problem. Something like:
>
>  1. The system has a hidden ref namespace like refs/gitlab that normal
>     remote push/fetch users are not allowed to read/write to.
>
>  2. The system tries to make a symlink within that namespace. Say,
>     "refs/gitlab/metadata/HEAD" to point to
>     "refs/gitlab/metadata/branches/main" or something.
>
>  3. The user pushes up "refs/heads/ref" with a tree that contains
>     "refs/gitlab/metadata/branches/main". Now when (2) happens, the
>     hidden ref points to user-controlled data.
>
> That's pretty convoluted. But we can avoid it entirely if there's no
> ambiguity in the protocol at all.
>
> -Peff


Thanks Peff, that is indeed something I totally missed thinking about.

This also brings light onto the previous versions we were considering:

    symref-update SP <ref> SP <new-target> [SP (<old-target> | <old-oid>)] LF

There is also some ambiguity here which we missed, especially when we
support dangling refs. If we're updating a dangling ref <ref>, and we
provide an old value. Then there is uncertainty around whether the
provided value is actually a <old-target> or if it's an <old-oid>.

For non dangling ref symref, we first parse it as an <old-target> and
since the <old-target> would exist, we can move on.

So I see two ways to go about this,

1. In the symref-update function, we need to parse and see if <ref> is a
regular ref or a symref, if it is symref, we simply set the provided old
value as <old-target>, if not, we set it as <old-oid>. This seems clunky
because we'll be parsing the ref and trying to understand its type in
'update-ref.c', before the actual update.

2. We change the syntax to something like

    symref-update SP <ref> SP <new-ref> [SP (ref <old-target> | oid
<old-oid>)] LF

this would remove any ambiguity since the user specifies the data type
they're providing.

Overall, I think it is best to discard this version and I will push v4
with the older schematics of introducing new commands.

I'm currently considering going ahead with the [2], but will wait for a
day or two to consider other opinions.

Also on a sidenote, it's worth considering that with the direction of
[2], we could also extrapolate to introduce {verify, update, create,
delete} v2, which support both symrefs and regular refs. But require
explicit types from the user:

    update-v2 SP <ref> NUL (oid <new-oid> | ref <new-target>) NUL
[(oid <old-oid> | ref <old-target>)] NUL
	create-v2 SP <ref> NUL (oid <new-oid> | ref <new-target>) NUL
	delete-v2 SP <ref> NUL [(oid <old-oid> | ref <old-target>)] NUL
	verify-v2 SP <ref> NUL [(oid <old-oid> | ref <old-target>)] NUL

This is similar to the v3 patches I've currently sent out, in that it
would also allow cross operations between regular refs and symrefs.

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

^ permalink raw reply	[relevance 0%]

* [PATCH v3 0/2] Use a "best effort" strategy in scheduled maintenance
  2024-04-18 12:53  3% ` [PATCH v2 " Johannes Schindelin via GitGitGadget
@ 2024-04-24 16:14  3%   ` Johannes Schindelin via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Johannes Schindelin via GitGitGadget @ 2024-04-24 16:14 UTC (permalink / raw)
  To: git; +Cc: Eric Sunshine, Patrick Steinhardt, Jeff King, Johannes Schindelin

Over in https://github.com/microsoft/git/issues/623, it was pointed out that
scheduled maintenance will error out when it encounters a missing
repository. The scheduled maintenance should exit with an error, all right,
but what about the remaining repositories for which maintenance was
scheduled, and that may not be missing?

This patch series addresses this by introducing a new for-each-repo option
and then using it in the command that is run via scheduled maintenance.

Changes since v2 (thanks Patrick, Jeff and Junio):

 * When not passing the new --keep-going option, the exit code is the same
   as before.
 * Clarified in the documentation of the --keep-going option that it is 0
   for success, 1 for failure, no matter the exact exit code of the failing
   command invocation(s).

Changes since v1 (thanks Eric!):

 * Changed the option's documentation to reflect the current state (instead
   of the original design)
 * Fixed grammar issues

Johannes Schindelin (2):
  for-each-repo: optionally keep going on an error
  maintenance: running maintenance should not stop on errors

 Documentation/git-for-each-repo.txt |  9 +++++++++
 builtin/for-each-repo.c             | 13 +++++++++++--
 builtin/gc.c                        |  7 ++++---
 t/t0068-for-each-repo.sh            | 16 ++++++++++++++++
 t/t7900-maintenance.sh              |  6 +++---
 5 files changed, 43 insertions(+), 8 deletions(-)


base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1719%2Fdscho%2Ffor-each-repo-stop-on-error-2.44-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1719/dscho/for-each-repo-stop-on-error-2.44-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1719

Range-diff vs v2:

 1:  abd796894c8 ! 1:  39ee6386aab for-each-repo: optionally keep going on an error
     @@ Commit message
          repository, still setting the exit code to indicate an error occurred.
      
          Helped-by: Eric Sunshine <sunshine@sunshineco.com>
     +    Helped-by: Patrick Steinhardt <ps@pks.im>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
      
       ## Documentation/git-for-each-repo.txt ##
     @@ Documentation/git-for-each-repo.txt: These config values are loaded from system,
      +	Continue with the remaining repositories if the command failed
      +	on a repository. The exit code will still indicate that the
      +	overall operation was not successful.
     +++
     ++Note that the exact exit code of the failing command is not passed
     ++through as the exit code of the `for-each-repo` command: If the command
     ++failed in any of the specified repositories, the overall exit code will
     ++be 1.
       
       SUBPROCESS BEHAVIOR
       -------------------
     @@ builtin/for-each-repo.c: int cmd_for_each_repo(int argc, const char **argv, cons
       
      -	for (i = 0; !result && i < values->nr; i++)
      -		result = run_command_on_repo(values->items[i].string, argc, argv);
     -+	for (i = 0; (keep_going || !result) && i < values->nr; i++)
     -+		if (run_command_on_repo(values->items[i].string, argc, argv))
     ++	for (i = 0; i < values->nr; i++) {
     ++		int ret = run_command_on_repo(values->items[i].string, argc, argv);
     ++		if (ret) {
     ++			if (!keep_going)
     ++					return ret;
      +			result = 1;
     ++		}
     ++	}
       
       	return result;
       }
 2:  1ae11553052 = 2:  540962859c5 maintenance: running maintenance should not stop on errors

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v3 0/8] refs: add symref support to 'git-update-ref'
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
  2024-04-23 21:28  7%     ` [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
  2024-04-23 21:28 12%     ` [PATCH v3 5/8] update-ref: support symrefs in the delete command Karthik Nayak
@ 2024-04-23 22:03  2%     ` Jeff King
  2024-04-24 16:25  0%       ` Karthik Nayak
  2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
  3 siblings, 1 reply; 200+ results
From: Jeff King @ 2024-04-23 22:03 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: chris.torek, git, gitster, ps

On Tue, Apr 23, 2024 at 11:28:10PM +0200, Karthik Nayak wrote:

> Changes from v2:
> 1. We no longer have separate commands for symrefs, instead the regular
> commands learn to parse 'ref:<target>' as symref targets. This reduces
> the code in this series. Thanks Patrick for the suggestion.

Hmm. I can see how this makes things a lot simpler, but it introduces an
ambiguity, since you can pass full ref expressions to "update-ref" (like
"ref:foo" to find the "foo" entry in ref^{tree}). I see that you only
kick in the symref "ref:" handling if the regular oid lookup failed, so
there's no backwards-compatibility issue (anything that used to work
will still take precedence, and the new code only runs when the old code
would have reported an error).

But I wonder if it would let somebody cause mischief in a repository
they can push to, but which may get updates from other sources. For
example, imagine a forge like GitLab runs the equivalent of:

  echo "create refs/whatever ref:refs/heads/main" |
  git update-ref --stdin

as part of some system process. Now if I push up "refs/heads/ref" that
contains the path "refs/heads/main" in its tree, that will take
precedence, causing the system process to do something it did not
expect.

I think you'd have to pile on a lot of assumptions to get any kind of
security problem. Something like:

 1. The system has a hidden ref namespace like refs/gitlab that normal
    remote push/fetch users are not allowed to read/write to.

 2. The system tries to make a symlink within that namespace. Say,
    "refs/gitlab/metadata/HEAD" to point to
    "refs/gitlab/metadata/branches/main" or something.

 3. The user pushes up "refs/heads/ref" with a tree that contains
    "refs/gitlab/metadata/branches/main". Now when (2) happens, the
    hidden ref points to user-controlled data.

That's pretty convoluted. But we can avoid it entirely if there's no
ambiguity in the protocol at all.

-Peff


^ permalink raw reply	[relevance 2%]

* [PATCH v3 5/8] update-ref: support symrefs in the delete command
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
  2024-04-23 21:28  7%     ` [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
@ 2024-04-23 21:28 12%     ` Karthik Nayak
  2024-04-23 22:03  2%     ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Jeff King
  2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
  3 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-23 21:28 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The 'delete' command in 'git-update-ref' allows users to delete `<ref>`
after verifying it exists with `<old-oid>`, if given. Extend this command
to alternatively take in `ref:<old-target>` which is used to verify if
the symbolic ref targets the provided `<old-target>` before deletion.
This will only work when used with the 'no-deref' mode as it doesn't
make sense to deref a symref during deletion.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-update-ref.txt | 10 +++++---
 builtin/fetch.c                  |  2 +-
 builtin/receive-pack.c           |  3 ++-
 builtin/update-ref.c             | 18 +++++++++----
 refs.c                           | 12 ++++++---
 refs.h                           |  4 ++-
 refs/files-backend.c             |  2 +-
 refs/reftable-backend.c          |  2 +-
 t/t1400-update-ref.sh            | 44 ++++++++++++++++++++++++++++++++
 9 files changed, 79 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 9f8c059944..f28b026cd7 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -63,7 +63,7 @@ performs all modifications together.  Specify commands of the form:
 
 	update SP <ref> SP <new-oid> [SP <old-oid>] LF
 	create SP <ref> SP <new-oid> LF
-	delete SP <ref> [SP <old-oid>] LF
+	delete SP <ref> [SP (<old-oid> | ref:<old-target>)] LF
 	verify SP <ref> [SP (<old-oid> | ref:<old-target>)] LF
 	option SP <opt> LF
 	start LF
@@ -84,7 +84,7 @@ quoting:
 
 	update SP <ref> NUL <new-oid> NUL [<old-oid>] NUL
 	create SP <ref> NUL <new-oid> NUL
-	delete SP <ref> NUL [<old-oid>] NUL
+	delete SP <ref> NUL [(<old-oid> | ref:<old-target>)] NUL
 	verify SP <ref> NUL [(<old-oid> | ref:<old-target>)] NUL
 	option SP <opt> NUL
 	start NUL
@@ -116,8 +116,10 @@ create::
 	exist.  The given <new-oid> may not be zero.
 
 delete::
-	Delete <ref> after verifying it exists with <old-oid>, if
-	given.  If given, <old-oid> may not be zero.
+	Delete <ref> after verifying it exists with <old-oid>, if given.
+	If given, <old-oid> may not be zero.  If instead, ref:<old-target>
+	is provided, verify that the symbolic ref <ref> targets
+	<old-target> before deleting it.
 
 verify::
 	Verify <ref> against <old-oid> but do not change it.  If
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 66840b7c5b..d02592efca 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1383,7 +1383,7 @@ static int prune_refs(struct display_state *display_state,
 		if (transaction) {
 			for (ref = stale_refs; ref; ref = ref->next) {
 				result = ref_transaction_delete(transaction, ref->name, NULL, 0,
-								"fetch: prune", &err);
+								NULL, "fetch: prune", &err);
 				if (result)
 					goto cleanup;
 			}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b150ef39a8..9a4667d57d 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_delete(transaction,
 					   namespaced_name,
 					   old_oid,
-					   0, "push", &err)) {
+					   0, NULL,
+					   "push", &err)) {
 			rp_error("%s", err.buf);
 			ret = "failed to delete";
 		} else {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 246167e835..cee7a5ebc0 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -274,6 +274,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 			     const char *next, const char *end)
 {
 	struct strbuf err = STRBUF_INIT;
+	struct strbuf old_target = STRBUF_INIT;
 	char *refname;
 	struct object_id old_oid;
 	int have_old;
@@ -282,26 +283,33 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 	if (!refname)
 		die("delete: missing <ref>");
 
-	if (parse_next_arg(&next, end, &old_oid, NULL,
-			   "delete", refname, PARSE_SHA1_OLD)) {
+	if (parse_next_arg(&next, end, &old_oid, &old_target,
+			   "delete", refname, PARSE_SHA1_OLD |
+			   PARSE_REFNAME_TARGETS)) {
 		have_old = 0;
 	} else {
-		if (is_null_oid(&old_oid))
+		if (!old_target.len && is_null_oid(&old_oid))
 			die("delete %s: zero <old-oid>", refname);
-		have_old = 1;
+		have_old = 1 && !old_target.len;
 	}
 
+	if (old_target.len && !(update_flags & REF_NO_DEREF))
+		die("delete %s: cannot operate on symrefs in deref mode", refname);
+
 	if (*next != line_termination)
 		die("delete %s: extra input: %s", refname, next);
 
 	if (ref_transaction_delete(transaction, refname,
 				   have_old ? &old_oid : NULL,
-				   update_flags, msg, &err))
+				   update_flags,
+				   old_target.len ? old_target.buf : NULL,
+				   msg, &err))
 		die("%s", err.buf);
 
 	update_flags = default_flags;
 	free(refname);
 	strbuf_release(&err);
+	strbuf_release(&old_target);
 }
 
 static void parse_cmd_verify(struct ref_transaction *transaction,
diff --git a/refs.c b/refs.c
index 0e1013b5ab..6b7c46bfd8 100644
--- a/refs.c
+++ b/refs.c
@@ -979,7 +979,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
 	transaction = ref_store_transaction_begin(refs, &err);
 	if (!transaction ||
 	    ref_transaction_delete(transaction, refname, old_oid,
-				   flags, msg, &err) ||
+				   flags, NULL, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ref_transaction_free(transaction);
@@ -1318,14 +1318,18 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_target,
+			   const char *msg,
 			   struct strbuf *err)
 {
 	if (old_oid && is_null_oid(old_oid))
 		BUG("delete called with old_oid set to zeros");
+	if (old_target && !(flags & REF_NO_DEREF))
+		BUG("delete cannot operate on symrefs with deref mode");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      NULL, NULL, flags,
+				      NULL, old_target, flags,
 				      msg, err);
 }
 
@@ -2752,7 +2756,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
 
 	for_each_string_list_item(item, refnames) {
 		ret = ref_transaction_delete(transaction, item->string,
-					     NULL, flags, msg, &err);
+					     NULL, flags, NULL, msg, &err);
 		if (ret) {
 			warning(_("could not delete reference %s: %s"),
 				item->string, err.buf);
diff --git a/refs.h b/refs.h
index 27b9aeaf54..4be4930f04 100644
--- a/refs.h
+++ b/refs.h
@@ -766,7 +766,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_target,
+			   const char *msg,
 			   struct strbuf *err);
 
 /*
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 53197fa3af..fc5037fe5a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2516,7 +2516,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
 	files_assert_main_repository(refs, "lock_ref_for_update");
 
-	if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
+	if ((update->flags & REF_HAVE_NEW) && ref_update_is_null_new_value(update))
 		update->flags |= REF_DELETING;
 
 	if (head_ref) {
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index a2474245aa..2b2cbca8c0 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1120,7 +1120,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
 		if (u->flags & REF_LOG_ONLY)
 			continue;
 
-		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+		if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) {
 			struct reftable_ref_record ref = {
 				.refname = (char *)u->refname,
 				.update_index = ts,
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1f2b63755a..cd1ad0d2ec 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1714,6 +1714,50 @@ do
 		test_cmp expect actual
 	'
 
+	test_expect_success "stdin ${type} delete symref fails without --no-deref" '
+		git symbolic-ref refs/heads/symref $a &&
+		create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" &&
+		test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
+		grep "fatal: delete refs/heads/symref: cannot operate on symrefs in deref mode" err
+	'
+
+	test_expect_success "stdin ${type} delete symref fails with no ref" '
+		create_stdin_buf ${type} "delete " &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		grep "fatal: delete: missing <ref>" err
+	'
+
+	test_expect_success "stdin ${type} delete symref fails with too many arguments" '
+		create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" "ref:$a" &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		if test "$type" = "-z"
+		then
+			grep "fatal: unknown command: ref:$a" err
+		else
+			grep "fatal: delete refs/heads/symref: extra input:  ref:$a" err
+		fi
+	'
+
+	test_expect_success "stdin ${type} delete symref fails with wrong old value" '
+		create_stdin_buf ${type} "delete refs/heads/symref" "ref:$m" &&
+		test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+		if test_have_prereq REFTABLE
+		then
+			grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err
+		else
+			grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}" err
+		fi &&
+		git symbolic-ref refs/heads/symref >expect &&
+		echo $a >actual &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "stdin ${type} delete symref works with right old value" '
+		create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" &&
+		git update-ref --stdin ${type} --no-deref <stdin &&
+		test_must_fail git rev-parse --verify -q $b
+	'
+
 done
 
 test_done
-- 
2.43.GIT



^ permalink raw reply related	[relevance 12%]

* [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update`
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
@ 2024-04-23 21:28  7%     ` Karthik Nayak
  2024-04-23 21:28 12%     ` [PATCH v3 5/8] update-ref: support symrefs in the delete command Karthik Nayak
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-23 21:28 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The `ref_transaction[_add]_update` functions obtain ref information and
flags to create a `ref_update` and add it to the transaction at hand.

To extend symref support in transactions, we need to also accept the
old and new ref targets and process it. In this commit, let's add the
required parameters to the function and modify all call sites.

The two parameters added are `new_target` and `old_target`. The
`new_target` is used to denote what the reference should point to when
the transaction is applied. Some functions allow this parameter to be
NULL, meaning that the reference is not changed.

The `old_target` denotes the value the reference must have before the
update. Some functions allow this parameter to be NULL, meaning that the
old value of the reference is not checked. A copy of this value is made
in the transaction.

The handling logic of these parameters will be added in consequent
commits as we add symref support to the existing 'git-update-ref'
commands.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  5 +++--
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  1 +
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  1 +
 builtin/update-ref.c    |  1 +
 refs.c                  | 22 +++++++++++++++++-----
 refs.h                  | 17 ++++++++++++++++-
 refs/files-backend.c    | 12 ++++++------
 refs/refs-internal.h    | 13 +++++++++++++
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  9 +++++----
 walker.c                |  2 +-
 14 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/branch.c b/branch.c
index e4a738fc7b..48af4c3ceb 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d32dd..297dfb91a1 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1675,7 +1675,8 @@ static void dump_tags(void)
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..66840b7c5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df14b6..b150ef39a8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..7690687b0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..40a65fdebc 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..21fdbf6ac8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..060a31616d 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg)
 {
 	struct ref_update *update;
@@ -1235,6 +1236,11 @@ struct ref_update *ref_transaction_add_update(
 	if (transaction->state != REF_TRANSACTION_OPEN)
 		BUG("update called for transaction that is not open");
 
+	if (old_oid && !is_null_oid(old_oid) && old_target)
+		BUG("Only one of old_oid and old_target should be non NULL");
+	if (new_oid && !is_null_oid(new_oid) && new_target)
+		BUG("Only one of new_oid and new_target should be non NULL");
+
 	FLEX_ALLOC_STR(update, refname, refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
@@ -1253,6 +1259,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err)
 {
@@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, new_target,
+				   old_target, msg);
 	return 0;
 }
 
@@ -1295,7 +1304,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1318,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1332,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
 				      NULL, old_oid,
+				      NULL, NULL,
 				      flags, NULL, err);
 }
 
@@ -1335,8 +1347,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
-	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+				   flags, msg, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index d278775e08..c792e13a64 100644
--- a/refs.h
+++ b/refs.h
@@ -648,6 +648,15 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  *         before the update. A copy of this value is made in the
  *         transaction.
  *
+ *     new_target -- the target reference that the reference will be
+ *         update to point to. This takes precedence over new_oid when
+ *         set. If the reference is a regular reference, it will be
+ *         converted to a symbolic reference.
+ *
+ *     old_target -- the reference that the reference must be pointing to.
+ *         Will only be taken into account when the reference is a symbolic
+ *         reference.
+ *
  *     flags -- flags affecting the update, passed to
  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
  *         REF_FORCE_CREATE_REFLOG. See those constants for more
@@ -713,7 +722,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  * beforehand. The old value is checked after the lock is taken to
  * prevent races. If the old value doesn't agree with old_oid, the
  * whole transaction fails. If old_oid is NULL, then the previous
- * value is not checked.
+ * value is not checked. If `old_target` is not NULL, treat the reference
+ * as a symbolic ref and validate that its target before the update is
+ * `old_target`. If the `new_target` is not NULL, then the reference
+ * will be updated to a symbolic ref which targets `new_target`.
+ * Together, these allow us to update between regular refs and symrefs.
  *
  * See the above comment "Reference transaction updates" for more
  * information.
@@ -722,6 +735,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_target,
+			   const char *old_target,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea0..e4d0aa3d41 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
+					   iter->oid, NULL, NULL, NULL,
 					   REF_NO_DEREF, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
@@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL, NULL);
 		}
 	}
 
@@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..3040d4797c 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,18 @@ struct ref_update {
 	 */
 	struct object_id old_oid;
 
+	/*
+	 * If set, point the reference to this value. This can also be
+	 * used to convert regular references to become symbolic refs.
+	 */
+	const char *new_target;
+
+	/*
+	 * If set and the reference is a symbolic ref, check that the
+	 * reference previously pointed to this value.
+	 */
+	const char *old_target;
+
 	/*
 	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
 	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
@@ -173,6 +185,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_target, const char *old_target,
 		const char *msg);
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c504..6104471199 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 2c19846385..af1b25692b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -616,7 +616,7 @@ static int fast_forward_to(struct repository *r,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
-				   null_oid() : from,
+				   null_oid() : from, NULL, NULL,
 				   0, sb.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
@@ -1248,7 +1248,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   NULL, NULL, 0, sb.buf, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3764,8 +3764,9 @@ static int do_label(struct repository *r, const char *name, int len)
 	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
-	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+	} else if (ref_transaction_update(transaction, ref_name.buf,
+					  &head_oid, NULL, NULL, NULL,
+					  0, msg.buf, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index c0fd632d92..1b3df43906 100644
--- a/walker.c
+++ b/walker.c
@@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		strbuf_reset(&refname);
 		strbuf_addf(&refname, "refs/%s", write_ref[i]);
 		if (ref_transaction_update(transaction, refname.buf,
-					   oids + i, NULL, 0,
+					   oids + i, NULL, NULL, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
 					   &err)) {
 			error("%s", err.buf);
-- 
2.43.GIT



^ permalink raw reply related	[relevance 7%]

* [PATCH v3 0/8] refs: add symref support to 'git-update-ref'
  2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
  2024-04-12  9:59  8%   ` [PATCH v2 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
  2024-04-12  9:59 12%   ` [PATCH v2 3/7] update-ref: add support for symref-delete Karthik Nayak
@ 2024-04-23 21:28  1%   ` Karthik Nayak
  2024-04-23 21:28  7%     ` [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
                       ` (3 more replies)
  2 siblings, 4 replies; 200+ results
From: Karthik Nayak @ 2024-04-23 21:28 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The 'git-update-ref(1)' command allows transactional reference updates.
But currently only supports regular reference updates. Meaning, if one
wants to update HEAD (symbolic ref) in a transaction, there is no tool
to do so.

One option to obtain transactional updates for the HEAD ref is to
manually create the HEAD.lock file and commit. This is intrusive, where
the user needs to mimic internal git behavior. Also, this only works
when using the files backend.

At GitLab, we've been using the manual process till date, to allow users
to set and change their default branch. But with the introduction of
reftables as a reference backend, this becomes a necessity to be solved
within git.

The patch series adds symref support to the existing commands {verify,
create, delete, update} within 'git-update-ref'. This is done by parsing
inputs with the 'ref:' prefix as symref targets. This updates our current
commands to:

update SP <ref> SP (<new-oid> | ref:<new-target>) [SP (<old-oid> | ref:<old-target>)] LF
create SP <ref> SP (<new-oid> | ref:<new-target>) LF
delete SP <ref> [SP (<old-oid> | ref:<old-target>)] LF
verify SP <ref> [SP (<old-oid> | ref:<old-target>)] LF

Wherein, when ref:<new-target> is provided, the update ensures that
the <ref> is a symbolic ref which targets <new-target>. When
ref:<old-target> is provided, we ensure that <ref> is a symbolic ref
which targets <old-target> before the update.

With this it is possible to:
1. Create, verify, delete, and update symrefs
2. Create dangling symrefs
3. Update regular refs to become symrefs
4. Update symrefs to become regular refs

V1 of the patch series can be found here:
https://lore.kernel.org/git/20240330224623.579457-1-knayak@gitlab.com/
V2 of the patch series can be found here:
https://lore.kernel.org/git/20240412095908.1134387-1-knayak@gitlab.com/

Changes from v2:
1. We no longer have separate commands for symrefs, instead the regular
commands learn to parse 'ref:<target>' as symref targets. This reduces
the code in this series. Thanks Patrick for the suggestion.
2. Apart from supporting regular refs => symrefs. We also support the
inverse now.
3. Also allow creation of dangling refs.
4. Bunch of cleanups
   - whitespace issues in the previous patch series
   - uneeded header includes
   - uneeded tests
5. Added more documentation and better error messages, especially in reftables.
6. Better assertions around the input data.

Thanks all for the reviews on the previous iteration. Appreciate the support!

Range diff against v2:

1:  3269d0e91e ! 1:  4e49d54dcc refs: accept symref values in `ref_transaction[_add]_update`
    @@ Commit message
         flags to create a `ref_update` and add it to the transaction at hand.
     
         To extend symref support in transactions, we need to also accept the
    -    old and new ref values and process it. In this commit, let's add the
    -    required paramaters to the function and modify all call sites.
    +    old and new ref targets and process it. In this commit, let's add the
    +    required parameters to the function and modify all call sites.
     
    -    The two paramaters added are `new_ref` and `old_ref`. The `new_ref` is
    -    used to denote what the reference should point to when the transaction
    -    is applied. Some functions allow this parameter to be NULL, meaning that
    -    the reference is not changed, or `""`, meaning that the reference should
    -    be deleted.
    +    The two parameters added are `new_target` and `old_target`. The
    +    `new_target` is used to denote what the reference should point to when
    +    the transaction is applied. Some functions allow this parameter to be
    +    NULL, meaning that the reference is not changed.
     
    -    The `old_ref` denotes the value of that the reference must have before
    -    the update. Some functions allow this parameter to be NULL, meaning that
    -    the old value of the reference is not checked, or `""`, meaning that the
    -    reference must not exist before the update. A copy of this value is made
    +    The `old_target` denotes the value the reference must have before the
    +    update. Some functions allow this parameter to be NULL, meaning that the
    +    old value of the reference is not checked. A copy of this value is made
         in the transaction.
     
         The handling logic of these parameters will be added in consequent
    -    commits as we implement symref-{create, update, delete, verify}.
    +    commits as we add symref support to the existing 'git-update-ref'
    +    commands.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ refs.c: struct ref_update *ref_transaction_add_update(
      		const char *refname, unsigned int flags,
      		const struct object_id *new_oid,
      		const struct object_id *old_oid,
    -+		const char *new_ref, const char *old_ref,
    ++		const char *new_target, const char *old_target,
      		const char *msg)
      {
      	struct ref_update *update;
    +@@ refs.c: struct ref_update *ref_transaction_add_update(
    + 	if (transaction->state != REF_TRANSACTION_OPEN)
    + 		BUG("update called for transaction that is not open");
    + 
    ++	if (old_oid && !is_null_oid(old_oid) && old_target)
    ++		BUG("Only one of old_oid and old_target should be non NULL");
    ++	if (new_oid && !is_null_oid(new_oid) && new_target)
    ++		BUG("Only one of new_oid and new_target should be non NULL");
    ++
    + 	FLEX_ALLOC_STR(update, refname, refname);
    + 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
    + 	transaction->updates[transaction->nr++] = update;
     @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
      			   const struct object_id *old_oid,
    -+			   const char *new_ref, const char *old_ref,
    ++			   const char *new_target,
    ++			   const char *old_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err)
      {
    @@ refs.c: int ref_transaction_update(struct ref_transaction *transaction,
      
      	ref_transaction_add_update(transaction, refname, flags,
     -				   new_oid, old_oid, msg);
    -+				   new_oid, old_oid, new_ref, old_ref, msg);
    ++				   new_oid, old_oid, new_target,
    ++				   old_target, msg);
      	return 0;
      }
      
    @@ refs.c: int refs_update_ref(struct ref_store *refs, const char *msg,
     
      ## refs.h ##
     @@ refs.h: struct ref_transaction *ref_transaction_begin(struct strbuf *err);
    -  */
    - #define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
    - 
    -+/*
    -+ * The reference update is considered to be done on a symbolic reference. This
    -+ * ensures that we verify, delete, create and update the ref correspondingly.
    -+ */
    -+#define REF_SYMREF_UPDATE (1 << 12)
    -+
    - /*
    -  * Bitmask of all of the flags that are allowed to be passed in to
    -  * ref_transaction_update() and friends:
    -  */
    - #define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS                                  \
    - 	(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
    --	 REF_SKIP_REFNAME_VERIFICATION)
    -+	 REF_SKIP_REFNAME_VERIFICATION | REF_SYMREF_UPDATE )
    - 
    - /*
    -  * Add a reference update to transaction. `new_oid` is the value that
    +  *         before the update. A copy of this value is made in the
    +  *         transaction.
    +  *
    ++ *     new_target -- the target reference that the reference will be
    ++ *         update to point to. This takes precedence over new_oid when
    ++ *         set. If the reference is a regular reference, it will be
    ++ *         converted to a symbolic reference.
    ++ *
    ++ *     old_target -- the reference that the reference must be pointing to.
    ++ *         Will only be taken into account when the reference is a symbolic
    ++ *         reference.
    ++ *
    +  *     flags -- flags affecting the update, passed to
    +  *         update_ref_lock(). Possible flags: REF_NO_DEREF,
    +  *         REF_FORCE_CREATE_REFLOG. See those constants for more
    +@@ refs.h: struct ref_transaction *ref_transaction_begin(struct strbuf *err);
    +  * beforehand. The old value is checked after the lock is taken to
    +  * prevent races. If the old value doesn't agree with old_oid, the
    +  * whole transaction fails. If old_oid is NULL, then the previous
    +- * value is not checked.
    ++ * value is not checked. If `old_target` is not NULL, treat the reference
    ++ * as a symbolic ref and validate that its target before the update is
    ++ * `old_target`. If the `new_target` is not NULL, then the reference
    ++ * will be updated to a symbolic ref which targets `new_target`.
    ++ * Together, these allow us to update between regular refs and symrefs.
    +  *
    +  * See the above comment "Reference transaction updates" for more
    +  * information.
     @@ refs.h: int ref_transaction_update(struct ref_transaction *transaction,
      			   const char *refname,
      			   const struct object_id *new_oid,
      			   const struct object_id *old_oid,
    -+			   const char *new_ref, const char *old_ref,
    ++			   const char *new_target,
    ++			   const char *old_target,
      			   unsigned int flags, const char *msg,
      			   struct strbuf *err);
      
    @@ refs/refs-internal.h: struct ref_update {
      	struct object_id old_oid;
      
     +	/*
    -+	 * If (flags & REF_SYMREF_UPDATE), set the reference to this
    -+	 * value (or delete it, if `new_ref` is an empty string).
    ++	 * If set, point the reference to this value. This can also be
    ++	 * used to convert regular references to become symbolic refs.
     +	 */
    -+	const char *new_ref;
    ++	const char *new_target;
     +
     +	/*
    -+	 * If (type & REF_SYMREF_UPDATE), check that the reference
    -+	 * previously had this value (or didn't previously exist,
    -+	 * if `old_ref` is an empty string).
    ++	 * If set and the reference is a symbolic ref, check that the
    ++	 * reference previously pointed to this value.
     +	 */
    -+	const char *old_ref;
    ++	const char *old_target;
     +
      	/*
      	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
    @@ refs/refs-internal.h: struct ref_update *ref_transaction_add_update(
      		const char *refname, unsigned int flags,
      		const struct object_id *new_oid,
      		const struct object_id *old_oid,
    -+		const char *new_ref, const char *old_ref,
    ++		const char *new_target, const char *old_target,
      		const char *msg);
      
      /*
2:  a8cb0e0a1d < -:  ---------- update-ref: add support for symref-verify
3:  37c3e006da < -:  ---------- update-ref: add support for symref-delete
-:  ---------- > 2:  37b7aadca4 update-ref: support parsing ref targets in `parse_next_oid`
4:  53fdb408ef = 3:  9cb7817f94 files-backend: extract out `create_symref_lock`
5:  8fa0151f94 < -:  ---------- update-ref: add support for symref-create
6:  714492ede3 < -:  ---------- update-ref: add support for symref-update
-:  ---------- > 4:  c7f43f6058 update-ref: support symrefs in the verify command
-:  ---------- > 5:  4016d6ca98 update-ref: support symrefs in the delete command
-:  ---------- > 6:  9f19e82f00 update-ref: support symrefs in the create command
-:  ---------- > 7:  132dbfcc5f update-ref: support symrefs in the update command
7:  c483104562 ! 8:  1b709f995b refs: support symrefs in 'reference-transaction' hook
    @@ Metadata
     Author: Karthik Nayak <karthik.188@gmail.com>
     
      ## Commit message ##
    -    refs: support symrefs in 'reference-transaction' hook
    +    ref: support symrefs in 'reference-transaction' hook
     
         The 'reference-transaction' hook runs whenever a reference update is
    -    made to the system. In the previous commits, we added support for
    -    various symref commands in `git-update-ref`. While it allowed us to now
    +    made to the system. In the previous commits, we added symref support for
    +    various commands in `git-update-ref`. While it allowed us to now
         manipulate symbolic refs via `git-update-ref`, it didn't activate the
         'reference-transaction' hook.
     
    @@ Commit message
         new format described for this and we stick to the existing format of:
             <old-value> SP <new-value> SP <ref-name> LF
         but now, <old-value> and <new-value> could also denote references
    -    instead of objects.
    +    instead of objects, where the format is similar to that in
    +    'git-update-ref', i.e. 'ref:<ref-target>'.
     
         While this seems to be backward incompatible, it is okay, since the only
         way the `reference-transaction` hook has refs in its output is when
    -    `git-update-ref` is used with `update-symref` command. Also the
    -    documentation for reference-transaction hook always stated that support
    -    for symbolic references may be added in the future.
    +    `git-update-ref` is used to manipulate symrefs. Also the documentation
    +    for reference-transaction hook always stated that support for symbolic
    +    references may be added in the future.
     
         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
     
    @@ Documentation/githooks.txt: given reference transaction is in:
      `<ref-name>` via `git rev-parse`.
      
     +For symbolic reference updates the `<old_value>` and `<new-value>`
    -+fields could denote references instead of objects.
    ++fields could denote references instead of objects, denoted via the
    ++`ref:<ref-target>` format.
     +
      The exit status of the hook is ignored for any state except for the
      "prepared" state. In the "prepared" state, a non-zero exit status will
    @@ refs.c: static int run_transaction_hook(struct ref_transaction *transaction,
      
      	for (i = 0; i < transaction->nr; i++) {
      		struct ref_update *update = transaction->updates[i];
    -+		const char *new_value, *old_value;
    ++		strbuf_reset(&buf);
      
    --		if (update->flags & REF_SYMREF_UPDATE)
    +-		/*
    +-		 * Skip reference transaction for symbolic refs.
    +-		 */
    +-		if (update->new_target || update->old_target)
     -			continue;
    -+		new_value = oid_to_hex(&update->new_oid);
    -+		old_value = oid_to_hex(&update->old_oid);
    -+
    -+		if (update->flags & REF_SYMREF_UPDATE) {
    -+			if (update->flags & REF_HAVE_NEW && !null_new_value(update))
    -+				new_value = update->new_ref;
    -+			if (update->flags & REF_HAVE_OLD && update->old_ref)
    -+				old_value = update->old_ref;
    -+		}
    ++		if (update->flags & REF_HAVE_OLD && update->old_target)
    ++			strbuf_addf(&buf, "ref:%s ", update->old_target);
    ++		else
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
      
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		strbuf_addf(&buf, "%s %s %s\n",
     -			    oid_to_hex(&update->old_oid),
     -			    oid_to_hex(&update->new_oid),
     -			    update->refname);
    -+		strbuf_addf(&buf, "%s %s %s\n", old_value, new_value, update->refname);
    ++		if (update->flags & REF_HAVE_NEW && update->new_target)
    ++			strbuf_addf(&buf, "ref:%s ", update->new_target);
    ++		else
    ++			strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
    ++
    ++		strbuf_addf(&buf, "%s\n", update->refname);
      
      		if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
      			if (errno != EPIPE) {
     
      ## t/t1416-ref-transaction-hooks.sh ##
    -@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'hook gets all queued updates in aborted state' '
    - 	test_cmp expect actual
    +@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls succeed' '
    + 	test_cmp expect target-repo.git/actual
      '
      
    -+# This test doesn't add a check for 'symref-delete' since there is a
    ++# This test doesn't add a check for symref 'delete' since there is a
     +# variation between the ref backends WRT 'delete'. In the files backend,
     +# 'delete' also triggers an additional transaction update on the
     +# packed-refs backend, which constitutes additional reflog entries.
    - test_expect_success 'interleaving hook calls succeed' '
    - 	test_when_finished "rm -r target-repo.git" &&
    - 
    -@@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls succeed' '
    - 	test_cmp expect target-repo.git/actual
    - '
    - 
     +test_expect_success 'hook gets all queued symref updates' '
     +	test_when_finished "rm actual" &&
     +
    @@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls s
     +
     +	cat >expect <<-EOF &&
     +		prepared
    -+		refs/heads/main $ZERO_OID refs/heads/symref
    -+		$ZERO_OID refs/heads/main refs/heads/symrefc
    -+		refs/heads/main refs/heads/branch refs/heads/symrefu
    ++		ref:refs/heads/main $ZERO_OID refs/heads/symref
    ++		$ZERO_OID ref:refs/heads/main refs/heads/symrefc
    ++		ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
     +		committed
    -+		refs/heads/main $ZERO_OID refs/heads/symref
    -+		$ZERO_OID refs/heads/main refs/heads/symrefc
    -+		refs/heads/main refs/heads/branch refs/heads/symrefu
    ++		ref:refs/heads/main $ZERO_OID refs/heads/symref
    ++		$ZERO_OID ref:refs/heads/main refs/heads/symrefc
    ++		ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu
     +	EOF
     +
     +	git update-ref --no-deref --stdin <<-EOF &&
     +		start
    -+		symref-verify refs/heads/symref refs/heads/main
    -+		symref-create refs/heads/symrefc refs/heads/main
    -+		symref-update refs/heads/symrefu refs/heads/branch refs/heads/main
    ++		verify refs/heads/symref ref:refs/heads/main
    ++		create refs/heads/symrefc ref:refs/heads/main
    ++		update refs/heads/symrefu ref:refs/heads/branch ref:refs/heads/main
     +		prepare
     +		commit
     +	EOF


Karthik Nayak (8):
  refs: accept symref values in `ref_transaction[_add]_update`
  update-ref: support parsing ref targets in `parse_next_oid`
  files-backend: extract out `create_symref_lock`
  update-ref: support symrefs in the verify command
  update-ref: support symrefs in the delete command
  update-ref: support symrefs in the create command
  update-ref: support symrefs in the update command
  ref: support symrefs in 'reference-transaction' hook

 Documentation/git-update-ref.txt |  41 ++--
 Documentation/githooks.txt       |  14 +-
 branch.c                         |   2 +-
 builtin/clone.c                  |   2 +-
 builtin/fast-import.c            |   5 +-
 builtin/fetch.c                  |   4 +-
 builtin/receive-pack.c           |   4 +-
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   1 +
 builtin/update-ref.c             | 106 ++++++++--
 refs.c                           |  78 +++++--
 refs.h                           |  23 +-
 refs/files-backend.c             | 141 +++++++++++--
 refs/refs-internal.h             |  20 ++
 refs/reftable-backend.c          |  49 ++++-
 sequencer.c                      |   9 +-
 t/t0600-reffiles-backend.sh      |  32 +++
 t/t1400-update-ref.sh            | 346 ++++++++++++++++++++++++++++++-
 t/t1416-ref-transaction-hooks.sh |  41 ++++
 walker.c                         |   2 +-
 20 files changed, 817 insertions(+), 105 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 1%]

* [PATCH v2 06/12] remote-curl: fix parsing of detached SHA256 heads
  2024-04-23  5:07  2% ` [PATCH v2 00/12] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
@ 2024-04-23  5:07 10%   ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-23  5:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, brian m. carlson, Justin Tobler

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

The dumb HTTP transport tries to read the remote HEAD reference by
downloading the "HEAD" file and then parsing it via `http_fetch_ref()`.
This function will either parse the file as an object ID in case it is
exactly `the_hash_algo->hexsz` long, or otherwise it will check whether
the reference starts with "ref :" and parse it as a symbolic ref.

This is broken when parsing detached HEADs of a remote SHA256 repository
because we never update `the_hash_algo` to the discovered remote object
hash. Consequently, `the_hash_algo` will always be the fallback SHA1
hash algorithm, which will cause us to fail parsing HEAD altogteher when
it contains a SHA256 object ID.

Fix this issue by setting up `the_hash_algo` via `repo_set_hash_algo()`.
While at it, let's make the expected SHA1 fallback explicit in our code,
which also addresses an upcoming issue where we are going to remove the
SHA1 fallback for `the_hash_algo`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c              | 19 ++++++++++++++++++-
 t/t5550-http-fetch-dumb.sh | 15 +++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/remote-curl.c b/remote-curl.c
index 0b6d7815fd..004b707fdf 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -266,12 +266,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 	return list;
 }
 
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
 static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
 {
 	const char *p = memchr(heads->buf, '\t', heads->len);
 	int algo;
+
+	/*
+	 * In case the remote has no refs we have no way to reliably determine
+	 * the object hash used by that repository. In that case we simply fall
+	 * back to SHA1, which may or may not be correct.
+	 */
 	if (!p)
-		return the_hash_algo;
+		return &hash_algos[GIT_HASH_SHA1];
 
 	algo = hash_algo_by_length((p - heads->buf) / 2);
 	if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +306,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
 		    "is this a git repository?",
 		    transport_anonymize_url(url.buf));
 
+	/*
+	 * Set the repository's hash algo to whatever we have just detected.
+	 * This ensures that we can correctly parse the remote references.
+	 */
+	repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
 	data = heads->buf;
 	start = NULL;
 	mid = data;
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 4c3b32785d..5f16cbc58d 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -55,6 +55,21 @@ test_expect_success 'list refs from outside any repository' '
 	test_cmp expect actual
 '
 
+
+test_expect_success 'list detached HEAD from outside any repository' '
+	git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+		"$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+		update-ref --no-deref HEAD refs/heads/main &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+	cat >expect <<-EOF &&
+	$(git rev-parse main)	HEAD
+	$(git rev-parse main)	refs/heads/main
+	EOF
+	nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'create password-protected repository' '
 	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
 	cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-- 
2.45.0-rc0


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

^ permalink raw reply related	[relevance 10%]

* [PATCH v2 00/12] Stop relying on SHA1 fallback for `the_hash_algo`
  2024-04-19  9:51  2% [PATCH 00/11] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
  2024-04-19  9:51 10% ` [PATCH 05/11] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
@ 2024-04-23  5:07  2% ` Patrick Steinhardt
  2024-04-23  5:07 10%   ` [PATCH v2 06/12] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
  2024-04-29  6:34  2% ` [PATCH v3 00/13] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
  2 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-23  5:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, brian m. carlson, Justin Tobler

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

Hi,

this is the second version of my patch series that causes us to stop
relying on the SHA1 default hash.

Changes compared to v1:

    - Various typo fixes in commit messages.

    - Added another patch that moves `validate_headref()` into "setup.c"
      to clarify that it is only used during repository discovery.

    - Indented a diff in a commit message so that git-am(1) is happy.

Thanks!

Patrick

Patrick Steinhardt (12):
  path: harden validation of HEAD with non-standard hashes
  path: move `validate_headref()` to its only user
  parse-options-cb: only abbreviate hashes when hash algo is known
  attr: don't recompute default attribute source
  attr: fix BUG() when parsing attrs outside of repo
  remote-curl: fix parsing of detached SHA256 heads
  builtin/rev-parse: allow shortening to more than 40 hex characters
  builtin/blame: don't access potentially unitialized `the_hash_algo`
  builtin/bundle: abort "verify" early when there is no repository
  builtin/diff: explicitly set hash algo when there is no repo
  builtin/shortlog: don't set up revisions without repo
  repository: stop setting SHA1 as the default object hash

 attr.c                     | 31 +++++++++++++++-------
 builtin/blame.c            |  5 ++--
 builtin/bundle.c           |  5 ++++
 builtin/diff.c             |  9 +++++++
 builtin/rev-parse.c        |  5 ++--
 builtin/shortlog.c         |  2 +-
 parse-options-cb.c         |  3 ++-
 path.c                     | 53 --------------------------------------
 path.h                     |  1 -
 remote-curl.c              | 19 +++++++++++++-
 repository.c               |  2 --
 setup.c                    | 53 ++++++++++++++++++++++++++++++++++++++
 t/t0003-attributes.sh      | 15 +++++++++++
 t/t0040-parse-options.sh   | 17 ++++++++++++
 t/t1500-rev-parse.sh       |  6 +++++
 t/t5550-http-fetch-dumb.sh | 15 +++++++++++
 16 files changed, 167 insertions(+), 74 deletions(-)

Range-diff against v1:
 1:  aa4d6f508b !  1:  a986b464d3 path: harden validation of HEAD with non-standard hashes
    @@ Commit message
         current version of Git doesn't understand yet. We'd still want to detect
         the repository as proper Git repository in that case, and we will fail
         eventually with a proper error message that the hash isn't understood
    -    when trying to set up the repostiory format.
    +    when trying to set up the repository format.
     
         It follows that we could just leave the current code intact, as in
         practice the code change doesn't have any user visible impact. But it
         also prepares us for `the_hash_algo` being unset when there is no
    -    repositroy.
    +    repository.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
 -:  ---------- >  2:  a347c7e6ca path: move `validate_headref()` to its only user
 2:  5daaaed2b9 !  3:  c0a15b2fa6 parse-options-cb: only abbreviate hashes when hash algo is known
    @@ Commit message
         parse-options-cb: only abbreviate hashes when hash algo is known
     
         The `OPT__ABBREV()` option can be used to add an option that abbreviates
    -    object IDs. When given an length longer than `the_hash_algo->hexsz`,
    -    then it will instead set the length to that maximum length.
    +    object IDs. When given a length longer than `the_hash_algo->hexsz`, then
    +    it will instead set the length to that maximum length.
     
         It may not always be guaranteed that we have `the_hash_algo` initialized
    -    properly as the hash algortihm can only be set up after we have set up
    +    properly as the hash algorithm can only be set up after we have set up
         `the_repository`. In that case, the hash would always be truncated to
         the hex length of SHA1, which may not be what the user desires.
     
 3:  ae91a27ffc !  4:  1b5f904eed attr: don't recompute default attribute source
    @@ Commit message
         variable is the null object ID then we try to look up the attr source,
         otherwise we skip over it.
     
    -    This has approach is flawed though: the variable will never be set to
    +    This approach is flawed though: the variable will never be set to
         anything else but the null object ID in case there is no attr source.
         Consequently, we re-compute the information on every call. And in the
         worst case, when we silently ignore bad trees, this will cause us to try
 4:  53c8e1cd7c =  5:  26909daca4 attr: fix BUG() when parsing attrs outside of repo
 5:  32a429fb60 =  6:  0b99184f50 remote-curl: fix parsing of detached SHA256 heads
 6:  9cb7baa50c =  7:  ccfda3c2d2 builtin/rev-parse: allow shortening to more than 40 hex characters
 7:  e189a4ad15 =  8:  1813e7eb5c builtin/blame: don't access potentially unitialized `the_hash_algo`
 8:  bc4bda3508 =  9:  31182a1fc6 builtin/bundle: abort "verify" early when there is no repository
 9:  39e56dab62 ! 10:  78e19d0a1b builtin/diff: explicitly set hash algo when there is no repo
    @@ Commit message
         hashing the files that we are diffing so that we can print the "index"
         line:
     
    -    ```
    -    diff --git a/a b/b
    -    index 7898192..6178079 100644
    -    --- a/a
    -    +++ b/b
    -    @@ -1 +1 @@
    -    -a
    -    +b
    -    ```
    +        ```
    +        diff --git a/a b/b
    +        index 7898192..6178079 100644
    +        --- a/a
    +        +++ b/b
    +        @@ -1 +1 @@
    +        -a
    +        +b
    +        ```
     
         We implicitly use SHA1 to calculate the hash here, which is because
         `the_repository` gets initialized with SHA1 during the startup routine.
10:  508e28ed1e ! 11:  51bcddbc31 builtin/shortlog: don't set up revisions without repo
    @@ Commit message
         repository in that context, it is thus unsupported to pass any revisions
         as arguments.
     
    -    Reghardless of that we still end up calling `setup_revisions()`. While
    +    Regardless of that we still end up calling `setup_revisions()`. While
         that works alright, it is somewhat strange. Furthermore, this is about
         to cause problems when we unset the default object hash.
     
11:  f86a6ff3ba = 12:  e8126371e1 repository: stop setting SHA1 as the default object hash

base-commit: 21306a098c3f174ad4c2a5cddb9069ee27a548b0
-- 
2.45.0-rc0


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

^ permalink raw reply	[relevance 2%]

* [PATCH] stash: fix "--staged" with binary files
@ 2024-04-22 10:28  3% Adam Johnson via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Adam Johnson via GitGitGadget @ 2024-04-22 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Randall S. Becker, Adam Johnson, Adam Johnson

From: Adam Johnson <me@adamj.eu>

"git stash --staged" would crash with binary files, after saving the stash.
This behaviour dates back to the addition of the feature in 41a28eb6c1
(stash: implement '--staged' option for 'push' and 'save', 2021-10-18).
Adding the "--binary" option of "diff-tree" fixes this. The "diff-tree" call
in stash_patch() also omits "--binary", but that is fine since binary files
cannot be selected interactively.

Helped-By: Jeff King <peff@peff.net>
Helped-By: Randall S. Becker <randall.becker@nexbridge.ca>
Signed-off-by: Adam Johnson <me@adamj.eu>
---
    stash: fix "--staged" with binary files

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1722%2Fadamchainz%2Faj%2Fstash-binary-fix-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1722/adamchainz/aj/stash-binary-fix-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1722

 builtin/stash.c  | 4 ++--
 t/t3903-stash.sh | 9 +++++++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 062be1fbc07..7751bca868e 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1205,8 +1205,8 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
 	}
 
 	cp_diff_tree.git_cmd = 1;
-	strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
-		     oid_to_hex(&info->w_tree), "--", NULL);
+	strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary",
+		     "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL);
 	if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
 		ret = -1;
 		goto done;
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 00db82fb245..a7f71f8126f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -393,6 +393,15 @@ test_expect_success 'stash --staged' '
 	test bar,bar4 = $(cat file),$(cat file2)
 '
 
+test_expect_success 'stash --staged with binary file' '
+	printf "\0" >file &&
+	git add file &&
+	git stash --staged &&
+	git stash pop &&
+	printf "\0" >expect &&
+	test_cmp expect file
+'
+
 test_expect_success 'dont assume push with non-option args' '
 	test_must_fail git stash -q drop 2>err &&
 	test_grep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err

base-commit: ae3196a5ea84a9e88991d576020cf66512487088
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* Re: [PATCH] git-gui: fix inability to quit after closing another instance
  @ 2024-04-20 21:58  0%   ` Orgad Shaneh
  0 siblings, 0 replies; 200+ results
From: Orgad Shaneh @ 2024-04-20 21:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Pratyush Yadav, git, Orgad Shaneh via GitGitGadget

Hi Junio/Yadav,

It's been more than a year since I opened a PR to git-gui upstream
(https://github.com/prati0100/git-gui/pull/91). I pinged several
times, but it looks like nobody's home.

Is it possible to accept it to git nevertheless? This is an annoying
issue, and the fix is trivial.

Thanks!

- Orgad


On Wed, Feb 1, 2023 at 7:22 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Orgad, thanks for a patch.
> Yadav, this came to git@vger.kernel.org, so I'm forwarding.
>
> cf. Documentation/SubmittingPatches
>
>     == Subsystems with dedicated maintainers
>
>     Some parts of the system have dedicated maintainers with their own
>     repositories.
>
>     - `git-gui/` comes from git-gui project, maintained by Pratyush Yadav:
>
>             https://github.com/prati0100/git-gui.git
>
> "Orgad Shaneh via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: Orgad Shaneh <orgads@gmail.com>
> >
> > If you open 2 git gui instances in the same directory, then close one
> > of them and try to close the other, an error message pops up, saying:
> > 'error renaming ".git/GITGUI_BCK": no such file or directory', and it
> > is no longer possible to close the window ever.
> >
> > Fix by catching this error, and proceeding even if the file no longer
> > exists.
> >
> > Signed-off-by: Orgad Shaneh <orgads@gmail.com>
> > ---
> >     git-gui: fix inability to quit after closing another instance
> >
> >     If you open 2 git gui instances in the same directory, then close one of
> >     them and try to close the other, an error message pops up, saying:
> >     'error renaming ".git/GITGUI_BCK": no such file or directory', and it is
> >     no longer possible to close the window ever.
> >
> >     Fix by catching this error, and proceeding even if the file no longer
> >     exists.
> >
> > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1446%2Forgads%2Fgit-gui-no-quit-v1
> > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1446/orgads/git-gui-no-quit-v1
> > Pull-Request: https://github.com/git/git/pull/1446
> >
> >  git-gui/git-gui.sh | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
> > index 201524c34ed..b00ee691e3b 100755
> > --- a/git-gui/git-gui.sh
> > +++ b/git-gui/git-gui.sh
> > @@ -2307,7 +2307,7 @@ proc do_quit {{rc {1}}} {
> >               #
> >               set save [gitdir GITGUI_MSG]
> >               if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
> > -                     file rename -force [gitdir GITGUI_BCK] $save
> > +                     catch { file rename -force [gitdir GITGUI_BCK] $save }
> >                       set GITGUI_BCK_exists 0
> >               } elseif {[$ui_comm edit modified]} {
> >                       set msg [string trim [$ui_comm get 0.0 end]]
> >
> > base-commit: 2fc9e9ca3c7505bc60069f11e7ef09b1aeeee473


^ permalink raw reply	[relevance 0%]

* [PATCH] docs: remove duplicate entry and fix typo in 2.45 changelog
@ 2024-04-20 19:51 10% Orgad Shaneh via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Orgad Shaneh via GitGitGadget @ 2024-04-20 19:51 UTC (permalink / raw)
  To: git; +Cc: Orgad Shaneh, Orgad Shaneh

From: Orgad Shaneh <orgads@gmail.com>

Signed-off-by: Orgad Shaneh <orgads@gmail.com>
---
    docs: remove duplicate entry and fix typo in 2.45 changelog

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1711%2Forgads%2Fchangelog-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1711/orgads/changelog-v1
Pull-Request: https://github.com/git/git/pull/1711

 Documentation/RelNotes/2.45.0.txt | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/Documentation/RelNotes/2.45.0.txt b/Documentation/RelNotes/2.45.0.txt
index 0570dcd8773..38458664407 100644
--- a/Documentation/RelNotes/2.45.0.txt
+++ b/Documentation/RelNotes/2.45.0.txt
@@ -100,7 +100,7 @@ Performance, Internal Implementation, Development Support etc.
 
  * The way placeholders are to be marked-up in documentation have been
    specified; use "_<placeholder>_" to typeset the word inside a pair
-   of <angle-brakets> emphasized.
+   of <angle-brackets> emphasized.
 
  * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
    fetching of objects from the promisor remote, which may be handy
@@ -110,9 +110,6 @@ Performance, Internal Implementation, Development Support etc.
    clean.requireForce has been simplified, together with the
    documentation.
 
- * The code to iterate over refs with the reftable backend has seen
-   some optimization.
-
  * Uses of xwrite() helper have been audited and updated for better
    error checking and simpler code.
 

base-commit: ae3196a5ea84a9e88991d576020cf66512487088
-- 
gitgitgadget


^ permalink raw reply related	[relevance 10%]

* [ANNOUNCE] Git v2.45.0-rc0
@ 2024-04-19 17:14  2% Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-19 17:14 UTC (permalink / raw)
  To: git; +Cc: Linux Kernel, git-packagers

An early preview release Git v2.45.0-rc0 is now available for
testing at the usual places.  It is comprised of 481 non-merge
commits since v2.44.0, contributed by 77 people, 32 of which are
new faces [*].

The tarballs are found at:

    https://www.kernel.org/pub/software/scm/git/testing/

The following public repositories all have a copy of the
'v2.45.0-rc0' tag and the 'master' branch that the tag points at:

  url = https://git.kernel.org/pub/scm/git/git
  url = https://kernel.googlesource.com/pub/scm/git/git
  url = git://repo.or.cz/alt-git.git
  url = https://github.com/gitster/git

New contributors whose contributions weren't in v2.44.0 are as follows.
Welcome to the Git development community!

  Ahelenia Ziemiańska, Angelo Dureghello, Aryan Gupta, Benjamin
  Flesch, Bo Anderson, Brian C Tracy, Brian Lyles, Bruno Haible,
  Chuck Lever, Dario Gjorgjevski, Dirk Gouders, Eugenio Gigante,
  Florian Schmidt, Haritha D, Harmen Stoppels, Jean-Rémy Falleri,
  Jiamu Sun, Jonas Wunderlich, Jonathan Davies, Julio Bacellari,
  Kipras Melnikovas, Matthew Rollings, Max Gautier, mirth hickford,
  Paweł Dominiak, Pi Fisher, Ralph Seichter, Richard Macklin,
  shejialuo, Steven Jeuris, Tiago Pascoal, and Vincenzo Mezzela.

Returning contributors who helped this release are as follows.
Thanks for your continued support.

  Alexander Shopov, Beat Bolli, brian m. carlson, Chandra Pratap,
  Christian Couder, Derrick Stolee, Dragan Simic, Elijah Newren,
  Eric Sunshine, Eric W. Biederman, Ghanshyam Thakkar, Han Young,
  Jakub Wilk, Jean-Noël Avila, Jeff Hostetler, Jeff King,
  Johannes Schindelin, Johannes Sixt, John Cai, Josh Steadmon,
  Josh Triplett, Junio C Hamano, Justin Tobler, Karthik Nayak,
  Kristoffer Haugsbakk, Kyle Lippincott, Kyle Meyer, Linus Arver,
  Manlio Perillo, Matthias Aßhauer, M Hickford, Michael Lohmann,
  Michael Osipov, Mike Hommey, Patrick Steinhardt, Peter Hutterer,
  Philippe Blain, Phillip Wood, René Scharfe, Rubén Justo,
  Sergey Organov, SZEDER Gábor, Taylor Blau, Ville Skyttä,
  and Yasushi SHOJI.

[*] We are counting not just the authorship contribution but issue
    reporting, mentoring, helping and reviewing that are recorded in
    the commit trailers.

----------------------------------------------------------------

Git v2.45 Release Notes (draft)
===============================

Backward Compatibility Notes

UI, Workflows & Features

 * Integrate the reftable code into the refs framework as a backend.
   With "git init --ref-format=reftable", hopefully it would be a lot
   more efficient to manage a repository with many references.

 * "git checkout -p" and friends learned that that "@" is a synonym
   for "HEAD".

 * Variants of vimdiff learned to honor mergetool.<variant>.layout
   settings.

 * "git reflog" learned a "list" subcommand that enumerates known reflogs.

 * When a merge conflicted at a submodule, merge-ort backend used to
   unconditionally give a lengthy message to suggest how to resolve
   it.  Now the message can be squelched as an advice message.

 * "git for-each-ref" learned "--include-root-refs" option to show
   even the stuff outside the 'refs/' hierarchy.

 * "git rev-list --missing=print" has learned to optionally take
   "--allow-missing-tips", which allows the objects at the starting
   points to be missing.

 * "git merge-tree" has learned that the three trees involved in the
   3-way merge only need to be trees, not necessarily commits.

 * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
   other kinds of *_HEAD pseudorefs.

 * Platform specific tweaks for OS/390 has been added to
   config.mak.uname.

 * Users with safe.bareRepository=explicit can still work from within
   $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
   of the primary worktree without explicitly specifying the $GIT_DIR
   environment variable or the --git-dir=<path> option.

 * The output format for dates "iso-strict" has been tweaked to show
   a time in the Zulu timezone with "Z" suffix, instead of "+00:00".

 * "git diff" and friends learned two extra configuration variables,
   diff.srcPrefix and diff.dstPrefix.

 * The status.showUntrackedFiles configuration variable had a name
   that tempts users to set a Boolean value expressed in our usual
   "false", "off", and "0", but it only took "no".  This has been
   corrected so "true" and its synonyms are taken as "normal", while
   "false" and its synonyms are taken as "no".

 * Remove an ancient and not well maintained Hg-to-git migration
   script from contrib/.

 * Hints that suggest what to do after resolving conflicts can now be
   squelched by disabling advice.mergeConflict.

 * Allow git-cherry-pick(1) to automatically drop redundant commits via
   a new `--empty` option, similar to the `--empty` options for
   git-rebase(1) and git-am(1). Includes a soft deprecation of
   `--keep-redundant-commits` as well as some related docs changes and
   sequencer code cleanup.

 * "git config" learned "--comment=<message>" option to leave a
   comment immediately after the "variable = value" on the same line
   in the configuration file.

 * core.commentChar used to be limited to a single byte, but has been
   updated to allow an arbitrary multi-byte sequence.

 * "git add -p" and other "interactive hunk selection" UI has learned to
   skip showing the hunk immediately after it has already been shown, and
   an additional action to explicitly ask to reshow the current hunk.

 * "git pack-refs" learned the "--auto" option, which is a useful
   addition to be triggered from "git gc --auto".

 * "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
   diagnose a pathspec element that did not match any files in certain
   situations, unlike "git add <pathspec>" did.

 * The userdiff patterns for C# has been updated.


Performance, Internal Implementation, Development Support etc.

 * The code to iterate over refs with the reftable backend has seen
   some optimization.

 * More tests that are marked as "ref-files only" have been updated to
   improve test coverage of reftable backend.

 * Some parts of command line completion script (in contrib/) have
   been micro-optimized.

 * The way placeholders are to be marked-up in documentation have been
   specified; use "_<placeholder>_" to typeset the word inside a pair
   of <angle-brakets> emphasized.

 * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
   fetching of objects from the promisor remote, which may be handy
   for debugging.

 * The implementation in "git clean" that makes "-n" and "-i" ignore
   clean.requireForce has been simplified, together with the
   documentation.

 * The code to iterate over refs with the reftable backend has seen
   some optimization.

 * Uses of xwrite() helper have been audited and updated for better
   error checking and simpler code.

 * Some trace2 events that lacked def_param have learned to show it,
   enriching the output.

 * The parse-options code that deals with abbreviated long option
   names have been cleaned up.

 * The code in reftable backend that creates new table files works
   better with the tempfile framework to avoid leaving cruft after a
   failure.

 * The reftable code has its own custom binary search function whose
   comparison callback has an unusual interface, which caused the
   binary search to degenerate into a linear search, which has been
   corrected.

 * The code to iterate over reflogs in the reftable has been optimized
   to reduce memory allocation and deallocation.

 * Work to support a repository that work with both SHA-1 and SHA-256
   hash algorithms has started.

 * A new fuzz target that exercises config parsing code has been
   added.

 * Fix the way recently added tests interpolate variables defined
   outside them, and document the best practice to help future
   developers.

 * Introduce an experimental protocol for contributors to propose the
   topic description to be used in the "What's cooking" report, the
   merge commit message for the topic, and in the release notes and
   document it in the SubmittingPatches document.

 * The t/README file now gives a hint on running individual tests in
   the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
   (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).

 * The "hint:" messages given by the advice mechanism, when given a
   message with a blank line, left a line with trailing whitespace,
   which has been cleansed.

 * Documentation rules has been explicitly described how to mark-up
   literal parts and a few manual pages have been updated as examples.

 * The .editorconfig file has been taught that a Makefile uses HT
   indentation.

 * t-prio-queue test has been cleaned up by using C99 compound
   literals; this is meant to also serve as a weather-balloon to smoke
   out folks with compilers who have trouble compiling code that uses
   the feature.

 * Windows binary used to decide the use of unix-domain socket at
   build time, but it learned to make the decision at runtime instead.

 * The "shared repository" test in the t0610 reftable test failed
   under restrictive umask setting (e.g. 007), which has been
   corrected.

 * Document and apply workaround for a buggy version of dash that
   mishandles "local var=val" construct.

 * The codepaths that reach date_mode_from_type() have been updated to
   pass "struct date_mode" by value to make them thread safe.

 * The strategy to compact multiple tables of reftables after many
   operations accumulate many entries has been improved to avoid
   accumulating too many tables uncollected.


Fixes since v2.44
-----------------

 * "git apply" on a filesystem without filemode support have learned
   to take a hint from what is in the index for the path, even when
   not working with the "--index" or "--cached" option, when checking
   the executable bit match what is required by the preimage in the
   patch.
   (merge 45b625142d cp/apply-core-filemode later to maint).

 * "git column" has been taught to reject negative padding value, as
   it would lead to nonsense behaviour including division by zero.
   (merge 76fb807faa kh/column-reject-negative-padding later to maint).

 * "git am --help" now tells readers what actions are available in
   "git am --whitespace=<action>", in addition to saying that the
   option is passed through to the underlying "git apply".
   (merge a171dac734 jc/am-whitespace-doc later to maint).

 * "git tag --column" failed to check the exit status of its "git
   column" invocation, which has been corrected.
   (merge 92e66478fc rj/tag-column-fix later to maint).

 * Credential helper based on libsecret (in contrib/) has been updated
   to handle an empty password correctly.
   (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).

 * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
   option; it used to always exit with 0 and signalled success.
   (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).

 * The code incorrectly attempted to use textconv cache when asked,
   even when we are not running in a repository, which has been
   corrected.
   (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).

 * Remove an empty file that shouldn't have been added in the first
   place.
   (merge 4f66942215 js/remove-cruft-files later to maint).

 * The logic to access reflog entries by date and number had ugly
   corner cases at the boundaries, which have been cleaned up.
   (merge 5edd126720 jk/reflog-special-cases-fix later to maint).

 * An error message from "git upload-pack", which responds to "git
   fetch" requests, had a trialing NUL in it, which has been
   corrected.
   (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).

 * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
   to be the first header file.
   (merge 4e89f0e07c jc/doc-compat-util later to maint).

 * "git commit -v --cleanup=scissors" used to add the scissors line
   twice in the log message buffer, which has been corrected.
   (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).

 * A custom remote helper no longer cannot access the newly created
   repository during "git clone", which is a regression in Git 2.44.
   This has been corrected.
   (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).

 * Various parts of upload-pack has been updated to bound the resource
   consumption relative to the size of the repository to protect from
   abusive clients.
   (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).

 * The upload-pack program, when talking over v2, accepted the
   packfile-uris protocol extension from the client, even if it did
   not advertise the capability, which has been corrected.
   (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).

 * Make sure failure return from merge_bases_many() is properly caught.
   (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).

 * FSMonitor client code was confused when FSEvents were given in a
   different case on a case-insensitive filesystem, which has been
   corrected.
   (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).

 * The "core.commentChar" configuration variable only allows an ASCII
   character, which was not clearly documented, which has been
   corrected.
   (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).

 * With release 2.44 we got rid of all uses of test_i18ngrep and there
   is no in-flight topic that adds a new use of it.  Make a call to
   test_i18ngrep a hard failure, so that we can remove it at the end
   of this release cycle.
   (merge 381a83dfa3 jc/test-i18ngrep later to maint).

 * The command line completion script (in contrib/) learned to
   complete "git reflog" better.
   (merge 1284f9cc11 rj/complete-reflog later to maint).

 * The logic to complete the command line arguments to "git worktree"
   subcommand (in contrib/) has been updated to correctly honor things
   like "git -C dir" etc.
   (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).

 * When git refuses to create a branch because the proposed branch
   name is not a valid refname, an advice message is given to refer
   the user to exact naming rules.
   (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).

 * Code simplification by getting rid of code that sets an environment
   variable that is no longer used.
   (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).

 * The code to find the effective end of log message can fall into an
   endless loop, which has been corrected.
   (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).

 * Mark-ups used in the documentation has been improved for
   consistency.
   (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).

 * The status.showUntrackedFiles configuration variable was
   incorrectly documented to accept "false", which has been corrected.

 * Leaks from "git restore" have been plugged.
   (merge 2f64da0790 rj/restore-plug-leaks later to maint).

 * "git bugreport --no-suffix" was not supported and instead
   segfaulted, which has been corrected.
   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).

 * The documentation for "%(trailers[:options])" placeholder in the
   "--pretty" option of commands in the "git log" family has been
   updated.
   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).

 * "git checkout --conflict=bad" reported a bad conflictStyle as if it
   were given to a configuration variable; it has been corrected to
   report that the command line option is bad.
   (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).

 * Code clean-up in the "git log" machinery that implements custom log
   message formatting.
   (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).

 * "git config" corrupted literal HT characters written in the
   configuration file as part of a value, which has been corrected.
   (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).

 * A unit test for reftable code tried to enumerate all files in a
   directory after reftable operations and expected to see nothing but
   the files it wanted to leave there, but was fooled by .nfs* cruft
   files left, which has been corrected.
   (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).

 * The implementation and documentation of "object-format" option
   exchange between the Git itself and its remote helpers did not
   quite match, which has been corrected.

 * The "--pretty=<shortHand>" option of the commands in the "git log"
   family, defined as "[pretty] shortHand = <expansion>" should have
   been looked up case insensitively, but was not, which has been
   corrected.
   (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).

 * "git apply" failed to extract the filename the patch applied to,
   when the change was about an empty file created in or deleted from
   a directory whose name ends with a SP, which has been corrected.
   (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).

 * Update a more recent tutorial doc.
   (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).

 * The test script had an incomplete and ineffective attempt to avoid
   clobbering the testing user's real crontab (and its equivalents),
   which has been completed.
   (merge 73cb87773b es/test-cron-safety later to maint).

 * Use advice_if_enabled() API to rewrite a simple pattern to
   call advise() after checking advice_enabled().
   (merge 6412d01527 rj/use-adv-if-enabled later to maint).

 * Another "set -u" fix for the bash prompt (in contrib/) script.
   (merge d7805bc743 vs/complete-with-set-u-fix later to maint).

 * "git checkout/switch --detach foo", after switching to the detached
   HEAD state, gave the tracking information for the 'foo' branch,
   which was pointless.

 * "git apply" has been updated to lift the hardcoded pathname length
   limit, which in turn allowed a mksnpath() function that is no
   longer used.
   (merge 708f7e0590 rs/apply-lift-path-length-limit later to maint).

 * A file descriptor leak in an error codepath, used when "git apply
   --reject" fails to create the *.rej file, has been corrected.
   (merge 2b1f456adf rs/apply-reject-fd-leakfix later to maint).

 * A config parser callback function fell through instead of returning
   after recognising and processing a variable, wasting cycles, which
   has been corrected.
   (merge a816ccd642 ds/fetch-config-parse-microfix later to maint).

 * Fix was added to work around a regression in libcURL 8.7.0 (which has
   already been fixed in their tip of the tree).
   (merge 92a209bf24 jk/libcurl-8.7-regression-workaround later to maint).

 * The variable that holds the value read from the core.excludefile
   configuration variable used to leak, which has been corrected.
   (merge 0e0fefb29f jc/unleak-core-excludesfile later to maint).

 * vreportf(), which is usede by error() and friends, has been taught
   to give the error message printf-format string when its vsnprintf()
   call fails, instead of showing nothing useful to identify the
   nature of the error.
   (merge c63adab961 rs/usage-fallback-to-show-message-format later to maint).

 * Adjust to an upcoming changes to GNU make that breaks our Makefiles.
   (merge 227b8fd902 tb/make-indent-conditional-with-non-spaces later to maint).

 * Other code cleanup, docfix, build fix, etc.
   (merge f0e578c69c rs/use-xstrncmpz later to maint).
   (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
   (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
   (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
   (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
   (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
   (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
   (merge 244001aa20 hs/rebase-not-in-progress later to maint).
   (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
   (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
   (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
   (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
   (merge 40b8076462 ak/rebase-autosquash later to maint).
   (merge 3223204456 eg/add-uflags later to maint).
   (merge 5f78d52dce es/config-doc-sort-sections later to maint).
   (merge 781fb7b4c2 as/option-names-in-messages later to maint).
   (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
   (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
   (merge 67471bc704 ja/doc-formatting-fix later to maint).
   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
   (merge 0d527842b7 az/grep-group-error-message-update later to maint).
   (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
   (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
   (merge 39bb692152 rs/imap-send-use-xsnprintf later to maint).
   (merge 8d320cec60 jc/t2104-style-fixes later to maint).
   (merge b4454d5a7b pw/t3428-cleanup later to maint).
   (merge 84a7c33a4b pf/commitish-committish later to maint).
   (merge 8882ee9d68 la/mailmap-entry later to maint).

----------------------------------------------------------------

Changes since v2.44.0 are as follows:

Ahelenia Ziemiańska (1):
      grep: improve errors for unmatched ( and )

Alexander Shopov (4):
      transport-helper.c: trivial fix of error message
      builtin/remote.c: trivial fix of error message
      builtin/clone.c: trivial fix of message
      revision.c: trivial fix to message

Aryan Gupta (1):
      tests: modernize the test script t0010-racy-git.sh

Beat Bolli (25):
      completion: use awk for filtering the config entries
      date: make "iso-strict" conforming for the UTC timezone
      t0006: add more tests with a negative TZ offset
      doc: avoid redundant use of cat
      contrib/subtree/t: avoid redundant use of cat
      t/lib-cvs.sh: avoid redundant use of cat
      t/annotate-tests.sh: avoid redundant use of cat
      t/perf: avoid redundant use of cat
      t/t0*: avoid redundant uses of cat
      t/t1*: avoid redundant uses of cat
      t/t3*: avoid redundant uses of cat
      t/t4*: avoid redundant uses of cat
      t/t5*: avoid redundant uses of cat
      t/t6*: avoid redundant uses of cat
      t/t7*: avoid redundant use of cat
      t/t8*: avoid redundant use of cat
      t/t9*: avoid redundant uses of cat
      t/t1*: merge a "grep | sed" pipeline
      t/t3*: merge a "grep | awk" pipeline
      t/t4*: merge a "grep | sed" pipeline
      t/t5*: merge a "grep | sed" pipeline
      t/t8*: merge "grep | sed" pipelines
      t/t9*: merge "grep | sed" pipelines
      contrib/coverage-diff: avoid redundant pipelines
      git-quiltimport: avoid an unnecessary subshell

Bo Anderson (5):
      t/lib-credential: clean additional credential
      osxkeychain: replace deprecated SecKeychain API
      osxkeychain: erase all matching credentials
      osxkeychain: erase matching passwords only
      osxkeychain: store new attributes

Brian C Tracy (1):
      fuzz: add fuzzer for config parsing

Brian Lyles (13):
      docs: clarify file options in git-config `--edit`
      docs: fix typo in git-config `--default`
      docs: correct trailer `key_value_separator` description
      docs: adjust trailer `separator` and `key_value_separator` language
      pretty: update tests to use `test_config`
      pretty: find pretty formats case-insensitively
      docs: address inaccurate `--empty` default with `--exec`
      docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
      rebase: update `--empty=ask` to `--empty=stop`
      sequencer: handle unborn branch with `--allow-empty`
      sequencer: do not require `allow_empty` for redundant commit options
      cherry-pick: enforce `--keep-redundant-commits` incompatibility
      cherry-pick: add `--empty` for more robust redundant commit handling

Chandra Pratap (2):
      apply: ignore working tree filemode when !core.filemode
      t9146: replace test -d/-e/-f with appropriate test_path_is_* function

Christian Couder (5):
      revision: clarify a 'return NULL' in get_reference()
      oidset: refactor oidset_insert_from_set()
      t6022: fix 'test' style and 'even though' typo
      rev-list: allow missing tips with --missing=[print|allow*]
      revision: fix --missing=[print|allow*] for annotated tags

Derrick Stolee (1):
      fetch: return when parsing submodule.recurse

Dirk Gouders (6):
      Documentation/user-manual.txt: example for generating object hashes
      MyFirstObjectWalk: use additional arg in config_fn_t
      MyFirstObjectWalk: fix misspelled "builtins/"
      MyFirstObjectWalk: fix filtered object walk
      MyFirstObjectWalk: fix description for counting omitted objects
      MyFirstObjectWalk: add stderr to pipe processing

Dragan Simic (8):
      documentation: send-email: use camel case consistently
      config: minor addition of whitespace
      config: really keep value-internal whitespace verbatim
      t1300: add more tests for whitespace and inline comments
      config.txt: describe handling of whitespace further
      grep docs: describe --recurse-submodules further and improve formatting a bit
      grep docs: describe --no-index further and improve formatting a bit
      config: fix some small capitalization issues, as spotted

Eric Sunshine (2):
      docs: sort configuration variable groupings alphabetically
      test-lib: fix non-functioning GIT_TEST_MAINT_SCHEDULER fallback

Eric W. Biederman (23):
      object-file-convert: stubs for converting from one object format to another
      oid-array: teach oid-array to handle multiple kinds of oids
      object-names: support input of oids in any supported hash
      repository: add a compatibility hash algorithm
      loose: compatibilty short name support
      object-file: update the loose object map when writing loose objects
      object-file: add a compat_oid_in parameter to write_object_file_flags
      commit: convert mergetag before computing the signature of a commit
      commit: export add_header_signature to support handling signatures on tags
      tag: sign both hashes
      object: factor out parse_mode out of fast-import and tree-walk into in object.h
      object-file-convert: don't leak when converting tag objects
      object-file-convert: convert commits that embed signed tags
      object-file: update object_info_extended to reencode objects
      rev-parse: add an --output-object-format parameter
      builtin/cat-file: let the oid determine the output algorithm
      tree-walk: init_tree_desc take an oid to get the hash algorithm
      object-file: handle compat objects in check_object_signature
      builtin/ls-tree: let the oid determine the output algorithm
      test-lib: compute the compatibility hash so tests may use it
      t1006: rename sha1 to oid
      t1006: test oid compatibility with cat-file
      t1016-compatObjectFormat: add tests to verify the conversion between objects

Eugenio Gigante (1):
      add: use unsigned type for collection of bits

Florian Schmidt (1):
      wt-status: don't find scissors line beyond buf len

Ghanshyam Thakkar (5):
      add-patch: classify '@' as a synonym for 'HEAD'
      add -p tests: remove PERL prerequisites
      setup: remove unnecessary variable
      builtin/commit: error out when passing untracked path with -i
      builtin/add: error out when passing untracked path with -u

Haritha D (1):
      build: support z/OS (OS/390).

Harmen Stoppels (1):
      rebase: make warning less passive aggressive

Jakub Wilk (1):
      git-remote.txt: fix typo

Jean-Noël Avila (17):
      doc: git-rev-parse: enforce command-line description syntax
      doc: close unclosed angle-bracket of a placeholder in git-clone doc
      doc: end sentences with full-stop
      doc: clarify the format of placeholders
      doc: git-init: format verbatim parts
      doc: git-init: format placeholders
      doc: git-init: rework definition lists
      doc: git-init: rework config item init.templateDir
      doc: git-clone: format verbatim words
      doc: git-clone: format placeholders
      doc: format alternatives in synopsis
      doc: fix some placeholders formating
      doc: rework CodingGuidelines with new formatting rules
      doc: allow literal and emphasis format in doc vs help tests
      doc: git-init: apply new documentation formatting guidelines
      doc: git-clone: apply new documentation formatting guidelines
      doc: git-clone: do not autoreference the manpage in itself

Jeff Hostetler (17):
      name-hash: add index_dir_find()
      t7527: add case-insensitve test for FSMonitor
      fsmonitor: refactor refresh callback on directory events
      fsmonitor: clarify handling of directory events in callback helper
      fsmonitor: refactor refresh callback for non-directory events
      dir: create untracked_cache_invalidate_trimmed_path()
      fsmonitor: refactor untracked-cache invalidation
      fsmonitor: move untracked-cache invalidation into helper functions
      fsmonitor: return invalidated cache-entry count on directory event
      fsmonitor: remove custom loop from non-directory path handler
      fsmonitor: return invalidated cache-entry count on non-directory event
      fsmonitor: trace the new invalidated cache-entry count
      fsmonitor: refactor bit invalidation in refresh callback
      fsmonitor: support case-insensitive events
      t0211: demonstrate missing 'def_param' events for certain commands
      trace2: avoid emitting 'def_param' set more than once
      trace2: emit 'def_param' set with 'cmd_name' event

Jeff King (51):
      t0303: check that helper_test_clean removes all credentials
      userdiff: skip textconv caching when not in a repository
      Revert "refs: allow @{n} to work with n-sized reflog"
      get_oid_basic(): special-case ref@{n} for oldest reflog entry
      read_ref_at(): special-case ref@{0} for an empty reflog
      upload-pack: drop separate v2 "haves" array
      upload-pack: switch deepen-not list to an oid_array
      upload-pack: use oidset for deepen_not list
      upload-pack: use a strmap for want-ref lines
      upload-pack: accept only a single packfile-uri line
      upload-pack: always turn off save_commit_buffer
      upload-pack: use PARSE_OBJECT_SKIP_HASH_CHECK in more places
      upload-pack: free tree buffers after parsing
      upload-pack: use repository struct to get config
      upload-pack: centralize setup of sideband-all config
      upload-pack: use existing config mechanism for advertisement
      upload-pack: only accept packfile-uris if we advertised it
      doc/gitremote-helpers: fix missing single-quote
      config: forbid newline as core.commentChar
      strbuf: simplify comment-handling in add_lines() helper
      strbuf: avoid static variables in strbuf_add_commented_lines()
      commit: refactor base-case of adjust_comment_line_char()
      strbuf: avoid shadowing global comment_line_char name
      environment: store comment_line_char as a string
      strbuf: accept a comment string for strbuf_stripspace()
      strbuf: accept a comment string for strbuf_commented_addf()
      strbuf: accept a comment string for strbuf_add_commented_lines()
      prefer comment_line_str to comment_line_char for printing
      find multi-byte comment chars in NUL-terminated strings
      find multi-byte comment chars in unterminated buffers
      sequencer: handle multi-byte comment characters when writing todo list
      wt-status: drop custom comment-char stringification
      environment: drop comment_line_char compatibility macro
      config: allow multi-byte core.commentChar
      shortlog: stop setting pp.print_email_subject
      pretty: split oneline and email subject printing
      pretty: drop print_email_subject flag
      log: do not set up extra_headers for non-email formats
      format-patch: return an allocated string from log_write_email_headers()
      format-patch: simplify after-subject MIME header handling
      doc/gitremote-helpers: fix more missing single-quotes
      transport-helper: use write helpers more consistently
      transport-helper: drop "object-format <algo>" option
      transport-helper: send "true" value for object-format option
      contrib: drop hg-to-git script
      format-patch: fix leak of empty header string
      rebase: use child_process_clear() to clean
      config: add core.commentString
      http: reset POSTFIELDSIZE when clearing curl handle
      INSTALL: bump libcurl version to 7.21.3
      remote-curl: add Transfer-Encoding header only for older curl

Jiamu Sun (1):
      bugreport.c: fix a crash in `git bugreport` with `--no-suffix` option

Johannes Schindelin (22):
      merge-tree: accept 3 trees as arguments
      merge-tree: fail with a non-zero exit code on missing tree objects
      merge-ort: do check `parse_tree()`'s return value
      t4301: verify that merge-tree fails on missing blob objects
      Always check `parse_tree*()`'s return value
      cache-tree: avoid an unnecessary check
      fill_tree_descriptor(): mark error message for translation
      neue: remove a bogus empty file
      commit-reach(paint_down_to_common): plug two memory leaks
      commit-reach(repo_in_merge_bases_many): optionally expect missing commits
      commit-reach(repo_in_merge_bases_many): report missing commits
      commit-reach(paint_down_to_common): prepare for handling shallow commits
      commit-reach(paint_down_to_common): start reporting errors
      commit-reach(merge_bases_many): pass on "missing commits" errors
      commit-reach(get_merge_bases_many_0): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases): pass on "missing commits" errors
      commit-reach(get_octopus_merge_bases): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many): pass on "missing commits" errors
      commit-reach(repo_get_merge_bases_many_dirty): pass on errors
      merge-recursive: prepare for `merge_submodule()` to report errors
      merge-ort/merge-recursive: do report errors in `merge_submodule()`
      merge-tree: fix argument type of the `--merge-base` option

John Cai (1):
      t5300: fix test_with_bad_commit()

Jonas Wunderlich (1):
      doc: status.showUntrackedFiles does not take "false"

Josh Triplett (2):
      commit: avoid redundant scissor line with --cleanup=scissors -v
      commit: unify logic to avoid multiple scissors lines when merging

Julio Bacellari (1):
      doc: remove outdated information about interactive.singleKey

Junio C Hamano (61):
      apply: correctly reverse patch's pre- and post-image mode bits
      apply: code simplification
      t9210: do not rely on lazy fetching to fail
      git: --no-lazy-fetch option
      doc: add shortcut to "am --whitespace=<action>"
      doc: apply the new placeholder rules to git-add documentation
      compat: drop inclusion of <git-compat-util.h>
      Start the 2.45 cycle
      git: document GIT_NO_REPLACE_OBJECTS environment variable
      doc: clarify the wording on <git-compat-util.h> requirement
      git: extend --no-lazy-fetch to work across subprocesses
      The second batch
      The third batch
      test_i18ngrep: hard deprecate and forbid its use
      unpack: replace xwrite() loop with write_in_full()
      sideband: avoid short write(2)
      repack: check error writing to pack-objects subprocess
      clean: further clean-up of implementation around "--force"
      The fourth batch
      The fifth batch
      setup: notice more types of implicit bare repositories
      The sixth batch
      status: unify parsing of --untracked= and status.showUntrackedFiles
      status: allow --untracked=false and friends
      The seventh batch
      The eighth batch
      config: fix --comment formatting
      config: allow tweaking whitespace between value and comment
      diff.*Prefix: use camelCase in the doc and test titles
      The ninth batch
      apply: parse names out of "diff --git" more carefully
      The tenth batch
      The eleventh batch
      SubmittingPatches: release-notes entry experiment
      The twelfth batch
      t4126: make sure a directory with SP at the end is usable
      t4126: fix "funny directory name" test on Windows (again)
      advice: omit trailing whitespace
      checkout: omit "tracking" information on a detached HEAD
      The thirteenth batch
      t2104: style fixes
      The fourteenth batch
      revision: optionally record matches with pathspec elements
      The fifteenth batch
      CodingGuidelines: describe "export VAR=VAL" rule
      CodingGuidelines: quote assigned value in 'local var=$val'
      t: local VAR="VAL" (quote positional parameters)
      t: local VAR="VAL" (quote command substitution)
      t: local VAR="VAL" (quote ${magic-reference})
      t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
      t0610: local VAR="VAL" fix
      t1016: local VAR="VAL" fix
      config: do not leak excludes_file
      Makefile(s): do not enforce "all indents must be done with tab"
      The sixteenth batch
      t2104: style fixes
      The seventeenth batch
      The eighteenth batch
      The ninteenth batch
      The twentieth batch
      Git 2.45-rc0

Justin Tobler (3):
      reftable/stack: expose option to disable auto-compaction
      reftable/stack: add env to disable autocompaction
      reftable/stack: use geometric table compaction

Karthik Nayak (7):
      refs: introduce `is_pseudoref()` and `is_headref()`
      refs: extract out `loose_fill_ref_dir_regular_file()`
      refs: introduce `refs_for_each_include_root_refs()`
      ref-filter: rename 'FILTER_REFS_ALL' to 'FILTER_REFS_REGULAR'
      for-each-ref: add new option to include root refs
      update-ref: use {old,new}-oid instead of {old,new}value
      githooks: use {old,new}-oid instead of {old,new}-value

Kipras Melnikovas (1):
      mergetools: vimdiff: use correct tool's name when reading mergetool config

Kristoffer Haugsbakk (9):
      column: disallow negative padding
      column: guard against negative padding
      gitcli: drop mention of “non-dashed form”
      config: document `core.commentChar` as ASCII-only
      t3200: improve test style
      advice: make all entries stylistically consistent
      advice: use backticks for verbatim
      advice: use double quotes for regular quoting
      branch: advise about ref syntax rules

Linus Arver (10):
      trailer: free trailer_info _after_ all related usage
      shortlog: add test for de-duplicating folded trailers
      trailer: rename functions to use 'trailer'
      trailer: reorder format_trailers_from_commit() parameters
      trailer: move interpret_trailers() to interpret-trailers.c
      trailer_info_get(): reorder parameters
      format_trailers(): use strbuf instead of FILE
      format_trailer_info(): move "fast path" to caller
      format_trailers_from_commit(): indirectly call trailer_info_get()
      mailmap: change primary address for Linus Arver

M Hickford (1):
      libsecret: retrieve empty password

Matthias Aßhauer (1):
      Win32: detect unix socket support at runtime

Max Gautier (1):
      editorconfig: add Makefiles to "text files"

Michael Lohmann (2):
      revision: ensure MERGE_HEAD is a ref in prepare_show_merge
      revision: implement `git log --merge` also for rebase/cherry-pick/revert

Patrick Steinhardt (84):
      refs: introduce reftable backend
      ci: add jobs to test with the reftable backend
      refs/reftable: fix leak when copying reflog fails
      reftable/record: introduce function to compare records by key
      reftable/merged: allocation-less dropping of shadowed records
      reftable/merged: skip comparison for records of the same subiter
      reftable/pq: allocation-less comparison of entry keys
      reftable/block: swap buffers instead of copying
      reftable/record: don't try to reallocate ref record name
      reftable/reader: add comments to `table_iter_next()`
      t: move tests exercising the "files" backend
      t0410: convert tests to use DEFAULT_REPO_FORMAT prereq
      t1400: exercise reflog with gaps with reftable backend
      t1404: make D/F conflict tests compatible with reftable backend
      t1405: remove unneeded cleanup step
      t2011: exercise D/F conflicts with HEAD with the reftable backend
      t7003: ensure filter-branch prunes reflogs with the reftable backend
      git-difftool--helper: honor `--trust-exit-code` with `--dir-diff`
      dir-iterator: pass name to `prepare_next_entry_data()` directly
      dir-iterator: support iteration in sorted order
      refs/files: sort reflogs returned by the reflog iterator
      refs/files: sort merged worktree and common reflogs
      refs: always treat iterators as ordered
      refs: drop unused params from the reflog iterator callback
      refs: stop resolving ref corresponding to reflogs
      builtin/reflog: introduce subcommand to list reflogs
      builtin/clone: allow remote helpers to detect repo
      refs/reftable: don't fail empty transactions in repo without HEAD
      reftable/pq: use `size_t` to track iterator index
      reftable/merged: make `merged_iter` structure private
      reftable/merged: advance subiter on subsequent iteration
      reftable/merged: make subiters own their records
      reftable/merged: remove unnecessary null check for subiters
      reftable/merged: handle subiter cleanup on close only
      reftable/merged: circumvent pqueue with single subiter
      reftable/merged: avoid duplicate pqueue emptiness check
      reftable/record: reuse refname when decoding
      reftable/record: reuse refname when copying
      reftable/record: decode keys in place
      reftable: allow inlining of a few functions
      refs/reftable: precompute prefix length
      refs/reftable: reload correct stack when creating reflog iter
      reftable/record: convert old and new object IDs to arrays
      reftable/record: avoid copying author info
      reftable/record: reuse refnames when decoding log records
      reftable/record: reuse message when decoding log records
      reftable/record: use scratch buffer when decoding records
      refs/reftable: track last log record name via strbuf
      t0610: remove unused variable assignment
      lockfile: report when rollback fails
      reftable/stack: register new tables as tempfiles
      reftable/stack: register lockfiles during compaction
      reftable/stack: register compacted tables as tempfiles
      reftable/record: fix memory leak when decoding object records
      reftable/block: fix binary search over restart counter
      t5601: exercise clones with "includeIf.*.onbranch"
      reftable: fix tests being broken by NFS' delete-after-close semantics
      t7800: improve test descriptions with empty arguments
      t7800: use single quotes for test bodies
      t/README: document how to loop around test cases
      reftable/stack: fix error handling in `reftable_stack_init_addition()`
      reftable/error: discern locked/outdated errors
      reftable/stack: use error codes when locking fails during compaction
      reftable/stack: gracefully handle failed auto-compaction due to locks
      refs/reftable: print errors on compaction failure
      t/helper: drop pack-refs wrapper
      refs: move `struct pack_refs_opts` to where it's used
      refs: remove `PACK_REFS_ALL` flag
      refs/reftable: expose auto compaction via new flag
      builtin/pack-refs: release allocated memory
      builtin/pack-refs: introduce new "--auto" flag
      builtin/gc: move `struct maintenance_run_opts`
      t6500: extract objects with "17" prefix
      builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
      builtin/gc: pack refs when using `git maintenance run --auto`
      reftable/basics: fix return type of `binsearch()` to be `size_t`
      reftable/basics: improve `binsearch()` test
      reftable/refname: refactor binary search over refnames
      reftable/block: refactor binary search over restart points
      reftable/block: fix error handling when searching restart points
      reftable/record: extract function to decode key lengths
      reftable/block: avoid decoding keys when searching restart points
      t0610: make `--shared=` tests reusable
      t0610: execute git-pack-refs(1) with specified umask

Peter Hutterer (1):
      diff: add diff.srcPrefix and diff.dstPrefix configuration variables

Philippe Blain (5):
      merge-ort: turn submodule conflict suggestions into an advice
      ci(github): make Windows test artifacts name unique
      sequencer: allow disabling conflict advice
      builtin/am: allow disabling conflict advice
      t/README: mention test files are make targets

Phillip Wood (9):
      rebase -i: stop setting GIT_CHERRY_PICK_HELP
      xdiff-interface: refactor parsing of merge.conflictstyle
      merge-ll: introduce LL_MERGE_OPTIONS_INIT
      merge options: add a conflict style member
      checkout: cleanup --conflict=<style> parsing
      checkout: fix interaction between --conflict and --merge
      t3428: modernize test setup
      t3428: use test_commit_message
      t3428: restore coverage for "apply" backend

Pi Fisher (1):
      typo: replace 'commitish' with 'committish'

Ralph Seichter (1):
      config: add --comment option to add a comment

René Scharfe (28):
      use xstrncmpz()
      fetch: convert strncmp() with strlen() to starts_with()
      mem-pool: add mem_pool_strfmt()
      name-rev: use mem_pool_strfmt()
      submodule: use strvec_pushf() for --submodule-prefix
      t-ctype: allow NUL anywhere in the specification string
      t-ctype: simplify EOF check
      t-ctype: align output of i
      t-ctype: avoid duplicating class names
      parse-options: recognize abbreviated negated option with arg
      parse-options: set arg of abbreviated option lazily
      parse-options: factor out register_abbrev() and struct parsed_option
      parse-options: detect ambiguous self-negation
      parse-options: normalize arg and long_name before comparison
      parse-options: rearrange long_name matching code
      t-prio-queue: shorten array index message
      t-prio-queue: check result array bounds
      factor out strbuf_expand_bad_format()
      cat-file: use strbuf_expand_bad_format()
      midx: use strvec_pushf() for pack-objects base name
      mem-pool: use st_add() in mem_pool_strvfmt()
      imap-send: use xsnprintf to format command
      t-prio-queue: simplify using compound literals
      apply: avoid fixed-size buffer in create_one_file()
      path: remove mksnpath()
      apply: don't leak fd on fdopen() error
      usage: report vsnprintf(3) failure
      date: make DATE_MODE thread-safe

Richard Macklin (1):
      rebase: fix typo in autosquash documentation

Rubén Justo (13):
      tag: error when git-column fails
      completion: fix __git_complete_worktree_paths
      completion: reflog with implicit "show"
      completion: reflog show <log-options>
      completion: introduce __git_find_subcommand
      completion: factor out __git_resolve_builtins
      completion: reflog subcommands and options
      checkout: plug some leaks in git-restore
      add-patch: introduce 'p' in interactive-patch
      add-patch: do not print hunks repeatedly
      add: use advise_if_enabled for ADVICE_ADD_IGNORED_FILE
      add: use advise_if_enabled for ADVICE_ADD_EMPTY_PATHSPEC
      add: use advise_if_enabled for ADVICE_ADD_EMBEDDED_REPO

SZEDER Gábor (1):
      upload-pack: don't send null character in abort message to the client

Sergey Organov (1):
      clean: improve -n and -f implementation and documentation

Steven Jeuris (1):
      userdiff: better method/property matching for C#

Taylor Blau (8):
      Documentation/config/pack.txt: fix broken AsciiDoc mark-up
      upload-pack: disallow object-info capability by default
      midx-write: move writing-related functions from midx.c
      midx-write.c: factor out common want_included_pack() routine
      midx-write.c: check count of packs to repack after grouping
      midx-write.c: use `--stdin-packs` when repacking
      t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `
      Makefile(s): avoid recipe prefix in conditional statements

Ville Skyttä (2):
      completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode
      completion: protect prompt against unset SHOWUPSTREAM in nounset mode

Vincenzo Mezzela (1):
      t7301: use test_path_is_(missing|file)

brian m. carlson (7):
      loose: add a mapping between SHA-1 and SHA-256 for loose objects
      commit: write commits for both hashes
      cache: add a function to read an OID of a specific algorithm
      object-file-convert: add a function to convert trees between algorithms
      object-file-convert: convert tag objects when writing
      object-file-convert: convert commit objects when writing
      repository: implement extensions.compatObjectFormat

shejialuo (1):
      t9117: prefer test_path_* helper functions



^ permalink raw reply	[relevance 2%]

* [PATCH 05/11] remote-curl: fix parsing of detached SHA256 heads
  2024-04-19  9:51  2% [PATCH 00/11] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
@ 2024-04-19  9:51 10% ` Patrick Steinhardt
  2024-04-23  5:07  2% ` [PATCH v2 00/12] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
  2024-04-29  6:34  2% ` [PATCH v3 00/13] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
  2 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-19  9:51 UTC (permalink / raw)
  To: git

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

The dumb HTTP transport tries to read the remote HEAD reference by
downloading the "HEAD" file and then parsing it via `http_fetch_ref()`.
This function will either parse the file as an object ID in case it is
exactly `the_hash_algo->hexsz` long, or otherwise it will check whether
the reference starts with "ref :" and parse it as a symbolic ref.

This is broken when parsing detached HEADs of a remote SHA256 repository
because we never update `the_hash_algo` to the discovered remote object
hash. Consequently, `the_hash_algo` will always be the fallback SHA1
hash algorithm, which will cause us to fail parsing HEAD altogteher when
it contains a SHA256 object ID.

Fix this issue by setting up `the_hash_algo` via `repo_set_hash_algo()`.
While at it, let's make the expected SHA1 fallback explicit in our code,
which also addresses an upcoming issue where we are going to remove the
SHA1 fallback for `the_hash_algo`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c              | 19 ++++++++++++++++++-
 t/t5550-http-fetch-dumb.sh | 15 +++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/remote-curl.c b/remote-curl.c
index 0b6d7815fd..004b707fdf 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -266,12 +266,23 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 	return list;
 }
 
+/*
+ * Try to detect the hash algorithm used by the remote repository when using
+ * the dumb HTTP transport. As dumb transports cannot tell us the object hash
+ * directly have to derive it from the advertised ref lengths.
+ */
 static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
 {
 	const char *p = memchr(heads->buf, '\t', heads->len);
 	int algo;
+
+	/*
+	 * In case the remote has no refs we have no way to reliably determine
+	 * the object hash used by that repository. In that case we simply fall
+	 * back to SHA1, which may or may not be correct.
+	 */
 	if (!p)
-		return the_hash_algo;
+		return &hash_algos[GIT_HASH_SHA1];
 
 	algo = hash_algo_by_length((p - heads->buf) / 2);
 	if (algo == GIT_HASH_UNKNOWN)
@@ -295,6 +306,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
 		    "is this a git repository?",
 		    transport_anonymize_url(url.buf));
 
+	/*
+	 * Set the repository's hash algo to whatever we have just detected.
+	 * This ensures that we can correctly parse the remote references.
+	 */
+	repo_set_hash_algo(the_repository, hash_algo_by_ptr(options.hash_algo));
+
 	data = heads->buf;
 	start = NULL;
 	mid = data;
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 4c3b32785d..5f16cbc58d 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -55,6 +55,21 @@ test_expect_success 'list refs from outside any repository' '
 	test_cmp expect actual
 '
 
+
+test_expect_success 'list detached HEAD from outside any repository' '
+	git clone --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
+		"$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" \
+		update-ref --no-deref HEAD refs/heads/main &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo-detached.git" update-server-info &&
+	cat >expect <<-EOF &&
+	$(git rev-parse main)	HEAD
+	$(git rev-parse main)	refs/heads/main
+	EOF
+	nongit git ls-remote "$HTTPD_URL/dumb/repo-detached.git" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'create password-protected repository' '
 	mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
 	cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 10%]

* [PATCH 00/11] Stop relying on SHA1 fallback for `the_hash_algo`
@ 2024-04-19  9:51  2% Patrick Steinhardt
  2024-04-19  9:51 10% ` [PATCH 05/11] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
                   ` (2 more replies)
  0 siblings, 3 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-19  9:51 UTC (permalink / raw)
  To: git

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

Hi,

when starting up, Git will initialize `the_repository` by calling
`initialize_the_repository()`. Part of this is also that we set the hash
algo of `the_repository` to SHA1, which implicitly sets `the_hash_algo`
because it is a macro expanding to `the_repository->hash_algo`.

Usually, we eventually set up the correct hash algorithm here once we
have properly set up `the_repository` via `setup_git_directory()`. But
in some commands we actually don't require a Git repository, and thus we
will leave the SHA1 hash algorithm in place.

This has led to some subtle bugs when the context really asks for a
SHA256 repository, which this patch series corrects for most of the
part. Some commands need further work, like for example git-diff(1),
where the user might want to have the ability to pick a hash function
when run outside of a repository.

Ultimately, the last patch then drops the setup of the fallback hash
algorithm completely. This will cause `the_hash_algo` to be a `NULL`
pointer unless explicitly configured, and thus we now start to crash
when it gets accessed without doing so beforehand. This is a rather
risky thing to do, but it does catch bugs where we might otherwise
accidentally do the wrong thing. And even though I think it is the right
thing to do even conceptually, I'd be okay to drop it if folks think
that the risk is not worth it.

Patrick

Patrick Steinhardt (11):
  path: harden validation of HEAD with non-standard hashes
  parse-options-cb: only abbreviate hashes when hash algo is known
  attr: don't recompute default attribute source
  attr: fix BUG() when parsing attrs outside of repo
  remote-curl: fix parsing of detached SHA256 heads
  builtin/rev-parse: allow shortening to more than 40 hex characters
  builtin/blame: don't access potentially unitialized `the_hash_algo`
  builtin/bundle: abort "verify" early when there is no repository
  builtin/diff: explicitly set hash algo when there is no repo
  builtin/shortlog: don't set up revisions without repo
  repository: stop setting SHA1 as the default object hash

 attr.c                     | 31 ++++++++++++++++++++++---------
 builtin/blame.c            |  5 ++---
 builtin/bundle.c           |  5 +++++
 builtin/diff.c             |  9 +++++++++
 builtin/rev-parse.c        |  5 ++---
 builtin/shortlog.c         |  2 +-
 parse-options-cb.c         |  3 ++-
 path.c                     |  2 +-
 remote-curl.c              | 19 ++++++++++++++++++-
 repository.c               |  2 --
 t/t0003-attributes.sh      | 15 +++++++++++++++
 t/t0040-parse-options.sh   | 17 +++++++++++++++++
 t/t1500-rev-parse.sh       |  6 ++++++
 t/t5550-http-fetch-dumb.sh | 15 +++++++++++++++
 14 files changed, 115 insertions(+), 21 deletions(-)

-- 
2.44.GIT


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

^ permalink raw reply	[relevance 2%]

* [PATCH v2 0/8] Make trailer_info struct private (plus sequencer cleanup)
  @ 2024-04-19  5:22  3% ` Linus Arver via GitGitGadget
  2024-04-26  0:26  2%   ` [PATCH v3 00/10] " Linus Arver via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-19  5:22 UTC (permalink / raw)
  To: git
  Cc: Christian Couder, Junio C Hamano, Emily Shaffer, Josh Steadmon,
	Randall S. Becker, Christian Couder, Kristoffer Haugsbakk,
	Linus Arver

NOTE: This series is based on the la/format-trailer-info topic branch (see
its discussion at [1]).

This series is based on the initial series [2], notably the v4 version of
patches 17-20 as suggested by Christian [3]. This version addresses the
review comments for those patches, namely the splitting up of Patch 19 there
into 3 separate patches [4] (as Patches 05-07 here) .

The central idea is to make the trailer_info struct private (that is, move
its definition from trailer.h to trailer.c) --- aka the "pimpl" idiom. See
the detailed commit message for Patch 07 for the motivation behind the
change.

Patch 04 makes sequencer.c a well-behaved trailer API consumer, by making
use of the trailer iterator. Patch 03 prepares us for Patch 04. Patch 08
slightly reduces the weight of the API by removing (from the API surface) an
unused function.


Notable changes in v2
=====================

 * Add unit tests at the beginning of the series (Patches 01 and 02) and use
   it to verify that the other edge cases remain unchanged when we add the
   "raw" member (Patch 03)

[1]
https://lore.kernel.org/git/pull.1694.git.1710485706.gitgitgadget@gmail.com/
[2]
https://lore.kernel.org/git/pull.1632.v4.git.1707196348.gitgitgadget@gmail.com/
[3]
https://lore.kernel.org/git/CAP8UFD08F0V13X0+CJ1uhMPzPWVMs2okGVMJch0DkQg5M3BWLA@mail.gmail.com/
[4]
https://lore.kernel.org/git/CAP8UFD1twELGKvvesxgCrZrypKZpgSt04ira3mvurG1UbpDfxQ@mail.gmail.com/

Linus Arver (8):
  Makefile: sort UNIT_TEST_PROGRAMS
  trailer: add unit tests for trailer iterator
  trailer: teach iterator about non-trailer lines
  sequencer: use the trailer iterator
  interpret-trailers: access trailer_info with new helpers
  trailer: make parse_trailers() return trailer_info pointer
  trailer: make trailer_info struct private
  trailer: retire trailer_info_get() from API

 Makefile                     |   5 +-
 builtin/interpret-trailers.c |  12 +--
 sequencer.c                  |  27 +++---
 t/unit-tests/t-trailer.c     | 181 +++++++++++++++++++++++++++++++++++
 trailer.c                    | 161 +++++++++++++++++++------------
 trailer.h                    |  46 ++++-----
 6 files changed, 321 insertions(+), 111 deletions(-)
 create mode 100644 t/unit-tests/t-trailer.c


base-commit: 3452d173241c8b87ecdd67f91f594cb14327e394
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1696%2Flistx%2Ftrailer-api-part-3-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1696/listx/trailer-api-part-3-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1696

Range-diff vs v1:

 -:  ----------- > 1:  b6a1304f8ae Makefile: sort UNIT_TEST_PROGRAMS
 -:  ----------- > 2:  e1fa05143ac trailer: add unit tests for trailer iterator
 1:  32ad0397737 ! 3:  5520a98e296 trailer: teach iterator about non-trailer lines
     @@ Commit message
      
          Signed-off-by: Linus Arver <linusa@google.com>
      
     + ## t/unit-tests/t-trailer.c ##
     +@@ t/unit-tests/t-trailer.c: static void run_t_trailer_iterator(void)
     + 			"not a trailer line\n"
     + 			"not a trailer line\n"
     + 			"Signed-off-by: x\n",
     +-			1
     ++			/*
     ++			 * Even though there is only really 1 real "trailer"
     ++			 * (Signed-off-by), we still have 4 trailer objects
     ++			 * because we still want to iterate through the entire
     ++			 * block.
     ++			 */
     ++			4
     + 		},
     + 		{
     + 			"with non-trailer lines (one too many) in trailer block",
     +
       ## trailer.c ##
      @@ trailer.c: void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
       
 2:  dc873c3b820 = 4:  84897cf5c83 sequencer: use the trailer iterator
 3:  872e67286c8 = 5:  e961d49cd40 interpret-trailers: access trailer_info with new helpers
 4:  c55ae2cbda9 = 6:  093f68f3658 trailer: make parse_trailers() return trailer_info pointer
 5:  cf59dee5064 = 7:  0e9ae049b88 trailer: make trailer_info struct private
 6:  19de7c64171 = 8:  eca77a1a462 trailer: retire trailer_info_get() from API

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [PATCH v6 0/8] docs: recommend using contrib/contacts/git-contacts
  2024-04-16 23:01  2%       ` [PATCH v5 " Linus Arver via GitGitGadget
@ 2024-04-18 21:51  3%         ` Linus Arver via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-18 21:51 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Patrick Steinhardt, Matthieu Moy, Eric Sunshine,
	Kipras Melnikovas, Linus Arver, Linus Arver

Make git-contacts more prominent in our docs.


Notable changes in v6
=====================

 * Prefix the command with "perl" to avoid the need to have it installed at
   /usr/bin/perl per the shebang line in git-contacts.
 * Drop "you must have Perl installed in your system" guidance because it's
   a bit moot now given the explicit call to "perl".


Notable changes in v5
=====================

 * Drop mention of "/usr/share/..." as an "installed" path for
   "git-contacts"; instead point users to the script as a relative path
   inside the Git codebase
 * Minor wording tweaks to commit messages


Notable changes in v4
=====================

 * Avoid using "should" for guidance around using "git-contacts"
 * Clarify where to find the "git-contacts" script (because it's not a
   default builtin command)


Notable changes in v3
=====================

 * Refer to GitGitGadget via a link to MyFirstContribution (instead of
   sending readers to GGG's homepage directly)
 * Soften the advice for using git-contacts


Notable changes in v2
=====================

 * Improve existing mention of git-contacts in SubmittingPatches (instead of
   adding a separate, entirely new paragraph)
 * Add example usage of integrating git-contacts with git-send-email with
   the latter's --cc-cmd flag.
 * Various smaller fixes to SubmittingPatches

Linus Arver (8):
  MyFirstContribution: mention contrib/contacts/git-contacts
  SubmittingPatches: clarify 'git-contacts' location
  SubmittingPatches: mention GitGitGadget
  SubmittingPatches: quote commands
  SubmittingPatches: discuss reviewers first
  SubmittingPatches: dedupe discussion of security patches
  SubmittingPatches: add heading for format-patch and send-email
  SubmittingPatches: demonstrate using git-contacts with git-send-email

 Documentation/MyFirstContribution.txt |  9 ++++
 Documentation/SubmittingPatches       | 72 ++++++++++++++++-----------
 2 files changed, 51 insertions(+), 30 deletions(-)


base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

Range-diff vs v5:

 1:  d2c9551ee0e ! 1:  4ced981b82e MyFirstContribution: mention contrib/contacts/git-contacts
     @@ Documentation/MyFirstContribution.txt: $ git send-email --to=target@example.com
       
      +:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
      +not part of the core `git` binary and must be called directly. Clone the Git +
     -+codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     -+in your system).]
     ++codebase and run `perl contrib/contacts/git-contacts`.]
      +
      +NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
      +list potential reviewers. In addition, you can do `git send-email
     -+--cc-cmd='contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
     ++--cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
      +automatically pass this list of emails to `send-email`.
      +
       NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
 2:  92d72a8a25a ! 2:  f26f0695f40 SubmittingPatches: clarify 'git-contacts' location
     @@ Documentation/SubmittingPatches: security relevant should not be submitted to th
       
      +:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
      +not part of the core `git` binary and must be called directly. Clone the Git +
     -+codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     -+in your system).]
     ++codebase and run `perl contrib/contacts/git-contacts`.]
      +
       Send your patch with "To:" set to the mailing list, with "cc:" listing
      -people who are involved in the area you are touching (the `git
 3:  7c4cc5a91f0 = 3:  c201b313644 SubmittingPatches: mention GitGitGadget
 4:  621912a64fb = 4:  0a79615cf2f SubmittingPatches: quote commands
 5:  8f44343c482 ! 5:  aac5dea0bfa SubmittingPatches: discuss reviewers first
     @@ Documentation/SubmittingPatches: letter.
      +
      +:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
      +not part of the core `git` binary and must be called directly. Clone the Git +
     -+codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     -+in your system).]
     ++codebase and run `perl contrib/contacts/git-contacts`.]
      +
      +Send your patch with "To:" set to the mailing list, with "cc:" listing
      +people who are involved in the area you are touching (the `git-contacts`
     @@ Documentation/SubmittingPatches: patch, format it as "multipart/signed", not a t
      -
      -:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
      -not part of the core `git` binary and must be called directly. Clone the Git +
     --codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     --in your system).]
     +-codebase and run `perl contrib/contacts/git-contacts`.]
      -
      -Send your patch with "To:" set to the mailing list, with "cc:" listing
      -people who are involved in the area you are touching (the `git-contacts`
 6:  fd8ad38cab0 = 6:  333775d4129 SubmittingPatches: dedupe discussion of security patches
 7:  b23c73459cc = 7:  ef031e30047 SubmittingPatches: add heading for format-patch and send-email
 8:  911d4f2a0e5 ! 8:  f346da95ee2 SubmittingPatches: demonstrate using git-contacts with git-send-email
     @@ Documentation/SubmittingPatches: trial merges of your topic to `next` and `seen`
      +this:
      +
      +....
     -+	git send-email --cc-cmd='contrib/contacts/git-contacts' feature/*.patch
     ++	git send-email --cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch
      +....
      +
       :current-maintainer: footnote:[The current maintainer: gitster@pobox.com]

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: [PATCH 0/4] upload-pack: support a missing-action
  2024-04-18 18:40  3% [PATCH 0/4] upload-pack: support a missing-action Christian Couder
  2024-04-18 18:40 10% ` [PATCH 4/4] upload-pack: allow configuring " Christian Couder
@ 2024-04-18 19:21  5% ` Junio C Hamano
  1 sibling, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-18 19:21 UTC (permalink / raw)
  To: Christian Couder; +Cc: git, John Cai, Patrick Steinhardt

Christian Couder <christian.couder@gmail.com> writes:

> `git pack-objects` already supports a `--missing=<missing-action>`
> option, so that it can avoid erroring out if some objects aren't
> available.
>
> It is interesting to have `git upload-pack` support a similar way to
> avoid sending some objects in case they aren't available on the
> server.

Is it interesting?  In what way?

> For example, in case both the server and the client are using a
> separate promisor remote that contain some objects, it can be better
> if the server doesn't try to send such objects back to the client, but
> instead let the client get those objects separately from the promisor
> remote. (The client needs to have the separate promisor remote
> configured, for that to work.)

It is unclear what the precondition for such an arrangement to work
reliably, and a lot more importantly, how we can validate that the
precondition holds when "fetch" talks to "upload-pack".  If you get
it wrong, you'd have a server that would corrupt repositories that
fetch from it.

That is where my "Is it really interesting?  I do not find your
explanation convincing yet." above primarily comes from.


Presumably "fetch" could tell "upload-pack" something like:

	I know how to fetch missing objects from this and that
	promisor remotes, so if you choose to, you may omit objects
	that you know are available from these promisor remotes when
	sending objects to me.

using a new capability, and we can allow upload-pack to omit objects
only when such a new capability tells it to?


^ permalink raw reply	[relevance 5%]

* [PATCH 4/4] upload-pack: allow configuring a missing-action
  2024-04-18 18:40  3% [PATCH 0/4] upload-pack: support a missing-action Christian Couder
@ 2024-04-18 18:40 10% ` Christian Couder
  2024-04-18 19:21  5% ` [PATCH 0/4] upload-pack: support " Junio C Hamano
  1 sibling, 0 replies; 200+ results
From: Christian Couder @ 2024-04-18 18:40 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, John Cai, Patrick Steinhardt, Christian Couder

From: Christian Couder <chriscool@tuxfamily.org>

In case some objects are missing from a server, it might still be
useful to be able to fetch or clone from it if the client already has
the missing objects or can get them in some way.

For example, in case both the server and the client are using a
separate promisor remote that contain some objects, it can be better
if the server doesn't try to send such objects back to the client, but
instead let the client get those objects separately from the promisor
remote. (The client needs to have the separate promisor remote
configured, for that to work.)

Another example could be a server where some objects have been
corrupted or deleted. It could still be useful for clients who could
get those objects from another source, like perhaps a different
client, to be able to fetch or clone from the server.

To configure what a server should do if there are such missing
objects, let's teach `git upload-pack` a new
`uploadpack.missingAction` configuration variable.

This missing-action works in a similar way as what `git rev-list` and
`git pack-objects` already support with their
`--missing=<missing-action>` command line options. In fact when
`uploadpack.missingAction` is set to something different than "error",
`git upload-pack` will just pass the corresponding
`--missing=<missing-action>` to `git pack-objects`.

So the `uploadpack.missingAction` has the same limitations as
`git pack-objects --missing=<missing-action>`. Especially when not
using promisor remotes, 'allow-any' works only for blobs.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 Documentation/config/uploadpack.txt |   9 ++
 missing.c                           |  16 ++++
 missing.h                           |   2 +
 t/t5706-upload-pack-missing.sh      | 125 ++++++++++++++++++++++++++++
 upload-pack.c                       |  19 +++++
 5 files changed, 171 insertions(+)
 create mode 100755 t/t5706-upload-pack-missing.sh

diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt
index 16264d82a7..cd70e853b3 100644
--- a/Documentation/config/uploadpack.txt
+++ b/Documentation/config/uploadpack.txt
@@ -82,3 +82,12 @@ uploadpack.allowRefInWant::
 	is intended for the benefit of load-balanced servers which may
 	not have the same view of what OIDs their refs point to due to
 	replication delay.
+
+uploadpack.missingAction::
+	If this option is set, `upload-pack` will call
+	linkgit:git-pack-objects[1] passing it the corresponding
+	`--missing=<missing-action>` command line option. See the
+	documentation for that option, to see the valid values and
+	their meaning. This could be useful if some objects are
+	missing on a server, but a client already has them or could
+	still get them from somewhere else.
diff --git a/missing.c b/missing.c
index d306dea2d9..022ebbe4be 100644
--- a/missing.c
+++ b/missing.c
@@ -24,3 +24,19 @@ int parse_missing_action_value(const char *value, int print_ok)
 
 	return -1;
 }
+
+const char *missing_action_to_string(enum missing_action action)
+{
+	switch (action) {
+	case MA_ERROR:
+		return "error";
+	case MA_ALLOW_ANY:
+		return "allow-any";
+	case MA_PRINT:
+		return "print";
+	case MA_ALLOW_PROMISOR:
+		return "allow-promisor";
+	default:
+		BUG("invalid missing action %d", action);
+	}
+}
diff --git a/missing.h b/missing.h
index 77906691e7..ca574f83a0 100644
--- a/missing.h
+++ b/missing.h
@@ -15,4 +15,6 @@ enum missing_action {
  */
 int parse_missing_action_value(const char *value, int print_ok);
 
+const char *missing_action_to_string(enum missing_action action);
+
 #endif /* MISSING_H */
diff --git a/t/t5706-upload-pack-missing.sh b/t/t5706-upload-pack-missing.sh
new file mode 100755
index 0000000000..21bb6aed2b
--- /dev/null
+++ b/t/t5706-upload-pack-missing.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+test_description='handling of missing objects in upload-pack'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# Setup the repository with three commits, this way HEAD is always
+# available and we can hide commit 1 or 2.
+test_expect_success 'setup: create "template" repository' '
+	git init template &&
+	test_commit -C template 1 &&
+	test_commit -C template 2 &&
+	test_commit -C template 3 &&
+	test-tool genrandom foo 10240 >template/foo &&
+	git -C template add foo &&
+	git -C template commit -m foo
+'
+
+# A bare repo will act as a server repo with unpacked objects.
+test_expect_success 'setup: create bare "server" repository' '
+	git clone --bare --no-local template server &&
+	mv server/objects/pack/pack-* . &&
+	packfile=$(ls pack-*.pack) &&
+	git -C server unpack-objects --strict <"$packfile"
+'
+
+# Fetching with 'uploadpack.missingAction=allow-any' only works with
+# blobs, as `git pack-objects --missing=allow-any` fails if a missing
+# object is not a blob.
+test_expect_success "fetch with uploadpack.missingAction=allow-any" '
+	oid="$(git -C server rev-parse HEAD:1.t)" &&
+	oid_path="$(test_oid_to_path $oid)" &&
+	path="server/objects/$oid_path" &&
+
+	mv "$path" "$path.hidden" &&
+	test_when_finished "mv $path.hidden $path" &&
+
+	git init client &&
+	test_when_finished "rm -rf client" &&
+
+	# Client needs the missing objects to be available somehow
+	client_path="client/.git/objects/$oid_path" &&
+	mkdir -p $(dirname "$client_path") &&
+	cp "$path.hidden" "$client_path" &&
+
+	test_must_fail git -C client fetch ../server &&
+	git -C server config uploadpack.missingAction error &&
+	test_must_fail git -C client fetch ../server &&
+	git -C server config uploadpack.missingAction allow-any &&
+	git -C client fetch ../server &&
+	git -C server config --unset uploadpack.missingAction
+'
+
+check_missing_objects () {
+	git -C "$1" rev-list --objects --all --missing=print > all.txt &&
+	sed -n "s/^\?\(.*\)/\1/p" <all.txt >missing.txt &&
+	test_line_count = "$2" missing.txt &&
+	test "$3" = "$(cat missing.txt)"
+}
+
+test_expect_success "setup for testing uploadpack.missingAction=allow-promisor" '
+	# Create another bare repo called "server2"
+	git init --bare server2 &&
+
+	# Copy the largest object from server to server2
+	obj="HEAD:foo" &&
+	oid="$(git -C server rev-parse $obj)" &&
+	oid_path="$(test_oid_to_path $oid)" &&
+	path="server/objects/$oid_path" &&
+	path2="server2/objects/$oid_path" &&
+	mkdir -p $(dirname "$path2") &&
+	cp "$path" "$path2" &&
+
+	# Repack everything first
+	git -C server -c repack.writebitmaps=false repack -a -d &&
+
+	# Repack without the largest object and create a promisor pack on server
+	git -C server -c repack.writebitmaps=false repack -a -d \
+	    --filter=blob:limit=5k --filter-to="$(pwd)" &&
+	promisor_file=$(ls server/objects/pack/*.pack | sed "s/\.pack/.promisor/") &&
+	> "$promisor_file" &&
+
+	# Check that only one object is missing on the server
+	check_missing_objects server 1 "$oid" &&
+
+	# Configure server2 as promisor remote for server
+	git -C server remote add server2 "file://$(pwd)/server2" &&
+	git -C server config remote.server2.promisor true &&
+
+	git -C server2 config uploadpack.allowFilter true &&
+	git -C server2 config uploadpack.allowAnySHA1InWant true &&
+	git -C server config uploadpack.allowFilter true &&
+	git -C server config uploadpack.allowAnySHA1InWant true
+'
+
+test_expect_success "fetch with uploadpack.missingAction=allow-promisor" '
+	git -C server config uploadpack.missingAction allow-promisor &&
+
+	# Clone from server to create a client
+	git clone -c remote.server2.promisor=true \
+		-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
+		-c remote.server2.url="file://$(pwd)/server2" \
+		--no-local --filter="blob:limit=5k" server client &&
+	test_when_finished "rm -rf client" &&
+
+	# Check that the largest object is still missing on the server
+	check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "fetch without uploadpack.missingAction=allow-promisor" '
+	git -C server config --unset uploadpack.missingAction &&
+
+	# Clone from server to create a client
+	git clone -c remote.server2.promisor=true \
+		-c remote.server2.fetch="+refs/heads/*:refs/remotes/server2/*" \
+		-c remote.server2.url="file://$(pwd)/server2" \
+		--no-local --filter="blob:limit=5k" server client &&
+	test_when_finished "rm -rf client" &&
+
+	# Check that the largest object is not missing on the server anymore
+	check_missing_objects server 0 ""
+'
+
+test_done
diff --git a/upload-pack.c b/upload-pack.c
index 902144b9d3..c355ebe1e5 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -29,6 +29,8 @@
 #include "write-or-die.h"
 #include "json-writer.h"
 #include "strmap.h"
+#include "missing.h"
+#include "object-file.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE	(1u << 11)
@@ -96,6 +98,8 @@ struct upload_pack_data {
 
 	const char *pack_objects_hook;
 
+	enum missing_action missing_action;
+
 	unsigned stateless_rpc : 1;				/* v0 only */
 	unsigned no_done : 1;					/* v0 only */
 	unsigned daemon_mode : 1;				/* v0 only */
@@ -315,6 +319,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,
 		strvec_push(&pack_objects.args, "--delta-base-offset");
 	if (pack_data->use_include_tag)
 		strvec_push(&pack_objects.args, "--include-tag");
+	if (pack_data->missing_action)
+		strvec_pushf(&pack_objects.args, "--missing=%s",
+			     missing_action_to_string(pack_data->missing_action));
 	if (pack_data->filter_options.choice) {
 		const char *spec =
 			expand_list_objects_filter_spec(&pack_data->filter_options);
@@ -1371,6 +1378,18 @@ static int upload_pack_config(const char *var, const char *value,
 		precomposed_unicode = git_config_bool(var, value);
 	} else if (!strcmp("transfer.advertisesid", var)) {
 		data->advertise_sid = git_config_bool(var, value);
+	} else if (!strcmp("uploadpack.missingaction", var)) {
+		int res = parse_missing_action_value(value, 0);
+		if (res < 0)
+			die(_("invalid value for '%s': '%s'"), var, value);
+		/*
+		 * parse_missing_action_value() unsets fetch_if_missing
+		 * but if we allow promisor we want to still fetch from
+		 * the promisor remote
+		 */
+		if (res == MA_ALLOW_PROMISOR)
+			fetch_if_missing =1;
+		data->missing_action = res;
 	}
 
 	if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
-- 
2.44.0.655.g111bceeb19



^ permalink raw reply related	[relevance 10%]

* [PATCH 0/4] upload-pack: support a missing-action
@ 2024-04-18 18:40  3% Christian Couder
  2024-04-18 18:40 10% ` [PATCH 4/4] upload-pack: allow configuring " Christian Couder
  2024-04-18 19:21  5% ` [PATCH 0/4] upload-pack: support " Junio C Hamano
  0 siblings, 2 replies; 200+ results
From: Christian Couder @ 2024-04-18 18:40 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, John Cai, Patrick Steinhardt, Christian Couder

`git pack-objects` already supports a `--missing=<missing-action>`
option, so that it can avoid erroring out if some objects aren't
available.

It is interesting to have `git upload-pack` support a similar way to
avoid sending some objects in case they aren't available on the
server.

For example, in case both the server and the client are using a
separate promisor remote that contain some objects, it can be better
if the server doesn't try to send such objects back to the client, but
instead let the client get those objects separately from the promisor
remote. (The client needs to have the separate promisor remote
configured, for that to work.)

Another example could be a server where some objects have been
corrupted or deleted. It could still be useful for clients who could
get those objects from another source, like perhaps a different
client, to be able to fetch or clone from the server.

As `git rev-list` also supports a `--missing=<missing-action>` option,
the first 3 patches in this series are about refactoring related code
from both `git rev-list` and `git pack-objects` into new
"missing.{c,h}" files. Patch 4/4 then adds a new
`uploadpack.missingAction` configuration variable.

Christian Couder (4):
  rev-list: refactor --missing=<missing-action>
  missing: support rejecting --missing=print
  pack-objects: use the missing action API
  upload-pack: allow configuring a missing-action

 Documentation/config/uploadpack.txt |   9 ++
 Makefile                            |   1 +
 builtin/pack-objects.c              |  46 +++++-----
 builtin/rev-list.c                  |  41 ++-------
 missing.c                           |  42 ++++++++++
 missing.h                           |  20 +++++
 t/t5706-upload-pack-missing.sh      | 125 ++++++++++++++++++++++++++++
 upload-pack.c                       |  19 +++++
 8 files changed, 244 insertions(+), 59 deletions(-)
 create mode 100644 missing.c
 create mode 100644 missing.h
 create mode 100755 t/t5706-upload-pack-missing.sh

-- 
2.44.0.655.g111bceeb19



^ permalink raw reply	[relevance 3%]

* [PATCH 0/5] rebase -m: fix --signoff with conflicts
@ 2024-04-18 13:14  3% Phillip Wood
  0 siblings, 0 replies; 200+ results
From: Phillip Wood @ 2024-04-18 13:14 UTC (permalink / raw)
  To: git; +Cc: David Bimmler, Johannes Schindelin

From: Phillip Wood <phillip.wood@dunelm.org.uk>

When rebasing with "--signoff" the commit created by "rebase --continue"
after resolving conflicts or editing a commit fails to add the
"Signed-off-by:" trailer. This happens because the message from the
original commit is reused instead of the one that would have been used
if the sequencer had not stopped for the user interaction. This series
fixes that by introducing an strbuf to hold the message which we then
write to rebase_path_message() when stopping for user interaction.

The patches are structured as follows:

Patches 1–3 add private "struct replay_ctx" to "struct replay_opts"
and move the private members from the latter into the former. These
changes are largely mechanical.

Patch 4 adds strbuf to "struct replay_ctx" to hold the commit
message. This change is also largely mechanical.

Patch 5 fixes the bug by using the changes in patch 4 to write the
correct message to disc when stopping for user interaction.

This series is based on a merge of 'maint' and 'pw/t3428-cleanup'. In
principle it would be passible to avoid the refactoring in patches 1–3
and add a new member to "struct replay_opts" instead. However the
changes in those patches are largely mechanical so should be low-risk
and they pave the way for more improvements and bug fixes in the
future.


base-commit: 7bd541c104a2e383dd250f5f8e514785c4cabbc0
Published-As: https://github.com/phillipwood/git/releases/tag/pw%2Frebase-fix-signoff%2Fv1
View-Changes-At: https://github.com/phillipwood/git/compare/7bd541c10...4c8f88437
Fetch-It-Via: git fetch https://github.com/phillipwood/git pw/rebase-fix-signoff/v1

Phillip Wood (5):
  sequencer: always free "struct replay_opts"
  sequencer: start removing private fields from public API
  sequencer: move current fixups to private context
  sequencer: store commit message in private context
  rebase -m: fix --signoff with conflicts

 sequencer.c               | 250 ++++++++++++++++++++++++--------------
 sequencer.h               |  11 +-
 t/t3428-rebase-signoff.sh |  96 ++++++++++++---
 t/t3434-rebase-i18n.sh    |   2 +-
 4 files changed, 243 insertions(+), 116 deletions(-)


base-commit: 7bd541c104a2e383dd250f5f8e514785c4cabbc0
-- 
2.44.0.661.ge68cfcc6c2f



^ permalink raw reply	[relevance 3%]

* [PATCH v2 0/2] Use a "best effort" strategy in scheduled maintenance
  2024-04-17  8:28  4% [PATCH 0/2] Use a "best effort" strategy in scheduled maintenance Johannes Schindelin via GitGitGadget
@ 2024-04-18 12:53  3% ` Johannes Schindelin via GitGitGadget
  2024-04-24 16:14  3%   ` [PATCH v3 " Johannes Schindelin via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Johannes Schindelin via GitGitGadget @ 2024-04-18 12:53 UTC (permalink / raw)
  To: git; +Cc: Eric Sunshine, Johannes Schindelin

Over in https://github.com/microsoft/git/issues/623, it was pointed out that
scheduled maintenance will error out when it encounters a missing
repository. The scheduled maintenance should exit with an error, all right,
but what about the remaining repositories for which maintenance was
scheduled, and that may not be missing?

This patch series addresses this by introducing a new for-each-repo option
and then using it in the command that is run via scheduled maintenance.

Changes since v1 (thanks Eric!):

 * Changed the option's documentation to reflect the current state (instead
   of the original design)
 * Fixed grammar issues

Johannes Schindelin (2):
  for-each-repo: optionally keep going on an error
  maintenance: running maintenance should not stop on errors

 Documentation/git-for-each-repo.txt |  4 ++++
 builtin/for-each-repo.c             |  8 ++++++--
 builtin/gc.c                        |  7 ++++---
 t/t0068-for-each-repo.sh            | 16 ++++++++++++++++
 t/t7900-maintenance.sh              |  6 +++---
 5 files changed, 33 insertions(+), 8 deletions(-)


base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1719%2Fdscho%2Ffor-each-repo-stop-on-error-2.44-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1719/dscho/for-each-repo-stop-on-error-2.44-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1719

Range-diff vs v1:

 1:  6721e3ada5d ! 1:  abd796894c8 for-each-repo: optionally keep going on an error
     @@ Commit message
      
          This is undesirable, and points out a gap in the design of `git
          for-each-repo`: We need a mode where that command does not stop on an
     -    error, but continues to try the running the specified command with the
     -    other repositories.
     +    error, but continues to try running the specified command with the other
     +    repositories.
      
          Imitating the `--keep-going` option of GNU make, this commit teaches
          `for-each-repo` the same trick: to continue with the operation on all
          the remaining repositories in case there was a problem with one
          repository, still setting the exit code to indicate an error occurred.
      
     +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
      
       ## Documentation/git-for-each-repo.txt ##
     @@ builtin/for-each-repo.c: int cmd_for_each_repo(int argc, const char **argv, cons
       		OPT_STRING(0, "config", &config_key, N_("config"),
       			   N_("config key storing a list of repository paths")),
      +		OPT_BOOL(0, "keep-going", &keep_going,
     -+			 N_("stop at the first repository where the operation failed")),
     ++			 N_("keep going even if command fails in a repository")),
       		OPT_END()
       	};
       
 2:  a86bcf2e1a0 = 2:  1ae11553052 maintenance: running maintenance should not stop on errors

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [PATCH v2 2/6] builtin: stop using `the_index`
  @ 2024-04-18 12:14  1%   ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-18 12:14 UTC (permalink / raw)
  To: git; +Cc: Karthik Nayak, Phillip Wood, Junio C Hamano

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

Convert builtins to use `the_repository->index` instead of `the_index`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/add.c               |  48 +++++++-------
 builtin/am.c                |  36 +++++------
 builtin/cat-file.c          |   4 +-
 builtin/check-attr.c        |   5 +-
 builtin/check-ignore.c      |   7 +--
 builtin/checkout-index.c    |  22 +++----
 builtin/checkout.c          |  87 +++++++++++++------------
 builtin/clean.c             |   7 +--
 builtin/commit.c            |  81 ++++++++++++------------
 builtin/describe.c          |   3 +-
 builtin/diff-tree.c         |   3 +-
 builtin/diff.c              |   6 +-
 builtin/difftool.c          |   4 +-
 builtin/merge-index.c       |  17 +++--
 builtin/merge-tree.c        |   3 +-
 builtin/merge.c             |  31 +++++----
 builtin/mv.c                |  68 ++++++++++----------
 builtin/pull.c              |   4 +-
 builtin/read-tree.c         |  15 +++--
 builtin/rebase.c            |   3 +-
 builtin/replay.c            |   1 -
 builtin/reset.c             |  32 +++++-----
 builtin/rev-parse.c         |   6 +-
 builtin/rm.c                |  40 ++++++------
 builtin/stash.c             |  45 +++++++------
 builtin/submodule--helper.c |  21 +++----
 builtin/update-index.c      | 122 ++++++++++++++++++------------------
 builtin/write-tree.c        |   6 +-
 28 files changed, 356 insertions(+), 371 deletions(-)

diff --git a/builtin/add.c b/builtin/add.c
index ae723bc85e..7689d8e7c5 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Linus Torvalds
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -40,20 +40,20 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 		int err;
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
 
 		if (!show_only)
-			err = chmod_index_entry(&the_index, ce, flip);
+			err = chmod_index_entry(the_repository->index, ce, flip);
 		else
 			err = S_ISREG(ce->ce_mode) ? 0 : -1;
 
@@ -68,20 +68,20 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
 {
 	int i, retval = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 		if (ce_stage(ce))
 			continue; /* do not touch unmerged paths */
 		if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
 			continue; /* do not touch non blobs */
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
-		retval |= add_file_to_index(&the_index, ce->name,
+		retval |= add_file_to_index(the_repository->index, ce->name,
 					    flags | ADD_CACHE_RENORMALIZE);
 	}
 
@@ -100,11 +100,11 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
 	i = dir->nr;
 	while (--i >= 0) {
 		struct dir_entry *entry = *src++;
-		if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+		if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen))
 			*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
-	add_pathspec_matches_against_index(pathspec, &the_index, seen,
+	add_pathspec_matches_against_index(pathspec, the_repository->index, seen,
 					   PS_IGNORE_SKIP_WORKTREE);
 	return seen;
 }
@@ -119,14 +119,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
 		    (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
 
 	seen = xcalloc(pathspec->nr, 1);
-	refresh_index(&the_index, flags, pathspec, seen,
+	refresh_index(the_repository->index, flags, pathspec, seen,
 		      _("Unstaged changes after refreshing the index:"));
 	for (i = 0; i < pathspec->nr; i++) {
 		if (!seen[i]) {
 			const char *path = pathspec->items[i].original;
 
 			if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
-			    !path_in_sparse_checkout(path, &the_index)) {
+			    !path_in_sparse_checkout(path, the_repository->index)) {
 				string_list_append(&only_match_skip_worktree,
 						   pathspec->items[i].original);
 			} else {
@@ -335,12 +335,12 @@ static int add_files(struct dir_struct *dir, int flags)
 
 	for (i = 0; i < dir->nr; i++) {
 		if (!include_sparse &&
-		    !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+		    !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) {
 			string_list_append(&matched_sparse_paths,
 					   dir->entries[i]->name);
 			continue;
 		}
-		if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+		if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) {
 			if (!ignore_add_errors)
 				die(_("adding files failed"));
 			exit_status = 1;
@@ -458,8 +458,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
 		die(_("index file corrupt"));
 
-	die_in_unpopulated_submodule(&the_index, prefix);
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_in_unpopulated_submodule(the_repository->index, prefix);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	if (add_new_files) {
 		int baselen;
@@ -471,7 +471,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		}
 
 		/* This picks up the paths that are not tracked */
-		baselen = fill_directory(&dir, &the_index, &pathspec);
+		baselen = fill_directory(&dir, the_repository->index, &pathspec);
 		if (pathspec.nr)
 			seen = prune_directory(&dir, &pathspec, baselen);
 	}
@@ -488,7 +488,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 		if (!seen)
 			seen = find_pathspecs_matching_against_index(&pathspec,
-					&the_index, PS_IGNORE_SKIP_WORKTREE);
+					the_repository->index, PS_IGNORE_SKIP_WORKTREE);
 
 		/*
 		 * file_exists() assumes exact match
@@ -524,8 +524,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 			    !file_exists(path)) {
 				if (ignore_missing) {
 					int dtype = DT_UNKNOWN;
-					if (is_excluded(&dir, &the_index, path, &dtype))
-						dir_add_ignored(&dir, &the_index,
+					if (is_excluded(&dir, the_repository->index, path, &dtype))
+						dir_add_ignored(&dir, the_repository->index,
 								path, pathspec.items[i].len);
 				} else
 					die(_("pathspec '%s' did not match any files"),
@@ -566,7 +566,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	end_odb_transaction();
 
 finish:
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write new index file"));
 
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2e8d..4db2bc3c2f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
  *
  * Based on git-am.sh by Junio C Hamano.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -1536,8 +1536,8 @@ static int run_apply(const struct am_state *state, const char *index_file)
 
 	if (index_file) {
 		/* Reload index as apply_all_patches() will have modified it. */
-		discard_index(&the_index);
-		read_index_from(&the_index, index_file, get_git_dir());
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, index_file, get_git_dir());
 	}
 
 	return 0;
@@ -1579,10 +1579,10 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 	if (build_fake_ancestor(state, index_path))
 		return error("could not build fake ancestor");
 
-	discard_index(&the_index);
-	read_index_from(&the_index, index_path, get_git_dir());
+	discard_index(the_repository->index);
+	read_index_from(the_repository->index, index_path, get_git_dir());
 
-	if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&orig_tree, the_repository->index, index_path, 0, NULL))
 		return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
 
 	say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1608,12 +1608,12 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		return error(_("Did you hand edit your patch?\n"
 				"It does not apply to blobs recorded in its index."));
 
-	if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL))
 		return error("could not write tree");
 
 	say(state, stdout, _("Falling back to patching base and 3-way merge..."));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
 
 	/*
@@ -1660,7 +1660,7 @@ static void do_commit(const struct am_state *state)
 	if (!state->no_verify && run_hooks("pre-applypatch"))
 		exit(1);
 
-	if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 
 	if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
@@ -1948,7 +1948,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
 		}
 	}
 
-	if (unmerged_index(&the_index)) {
+	if (unmerged_index(the_repository->index)) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
 			"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1987,12 +1987,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
@@ -2006,7 +2006,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2029,8 +2029,8 @@ static int merge_tree(struct tree *tree)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
@@ -2040,7 +2040,7 @@ static int merge_tree(struct tree *tree)
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2068,7 +2068,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
 	if (fast_forward_to(head_tree, head_tree, 1))
 		return -1;
 
-	if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL))
 		return -1;
 
 	index_tree = parse_tree_indirect(&index);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0c948f40fb..43a1d7ac49 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "convert.h"
@@ -77,7 +77,7 @@ static int filter_object(const char *path, unsigned mode,
 		struct checkout_metadata meta;
 
 		init_checkout_metadata(&meta, NULL, NULL, oid);
-		if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
+		if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) {
 			free(*buf);
 			*size = strbuf.len;
 			*buf = strbuf_detach(&strbuf, NULL);
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index c1da1d184e..9376810710 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "attr.h"
@@ -71,9 +70,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
 		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
-		git_all_attrs(&the_index, full_path, check);
+		git_all_attrs(the_repository->index, full_path, check);
 	} else {
-		git_check_attr(&the_index, full_path, check);
+		git_check_attr(the_repository->index, full_path, check);
 	}
 	output_attr(check, file);
 
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 906cd96753..6c43430ec4 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "dir.h"
@@ -95,21 +94,21 @@ static int check_ignore(struct dir_struct *dir,
 		       PATHSPEC_KEEP_ORDER,
 		       prefix, argv);
 
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	/*
 	 * look for pathspecs matching entries in the index, since these
 	 * should not be ignored, in order to be consistent with
 	 * 'git status', 'git add' etc.
 	 */
-	seen = find_pathspecs_matching_against_index(&pathspec, &the_index,
+	seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
 						     PS_HEED_SKIP_WORKTREE);
 	for (i = 0; i < pathspec.nr; i++) {
 		full_path = pathspec.items[i].match;
 		pattern = NULL;
 		if (!seen[i]) {
 			int dtype = DT_UNKNOWN;
-			pattern = last_matching_pattern(dir, &the_index,
+			pattern = last_matching_pattern(dir, the_repository->index,
 							full_path, &dtype);
 			if (!verbose && pattern &&
 			    pattern->flags & PATTERN_FLAG_NEGATIVE)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 2e086a204d..29e744d11b 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,7 +4,7 @@
  * Copyright (C) 2005 Linus Torvalds
  *
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -69,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
 static int checkout_file(const char *name, const char *prefix)
 {
 	int namelen = strlen(name);
-	int pos = index_name_pos(&the_index, name, namelen);
+	int pos = index_name_pos(the_repository->index, name, namelen);
 	int has_same_name = 0;
 	int is_file = 0;
 	int is_skipped = 1;
@@ -79,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix)
 	if (pos < 0)
 		pos = -pos - 1;
 
-	while (pos < the_index.cache_nr) {
-		struct cache_entry *ce = the_index.cache[pos];
+	while (pos <the_repository->index->cache_nr) {
+		struct cache_entry *ce =the_repository->index->cache[pos];
 		if (ce_namelen(ce) != namelen ||
 		    memcmp(ce->name, name, namelen))
 			break;
@@ -140,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length)
 	int i, errs = 0;
 	struct cache_entry *last_ce = NULL;
 
-	for (i = 0; i < the_index.cache_nr ; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr ; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
 			if (!ce_skip_worktree(ce))
@@ -154,8 +154,8 @@ static int checkout_all(const char *prefix, int prefix_length)
 			 * first entry inside the expanded sparse directory).
 			 */
 			if (ignore_skip_worktree) {
-				ensure_full_index(&the_index);
-				ce = the_index.cache[i];
+				ensure_full_index(the_repository->index);
+				ce = the_repository->index->cache[i];
 			}
 		}
 
@@ -260,7 +260,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
 			builtin_checkout_index_usage, 0);
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 	state.force = force;
 	state.quiet = quiet;
 	state.not_new = not_new;
@@ -280,7 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 	 */
 	if (index_opt && !state.base_dir_len && !to_tempfile) {
 		state.refresh_cache = 1;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 		repo_hold_locked_index(the_repository, &lock_file,
 				       LOCK_DIE_ON_ERROR);
 	}
@@ -339,7 +339,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 		return 1;
 
 	if (is_lock_file_locked(&lock_file) &&
-	    write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	    write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("Unable to write new index file");
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e6036aab..8a1d13b399 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "branch.h"
@@ -146,7 +145,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 		return READ_TREE_RECURSIVE;
 
 	len = base->len + strlen(pathname);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, base->buf, base->len);
 	memcpy(ce->name + base->len, pathname, len - base->len);
@@ -159,9 +158,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
 	 * do the right thing.
 	 */
-	pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
+	pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
 	if (pos >= 0) {
-		struct cache_entry *old = the_index.cache[pos];
+		struct cache_entry *old = the_repository->index->cache[pos];
 		if (ce->ce_mode == old->ce_mode &&
 		    !ce_intent_to_add(old) &&
 		    oideq(&ce->oid, &old->oid)) {
@@ -171,7 +170,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 		}
 	}
 
-	add_index_entry(&the_index, ce,
+	add_index_entry(the_repository->index, ce,
 			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	return 0;
 }
@@ -190,8 +189,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 
 static int skip_same_name(const struct cache_entry *ce, int pos)
 {
-	while (++pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name))
+	while (++pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name))
 		; /* skip */
 	return pos;
 }
@@ -199,9 +198,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
 static int check_stage(int stage, const struct cache_entry *ce, int pos,
 		       int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
 			return 0;
 		pos++;
 	}
@@ -218,8 +217,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 	unsigned seen = 0;
 	const char *name = ce->name;
 
-	while (pos < the_index.cache_nr) {
-		ce = the_index.cache[pos];
+	while (pos < the_repository->index->cache_nr) {
+		ce = the_repository->index->cache[pos];
 		if (strcmp(name, ce->name))
 			break;
 		seen |= (1 << ce_stage(ce));
@@ -235,10 +234,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 			  const struct checkout *state, int *nr_checkouts,
 			  int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
-			return checkout_entry(the_index.cache[pos], state,
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
+			return checkout_entry(the_repository->index->cache[pos], state,
 					      NULL, nr_checkouts);
 		pos++;
 	}
@@ -256,7 +255,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 			   int *nr_checkouts, struct mem_pool *ce_mem_pool,
 			   int conflict_style)
 {
-	struct cache_entry *ce = the_index.cache[pos];
+	struct cache_entry *ce = the_repository->index->cache[pos];
 	const char *path = ce->name;
 	mmfile_t ancestor, ours, theirs;
 	enum ll_merge_result merge_status;
@@ -269,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 	int renormalize = 0;
 
 	memset(threeway, 0, sizeof(threeway));
-	while (pos < the_index.cache_nr) {
+	while (pos < the_repository->index->cache_nr) {
 		int stage;
 		stage = ce_stage(ce);
 		if (!stage || strcmp(path, ce->name))
@@ -278,7 +277,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 		if (stage == 2)
 			mode = create_ce_mode(ce->ce_mode);
 		pos++;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 	}
 	if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
 		return error(_("path '%s' does not have necessary versions"), path);
@@ -356,7 +355,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
 	 * match_pathspec() for _all_ entries when
 	 * opts->source_tree != NULL.
 	 */
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched))
 		ce->ce_flags |= CE_MATCHED;
 }
 
@@ -367,7 +366,7 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 	ce->ce_flags &= ~CE_MATCHED;
 	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 		return;
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) {
 		ce->ce_flags |= CE_MATCHED;
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			/*
@@ -391,7 +390,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
 
 	state.force = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	mem_pool_init(&ce_mem_pool, 0);
 	get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -404,8 +403,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
 	if (pc_workers > 1)
 		init_parallel_checkout();
 
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state,
@@ -429,7 +428,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
 		errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
 					      NULL, NULL);
 	mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
-	remove_marked_cache_entries(&the_index, 1);
+	remove_marked_cache_entries(the_repository->index, 1);
 	remove_scheduled_dirs();
 	errs |= finish_delayed_checkout(&state, opts->show_progress);
 
@@ -571,7 +570,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->source_tree)
 		read_tree_some(opts->source_tree, &opts->pathspec);
 	if (opts->merge)
-		unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
+		unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
 
 	ps_matched = xcalloc(opts->pathspec.nr, 1);
 
@@ -579,13 +578,13 @@ static int checkout_paths(const struct checkout_opts *opts,
 	 * Make sure all pathspecs participated in locating the paths
 	 * to be checked out.
 	 */
-	for (pos = 0; pos < the_index.cache_nr; pos++)
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++)
 		if (opts->overlay_mode)
-			mark_ce_for_checkout_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_overlay(the_repository->index->cache[pos],
 						     ps_matched,
 						     opts);
 		else
-			mark_ce_for_checkout_no_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos],
 							ps_matched,
 							opts);
 
@@ -596,8 +595,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 	free(ps_matched);
 
 	/* Any unmerged paths? */
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
@@ -622,7 +621,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->checkout_worktree)
 		errs |= checkout_worktree(opts, new_branch_info);
 	else
-		remove_marked_cache_entries(&the_index, 1);
+		remove_marked_cache_entries(the_repository->index, 1);
 
 	/*
 	 * Allow updating the index when checking out from the index.
@@ -634,7 +633,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 		checkout_index = opts->checkout_index;
 
 	if (checkout_index) {
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die(_("unable to write new index file"));
 	} else {
 		/*
@@ -703,8 +702,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	opts.verbose_update = o->show_progress;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	init_checkout_metadata(&opts.meta, info->refname,
 			       info->commit ? &info->commit->object.oid : null_oid(),
 			       NULL);
@@ -756,12 +755,12 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
 {
 	memset(topts, 0, sizeof(*topts));
 	topts->head_idx = -1;
-	topts->src_index = &the_index;
-	topts->dst_index = &the_index;
+	topts->src_index = the_repository->index;
+	topts->dst_index = the_repository->index;
 
 	setup_unpack_trees_porcelain(topts, "checkout");
 
-	topts->initial_checkout = is_index_unborn(&the_index);
+	topts->initial_checkout = is_index_unborn(the_repository->index);
 	topts->update = 1;
 	topts->merge = 1;
 	topts->quiet = merge && old_commit;
@@ -783,7 +782,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (repo_read_index_preload(the_repository, NULL, 0) < 0)
 		return error(_("index file corrupt"));
 
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
 		if (new_branch_info->commit)
 			BUG("'switch --orphan' should never accept a commit as starting point");
@@ -807,9 +806,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		struct unpack_trees_options topts;
 		const struct object_id *old_commit_oid;
 
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-		if (unmerged_index(&the_index)) {
+		if (unmerged_index(the_repository->index)) {
 			error(_("you need to resolve your current index first"));
 			return 1;
 		}
@@ -919,10 +918,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		}
 	}
 
-	if (!cache_tree_fully_valid(the_index.cache_tree))
-		cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+	if (!cache_tree_fully_valid(the_repository->index->cache_tree))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
diff --git a/builtin/clean.c b/builtin/clean.c
index 29efe84153..ded5a91534 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,7 +6,6 @@
  * Based on git-clean.sh by Pavel Roskin
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -714,7 +713,7 @@ static int filter_by_patterns_cmd(void)
 		for_each_string_list_item(item, &del_list) {
 			int dtype = DT_UNKNOWN;
 
-			if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+			if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
 				*item->string = '\0';
 				changed++;
 			}
@@ -1021,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		       PATHSPEC_PREFER_CWD,
 		       prefix, argv);
 
-	fill_directory(&dir, &the_index, &pathspec);
+	fill_directory(&dir, the_repository->index, &pathspec);
 	correct_untracked_entries(&dir);
 
 	for (i = 0; i < dir.nr; i++) {
@@ -1029,7 +1028,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		struct stat st;
 		const char *rel;
 
-		if (!index_name_is_other(&the_index, ent->name, ent->len))
+		if (!index_name_is_other(the_repository->index, ent->name, ent->len))
 			continue;
 
 		if (lstat(ent->name, &st))
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e1484446b..c2943055ef 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,6 @@
  * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -266,19 +265,19 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
 	if (with_tree) {
 		char *max_prefix = common_prefix(pattern);
-		overlay_tree_on_index(&the_index, with_tree, max_prefix);
+		overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
 		free(max_prefix);
 	}
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		struct string_list_item *item;
 
 		if (ce->ce_flags & CE_UPDATE)
 			continue;
-		if (!ce_path_match(&the_index, ce, pattern, m))
+		if (!ce_path_match(the_repository->index, ce, pattern, m))
 			continue;
 		item = string_list_insert(list, ce->name);
 		if (ce_skip_worktree(ce))
@@ -302,10 +301,10 @@ static void add_remove_files(struct string_list *list)
 			continue;
 
 		if (!lstat(p->string, &st)) {
-			if (add_to_index(&the_index, p->string, &st, 0))
+			if (add_to_index(the_repository->index, p->string, &st, 0))
 				die(_("updating files failed"));
 		} else
-			remove_file_from_index(&the_index, p->string);
+			remove_file_from_index(the_repository->index, p->string);
 	}
 }
 
@@ -316,7 +315,7 @@ static void create_base_index(const struct commit *current_head)
 	struct tree_desc t;
 
 	if (!current_head) {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		return;
 	}
 
@@ -324,8 +323,8 @@ static void create_base_index(const struct commit *current_head)
 	opts.head_idx = 1;
 	opts.index_only = 1;
 	opts.merge = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	opts.fn = oneway_merge;
 	tree = parse_tree_indirect(&current_head->object.oid);
@@ -344,7 +343,7 @@ static void refresh_cache_or_die(int refresh_flags)
 	 * refresh_flags contains REFRESH_QUIET, so the only errors
 	 * are for unmerged entries.
 	 */
-	if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
 		die_resolve_conflict("commit");
 }
 
@@ -393,7 +392,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
 		refresh_cache_or_die(refresh_flags);
 
-		if (write_locked_index(&the_index, &index_lock, 0))
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to create temporary index"));
 
 		old_repo_index_file = the_repository->index_file;
@@ -412,13 +411,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
 			unsetenv(INDEX_ENVIRONMENT);
 		FREE_AND_NULL(old_index_env);
 
-		discard_index(&the_index);
-		read_index_from(&the_index, get_lock_file_path(&index_lock),
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, get_lock_file_path(&index_lock),
 				get_git_dir());
-		if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
+		if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) {
 			if (reopen_lock_file(&index_lock) < 0)
 				die(_("unable to write index file"));
-			if (write_locked_index(&the_index, &index_lock, 0))
+			if (write_locked_index(the_repository->index, &index_lock, 0))
 				die(_("unable to update temporary index"));
 		} else
 			warning(_("Failed to update main cache tree"));
@@ -450,8 +449,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
 			exit(128);
 
 		refresh_cache_or_die(refresh_flags);
-		cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock, 0))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_NORMAL;
 		ret = get_lock_file_path(&index_lock);
@@ -472,10 +471,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
 		repo_hold_locked_index(the_repository, &index_lock,
 				       LOCK_DIE_ON_ERROR);
 		refresh_cache_or_die(refresh_flags);
-		if (the_index.cache_changed
-		    || !cache_tree_fully_valid(the_index.cache_tree))
-			cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock,
+		if (the_repository->index->cache_changed
+		    || !cache_tree_fully_valid(the_repository->index->cache_tree))
+			cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_AS_IS;
@@ -516,15 +515,15 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
 		exit(1);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("cannot read the index"));
 
 	repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
-	cache_tree_update(&the_index, WRITE_TREE_SILENT);
-	if (write_locked_index(&the_index, &index_lock, 0))
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+	cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+	if (write_locked_index(the_repository->index, &index_lock, 0))
 		die(_("unable to write new index file"));
 
 	hold_lock_file_for_update(&false_lock,
@@ -534,14 +533,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
 	create_base_index(current_head);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-	if (write_locked_index(&the_index, &false_lock, 0))
+	if (write_locked_index(the_repository->index, &false_lock, 0))
 		die(_("unable to write temporary index file"));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	ret = get_lock_file_path(&false_lock);
-	read_index_from(&the_index, ret, get_git_dir());
+	read_index_from(the_repository->index, ret, get_git_dir());
 out:
 	string_list_clear(&partial, 0);
 	clear_pathspec(&pathspec);
@@ -999,7 +998,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct object_id oid;
 		const char *parent = "HEAD";
 
-		if (!the_index.initialized && repo_read_index(the_repository) < 0)
+		if (!the_repository->index->initialized && repo_read_index(the_repository) < 0)
 			die(_("Cannot read index"));
 
 		if (amend)
@@ -1009,11 +1008,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 			int i, ita_nr = 0;
 
 			/* TODO: audit for interaction with sparse-index. */
-			ensure_full_index(&the_index);
-			for (i = 0; i < the_index.cache_nr; i++)
-				if (ce_intent_to_add(the_index.cache[i]))
+			ensure_full_index(the_repository->index);
+			for (i = 0; i < the_repository->index->cache_nr; i++)
+				if (ce_intent_to_add(the_repository->index->cache[i]))
 					ita_nr++;
-			committable = the_index.cache_nr - ita_nr > 0;
+			committable = the_repository->index->cache_nr - ita_nr > 0;
 		} else {
 			/*
 			 * Unless the user did explicitly request a submodule
@@ -1081,11 +1080,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		 * and could have updated it. We must do this before we invoke
 		 * the editor and after we invoke run_status above.
 		 */
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 
-	if (cache_tree_update(&the_index, 0)) {
+	if (cache_tree_update(the_repository->index, 0)) {
 		error(_("Error building trees"));
 		return 0;
 	}
@@ -1586,7 +1585,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	    status_format != STATUS_FORMAT_PORCELAIN_V2)
 		progress_flag = REFRESH_PROGRESS;
 	repo_read_index(the_repository);
-	refresh_index(&the_index,
+	refresh_index(the_repository->index,
 		      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
 		      &s.pathspec, NULL, NULL);
 
@@ -1856,7 +1855,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		append_merge_tag_headers(parents, &tail);
 	}
 
-	if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
+	if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
 				 parents, &oid, author_ident.buf, NULL,
 				 sign_commit, extra)) {
 		rollback_index_files();
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a714f..c0e3301e3c 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -674,7 +673,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 			prepare_repo_settings(the_repository);
 			the_repository->settings.command_requires_full_index = 0;
 			repo_read_index(the_repository);
-			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+			refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED,
 				      NULL, NULL, NULL);
 			fd = repo_hold_locked_index(the_repository,
 						    &index_lock, 0);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a8e68ce8ef..0d3c611aac 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "diff.h"
@@ -206,7 +205,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 		opt->diffopt.rotate_to_strict = 0;
 		opt->diffopt.no_free = 1;
 		if (opt->diffopt.detect_rename) {
-			if (!the_index.cache)
+			if (the_repository->index->cache)
 				repo_read_index(the_repository);
 			opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
 		}
diff --git a/builtin/diff.c b/builtin/diff.c
index 6e196e0c7d..efc37483b3 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "ewah/ewok.h"
@@ -239,9 +239,9 @@ static void refresh_index_quietly(void)
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
 	if (fd < 0)
 		return;
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
 		      NULL);
 	repo_update_index_if_able(the_repository, &lock_file);
 }
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a3c72b8258..a130faae4f 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,7 +11,7 @@
  *
  * Copyright (C) 2016 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -117,7 +117,7 @@ static int use_wt_file(const char *workdir, const char *name,
 		int fd = open(buf.buf, O_RDONLY);
 
 		if (fd >= 0 &&
-		    !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+		    !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
 			if (is_null_oid(oid)) {
 				oidcpy(oid, &wt_oid);
 				use = 1;
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 270d5f644a..0fabe3f6bb 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "hex.h"
 #include "read-cache-ll.h"
@@ -18,11 +17,11 @@ static int merge_entry(int pos, const char *path)
 	char ownbuf[4][60];
 	struct child_process cmd = CHILD_PROCESS_INIT;
 
-	if (pos >= the_index.cache_nr)
+	if (pos >= the_repository->index->cache_nr)
 		die("git merge-index: %s not in the cache", path);
 	found = 0;
 	do {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		int stage = ce_stage(ce);
 
 		if (strcmp(ce->name, path))
@@ -32,7 +31,7 @@ static int merge_entry(int pos, const char *path)
 		xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
 		arguments[stage] = hexbuf[stage];
 		arguments[stage + 4] = ownbuf[stage];
-	} while (++pos < the_index.cache_nr);
+	} while (++pos < the_repository->index->cache_nr);
 	if (!found)
 		die("git merge-index: %s not in the cache", path);
 
@@ -51,7 +50,7 @@ static int merge_entry(int pos, const char *path)
 
 static void merge_one_path(const char *path)
 {
-	int pos = index_name_pos(&the_index, path, strlen(path));
+	int pos = index_name_pos(the_repository->index, path, strlen(path));
 
 	/*
 	 * If it already exists in the cache as stage0, it's
@@ -65,9 +64,9 @@ static void merge_all(void)
 {
 	int i;
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		if (!ce_stage(ce))
 			continue;
 		i += merge_entry(i, ce->name)-1;
@@ -89,7 +88,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
 	repo_read_index(the_repository);
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
+	ensure_full_index(the_repository->index);
 
 	i = 1;
 	if (!strcmp(argv[i], "-o")) {
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8bdb439131..1082d919fd 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "tree-walk.h"
 #include "xdiff-interface.h"
@@ -364,7 +363,7 @@ static void trivial_merge_trees(struct tree_desc t[3], const char *base)
 
 	setup_traverse_info(&info, base);
 	info.fn = threeway_callback;
-	traverse_trees(&the_index, 3, t, &info);
+	traverse_trees(the_repository->index, 3, t, &info);
 }
 
 static void *get_tree_descriptor(struct repository *r,
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec87fc..6a6d379885 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,6 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -300,7 +299,7 @@ static int save_state(struct object_id *stash)
 	int rc = -1;
 
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
 		repo_update_index_if_able(the_repository, &lock_file);
 	rollback_lock_file(&lock_file);
@@ -372,7 +371,7 @@ static void restore_state(const struct object_id *head,
 	run_command(&cmd);
 
 refresh_cache:
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("could not read index"));
 }
@@ -657,8 +656,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 2;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.verbose_update = 1;
 	opts.trivial_merges_only = 1;
@@ -674,7 +673,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 	if (!trees[nr_trees++])
 		return -1;
 	opts.fn = threeway_merge;
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		parse_tree(trees[i]);
 		init_tree_desc(t+i, &trees[i]->object.oid,
@@ -687,7 +686,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 static void write_tree_trivial(struct object_id *oid)
 {
-	if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 }
 
@@ -745,7 +744,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 			rollback_lock_file(&lock);
 			return 2;
 		}
-		if (write_locked_index(&the_index, &lock,
+		if (write_locked_index(the_repository->index, &lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write %s"), get_index_file());
 		return clean ? 0 : 1;
@@ -768,8 +767,8 @@ static int count_unmerged_entries(void)
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++)
-		if (ce_stage(the_index.cache[i]))
+	for (i = 0; i < the_repository->index->cache_nr; i++)
+		if (ce_stage(the_repository->index->cache[i]))
 			ret++;
 
 	return ret;
@@ -843,9 +842,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 		 * the editor and after we invoke run_status above.
 		 */
 		if (invoked_hook)
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 	strbuf_addbuf(&msg, &merge_msg);
 	if (squash)
 		BUG("the control must not reach here under --squash");
@@ -957,7 +956,7 @@ static int suggest_conflicts(void)
 	 * Thus, we will get the cleanup mode which is returned when we _are_
 	 * using an editor.
 	 */
-	append_conflicts_hint(&the_index, &msgbuf,
+	append_conflicts_hint(the_repository->index, &msgbuf,
 			      get_cleanup_mode(cleanup_arg, 1));
 	fputs(msgbuf.buf, fp);
 	strbuf_release(&msgbuf);
@@ -1386,7 +1385,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		else
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	if (option_edit < 0)
 		option_edit = default_edit_option();
@@ -1595,7 +1594,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		 * We are not doing octopus, not fast-forward, and have
 		 * only one common.
 		 */
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 		if (allow_trivial && fast_forward != FF_ONLY) {
 			/*
 			 * Must first ensure that index matches HEAD before
@@ -1784,6 +1783,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	return ret;
 }
diff --git a/builtin/mv.c b/builtin/mv.c
index 22e64fc290..74aa9746aa 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -95,9 +95,9 @@ static void prepare_move_submodule(const char *src, int first,
 				   const char **submodule_gitfile)
 {
 	struct strbuf submodule_dotgit = STRBUF_INIT;
-	if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
+	if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
 		die(_("Directory %s is in index and no submodule?"), src);
-	if (!is_staging_gitmodules_ok(&the_index))
+	if (!is_staging_gitmodules_ok(the_repository->index))
 		die(_("Please stage your changes to .gitmodules or stash them to proceed"));
 	strbuf_addf(&submodule_dotgit, "%s/.git", src);
 	*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
@@ -114,13 +114,13 @@ static int index_range_of_same_dir(const char *src, int length,
 	const char *src_w_slash = add_slash(src);
 	int first, last, len_w_slash = length + 1;
 
-	first = index_name_pos(&the_index, src_w_slash, len_w_slash);
+	first = index_name_pos(the_repository->index, src_w_slash, len_w_slash);
 	if (first >= 0)
 		die(_("%.*s is in index"), len_w_slash, src_w_slash);
 
 	first = -1 - first;
-	for (last = first; last < the_index.cache_nr; last++) {
-		const char *path = the_index.cache[last]->name;
+	for (last = first; last < the_repository->index->cache_nr; last++) {
+		const char *path = the_repository->index->cache[last]->name;
 		if (strncmp(path, src_w_slash, len_w_slash))
 			break;
 	}
@@ -144,14 +144,14 @@ static int empty_dir_has_sparse_contents(const char *name)
 	const char *with_slash = add_slash(name);
 	int length = strlen(with_slash);
 
-	int pos = index_name_pos(&the_index, with_slash, length);
+	int pos = index_name_pos(the_repository->index, with_slash, length);
 	const struct cache_entry *ce;
 
 	if (pos < 0) {
 		pos = -pos - 1;
-		if (pos >= the_index.cache_nr)
+		if (pos >= the_repository->index->cache_nr)
 			goto free_return;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 		if (strncmp(with_slash, ce->name, length))
 			goto free_return;
 		if (ce_skip_worktree(ce))
@@ -223,7 +223,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			S_ISDIR(st.st_mode)) {
 		destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
 	} else {
-		if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
+		if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) &&
 		    empty_dir_has_sparse_contents(dst_w_slash)) {
 			destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
 			dst_mode = SKIP_WORKTREE_DIR;
@@ -239,7 +239,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			 * is deprecated at this point) sparse-checkout. As
 			 * SPARSE here is only considering cone-mode situation.
 			 */
-			if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
+			if (!path_in_cone_mode_sparse_checkout(destination[0], the_repository->index))
 				dst_mode = SPARSE;
 		}
 	}
@@ -263,10 +263,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			int pos;
 			const struct cache_entry *ce;
 
-			pos = index_name_pos(&the_index, src, length);
+			pos = index_name_pos(the_repository->index, src, length);
 			if (pos < 0) {
 				const char *src_w_slash = add_slash(src);
-				if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+				if (!path_in_sparse_checkout(src_w_slash, the_repository->index) &&
 				    empty_dir_has_sparse_contents(src)) {
 					modes[i] |= SKIP_WORKTREE_DIR;
 					goto dir_check;
@@ -276,7 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 					bad = _("bad source");
 				goto act_on_entry;
 			}
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!ce_skip_worktree(ce)) {
 				bad = _("bad source");
 				goto act_on_entry;
@@ -286,7 +286,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 				goto act_on_entry;
 			}
 			/* Check if dst exists in index */
-			if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+			if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) {
 				modes[i] |= SPARSE;
 				goto act_on_entry;
 			}
@@ -311,7 +311,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 dir_check:
 		if (S_ISDIR(st.st_mode)) {
 			int j, dst_len, n;
-			int first = index_name_pos(&the_index, src, length), last;
+			int first = index_name_pos(the_repository->index, src, length), last;
 
 			if (first >= 0) {
 				prepare_move_submodule(src, first,
@@ -339,7 +339,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			dst_len = strlen(dst);
 
 			for (j = 0; j < last - first; j++) {
-				const struct cache_entry *ce = the_index.cache[first + j];
+				const struct cache_entry *ce = the_repository->index->cache[first + j];
 				const char *path = ce->name;
 				source[argc + j] = path;
 				destination[argc + j] =
@@ -351,7 +351,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			argc += last - first;
 			goto act_on_entry;
 		}
-		if (!(ce = index_file_exists(&the_index, src, length, 0))) {
+		if (!(ce = index_file_exists(the_repository->index, src, length, 0))) {
 			bad = _("not under version control");
 			goto act_on_entry;
 		}
@@ -387,7 +387,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
 		if (ignore_sparse &&
 		    (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
-		    index_entry_exists(&the_index, dst, strlen(dst))) {
+		    index_entry_exists(the_repository->index, dst, strlen(dst))) {
 			bad = _("destination exists in the index");
 			if (force) {
 				if (verbose)
@@ -404,12 +404,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		 * option as a way to have a successful run.
 		 */
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(src, &the_index)) {
+		    !path_in_sparse_checkout(src, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, src);
 			skip_sparse = 1;
 		}
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(dst, &the_index)) {
+		    !path_in_sparse_checkout(dst, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, dst);
 			skip_sparse = 1;
 		}
@@ -449,7 +449,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		int pos;
 		int sparse_and_dirty = 0;
 		struct checkout state = CHECKOUT_INIT;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 
 		if (force)
 			state.force = 1;
@@ -476,14 +476,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
 			continue;
 
-		pos = index_name_pos(&the_index, src, strlen(src));
+		pos = index_name_pos(the_repository->index, src, strlen(src));
 		assert(pos >= 0);
 		if (!(mode & SPARSE) && !lstat(src, &st))
-			sparse_and_dirty = ie_modified(&the_index,
-						       the_index.cache[pos],
+			sparse_and_dirty = ie_modified(the_repository->index,
+						       the_repository->index->cache[pos],
 						       &st,
 						       0);
-		rename_index_entry_at(&the_index, pos, dst);
+		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
 		    core_apply_sparse_checkout &&
@@ -495,11 +495,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			 * should be added in a future patch.
 			 */
 			if ((mode & SPARSE) &&
-			    path_in_sparse_checkout(dst, &the_index)) {
+			    path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from out-of-cone to in-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
@@ -507,11 +507,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 					die(_("cannot checkout %s"), dst_ce->name);
 			} else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
 				   !(mode & SPARSE) &&
-				   !path_in_sparse_checkout(dst, &the_index)) {
+				   !path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from in-cone to out-of-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				/*
 				 * if src is clean, it will suffice to remove it
@@ -559,9 +559,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		advise_on_moving_dirty_path(&dirty_paths);
 
 	if (gitmodules_modified)
-		stage_updated_gitmodules(&the_index);
+		stage_updated_gitmodules(the_repository->index);
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76d52..66869210db 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,7 +5,7 @@
  *
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -1044,7 +1044,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 		if (opt_autostash == -1)
 			opt_autostash = config_autostash;
 
-		if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
+		if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index))
 			die(_("Updating an unborn branch with changes added to the index."));
 
 		if (!opt_autostash)
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 6f89cec0fb..a8cf8504b8 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,7 +4,6 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -159,8 +158,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = -1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	git_config(git_read_tree_config, NULL);
 
@@ -197,7 +196,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 			die(_("You need to resolve your current index first"));
 		stage = opts.merge = 1;
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
@@ -225,7 +224,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 		setup_work_tree();
 
 	if (opts.skip_sparse_checkout)
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 	if (opts.merge) {
 		switch (stage - 1) {
@@ -237,7 +236,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 			break;
 		case 2:
 			opts.fn = twoway_merge;
-			opts.initial_checkout = is_index_unborn(&the_index);
+			opts.initial_checkout = is_index_unborn(the_repository->index);
 			break;
 		case 3:
 		default:
@@ -258,7 +257,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 	if (nr_trees == 1 && !opts.prefix)
 		opts.skip_cache_tree_update = 1;
 
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
 		if (parse_tree(tree) < 0)
@@ -282,7 +281,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 				 the_repository->index,
 				 trees[0]);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("unable to write new index file");
 	return 0;
 }
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 891f28468e..fe17d562a8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,7 +4,6 @@
  * Copyright (c) 2018 Pratik Karki
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -295,7 +294,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 	if (ret)
 		error(_("could not generate todo list"));
 	else {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 						&todo_list))
 			BUG("unusable todo list");
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bc4b47f09..6bf0691f15 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,7 +2,6 @@
  * "git replay" builtin command
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "git-compat-util.h"
 
 #include "builtin.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6332..b6dacf9678 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -66,8 +66,8 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.fn = oneway_merge;
 	opts.merge = 1;
 	init_checkout_metadata(&opts.meta, ref, oid, NULL);
@@ -159,11 +159,11 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		struct cache_entry *ce;
 
 		if (!is_in_reset_tree && !intent_to_add) {
-			remove_file_from_index(&the_index, one->path);
+			remove_file_from_index(the_repository->index, one->path);
 			continue;
 		}
 
-		ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+		ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path,
 				      0, 0);
 
 		/*
@@ -174,9 +174,9 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		 * if this entry is outside the sparse cone - this is necessary
 		 * to properly construct the reset sparse directory.
 		 */
-		pos = index_name_pos(&the_index, one->path, strlen(one->path));
-		if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
-		    (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+		pos = index_name_pos(the_repository->index, one->path, strlen(one->path));
+		if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) ||
+		    (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index)))
 			ce->ce_flags |= CE_SKIP_WORKTREE;
 
 		if (!ce)
@@ -186,7 +186,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 			ce->ce_flags |= CE_INTENT_TO_ADD;
 			set_object_name_for_intent_to_add_entry(ce);
 		}
-		add_index_entry(&the_index, ce,
+		add_index_entry(the_repository->index, ce,
 				ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	}
 }
@@ -208,8 +208,8 @@ static int read_from_tree(const struct pathspec *pathspec,
 	opt.change = diff_change;
 	opt.add_remove = diff_addremove;
 
-	if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec))
+		ensure_full_index(the_repository->index);
 
 	if (do_diff_cache(tree_oid, &opt))
 		return 1;
@@ -235,7 +235,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
 
 static void die_if_unmerged_cache(int reset_type)
 {
-	if (is_merge() || unmerged_index(&the_index))
+	if (is_merge() || unmerged_index(the_repository->index))
 		die(_("Cannot do a %s reset in the middle of a merge."),
 		    _(reset_type_names[reset_type]));
 
@@ -470,12 +470,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 				update_ref_status = 1;
 				goto cleanup;
 			}
-			the_index.updated_skipworktree = 1;
+			the_repository->index->updated_skipworktree = 1;
 			if (!no_refresh && get_git_work_tree()) {
 				uint64_t t_begin, t_delta_in_ms;
 
 				t_begin = getnanotime();
-				refresh_index(&the_index, flags, NULL, NULL,
+				refresh_index(the_repository->index, flags, NULL, NULL,
 					      _("Unstaged changes after reset:"));
 				t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
 				if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
@@ -501,7 +501,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			free(ref);
 		}
 
-		if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK))
 			die(_("Could not write new index file."));
 	}
 
@@ -516,7 +516,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (!pathspec.nr)
 		remove_branch_state(the_repository, 0);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 
 cleanup:
 	clear_pathspec(&pathspec);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 624182e507..af79538632 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -1049,8 +1049,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 			if (!strcmp(arg, "--shared-index-path")) {
 				if (repo_read_index(the_repository) < 0)
 					die(_("Could not read the index"));
-				if (the_index.split_index) {
-					const struct object_id *oid = &the_index.split_index->base_oid;
+				if (the_repository->index->split_index) {
+					const struct object_id *oid = &the_repository->index->split_index->base_oid;
 					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
 					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
diff --git a/builtin/rm.c b/builtin/rm.c
index fd130cea2d..d195c16e74 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -41,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos)
 {
 	int i = -pos - 1;
 
-	while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
-		if (ce_stage(the_index.cache[i]) == 2)
+	while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
+		if (ce_stage(the_repository->index->cache[i]) == 2)
 			return i;
 		i++;
 	}
@@ -78,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void)
 		int pos;
 		const struct cache_entry *ce;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			pos = get_ours_cache_pos(name, pos);
 			if (pos < 0)
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (!S_ISGITLINK(ce->ce_mode) ||
 		    !file_exists(ce->name) ||
@@ -122,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 		int local_changes = 0;
 		int staged_changes = 0;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			/*
 			 * Skip unmerged entries except for populated submodules
@@ -132,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only)
 			if (pos < 0)
 				continue;
 
-			if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
+			if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) ||
 			    is_empty_dir(name))
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (lstat(ce->name, &st) < 0) {
 			if (!is_missing_file_error(errno))
@@ -173,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 		 * Is the index different from the file in the work tree?
 		 * If it's a submodule, is its work tree modified?
 		 */
-		if (ie_match_stat(&the_index, ce, &st, 0) ||
+		if (ie_match_stat(the_repository->index, ce, &st, 0) ||
 		    (S_ISGITLINK(ce->ce_mode) &&
 		     bad_to_remove_submodule(ce->name,
 				SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -301,27 +301,27 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
 
-	if (pathspec_needs_expanded_index(&the_index, &pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
+		ensure_full_index(the_repository->index);
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
-		if (!ce_path_match(&the_index, ce, &pathspec, seen))
+		if (!ce_path_match(the_repository->index, ce, &pathspec, seen))
 			continue;
 		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
 		list.entry[list.nr].name = xstrdup(ce->name);
 		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
 		if (list.entry[list.nr++].is_submodule &&
-		    !is_staging_gitmodules_ok(&the_index))
+		    !is_staging_gitmodules_ok(the_repository->index))
 			die(_("please stage your changes to .gitmodules or stash them to proceed"));
 	}
 
@@ -391,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		if (!quiet)
 			printf("rm '%s'\n", path);
 
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die(_("git rm: unable to remove %s"), path);
 	}
 
@@ -432,10 +432,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		}
 		strbuf_release(&buf);
 		if (gitmodules_modified)
-			stage_updated_gitmodules(&the_index);
+			stage_updated_gitmodules(the_repository->index);
 	}
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 062be1fbc0..69c01ba136 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -273,7 +272,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	struct lock_file lock_file = LOCK_INIT;
 
 	repo_read_index_preload(the_repository, NULL, 0);
-	if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL))
 		return -1;
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -287,8 +286,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
 	opts.update = update;
@@ -299,7 +298,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	if (unpack_trees(nr_trees, t, &opts))
 		return -1;
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		return error(_("unable to write new index file"));
 
 	return 0;
@@ -430,7 +429,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	/*
 	 * Step 1: get a difference between orig_tree (which corresponding
@@ -454,7 +453,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 
 		/* Look up the path's position in the current index. */
 		p = diff_queued_diff.queue[i];
-		pos = index_name_pos(&the_index, p->two->path,
+		pos = index_name_pos(the_repository->index, p->two->path,
 				     strlen(p->two->path));
 
 		/*
@@ -465,10 +464,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 		 * path, but left it out of the working tree, then clear the
 		 * SKIP_WORKTREE bit and write it to the working tree.
 		 */
-		if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
+		if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) {
 			struct stat st;
 
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!lstat(ce->name, &st)) {
 				/* Conflicting path present; relocate it */
 				struct strbuf new_path = STRBUF_INIT;
@@ -504,12 +503,12 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 			if (pos < 0)
 				option = ADD_CACHE_OK_TO_ADD;
 
-			ce = make_cache_entry(&the_index,
+			ce = make_cache_entry(the_repository->index,
 					      p->one->mode,
 					      &p->one->oid,
 					      p->one->path,
 					      0, 0);
-			add_index_entry(&the_index, ce, option);
+			add_index_entry(the_repository->index, ce, option);
 		}
 	}
 	diff_flush(&diff_opts);
@@ -518,7 +517,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 	 * Step 4: write the new index to disk
 	 */
 	repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
-	if (write_locked_index(&the_index, &lock,
+	if (write_locked_index(the_repository->index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("could not write index"));
 }
@@ -539,7 +538,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 					 NULL, NULL, NULL))
 		return error(_("could not write index"));
 
-	if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0,
 				NULL))
 		return error(_("cannot apply a stash in the middle of a merge"));
 
@@ -562,14 +561,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 				return error(_("conflicts in index. "
 					       "Try without --index."));
 
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
-			if (write_index_as_tree(&index_tree, &the_index,
+			if (write_index_as_tree(&index_tree, the_repository->index,
 						get_index_file(), 0, NULL))
 				return error(_("could not save index tree"));
 
 			reset_head();
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
 		}
 	}
@@ -875,8 +874,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 	}
 
 	unpack_tree_opt.head_idx = -1;
-	unpack_tree_opt.src_index = &the_index;
-	unpack_tree_opt.dst_index = &the_index;
+	unpack_tree_opt.src_index = the_repository->index;
+	unpack_tree_opt.dst_index = the_repository->index;
 	unpack_tree_opt.merge = 1;
 	unpack_tree_opt.fn = stash_worktree_untracked_merge;
 
@@ -1395,7 +1394,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 	strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
 	commit_list_insert(head_commit, &parents);
-	if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0,
 				NULL) ||
 	    commit_tree(commit_tree_label.buf, commit_tree_label.len,
 			&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
@@ -1540,9 +1539,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 		char *ps_matched = xcalloc(ps->nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
-		for (i = 0; i < the_index.cache_nr; i++)
-			ce_path_match(&the_index, the_index.cache[i], ps,
+		ensure_full_index(the_repository->index);
+		for (i = 0; i < the_repository->index->cache_nr; i++)
+			ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
 				      ps_matched);
 
 		if (report_path_error(ps_matched, ps)) {
@@ -1612,7 +1611,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 				goto done;
 			}
 		}
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (ps->nr) {
 			struct child_process cp_add = CHILD_PROCESS_INIT;
 			struct child_process cp_diff = CHILD_PROCESS_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e4e18adb57..93f4b9d726 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -207,18 +206,18 @@ static int module_list_compute(const char **argv,
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
-		if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+		if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce),
 				    0, ps_matched, 1) ||
 		    !S_ISGITLINK(ce->ce_mode))
 			continue;
 
 		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
 		list->entries[list->nr++] = ce;
-		while (i + 1 < the_index.cache_nr &&
-		       !strcmp(ce->name, the_index.cache[i + 1]->name))
+		while (i + 1 < the_repository->index->cache_nr &&
+		       !strcmp(ce->name, the_repository->index->cache[i + 1]->name))
 			/*
 			 * Skip entries with the same name in different stages
 			 * to make sure an entry is returned only once.
@@ -907,7 +906,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 			int fd = open(p->sm_path, O_RDONLY);
 
 			if (fd < 0 || fstat(fd, &st) < 0 ||
-			    index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+			    index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB,
 				     p->sm_path, 0))
 				error(_("couldn't hash object from '%s'"), p->sm_path);
 		} else {
@@ -3243,21 +3242,21 @@ static void die_on_index_match(const char *path, int force)
 		char *ps_matched = xcalloc(ps.nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 		/*
 		 * Since there is only one pathspec, we just need to
 		 * check ps_matched[0] to know if a cache entry matched.
 		 */
-		for (i = 0; i < the_index.cache_nr; i++) {
-			ce_path_match(&the_index, the_index.cache[i], &ps,
+		for (i = 0; i < the_repository->index->cache_nr; i++) {
+			ce_path_match(the_repository->index, the_repository->index->cache[i], &ps,
 				      ps_matched);
 
 			if (ps_matched[0]) {
 				if (!force)
 					die(_("'%s' already exists in the index"),
 					    path);
-				if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
+				if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode))
 					die(_("'%s' already exists in the index "
 					      "and is not a submodule"), path);
 				break;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa1476c..6321810006 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "bulk-checkin.h"
 #include "config.h"
@@ -247,16 +247,16 @@ static int test_if_untracked_cache_is_supported(void)
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
 	int namelen = strlen(path);
-	int pos = index_name_pos(&the_index, path, namelen);
+	int pos = index_name_pos(the_repository->index, path, namelen);
 	if (0 <= pos) {
-		mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
+		mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]);
 		if (mark)
-			the_index.cache[pos]->ce_flags |= flag;
+			the_repository->index->cache[pos]->ce_flags |= flag;
 		else
-			the_index.cache[pos]->ce_flags &= ~flag;
-		the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
-		cache_tree_invalidate_path(&the_index, path);
-		the_index.cache_changed |= CE_ENTRY_CHANGED;
+			the_repository->index->cache[pos]->ce_flags &= ~flag;
+		the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+		cache_tree_invalidate_path(the_repository->index, path);
+		the_repository->index->cache_changed |= CE_ENTRY_CHANGED;
 		return 0;
 	}
 	return -1;
@@ -266,7 +266,7 @@ static int remove_one_path(const char *path)
 {
 	if (!allow_remove)
 		return error("%s: does not exist and --remove not passed", path);
-	if (remove_file_from_index(&the_index, path))
+	if (remove_file_from_index(the_repository->index, path))
 		return error("%s: cannot remove from the index", path);
 	return 0;
 }
@@ -291,24 +291,24 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 	struct cache_entry *ce;
 
 	/* Was the old index entry already up-to-date? */
-	if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
+	if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0))
 		return 0;
 
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	memcpy(ce->name, path, len);
 	ce->ce_flags = create_ce_flags(0);
 	ce->ce_namelen = len;
-	fill_stat_cache_info(&the_index, ce, st);
+	fill_stat_cache_info(the_repository->index, ce, st);
 	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-	if (index_path(&the_index, &ce->oid, path, st,
+	if (index_path(the_repository->index, &ce->oid, path, st,
 		       info_only ? 0 : HASH_WRITE_OBJECT)) {
 		discard_cache_entry(ce);
 		return -1;
 	}
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option)) {
+	if (add_index_entry(the_repository->index, ce, option)) {
 		discard_cache_entry(ce);
 		return error("%s: cannot add to the index - missing --add option?", path);
 	}
@@ -341,11 +341,11 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 static int process_directory(const char *path, int len, struct stat *st)
 {
 	struct object_id oid;
-	int pos = index_name_pos(&the_index, path, len);
+	int pos = index_name_pos(the_repository->index, path, len);
 
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (S_ISGITLINK(ce->ce_mode)) {
 
 			/* Do nothing to the index if there is no HEAD! */
@@ -360,8 +360,8 @@ static int process_directory(const char *path, int len, struct stat *st)
 
 	/* Inexact match: is there perhaps a subdirectory match? */
 	pos = -pos-1;
-	while (pos < the_index.cache_nr) {
-		const struct cache_entry *ce = the_index.cache[pos++];
+	while (pos < the_repository->index->cache_nr) {
+		const struct cache_entry *ce = the_repository->index->cache[pos++];
 
 		if (strncmp(ce->name, path, len))
 			break;
@@ -391,8 +391,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
 	if (has_symlink_leading_path(path, len))
 		return error("'%s' is beyond a symbolic link", path);
 
-	pos = index_name_pos(&the_index, path, len);
-	ce = pos < 0 ? NULL : the_index.cache[pos];
+	pos = index_name_pos(the_repository->index, path, len);
+	ce = pos < 0 ? NULL : the_repository->index->cache[pos];
 	if (ce && ce_skip_worktree(ce)) {
 		/*
 		 * working directory version is assumed "good"
@@ -400,7 +400,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
 		 * On the other hand, removing it from index should work
 		 */
 		if (!ignore_skip_worktree_entries && allow_remove &&
-		    remove_file_from_index(&the_index, path))
+		    remove_file_from_index(the_repository->index, path))
 			return error("%s: cannot remove from the index", path);
 		return 0;
 	}
@@ -428,7 +428,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
 		return error("Invalid path '%s'", path);
 
 	len = strlen(path);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, path, len);
@@ -439,7 +439,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
 		ce->ce_flags |= CE_VALID;
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option))
+	if (add_index_entry(the_repository->index, ce, option))
 		return error("%s: cannot add to the index - missing --add option?",
 			     path);
 	report("add '%s'", path);
@@ -451,11 +451,11 @@ static void chmod_path(char flip, const char *path)
 	int pos;
 	struct cache_entry *ce;
 
-	pos = index_name_pos(&the_index, path, strlen(path));
+	pos = index_name_pos(the_repository->index, path, strlen(path));
 	if (pos < 0)
 		goto fail;
-	ce = the_index.cache[pos];
-	if (chmod_index_entry(&the_index, ce, flip) < 0)
+	ce = the_repository->index->cache[pos];
+	if (chmod_index_entry(the_repository->index, ce, flip) < 0)
 		goto fail;
 
 	report("chmod %cx '%s'", flip, path);
@@ -498,7 +498,7 @@ static void update_one(const char *path)
 	}
 
 	if (force_remove) {
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die("git update-index: unable to remove %s", path);
 		report("remove '%s'", path);
 		return;
@@ -581,7 +581,7 @@ static void read_index_info(int nul_term_line)
 
 		if (!mode) {
 			/* mode == 0 means there is no such path -- remove */
-			if (remove_file_from_index(&the_index, path_name))
+			if (remove_file_from_index(the_repository->index, path_name))
 				die("git update-index: unable to remove %s",
 				    ptr);
 		}
@@ -622,12 +622,12 @@ static struct cache_entry *read_one_ent(const char *which,
 			error("%s: not in %s branch.", path, which);
 		return NULL;
 	}
-	if (!the_index.sparse_index && mode == S_IFDIR) {
+	if (!the_repository->index->sparse_index && mode == S_IFDIR) {
 		if (which)
 			error("%s: not a blob in %s branch.", path, which);
 		return NULL;
 	}
-	ce = make_empty_cache_entry(&the_index, namelen);
+	ce = make_empty_cache_entry(the_repository->index, namelen);
 
 	oidcpy(&ce->oid, &oid);
 	memcpy(ce->name, path, namelen);
@@ -642,12 +642,12 @@ static int unresolve_one(const char *path)
 	struct string_list_item *item;
 	int res = 0;
 
-	if (!the_index.resolve_undo)
+	if (!the_repository->index->resolve_undo)
 		return res;
-	item = string_list_lookup(the_index.resolve_undo, path);
+	item = string_list_lookup(the_repository->index->resolve_undo, path);
 	if (!item)
 		return res; /* no resolve-undo record for the path */
-	res = unmerge_index_entry(&the_index, path, item->util, 0);
+	res = unmerge_index_entry(the_repository->index, path, item->util, 0);
 	FREE_AND_NULL(item->util);
 	return res;
 }
@@ -688,13 +688,13 @@ static int do_reupdate(const char **paths,
 		 */
 		has_head = 0;
  redo:
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		struct cache_entry *old = NULL;
 		int save_nr;
 		char *path;
 
-		if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+		if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL))
 			continue;
 		if (has_head)
 			old = read_one_ent(NULL, &head_oid,
@@ -710,7 +710,7 @@ static int do_reupdate(const char **paths,
 		 * to process each path individually
 		 */
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
-			ensure_full_index(&the_index);
+			ensure_full_index(the_repository->index);
 			goto redo;
 		}
 
@@ -718,12 +718,12 @@ static int do_reupdate(const char **paths,
 		 * path anymore, in which case, under 'allow_remove',
 		 * or worse yet 'allow_replace', active_nr may decrease.
 		 */
-		save_nr = the_index.cache_nr;
+		save_nr = the_repository->index->cache_nr;
 		path = xstrdup(ce->name);
 		update_one(path);
 		free(path);
 		discard_cache_entry(old);
-		if (save_nr != the_index.cache_nr)
+		if (save_nr != the_repository->index->cache_nr)
 			goto redo;
 	}
 	clear_pathspec(&pathspec);
@@ -739,9 +739,9 @@ static int refresh(struct refresh_params *o, unsigned int flag)
 {
 	setup_work_tree();
 	repo_read_index(the_repository);
-	*o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+	*o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
 					NULL, NULL);
-	if (has_racy_timestamp(&the_index)) {
+	if (has_racy_timestamp(the_repository->index)) {
 		/*
 		 * Even if nothing else has changed, updating the file
 		 * increases the chance that racy timestamps become
@@ -750,7 +750,7 @@ static int refresh(struct refresh_params *o, unsigned int flag)
 		 * refresh_index() as these are no actual errors.
 		 * cmd_status() does the same.
 		 */
-		the_index.cache_changed |= SOMETHING_CHANGED;
+		the_repository->index->cache_changed |= SOMETHING_CHANGED;
 	}
 	return 0;
 }
@@ -787,7 +787,7 @@ static int resolve_undo_clear_callback(const struct option *opt UNUSED,
 {
 	BUG_ON_OPT_NEG(unset);
 	BUG_ON_OPT_ARG(arg);
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	return 0;
 }
 
@@ -888,7 +888,7 @@ static enum parse_opt_result unresolve_callback(
 	*has_errors = do_unresolve(ctx->argc, ctx->argv,
 				prefix, prefix ? strlen(prefix) : 0);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -909,7 +909,7 @@ static enum parse_opt_result reupdate_callback(
 	setup_work_tree();
 	*has_errors = do_reupdate(ctx->argv + 1, prefix);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -1056,7 +1056,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 	if (entries < 0)
 		die("cache corrupted");
 
-	the_index.updated_skipworktree = 1;
+	the_repository->index->updated_skipworktree = 1;
 
 	/*
 	 * Custom copy of parse_options() because we want to handle
@@ -1111,18 +1111,18 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
 	if (preferred_index_format) {
 		if (preferred_index_format < 0) {
-			printf(_("%d\n"), the_index.version);
+			printf(_("%d\n"), the_repository->index->version);
 		} else if (preferred_index_format < INDEX_FORMAT_LB ||
 			   INDEX_FORMAT_UB < preferred_index_format) {
 			die("index-version %d not in range: %d..%d",
 			    preferred_index_format,
 			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 		} else {
-			if (the_index.version != preferred_index_format)
-				the_index.cache_changed |= SOMETHING_CHANGED;
+			if (the_repository->index->version != preferred_index_format)
+				the_repository->index->cache_changed |= SOMETHING_CHANGED;
 			report(_("index-version: was %d, set to %d"),
-			       the_index.version, preferred_index_format);
-			the_index.version = preferred_index_format;
+			       the_repository->index->version, preferred_index_format);
+			the_repository->index->version = preferred_index_format;
 		}
 	}
 
@@ -1159,16 +1159,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.splitIndex is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable split index"));
-		if (the_index.split_index)
-			the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+		if (the_repository->index->split_index)
+			the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED;
 		else
-			add_split_index(&the_index);
+			add_split_index(the_repository->index);
 	} else if (!split_index) {
 		if (git_config_get_split_index() == 1)
 			warning(_("core.splitIndex is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable split index"));
-		remove_split_index(&the_index);
+		remove_split_index(the_repository->index);
 	}
 
 	prepare_repo_settings(r);
@@ -1180,7 +1180,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.untrackedCache is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable the untracked cache"));
-		remove_untracked_cache(&the_index);
+		remove_untracked_cache(the_repository->index);
 		report(_("Untracked cache disabled"));
 		break;
 	case UC_TEST:
@@ -1192,7 +1192,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.untrackedCache is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable the untracked cache"));
-		add_untracked_cache(&the_index);
+		add_untracked_cache(the_repository->index);
 		report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
 		break;
 	default:
@@ -1222,7 +1222,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 				"set it if you really want to "
 				"enable fsmonitor"));
 		}
-		add_fsmonitor(&the_index);
+		add_fsmonitor(the_repository->index);
 		report(_("fsmonitor enabled"));
 	} else if (!fsmonitor) {
 		enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
@@ -1230,17 +1230,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.fsmonitor is set; "
 				"remove it if you really want to "
 				"disable fsmonitor"));
-		remove_fsmonitor(&the_index);
+		remove_fsmonitor(the_repository->index);
 		report(_("fsmonitor disabled"));
 	}
 
-	if (the_index.cache_changed || force_write) {
+	if (the_repository->index->cache_changed || force_write) {
 		if (newfd < 0) {
 			if (refresh_args.flags & REFRESH_QUIET)
 				exit(128);
 			unable_to_lock_die(get_index_file(), lock_error);
 		}
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 66e83d0ecb..8c75b4609b 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -44,8 +44,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
-	ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
-				  tree_prefix);
+	ret = write_index_as_tree(&oid, the_repository->index, get_index_file(),
+				  flags, tree_prefix);
 	switch (ret) {
 	case 0:
 		printf("%s\n", oid_to_hex(&oid));
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 1%]

* What's cooking in git.git (Apr 2024, #06; Wed, 17)
@ 2024-04-18  0:25  1% Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-18  0:25 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking in my tree.  Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and are candidate to be in a
future release).  Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all and may be annotated with an URL
to a message that raises issues but they are no means exhaustive.  A
topic without enough support may be discarded after a long period of
no activity (of course they can be resubmit when new interests
arise).

Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors.  Some
repositories have only a subset of branches.

With maint, master, next, seen, todo:

	git://git.kernel.org/pub/scm/git/git.git/
	git://repo.or.cz/alt-git.git/
	https://kernel.googlesource.com/pub/scm/git/git/
	https://github.com/git/git/
	https://gitlab.com/git-scm/git/

With all the integration branches and topics broken out:

	https://github.com/gitster/git/

Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):

	git://git.kernel.org/pub/scm/git/git-htmldocs.git/
	https://github.com/gitster/git-htmldocs.git/

Release tarballs are available at:

	https://www.kernel.org/pub/software/scm/git/

--------------------------------------------------
[Graduated to 'master']

* ba/osxkeychain-updates (2024-04-01) 4 commits
  (merged to 'next' on 2024-04-10 at 1e7d925a43)
 + osxkeychain: store new attributes
 + osxkeychain: erase matching passwords only
 + osxkeychain: erase all matching credentials
 + osxkeychain: replace deprecated SecKeychain API

 Update osxkeychain backend with features required for the recent
 credential subsystem.
 source: <pull.1667.git.1708212896.gitgitgadget@gmail.com>


* ds/fetch-config-parse-microfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 585dcadd63)
 + fetch: return when parsing submodule.recurse

 A config parser callback function fell through instead of returning
 after recognising and processing a variable, wasting cycles, which
 has been corrected.
 source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>


* gt/add-u-commit-i-pathspec-check (2024-04-03) 3 commits
  (merged to 'next' on 2024-04-09 at 1a0c757907)
 + builtin/add: error out when passing untracked path with -u
 + builtin/commit: error out when passing untracked path with -i
 + revision: optionally record matches with pathspec elements

 "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
 diagnose a pathspec element that did not match any files in certain
 situations, unlike "git add <pathspec>" did.
 source: <20240402213640.139682-2-shyamthakkar001@gmail.com>


* jc/local-extern-shell-rules (2024-04-05) 8 commits
  (merged to 'next' on 2024-04-10 at d3a13273e7)
 + t1016: local VAR="VAL" fix
 + t0610: local VAR="VAL" fix
 + t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
 + t: local VAR="VAL" (quote ${magic-reference})
 + t: local VAR="VAL" (quote command substitution)
 + t: local VAR="VAL" (quote positional parameters)
 + CodingGuidelines: quote assigned value in 'local var=$val'
 + CodingGuidelines: describe "export VAR=VAL" rule

 Document and apply workaround for a buggy version of dash that
 mishandles "local var=val" construct.
 source: <20240406000902.3082301-1-gitster@pobox.com>


* jc/t2104-style-fixes (2024-04-09) 1 commit
  (merged to 'next' on 2024-04-11 at 7678ec509b)
 + t2104: style fixes

 Test style fixes.
 source: <xmqqmsqb4ngg.fsf@gitster.g>


* jc/unleak-core-excludesfile (2024-04-08) 1 commit
  (merged to 'next' on 2024-04-10 at ffb0c01871)
 + config: do not leak excludes_file

 The variable that holds the value read from the core.excludefile
 configuration variable used to leak, which has been corrected.
 source: <xmqqttkeicov.fsf@gitster.g>


* jk/libcurl-8.7-regression-workaround (2024-04-05) 3 commits
  (merged to 'next' on 2024-04-10 at 3b76577bfc)
 + remote-curl: add Transfer-Encoding header only for older curl
 + INSTALL: bump libcurl version to 7.21.3
 + http: reset POSTFIELDSIZE when clearing curl handle

 Fix was added to work around a regression in libcURL 8.7.0 (which has
 already been fixed in their tip of the tree).
 source: <20240402200254.GA874754@coredump.intra.peff.net>


* jt/reftable-geometric-compaction (2024-04-08) 4 commits
  (merged to 'next' on 2024-04-10 at 7e868a831c)
 + reftable/stack: use geometric table compaction
 + reftable/stack: add env to disable autocompaction
 + reftable/stack: expose option to disable auto-compaction
 + Merge branch 'ps/pack-refs-auto' into jt/reftable-geometric-compaction

 The strategy to compact multiple tables of reftables after many
 operations accumulate many entries has been improved to avoid
 accumulating too many tables uncollected.
 source: <pull.1683.v6.git.1712593016.gitgitgadget@gmail.com>


* ma/win32-unix-domain-socket (2024-04-03) 1 commit
  (merged to 'next' on 2024-04-09 at b98021a65c)
 + Win32: detect unix socket support at runtime

 Windows binary used to decide the use of unix-domain socket at
 build time, but it learned to make the decision at runtime instead.
 source: <pull.1708.git.1712158923106.gitgitgadget@gmail.com>


* ps/t0610-umask-fix (2024-04-09) 2 commits
  (merged to 'next' on 2024-04-10 at 659a29b138)
 + t0610: execute git-pack-refs(1) with specified umask
 + t0610: make `--shared=` tests reusable

 The "shared repository" test in the t0610 reftable test failed
 under restrictive umask setting (e.g. 007), which has been
 corrected.
 source: <cover.1712656576.git.ps@pks.im>


* pw/t3428-cleanup (2024-04-09) 3 commits
  (merged to 'next' on 2024-04-11 at 3c40516874)
 + t3428: restore coverage for "apply" backend
 + t3428: use test_commit_message
 + t3428: modernize test setup

 Test cleanup.
 source: <pull.1713.git.1712676444.gitgitgadget@gmail.com>


* rs/apply-lift-path-length-limit (2024-04-05) 2 commits
  (merged to 'next' on 2024-04-09 at 3270d194fd)
 + path: remove mksnpath()
 + apply: avoid fixed-size buffer in create_one_file()

 "git apply" has been updated to lift the hardcoded pathname length
 limit, which in turn allowed a mksnpath() function that is no
 longer used.
 source: <df774306-f29b-4a75-a282-59db89812b9a@web.de>


* rs/apply-reject-fd-leakfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 11efa0543c)
 + apply: don't leak fd on fdopen() error

 A file descriptor leak in an error codepath, used when "git apply
 --reject" fails to create the *.rej file, has been corrected.
 source: <5ba55ee4-94c7-4094-a744-584fc623b391@web.de>


* rs/date-mode-pass-by-value (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at cc3c17d31c)
 + date: make DATE_MODE thread-safe

 The codepaths that reach date_mode_from_type() have been updated to
 pass "struct date_mode" by value to make them thread safe.
 source: <c6cb255a-72f0-4ac2-81a2-1d8e95570a81@web.de>


* rs/usage-fallback-to-show-message-format (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at 9a34aed4d5)
 + usage: report vsnprintf(3) failure

 vreportf(), which is usede by error() and friends, has been taught
 to give the error message printf-format string when its vsnprintf()
 call fails, instead of showing nothing useful to identify the
 nature of the error.
 source: <3da13298-b6a6-4391-b8e8-5dae9a28b860@web.de>


* sj/userdiff-c-sharp (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at 56aaf254a7)
 + userdiff: better method/property matching for C#

 The userdiff patterns for C# has been updated.

 Acked-by: Johannes Sixt <j6t@kdbg.org>
 cf. <c2154457-3f2f-496e-9b8b-c8ea7257027b@kdbg.org>
 source: <pull.1682.v5.git.git.1712180564927.gitgitgadget@gmail.com>


* tb/make-indent-conditional-with-non-spaces (2024-04-08) 2 commits
  (merged to 'next' on 2024-04-10 at 98aa239dc3)
 + Makefile(s): do not enforce "all indents must be done with tab"
 + Makefile(s): avoid recipe prefix in conditional statements

 Adjust to an upcoming changes to GNU make that breaks our Makefiles.
 source: <9d14c08ca6cc06cdf8fb4ba33d2470053dca3966.1712591504.git.me@ttaylorr.com>


* tb/t7700-fixup (2024-04-03) 1 commit
  (merged to 'next' on 2024-04-10 at ff1f877ef7)
 + t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `

 Test fix.
 source: <7e8d435d58eea19d2aae0be366720f5956d29a5d.1712075189.git.me@ttaylorr.com>

--------------------------------------------------
[New Topics]

* mr/rerere-crash-fix (2024-04-16) 1 commit
  (merged to 'next' on 2024-04-17 at 60be8e2d74)
 + rerere: fix crashes due to unmatched opening conflict markers

 When .git/rr-cache/ rerere database gets corrupted or rerere is fed to
 work on a file with conflicted hunks resolved incompletely, the rerere
 machinery got confused and segfaulted, which has been corrected.

 Will merge to 'master'.
 source: <20240416105320.1113401-1-marcel@roethke.info>


* pk/bisect-use-show (2024-04-15) 1 commit
 - bisect: report the found commit with "show"

 When "git bisect" reports the commit it determined to be the
 culprit, we used to show it in a format that does not honor common
 UI tweaks, like log.date and log.decorate.  The code has been
 taught to use "git show" to follow more customizations.

 Will merge to 'next'.
 source: <965ae345-fd58-c46c-5a7a-de181e901f21@softwolves.pp.se>


* ps/missing-btmp-fix (2024-04-15) 1 commit
  (merged to 'next' on 2024-04-16 at c70779ba4b)
 + pack-bitmap: gracefully handle missing BTMP chunks

 GIt 2.44 introduced a regression that makes the updated code to
 barf in repositories with multi-pack index written by older
 versions of Git, which has been corrected.

 Will merge to 'master'.
 source: <a8251f8278ba9a3b41a8e299cb4918a62df6d1c7.1713163238.git.ps@pks.im>


* rj/launch-editor-error-message (2024-04-15) 1 commit
  (merged to 'next' on 2024-04-16 at 3d0dd46fc2)
 + launch_editor: waiting message on error

 Git writes a "waiting for your editor" message on an incomplete
 line after launching an editor, and then append another error
 message on the same line if the editor errors out.  It now clears
 the "waiting for..." line before giving the error message.

 Will merge to 'master'.
 source: <e208da74-8f16-44ae-912e-ae968da82057@gmail.com>


* rs/imap-send-simplify-cmd-issuing-codepath (2024-04-15) 1 commit
  (merged to 'next' on 2024-04-17 at 0255e49f8b)
 + imap-send: increase command size limit

 Code simplification.

 Will merge to 'master'.
 source: <7026075c-db4e-4d43-bbd1-d2edb52da9b7@web.de>


* rs/no-openssl-compilation-fix-on-macos (2024-04-15) 1 commit
  (merged to 'next' on 2024-04-15 at 48cab93d0a)
 + git-compat-util: fix NO_OPENSSL on current macOS

 Build fix.

 Will merge to 'master'.
 source: <3188f4e2-9744-40b1-8f05-0896b8679d25@web.de>


* yb/replay-doc-linkfix (2024-04-15) 1 commit
  (merged to 'next' on 2024-04-15 at e8cf9cd9a8)
 + Documentation: fix linkgit reference

 Docfix.

 Will merge to 'master'.
 source: <pull.1706.git.git.1713132482976.gitgitgadget@gmail.com>


* rs/apply-reject-long-name (2024-04-16) 1 commit
  (merged to 'next' on 2024-04-17 at 701ccded8b)
 + apply: avoid using fixed-size buffer in write_out_one_reject()

 The filename used for rejected hunks "git apply --reject" creates
 was limited to PATH_MAX, which has been lifted.

 Will merge to 'master'.
 source: <a93cd243-cb17-4ad5-8d23-30768dc5213b@web.de>


* js/for-each-repo-keep-going (2024-04-17) 2 commits
 - maintenance: running maintenance should not stop on errors
 - for-each-repo: optionally keep going on an error

 A scheduled "git maintenance" job is expected to work on all
 repositories it knows about, but it stopped at the first one that
 errored out.  Now it keeps going.

 Expecting a (hopefully minor and final) reroll.
 cf. <CAPig+cSjoGe7Eeynz=jGSaNYWXQ-VkvWv7mv1NDeCXPFEtdqOA@mail.gmail.com>
 source: <pull.1719.git.1713342535.gitgitgadget@gmail.com>


* ps/run-auto-maintenance-in-receive-pack (2024-04-17) 2 commits
 - builtin/receive-pack: convert to use git-maintenance(1)
 - run-command: introduce function to prepare auto-maintenance process

 The "receive-pack" program (which responds to "git push") was not
 converted to run "git maintenance --auto" when other codepaths that
 used to run "git gc --auto" were updated, which has been corrected.

 Will merge to 'next'.
 source: <cover.1713334241.git.ps@pks.im>


* la/mailmap-entry (2024-04-16) 1 commit
  (merged to 'next' on 2024-04-17 at 440b18b8be)
 + mailmap: change primary address for Linus Arver

 source: <pull.1720.git.1713309711217.gitgitgadget@gmail.com>


* xx/disable-replace-when-building-midx (2024-04-17) 1 commit
 - midx: disable replace objects

 source: <pull.1711.v2.git.1712554017808.gitgitgadget@gmail.com>

--------------------------------------------------
[Cooking]

* dd/t9604-use-posix-timezones (2024-04-10) 1 commit
  (merged to 'next' on 2024-04-16 at 46ab81737f)
 + t9604: Fix test for musl libc and new Debian

 The cvsimport tests required that the platform understands
 traditional timezone notations like CST6CDT, which has been
 updated to work on those systems as long as they understand
 POSIX notation with explicit tz transition dates.

 Will merge to 'master'.
 source: <20240410032812.30476-1-congdanhqx@gmail.com>


* kn/update-ref-symrefs (2024-04-12) 8 commits
 - SQUASH???
 - refs: support symrefs in 'reference-transaction' hook
 - update-ref: add support for symref-update
 - update-ref: add support for symref-create
 - files-backend: extract out `create_symref_lock`
 - update-ref: add support for symref-delete
 - update-ref: add support for symref-verify
 - refs: accept symref values in `ref_transaction[_add]_update`

 source: <20240412095908.1134387-1-knayak@gitlab.com>


* pf/commitish-committish (2024-04-11) 1 commit
  (merged to 'next' on 2024-04-12 at 7ef816cb64)
 + typo: replace 'commitish' with 'committish'

 Spellfix.

 Will merge to 'master'.
 source: <20240407212111.55362-1-Pi.L.D.Fisher@gmail.com>


* ta/fast-import-parse-path-fix (2024-04-15) 8 commits
  (merged to 'next' on 2024-04-15 at 00cc71a679)
 + fast-import: make comments more precise
 + fast-import: forbid escaped NUL in paths
 + fast-import: document C-style escapes for paths
 + fast-import: improve documentation for path quoting
 + fast-import: remove dead strbuf
 + fast-import: allow unquoted empty path for root
 + fast-import: directly use strbufs for paths
 + fast-import: tighten path unquoting

 The way "git fast-import" handles paths described in its input has
 been tightened up and more clearly documented.

 Will merge to 'master'.
 source: <cover.1713056559.git.thalia@archibald.dev>


* xx/rfc2822-date-format-in-doc (2024-04-12) 1 commit
  (merged to 'next' on 2024-04-17 at f2186bd6e8)
 + Documentation: fix typos describing date format

 Docfix.

 Will merge to 'master'.
 source: <pull.1716.git.1712911876943.gitgitgadget@gmail.com>


* la/doc-use-of-contacts-when-contributing (2024-04-17) 9 commits
 - SQUASH???
 - SubmittingPatches: demonstrate using git-contacts with git-send-email
 - SubmittingPatches: add heading for format-patch and send-email
 - SubmittingPatches: dedupe discussion of security patches
 - SubmittingPatches: discuss reviewers first
 - SubmittingPatches: quote commands
 - SubmittingPatches: mention GitGitGadget
 - SubmittingPatches: clarify 'git-contacts' location
 - MyFirstContribution: mention contrib/contacts/git-contacts

 Advertise "git contacts", a tool for newcomers to find people to
 ask review for their patches, a bit more in our developer
 documentation.

 Almost there?
 source: <pull.1704.v5.git.1713308518.gitgitgadget@gmail.com>


* ps/ci-test-with-jgit (2024-04-12) 13 commits
 - t0612: add tests to exercise Git/JGit reftable compatibility
 - t0610: fix non-portable variable assignment
 - t06xx: always execute backend-specific tests
 - ci: install JGit dependency
 - ci: make Perforce binaries executable for all users
 - ci: merge scripts which install dependencies
 - ci: fix setup of custom path for GitLab CI
 - ci: merge custom PATH directories
 - ci: convert "install-dependencies.sh" to use "/bin/sh"
 - ci: drop duplicate package installation for "linux-gcc-default"
 - ci: skip sudo when we are already root
 - ci: expose distro name in dockerized GitHub jobs
 - ci: rename "runs_on_pool" to "distro"

 Tests to ensure interoperability between reftable written by jgit
 and our code have been added and enabled in CI.
 source: <cover.1712896868.git.ps@pks.im>


* pw/rebase-i-error-message (2024-04-08) 2 commits
 - rebase -i: improve error message when picking merge
 - rebase -i: pass struct replay_opts to parse_insn_line()

 When the user adds to "git rebase -i" instruction to "pick" a merge
 commit, the error experience is not pleasant.  Such an error is now
 caught earlier in the process that parses the todo list.

 Comments?
 source: <pull.1672.v2.git.1712585787.gitgitgadget@gmail.com>


* ps/reftable-write-optim (2024-04-08) 11 commits
 - reftable/block: reuse compressed array
 - reftable/block: reuse zstream when writing log blocks
 - reftable/writer: reset `last_key` instead of releasing it
 - reftable/writer: unify releasing memory
 - reftable/writer: refactorings for `writer_flush_nonempty_block()`
 - reftable/writer: refactorings for `writer_add_record()`
 - refs/reftable: don't recompute committer ident
 - reftable: remove name checks
 - refs/reftable: skip duplicate name checks
 - refs/reftable: perform explicit D/F check when writing symrefs
 - refs/reftable: fix D/F conflict error message on ref copy

 Code to write out reftable has seen some optimization and
 simplification.
 source: <cover.1712578837.git.ps@pks.im>


* ds/send-email-per-message-block (2024-04-10) 2 commits
 - send-email: make it easy to discern the messages for each patch
 - send-email: move newline characters out of a few translatable strings

 "git send-email" learned to separate its reports on each message it
 sends out with an extra blank line in between.

 Comments?
 source: <cover.1712732383.git.dsimic@manjaro.org>


* ew/khash-to-khashl (2024-03-28) 3 commits
 - khashl: fix ensemble lookups on empty table
 - treewide: switch to khashl for memory savings
 - list-objects-filter: use kh_size API

 The hashtable library "khash.h" has been replaced with "khashl.h"
 that has better memory usage characteristics.

 Needs review.
 source: <20240328101356.300374-1-e@80x24.org>


* ps/reftable-block-iteration-optim (2024-04-15) 10 commits
  (merged to 'next' on 2024-04-15 at 3a2353c7f2)
 + reftable/block: avoid copying block iterators on seek
 + reftable/block: reuse `zstream` state on inflation
 + reftable/block: open-code call to `uncompress2()`
 + reftable/block: reuse uncompressed blocks
 + reftable/reader: iterate to next block in place
 + reftable/block: move ownership of block reader into `struct table_iter`
 + reftable/block: introduce `block_reader_release()`
 + reftable/block: better grouping of functions
 + reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
 + reftable/block: rename `block_reader_start()`

 The code to iterate over reftable blocks has seen some optimization
 to reduce memory allocation and deallocation.

 Will merge to 'master'.
 source: <cover.1712578376.git.ps@pks.im>


* bc/credential-scheme-enhancement (2024-04-16) 16 commits
 - credential: add method for querying capabilities
 - credential-cache: implement authtype capability
 - t: add credential tests for authtype
 - credential: add support for multistage credential rounds
 - t5563: refactor for multi-stage authentication
 - docs: set a limit on credential line length
 - credential: enable state capability
 - credential: add an argument to keep state
 - http: add support for authtype and credential
 - docs: indicate new credential protocol fields
 - credential: add a field called "ephemeral"
 - credential: gate new fields on capability
 - credential: add a field for pre-encoded credentials
 - http: use new headers for each object request
 - remote-curl: reset headers on new request
 - credential: add an authtype field

 The credential helper protocol, together with the HTTP layer, have
 been enhanced to support authentication schemes different from
 username & password pair, like Bearer and NTLM.
 source: <20240417000240.3611948-1-sandals@crustytoothpaste.net>


* tb/pseudo-merge-reachability-bitmap (2024-03-20) 24 commits
 - t/perf: implement performace tests for pseudo-merge bitmaps
 - pseudo-merge: implement support for finding existing merges
 - ewah: `bitmap_equals_ewah()`
 - pack-bitmap: extra trace2 information
 - pack-bitmap.c: use pseudo-merges during traversal
 - t/test-lib-functions.sh: support `--date` in `test_commit_bulk()`
 - pack-bitmap: implement test helpers for pseudo-merge
 - ewah: implement `ewah_bitmap_popcount()`
 - pseudo-merge: implement support for reading pseudo-merge commits
 - pack-bitmap.c: read pseudo-merge extension
 - pseudo-merge: scaffolding for reads
 - pack-bitmap: extract `read_bitmap()` function
 - pack-bitmap-write.c: write pseudo-merge table
 - pack-bitmap-write.c: select pseudo-merge commits
 - pseudo-merge: implement support for selecting pseudo-merge commits
 - pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` public
 - pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`
 - pack-bitmap-write: support storing pseudo-merge commits
 - pseudo-merge.ch: initial commit
 - pack-bitmap: move some initialization to `bitmap_writer_init()`
 - pack-bitmap: drop unused `max_bitmaps` parameter
 - ewah: implement `ewah_bitmap_is_subset()`
 - config: repo_config_get_expiry()
 - Documentation/technical: describe pseudo-merge bitmaps format

 The pack-bitmap machinery learned to write pseudo-merge bitmaps,
 which act as imaginary octopus merges covering un-bitmapped
 reference tips. This enhances bitmap coverage, and thus,
 performance, for repositories with many references using bitmaps.

 Expecting a reroll.
 cf. <ZfyxCLpjbaScIdWA@nand.local>
 source: <cover.1710972293.git.me@ttaylorr.com>


* la/hide-trailer-info (2024-03-16) 7 commits
 - trailer: retire trailer_info_get() from API
 - trailer: make trailer_info struct private
 - trailer: make parse_trailers() return trailer_info pointer
 - interpret-trailers: access trailer_info with new helpers
 - sequencer: use the trailer iterator
 - trailer: teach iterator about non-trailer lines
 - Merge branch 'la/format-trailer-info' into la/hide-trailer-info
 (this branch uses la/format-trailer-info.)

 The trailer API has been reshuffled a bit.

 Needs review.
 source: <pull.1696.git.1710570428.gitgitgadget@gmail.com>


* ds/doc-config-reflow (2024-03-14) 1 commit
 - config.txt: perform some minor reformatting

 Reflow a paragraph in the documentation source without any effect
 to the formatted text.

 Will discard.
 source: <97bdaf075bf5a68554cca1731eca78aff2662907.1710444774.git.dsimic@manjaro.org>


* la/format-trailer-info (2024-03-15) 5 commits
  (merged to 'next' on 2024-04-16 at dca4784407)
 + trailer: finish formatting unification
 + trailer: begin formatting unification
 + format_trailer_info(): append newline for non-trailer lines
 + format_trailer_info(): drop redundant unfold_value()
 + format_trailer_info(): use trailer_item objects
 (this branch is used by la/hide-trailer-info.)

 The code to format trailers have been cleaned up.

 Will merge to 'master'.
 source: <pull.1694.git.1710485706.gitgitgadget@gmail.com>


* ie/config-includeif-hostname (2024-03-19) 2 commits
 - config: learn the "hostname:" includeIf condition
 - t: add a test helper for getting hostname

 The conditional inclusion mechanism for configuration files learned
 to switch on the hostname.

 Expecting a reroll.
 cf. <20240319210428.GC1159535@coredump.intra.peff.net>
 cf. <20240320001934.GA903718@coredump.intra.peff.net>
 source: <20240319183722.211300-1-ignacio@iencinas.com>


* js/build-fuzz-more-often (2024-04-11) 2 commits
 - fuzz: link fuzz programs with `make all` on Linux
 - ci: also define CXX environment variable

 In addition to building the objects needed, try to link the objects
 that are used in fuzzer tests, to make sure at least they build
 without bitrot, in Linux CI runs.

 Expecting a hopefully minor and final reroll.
 cf. <20240412042247.GA1077925@coredump.intra.peff.net>
 source: <cover.1712858920.git.steadmon@google.com>


* cw/git-std-lib (2024-02-28) 4 commits
 - SQUASH??? get rid of apparent debugging crufts
 - test-stdlib: show that git-std-lib is independent
 - git-std-lib: introduce Git Standard Library
 - pager: include stdint.h because uintmax_t is used

 Split libgit.a out to a separate git-std-lib tor easier reuse.

 Expecting a reroll.
 source: <cover.1696021277.git.jonathantanmy@google.com>


* js/cmake-with-test-tool (2024-02-23) 2 commits
 - cmake: let `test-tool` run the unit tests, too
 - Merge branch 'js/unit-test-suite-runner' into js/cmake-with-test-tool
 (this branch uses js/unit-test-suite-runner.)

 "test-tool" is now built in CMake build to also run the unit tests.

 May want to roll it into the base topic.
 source: <pull.1666.git.1708038924522.gitgitgadget@gmail.com>


* js/unit-test-suite-runner (2024-02-23) 8 commits
 - ci: use test-tool as unit test runner on Windows
 - t/Makefile: run unit tests alongside shell tests
 - unit tests: add rule for running with test-tool
 - test-tool run-command testsuite: support unit tests
 - test-tool run-command testsuite: remove hardcoded filter
 - test-tool run-command testsuite: get shell from env
 - t0080: turn t-basic unit test into a helper
 - Merge branch 'jk/unit-tests-buildfix' into js/unit-test-suite-runner
 (this branch is used by js/cmake-with-test-tool.)

 The "test-tool" has been taught to run testsuite tests in parallel,
 bypassing the need to use the "prove" tool.

 Needs review.
 source: <cover.1708728717.git.steadmon@google.com>


* bk/complete-dirname-for-am-and-format-patch (2024-01-12) 1 commit
 - completion: dir-type optargs for am, format-patch

 Command line completion support (in contrib/) has been
 updated for a few commands to complete directory names where a
 directory name is expected.

 Expecting a reroll.
 cf. <40c3a824-a961-490b-94d4-4eb23c8f713d@gmail.com>
 cf. <6683f24e-7e56-489d-be2d-8afe1fc38d2b@gmail.com>
 source: <d37781c3-6af2-409b-95a8-660a9b92d20b@smtp-relay.sendinblue.com>


* bk/complete-send-email (2024-01-12) 1 commit
 - completion: don't complete revs when --no-format-patch

 Command line completion support (in contrib/) has been taught to
 avoid offering revision names as candidates to "git send-email" when
 the command is used to send pre-generated files.

 Expecting a reroll.
 cf. <CAC4O8c88Z3ZqxH2VVaNPpEGB3moL5dJcg3cOWuLWwQ_hLrJMtA@mail.gmail.com>
 source: <a718b5ee-afb0-44bd-a299-3208fac43506@smtp-relay.sendinblue.com>


* tb/path-filter-fix (2024-01-31) 16 commits
 - bloom: introduce `deinit_bloom_filters()`
 - commit-graph: reuse existing Bloom filters where possible
 - object.h: fix mis-aligned flag bits table
 - commit-graph: new Bloom filter version that fixes murmur3
 - commit-graph: unconditionally load Bloom filters
 - bloom: prepare to discard incompatible Bloom filters
 - bloom: annotate filters with hash version
 - repo-settings: introduce commitgraph.changedPathsVersion
 - t4216: test changed path filters with high bit paths
 - t/helper/test-read-graph: implement `bloom-filters` mode
 - bloom.h: make `load_bloom_filter_from_graph()` public
 - t/helper/test-read-graph.c: extract `dump_graph_info()`
 - gitformat-commit-graph: describe version 2 of BDAT
 - commit-graph: ensure Bloom filters are read with consistent settings
 - revision.c: consult Bloom filters for root commits
 - t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`

 The Bloom filter used for path limited history traversal was broken
 on systems whose "char" is unsigned; update the implementation and
 bump the format version to 2.

 Waiting for a final ack?
 cf. <ZcFjkfbsBfk7JQIH@nand.local>
 source: <cover.1706741516.git.me@ttaylorr.com>


* jc/rerere-cleanup (2023-08-25) 4 commits
 - rerere: modernize use of empty strbuf
 - rerere: try_merge() should use LL_MERGE_ERROR when it means an error
 - rerere: fix comment on handle_file() helper
 - rerere: simplify check_one_conflict() helper function

 Code clean-up.

 Not ready to be reviewed yet.
 source: <20230824205456.1231371-1-gitster@pobox.com>


^ permalink raw reply	[relevance 1%]

* Re: [PATCH 2/2] builtin/receive-pack: convert to use git-maintenance(1)
  2024-04-17  6:16  2% ` [PATCH 2/2] " Patrick Steinhardt
@ 2024-04-17 16:50  0%   ` Karthik Nayak
  0 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-17 16:50 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: Derrick Stolee, Junio C Hamano

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

Patrick Steinhardt <ps@pks.im> writes:

> In 850b6edefa (auto-gc: extract a reusable helper from "git fetch",
> 2020-05-06), we have introduced a helper function `run_auto_gc()` that
> kicks off `git gc --auto`. The intent of this function was to pass down
> the "--quiet" flag to git-gc(1) as required without duplicating this at
> all callsites. In 7c3e9e8cfb (auto-gc: pass --quiet down from am,
> commit, merge and rebase, 2020-05-06) we then converted callsites that
> need to pass down this flag to use the new helper function. This has the
> notable omission of git-receive-pack(1), which is the only remaining
> user of `git gc --auto` that sets up the proccess manually. This is
> probably because it unconditionally passes down the `--quiet` flag and
> thus didn't benefit much from the new helper function.
>
> In a95ce12430 (maintenance: replace run_auto_gc(), 2020-09-17) we then
> replaced `run_auto_gc()` with `run_auto_maintenance()` which invokes
> git-maintenance(1) instead of git-gc(1). This command is the modern
> replacement for git-gc(1) and is both more thorough and also more
> flexible because administrators can configure which tasks exactly to run
> during maintenance.
>
> But due to git-receive-pack(1) not using `run_auto_gc()` in the first
> place it did not get converted to use git-maintenance(1) like we do
> everywhere else now. Address this oversight and start to use the newly
> introduced function `prepare_auto_maintenance()`. This will also make it
> easier for us to adapt this code together with all the other callsites
> that invoke auto-maintenance in the future.

This commit explains my earlier question. Thanks.

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

^ permalink raw reply	[relevance 0%]

* [PATCH 0/2] Use a "best effort" strategy in scheduled maintenance
@ 2024-04-17  8:28  4% Johannes Schindelin via GitGitGadget
  2024-04-18 12:53  3% ` [PATCH v2 " Johannes Schindelin via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Johannes Schindelin via GitGitGadget @ 2024-04-17  8:28 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin

Over in https://github.com/microsoft/git/issues/623, it was pointed out that
scheduled maintenance will error out when it encounters a missing
repository. The scheduled maintenance should exit with an error, all right,
but what about the remaining repositories for which maintenance was
scheduled, and that may not be missing?

This patch series addresses this by introducing a new for-each-repo option
and then using it in the command that is run via scheduled maintenance.

Johannes Schindelin (2):
  for-each-repo: optionally keep going on an error
  maintenance: running maintenance should not stop on errors

 Documentation/git-for-each-repo.txt |  4 ++++
 builtin/for-each-repo.c             |  8 ++++++--
 builtin/gc.c                        |  7 ++++---
 t/t0068-for-each-repo.sh            | 16 ++++++++++++++++
 t/t7900-maintenance.sh              |  6 +++---
 5 files changed, 33 insertions(+), 8 deletions(-)


base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1719%2Fdscho%2Ffor-each-repo-stop-on-error-2.44-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1719/dscho/for-each-repo-stop-on-error-2.44-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1719
-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* [PATCH 2/2] builtin/receive-pack: convert to use git-maintenance(1)
  @ 2024-04-17  6:16  2% ` Patrick Steinhardt
  2024-04-17 16:50  0%   ` Karthik Nayak
  0 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-17  6:16 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Junio C Hamano

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

In 850b6edefa (auto-gc: extract a reusable helper from "git fetch",
2020-05-06), we have introduced a helper function `run_auto_gc()` that
kicks off `git gc --auto`. The intent of this function was to pass down
the "--quiet" flag to git-gc(1) as required without duplicating this at
all callsites. In 7c3e9e8cfb (auto-gc: pass --quiet down from am,
commit, merge and rebase, 2020-05-06) we then converted callsites that
need to pass down this flag to use the new helper function. This has the
notable omission of git-receive-pack(1), which is the only remaining
user of `git gc --auto` that sets up the proccess manually. This is
probably because it unconditionally passes down the `--quiet` flag and
thus didn't benefit much from the new helper function.

In a95ce12430 (maintenance: replace run_auto_gc(), 2020-09-17) we then
replaced `run_auto_gc()` with `run_auto_maintenance()` which invokes
git-maintenance(1) instead of git-gc(1). This command is the modern
replacement for git-gc(1) and is both more thorough and also more
flexible because administrators can configure which tasks exactly to run
during maintenance.

But due to git-receive-pack(1) not using `run_auto_gc()` in the first
place it did not get converted to use git-maintenance(1) like we do
everywhere else now. Address this oversight and start to use the newly
introduced function `prepare_auto_maintenance()`. This will also make it
easier for us to adapt this code together with all the other callsites
that invoke auto-maintenance in the future.

This removes the last internal user of `git gc --auto`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/config/receive.txt |  2 +-
 builtin/receive-pack.c           | 21 ++++++++++-----------
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/Documentation/config/receive.txt b/Documentation/config/receive.txt
index c77e55b1cd..36a1e6f2d2 100644
--- a/Documentation/config/receive.txt
+++ b/Documentation/config/receive.txt
@@ -8,7 +8,7 @@ receive.advertisePushOptions::
 	capability to its clients. False by default.
 
 receive.autogc::
-	By default, git-receive-pack will run "git-gc --auto" after
+	By default, git-receive-pack will run "git maintenance run --auto" after
 	receiving data from git-push and updating refs.  You can stop
 	it by setting this variable to false.
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 56d8a77ed7..e8d7df14b6 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -2585,17 +2585,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 		if (auto_gc) {
 			struct child_process proc = CHILD_PROCESS_INIT;
 
-			proc.no_stdin = 1;
-			proc.stdout_to_stderr = 1;
-			proc.err = use_sideband ? -1 : 0;
-			proc.git_cmd = proc.close_object_store = 1;
-			strvec_pushl(&proc.args, "gc", "--auto", "--quiet",
-				     NULL);
-
-			if (!start_command(&proc)) {
-				if (use_sideband)
-					copy_to_sideband(proc.err, -1, NULL);
-				finish_command(&proc);
+			if (prepare_auto_maintenance(1, &proc)) {
+				proc.no_stdin = 1;
+				proc.stdout_to_stderr = 1;
+				proc.err = use_sideband ? -1 : 0;
+
+				if (!start_command(&proc)) {
+					if (use_sideband)
+						copy_to_sideband(proc.err, -1, NULL);
+					finish_command(&proc);
+				}
 			}
 		}
 		if (auto_update_server_info)
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 2%]

* [PATCH v2 08/16] http: add support for authtype and credential
    2024-04-17  0:02  4%   ` [PATCH v2 03/16] http: use new headers for each object request brian m. carlson
@ 2024-04-17  0:02  2%   ` brian m. carlson
  1 sibling, 0 replies; 200+ results
From: brian m. carlson @ 2024-04-17  0:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matthew John Cheetham, M Hickford, Jeff King,
	Patrick Steinhardt

Now that we have the credential helper code set up to handle arbitrary
authentications schemes, let's add support for this in the HTTP code,
where we really want to use it.  If we're using this new functionality,
don't set a username and password, and instead set a header wherever
we'd normally do so, including for proxy authentication.

Since we can now handle this case, ask the credential helper to enable
the appropriate capabilities.

Finally, if we're using the authtype value, set "Expect: 100-continue".
Any type of authentication that requires multiple rounds (such as NTLM
or Kerberos) requires a 100 Continue (if we're larger than
http.postBuffer) because otherwise we send the pack data before we're
authenticated, the push gets a 401 response, and we can't rewind the
stream.  We don't know for certain what other custom schemes might
require this, the HTTP/1.1 standard has required handling this since
1999, the broken HTTP server for which we disabled this (Google's) is
now fixed and has been for some time, and libcurl has a 1-second
fallback in case the HTTP server is still broken.  In addition, it is
not unreasonable to require compliance with a 25-year old standard to
use new Git features.  For all of these reasons, do so here.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 http.c                      |  48 ++++++++++---
 http.h                      |   3 +
 remote-curl.c               |   4 +-
 t/t5563-simple-http-auth.sh | 133 ++++++++++++++++++++++++++++++++++++
 4 files changed, 176 insertions(+), 12 deletions(-)

diff --git a/http.c b/http.c
index 54ddff03fb..906eb098c8 100644
--- a/http.c
+++ b/http.c
@@ -561,18 +561,34 @@ static int curl_empty_auth_enabled(void)
 	return 0;
 }
 
+struct curl_slist *http_append_auth_header(const struct credential *c,
+					   struct curl_slist *headers)
+{
+	if (c->authtype && c->credential) {
+		struct strbuf auth = STRBUF_INIT;
+		strbuf_addf(&auth, "Authorization: %s %s",
+			    c->authtype, c->credential);
+		headers = curl_slist_append(headers, auth.buf);
+		strbuf_release(&auth);
+	}
+	return headers;
+}
+
 static void init_curl_http_auth(CURL *result)
 {
-	if (!http_auth.username || !*http_auth.username) {
+	if ((!http_auth.username || !*http_auth.username) &&
+	    (!http_auth.credential || !*http_auth.credential)) {
 		if (curl_empty_auth_enabled())
 			curl_easy_setopt(result, CURLOPT_USERPWD, ":");
 		return;
 	}
 
-	credential_fill(&http_auth, 0);
+	credential_fill(&http_auth, 1);
 
-	curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
-	curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+	if (http_auth.password) {
+		curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+		curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+	}
 }
 
 /* *var must be free-able */
@@ -586,17 +602,22 @@ static void var_override(const char **var, char *value)
 
 static void set_proxyauth_name_password(CURL *result)
 {
+	if (proxy_auth.password) {
 		curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
 			proxy_auth.username);
 		curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
 			proxy_auth.password);
+	} else if (proxy_auth.authtype && proxy_auth.credential) {
+		curl_easy_setopt(result, CURLOPT_PROXYHEADER,
+				 http_append_auth_header(&proxy_auth, NULL));
+	}
 }
 
 static void init_curl_proxy_auth(CURL *result)
 {
 	if (proxy_auth.username) {
-		if (!proxy_auth.password)
-			credential_fill(&proxy_auth, 0);
+		if (!proxy_auth.password && !proxy_auth.credential)
+			credential_fill(&proxy_auth, 1);
 		set_proxyauth_name_password(result);
 	}
 
@@ -1468,7 +1489,7 @@ struct active_request_slot *get_active_slot(void)
 
 	curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
-	if (http_auth.password || curl_empty_auth_enabled())
+	if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
 		init_curl_http_auth(slot->curl);
 
 	return slot;
@@ -1757,7 +1778,8 @@ static int handle_curl_result(struct slot_results *results)
 	} else if (missing_target(results))
 		return HTTP_MISSING_TARGET;
 	else if (results->http_code == 401) {
-		if (http_auth.username && http_auth.password) {
+		if ((http_auth.username && http_auth.password) ||\
+		    (http_auth.authtype && http_auth.credential)) {
 			credential_reject(&http_auth);
 			return HTTP_NOAUTH;
 		} else {
@@ -2065,11 +2087,15 @@ static int http_request(const char *url,
 	/* Add additional headers here */
 	if (options && options->extra_headers) {
 		const struct string_list_item *item;
-		for_each_string_list_item(item, options->extra_headers) {
-			headers = curl_slist_append(headers, item->string);
+		if (options && options->extra_headers) {
+			for_each_string_list_item(item, options->extra_headers) {
+				headers = curl_slist_append(headers, item->string);
+			}
 		}
 	}
 
+	headers = http_append_auth_header(&http_auth, headers);
+
 	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
 	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2190,7 +2216,7 @@ static int http_request_reauth(const char *url,
 		BUG("Unknown http_request target");
 	}
 
-	credential_fill(&http_auth, 0);
+	credential_fill(&http_auth, 1);
 
 	return http_request(url, result, target, options);
 }
diff --git a/http.h b/http.h
index c5f8cc4620..a516ca4a9a 100644
--- a/http.h
+++ b/http.h
@@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename,
 
 int http_fetch_ref(const char *base, struct ref *ref);
 
+struct curl_slist *http_append_auth_header(const struct credential *c,
+					   struct curl_slist *headers);
+
 /* Helpers for fetching packs */
 int http_get_info_packs(const char *base_url,
 			struct packed_git **packs_head);
diff --git a/remote-curl.c b/remote-curl.c
index f96bda2431..1c5416812a 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -931,7 +931,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 		if (err != HTTP_OK)
 			return -1;
 
-		if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
+		if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype)
 			needs_100_continue = 1;
 	}
 
@@ -942,6 +942,8 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 	headers = curl_slist_append(headers, needs_100_continue ?
 		"Expect: 100-continue" : "Expect:");
 
+	headers = http_append_auth_header(&http_auth, headers);
+
 	/* Add Accept-Language header */
 	if (rpc->hdr_accept_language)
 		headers = curl_slist_append(headers, rpc->hdr_accept_language);
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index ab8a721ccc..b3ed0d9fc2 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -74,6 +74,7 @@ test_expect_success 'access using basic auth' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=Basic realm="example.com"
@@ -87,6 +88,43 @@ test_expect_success 'access using basic auth' '
 	EOF
 '
 
+test_expect_success 'access using basic auth via authtype' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Basic
+	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	# Basic base64(alice:secret-passwd)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	capability[]=authtype
+	authtype=Basic
+	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	protocol=http
+	host=$HTTPD_DEST
+	EOF
+'
+
 test_expect_success 'access using basic auth invalid credentials' '
 	test_when_finished "per_test_cleanup" &&
 
@@ -108,6 +146,7 @@ test_expect_success 'access using basic auth invalid credentials' '
 	test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=Basic realm="example.com"
@@ -145,6 +184,7 @@ test_expect_success 'access using basic auth with extra challenges' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -183,6 +223,7 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=foobar param1="value1" param2="value2"
@@ -226,6 +267,7 @@ test_expect_success 'access using basic auth with wwwauth header continuations'
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -271,6 +313,7 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -312,6 +355,7 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -326,4 +370,93 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
 	EOF
 '
 
+test_expect_success 'access using bearer auth' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Bearer
+	credential=YS1naXQtdG9rZW4=
+	EOF
+
+	# Basic base64(a-git-token)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Bearer YS1naXQtdG9rZW4=
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1" param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	capability[]=authtype
+	authtype=Bearer
+	credential=YS1naXQtdG9rZW4=
+	protocol=http
+	host=$HTTPD_DEST
+	EOF
+'
+
+test_expect_success 'access using bearer auth with invalid credentials' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Bearer
+	credential=incorrect-token
+	EOF
+
+	# Basic base64(a-git-token)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Bearer YS1naXQtdG9rZW4=
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1" param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query erase <<-EOF
+	capability[]=authtype
+	authtype=Bearer
+	credential=incorrect-token
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+'
+
 test_done


^ permalink raw reply related	[relevance 2%]

* [PATCH v2 03/16] http: use new headers for each object request
  @ 2024-04-17  0:02  4%   ` brian m. carlson
  2024-04-17  0:02  2%   ` [PATCH v2 08/16] http: add support for authtype and credential brian m. carlson
  1 sibling, 0 replies; 200+ results
From: brian m. carlson @ 2024-04-17  0:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matthew John Cheetham, M Hickford, Jeff King,
	Patrick Steinhardt

Currently we create one set of headers for all object requests and reuse
it.  However, we'll need to adjust the headers for authentication
purposes in the future, so let's create a new set for each request so
that we can adjust them if the authentication changes.

Note that the cost of allocation here is tiny compared to the fact that
we're making a network call, not to mention probably a full TLS
connection, so this shouldn't have a significant impact on performance.
Moreover, nobody who cares about performance is using the dumb HTTP
protocol anyway, since it often makes huge numbers of requests compared
to the smart protocol.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 http.c | 20 +++++++++++---------
 http.h |  2 ++
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/http.c b/http.c
index e73b136e58..22639b0974 100644
--- a/http.c
+++ b/http.c
@@ -128,7 +128,6 @@ static unsigned long empty_auth_useless =
 	| CURLAUTH_DIGEST;
 
 static struct curl_slist *pragma_header;
-static struct curl_slist *no_pragma_header;
 static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct curl_slist *host_resolutions;
@@ -299,6 +298,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
 	return nmemb;
 }
 
+static struct curl_slist *object_request_headers(void)
+{
+	return curl_slist_append(http_copy_default_headers(), "Pragma:");
+}
+
 static void closedown_active_slot(struct active_request_slot *slot)
 {
 	active_requests--;
@@ -1275,8 +1279,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 
 	pragma_header = curl_slist_append(http_copy_default_headers(),
 		"Pragma: no-cache");
-	no_pragma_header = curl_slist_append(http_copy_default_headers(),
-		"Pragma:");
 
 	{
 		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
@@ -1360,9 +1362,6 @@ void http_cleanup(void)
 	curl_slist_free_all(pragma_header);
 	pragma_header = NULL;
 
-	curl_slist_free_all(no_pragma_header);
-	no_pragma_header = NULL;
-
 	curl_slist_free_all(host_resolutions);
 	host_resolutions = NULL;
 
@@ -2370,6 +2369,7 @@ void release_http_pack_request(struct http_pack_request *preq)
 	}
 	preq->slot = NULL;
 	strbuf_release(&preq->tmpfile);
+	curl_slist_free_all(preq->headers);
 	free(preq->url);
 	free(preq);
 }
@@ -2454,11 +2454,11 @@ struct http_pack_request *new_direct_http_pack_request(
 	}
 
 	preq->slot = get_active_slot();
+	preq->headers = object_request_headers();
 	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
-	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
-		no_pragma_header);
+	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
 
 	/*
 	 * If there is data present from a previous transfer attempt,
@@ -2624,13 +2624,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
 	}
 
 	freq->slot = get_active_slot();
+	freq->headers = object_request_headers();
 
 	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
-	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
 
 	/*
 	 * If we have successfully processed data from a previous fetch
@@ -2718,5 +2719,6 @@ void release_http_object_request(struct http_object_request *freq)
 		release_active_slot(freq->slot);
 		freq->slot = NULL;
 	}
+	curl_slist_free_all(freq->headers);
 	strbuf_release(&freq->tmpfile);
 }
diff --git a/http.h b/http.h
index 3af19a8bf5..c5f8cc4620 100644
--- a/http.h
+++ b/http.h
@@ -196,6 +196,7 @@ struct http_pack_request {
 	FILE *packfile;
 	struct strbuf tmpfile;
 	struct active_request_slot *slot;
+	struct curl_slist *headers;
 };
 
 struct http_pack_request *new_http_pack_request(
@@ -229,6 +230,7 @@ struct http_object_request {
 	int zret;
 	int rename;
 	struct active_request_slot *slot;
+	struct curl_slist *headers;
 };
 
 struct http_object_request *new_http_object_request(


^ permalink raw reply related	[relevance 4%]

* [PATCH] mailmap: change primary address for Linus Arver
@ 2024-04-16 23:21  4% Linus Arver via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-16 23:21 UTC (permalink / raw)
  To: git; +Cc: Linus Arver, Linus Arver

From: Linus Arver <linusa@google.com>

Linus will lose access to his work email soon.

Signed-off-by: Linus Arver <linusa@google.com>
---
    mailmap: change primary address for Linus Arver

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1720%2Flistx%2Fmailmap-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1720/listx/mailmap-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1720

 .mailmap | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.mailmap b/.mailmap
index 82129be449f..18128a1250b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -152,6 +152,7 @@ Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
 Lars Noschinski <lars@public.noschinski.de> <lars.noschinski@rwth-aachen.de>
 Li Hong <leehong@pku.edu.cn>
+Linus Arver <linus@ucla.edu> <linusa@google.com>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@evo.osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>

base-commit: 21306a098c3f174ad4c2a5cddb9069ee27a548b0
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [PATCH v5 0/8] docs: recommend using contrib/contacts/git-contacts
  2024-04-11 23:32  2%     ` [PATCH v4 " Linus Arver via GitGitGadget
@ 2024-04-16 23:01  2%       ` Linus Arver via GitGitGadget
  2024-04-18 21:51  3%         ` [PATCH v6 " Linus Arver via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-16 23:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Patrick Steinhardt, Matthieu Moy, Eric Sunshine,
	Kipras Melnikovas, Linus Arver

Make git-contacts more prominent in our docs.


Notable changes in v5
=====================

 * Drop mention of "/usr/share/..." as an "installed" path for
   "git-contacts"; instead point users to the script as a relative path
   inside the Git codebase
 * Minor wording tweaks to commit messages


Notable changes in v4
=====================

 * Avoid using "should" for guidance around using "git-contacts"
 * Clarify where to find the "git-contacts" script (because it's not a
   default builtin command)


Notable changes in v3
=====================

 * Refer to GitGitGadget via a link to MyFirstContribution (instead of
   sending readers to GGG's homepage directly)
 * Soften the advice for using git-contacts


Notable changes in v2
=====================

 * Improve existing mention of git-contacts in SubmittingPatches (instead of
   adding a separate, entirely new paragraph)
 * Add example usage of integrating git-contacts with git-send-email with
   the latter's --cc-cmd flag.
 * Various smaller fixes to SubmittingPatches

Linus Arver (8):
  MyFirstContribution: mention contrib/contacts/git-contacts
  SubmittingPatches: clarify 'git-contacts' location
  SubmittingPatches: mention GitGitGadget
  SubmittingPatches: quote commands
  SubmittingPatches: discuss reviewers first
  SubmittingPatches: dedupe discussion of security patches
  SubmittingPatches: add heading for format-patch and send-email
  SubmittingPatches: demonstrate using git-contacts with git-send-email

 Documentation/MyFirstContribution.txt | 10 ++++
 Documentation/SubmittingPatches       | 73 ++++++++++++++++-----------
 2 files changed, 53 insertions(+), 30 deletions(-)


base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

Range-diff vs v4:

 1:  ad469e4e6db ! 1:  d2c9551ee0e MyFirstContribution: mention contrib/contacts/git-contacts
     @@ Documentation/MyFirstContribution.txt: $ git send-email --to=target@example.com
       NOTE: Check `git help send-email` for some other options which you may find
       valuable, such as changing the Reply-to address or adding more CC and BCC lines.
       
     -+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     -+part of the core `git` binary and must be called separately. Consult your +
     -+package manager to determine where it is located. For example&#44; on Ubuntu-based +
     -+systems it could be installed under +
     -+`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     -+with `perl ...` if it does not have the executable bit set.]
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
     ++not part of the core `git` binary and must be called directly. Clone the Git +
     ++codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     ++in your system).]
      +
      +NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
      +list potential reviewers. In addition, you can do `git send-email
     -+--cc-cmd='/path/to/git-contacts' feature/*.patch`{contrib-scripts} to
     ++--cc-cmd='contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
      +automatically pass this list of emails to `send-email`.
      +
       NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
 2:  c43de19d867 ! 2:  92d72a8a25a SubmittingPatches: clarify 'git-contacts' location
     @@ Metadata
       ## Commit message ##
          SubmittingPatches: clarify 'git-contacts' location
      
     -    Use a dash ("git-contacts", not "git contacts") because the script
     -    is not a core builtin command that is compiled into the `git` binary.
     -    This also puts the script on one line, which should make it easier to
     -    grep for with a loose search query, such as
     +    Use a dash ("git-contacts", not "git contacts") because the script is
     +    not installed as part of "git" toolset. This also puts the script on
     +    one line, which should make it easier to grep for with a loose search
     +    query, such as
      
              $ git grep git.contacts Documentation
      
     -    . Also add a footnote to describe where the script could actually be
     -    located, to help readers who may not be familiar with such "contrib"
     -    scripts (and how they are not accessible with the usual "git
     -    <subcommand>" syntax).
     +    Also add a footnote to describe where the script is located, to help
     +    readers who may not be familiar with such "contrib" scripts (and how
     +    they are not accessible with the usual "git <subcommand>" syntax).
      
          Signed-off-by: Linus Arver <linusa@google.com>
      
     @@ Documentation/SubmittingPatches: security relevant should not be submitted to th
       mentioned below, but should instead be sent privately to the Git
       Security mailing list{security-ml-ref}.
       
     -+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     -+part of the core `git` binary and must be called separately. Consult your +
     -+package manager to determine where it is located. For example&#44; on Ubuntu-based +
     -+systems it could be installed under +
     -+`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     -+with `perl ...` if it does not have the executable bit set.]
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
     ++not part of the core `git` binary and must be called directly. Clone the Git +
     ++codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     ++in your system).]
      +
       Send your patch with "To:" set to the mailing list, with "cc:" listing
      -people who are involved in the area you are touching (the `git
 3:  cd941704176 = 3:  7c4cc5a91f0 SubmittingPatches: mention GitGitGadget
 4:  44470a5d70e = 4:  621912a64fb SubmittingPatches: quote commands
 5:  15f9356ff97 ! 5:  8f44343c482 SubmittingPatches: discuss reviewers first
     @@ Documentation/SubmittingPatches: letter.
      +mentioned below, but should instead be sent privately to the Git
      +Security mailing list{security-ml-ref}.
      +
     -+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     -+part of the core `git` binary and must be called separately. Consult your +
     -+package manager to determine where it is located. For example&#44; on Ubuntu-based +
     -+systems it could be installed under +
     -+`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     -+with `perl ...` if it does not have the executable bit set.]
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
     ++not part of the core `git` binary and must be called directly. Clone the Git +
     ++codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     ++in your system).]
      +
      +Send your patch with "To:" set to the mailing list, with "cc:" listing
      +people who are involved in the area you are touching (the `git-contacts`
     @@ Documentation/SubmittingPatches: patch, format it as "multipart/signed", not a t
      -mentioned below, but should instead be sent privately to the Git
      -Security mailing list{security-ml-ref}.
      -
     --:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     --part of the core `git` binary and must be called separately. Consult your +
     --package manager to determine where it is located. For example&#44; on Ubuntu-based +
     --systems it could be installed under +
     --`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     --with `perl ...` if it does not have the executable bit set.]
     +-:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
     +-not part of the core `git` binary and must be called directly. Clone the Git +
     +-codebase and run `contrib/contacts/git-contacts` (you must have Perl installed +
     +-in your system).]
      -
      -Send your patch with "To:" set to the mailing list, with "cc:" listing
      -people who are involved in the area you are touching (the `git-contacts`
 6:  e889e64bd45 ! 6:  fd8ad38cab0 SubmittingPatches: dedupe discussion of security patches
     @@ Documentation/SubmittingPatches: letter.
      +security relevant should be submitted privately to the Git Security
      +mailing list{security-ml}, instead of the public mailing list.
       
     - :contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     - part of the core `git` binary and must be called separately. Consult your +
     + :contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
     + not part of the core `git` binary and must be called directly. Clone the Git +
      @@ Documentation/SubmittingPatches: Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
       `Tested-by:` lines as necessary to credit people who helped your
       patch, and "cc:" them when sending such a final version for inclusion.
 7:  81556298599 = 7:  b23c73459cc SubmittingPatches: add heading for format-patch and send-email
 8:  84b1cf3f914 ! 8:  911d4f2a0e5 SubmittingPatches: demonstrate using git-contacts with git-send-email
     @@ Documentation/SubmittingPatches: trial merges of your topic to `next` and `seen`
       work by others conflicting with your changes.  There is a good possibility
       that these people may know the area you are touching well.
       
     -+If you are using `send-email`, you can feed it the output of `git contacts` like
     ++If you are using `send-email`, you can feed it the output of `git-contacts` like
      +this:
      +
      +....
     -+	git send-email --cc-cmd='git contacts' feature/*.patch
     ++	git send-email --cc-cmd='contrib/contacts/git-contacts' feature/*.patch
      +....
      +
       :current-maintainer: footnote:[The current maintainer: gitster@pobox.com]

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* Re: [PATCH] Documentation: fix linkgit reference
  2024-04-15  7:22  0% ` Patrick Steinhardt
@ 2024-04-15 18:02  0%   ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-15 18:02 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Yehezkel Bernat via GitGitGadget, git, Yehezkel Bernat

Patrick Steinhardt <ps@pks.im> writes:

> On Sun, Apr 14, 2024 at 10:08:02PM +0000, Yehezkel Bernat via GitGitGadget wrote:
>> From: Yehezkel Bernat <yehezkelshb@gmail.com>
>> 
>> In git-replay documentation, linkgit to git-rev-parse is missing the man
>> section which breaks its rendering
>> 
>> Add section number as done in other references to this command
>
> Nit: sentences should end with a dot.
>
>> Signed-off-by: Yehezkel Bernat <YehezkelShB@gmail.com>
>
> Other than that this patch looks good to me, thanks!
>
> Patrick

Thanks, both.  Will just tweak the proposed log message while
queuing.


>> ---
>>     Documentation: fix linkgit reference
>> 
>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1706%2FYehezkelShB%2Fyb%2Ffix-linkgit-reference-v1
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1706/YehezkelShB/yb/fix-linkgit-reference-v1
>> Pull-Request: https://github.com/git/git/pull/1706
>> 
>>  Documentation/git-replay.txt | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
>> index f6c269c62d5..8f3300c683a 100644
>> --- a/Documentation/git-replay.txt
>> +++ b/Documentation/git-replay.txt
>> @@ -46,7 +46,7 @@ the new commits (in other words, this mimics a cherry-pick operation).
>>  	Range of commits to replay. More than one <revision-range> can
>>  	be passed, but in `--advance <branch>` mode, they should have
>>  	a single tip, so that it's clear where <branch> should point
>> -	to. See "Specifying Ranges" in linkgit:git-rev-parse and the
>> +	to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
>>  	"Commit Limiting" options below.
>>  
>>  include::rev-list-options.txt[]
>> 
>> base-commit: 8f7582d995682f785e80e344197cc715e6bc7d8e
>> -- 
>> gitgitgadget
>> 


^ permalink raw reply	[relevance 0%]

* [PATCH 2/5] builtin: stop using `the_index`
  @ 2024-04-15 11:42  1% ` Patrick Steinhardt
    1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-15 11:42 UTC (permalink / raw)
  To: git

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

Convert builtins to use `the_repository->index` instead of `the_index`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/add.c               |  48 +++++++-------
 builtin/am.c                |  36 +++++------
 builtin/cat-file.c          |   3 +-
 builtin/check-attr.c        |   5 +-
 builtin/check-ignore.c      |   7 +--
 builtin/checkout-index.c    |  22 +++----
 builtin/checkout.c          |  87 +++++++++++++------------
 builtin/clean.c             |   7 +--
 builtin/commit.c            |  81 ++++++++++++------------
 builtin/describe.c          |   3 +-
 builtin/diff-tree.c         |   3 +-
 builtin/diff.c              |   6 +-
 builtin/difftool.c          |   4 +-
 builtin/merge-index.c       |  17 +++--
 builtin/merge-tree.c        |   3 +-
 builtin/merge.c             |  31 +++++----
 builtin/mv.c                |  68 ++++++++++----------
 builtin/pull.c              |   4 +-
 builtin/read-tree.c         |  15 +++--
 builtin/rebase.c            |   3 +-
 builtin/replay.c            |   1 -
 builtin/reset.c             |  32 +++++-----
 builtin/rev-parse.c         |   6 +-
 builtin/rm.c                |  40 ++++++------
 builtin/stash.c             |  45 +++++++------
 builtin/submodule--helper.c |  21 +++----
 builtin/update-index.c      | 122 ++++++++++++++++++------------------
 builtin/write-tree.c        |   6 +-
 28 files changed, 355 insertions(+), 371 deletions(-)

diff --git a/builtin/add.c b/builtin/add.c
index e97699d6b9..dd1e536c03 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Linus Torvalds
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -40,20 +40,20 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 		int err;
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
 
 		if (!show_only)
-			err = chmod_index_entry(&the_index, ce, flip);
+			err = chmod_index_entry(the_repository->index, ce, flip);
 		else
 			err = S_ISREG(ce->ce_mode) ? 0 : -1;
 
@@ -68,20 +68,20 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
 {
 	int i, retval = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 		if (ce_stage(ce))
 			continue; /* do not touch unmerged paths */
 		if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
 			continue; /* do not touch non blobs */
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
-		retval |= add_file_to_index(&the_index, ce->name,
+		retval |= add_file_to_index(the_repository->index, ce->name,
 					    flags | ADD_CACHE_RENORMALIZE);
 	}
 
@@ -100,11 +100,11 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
 	i = dir->nr;
 	while (--i >= 0) {
 		struct dir_entry *entry = *src++;
-		if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+		if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen))
 			*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
-	add_pathspec_matches_against_index(pathspec, &the_index, seen,
+	add_pathspec_matches_against_index(pathspec, the_repository->index, seen,
 					   PS_IGNORE_SKIP_WORKTREE);
 	return seen;
 }
@@ -119,14 +119,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
 		    (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
 
 	seen = xcalloc(pathspec->nr, 1);
-	refresh_index(&the_index, flags, pathspec, seen,
+	refresh_index(the_repository->index, flags, pathspec, seen,
 		      _("Unstaged changes after refreshing the index:"));
 	for (i = 0; i < pathspec->nr; i++) {
 		if (!seen[i]) {
 			const char *path = pathspec->items[i].original;
 
 			if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
-			    !path_in_sparse_checkout(path, &the_index)) {
+			    !path_in_sparse_checkout(path, the_repository->index)) {
 				string_list_append(&only_match_skip_worktree,
 						   pathspec->items[i].original);
 			} else {
@@ -335,12 +335,12 @@ static int add_files(struct dir_struct *dir, int flags)
 
 	for (i = 0; i < dir->nr; i++) {
 		if (!include_sparse &&
-		    !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+		    !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) {
 			string_list_append(&matched_sparse_paths,
 					   dir->entries[i]->name);
 			continue;
 		}
-		if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+		if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) {
 			if (!ignore_add_errors)
 				die(_("adding files failed"));
 			exit_status = 1;
@@ -457,8 +457,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
 		die(_("index file corrupt"));
 
-	die_in_unpopulated_submodule(&the_index, prefix);
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_in_unpopulated_submodule(the_repository->index, prefix);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	if (add_new_files) {
 		int baselen;
@@ -470,7 +470,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		}
 
 		/* This picks up the paths that are not tracked */
-		baselen = fill_directory(&dir, &the_index, &pathspec);
+		baselen = fill_directory(&dir, the_repository->index, &pathspec);
 		if (pathspec.nr)
 			seen = prune_directory(&dir, &pathspec, baselen);
 	}
@@ -487,7 +487,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 		if (!seen)
 			seen = find_pathspecs_matching_against_index(&pathspec,
-					&the_index, PS_IGNORE_SKIP_WORKTREE);
+					the_repository->index, PS_IGNORE_SKIP_WORKTREE);
 
 		/*
 		 * file_exists() assumes exact match
@@ -523,8 +523,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 			    !file_exists(path)) {
 				if (ignore_missing) {
 					int dtype = DT_UNKNOWN;
-					if (is_excluded(&dir, &the_index, path, &dtype))
-						dir_add_ignored(&dir, &the_index,
+					if (is_excluded(&dir, the_repository->index, path, &dtype))
+						dir_add_ignored(&dir, the_repository->index,
 								path, pathspec.items[i].len);
 				} else
 					die(_("pathspec '%s' did not match any files"),
@@ -560,7 +560,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	end_odb_transaction();
 
 finish:
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write new index file"));
 
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2e8d..4db2bc3c2f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
  *
  * Based on git-am.sh by Junio C Hamano.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -1536,8 +1536,8 @@ static int run_apply(const struct am_state *state, const char *index_file)
 
 	if (index_file) {
 		/* Reload index as apply_all_patches() will have modified it. */
-		discard_index(&the_index);
-		read_index_from(&the_index, index_file, get_git_dir());
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, index_file, get_git_dir());
 	}
 
 	return 0;
@@ -1579,10 +1579,10 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 	if (build_fake_ancestor(state, index_path))
 		return error("could not build fake ancestor");
 
-	discard_index(&the_index);
-	read_index_from(&the_index, index_path, get_git_dir());
+	discard_index(the_repository->index);
+	read_index_from(the_repository->index, index_path, get_git_dir());
 
-	if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&orig_tree, the_repository->index, index_path, 0, NULL))
 		return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
 
 	say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1608,12 +1608,12 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		return error(_("Did you hand edit your patch?\n"
 				"It does not apply to blobs recorded in its index."));
 
-	if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL))
 		return error("could not write tree");
 
 	say(state, stdout, _("Falling back to patching base and 3-way merge..."));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
 
 	/*
@@ -1660,7 +1660,7 @@ static void do_commit(const struct am_state *state)
 	if (!state->no_verify && run_hooks("pre-applypatch"))
 		exit(1);
 
-	if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 
 	if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
@@ -1948,7 +1948,7 @@ static void am_resolve(struct am_state *state, int allow_empty)
 		}
 	}
 
-	if (unmerged_index(&the_index)) {
+	if (unmerged_index(the_repository->index)) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
 			"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1987,12 +1987,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
@@ -2006,7 +2006,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2029,8 +2029,8 @@ static int merge_tree(struct tree *tree)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
@@ -2040,7 +2040,7 @@ static int merge_tree(struct tree *tree)
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2068,7 +2068,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
 	if (fast_forward_to(head_tree, head_tree, 1))
 		return -1;
 
-	if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL))
 		return -1;
 
 	index_tree = parse_tree_indirect(&index);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0c948f40fb..fe873807ed 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,7 +3,6 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "convert.h"
@@ -77,7 +76,7 @@ static int filter_object(const char *path, unsigned mode,
 		struct checkout_metadata meta;
 
 		init_checkout_metadata(&meta, NULL, NULL, oid);
-		if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
+		if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) {
 			free(*buf);
 			*size = strbuf.len;
 			*buf = strbuf_detach(&strbuf, NULL);
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index c1da1d184e..9376810710 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "attr.h"
@@ -71,9 +70,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
 		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
-		git_all_attrs(&the_index, full_path, check);
+		git_all_attrs(the_repository->index, full_path, check);
 	} else {
-		git_check_attr(&the_index, full_path, check);
+		git_check_attr(the_repository->index, full_path, check);
 	}
 	output_attr(check, file);
 
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 906cd96753..6c43430ec4 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "dir.h"
@@ -95,21 +94,21 @@ static int check_ignore(struct dir_struct *dir,
 		       PATHSPEC_KEEP_ORDER,
 		       prefix, argv);
 
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	/*
 	 * look for pathspecs matching entries in the index, since these
 	 * should not be ignored, in order to be consistent with
 	 * 'git status', 'git add' etc.
 	 */
-	seen = find_pathspecs_matching_against_index(&pathspec, &the_index,
+	seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
 						     PS_HEED_SKIP_WORKTREE);
 	for (i = 0; i < pathspec.nr; i++) {
 		full_path = pathspec.items[i].match;
 		pattern = NULL;
 		if (!seen[i]) {
 			int dtype = DT_UNKNOWN;
-			pattern = last_matching_pattern(dir, &the_index,
+			pattern = last_matching_pattern(dir, the_repository->index,
 							full_path, &dtype);
 			if (!verbose && pattern &&
 			    pattern->flags & PATTERN_FLAG_NEGATIVE)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 2e086a204d..29e744d11b 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,7 +4,7 @@
  * Copyright (C) 2005 Linus Torvalds
  *
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -69,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix)
 static int checkout_file(const char *name, const char *prefix)
 {
 	int namelen = strlen(name);
-	int pos = index_name_pos(&the_index, name, namelen);
+	int pos = index_name_pos(the_repository->index, name, namelen);
 	int has_same_name = 0;
 	int is_file = 0;
 	int is_skipped = 1;
@@ -79,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix)
 	if (pos < 0)
 		pos = -pos - 1;
 
-	while (pos < the_index.cache_nr) {
-		struct cache_entry *ce = the_index.cache[pos];
+	while (pos <the_repository->index->cache_nr) {
+		struct cache_entry *ce =the_repository->index->cache[pos];
 		if (ce_namelen(ce) != namelen ||
 		    memcmp(ce->name, name, namelen))
 			break;
@@ -140,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length)
 	int i, errs = 0;
 	struct cache_entry *last_ce = NULL;
 
-	for (i = 0; i < the_index.cache_nr ; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr ; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
 			if (!ce_skip_worktree(ce))
@@ -154,8 +154,8 @@ static int checkout_all(const char *prefix, int prefix_length)
 			 * first entry inside the expanded sparse directory).
 			 */
 			if (ignore_skip_worktree) {
-				ensure_full_index(&the_index);
-				ce = the_index.cache[i];
+				ensure_full_index(the_repository->index);
+				ce = the_repository->index->cache[i];
 			}
 		}
 
@@ -260,7 +260,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
 			builtin_checkout_index_usage, 0);
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 	state.force = force;
 	state.quiet = quiet;
 	state.not_new = not_new;
@@ -280,7 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 	 */
 	if (index_opt && !state.base_dir_len && !to_tempfile) {
 		state.refresh_cache = 1;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 		repo_hold_locked_index(the_repository, &lock_file,
 				       LOCK_DIE_ON_ERROR);
 	}
@@ -339,7 +339,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 		return 1;
 
 	if (is_lock_file_locked(&lock_file) &&
-	    write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	    write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("Unable to write new index file");
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 947827de1d..d9dd7ab325 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "branch.h"
@@ -146,7 +145,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 		return READ_TREE_RECURSIVE;
 
 	len = base->len + strlen(pathname);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, base->buf, base->len);
 	memcpy(ce->name + base->len, pathname, len - base->len);
@@ -159,9 +158,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
 	 * do the right thing.
 	 */
-	pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
+	pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
 	if (pos >= 0) {
-		struct cache_entry *old = the_index.cache[pos];
+		struct cache_entry *old = the_repository->index->cache[pos];
 		if (ce->ce_mode == old->ce_mode &&
 		    !ce_intent_to_add(old) &&
 		    oideq(&ce->oid, &old->oid)) {
@@ -171,7 +170,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 		}
 	}
 
-	add_index_entry(&the_index, ce,
+	add_index_entry(the_repository->index, ce,
 			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	return 0;
 }
@@ -190,8 +189,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 
 static int skip_same_name(const struct cache_entry *ce, int pos)
 {
-	while (++pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name))
+	while (++pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name))
 		; /* skip */
 	return pos;
 }
@@ -199,9 +198,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos)
 static int check_stage(int stage, const struct cache_entry *ce, int pos,
 		       int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
 			return 0;
 		pos++;
 	}
@@ -218,8 +217,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 	unsigned seen = 0;
 	const char *name = ce->name;
 
-	while (pos < the_index.cache_nr) {
-		ce = the_index.cache[pos];
+	while (pos < the_repository->index->cache_nr) {
+		ce = the_repository->index->cache[pos];
 		if (strcmp(name, ce->name))
 			break;
 		seen |= (1 << ce_stage(ce));
@@ -235,10 +234,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 			  const struct checkout *state, int *nr_checkouts,
 			  int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
-			return checkout_entry(the_index.cache[pos], state,
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
+			return checkout_entry(the_repository->index->cache[pos], state,
 					      NULL, nr_checkouts);
 		pos++;
 	}
@@ -256,7 +255,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 			   int *nr_checkouts, struct mem_pool *ce_mem_pool,
 			   int conflict_style)
 {
-	struct cache_entry *ce = the_index.cache[pos];
+	struct cache_entry *ce = the_repository->index->cache[pos];
 	const char *path = ce->name;
 	mmfile_t ancestor, ours, theirs;
 	enum ll_merge_result merge_status;
@@ -269,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 	int renormalize = 0;
 
 	memset(threeway, 0, sizeof(threeway));
-	while (pos < the_index.cache_nr) {
+	while (pos < the_repository->index->cache_nr) {
 		int stage;
 		stage = ce_stage(ce);
 		if (!stage || strcmp(path, ce->name))
@@ -278,7 +277,7 @@ static int checkout_merged(int pos, const struct checkout *state,
 		if (stage == 2)
 			mode = create_ce_mode(ce->ce_mode);
 		pos++;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 	}
 	if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
 		return error(_("path '%s' does not have necessary versions"), path);
@@ -356,7 +355,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
 	 * match_pathspec() for _all_ entries when
 	 * opts->source_tree != NULL.
 	 */
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched))
 		ce->ce_flags |= CE_MATCHED;
 }
 
@@ -367,7 +366,7 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 	ce->ce_flags &= ~CE_MATCHED;
 	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 		return;
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) {
 		ce->ce_flags |= CE_MATCHED;
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			/*
@@ -391,7 +390,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
 
 	state.force = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	mem_pool_init(&ce_mem_pool, 0);
 	get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -404,8 +403,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
 	if (pc_workers > 1)
 		init_parallel_checkout();
 
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state,
@@ -429,7 +428,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
 		errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
 					      NULL, NULL);
 	mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
-	remove_marked_cache_entries(&the_index, 1);
+	remove_marked_cache_entries(the_repository->index, 1);
 	remove_scheduled_dirs();
 	errs |= finish_delayed_checkout(&state, opts->show_progress);
 
@@ -571,7 +570,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->source_tree)
 		read_tree_some(opts->source_tree, &opts->pathspec);
 	if (opts->merge)
-		unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
+		unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
 
 	ps_matched = xcalloc(opts->pathspec.nr, 1);
 
@@ -579,13 +578,13 @@ static int checkout_paths(const struct checkout_opts *opts,
 	 * Make sure all pathspecs participated in locating the paths
 	 * to be checked out.
 	 */
-	for (pos = 0; pos < the_index.cache_nr; pos++)
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++)
 		if (opts->overlay_mode)
-			mark_ce_for_checkout_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_overlay(the_repository->index->cache[pos],
 						     ps_matched,
 						     opts);
 		else
-			mark_ce_for_checkout_no_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos],
 							ps_matched,
 							opts);
 
@@ -596,8 +595,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 	free(ps_matched);
 
 	/* Any unmerged paths? */
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
@@ -622,7 +621,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	if (opts->checkout_worktree)
 		errs |= checkout_worktree(opts, new_branch_info);
 	else
-		remove_marked_cache_entries(&the_index, 1);
+		remove_marked_cache_entries(the_repository->index, 1);
 
 	/*
 	 * Allow updating the index when checking out from the index.
@@ -634,7 +633,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 		checkout_index = opts->checkout_index;
 
 	if (checkout_index) {
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die(_("unable to write new index file"));
 	} else {
 		/*
@@ -703,8 +702,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	opts.verbose_update = o->show_progress;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	init_checkout_metadata(&opts.meta, info->refname,
 			       info->commit ? &info->commit->object.oid : null_oid(),
 			       NULL);
@@ -756,12 +755,12 @@ static void init_topts(struct unpack_trees_options *topts, int merge,
 {
 	memset(topts, 0, sizeof(*topts));
 	topts->head_idx = -1;
-	topts->src_index = &the_index;
-	topts->dst_index = &the_index;
+	topts->src_index = the_repository->index;
+	topts->dst_index = the_repository->index;
 
 	setup_unpack_trees_porcelain(topts, "checkout");
 
-	topts->initial_checkout = is_index_unborn(&the_index);
+	topts->initial_checkout = is_index_unborn(the_repository->index);
 	topts->update = 1;
 	topts->merge = 1;
 	topts->quiet = merge && old_commit;
@@ -783,7 +782,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 	if (repo_read_index_preload(the_repository, NULL, 0) < 0)
 		return error(_("index file corrupt"));
 
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
 		if (new_branch_info->commit)
 			BUG("'switch --orphan' should never accept a commit as starting point");
@@ -807,9 +806,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		struct unpack_trees_options topts;
 		const struct object_id *old_commit_oid;
 
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-		if (unmerged_index(&the_index)) {
+		if (unmerged_index(the_repository->index)) {
 			error(_("you need to resolve your current index first"));
 			return 1;
 		}
@@ -918,10 +917,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
 		}
 	}
 
-	if (!cache_tree_fully_valid(the_index.cache_tree))
-		cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+	if (!cache_tree_fully_valid(the_repository->index->cache_tree))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
diff --git a/builtin/clean.c b/builtin/clean.c
index 29efe84153..ded5a91534 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,7 +6,6 @@
  * Based on git-clean.sh by Pavel Roskin
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -714,7 +713,7 @@ static int filter_by_patterns_cmd(void)
 		for_each_string_list_item(item, &del_list) {
 			int dtype = DT_UNKNOWN;
 
-			if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+			if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
 				*item->string = '\0';
 				changed++;
 			}
@@ -1021,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		       PATHSPEC_PREFER_CWD,
 		       prefix, argv);
 
-	fill_directory(&dir, &the_index, &pathspec);
+	fill_directory(&dir, the_repository->index, &pathspec);
 	correct_untracked_entries(&dir);
 
 	for (i = 0; i < dir.nr; i++) {
@@ -1029,7 +1028,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		struct stat st;
 		const char *rel;
 
-		if (!index_name_is_other(&the_index, ent->name, ent->len))
+		if (!index_name_is_other(the_repository->index, ent->name, ent->len))
 			continue;
 
 		if (lstat(ent->name, &st))
diff --git a/builtin/commit.c b/builtin/commit.c
index 7ba7201cfb..09df930d6a 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,6 @@
  * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -266,19 +265,19 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
 	if (with_tree) {
 		char *max_prefix = common_prefix(pattern);
-		overlay_tree_on_index(&the_index, with_tree, max_prefix);
+		overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
 		free(max_prefix);
 	}
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		struct string_list_item *item;
 
 		if (ce->ce_flags & CE_UPDATE)
 			continue;
-		if (!ce_path_match(&the_index, ce, pattern, m))
+		if (!ce_path_match(the_repository->index, ce, pattern, m))
 			continue;
 		item = string_list_insert(list, ce->name);
 		if (ce_skip_worktree(ce))
@@ -302,10 +301,10 @@ static void add_remove_files(struct string_list *list)
 			continue;
 
 		if (!lstat(p->string, &st)) {
-			if (add_to_index(&the_index, p->string, &st, 0))
+			if (add_to_index(the_repository->index, p->string, &st, 0))
 				die(_("updating files failed"));
 		} else
-			remove_file_from_index(&the_index, p->string);
+			remove_file_from_index(the_repository->index, p->string);
 	}
 }
 
@@ -316,7 +315,7 @@ static void create_base_index(const struct commit *current_head)
 	struct tree_desc t;
 
 	if (!current_head) {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		return;
 	}
 
@@ -324,8 +323,8 @@ static void create_base_index(const struct commit *current_head)
 	opts.head_idx = 1;
 	opts.index_only = 1;
 	opts.merge = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	opts.fn = oneway_merge;
 	tree = parse_tree_indirect(&current_head->object.oid);
@@ -344,7 +343,7 @@ static void refresh_cache_or_die(int refresh_flags)
 	 * refresh_flags contains REFRESH_QUIET, so the only errors
 	 * are for unmerged entries.
 	 */
-	if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
 		die_resolve_conflict("commit");
 }
 
@@ -393,7 +392,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
 		refresh_cache_or_die(refresh_flags);
 
-		if (write_locked_index(&the_index, &index_lock, 0))
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to create temporary index"));
 
 		old_repo_index_file = the_repository->index_file;
@@ -412,13 +411,13 @@ static const char *prepare_index(const char **argv, const char *prefix,
 			unsetenv(INDEX_ENVIRONMENT);
 		FREE_AND_NULL(old_index_env);
 
-		discard_index(&the_index);
-		read_index_from(&the_index, get_lock_file_path(&index_lock),
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, get_lock_file_path(&index_lock),
 				get_git_dir());
-		if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
+		if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) {
 			if (reopen_lock_file(&index_lock) < 0)
 				die(_("unable to write index file"));
-			if (write_locked_index(&the_index, &index_lock, 0))
+			if (write_locked_index(the_repository->index, &index_lock, 0))
 				die(_("unable to update temporary index"));
 		} else
 			warning(_("Failed to update main cache tree"));
@@ -446,8 +445,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
 		add_files_to_cache(the_repository, also ? prefix : NULL,
 				   &pathspec, 0, 0);
 		refresh_cache_or_die(refresh_flags);
-		cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock, 0))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_NORMAL;
 		ret = get_lock_file_path(&index_lock);
@@ -467,10 +466,10 @@ static const char *prepare_index(const char **argv, const char *prefix,
 		repo_hold_locked_index(the_repository, &index_lock,
 				       LOCK_DIE_ON_ERROR);
 		refresh_cache_or_die(refresh_flags);
-		if (the_index.cache_changed
-		    || !cache_tree_fully_valid(the_index.cache_tree))
-			cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock,
+		if (the_repository->index->cache_changed
+		    || !cache_tree_fully_valid(the_repository->index->cache_tree))
+			cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_AS_IS;
@@ -511,15 +510,15 @@ static const char *prepare_index(const char **argv, const char *prefix,
 	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
 		exit(1);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("cannot read the index"));
 
 	repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
-	cache_tree_update(&the_index, WRITE_TREE_SILENT);
-	if (write_locked_index(&the_index, &index_lock, 0))
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+	cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+	if (write_locked_index(the_repository->index, &index_lock, 0))
 		die(_("unable to write new index file"));
 
 	hold_lock_file_for_update(&false_lock,
@@ -529,14 +528,14 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
 	create_base_index(current_head);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-	if (write_locked_index(&the_index, &false_lock, 0))
+	if (write_locked_index(the_repository->index, &false_lock, 0))
 		die(_("unable to write temporary index file"));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	ret = get_lock_file_path(&false_lock);
-	read_index_from(&the_index, ret, get_git_dir());
+	read_index_from(the_repository->index, ret, get_git_dir());
 out:
 	string_list_clear(&partial, 0);
 	clear_pathspec(&pathspec);
@@ -994,7 +993,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct object_id oid;
 		const char *parent = "HEAD";
 
-		if (!the_index.initialized && repo_read_index(the_repository) < 0)
+		if (!the_repository->index->initialized && repo_read_index(the_repository) < 0)
 			die(_("Cannot read index"));
 
 		if (amend)
@@ -1004,11 +1003,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 			int i, ita_nr = 0;
 
 			/* TODO: audit for interaction with sparse-index. */
-			ensure_full_index(&the_index);
-			for (i = 0; i < the_index.cache_nr; i++)
-				if (ce_intent_to_add(the_index.cache[i]))
+			ensure_full_index(the_repository->index);
+			for (i = 0; i < the_repository->index->cache_nr; i++)
+				if (ce_intent_to_add(the_repository->index->cache[i]))
 					ita_nr++;
-			committable = the_index.cache_nr - ita_nr > 0;
+			committable = the_repository->index->cache_nr - ita_nr > 0;
 		} else {
 			/*
 			 * Unless the user did explicitly request a submodule
@@ -1076,11 +1075,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		 * and could have updated it. We must do this before we invoke
 		 * the editor and after we invoke run_status above.
 		 */
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 
-	if (cache_tree_update(&the_index, 0)) {
+	if (cache_tree_update(the_repository->index, 0)) {
 		error(_("Error building trees"));
 		return 0;
 	}
@@ -1581,7 +1580,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	    status_format != STATUS_FORMAT_PORCELAIN_V2)
 		progress_flag = REFRESH_PROGRESS;
 	repo_read_index(the_repository);
-	refresh_index(&the_index,
+	refresh_index(the_repository->index,
 		      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
 		      &s.pathspec, NULL, NULL);
 
@@ -1851,7 +1850,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		append_merge_tag_headers(parents, &tail);
 	}
 
-	if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
+	if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
 				 parents, &oid, author_ident.buf, NULL,
 				 sign_commit, extra)) {
 		rollback_index_files();
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a714f..c0e3301e3c 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -674,7 +673,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 			prepare_repo_settings(the_repository);
 			the_repository->settings.command_requires_full_index = 0;
 			repo_read_index(the_repository);
-			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+			refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED,
 				      NULL, NULL, NULL);
 			fd = repo_hold_locked_index(the_repository,
 						    &index_lock, 0);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a8e68ce8ef..0d3c611aac 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "diff.h"
@@ -206,7 +205,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 		opt->diffopt.rotate_to_strict = 0;
 		opt->diffopt.no_free = 1;
 		if (opt->diffopt.detect_rename) {
-			if (!the_index.cache)
+			if (the_repository->index->cache)
 				repo_read_index(the_repository);
 			opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
 		}
diff --git a/builtin/diff.c b/builtin/diff.c
index 6e196e0c7d..efc37483b3 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "ewah/ewok.h"
@@ -239,9 +239,9 @@ static void refresh_index_quietly(void)
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
 	if (fd < 0)
 		return;
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
 		      NULL);
 	repo_update_index_if_able(the_repository, &lock_file);
 }
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a3c72b8258..a130faae4f 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,7 +11,7 @@
  *
  * Copyright (C) 2016 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -117,7 +117,7 @@ static int use_wt_file(const char *workdir, const char *name,
 		int fd = open(buf.buf, O_RDONLY);
 
 		if (fd >= 0 &&
-		    !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+		    !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
 			if (is_null_oid(oid)) {
 				oidcpy(oid, &wt_oid);
 				use = 1;
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 270d5f644a..0fabe3f6bb 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "hex.h"
 #include "read-cache-ll.h"
@@ -18,11 +17,11 @@ static int merge_entry(int pos, const char *path)
 	char ownbuf[4][60];
 	struct child_process cmd = CHILD_PROCESS_INIT;
 
-	if (pos >= the_index.cache_nr)
+	if (pos >= the_repository->index->cache_nr)
 		die("git merge-index: %s not in the cache", path);
 	found = 0;
 	do {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		int stage = ce_stage(ce);
 
 		if (strcmp(ce->name, path))
@@ -32,7 +31,7 @@ static int merge_entry(int pos, const char *path)
 		xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
 		arguments[stage] = hexbuf[stage];
 		arguments[stage + 4] = ownbuf[stage];
-	} while (++pos < the_index.cache_nr);
+	} while (++pos < the_repository->index->cache_nr);
 	if (!found)
 		die("git merge-index: %s not in the cache", path);
 
@@ -51,7 +50,7 @@ static int merge_entry(int pos, const char *path)
 
 static void merge_one_path(const char *path)
 {
-	int pos = index_name_pos(&the_index, path, strlen(path));
+	int pos = index_name_pos(the_repository->index, path, strlen(path));
 
 	/*
 	 * If it already exists in the cache as stage0, it's
@@ -65,9 +64,9 @@ static void merge_all(void)
 {
 	int i;
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		if (!ce_stage(ce))
 			continue;
 		i += merge_entry(i, ce->name)-1;
@@ -89,7 +88,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
 	repo_read_index(the_repository);
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
+	ensure_full_index(the_repository->index);
 
 	i = 1;
 	if (!strcmp(argv[i], "-o")) {
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8bdb439131..1082d919fd 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "tree-walk.h"
 #include "xdiff-interface.h"
@@ -364,7 +363,7 @@ static void trivial_merge_trees(struct tree_desc t[3], const char *base)
 
 	setup_traverse_info(&info, base);
 	info.fn = threeway_callback;
-	traverse_trees(&the_index, 3, t, &info);
+	traverse_trees(the_repository->index, 3, t, &info);
 }
 
 static void *get_tree_descriptor(struct repository *r,
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec87fc..6a6d379885 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,6 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -300,7 +299,7 @@ static int save_state(struct object_id *stash)
 	int rc = -1;
 
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
 		repo_update_index_if_able(the_repository, &lock_file);
 	rollback_lock_file(&lock_file);
@@ -372,7 +371,7 @@ static void restore_state(const struct object_id *head,
 	run_command(&cmd);
 
 refresh_cache:
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("could not read index"));
 }
@@ -657,8 +656,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 2;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.verbose_update = 1;
 	opts.trivial_merges_only = 1;
@@ -674,7 +673,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 	if (!trees[nr_trees++])
 		return -1;
 	opts.fn = threeway_merge;
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		parse_tree(trees[i]);
 		init_tree_desc(t+i, &trees[i]->object.oid,
@@ -687,7 +686,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 static void write_tree_trivial(struct object_id *oid)
 {
-	if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 }
 
@@ -745,7 +744,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 			rollback_lock_file(&lock);
 			return 2;
 		}
-		if (write_locked_index(&the_index, &lock,
+		if (write_locked_index(the_repository->index, &lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write %s"), get_index_file());
 		return clean ? 0 : 1;
@@ -768,8 +767,8 @@ static int count_unmerged_entries(void)
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++)
-		if (ce_stage(the_index.cache[i]))
+	for (i = 0; i < the_repository->index->cache_nr; i++)
+		if (ce_stage(the_repository->index->cache[i]))
 			ret++;
 
 	return ret;
@@ -843,9 +842,9 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 		 * the editor and after we invoke run_status above.
 		 */
 		if (invoked_hook)
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 	strbuf_addbuf(&msg, &merge_msg);
 	if (squash)
 		BUG("the control must not reach here under --squash");
@@ -957,7 +956,7 @@ static int suggest_conflicts(void)
 	 * Thus, we will get the cleanup mode which is returned when we _are_
 	 * using an editor.
 	 */
-	append_conflicts_hint(&the_index, &msgbuf,
+	append_conflicts_hint(the_repository->index, &msgbuf,
 			      get_cleanup_mode(cleanup_arg, 1));
 	fputs(msgbuf.buf, fp);
 	strbuf_release(&msgbuf);
@@ -1386,7 +1385,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		else
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	if (option_edit < 0)
 		option_edit = default_edit_option();
@@ -1595,7 +1594,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		 * We are not doing octopus, not fast-forward, and have
 		 * only one common.
 		 */
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 		if (allow_trivial && fast_forward != FF_ONLY) {
 			/*
 			 * Must first ensure that index matches HEAD before
@@ -1784,6 +1783,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	return ret;
 }
diff --git a/builtin/mv.c b/builtin/mv.c
index 22e64fc290..74aa9746aa 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -95,9 +95,9 @@ static void prepare_move_submodule(const char *src, int first,
 				   const char **submodule_gitfile)
 {
 	struct strbuf submodule_dotgit = STRBUF_INIT;
-	if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
+	if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
 		die(_("Directory %s is in index and no submodule?"), src);
-	if (!is_staging_gitmodules_ok(&the_index))
+	if (!is_staging_gitmodules_ok(the_repository->index))
 		die(_("Please stage your changes to .gitmodules or stash them to proceed"));
 	strbuf_addf(&submodule_dotgit, "%s/.git", src);
 	*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
@@ -114,13 +114,13 @@ static int index_range_of_same_dir(const char *src, int length,
 	const char *src_w_slash = add_slash(src);
 	int first, last, len_w_slash = length + 1;
 
-	first = index_name_pos(&the_index, src_w_slash, len_w_slash);
+	first = index_name_pos(the_repository->index, src_w_slash, len_w_slash);
 	if (first >= 0)
 		die(_("%.*s is in index"), len_w_slash, src_w_slash);
 
 	first = -1 - first;
-	for (last = first; last < the_index.cache_nr; last++) {
-		const char *path = the_index.cache[last]->name;
+	for (last = first; last < the_repository->index->cache_nr; last++) {
+		const char *path = the_repository->index->cache[last]->name;
 		if (strncmp(path, src_w_slash, len_w_slash))
 			break;
 	}
@@ -144,14 +144,14 @@ static int empty_dir_has_sparse_contents(const char *name)
 	const char *with_slash = add_slash(name);
 	int length = strlen(with_slash);
 
-	int pos = index_name_pos(&the_index, with_slash, length);
+	int pos = index_name_pos(the_repository->index, with_slash, length);
 	const struct cache_entry *ce;
 
 	if (pos < 0) {
 		pos = -pos - 1;
-		if (pos >= the_index.cache_nr)
+		if (pos >= the_repository->index->cache_nr)
 			goto free_return;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 		if (strncmp(with_slash, ce->name, length))
 			goto free_return;
 		if (ce_skip_worktree(ce))
@@ -223,7 +223,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			S_ISDIR(st.st_mode)) {
 		destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
 	} else {
-		if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
+		if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) &&
 		    empty_dir_has_sparse_contents(dst_w_slash)) {
 			destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
 			dst_mode = SKIP_WORKTREE_DIR;
@@ -239,7 +239,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			 * is deprecated at this point) sparse-checkout. As
 			 * SPARSE here is only considering cone-mode situation.
 			 */
-			if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
+			if (!path_in_cone_mode_sparse_checkout(destination[0], the_repository->index))
 				dst_mode = SPARSE;
 		}
 	}
@@ -263,10 +263,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			int pos;
 			const struct cache_entry *ce;
 
-			pos = index_name_pos(&the_index, src, length);
+			pos = index_name_pos(the_repository->index, src, length);
 			if (pos < 0) {
 				const char *src_w_slash = add_slash(src);
-				if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+				if (!path_in_sparse_checkout(src_w_slash, the_repository->index) &&
 				    empty_dir_has_sparse_contents(src)) {
 					modes[i] |= SKIP_WORKTREE_DIR;
 					goto dir_check;
@@ -276,7 +276,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 					bad = _("bad source");
 				goto act_on_entry;
 			}
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!ce_skip_worktree(ce)) {
 				bad = _("bad source");
 				goto act_on_entry;
@@ -286,7 +286,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 				goto act_on_entry;
 			}
 			/* Check if dst exists in index */
-			if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+			if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) {
 				modes[i] |= SPARSE;
 				goto act_on_entry;
 			}
@@ -311,7 +311,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 dir_check:
 		if (S_ISDIR(st.st_mode)) {
 			int j, dst_len, n;
-			int first = index_name_pos(&the_index, src, length), last;
+			int first = index_name_pos(the_repository->index, src, length), last;
 
 			if (first >= 0) {
 				prepare_move_submodule(src, first,
@@ -339,7 +339,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			dst_len = strlen(dst);
 
 			for (j = 0; j < last - first; j++) {
-				const struct cache_entry *ce = the_index.cache[first + j];
+				const struct cache_entry *ce = the_repository->index->cache[first + j];
 				const char *path = ce->name;
 				source[argc + j] = path;
 				destination[argc + j] =
@@ -351,7 +351,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			argc += last - first;
 			goto act_on_entry;
 		}
-		if (!(ce = index_file_exists(&the_index, src, length, 0))) {
+		if (!(ce = index_file_exists(the_repository->index, src, length, 0))) {
 			bad = _("not under version control");
 			goto act_on_entry;
 		}
@@ -387,7 +387,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
 		if (ignore_sparse &&
 		    (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
-		    index_entry_exists(&the_index, dst, strlen(dst))) {
+		    index_entry_exists(the_repository->index, dst, strlen(dst))) {
 			bad = _("destination exists in the index");
 			if (force) {
 				if (verbose)
@@ -404,12 +404,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		 * option as a way to have a successful run.
 		 */
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(src, &the_index)) {
+		    !path_in_sparse_checkout(src, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, src);
 			skip_sparse = 1;
 		}
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(dst, &the_index)) {
+		    !path_in_sparse_checkout(dst, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, dst);
 			skip_sparse = 1;
 		}
@@ -449,7 +449,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		int pos;
 		int sparse_and_dirty = 0;
 		struct checkout state = CHECKOUT_INIT;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 
 		if (force)
 			state.force = 1;
@@ -476,14 +476,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
 			continue;
 
-		pos = index_name_pos(&the_index, src, strlen(src));
+		pos = index_name_pos(the_repository->index, src, strlen(src));
 		assert(pos >= 0);
 		if (!(mode & SPARSE) && !lstat(src, &st))
-			sparse_and_dirty = ie_modified(&the_index,
-						       the_index.cache[pos],
+			sparse_and_dirty = ie_modified(the_repository->index,
+						       the_repository->index->cache[pos],
 						       &st,
 						       0);
-		rename_index_entry_at(&the_index, pos, dst);
+		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
 		    core_apply_sparse_checkout &&
@@ -495,11 +495,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 			 * should be added in a future patch.
 			 */
 			if ((mode & SPARSE) &&
-			    path_in_sparse_checkout(dst, &the_index)) {
+			    path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from out-of-cone to in-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
@@ -507,11 +507,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 					die(_("cannot checkout %s"), dst_ce->name);
 			} else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
 				   !(mode & SPARSE) &&
-				   !path_in_sparse_checkout(dst, &the_index)) {
+				   !path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from in-cone to out-of-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				/*
 				 * if src is clean, it will suffice to remove it
@@ -559,9 +559,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 		advise_on_moving_dirty_path(&dirty_paths);
 
 	if (gitmodules_modified)
-		stage_updated_gitmodules(&the_index);
+		stage_updated_gitmodules(the_repository->index);
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76d52..66869210db 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,7 +5,7 @@
  *
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -1044,7 +1044,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 		if (opt_autostash == -1)
 			opt_autostash = config_autostash;
 
-		if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
+		if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index))
 			die(_("Updating an unborn branch with changes added to the index."));
 
 		if (!opt_autostash)
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 6f89cec0fb..a8cf8504b8 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,7 +4,6 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -159,8 +158,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = -1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	git_config(git_read_tree_config, NULL);
 
@@ -197,7 +196,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 			die(_("You need to resolve your current index first"));
 		stage = opts.merge = 1;
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
@@ -225,7 +224,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 		setup_work_tree();
 
 	if (opts.skip_sparse_checkout)
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 	if (opts.merge) {
 		switch (stage - 1) {
@@ -237,7 +236,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 			break;
 		case 2:
 			opts.fn = twoway_merge;
-			opts.initial_checkout = is_index_unborn(&the_index);
+			opts.initial_checkout = is_index_unborn(the_repository->index);
 			break;
 		case 3:
 		default:
@@ -258,7 +257,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 	if (nr_trees == 1 && !opts.prefix)
 		opts.skip_cache_tree_update = 1;
 
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
 		if (parse_tree(tree) < 0)
@@ -282,7 +281,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 				 the_repository->index,
 				 trees[0]);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("unable to write new index file");
 	return 0;
 }
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 891f28468e..fe17d562a8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,7 +4,6 @@
  * Copyright (c) 2018 Pratik Karki
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -295,7 +294,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 	if (ret)
 		error(_("could not generate todo list"));
 	else {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 						&todo_list))
 			BUG("unusable todo list");
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bc4b47f09..6bf0691f15 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,7 +2,6 @@
  * "git replay" builtin command
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "git-compat-util.h"
 
 #include "builtin.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6332..b6dacf9678 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -66,8 +66,8 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.fn = oneway_merge;
 	opts.merge = 1;
 	init_checkout_metadata(&opts.meta, ref, oid, NULL);
@@ -159,11 +159,11 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		struct cache_entry *ce;
 
 		if (!is_in_reset_tree && !intent_to_add) {
-			remove_file_from_index(&the_index, one->path);
+			remove_file_from_index(the_repository->index, one->path);
 			continue;
 		}
 
-		ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+		ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path,
 				      0, 0);
 
 		/*
@@ -174,9 +174,9 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		 * if this entry is outside the sparse cone - this is necessary
 		 * to properly construct the reset sparse directory.
 		 */
-		pos = index_name_pos(&the_index, one->path, strlen(one->path));
-		if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
-		    (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+		pos = index_name_pos(the_repository->index, one->path, strlen(one->path));
+		if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) ||
+		    (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index)))
 			ce->ce_flags |= CE_SKIP_WORKTREE;
 
 		if (!ce)
@@ -186,7 +186,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 			ce->ce_flags |= CE_INTENT_TO_ADD;
 			set_object_name_for_intent_to_add_entry(ce);
 		}
-		add_index_entry(&the_index, ce,
+		add_index_entry(the_repository->index, ce,
 				ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	}
 }
@@ -208,8 +208,8 @@ static int read_from_tree(const struct pathspec *pathspec,
 	opt.change = diff_change;
 	opt.add_remove = diff_addremove;
 
-	if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec))
+		ensure_full_index(the_repository->index);
 
 	if (do_diff_cache(tree_oid, &opt))
 		return 1;
@@ -235,7 +235,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action,
 
 static void die_if_unmerged_cache(int reset_type)
 {
-	if (is_merge() || unmerged_index(&the_index))
+	if (is_merge() || unmerged_index(the_repository->index))
 		die(_("Cannot do a %s reset in the middle of a merge."),
 		    _(reset_type_names[reset_type]));
 
@@ -470,12 +470,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 				update_ref_status = 1;
 				goto cleanup;
 			}
-			the_index.updated_skipworktree = 1;
+			the_repository->index->updated_skipworktree = 1;
 			if (!no_refresh && get_git_work_tree()) {
 				uint64_t t_begin, t_delta_in_ms;
 
 				t_begin = getnanotime();
-				refresh_index(&the_index, flags, NULL, NULL,
+				refresh_index(the_repository->index, flags, NULL, NULL,
 					      _("Unstaged changes after reset:"));
 				t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
 				if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
@@ -501,7 +501,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			free(ref);
 		}
 
-		if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK))
 			die(_("Could not write new index file."));
 	}
 
@@ -516,7 +516,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (!pathspec.nr)
 		remove_branch_state(the_repository, 0);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 
 cleanup:
 	clear_pathspec(&pathspec);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 624182e507..af79538632 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -1049,8 +1049,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 			if (!strcmp(arg, "--shared-index-path")) {
 				if (repo_read_index(the_repository) < 0)
 					die(_("Could not read the index"));
-				if (the_index.split_index) {
-					const struct object_id *oid = &the_index.split_index->base_oid;
+				if (the_repository->index->split_index) {
+					const struct object_id *oid = &the_repository->index->split_index->base_oid;
 					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
 					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
diff --git a/builtin/rm.c b/builtin/rm.c
index fd130cea2d..d195c16e74 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -41,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos)
 {
 	int i = -pos - 1;
 
-	while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
-		if (ce_stage(the_index.cache[i]) == 2)
+	while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
+		if (ce_stage(the_repository->index->cache[i]) == 2)
 			return i;
 		i++;
 	}
@@ -78,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void)
 		int pos;
 		const struct cache_entry *ce;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			pos = get_ours_cache_pos(name, pos);
 			if (pos < 0)
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (!S_ISGITLINK(ce->ce_mode) ||
 		    !file_exists(ce->name) ||
@@ -122,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 		int local_changes = 0;
 		int staged_changes = 0;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			/*
 			 * Skip unmerged entries except for populated submodules
@@ -132,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only)
 			if (pos < 0)
 				continue;
 
-			if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
+			if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) ||
 			    is_empty_dir(name))
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (lstat(ce->name, &st) < 0) {
 			if (!is_missing_file_error(errno))
@@ -173,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only)
 		 * Is the index different from the file in the work tree?
 		 * If it's a submodule, is its work tree modified?
 		 */
-		if (ie_match_stat(&the_index, ce, &st, 0) ||
+		if (ie_match_stat(the_repository->index, ce, &st, 0) ||
 		    (S_ISGITLINK(ce->ce_mode) &&
 		     bad_to_remove_submodule(ce->name,
 				SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -301,27 +301,27 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
 
-	if (pathspec_needs_expanded_index(&the_index, &pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
+		ensure_full_index(the_repository->index);
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
-		if (!ce_path_match(&the_index, ce, &pathspec, seen))
+		if (!ce_path_match(the_repository->index, ce, &pathspec, seen))
 			continue;
 		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
 		list.entry[list.nr].name = xstrdup(ce->name);
 		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
 		if (list.entry[list.nr++].is_submodule &&
-		    !is_staging_gitmodules_ok(&the_index))
+		    !is_staging_gitmodules_ok(the_repository->index))
 			die(_("please stage your changes to .gitmodules or stash them to proceed"));
 	}
 
@@ -391,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		if (!quiet)
 			printf("rm '%s'\n", path);
 
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die(_("git rm: unable to remove %s"), path);
 	}
 
@@ -432,10 +432,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		}
 		strbuf_release(&buf);
 		if (gitmodules_modified)
-			stage_updated_gitmodules(&the_index);
+			stage_updated_gitmodules(the_repository->index);
 	}
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 062be1fbc0..69c01ba136 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -273,7 +272,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	struct lock_file lock_file = LOCK_INIT;
 
 	repo_read_index_preload(the_repository, NULL, 0);
-	if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL))
 		return -1;
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -287,8 +286,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
 	opts.update = update;
@@ -299,7 +298,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
 	if (unpack_trees(nr_trees, t, &opts))
 		return -1;
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		return error(_("unable to write new index file"));
 
 	return 0;
@@ -430,7 +429,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	/*
 	 * Step 1: get a difference between orig_tree (which corresponding
@@ -454,7 +453,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 
 		/* Look up the path's position in the current index. */
 		p = diff_queued_diff.queue[i];
-		pos = index_name_pos(&the_index, p->two->path,
+		pos = index_name_pos(the_repository->index, p->two->path,
 				     strlen(p->two->path));
 
 		/*
@@ -465,10 +464,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 		 * path, but left it out of the working tree, then clear the
 		 * SKIP_WORKTREE bit and write it to the working tree.
 		 */
-		if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
+		if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) {
 			struct stat st;
 
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!lstat(ce->name, &st)) {
 				/* Conflicting path present; relocate it */
 				struct strbuf new_path = STRBUF_INIT;
@@ -504,12 +503,12 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 			if (pos < 0)
 				option = ADD_CACHE_OK_TO_ADD;
 
-			ce = make_cache_entry(&the_index,
+			ce = make_cache_entry(the_repository->index,
 					      p->one->mode,
 					      &p->one->oid,
 					      p->one->path,
 					      0, 0);
-			add_index_entry(&the_index, ce, option);
+			add_index_entry(the_repository->index, ce, option);
 		}
 	}
 	diff_flush(&diff_opts);
@@ -518,7 +517,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
 	 * Step 4: write the new index to disk
 	 */
 	repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
-	if (write_locked_index(&the_index, &lock,
+	if (write_locked_index(the_repository->index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("could not write index"));
 }
@@ -539,7 +538,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 					 NULL, NULL, NULL))
 		return error(_("could not write index"));
 
-	if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0,
 				NULL))
 		return error(_("cannot apply a stash in the middle of a merge"));
 
@@ -562,14 +561,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 				return error(_("conflicts in index. "
 					       "Try without --index."));
 
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
-			if (write_index_as_tree(&index_tree, &the_index,
+			if (write_index_as_tree(&index_tree, the_repository->index,
 						get_index_file(), 0, NULL))
 				return error(_("could not save index tree"));
 
 			reset_head();
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
 		}
 	}
@@ -875,8 +874,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 	}
 
 	unpack_tree_opt.head_idx = -1;
-	unpack_tree_opt.src_index = &the_index;
-	unpack_tree_opt.dst_index = &the_index;
+	unpack_tree_opt.src_index = the_repository->index;
+	unpack_tree_opt.dst_index = the_repository->index;
 	unpack_tree_opt.merge = 1;
 	unpack_tree_opt.fn = stash_worktree_untracked_merge;
 
@@ -1395,7 +1394,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 	strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
 	commit_list_insert(head_commit, &parents);
-	if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0,
 				NULL) ||
 	    commit_tree(commit_tree_label.buf, commit_tree_label.len,
 			&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
@@ -1540,9 +1539,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 		char *ps_matched = xcalloc(ps->nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
-		for (i = 0; i < the_index.cache_nr; i++)
-			ce_path_match(&the_index, the_index.cache[i], ps,
+		ensure_full_index(the_repository->index);
+		for (i = 0; i < the_repository->index->cache_nr; i++)
+			ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
 				      ps_matched);
 
 		if (report_path_error(ps_matched, ps)) {
@@ -1612,7 +1611,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 				goto done;
 			}
 		}
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (ps->nr) {
 			struct child_process cp_add = CHILD_PROCESS_INIT;
 			struct child_process cp_diff = CHILD_PROCESS_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e4e18adb57..93f4b9d726 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -207,18 +206,18 @@ static int module_list_compute(const char **argv,
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
-		if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+		if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce),
 				    0, ps_matched, 1) ||
 		    !S_ISGITLINK(ce->ce_mode))
 			continue;
 
 		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
 		list->entries[list->nr++] = ce;
-		while (i + 1 < the_index.cache_nr &&
-		       !strcmp(ce->name, the_index.cache[i + 1]->name))
+		while (i + 1 < the_repository->index->cache_nr &&
+		       !strcmp(ce->name, the_repository->index->cache[i + 1]->name))
 			/*
 			 * Skip entries with the same name in different stages
 			 * to make sure an entry is returned only once.
@@ -907,7 +906,7 @@ static void generate_submodule_summary(struct summary_cb *info,
 			int fd = open(p->sm_path, O_RDONLY);
 
 			if (fd < 0 || fstat(fd, &st) < 0 ||
-			    index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+			    index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB,
 				     p->sm_path, 0))
 				error(_("couldn't hash object from '%s'"), p->sm_path);
 		} else {
@@ -3243,21 +3242,21 @@ static void die_on_index_match(const char *path, int force)
 		char *ps_matched = xcalloc(ps.nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 		/*
 		 * Since there is only one pathspec, we just need to
 		 * check ps_matched[0] to know if a cache entry matched.
 		 */
-		for (i = 0; i < the_index.cache_nr; i++) {
-			ce_path_match(&the_index, the_index.cache[i], &ps,
+		for (i = 0; i < the_repository->index->cache_nr; i++) {
+			ce_path_match(the_repository->index, the_repository->index->cache[i], &ps,
 				      ps_matched);
 
 			if (ps_matched[0]) {
 				if (!force)
 					die(_("'%s' already exists in the index"),
 					    path);
-				if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
+				if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode))
 					die(_("'%s' already exists in the index "
 					      "and is not a submodule"), path);
 				break;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa1476c..6321810006 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "bulk-checkin.h"
 #include "config.h"
@@ -247,16 +247,16 @@ static int test_if_untracked_cache_is_supported(void)
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
 	int namelen = strlen(path);
-	int pos = index_name_pos(&the_index, path, namelen);
+	int pos = index_name_pos(the_repository->index, path, namelen);
 	if (0 <= pos) {
-		mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
+		mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]);
 		if (mark)
-			the_index.cache[pos]->ce_flags |= flag;
+			the_repository->index->cache[pos]->ce_flags |= flag;
 		else
-			the_index.cache[pos]->ce_flags &= ~flag;
-		the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
-		cache_tree_invalidate_path(&the_index, path);
-		the_index.cache_changed |= CE_ENTRY_CHANGED;
+			the_repository->index->cache[pos]->ce_flags &= ~flag;
+		the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+		cache_tree_invalidate_path(the_repository->index, path);
+		the_repository->index->cache_changed |= CE_ENTRY_CHANGED;
 		return 0;
 	}
 	return -1;
@@ -266,7 +266,7 @@ static int remove_one_path(const char *path)
 {
 	if (!allow_remove)
 		return error("%s: does not exist and --remove not passed", path);
-	if (remove_file_from_index(&the_index, path))
+	if (remove_file_from_index(the_repository->index, path))
 		return error("%s: cannot remove from the index", path);
 	return 0;
 }
@@ -291,24 +291,24 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 	struct cache_entry *ce;
 
 	/* Was the old index entry already up-to-date? */
-	if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
+	if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0))
 		return 0;
 
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	memcpy(ce->name, path, len);
 	ce->ce_flags = create_ce_flags(0);
 	ce->ce_namelen = len;
-	fill_stat_cache_info(&the_index, ce, st);
+	fill_stat_cache_info(the_repository->index, ce, st);
 	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-	if (index_path(&the_index, &ce->oid, path, st,
+	if (index_path(the_repository->index, &ce->oid, path, st,
 		       info_only ? 0 : HASH_WRITE_OBJECT)) {
 		discard_cache_entry(ce);
 		return -1;
 	}
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option)) {
+	if (add_index_entry(the_repository->index, ce, option)) {
 		discard_cache_entry(ce);
 		return error("%s: cannot add to the index - missing --add option?", path);
 	}
@@ -341,11 +341,11 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 static int process_directory(const char *path, int len, struct stat *st)
 {
 	struct object_id oid;
-	int pos = index_name_pos(&the_index, path, len);
+	int pos = index_name_pos(the_repository->index, path, len);
 
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (S_ISGITLINK(ce->ce_mode)) {
 
 			/* Do nothing to the index if there is no HEAD! */
@@ -360,8 +360,8 @@ static int process_directory(const char *path, int len, struct stat *st)
 
 	/* Inexact match: is there perhaps a subdirectory match? */
 	pos = -pos-1;
-	while (pos < the_index.cache_nr) {
-		const struct cache_entry *ce = the_index.cache[pos++];
+	while (pos < the_repository->index->cache_nr) {
+		const struct cache_entry *ce = the_repository->index->cache[pos++];
 
 		if (strncmp(ce->name, path, len))
 			break;
@@ -391,8 +391,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
 	if (has_symlink_leading_path(path, len))
 		return error("'%s' is beyond a symbolic link", path);
 
-	pos = index_name_pos(&the_index, path, len);
-	ce = pos < 0 ? NULL : the_index.cache[pos];
+	pos = index_name_pos(the_repository->index, path, len);
+	ce = pos < 0 ? NULL : the_repository->index->cache[pos];
 	if (ce && ce_skip_worktree(ce)) {
 		/*
 		 * working directory version is assumed "good"
@@ -400,7 +400,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
 		 * On the other hand, removing it from index should work
 		 */
 		if (!ignore_skip_worktree_entries && allow_remove &&
-		    remove_file_from_index(&the_index, path))
+		    remove_file_from_index(the_repository->index, path))
 			return error("%s: cannot remove from the index", path);
 		return 0;
 	}
@@ -428,7 +428,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
 		return error("Invalid path '%s'", path);
 
 	len = strlen(path);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, path, len);
@@ -439,7 +439,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
 		ce->ce_flags |= CE_VALID;
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option))
+	if (add_index_entry(the_repository->index, ce, option))
 		return error("%s: cannot add to the index - missing --add option?",
 			     path);
 	report("add '%s'", path);
@@ -451,11 +451,11 @@ static void chmod_path(char flip, const char *path)
 	int pos;
 	struct cache_entry *ce;
 
-	pos = index_name_pos(&the_index, path, strlen(path));
+	pos = index_name_pos(the_repository->index, path, strlen(path));
 	if (pos < 0)
 		goto fail;
-	ce = the_index.cache[pos];
-	if (chmod_index_entry(&the_index, ce, flip) < 0)
+	ce = the_repository->index->cache[pos];
+	if (chmod_index_entry(the_repository->index, ce, flip) < 0)
 		goto fail;
 
 	report("chmod %cx '%s'", flip, path);
@@ -498,7 +498,7 @@ static void update_one(const char *path)
 	}
 
 	if (force_remove) {
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die("git update-index: unable to remove %s", path);
 		report("remove '%s'", path);
 		return;
@@ -581,7 +581,7 @@ static void read_index_info(int nul_term_line)
 
 		if (!mode) {
 			/* mode == 0 means there is no such path -- remove */
-			if (remove_file_from_index(&the_index, path_name))
+			if (remove_file_from_index(the_repository->index, path_name))
 				die("git update-index: unable to remove %s",
 				    ptr);
 		}
@@ -622,12 +622,12 @@ static struct cache_entry *read_one_ent(const char *which,
 			error("%s: not in %s branch.", path, which);
 		return NULL;
 	}
-	if (!the_index.sparse_index && mode == S_IFDIR) {
+	if (!the_repository->index->sparse_index && mode == S_IFDIR) {
 		if (which)
 			error("%s: not a blob in %s branch.", path, which);
 		return NULL;
 	}
-	ce = make_empty_cache_entry(&the_index, namelen);
+	ce = make_empty_cache_entry(the_repository->index, namelen);
 
 	oidcpy(&ce->oid, &oid);
 	memcpy(ce->name, path, namelen);
@@ -642,12 +642,12 @@ static int unresolve_one(const char *path)
 	struct string_list_item *item;
 	int res = 0;
 
-	if (!the_index.resolve_undo)
+	if (!the_repository->index->resolve_undo)
 		return res;
-	item = string_list_lookup(the_index.resolve_undo, path);
+	item = string_list_lookup(the_repository->index->resolve_undo, path);
 	if (!item)
 		return res; /* no resolve-undo record for the path */
-	res = unmerge_index_entry(&the_index, path, item->util, 0);
+	res = unmerge_index_entry(the_repository->index, path, item->util, 0);
 	FREE_AND_NULL(item->util);
 	return res;
 }
@@ -688,13 +688,13 @@ static int do_reupdate(const char **paths,
 		 */
 		has_head = 0;
  redo:
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		struct cache_entry *old = NULL;
 		int save_nr;
 		char *path;
 
-		if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+		if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL))
 			continue;
 		if (has_head)
 			old = read_one_ent(NULL, &head_oid,
@@ -710,7 +710,7 @@ static int do_reupdate(const char **paths,
 		 * to process each path individually
 		 */
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
-			ensure_full_index(&the_index);
+			ensure_full_index(the_repository->index);
 			goto redo;
 		}
 
@@ -718,12 +718,12 @@ static int do_reupdate(const char **paths,
 		 * path anymore, in which case, under 'allow_remove',
 		 * or worse yet 'allow_replace', active_nr may decrease.
 		 */
-		save_nr = the_index.cache_nr;
+		save_nr = the_repository->index->cache_nr;
 		path = xstrdup(ce->name);
 		update_one(path);
 		free(path);
 		discard_cache_entry(old);
-		if (save_nr != the_index.cache_nr)
+		if (save_nr != the_repository->index->cache_nr)
 			goto redo;
 	}
 	clear_pathspec(&pathspec);
@@ -739,9 +739,9 @@ static int refresh(struct refresh_params *o, unsigned int flag)
 {
 	setup_work_tree();
 	repo_read_index(the_repository);
-	*o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+	*o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
 					NULL, NULL);
-	if (has_racy_timestamp(&the_index)) {
+	if (has_racy_timestamp(the_repository->index)) {
 		/*
 		 * Even if nothing else has changed, updating the file
 		 * increases the chance that racy timestamps become
@@ -750,7 +750,7 @@ static int refresh(struct refresh_params *o, unsigned int flag)
 		 * refresh_index() as these are no actual errors.
 		 * cmd_status() does the same.
 		 */
-		the_index.cache_changed |= SOMETHING_CHANGED;
+		the_repository->index->cache_changed |= SOMETHING_CHANGED;
 	}
 	return 0;
 }
@@ -787,7 +787,7 @@ static int resolve_undo_clear_callback(const struct option *opt UNUSED,
 {
 	BUG_ON_OPT_NEG(unset);
 	BUG_ON_OPT_ARG(arg);
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	return 0;
 }
 
@@ -888,7 +888,7 @@ static enum parse_opt_result unresolve_callback(
 	*has_errors = do_unresolve(ctx->argc, ctx->argv,
 				prefix, prefix ? strlen(prefix) : 0);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -909,7 +909,7 @@ static enum parse_opt_result reupdate_callback(
 	setup_work_tree();
 	*has_errors = do_reupdate(ctx->argv + 1, prefix);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -1056,7 +1056,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 	if (entries < 0)
 		die("cache corrupted");
 
-	the_index.updated_skipworktree = 1;
+	the_repository->index->updated_skipworktree = 1;
 
 	/*
 	 * Custom copy of parse_options() because we want to handle
@@ -1111,18 +1111,18 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
 	if (preferred_index_format) {
 		if (preferred_index_format < 0) {
-			printf(_("%d\n"), the_index.version);
+			printf(_("%d\n"), the_repository->index->version);
 		} else if (preferred_index_format < INDEX_FORMAT_LB ||
 			   INDEX_FORMAT_UB < preferred_index_format) {
 			die("index-version %d not in range: %d..%d",
 			    preferred_index_format,
 			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 		} else {
-			if (the_index.version != preferred_index_format)
-				the_index.cache_changed |= SOMETHING_CHANGED;
+			if (the_repository->index->version != preferred_index_format)
+				the_repository->index->cache_changed |= SOMETHING_CHANGED;
 			report(_("index-version: was %d, set to %d"),
-			       the_index.version, preferred_index_format);
-			the_index.version = preferred_index_format;
+			       the_repository->index->version, preferred_index_format);
+			the_repository->index->version = preferred_index_format;
 		}
 	}
 
@@ -1159,16 +1159,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.splitIndex is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable split index"));
-		if (the_index.split_index)
-			the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+		if (the_repository->index->split_index)
+			the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED;
 		else
-			add_split_index(&the_index);
+			add_split_index(the_repository->index);
 	} else if (!split_index) {
 		if (git_config_get_split_index() == 1)
 			warning(_("core.splitIndex is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable split index"));
-		remove_split_index(&the_index);
+		remove_split_index(the_repository->index);
 	}
 
 	prepare_repo_settings(r);
@@ -1180,7 +1180,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.untrackedCache is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable the untracked cache"));
-		remove_untracked_cache(&the_index);
+		remove_untracked_cache(the_repository->index);
 		report(_("Untracked cache disabled"));
 		break;
 	case UC_TEST:
@@ -1192,7 +1192,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.untrackedCache is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable the untracked cache"));
-		add_untracked_cache(&the_index);
+		add_untracked_cache(the_repository->index);
 		report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
 		break;
 	default:
@@ -1222,7 +1222,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 				"set it if you really want to "
 				"enable fsmonitor"));
 		}
-		add_fsmonitor(&the_index);
+		add_fsmonitor(the_repository->index);
 		report(_("fsmonitor enabled"));
 	} else if (!fsmonitor) {
 		enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
@@ -1230,17 +1230,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 			warning(_("core.fsmonitor is set; "
 				"remove it if you really want to "
 				"disable fsmonitor"));
-		remove_fsmonitor(&the_index);
+		remove_fsmonitor(the_repository->index);
 		report(_("fsmonitor disabled"));
 	}
 
-	if (the_index.cache_changed || force_write) {
+	if (the_repository->index->cache_changed || force_write) {
 		if (newfd < 0) {
 			if (refresh_args.flags & REFRESH_QUIET)
 				exit(128);
 			unable_to_lock_die(get_index_file(), lock_error);
 		}
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 66e83d0ecb..8c75b4609b 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -44,8 +44,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
-	ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
-				  tree_prefix);
+	ret = write_index_as_tree(&oid, the_repository->index, get_index_file(),
+				  flags, tree_prefix);
 	switch (ret) {
 	case 0:
 		printf("%s\n", oid_to_hex(&oid));
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 1%]

* Re: [PATCH v2] pack-bitmap: gracefully handle missing BTMP chunks
  2024-04-15  6:41  3% ` [PATCH v2] " Patrick Steinhardt
@ 2024-04-15  8:51  0%   ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-15  8:51 UTC (permalink / raw)
  To: git; +Cc: Taylor Blau, Junio C Hamano

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

On Mon, Apr 15, 2024 at 08:41:25AM +0200, Patrick Steinhardt wrote:
> In 0fea6b73f1 (Merge branch 'tb/multi-pack-verbatim-reuse', 2024-01-12)
> we have introduced multi-pack verbatim reuse of objects. This series has
> introduced a new BTMP chunk, which encodes information about bitmapped
> objects in the multi-pack index. Starting with dab60934e3 (pack-bitmap:
> pass `bitmapped_pack` struct to pack-reuse functions, 2023-12-14) we use
> this information to figure out objects which we can reuse from each of
> the packfiles.
> 
> One thing that we glossed over though is backwards compatibility with
> repositories that do not yet have BTMP chunks in their multi-pack index.
> In that case, `nth_bitmapped_pack()` would return an error, which causes
> us to emit a warning followed by another error message. These warnings
> are visible to users that fetch from a repository:
> 
> ```
> $ git fetch
> ...
> remote: error: MIDX does not contain the BTMP chunk
> remote: warning: unable to load pack: 'pack-f6bb7bd71d345ea9fe604b60cab9ba9ece54ffbe.idx', disabling pack-reuse
> remote: Enumerating objects: 40, done.
> remote: Counting objects: 100% (40/40), done.
> remote: Compressing objects: 100% (39/39), done.
> remote: Total 40 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
> ...
> ```
> 
> While the fetch succeeds the user is left wondering what they did wrong.
> Furthermore, as visible both from the warning and from the reuse stats,
> pack-reuse is completely disabled in such repositories.
> 
> What is quite interesting is that this issue can even be triggered in
> case `pack.allowPackReuse=single` is set, which is the default value.
> One could have expected that in this case we fall back to the old logic,
> which is to use the preferred packfile without consulting BTMP chunks at
> all. But either we fail with the above error in case they are missing,
> or we use the first pack in the multi-pack-index. The former case
> disables pack-reuse altogether, whereas the latter case may result in
> reusing objects from a suboptimal packfile.
> 
> Fix this issue by partially reverting the logic back to what we had
> before this patch series landed. Namely, in the case where we have no
> BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
> preferred pack instead of consulting the BTMP chunks.
> 
> Helped-by: Taylor Blau <me@ttaylorr.com>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>

Junio, it would be great if we could still land this fix in Git v2.45
given that it is addressing a regression in Git v2.44. This of course
assumes that the current version of this patch looks good to Taylor.

Patrick

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

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] Documentation: fix linkgit reference
  2024-04-14 22:08  4% [PATCH] Documentation: fix linkgit reference Yehezkel Bernat via GitGitGadget
@ 2024-04-15  7:22  0% ` Patrick Steinhardt
  2024-04-15 18:02  0%   ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-15  7:22 UTC (permalink / raw)
  To: Yehezkel Bernat via GitGitGadget; +Cc: git, Yehezkel Bernat

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

On Sun, Apr 14, 2024 at 10:08:02PM +0000, Yehezkel Bernat via GitGitGadget wrote:
> From: Yehezkel Bernat <yehezkelshb@gmail.com>
> 
> In git-replay documentation, linkgit to git-rev-parse is missing the man
> section which breaks its rendering
> 
> Add section number as done in other references to this command

Nit: sentences should end with a dot.

> Signed-off-by: Yehezkel Bernat <YehezkelShB@gmail.com>

Other than that this patch looks good to me, thanks!

Patrick

> ---
>     Documentation: fix linkgit reference
> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1706%2FYehezkelShB%2Fyb%2Ffix-linkgit-reference-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1706/YehezkelShB/yb/fix-linkgit-reference-v1
> Pull-Request: https://github.com/git/git/pull/1706
> 
>  Documentation/git-replay.txt | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
> index f6c269c62d5..8f3300c683a 100644
> --- a/Documentation/git-replay.txt
> +++ b/Documentation/git-replay.txt
> @@ -46,7 +46,7 @@ the new commits (in other words, this mimics a cherry-pick operation).
>  	Range of commits to replay. More than one <revision-range> can
>  	be passed, but in `--advance <branch>` mode, they should have
>  	a single tip, so that it's clear where <branch> should point
> -	to. See "Specifying Ranges" in linkgit:git-rev-parse and the
> +	to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
>  	"Commit Limiting" options below.
>  
>  include::rev-list-options.txt[]
> 
> base-commit: 8f7582d995682f785e80e344197cc715e6bc7d8e
> -- 
> gitgitgadget
> 

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

^ permalink raw reply	[relevance 0%]

* [PATCH v2] pack-bitmap: gracefully handle missing BTMP chunks
  2024-04-09  5:59  3% [PATCH] pack-bitmap: gracefully handle missing BTMP chunks Patrick Steinhardt
  2024-04-10 15:02  3% ` Taylor Blau
@ 2024-04-15  6:41  3% ` Patrick Steinhardt
  2024-04-15  8:51  0%   ` Patrick Steinhardt
  1 sibling, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-15  6:41 UTC (permalink / raw)
  To: git; +Cc: Taylor Blau

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

In 0fea6b73f1 (Merge branch 'tb/multi-pack-verbatim-reuse', 2024-01-12)
we have introduced multi-pack verbatim reuse of objects. This series has
introduced a new BTMP chunk, which encodes information about bitmapped
objects in the multi-pack index. Starting with dab60934e3 (pack-bitmap:
pass `bitmapped_pack` struct to pack-reuse functions, 2023-12-14) we use
this information to figure out objects which we can reuse from each of
the packfiles.

One thing that we glossed over though is backwards compatibility with
repositories that do not yet have BTMP chunks in their multi-pack index.
In that case, `nth_bitmapped_pack()` would return an error, which causes
us to emit a warning followed by another error message. These warnings
are visible to users that fetch from a repository:

```
$ git fetch
...
remote: error: MIDX does not contain the BTMP chunk
remote: warning: unable to load pack: 'pack-f6bb7bd71d345ea9fe604b60cab9ba9ece54ffbe.idx', disabling pack-reuse
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 40 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
...
```

While the fetch succeeds the user is left wondering what they did wrong.
Furthermore, as visible both from the warning and from the reuse stats,
pack-reuse is completely disabled in such repositories.

What is quite interesting is that this issue can even be triggered in
case `pack.allowPackReuse=single` is set, which is the default value.
One could have expected that in this case we fall back to the old logic,
which is to use the preferred packfile without consulting BTMP chunks at
all. But either we fail with the above error in case they are missing,
or we use the first pack in the multi-pack-index. The former case
disables pack-reuse altogether, whereas the latter case may result in
reusing objects from a suboptimal packfile.

Fix this issue by partially reverting the logic back to what we had
before this patch series landed. Namely, in the case where we have no
BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
preferred pack instead of consulting the BTMP chunks.

Helped-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
Range-diff against v1:
1:  5933a302b5 ! 1:  a8251f8278 pack-bitmap: gracefully handle missing BTMP chunks
    @@ Commit message
         BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
         preferred pack instead of consulting the BTMP chunks.
     
    +    Helped-by: Taylor Blau <me@ttaylorr.com>
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## midx.c ##
    -@@ midx.c: static int write_midx_internal(const char *object_dir,
    - 		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
    - 			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
    - 			  write_midx_revindex);
    --		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
    --			  bitmapped_packs_concat_len,
    --			  write_midx_bitmapped_packs);
    -+		if (git_env_bool("GIT_TEST_MIDX_WRITE_BTMP", 1))
    -+			add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
    -+				  bitmapped_packs_concat_len,
    -+				  write_midx_bitmapped_packs);
    - 	}
    +@@ midx.c: struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
      
    - 	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
    + 	pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets,
    + 		   &m->chunk_large_offsets_len);
    +-	pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
    +-		   (const unsigned char **)&m->chunk_bitmapped_packs,
    +-		   &m->chunk_bitmapped_packs_len);
    ++	if (git_env_bool("GIT_TEST_MIDX_READ_BTMP", 1))
    ++		pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
    ++			   (const unsigned char **)&m->chunk_bitmapped_packs,
    ++			   &m->chunk_bitmapped_packs_len);
    + 
    + 	if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1))
    + 		pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex,
     
      ## pack-bitmap.c ##
     @@ pack-bitmap.c: void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
    @@ pack-bitmap.c: void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitm
      	load_reverse_index(r, bitmap_git);
      
     -	if (bitmap_is_midx(bitmap_git)) {
    -+	if (bitmap_is_midx(bitmap_git) &&
    -+	    (!multi_pack_reuse || !bitmap_git->midx->chunk_bitmapped_packs)) {
    -+		uint32_t preferred_pack_pos;
    -+		struct packed_git *pack;
    -+
    -+		if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
    -+			warning(_("unable to compute preferred pack, disabling pack-reuse"));
    -+			return;
    -+		}
    -+
    -+		pack = bitmap_git->midx->packs[preferred_pack_pos];
    ++	if (!bitmap_is_midx(bitmap_git) || !bitmap_git->midx->chunk_bitmapped_packs)
    ++		multi_pack_reuse = 0;
     +
    -+		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
    -+		packs[packs_nr].p = pack;
    -+		packs[packs_nr].bitmap_nr = pack->num_objects;
    -+		packs[packs_nr].bitmap_pos = 0;
    -+
    -+		objects_nr = packs[packs_nr++].bitmap_nr;
    -+	} else if (bitmap_is_midx(bitmap_git)) {
    ++	if (multi_pack_reuse) {
      		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
      			struct bitmapped_pack pack;
      			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
    @@ pack-bitmap.c: void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitm
      		}
      
      		QSORT(packs, packs_nr, bitmapped_pack_cmp);
    + 	} else {
    +-		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
    ++		struct packed_git *pack;
    ++
    ++		if (bitmap_is_midx(bitmap_git)) {
    ++			uint32_t preferred_pack_pos;
    ++
    ++			if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
    ++				warning(_("unable to compute preferred pack, disabling pack-reuse"));
    ++				return;
    ++			}
    + 
    +-		packs[packs_nr].p = bitmap_git->pack;
    +-		packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
    ++			pack = bitmap_git->midx->packs[preferred_pack_pos];
    ++		} else {
    ++			pack = bitmap_git->pack;
    ++		}
    ++
    ++		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
    ++		packs[packs_nr].p = pack;
    ++		packs[packs_nr].bitmap_nr = pack->num_objects;
    + 		packs[packs_nr].bitmap_pos = 0;
    + 
    + 		objects_nr = packs[packs_nr++].bitmap_nr;
     
      ## t/t5326-multi-pack-bitmaps.sh ##
     @@ t/t5326-multi-pack-bitmaps.sh: test_expect_success 'corrupt MIDX with bitmap causes fallback' '
    @@ t/t5326-multi-pack-bitmaps.sh: test_expect_success 'corrupt MIDX with bitmap cau
      
     +for allow_pack_reuse in single multi
     +do
    -+	test_expect_success "MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
    ++	test_expect_success "reading MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
     +		test_when_finished "rm -rf midx-without-btmp" &&
     +		git init midx-without-btmp &&
     +		(
     +			cd midx-without-btmp &&
     +			test_commit initial &&
     +
    -+			# Write a multi-pack index that does have a bitmap, but
    -+			# no BTMP chunk. Such MIDX files would not be generated
    -+			# by modern Git anymore, but they were generated by
    -+			# older Git versions.
    -+			GIT_TEST_MIDX_WRITE_BTMP=false \
    -+				git repack -Adbl --write-bitmap-index --write-midx &&
    -+			git -c pack.allowPackReuse=$allow_pack_reuse \
    ++			git repack -Adbl --write-bitmap-index --write-midx &&
    ++			GIT_TEST_MIDX_READ_BTMP=false git -c pack.allowPackReuse=$allow_pack_reuse \
     +				pack-objects --all --use-bitmap-index --stdout </dev/null >/dev/null 2>err &&
     +			test_must_be_empty err
     +		)

 midx.c                        |  7 +++---
 pack-bitmap.c                 | 41 ++++++++++++++++++-----------------
 t/t5326-multi-pack-bitmaps.sh | 17 +++++++++++++++
 3 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/midx.c b/midx.c
index ae3b49166c..6f07de3688 100644
--- a/midx.c
+++ b/midx.c
@@ -170,9 +170,10 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
 
 	pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets,
 		   &m->chunk_large_offsets_len);
-	pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
-		   (const unsigned char **)&m->chunk_bitmapped_packs,
-		   &m->chunk_bitmapped_packs_len);
+	if (git_env_bool("GIT_TEST_MIDX_READ_BTMP", 1))
+		pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+			   (const unsigned char **)&m->chunk_bitmapped_packs,
+			   &m->chunk_bitmapped_packs_len);
 
 	if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1))
 		pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex,
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 2baeabacee..35c5ef9d3c 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -2049,7 +2049,10 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 
 	load_reverse_index(r, bitmap_git);
 
-	if (bitmap_is_midx(bitmap_git)) {
+	if (!bitmap_is_midx(bitmap_git) || !bitmap_git->midx->chunk_bitmapped_packs)
+		multi_pack_reuse = 0;
+
+	if (multi_pack_reuse) {
 		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
 			struct bitmapped_pack pack;
 			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
@@ -2062,34 +2065,32 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 			if (!pack.bitmap_nr)
 				continue;
 
-			if (!multi_pack_reuse && pack.bitmap_pos) {
-				/*
-				 * If we're only reusing a single pack, skip
-				 * over any packs which are not positioned at
-				 * the beginning of the MIDX bitmap.
-				 *
-				 * This is consistent with the existing
-				 * single-pack reuse behavior, which only reuses
-				 * parts of the MIDX's preferred pack.
-				 */
-				continue;
-			}
-
 			ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
 			memcpy(&packs[packs_nr++], &pack, sizeof(pack));
 
 			objects_nr += pack.p->num_objects;
-
-			if (!multi_pack_reuse)
-				break;
 		}
 
 		QSORT(packs, packs_nr, bitmapped_pack_cmp);
 	} else {
+		struct packed_git *pack;
+
+		if (bitmap_is_midx(bitmap_git)) {
+			uint32_t preferred_pack_pos;
+
+			if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
+				warning(_("unable to compute preferred pack, disabling pack-reuse"));
+				return;
+			}
+
+			pack = bitmap_git->midx->packs[preferred_pack_pos];
+		} else {
+			pack = bitmap_git->pack;
+		}
+
 		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
-
-		packs[packs_nr].p = bitmap_git->pack;
-		packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
+		packs[packs_nr].p = pack;
+		packs[packs_nr].bitmap_nr = pack->num_objects;
 		packs[packs_nr].bitmap_pos = 0;
 
 		objects_nr = packs[packs_nr++].bitmap_nr;
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58709..5d7d321840 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -513,4 +513,21 @@ test_expect_success 'corrupt MIDX with bitmap causes fallback' '
 	)
 '
 
+for allow_pack_reuse in single multi
+do
+	test_expect_success "reading MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
+		test_when_finished "rm -rf midx-without-btmp" &&
+		git init midx-without-btmp &&
+		(
+			cd midx-without-btmp &&
+			test_commit initial &&
+
+			git repack -Adbl --write-bitmap-index --write-midx &&
+			GIT_TEST_MIDX_READ_BTMP=false git -c pack.allowPackReuse=$allow_pack_reuse \
+				pack-objects --all --use-bitmap-index --stdout </dev/null >/dev/null 2>err &&
+			test_must_be_empty err
+		)
+	'
+done
+
 test_done

base-commit: 19981daefd7c147444462739375462b49412ce33
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 3%]

* [PATCH] Documentation: fix linkgit reference
@ 2024-04-14 22:08  4% Yehezkel Bernat via GitGitGadget
  2024-04-15  7:22  0% ` Patrick Steinhardt
  0 siblings, 1 reply; 200+ results
From: Yehezkel Bernat via GitGitGadget @ 2024-04-14 22:08 UTC (permalink / raw)
  To: git; +Cc: Yehezkel Bernat, Yehezkel Bernat

From: Yehezkel Bernat <yehezkelshb@gmail.com>

In git-replay documentation, linkgit to git-rev-parse is missing the man
section which breaks its rendering

Add section number as done in other references to this command

Signed-off-by: Yehezkel Bernat <YehezkelShB@gmail.com>
---
    Documentation: fix linkgit reference

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1706%2FYehezkelShB%2Fyb%2Ffix-linkgit-reference-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1706/YehezkelShB/yb/fix-linkgit-reference-v1
Pull-Request: https://github.com/git/git/pull/1706

 Documentation/git-replay.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
index f6c269c62d5..8f3300c683a 100644
--- a/Documentation/git-replay.txt
+++ b/Documentation/git-replay.txt
@@ -46,7 +46,7 @@ the new commits (in other words, this mimics a cherry-pick operation).
 	Range of commits to replay. More than one <revision-range> can
 	be passed, but in `--advance <branch>` mode, they should have
 	a single tip, so that it's clear where <branch> should point
-	to. See "Specifying Ranges" in linkgit:git-rev-parse and the
+	to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
 	"Commit Limiting" options below.
 
 include::rev-list-options.txt[]

base-commit: 8f7582d995682f785e80e344197cc715e6bc7d8e
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* What's cooking in git.git (Apr 2024, #05; Fri, 12)
@ 2024-04-13  1:36  1% Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-13  1:36 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking in my tree.  Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and are candidate to be in a
future release).  Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all and may be annotated with an URL
to a message that raises issues but they are no means exhaustive.  A
topic without enough support may be discarded after a long period of
no activity (of course they can be resubmit when new interests
arise).

Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors.  Some
repositories have only a subset of branches.

With maint, master, next, seen, todo:

	git://git.kernel.org/pub/scm/git/git.git/
	git://repo.or.cz/alt-git.git/
	https://kernel.googlesource.com/pub/scm/git/git/
	https://github.com/git/git/
	https://gitlab.com/git-scm/git/

With all the integration branches and topics broken out:

	https://github.com/gitster/git/

Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):

	git://git.kernel.org/pub/scm/git/git-htmldocs.git/
	https://github.com/gitster/git-htmldocs.git/

Release tarballs are available at:

	https://www.kernel.org/pub/software/scm/git/

--------------------------------------------------
[Graduated to 'master']

* ds/typofix-core-config-doc (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 79496fcfc4)
 + config: fix some small capitalization issues, as spotted

 Typofix.
 source: <26135b06c48565ee8ac6dcfc1ef5431511e6202c.1711918168.git.dsimic@manjaro.org>


* jc/checkout-detach-wo-tracking-report (2024-03-30) 1 commit
  (merged to 'next' on 2024-04-04 at 161eca247d)
 + checkout: omit "tracking" information on a detached HEAD

 "git checkout/switch --detach foo", after switching to the detached
 HEAD state, gave the tracking information for the 'foo' branch,
 which was pointless.

 Tested-by: M Hickford <mirth.hickford@gmail.com>
 cf. <CAGJzqsmE9FDEBn=u3ge4LA3ha4fDbm4OWiuUbMaztwjELBd7ug@mail.gmail.com>
 source: <xmqqa5mfl7ud.fsf@gitster.g>


* jc/t2104-style-update (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-03 at 0449835479)
 + t2104: style fixes

 Coding style fixes.
 source: <xmqqmsqb4ngg.fsf@gitster.g>


* js/merge-tree-3-trees (2024-04-12) 1 commit
  (merged to 'next' on 2024-04-12 at d4235d1f47)
 + merge-tree: fix argument type of the `--merge-base` option

 Match the option argument type in the help text to the correct type
 updated by a recent series.
 source: <pull.1717.git.1712923841235.gitgitgadget@gmail.com>


* kn/clarify-update-ref-doc (2024-04-02) 2 commits
  (merged to 'next' on 2024-04-02 at d1b9c5aa67)
 + githooks: use {old,new}-oid instead of {old,new}-value
 + update-ref: use {old,new}-oid instead of {old,new}value

 Doc update, as a preparation to enhance "git update-ref --stdin".
 source: <20240402064915.191104-1-knayak@gitlab.com>


* ps/reftable-binsearch-updates (2024-04-03) 7 commits
  (merged to 'next' on 2024-04-04 at 40e6d5a36b)
 + reftable/block: avoid decoding keys when searching restart points
 + reftable/record: extract function to decode key lengths
 + reftable/block: fix error handling when searching restart points
 + reftable/block: refactor binary search over restart points
 + reftable/refname: refactor binary search over refnames
 + reftable/basics: improve `binsearch()` test
 + reftable/basics: fix return type of `binsearch()` to be `size_t`

 Reftable code clean-up and some bugfixes.
 source: <cover.1712123093.git.ps@pks.im>


* rs/imap-send-use-xsnprintf (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 789ad853e1)
 + imap-send: use xsnprintf to format command

 Code clean-up and duplicate reduction.
 source: <f9ad9f41-5b9b-474e-9818-f91fc937daae@web.de>


* rs/mem-pool-size-t-safety (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 3517d48210)
 + mem-pool: use st_add() in mem_pool_strvfmt()

 size_t arithmetic safety.
 source: <bbe00b9e-64d8-4ec8-a2b9-2c6917c72dbd@web.de>


* rs/t-prio-queue-cleanup (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 7961c838ac)
 + t-prio-queue: simplify using compound literals

 t-prio-queue test has been cleaned up by using C99 compound
 literals; this is meant to also serve as a weather-balloon to smoke
 out folks with compilers who have trouble compiling code that uses
 the feature.
 source: <520da361-1b80-4ba3-87b2-86d6fdfc18b5@web.de>


* tb/midx-write (2024-04-01) 5 commits
  (merged to 'next' on 2024-04-05 at b4870116f7)
 + midx-write.c: use `--stdin-packs` when repacking
 + midx-write.c: check count of packs to repack after grouping
 + midx-write.c: factor out common want_included_pack() routine
 + midx-write: move writing-related functions from midx.c
 + Merge branch 'rs/midx-use-strvec-pushf' into tb/midx-write

 Code clean-up by splitting code responsible for writing midx files
 into its own file.
 source: <cover.1712006190.git.me@ttaylorr.com>


* vs/complete-with-set-u-fix (2024-04-01) 2 commits
  (merged to 'next' on 2024-04-02 at d8f6a511e8)
 + completion: protect prompt against unset SHOWUPSTREAM in nounset mode
 + completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode

 Another "set -u" fix for the bash prompt (in contrib/) script.
 source: <20240401190751.8676-1-ville.skytta@iki.fi>

--------------------------------------------------
[New Topics]

* dd/t9604-use-posix-timezones (2024-04-10) 1 commit
 - t9604: Fix test for musl libc and new Debian

 The cvsimport tests required that the platform understands
 traditional timezone notations like CST6CDT, which has been
 updated to work on those systems as long as they understand
 POSIX notation with explicit tz transition dates.

 Will merge to 'next'.
 source: <20240410032812.30476-1-congdanhqx@gmail.com>


* jc/t2104-style-fixes (2024-04-09) 1 commit
  (merged to 'next' on 2024-04-11 at 7678ec509b)
 + t2104: style fixes

 Test style fixes.

 Will merge to 'master'.
 source: <xmqqmsqb4ngg.fsf@gitster.g>


* kn/update-ref-symrefs (2024-04-12) 8 commits
 - SQUASH???
 - refs: support symrefs in 'reference-transaction' hook
 - update-ref: add support for symref-update
 - update-ref: add support for symref-create
 - files-backend: extract out `create_symref_lock`
 - update-ref: add support for symref-delete
 - update-ref: add support for symref-verify
 - refs: accept symref values in `ref_transaction[_add]_update`

 source: <20240412095908.1134387-1-knayak@gitlab.com>


* pf/commitish-committish (2024-04-11) 1 commit
  (merged to 'next' on 2024-04-12 at 7ef816cb64)
 + typo: replace 'commitish' with 'committish'

 Spellfix.

 Will merge to 'master'.
 source: <20240407212111.55362-1-Pi.L.D.Fisher@gmail.com>


* pw/t3428-cleanup (2024-04-09) 3 commits
  (merged to 'next' on 2024-04-11 at 3c40516874)
 + t3428: restore coverage for "apply" backend
 + t3428: use test_commit_message
 + t3428: modernize test setup

 Test cleanup.

 Will merge to 'master'.
 source: <pull.1713.git.1712676444.gitgitgadget@gmail.com>


* ta/fast-import-parse-path-fix (2024-04-12) 8 commits
 - fast-import: make comments more precise
 - fast-import: forbid escaped NUL in paths
 - fast-import: document C-style escapes for paths
 - fast-import: improve documentation for path quoting
 - fast-import: remove dead strbuf
 - fast-import: allow unquoted empty path for root
 - fast-import: directly use strbufs for paths
 - fast-import: tighten path unquoting

 The way "git fast-import" handles paths described in its input has
 been tightened up and more clearly documented.

 Will merge to 'next'?
 source: <cover.1712907684.git.thalia@archibald.dev>


* xx/rfc2822-date-format-in-doc (2024-04-12) 1 commit
 - Documentation: fix typos describing date format

 Docfix.

 Will merge to 'next'?
 source: <pull.1716.git.1712911876943.gitgitgadget@gmail.com>

--------------------------------------------------
[Cooking]

* rs/date-mode-pass-by-value (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at cc3c17d31c)
 + date: make DATE_MODE thread-safe

 The codepaths that reach date_mode_from_type() have been updated to
 pass "struct date_mode" by value to make them thread safe.

 Will merge to 'master'.
 source: <c6cb255a-72f0-4ac2-81a2-1d8e95570a81@web.de>


* rs/usage-fallback-to-show-message-format (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at 9a34aed4d5)
 + usage: report vsnprintf(3) failure

 vreportf(), which is usede by error() and friends, has been taught
 to give the error message printf-format string when its vsnprintf()
 call fails, instead of showing nothing useful to identify the
 nature of the error.

 Will merge to 'master'.
 source: <3da13298-b6a6-4391-b8e8-5dae9a28b860@web.de>


* jc/local-extern-shell-rules (2024-04-05) 8 commits
  (merged to 'next' on 2024-04-10 at d3a13273e7)
 + t1016: local VAR="VAL" fix
 + t0610: local VAR="VAL" fix
 + t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
 + t: local VAR="VAL" (quote ${magic-reference})
 + t: local VAR="VAL" (quote command substitution)
 + t: local VAR="VAL" (quote positional parameters)
 + CodingGuidelines: quote assigned value in 'local var=$val'
 + CodingGuidelines: describe "export VAR=VAL" rule

 Document and apply workaround for a buggy version of dash that
 mishandles "local var=val" construct.

 Will merge to 'master'.
 source: <20240406000902.3082301-1-gitster@pobox.com>


* jc/unleak-core-excludesfile (2024-04-08) 1 commit
  (merged to 'next' on 2024-04-10 at ffb0c01871)
 + config: do not leak excludes_file

 The variable that holds the value read from the core.excludefile
 configuration variable used to leak, which has been corrected.

 Will merge to 'master'.
 source: <xmqqttkeicov.fsf@gitster.g>


* la/doc-use-of-contacts-when-contributing (2024-04-12) 8 commits
 - SubmittingPatches: demonstrate using git-contacts with git-send-email
 - SubmittingPatches: add heading for format-patch and send-email
 - SubmittingPatches: dedupe discussion of security patches
 - SubmittingPatches: discuss reviewers first
 - SubmittingPatches: quote commands
 - SubmittingPatches: mention GitGitGadget
 - SubmittingPatches: clarify 'git-contacts' location
 - MyFirstContribution: mention contrib/contacts/git-contacts

 Advertise "git contacts", a tool for newcomers to find people to
 ask review for their patches, a bit more in our developer
 documentation.

 source: <pull.1704.v4.git.1712878339.gitgitgadget@gmail.com>


* ps/ci-test-with-jgit (2024-04-12) 13 commits
 - t0612: add tests to exercise Git/JGit reftable compatibility
 - t0610: fix non-portable variable assignment
 - t06xx: always execute backend-specific tests
 - ci: install JGit dependency
 - ci: make Perforce binaries executable for all users
 - ci: merge scripts which install dependencies
 - ci: fix setup of custom path for GitLab CI
 - ci: merge custom PATH directories
 - ci: convert "install-dependencies.sh" to use "/bin/sh"
 - ci: drop duplicate package installation for "linux-gcc-default"
 - ci: skip sudo when we are already root
 - ci: expose distro name in dockerized GitHub jobs
 - ci: rename "runs_on_pool" to "distro"

 Tests to ensure interoperability between reftable written by jgit
 and our code have been added and enabled in CI.

 source: <cover.1712896868.git.ps@pks.im>


* pw/rebase-i-error-message (2024-04-08) 2 commits
 - rebase -i: improve error message when picking merge
 - rebase -i: pass struct replay_opts to parse_insn_line()

 When the user adds to "git rebase -i" instruction to "pick" a merge
 commit, the error experience is not pleasant.  Such an error is now
 caught earlier in the process that parses the todo list.

 Comments?
 source: <pull.1672.v2.git.1712585787.gitgitgadget@gmail.com>


* tb/make-indent-conditional-with-non-spaces (2024-04-08) 2 commits
  (merged to 'next' on 2024-04-10 at 98aa239dc3)
 + Makefile(s): do not enforce "all indents must be done with tab"
 + Makefile(s): avoid recipe prefix in conditional statements

 Adjust to an upcoming changes to GNU make that breaks our Makefiles.

 Will merge to 'master'.
 source: <9d14c08ca6cc06cdf8fb4ba33d2470053dca3966.1712591504.git.me@ttaylorr.com>


* ps/t0610-umask-fix (2024-04-09) 2 commits
  (merged to 'next' on 2024-04-10 at 659a29b138)
 + t0610: execute git-pack-refs(1) with specified umask
 + t0610: make `--shared=` tests reusable

 The "shared repository" test in the t0610 reftable test failed
 under restrictive umask setting (e.g. 007), which has been
 corrected.

 Will merge to 'master'.
 source: <cover.1712656576.git.ps@pks.im>


* ma/win32-unix-domain-socket (2024-04-03) 1 commit
  (merged to 'next' on 2024-04-09 at b98021a65c)
 + Win32: detect unix socket support at runtime

 Windows binary used to decide the use of unix-domain socket at
 build time, but it learned to make the decision at runtime instead.

 Will merge to 'master'.
 source: <pull.1708.git.1712158923106.gitgitgadget@gmail.com>


* ps/reftable-write-optim (2024-04-08) 11 commits
 - reftable/block: reuse compressed array
 - reftable/block: reuse zstream when writing log blocks
 - reftable/writer: reset `last_key` instead of releasing it
 - reftable/writer: unify releasing memory
 - reftable/writer: refactorings for `writer_flush_nonempty_block()`
 - reftable/writer: refactorings for `writer_add_record()`
 - refs/reftable: don't recompute committer ident
 - reftable: remove name checks
 - refs/reftable: skip duplicate name checks
 - refs/reftable: perform explicit D/F check when writing symrefs
 - refs/reftable: fix D/F conflict error message on ref copy

 Code to write out reftable has seen some optimization and
 simplification.
 source: <cover.1712578837.git.ps@pks.im>


* ds/send-email-per-message-block (2024-04-10) 2 commits
 - send-email: make it easy to discern the messages for each patch
 - send-email: move newline characters out of a few translatable strings

 "git send-email" learned to separate its reports on each message it
 sends out with an extra blank line in between.

 Comments?
 source: <cover.1712732383.git.dsimic@manjaro.org>


* ds/fetch-config-parse-microfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 585dcadd63)
 + fetch: return when parsing submodule.recurse

 A config parser callback function fell through instead of returning
 after recognising and processing a variable, wasting cycles, which
 has been corrected.

 Will merge to 'master'.
 source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>


* rs/apply-lift-path-length-limit (2024-04-05) 2 commits
  (merged to 'next' on 2024-04-09 at 3270d194fd)
 + path: remove mksnpath()
 + apply: avoid fixed-size buffer in create_one_file()

 "git apply" has been updated to lift the hardcoded pathname length
 limit, which in turn allowed a mksnpath() function that is no
 longer used.

 Will merge to 'master'.
 source: <df774306-f29b-4a75-a282-59db89812b9a@web.de>


* rs/apply-reject-fd-leakfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 11efa0543c)
 + apply: don't leak fd on fdopen() error

 A file descriptor leak in an error codepath, used when "git apply
 --reject" fails to create the *.rej file, has been corrected.

 Will merge to 'master'.
 source: <5ba55ee4-94c7-4094-a744-584fc623b391@web.de>


* ba/osxkeychain-updates (2024-04-01) 4 commits
  (merged to 'next' on 2024-04-10 at 1e7d925a43)
 + osxkeychain: store new attributes
 + osxkeychain: erase matching passwords only
 + osxkeychain: erase all matching credentials
 + osxkeychain: replace deprecated SecKeychain API

 Update osxkeychain backend with features required for the recent
 credential subsystem.

 Will merge to 'master'.
 source: <pull.1667.git.1708212896.gitgitgadget@gmail.com>


* jk/libcurl-8.7-regression-workaround (2024-04-05) 3 commits
  (merged to 'next' on 2024-04-10 at 3b76577bfc)
 + remote-curl: add Transfer-Encoding header only for older curl
 + INSTALL: bump libcurl version to 7.21.3
 + http: reset POSTFIELDSIZE when clearing curl handle

 Fix was added to work around a regression in libcURL 8.7.0 (which has
 already been fixed in their tip of the tree).

 Will merge to 'master'.
 source: <20240402200254.GA874754@coredump.intra.peff.net>


* tb/t7700-fixup (2024-04-03) 1 commit
  (merged to 'next' on 2024-04-10 at ff1f877ef7)
 + t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `

 Test fix.

 Will merge to 'master'.
 source: <7e8d435d58eea19d2aae0be366720f5956d29a5d.1712075189.git.me@ttaylorr.com>


* gt/add-u-commit-i-pathspec-check (2024-04-03) 3 commits
  (merged to 'next' on 2024-04-09 at 1a0c757907)
 + builtin/add: error out when passing untracked path with -u
 + builtin/commit: error out when passing untracked path with -i
 + revision: optionally record matches with pathspec elements

 "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
 diagnose a pathspec element that did not match any files in certain
 situations, unlike "git add <pathspec>" did.

 Will merge to 'master'.
 source: <20240402213640.139682-2-shyamthakkar001@gmail.com>


* jt/reftable-geometric-compaction (2024-04-08) 4 commits
  (merged to 'next' on 2024-04-10 at 7e868a831c)
 + reftable/stack: use geometric table compaction
 + reftable/stack: add env to disable autocompaction
 + reftable/stack: expose option to disable auto-compaction
 + Merge branch 'ps/pack-refs-auto' into jt/reftable-geometric-compaction

 The strategy to compact multiple tables of reftables after many
 operations accumulate many entries has been improved to avoid
 accumulating too many tables uncollected.

 Will merge to 'master'.
 source: <pull.1683.v6.git.1712593016.gitgitgadget@gmail.com>


* ew/khash-to-khashl (2024-03-28) 3 commits
 - khashl: fix ensemble lookups on empty table
 - treewide: switch to khashl for memory savings
 - list-objects-filter: use kh_size API

 The hashtable library "khash.h" has been replaced with "khashl.h"
 that has better memory usage characteristics.

 Needs review.
 source: <20240328101356.300374-1-e@80x24.org>


* ps/reftable-block-iteration-optim (2024-03-27) 9 commits
 - reftable/block: reuse `zstream` state on inflation
 - reftable/block: open-code call to `uncompress2()`
 - reftable/block: reuse uncompressed blocks
 - reftable/reader: iterate to next block in place
 - reftable/block: move ownership of block reader into `struct table_iter`
 - reftable/block: introduce `block_reader_release()`
 - reftable/block: better grouping of functions
 - reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
 - reftable/block: rename `block_reader_start()`

 The code to iterate over reftable blocks has seen some optimization
 to reduce memory allocation and deallocation.

 Needs review.
 source: <cover.1711519925.git.ps@pks.im>


* bc/credential-scheme-enhancement (2024-03-27) 12 commits
 . credential: add support for multistage credential rounds
 . t5563: refactor for multi-stage authentication
 . docs: set a limit on credential line length
 . credential: enable state capability
 . credential: add an argument to keep state
 . http: add support for authtype and credential
 . docs: indicate new credential protocol fields
 . credential: gate new fields on capability
 . credential: add a field for pre-encoded credentials
 . http: use new headers for each object request
 . remote-curl: reset headers on new request
 . credential: add an authtype field

 The credential helper protocol, together with the HTTP layer, have
 been enhanced to support authentication schemes different from
 username & password pair, like Bearer and NTLM.

 Expecting a reroll.
 cf. <ZgSQ5o_KyqDaxz1m@tapette.crustytoothpaste.net>
 source: <20240324011301.1553072-1-sandals@crustytoothpaste.net>


* tb/pseudo-merge-reachability-bitmap (2024-03-20) 24 commits
 - t/perf: implement performace tests for pseudo-merge bitmaps
 - pseudo-merge: implement support for finding existing merges
 - ewah: `bitmap_equals_ewah()`
 - pack-bitmap: extra trace2 information
 - pack-bitmap.c: use pseudo-merges during traversal
 - t/test-lib-functions.sh: support `--date` in `test_commit_bulk()`
 - pack-bitmap: implement test helpers for pseudo-merge
 - ewah: implement `ewah_bitmap_popcount()`
 - pseudo-merge: implement support for reading pseudo-merge commits
 - pack-bitmap.c: read pseudo-merge extension
 - pseudo-merge: scaffolding for reads
 - pack-bitmap: extract `read_bitmap()` function
 - pack-bitmap-write.c: write pseudo-merge table
 - pack-bitmap-write.c: select pseudo-merge commits
 - pseudo-merge: implement support for selecting pseudo-merge commits
 - pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` public
 - pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`
 - pack-bitmap-write: support storing pseudo-merge commits
 - pseudo-merge.ch: initial commit
 - pack-bitmap: move some initialization to `bitmap_writer_init()`
 - pack-bitmap: drop unused `max_bitmaps` parameter
 - ewah: implement `ewah_bitmap_is_subset()`
 - config: repo_config_get_expiry()
 - Documentation/technical: describe pseudo-merge bitmaps format

 The pack-bitmap machinery learned to write pseudo-merge bitmaps,
 which act as imaginary octopus merges covering un-bitmapped
 reference tips. This enhances bitmap coverage, and thus,
 performance, for repositories with many references using bitmaps.

 Expecting a reroll.
 cf. <ZfyxCLpjbaScIdWA@nand.local>
 source: <cover.1710972293.git.me@ttaylorr.com>


* la/hide-trailer-info (2024-03-16) 7 commits
 - trailer: retire trailer_info_get() from API
 - trailer: make trailer_info struct private
 - trailer: make parse_trailers() return trailer_info pointer
 - interpret-trailers: access trailer_info with new helpers
 - sequencer: use the trailer iterator
 - trailer: teach iterator about non-trailer lines
 - Merge branch 'la/format-trailer-info' into la/hide-trailer-info
 (this branch uses la/format-trailer-info.)

 The trailer API has been reshuffled a bit.

 Needs review.
 source: <pull.1696.git.1710570428.gitgitgadget@gmail.com>


* ds/doc-config-reflow (2024-03-14) 1 commit
 - config.txt: perform some minor reformatting

 Reflow a paragraph in the documentation source without any effect
 to the formatted text.

 Will discard.
 source: <97bdaf075bf5a68554cca1731eca78aff2662907.1710444774.git.dsimic@manjaro.org>


* la/format-trailer-info (2024-03-15) 5 commits
 - trailer: finish formatting unification
 - trailer: begin formatting unification
 - format_trailer_info(): append newline for non-trailer lines
 - format_trailer_info(): drop redundant unfold_value()
 - format_trailer_info(): use trailer_item objects
 (this branch is used by la/hide-trailer-info.)

 The code to format trailers have been cleaned up.

 Comments?
 source: <pull.1694.git.1710485706.gitgitgadget@gmail.com>


* ie/config-includeif-hostname (2024-03-19) 2 commits
 - config: learn the "hostname:" includeIf condition
 - t: add a test helper for getting hostname

 The conditional inclusion mechanism for configuration files learned
 to switch on the hostname.

 Expecting a reroll.
 cf. <20240319210428.GC1159535@coredump.intra.peff.net>
 cf. <20240320001934.GA903718@coredump.intra.peff.net>
 source: <20240319183722.211300-1-ignacio@iencinas.com>


* js/build-fuzz-more-often (2024-04-11) 2 commits
 - fuzz: link fuzz programs with `make all` on Linux
 - ci: also define CXX environment variable

 In addition to building the objects needed, try to link the objects
 that are used in fuzzer tests, to make sure at least they build
 without bitrot, in Linux CI runs.

 Expecting a hopefully minor and final reroll.
 cf. <20240412042247.GA1077925@coredump.intra.peff.net>
 source: <cover.1712858920.git.steadmon@google.com>


* sj/userdiff-c-sharp (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-10 at 56aaf254a7)
 + userdiff: better method/property matching for C#

 The userdiff patterns for C# has been updated.

 Acked-by: Johannes Sixt <j6t@kdbg.org>
 cf. <c2154457-3f2f-496e-9b8b-c8ea7257027b@kdbg.org>

 Will merge to 'master'.
 source: <pull.1682.v5.git.git.1712180564927.gitgitgadget@gmail.com>


* cw/git-std-lib (2024-02-28) 4 commits
 - SQUASH??? get rid of apparent debugging crufts
 - test-stdlib: show that git-std-lib is independent
 - git-std-lib: introduce Git Standard Library
 - pager: include stdint.h because uintmax_t is used

 Split libgit.a out to a separate git-std-lib tor easier reuse.

 Expecting a reroll.
 source: <cover.1696021277.git.jonathantanmy@google.com>


* js/cmake-with-test-tool (2024-02-23) 2 commits
 - cmake: let `test-tool` run the unit tests, too
 - Merge branch 'js/unit-test-suite-runner' into js/cmake-with-test-tool
 (this branch uses js/unit-test-suite-runner.)

 "test-tool" is now built in CMake build to also run the unit tests.

 May want to roll it into the base topic.
 source: <pull.1666.git.1708038924522.gitgitgadget@gmail.com>


* js/unit-test-suite-runner (2024-02-23) 8 commits
 - ci: use test-tool as unit test runner on Windows
 - t/Makefile: run unit tests alongside shell tests
 - unit tests: add rule for running with test-tool
 - test-tool run-command testsuite: support unit tests
 - test-tool run-command testsuite: remove hardcoded filter
 - test-tool run-command testsuite: get shell from env
 - t0080: turn t-basic unit test into a helper
 - Merge branch 'jk/unit-tests-buildfix' into js/unit-test-suite-runner
 (this branch is used by js/cmake-with-test-tool.)

 The "test-tool" has been taught to run testsuite tests in parallel,
 bypassing the need to use the "prove" tool.

 Needs review.
 source: <cover.1708728717.git.steadmon@google.com>


* bk/complete-dirname-for-am-and-format-patch (2024-01-12) 1 commit
 - completion: dir-type optargs for am, format-patch

 Command line completion support (in contrib/) has been
 updated for a few commands to complete directory names where a
 directory name is expected.

 Expecting a reroll.
 cf. <40c3a824-a961-490b-94d4-4eb23c8f713d@gmail.com>
 cf. <6683f24e-7e56-489d-be2d-8afe1fc38d2b@gmail.com>
 source: <d37781c3-6af2-409b-95a8-660a9b92d20b@smtp-relay.sendinblue.com>


* bk/complete-send-email (2024-01-12) 1 commit
 - completion: don't complete revs when --no-format-patch

 Command line completion support (in contrib/) has been taught to
 avoid offering revision names as candidates to "git send-email" when
 the command is used to send pre-generated files.

 Expecting a reroll.
 cf. <CAC4O8c88Z3ZqxH2VVaNPpEGB3moL5dJcg3cOWuLWwQ_hLrJMtA@mail.gmail.com>
 source: <a718b5ee-afb0-44bd-a299-3208fac43506@smtp-relay.sendinblue.com>


* tb/path-filter-fix (2024-01-31) 16 commits
 - bloom: introduce `deinit_bloom_filters()`
 - commit-graph: reuse existing Bloom filters where possible
 - object.h: fix mis-aligned flag bits table
 - commit-graph: new Bloom filter version that fixes murmur3
 - commit-graph: unconditionally load Bloom filters
 - bloom: prepare to discard incompatible Bloom filters
 - bloom: annotate filters with hash version
 - repo-settings: introduce commitgraph.changedPathsVersion
 - t4216: test changed path filters with high bit paths
 - t/helper/test-read-graph: implement `bloom-filters` mode
 - bloom.h: make `load_bloom_filter_from_graph()` public
 - t/helper/test-read-graph.c: extract `dump_graph_info()`
 - gitformat-commit-graph: describe version 2 of BDAT
 - commit-graph: ensure Bloom filters are read with consistent settings
 - revision.c: consult Bloom filters for root commits
 - t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`

 The Bloom filter used for path limited history traversal was broken
 on systems whose "char" is unsigned; update the implementation and
 bump the format version to 2.

 Waiting for a final ack?
 cf. <ZcFjkfbsBfk7JQIH@nand.local>
 source: <cover.1706741516.git.me@ttaylorr.com>


* jc/rerere-cleanup (2023-08-25) 4 commits
 - rerere: modernize use of empty strbuf
 - rerere: try_merge() should use LL_MERGE_ERROR when it means an error
 - rerere: fix comment on handle_file() helper
 - rerere: simplify check_one_conflict() helper function

 Code clean-up.

 Not ready to be reviewed yet.
 source: <20230824205456.1231371-1-gitster@pobox.com>


^ permalink raw reply	[relevance 1%]

* Re: [PATCH] merge-tree: fix argument type of the `--merge-base` option
  2024-04-12 12:10  4% [PATCH] merge-tree: fix argument type of the `--merge-base` option Johannes Schindelin via GitGitGadget
@ 2024-04-12 16:09  0% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-12 16:09 UTC (permalink / raw)
  To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin

"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Johannes Schindelin <johannes.schindelin@gmx.de>
>
> In 5f43cf5b2e4 (merge-tree: accept 3 trees as arguments, 2024-01-28), I
> taught `git merge-tree` to perform three-way merges on trees. This
> commit even changed the manual page to state that the `--merge-base`
> option takes a tree-ish rather than requiring a commit.
>
> But I forgot to adjust the in-program help text. This patch fixes that.

Thanks for being careful---better late than never ;-)

Will queue and fast-track, as I do not see much chance of regression
coming from here.

>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>     merge-tree: adjust argument type of the --merge-base option
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1717%2Fdscho%2Fmerge-tree-document-merge-base-treeish-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1717/dscho/merge-tree-document-merge-base-treeish-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1717
>
>  builtin/merge-tree.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
> index 3492a575a6c..60eaf0ca9f1 100644
> --- a/builtin/merge-tree.c
> +++ b/builtin/merge-tree.c
> @@ -563,7 +563,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
>  			   PARSE_OPT_NONEG),
>  		OPT_STRING(0, "merge-base",
>  			   &merge_base,
> -			   N_("commit"),
> +			   N_("tree-ish"),
>  			   N_("specify a merge-base for the merge")),
>  		OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
>  			N_("option for selected merge strategy")),
>
> base-commit: 342990c7aaef5ac645e89101cb84569caf64baf4


^ permalink raw reply	[relevance 0%]

* [PATCH] merge-tree: fix argument type of the `--merge-base` option
@ 2024-04-12 12:10  4% Johannes Schindelin via GitGitGadget
  2024-04-12 16:09  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Johannes Schindelin via GitGitGadget @ 2024-04-12 12:10 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin

From: Johannes Schindelin <johannes.schindelin@gmx.de>

In 5f43cf5b2e4 (merge-tree: accept 3 trees as arguments, 2024-01-28), I
taught `git merge-tree` to perform three-way merges on trees. This
commit even changed the manual page to state that the `--merge-base`
option takes a tree-ish rather than requiring a commit.

But I forgot to adjust the in-program help text. This patch fixes that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
    merge-tree: adjust argument type of the --merge-base option

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1717%2Fdscho%2Fmerge-tree-document-merge-base-treeish-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1717/dscho/merge-tree-document-merge-base-treeish-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1717

 builtin/merge-tree.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 3492a575a6c..60eaf0ca9f1 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -563,7 +563,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 			   PARSE_OPT_NONEG),
 		OPT_STRING(0, "merge-base",
 			   &merge_base,
-			   N_("commit"),
+			   N_("tree-ish"),
 			   N_("specify a merge-base for the merge")),
 		OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
 			N_("option for selected merge strategy")),

base-commit: 342990c7aaef5ac645e89101cb84569caf64baf4
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [PATCH v2 3/7] update-ref: add support for symref-delete
  2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
  2024-04-12  9:59  8%   ` [PATCH v2 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
@ 2024-04-12  9:59 12%   ` Karthik Nayak
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
  2 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-12  9:59 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

Similar to the previous commit, add 'symref-delete' to allow deletions
of symbolic refs in a transaction via the 'git-update-ref' command. The
'symref-delete' command can when given with an <old-ref>, deletes the
provided <ref> only when it points to <old-ref>.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-update-ref.txt |  6 +++++
 builtin/fetch.c                  |  2 +-
 builtin/receive-pack.c           |  3 ++-
 builtin/update-ref.c             | 34 +++++++++++++++++++++++++++-
 refs.c                           | 16 +++++++++----
 refs.h                           |  4 +++-
 refs/files-backend.c             |  2 +-
 refs/reftable-backend.c          |  2 +-
 t/t1400-update-ref.sh            | 39 ++++++++++++++++++++++++++++++++
 9 files changed, 97 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 749aaa7892..ef22a1a2f4 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -65,6 +65,7 @@ performs all modifications together.  Specify commands of the form:
 	create SP <ref> SP <new-oid> LF
 	delete SP <ref> [SP <old-oid>] LF
 	verify SP <ref> [SP <old-oid>] LF
+	symref-delete SP <ref> [SP <old-ref>] LF
 	symref-verify SP <ref> [SP <old-ref>] LF
 	option SP <opt> LF
 	start LF
@@ -87,6 +88,7 @@ quoting:
 	create SP <ref> NUL <new-oid> NUL
 	delete SP <ref> NUL [<old-oid>] NUL
 	verify SP <ref> NUL [<old-oid>] NUL
+	symref-delete SP <ref> [NUL <old-ref>] NUL
 	symref-verify SP <ref> [NUL <old-ref>] NUL
 	option SP <opt> NUL
 	start NUL
@@ -119,6 +121,10 @@ verify::
 	Verify <ref> against <old-oid> but do not change it.  If
 	<old-oid> is zero or missing, the ref must not exist.
 
+symref-delete::
+	Delete <ref> after verifying it exists with <old-ref>, if
+	given.
+
 symref-verify::
 	Verify symbolic <ref> against <old-ref> but do not change it.
 	If <old-ref> is missing, the ref must not exist.  Can only be
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 66840b7c5b..d02592efca 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1383,7 +1383,7 @@ static int prune_refs(struct display_state *display_state,
 		if (transaction) {
 			for (ref = stale_refs; ref; ref = ref->next) {
 				result = ref_transaction_delete(transaction, ref->name, NULL, 0,
-								"fetch: prune", &err);
+								NULL, "fetch: prune", &err);
 				if (result)
 					goto cleanup;
 			}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ebea1747cb..6b728baaac 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_delete(transaction,
 					   namespaced_name,
 					   old_oid,
-					   0, "push", &err)) {
+					   0, NULL,
+					   "push", &err)) {
 			rp_error("%s", err.buf);
 			ret = "failed to delete";
 		} else {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 4ae6bdcb12..3be9ae0d00 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -294,7 +294,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 
 	if (ref_transaction_delete(transaction, refname,
 				   have_old ? &old_oid : NULL,
-				   update_flags, msg, &err))
+				   update_flags, NULL, msg, &err))
 		die("%s", err.buf);
 
 	update_flags = default_flags;
@@ -302,6 +302,37 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
 	strbuf_release(&err);
 }
 
+static void parse_cmd_symref_delete(struct ref_transaction *transaction,
+				    const char *next, const char *end)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *refname, *old_ref;
+
+	if (!(update_flags & REF_NO_DEREF))
+                die("symref-delete: cannot operate with deref mode");
+
+	refname = parse_refname(&next);
+	if (!refname)
+		die("symref-delete: missing <ref>");
+
+        old_ref = parse_next_refname(&next);
+	if (old_ref && read_ref(old_ref, NULL))
+		die("symref-delete %s: invalid <old-ref>", refname);
+
+	if (*next != line_termination)
+		die("symref-delete %s: extra input: %s", refname, next);
+
+	if (ref_transaction_delete(transaction, refname, NULL,
+				   update_flags | REF_SYMREF_UPDATE,
+				   old_ref, msg, &err))
+		die("%s", err.buf);
+
+	update_flags = default_flags;
+	free(refname);
+	free(old_ref);
+	strbuf_release(&err);
+}
+
 static void parse_cmd_verify(struct ref_transaction *transaction,
 			     const char *next, const char *end)
 {
@@ -445,6 +476,7 @@ static const struct parse_cmd {
 	{ "create",        parse_cmd_create,        2, UPDATE_REFS_OPEN },
 	{ "delete",        parse_cmd_delete,        2, UPDATE_REFS_OPEN },
 	{ "verify",        parse_cmd_verify,        2, UPDATE_REFS_OPEN },
+	{ "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN },
 	{ "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN },
 	{ "option",        parse_cmd_option,        1, UPDATE_REFS_OPEN },
 	{ "start",         parse_cmd_start,         0, UPDATE_REFS_STARTED },
diff --git a/refs.c b/refs.c
index 124b294c9f..6d98d9652d 100644
--- a/refs.c
+++ b/refs.c
@@ -981,7 +981,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
 	transaction = ref_store_transaction_begin(refs, &err);
 	if (!transaction ||
 	    ref_transaction_delete(transaction, refname, old_oid,
-				   flags, msg, &err) ||
+				   flags, NULL, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ref_transaction_free(transaction);
@@ -1220,6 +1220,7 @@ void ref_transaction_free(struct ref_transaction *transaction)
 	for (i = 0; i < transaction->nr; i++) {
 		free(transaction->updates[i]->msg);
 		free((void *)transaction->updates[i]->old_ref);
+		free((void *)transaction->updates[i]->new_ref);
 		free(transaction->updates[i]);
 	}
 	free(transaction->updates);
@@ -1252,6 +1253,8 @@ struct ref_update *ref_transaction_add_update(
 	if (update->flags & REF_SYMREF_UPDATE) {
 		if (old_ref)
 			update->old_ref = xstrdup(old_ref);
+		if (new_ref)
+			update->new_ref = xstrdup(new_ref);
 	} else {
 		if (flags & REF_HAVE_NEW)
 			oidcpy(&update->new_oid, new_oid);
@@ -1317,14 +1320,17 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_ref,
+			   const char *msg,
 			   struct strbuf *err)
 {
-	if (old_oid && is_null_oid(old_oid))
+	if (!(flags & REF_SYMREF_UPDATE) && old_oid &&
+	    is_null_oid(old_oid))
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      NULL, NULL, flags,
+				      NULL, old_ref, flags,
 				      msg, err);
 }
 
@@ -2748,7 +2754,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
 
 	for_each_string_list_item(item, refnames) {
 		ret = ref_transaction_delete(transaction, item->string,
-					     NULL, flags, msg, &err);
+					     NULL, flags, 0, msg, &err);
 		if (ret) {
 			warning(_("could not delete reference %s: %s"),
 				item->string, err.buf);
diff --git a/refs.h b/refs.h
index a988e672ff..60e6a21a31 100644
--- a/refs.h
+++ b/refs.h
@@ -758,7 +758,9 @@ int ref_transaction_create(struct ref_transaction *transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *old_oid,
-			   unsigned int flags, const char *msg,
+			   unsigned int flags,
+			   const char *old_ref,
+			   const char *msg,
 			   struct strbuf *err);
 
 /*
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8421530bde..f74ea308b5 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2501,7 +2501,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
 	files_assert_main_repository(refs, "lock_ref_for_update");
 
-	if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
+	if ((update->flags & REF_HAVE_NEW) && null_new_value(update))
 		update->flags |= REF_DELETING;
 
 	if (head_ref) {
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 7a03922c7b..935bf407df 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1122,7 +1122,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
 		if (u->flags & REF_LOG_ONLY)
 			continue;
 
-		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+		if (u->flags & REF_HAVE_NEW && null_new_value(u)) {
 			struct reftable_ref_record ref = {
 				.refname = (char *)u->refname,
 				.update_index = ts,
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index d8ffda4096..cf01c5d867 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1715,6 +1715,45 @@ test_expect_success "stdin ${type} symref-verify fails for mistaken null value"
 	test_cmp expect actual
 '
 
+test_expect_success "stdin ${type} symref-delete fails without --no-deref" '
+	git symbolic-ref refs/heads/symref $a &&
+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
+	test_must_fail git update-ref --stdin ${type} <stdin 2>err &&
+	grep "fatal: symref-delete: cannot operate with deref mode" err
+'
+
+test_expect_success "stdin ${type} fails symref-delete with no ref" '
+	create_stdin_buf ${type} "symref-delete " &&
+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+	grep "fatal: symref-delete: missing <ref>" err
+'
+
+test_expect_success "stdin ${type} fails symref-delete with too many arguments" '
+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" "$a" &&
+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+	if test "$type" = "-z"
+	then
+		grep "fatal: unknown command: $a" err
+	else
+		grep "fatal: symref-delete refs/heads/symref: extra input:  $a" err
+	fi
+'
+
+test_expect_success "stdin ${type} symref-delete ref fails with wrong old value" '
+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$m" &&
+	test_must_fail git update-ref --stdin ${type} --no-deref <stdin 2>err &&
+	grep "fatal: cannot lock ref '"'"'refs/heads/symref'"'"'" err &&
+	git symbolic-ref refs/heads/symref >expect &&
+	echo $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success "stdin ${type} symref-delete ref works with right old value" '
+	create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" &&
+	git update-ref --stdin ${type} --no-deref <stdin &&
+	test_must_fail git rev-parse --verify -q $b
+'
+
 done
 
 test_done
-- 
2.43.GIT



^ permalink raw reply related	[relevance 12%]

* [PATCH v2 1/7] refs: accept symref values in `ref_transaction[_add]_update`
  2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
@ 2024-04-12  9:59  8%   ` Karthik Nayak
  2024-04-12  9:59 12%   ` [PATCH v2 3/7] update-ref: add support for symref-delete Karthik Nayak
  2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
  2 siblings, 0 replies; 200+ results
From: Karthik Nayak @ 2024-04-12  9:59 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The `ref_transaction[_add]_update` functions obtain ref information and
flags to create a `ref_update` and add it to the transaction at hand.

To extend symref support in transactions, we need to also accept the
old and new ref values and process it. In this commit, let's add the
required paramaters to the function and modify all call sites.

The two paramaters added are `new_ref` and `old_ref`. The `new_ref` is
used to denote what the reference should point to when the transaction
is applied. Some functions allow this parameter to be NULL, meaning that
the reference is not changed, or `""`, meaning that the reference should
be deleted.

The `old_ref` denotes the value of that the reference must have before
the update. Some functions allow this parameter to be NULL, meaning that
the old value of the reference is not checked, or `""`, meaning that the
reference must not exist before the update. A copy of this value is made
in the transaction.

The handling logic of these parameters will be added in consequent
commits as we implement symref-{create, update, delete, verify}.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  5 +++--
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  1 +
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  1 +
 builtin/update-ref.c    |  1 +
 refs.c                  | 15 ++++++++++-----
 refs.h                  |  9 ++++++++-
 refs/files-backend.c    | 12 ++++++------
 refs/refs-internal.h    | 14 ++++++++++++++
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  9 +++++----
 walker.c                |  2 +-
 14 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/branch.c b/branch.c
index 621019fcf4..3ebcfdca65 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 782bda007c..6a0b39de32 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1675,7 +1675,8 @@ static void dump_tags(void)
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d860db..66840b7c5b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -668,7 +668,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 56d8a77ed7..ebea1747cb 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..7690687b0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b4..40a65fdebc 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc46d..21fdbf6ac8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..967c81167e 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,6 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_ref, const char *old_ref,
 		const char *msg)
 {
 	struct ref_update *update;
@@ -1253,6 +1254,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_ref, const char *old_ref,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err)
 {
@@ -1280,7 +1282,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, new_ref, old_ref, msg);
 	return 0;
 }
 
@@ -1295,7 +1297,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1311,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      NULL, NULL, flags,
+				      msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1325,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
 				      NULL, old_oid,
+				      NULL, NULL,
 				      flags, NULL, err);
 }
 
@@ -1335,8 +1340,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
-	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+	    ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+				   flags, msg, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index d278775e08..645fe9fdb8 100644
--- a/refs.h
+++ b/refs.h
@@ -696,13 +696,19 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  */
 #define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
 
+/*
+ * The reference update is considered to be done on a symbolic reference. This
+ * ensures that we verify, delete, create and update the ref correspondingly.
+ */
+#define REF_SYMREF_UPDATE (1 << 12)
+
 /*
  * Bitmask of all of the flags that are allowed to be passed in to
  * ref_transaction_update() and friends:
  */
 #define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS                                  \
 	(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
-	 REF_SKIP_REFNAME_VERIFICATION)
+	 REF_SKIP_REFNAME_VERIFICATION | REF_SYMREF_UPDATE )
 
 /*
  * Add a reference update to transaction. `new_oid` is the value that
@@ -722,6 +728,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const char *refname,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
+			   const char *new_ref, const char *old_ref,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index a098d14ea0..e4d0aa3d41 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
+					   iter->oid, NULL, NULL, NULL,
 					   REF_NO_DEREF, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
@@ -2309,7 +2309,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2372,7 +2372,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			NULL, NULL, update->msg);
 
 	new_update->parent_update = update;
 
@@ -2763,7 +2763,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL, NULL);
 		}
 	}
 
@@ -3048,7 +3048,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..4c5fe02687 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,19 @@ struct ref_update {
 	 */
 	struct object_id old_oid;
 
+	/*
+	 * If (flags & REF_SYMREF_UPDATE), set the reference to this
+	 * value (or delete it, if `new_ref` is an empty string).
+	 */
+	const char *new_ref;
+
+	/*
+	 * If (type & REF_SYMREF_UPDATE), check that the reference
+	 * previously had this value (or didn't previously exist,
+	 * if `old_ref` is an empty string).
+	 */
+	const char *old_ref;
+
 	/*
 	 * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
 	 * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
@@ -173,6 +186,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
+		const char *new_ref, const char *old_ref,
 		const char *msg);
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1cda48c504..6104471199 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -829,7 +829,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 2c19846385..af1b25692b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -616,7 +616,7 @@ static int fast_forward_to(struct repository *r,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
-				   null_oid() : from,
+				   null_oid() : from, NULL, NULL,
 				   0, sb.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
@@ -1248,7 +1248,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   NULL, NULL, 0, sb.buf, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3764,8 +3764,9 @@ static int do_label(struct repository *r, const char *name, int len)
 	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
-	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+	} else if (ref_transaction_update(transaction, ref_name.buf,
+					  &head_oid, NULL, NULL, NULL,
+					  0, msg.buf, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index c0fd632d92..1b3df43906 100644
--- a/walker.c
+++ b/walker.c
@@ -324,7 +324,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		strbuf_reset(&refname);
 		strbuf_addf(&refname, "refs/%s", write_ref[i]);
 		if (ref_transaction_update(transaction, refname.buf,
-					   oids + i, NULL, 0,
+					   oids + i, NULL, NULL, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
 					   &err)) {
 			error("%s", err.buf);
-- 
2.43.GIT



^ permalink raw reply related	[relevance 8%]

* [PATCH v2 0/7] update-ref: add symref oriented commands
  2024-03-30 22:46  2% [PATCH 0/8] update-ref: add support for update-symref option Karthik Nayak
  2024-03-30 22:46  8% ` [PATCH 4/8] refs: accept symref in `ref_transaction_add_update` Karthik Nayak
@ 2024-04-12  9:59  2% ` Karthik Nayak
  2024-04-12  9:59  8%   ` [PATCH v2 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
                     ` (2 more replies)
  1 sibling, 3 replies; 200+ results
From: Karthik Nayak @ 2024-04-12  9:59 UTC (permalink / raw)
  To: karthik.188; +Cc: chris.torek, git, gitster, ps

From: Karthik Nayak <karthik.188@gmail.com>

The 'git-update-ref(1)' command allows transactional reference updates.
But currently only supports regular reference updates. Meaning, if one
wants to update HEAD (symbolic ref) in a transaction, there is no tool
to do so.

One option to obtain transactional updates for the HEAD ref is to
manually create the HEAD.lock file and commit. This is intrusive, where
the user needs to mimic internal git behavior. Also, this only works
when using the files backend.

At GitLab, we've been using the manual process till date, to allow users
to set and change their default branch. But with the introduction of
reftables as a reference backend, this becomes a necessity to be solved
within git.

This patch series goes about introducing a set of commands
symref-{create,verify,delete,update} to work with symrefs complimenting
the existing commands for the regular refs within 'git-update-ref(1)'.

The 'symref-verify' command can be used to verify if a symref exists and
its existing value.

The 'symref-create' command can be used to create a new symref.

The 'symref-delete' command can be used to delete an existing symref while
optionally checking its existing value.

The 'symref-update' command can be used to update a symref, create a symref,
delete a symref or even convert an existing regular ref to a symref. Wherein
like the regular 'update' command, the zero OID can be used to create/delete
a symref.

V1 of the patch series can be found here:
https://lore.kernel.org/git/20240330224623.579457-1-knayak@gitlab.com/

I'm not adding a range diff here, cause I redid the whole series, things which
have changed:
1. The earlier series simply propagated a 'symref_target' and only supported the
'symref-update' command, without checks for existing values and such. Now we
support the entire fleet of commands with support for checking old_values.
2. The flow is now changedc to send an old_ref, new_ref pair in supplement to
the existing old_oid, new_oid pair to the reference backends. This allows the
backends to simply do a combination of changes based on what values are set.
This allows us to do symref-update's where we change a regular ref to a symref
while also validating its old OID.
3. I added a lot more tests to cover reflog checks and also '-z' input.
4. The entered <old-ref> and <new-ref> values are checked within update-ref, to
ensure we don't create dangling refs. This could be extended in the future with
a flag, maybe "REF_ALLOW_DANGLING_SYMREF" and a corresponding option within
update-ref.
5. Removed some commits where reftable backend code was reused for symref creation.
This actually caused issues since the reused code also created a reflog along with
the symref but we should defer reflog creations only after all ref creations have
taken place. There is a bit of DRY here, but I think overall its still much cleaner.

Thanks all for the review and discussion in the previous version. Patrick, I did
incorporate the changes you suggested, but I just noticed that I didn't reply to
your emails.

Karthik Nayak (7):
  refs: accept symref values in `ref_transaction[_add]_update`
  update-ref: add support for symref-verify
  update-ref: add support for symref-delete
  files-backend: extract out `create_symref_lock`
  update-ref: add support for symref-create
  update-ref: add support for symref-update
  refs: support symrefs in 'reference-transaction' hook

 Documentation/git-update-ref.txt |  25 +++
 Documentation/githooks.txt       |  13 +-
 branch.c                         |   2 +-
 builtin/clone.c                  |   2 +-
 builtin/fast-import.c            |   5 +-
 builtin/fetch.c                  |   4 +-
 builtin/receive-pack.c           |   4 +-
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   1 +
 builtin/update-ref.c             | 202 +++++++++++++++++--
 refs.c                           |  74 +++++--
 refs.h                           |  15 +-
 refs/files-backend.c             | 143 +++++++++++---
 refs/refs-internal.h             |  21 ++
 refs/reftable-backend.c          |  51 ++++-
 sequencer.c                      |   9 +-
 t/t0600-reffiles-backend.sh      |  32 ++++
 t/t1400-update-ref.sh            | 320 ++++++++++++++++++++++++++++++-
 t/t1416-ref-transaction-hooks.sh |  41 ++++
 walker.c                         |   2 +-
 20 files changed, 886 insertions(+), 82 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 2%]

* [PATCH] Documentation: fix typos describing date format
@ 2024-04-12  8:51  4% blanet via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: blanet via GitGitGadget @ 2024-04-12  8:51 UTC (permalink / raw)
  To: git; +Cc: blanet, Xing Xin

From: Xing Xin <xingxin.xx@bytedance.com>

This commit corrects a typographical error found in both
date-formats.txt and git-fast-import.txt documentation, where the term
`email format` was mistakenly used instead of `date format`.

Signed-off-by: Xing Xin <xingxin.xx@bytedance.com>
---
    Documentation: fix typos describing date format

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1716%2Fblanet%2Fxx%2Fdocumentation-typofix-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1716/blanet/xx/documentation-typofix-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1716

 Documentation/date-formats.txt    | 2 +-
 Documentation/git-fast-import.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
index 67645cae64f..e24517c496f 100644
--- a/Documentation/date-formats.txt
+++ b/Documentation/date-formats.txt
@@ -11,7 +11,7 @@ Git internal format::
 	For example CET (which is 1 hour ahead of UTC) is `+0100`.
 
 RFC 2822::
-	The standard email format as described by RFC 2822, for example
+	The standard date format as described by RFC 2822, for example
 	`Thu, 07 Apr 2005 22:13:13 +0200`.
 
 ISO 8601::
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index b2607366b91..0ccede255ea 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -303,7 +303,7 @@ and some sanity checks on the numeric values may also be performed.
 	with e.g. bogus timezone values.
 
 `rfc2822`::
-	This is the standard email format as described by RFC 2822.
+	This is the standard date format as described by RFC 2822.
 +
 An example value is ``Tue Feb 6 11:22:18 2007 -0500''.  The Git
 parser is accurate, but a little on the lenient side.  It is the

base-commit: 436d4e5b14df49870a897f64fe92c0ddc7017e4c
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [PATCH v4 0/8] docs: recommend using contrib/contacts/git-contacts
  2024-04-09 21:56  3%   ` [PATCH v3 " Linus Arver via GitGitGadget
@ 2024-04-11 23:32  2%     ` Linus Arver via GitGitGadget
  2024-04-16 23:01  2%       ` [PATCH v5 " Linus Arver via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-11 23:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Patrick Steinhardt, Matthieu Moy, Eric Sunshine,
	Kipras Melnikovas, Linus Arver

Make git-contacts more prominent in our docs.


Notable changes in v4
=====================

 * Avoid using "should" for guidance around using "git-contacts"
 * Clarify where to find the "git-contacts" script (because it's not a
   default builtin command)


Notable changes in v3
=====================

 * Refer to GitGitGadget via a link to MyFirstContribution (instead of
   sending readers to GGG's homepage directly)
 * Soften the advice for using git-contacts


Notable changes in v2
=====================

 * Improve existing mention of git-contacts in SubmittingPatches (instead of
   adding a separate, entirely new paragraph)
 * Add example usage of integrating git-contacts with git-send-email with
   the latter's --cc-cmd flag.
 * Various smaller fixes to SubmittingPatches

Linus Arver (8):
  MyFirstContribution: mention contrib/contacts/git-contacts
  SubmittingPatches: clarify 'git-contacts' location
  SubmittingPatches: mention GitGitGadget
  SubmittingPatches: quote commands
  SubmittingPatches: discuss reviewers first
  SubmittingPatches: dedupe discussion of security patches
  SubmittingPatches: add heading for format-patch and send-email
  SubmittingPatches: demonstrate using git-contacts with git-send-email

 Documentation/MyFirstContribution.txt | 12 +++++
 Documentation/SubmittingPatches       | 75 ++++++++++++++++-----------
 2 files changed, 57 insertions(+), 30 deletions(-)


base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

Range-diff vs v3:

 1:  1f65dc5ba3d ! 1:  ad469e4e6db MyFirstContribution: mention contrib/contacts/git-contacts
     @@ Commit message
          git-contacts helper, 2013-07-21), we don't mention it in our
          introductory docs. Do so now.
      
     +    Helped-by: Junio C Hamano <gitster@pobox.com>
          Signed-off-by: Linus Arver <linusa@google.com>
      
       ## Documentation/MyFirstContribution.txt ##
     @@ Documentation/MyFirstContribution.txt: $ git send-email --to=target@example.com
       NOTE: Check `git help send-email` for some other options which you may find
       valuable, such as changing the Reply-to address or adding more CC and BCC lines.
       
     -+NOTE: If you're not sure who to CC, use `contrib/contacts/git-contacts` to get a
     -+list of reviewers you should include in the CC list. In addition, you can do
     -+`git send-email --cc-cmd='git contacts' feature/*.patch` to automatically pass
     -+this list of emails to `send-email`.
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     ++part of the core `git` binary and must be called separately. Consult your +
     ++package manager to determine where it is located. For example&#44; on Ubuntu-based +
     ++systems it could be installed under +
     ++`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     ++with `perl ...` if it does not have the executable bit set.]
     ++
     ++NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
     ++list potential reviewers. In addition, you can do `git send-email
     ++--cc-cmd='/path/to/git-contacts' feature/*.patch`{contrib-scripts} to
     ++automatically pass this list of emails to `send-email`.
      +
       NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
       please don't send your patchset from the tutorial to the real mailing list! For
 2:  6e8b1b50ac5 ! 2:  c43de19d867 SubmittingPatches: make 'git contacts' grep-friendly
     @@ Metadata
      Author: Linus Arver <linusa@google.com>
      
       ## Commit message ##
     -    SubmittingPatches: make 'git contacts' grep-friendly
     +    SubmittingPatches: clarify 'git-contacts' location
      
     -    Avoid splitting up the command over two lines. This way, a command like
     +    Use a dash ("git-contacts", not "git contacts") because the script
     +    is not a core builtin command that is compiled into the `git` binary.
     +    This also puts the script on one line, which should make it easier to
     +    grep for with a loose search query, such as
      
              $ git grep git.contacts Documentation
      
     -    will return a positive hit for this location.
     +    . Also add a footnote to describe where the script could actually be
     +    located, to help readers who may not be familiar with such "contrib"
     +    scripts (and how they are not accessible with the usual "git
     +    <subcommand>" syntax).
      
          Signed-off-by: Linus Arver <linusa@google.com>
      
       ## Documentation/SubmittingPatches ##
     -@@ Documentation/SubmittingPatches: mentioned below, but should instead be sent privately to the Git
     +@@ Documentation/SubmittingPatches: security relevant should not be submitted to the public mailing list
     + mentioned below, but should instead be sent privately to the Git
       Security mailing list{security-ml-ref}.
       
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     ++part of the core `git` binary and must be called separately. Consult your +
     ++package manager to determine where it is located. For example&#44; on Ubuntu-based +
     ++systems it could be installed under +
     ++`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     ++with `perl ...` if it does not have the executable bit set.]
     ++
       Send your patch with "To:" set to the mailing list, with "cc:" listing
      -people who are involved in the area you are touching (the `git
      -contacts` command in `contrib/contacts/` can help to
     -+people who are involved in the area you are touching (the `git contacts`
     -+command in `contrib/contacts/` can help to
     ++people who are involved in the area you are touching (the `git-contacts`
     ++script in `contrib/contacts/`{contrib-scripts} can help to
       identify them), to solicit comments and reviews.  Also, when you made
       trial merges of your topic to `next` and `seen`, you may have noticed
       work by others conflicting with your changes.  There is a good possibility
 3:  7f8fdc053f7 = 3:  cd941704176 SubmittingPatches: mention GitGitGadget
 4:  ef26bdb75c5 = 4:  44470a5d70e SubmittingPatches: quote commands
 5:  6f71b1731f2 ! 5:  15f9356ff97 SubmittingPatches: discuss reviewers first
     @@ Documentation/SubmittingPatches: letter.
      +mentioned below, but should instead be sent privately to the Git
      +Security mailing list{security-ml-ref}.
      +
     ++:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     ++part of the core `git` binary and must be called separately. Consult your +
     ++package manager to determine where it is located. For example&#44; on Ubuntu-based +
     ++systems it could be installed under +
     ++`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     ++with `perl ...` if it does not have the executable bit set.]
     ++
      +Send your patch with "To:" set to the mailing list, with "cc:" listing
     -+people who are involved in the area you are touching (the `git contacts`
     -+command in `contrib/contacts/` can help to
     ++people who are involved in the area you are touching (the `git-contacts`
     ++script in `contrib/contacts/`{contrib-scripts} can help to
      +identify them), to solicit comments and reviews.  Also, when you made
      +trial merges of your topic to `next` and `seen`, you may have noticed
      +work by others conflicting with your changes.  There is a good possibility
     @@ Documentation/SubmittingPatches: patch, format it as "multipart/signed", not a t
      -mentioned below, but should instead be sent privately to the Git
      -Security mailing list{security-ml-ref}.
      -
     +-:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     +-part of the core `git` binary and must be called separately. Consult your +
     +-package manager to determine where it is located. For example&#44; on Ubuntu-based +
     +-systems it could be installed under +
     +-`/usr/share/doc/git/contrib/contacts/git-contacts` and may need to be called +
     +-with `perl ...` if it does not have the executable bit set.]
     +-
      -Send your patch with "To:" set to the mailing list, with "cc:" listing
     --people who are involved in the area you are touching (the `git contacts`
     --command in `contrib/contacts/` can help to
     +-people who are involved in the area you are touching (the `git-contacts`
     +-script in `contrib/contacts/`{contrib-scripts} can help to
      -identify them), to solicit comments and reviews.  Also, when you made
      -trial merges of your topic to `next` and `seen`, you may have noticed
      -work by others conflicting with your changes.  There is a good possibility
 6:  f5e44f69941 ! 6:  e889e64bd45 SubmittingPatches: dedupe discussion of security patches
     @@ Documentation/SubmittingPatches: letter.
      +security relevant should be submitted privately to the Git Security
      +mailing list{security-ml}, instead of the public mailing list.
       
     - Send your patch with "To:" set to the mailing list, with "cc:" listing
     - people who are involved in the area you are touching (the `git contacts`
     + :contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are not +
     + part of the core `git` binary and must be called separately. Consult your +
      @@ Documentation/SubmittingPatches: Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
       `Tested-by:` lines as necessary to credit people who helped your
       patch, and "cc:" them when sending such a final version for inclusion.
 7:  e64fc5c888a = 7:  81556298599 SubmittingPatches: add heading for format-patch and send-email
 8:  4fcab0d3319 = 8:  84b1cf3f914 SubmittingPatches: demonstrate using git-contacts with git-send-email

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* Re: Short form of --force-with-lease
  @ 2024-04-11 21:30  3%     ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-11 21:30 UTC (permalink / raw)
  To: rsbecker; +Cc: 'Kipras Melnikovas', wyattscarpenter, git

<rsbecker@nexbridge.com> writes:

> If this is mostly about saving typing, you could get a similar effect adding
> an alias. Something like
>
> git config --global alias.pushfl 'push --force-with-lease'

The use of --force-with-lease without specifying which commit you
took lease on is not all that safe [*], so I am not sure how useful
such an alias will be.

Configuring how "--force" behaves and changing it to an unadorned
"--force-with-lease" is to promote a not-so-safe feature as if it is
safe with false sense of safety, which is not something we would
want to do.


[Footnote]

 * This of course highly depends on your workflow and third-party
   companion tools.  If you (or your IDE in the background) fetch
   from the remote after you started working on the commit to be
   force-pushed, it would update the tip of the remote-tracking
   branch, making --force-with-lease base its decision on a wrong
   commit that your work is not based on.


^ permalink raw reply	[relevance 3%]

* [PATCH v2 0/2] Fix the inconsistency in the description of the namespace option's parameter
  @ 2024-04-11  7:56  4% ` 秃头灯笼鱼 via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: 秃头灯笼鱼 via GitGitGadget @ 2024-04-11  7:56 UTC (permalink / raw)
  To: git; +Cc: 秃头灯笼鱼

Since the parameter of the --namespace option can contain path separators \
and can be correctly parsed, I believe that --namespace=<name> in the
SYNOPSIS section should be changed to --namespace=<path> to match the
description in the OPTIONS section.

秃头灯笼鱼 (2):
  doc: git.txt-Fix inconsistency param description
  doc: git.txt-Change "--user-formats" to "--user-interfaces"

 Documentation/git.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)


base-commit: 5216f8f5c4089ec29ce49afa147434c23e0f0163
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1669%2Fttdly%2Fdoc%2Fgit.txt-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1669/ttdly/doc/git.txt-v2
Pull-Request: https://github.com/git/git/pull/1669

Range-diff vs v1:

 1:  28a5625f32c = 1:  28a5625f32c doc: git.txt-Fix inconsistency param description
 -:  ----------- > 2:  af548abd004 doc: git.txt-Change "--user-formats" to "--user-interfaces"

-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] pack-bitmap: gracefully handle missing BTMP chunks
  2024-04-09  5:59  3% [PATCH] pack-bitmap: gracefully handle missing BTMP chunks Patrick Steinhardt
@ 2024-04-10 15:02  3% ` Taylor Blau
  2024-04-15  6:41  3% ` [PATCH v2] " Patrick Steinhardt
  1 sibling, 0 replies; 200+ results
From: Taylor Blau @ 2024-04-10 15:02 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git

On Tue, Apr 09, 2024 at 07:59:25AM +0200, Patrick Steinhardt wrote:
> One thing that we glossed over though is backwards compatibility with
> repositories that do not yet have BTMP chunks in their multi-pack index.
> In that case, `nth_bitmapped_pack()` would return an error, which causes
> us to emit a warning followed by another error message. These warnings
> are visible to users that fetch from a repository:
>
> ```
> $ git fetch
> ...
> remote: error: MIDX does not contain the BTMP chunk
> remote: warning: unable to load pack: 'pack-f6bb7bd71d345ea9fe604b60cab9ba9ece54ffbe.idx', disabling pack-reuse
> remote: Enumerating objects: 40, done.
> remote: Counting objects: 100% (40/40), done.
> remote: Compressing objects: 100% (39/39), done.
> remote: Total 40 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
> ...
> ```

Nice catch. This is definitely an oversight from my original series,
which should not disrupt single-pack reuse from the MIDX's preferred
pack when using a MIDX/bitmap written prior to the introduction of the
BTMP chunk.

> Fix this issue by partially reverting the logic back to what we had
> before this patch series landed. Namely, in the case where we have no
> BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
> preferred pack instead of consulting the BTMP chunks.

I think that the approach here makes sense to me. The gist is:

- If we don't have a BTMP chunk, then only reuse objects from the
  preferred pack.

- Otherwise, we do have a BTMP chunk (or we're not doing multi-pack
  reuse). If we are reusing objects from multiple packs, then do the
  usual MIDX reuse routines.

- Otherwise, we're doing single-pack reuse. In that case, only reuse
  objects from the preferred pack.

> diff --git a/midx.c b/midx.c
> index 41521e019c..6903e9dfd2 100644
> --- a/midx.c
> +++ b/midx.c
> @@ -1661,9 +1661,10 @@ static int write_midx_internal(const char *object_dir,
>  		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
>  			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
>  			  write_midx_revindex);
> -		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
> -			  bitmapped_packs_concat_len,
> -			  write_midx_bitmapped_packs);
> +		if (git_env_bool("GIT_TEST_MIDX_WRITE_BTMP", 1))
> +			add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
> +				  bitmapped_packs_concat_len,
> +				  write_midx_bitmapped_packs);

I wish that this were possible to exercise without a new
GIT_TEST_-variable. I think there are a couple of alternatives:

You could introduce a new GIT_TEST_MIDX_READ_BTMP variable, and then set
that to control whether or not we read the BTMP chunk. This is what we
did in:

  - 28cd730680d (pack-bitmap: prepare to read lookup table extension,
    2022-08-14), as well as in

  - 7f514b7a5e7 (midx: read `RIDX` chunk when present, 2022-01-25)

. I have a vague preference towards controlling whether or not we read
the BTMP chunk (as opposed to whether or not we write it) as this
removes a potential footgun for users who might accidentally disable
writing a BTMP chunk (in which case you have to rewrite the whole MIDX)
as opposed to reading it (in which case you just change your environment
variable).

Of course, that is still using a GIT_TEST_-variable, which is less than
ideal IMHO. The other alternative would be to store a MIDX file as a
test fixture in the tree (which we do in a couple of places). But with
the recent xz shenanigans, I'm not sure that's a great idea either ;-).

> diff --git a/pack-bitmap.c b/pack-bitmap.c
> index 2baeabacee..f286805724 100644
> --- a/pack-bitmap.c
> +++ b/pack-bitmap.c
> @@ -2049,7 +2049,25 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
>
>  	load_reverse_index(r, bitmap_git);
>
> -	if (bitmap_is_midx(bitmap_git)) {
> +	if (bitmap_is_midx(bitmap_git) &&
> +	    (!multi_pack_reuse || !bitmap_git->midx->chunk_bitmapped_packs)) {
> +		uint32_t preferred_pack_pos;
> +		struct packed_git *pack;
> +
> +		if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
> +			warning(_("unable to compute preferred pack, disabling pack-reuse"));
> +			return;
> +		}
> +
> +		pack = bitmap_git->midx->packs[preferred_pack_pos];
> +
> +		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
> +		packs[packs_nr].p = pack;
> +		packs[packs_nr].bitmap_nr = pack->num_objects;
> +		packs[packs_nr].bitmap_pos = 0;
> +
> +		objects_nr = packs[packs_nr++].bitmap_nr;
> +	} else if (bitmap_is_midx(bitmap_git)) {
>  		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
>  			struct bitmapped_pack pack;
>  			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {

This all makes sense to me. I think we could make the result slightly
more readable by handling the case where we're doing multi-pack reuse
separately from the case where we're not.

I tried to make that change locally to see if it was a good idea, and
I'm reasonably happy with the result. I can't think of a great way to
talk about it without just showing the resulting patch (as the
inter-diff is fairly difficult to read IMHO). So here is the resulting
patch (forging your s-o-b):

--- 8< ---
Subject: [PATCH] pack-bitmap: gracefully handle missing BTMP chunks

In 0fea6b73f1 (Merge branch 'tb/multi-pack-verbatim-reuse', 2024-01-12)
we have introduced multi-pack verbatim reuse of objects. This series has
introduced a new BTMP chunk, which encodes information about bitmapped
objects in the multi-pack index. Starting with dab60934e3 (pack-bitmap:
pass `bitmapped_pack` struct to pack-reuse functions, 2023-12-14) we use
this information to figure out objects which we can reuse from each of
the packfiles.

One thing that we glossed over though is backwards compatibility with
repositories that do not yet have BTMP chunks in their multi-pack index.
In that case, `nth_bitmapped_pack()` would return an error, which causes
us to emit a warning followed by another error message. These warnings
are visible to users that fetch from a repository:

```
$ git fetch
...
remote: error: MIDX does not contain the BTMP chunk
remote: warning: unable to load pack: 'pack-f6bb7bd71d345ea9fe604b60cab9ba9ece54ffbe.idx', disabling pack-reuse
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 40 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
...
```

While the fetch succeeds the user is left wondering what they did wrong.
Furthermore, as visible both from the warning and from the reuse stats,
pack-reuse is completely disabled in such repositories.

What is quite interesting is that this issue can even be triggered in
case `pack.allowPackReuse=single` is set, which is the default value.
One could have expected that in this case we fall back to the old logic,
which is to use the preferred packfile without consulting BTMP chunks at
all. But either we fail with the above error in case they are missing,
or we use the first pack in the multi-pack-index. The former case
disables pack-reuse altogether, whereas the latter case may result in
reusing objects from a suboptimal packfile.

Fix this issue by partially reverting the logic back to what we had
before this patch series landed. Namely, in the case where we have no
BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
preferred pack instead of consulting the BTMP chunks.

Helped-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 midx.c                        |  7 +++---
 pack-bitmap.c                 | 44 +++++++++++++++++++----------------
 t/t5326-multi-pack-bitmaps.sh | 22 ++++++++++++++++++
 3 files changed, 50 insertions(+), 23 deletions(-)

diff --git a/midx.c b/midx.c
index 41521e019c6..6903e9dfd25 100644
--- a/midx.c
+++ b/midx.c
@@ -1661,9 +1661,10 @@ static int write_midx_internal(const char *object_dir,
 		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
 			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
 			  write_midx_revindex);
-		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
-			  bitmapped_packs_concat_len,
-			  write_midx_bitmapped_packs);
+		if (git_env_bool("GIT_TEST_MIDX_WRITE_BTMP", 1))
+			add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+				  bitmapped_packs_concat_len,
+				  write_midx_bitmapped_packs);
 	}

 	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 2baeabacee1..44b32ee3561 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -2049,7 +2049,14 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,

 	load_reverse_index(r, bitmap_git);

-	if (bitmap_is_midx(bitmap_git)) {
+	if (!bitmap_is_midx(bitmap_git) ||
+	    !bitmap_git->midx->chunk_bitmapped_packs)
+		multi_pack_reuse = 0;
+
+	if (multi_pack_reuse) {
+		if (!bitmap_is_midx(bitmap_git))
+			BUG("attempting to perform multi-pack reuse on non-MIDX bitmap");
+
 		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
 			struct bitmapped_pack pack;
 			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
@@ -2062,36 +2069,33 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 			if (!pack.bitmap_nr)
 				continue;

-			if (!multi_pack_reuse && pack.bitmap_pos) {
-				/*
-				 * If we're only reusing a single pack, skip
-				 * over any packs which are not positioned at
-				 * the beginning of the MIDX bitmap.
-				 *
-				 * This is consistent with the existing
-				 * single-pack reuse behavior, which only reuses
-				 * parts of the MIDX's preferred pack.
-				 */
-				continue;
-			}
-
 			ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
 			memcpy(&packs[packs_nr++], &pack, sizeof(pack));

 			objects_nr += pack.p->num_objects;
-
-			if (!multi_pack_reuse)
-				break;
 		}

 		QSORT(packs, packs_nr, bitmapped_pack_cmp);
 	} else {
+		struct packed_git *pack;
+		if (bitmap_is_midx(bitmap_git)) {
+			uint32_t pack_int_id;
+			if (midx_preferred_pack(bitmap_git->midx, &pack_int_id) < 0) {
+				warning(_("unable to compute preferred pack, "
+					  "disabling pack-reuse"));
+				return;
+			}
+
+			pack = bitmap_git->midx->packs[pack_int_id];
+		} else {
+			pack = bitmap_git->pack;
+		}
+
 		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);

-		packs[packs_nr].p = bitmap_git->pack;
-		packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
+		packs[packs_nr].p = pack;
+		packs[packs_nr].bitmap_nr = pack->num_objects;
 		packs[packs_nr].bitmap_pos = 0;
-
 		objects_nr = packs[packs_nr++].bitmap_nr;
 	}

diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58709a..ee3843b2390 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -513,4 +513,26 @@ test_expect_success 'corrupt MIDX with bitmap causes fallback' '
 	)
 '

+for allow_pack_reuse in single multi
+do
+	test_expect_success "MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
+		test_when_finished "rm -rf midx-without-btmp" &&
+		git init midx-without-btmp &&
+		(
+			cd midx-without-btmp &&
+			test_commit initial &&
+
+			# Write a multi-pack index that does have a bitmap, but
+			# no BTMP chunk. Such MIDX files would not be generated
+			# by modern Git anymore, but they were generated by
+			# older Git versions.
+			GIT_TEST_MIDX_WRITE_BTMP=false \
+				git repack -Adbl --write-bitmap-index --write-midx &&
+			git -c pack.allowPackReuse=$allow_pack_reuse \
+				pack-objects --all --use-bitmap-index --stdout </dev/null >/dev/null 2>err &&
+			test_must_be_empty err
+		)
+	'
+done
+
 test_done

base-commit: 91ec36f2cca02d33ab0ed6e87195c6fe801debae
--
2.44.0.549.g74a2f60dcb0
--- >8 ---

The way I would structure this series is to first apply the portion of
the above patch *without* these lines:

-	if (bitmap_is_midx(bitmap_git)) {
+	if (!bitmap_is_midx(bitmap_git) ||
+	    !bitmap_git->midx->chunk_bitmapped_packs)
+		multi_pack_reuse = 0;
+

, so we're still able to reproduce the issue. Then, apply the remaining
portions (the above diff, the test, and the GIT_TEST_MIDX_WRITE_BTMP
stuff) to demonstrate that the issue is fixed via a separate commit.

I'm happy to write that up, and equally happy to not do it ;-). Sorry
for the lengthy review, but thank you very much for spotting and fixing
this issue.

Thanks,
Taylor


^ permalink raw reply related	[relevance 3%]

* What's cooking in git.git (Apr 2024, #04; Tue, 9)
@ 2024-04-09 23:31  1% Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-09 23:31 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking in my tree.  Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and are candidate to be in a
future release).  Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all and may be annotated with an URL
to a message that raises issues but they are no means exhaustive.  A
topic without enough support may be discarded after a long period of
no activity (of course they can be resubmit when new interests
arise).

Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors.  Some
repositories have only a subset of branches.

With maint, master, next, seen, todo:

	git://git.kernel.org/pub/scm/git/git.git/
	git://repo.or.cz/alt-git.git/
	https://kernel.googlesource.com/pub/scm/git/git/
	https://github.com/git/git/
	https://gitlab.com/git-scm/git/

With all the integration branches and topics broken out:

	https://github.com/gitster/git/

Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):

	git://git.kernel.org/pub/scm/git/git-htmldocs.git/
	https://github.com/gitster/git-htmldocs.git/

Release tarballs are available at:

	https://www.kernel.org/pub/software/scm/git/

--------------------------------------------------
[Graduated to 'master']

* dg/myfirstobjectwalk-updates (2024-03-27) 5 commits
  (merged to 'next' on 2024-04-02 at effa6a98a6)
 + MyFirstObjectWalk: add stderr to pipe processing
 + MyFirstObjectWalk: fix description for counting omitted objects
 + MyFirstObjectWalk: fix filtered object walk
 + MyFirstObjectWalk: fix misspelled "builtins/"
 + MyFirstObjectWalk: use additional arg in config_fn_t

 Update a more recent tutorial doc.
 source: <cover.1711537370.git.dirk@gouders.net>


* es/test-cron-safety (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at e383c8cfb2)
 + test-lib: fix non-functioning GIT_TEST_MAINT_SCHEDULER fallback

 The test script had an incomplete and ineffective attempt to avoid
 clobbering the testing user's real crontab (and its equivalents),
 which has been completed.
 source: <20240329222703.9343-1-ericsunshine@charter.net>


* ja/doc-markup-updates (2024-03-29) 5 commits
  (merged to 'next' on 2024-04-02 at 69b015d7ce)
 + doc: git-clone: do not autoreference the manpage in itself
 + doc: git-clone: apply new documentation formatting guidelines
 + doc: git-init: apply new documentation formatting guidelines
 + doc: allow literal and emphasis format in doc vs help tests
 + doc: rework CodingGuidelines with new formatting rules

 Documentation rules has been explicitly described how to mark-up
 literal parts and a few manual pages have been updated as examples.
 source: <pull.1702.v2.git.1711711181.gitgitgadget@gmail.com>


* jc/advice-sans-trailing-whitespace (2024-03-29) 1 commit
  (merged to 'next' on 2024-04-02 at 3cb0fda1bf)
 + advice: omit trailing whitespace

 The "hint:" messages given by the advice mechanism, when given a
 message with a blank line, left a line with trailing whitespace,
 which has been cleansed.
 source: <xmqq4jcooddp.fsf@gitster.g>


* jc/apply-parse-diff-git-header-names-fix (2024-03-29) 3 commits
  (merged to 'next' on 2024-04-02 at d1fa726c41)
 + t4126: fix "funny directory name" test on Windows (again)
  (merged to 'next' on 2024-03-28 at a35de15836)
 + t4126: make sure a directory with SP at the end is usable
  (merged to 'next' on 2024-03-27 at d586367985)
 + apply: parse names out of "diff --git" more carefully

 "git apply" failed to extract the filename the patch applied to,
 when the change was about an empty file created in or deleted from
 a directory whose name ends with a SP, which has been corrected.
 source: <xmqqfrwlltjn.fsf@gitster.g>
 source: <xmqqh6gqt674.fsf_-_@gitster.g>
 source: <xmqq5xx50x8p.fsf_-_@gitster.g>


* mg/editorconfig-makefile (2024-03-23) 1 commit
  (merged to 'next' on 2024-04-02 at 907b55579e)
 + editorconfig: add Makefiles to "text files"

 The .editorconfig file has been taught that a Makefile uses HT
 indentation.
 source: <20240322221813.13019-1-mg@max.gautier.name>


* ps/pack-refs-auto (2024-03-25) 16 commits
  (merged to 'next' on 2024-04-02 at 1d76dc3648)
 + builtin/gc: pack refs when using `git maintenance run --auto`
 + builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
 + t6500: extract objects with "17" prefix
 + builtin/gc: move `struct maintenance_run_opts`
 + builtin/pack-refs: introduce new "--auto" flag
 + builtin/pack-refs: release allocated memory
 + refs/reftable: expose auto compaction via new flag
 + refs: remove `PACK_REFS_ALL` flag
 + refs: move `struct pack_refs_opts` to where it's used
 + t/helper: drop pack-refs wrapper
 + refs/reftable: print errors on compaction failure
 + reftable/stack: gracefully handle failed auto-compaction due to locks
 + reftable/stack: use error codes when locking fails during compaction
 + reftable/error: discern locked/outdated errors
 + reftable/stack: fix error handling in `reftable_stack_init_addition()`
 + Merge branch 'ps/reftable-stack-tempfile' into ps/pack-refs-auto
 (this branch is used by jt/reftable-geometric-compaction.)

 "git pack-refs" learned the "--auto" option, which is a useful
 addition to be triggered from "git gc --auto".

 Acked-by: Karthik Nayak <karthik.188@gmail.com>
 cf. <CAOLa=ZRAEA7rSUoYL0h-2qfEELdbPHbeGpgBJRqesyhHi9Q6WQ@mail.gmail.com>
 source: <cover.1711360631.git.ps@pks.im>


* rj/add-p-explicit-reshow (2024-03-29) 2 commits
  (merged to 'next' on 2024-04-02 at 05c7e930af)
 + add-patch: do not print hunks repeatedly
 + add-patch: introduce 'p' in interactive-patch

 "git add -p" and other "interactive hunk selection" UI has learned to
 skip showing the hunk immediately after it has already been shown, and
 an additional action to explicitly ask to reshow the current hunk.
 source: <a9c515fe-6664-4b5d-abca-d88fdd32a883@gmail.com>


* rj/use-adv-if-enabled (2024-03-30) 3 commits
  (merged to 'next' on 2024-04-02 at 31d4453035)
 + add: use advise_if_enabled for ADVICE_ADD_EMBEDDED_REPO
 + add: use advise_if_enabled for ADVICE_ADD_EMPTY_PATHSPEC
 + add: use advise_if_enabled for ADVICE_ADD_IGNORED_FILE

 Use advice_if_enabled() API to rewrite a simple pattern to
 call advise() after checking advice_enabled().
 source: <46fba030-d7aa-49d2-88fa-e506850f7b6a@gmail.com>

--------------------------------------------------
[New Topics]

* rs/date-mode-pass-by-value (2024-04-05) 1 commit
 - date: make DATE_MODE thread-safe

 The codepaths that reach date_mode_from_type() have been updated to
 pass "struct date_mode" by value to make them thread safe.

 Will merge to 'next'.
 source: <c6cb255a-72f0-4ac2-81a2-1d8e95570a81@web.de>


* rs/usage-fallback-to-show-message-format (2024-04-05) 1 commit
 - usage: report vsnprintf(3) failure

 vreportf(), which is usede by error() and friends, has been taught
 to give the error message printf-format string when its vsnprintf()
 call fails, instead of showing nothing useful to identify the
 nature of the error.

 Will merge to 'next'.
 source: <3da13298-b6a6-4391-b8e8-5dae9a28b860@web.de>


* jc/local-extern-shell-rules (2024-04-05) 8 commits
 - t1016: local VAR="VAL" fix
 - t0610: local VAR="VAL" fix
 - t: teach lint that RHS of 'local VAR=VAL' needs to be quoted
 - t: local VAR="VAL" (quote ${magic-reference})
 - t: local VAR="VAL" (quote command substitution)
 - t: local VAR="VAL" (quote positional parameters)
 - CodingGuidelines: quote assigned value in 'local var=$val'
 - CodingGuidelines: describe "export VAR=VAL" rule

 Document and apply workaround for a buggy version of dash that
 mishandles "local var=val" construct.

 Will merge to 'next'.
 source: <20240406000902.3082301-1-gitster@pobox.com>


* jc/unleak-core-excludesfile (2024-04-08) 1 commit
 - config: do not leak excludes_file

 The variable that holds the value read from the core.excludefile
 configuration variable used to leak, which has been corrected.

 Will merge to 'next'.
 source: <xmqqttkeicov.fsf@gitster.g>


* la/doc-use-of-contacts-when-contributing (2024-04-05) 8 commits
 - SubmittingPatches: demonstrate using git-contacts with git-send-email
 - SubmittingPatches: add heading for format-patch and send-email
 - SubmittingPatches: dedupe discussion of security patches
 - SubmittingPatches: discuss reviewers first
 - SubmittingPatches: quote commands
 - SubmittingPatches: mention GitGitGadget
 - SubmittingPatches: make 'git contacts' grep-friendly
 - MyFirstContribution: mention contrib/contacts/git-contacts

 Advertise "git contacts", a tool for newcomers to find people to
 ask review for their patches, a bit more in our developer
 documentation.

 Expecting a reroll.
 cf. <owlypluzs5qa.fsf@fine.c.googlers.com>
 source: <pull.1704.v2.git.1712366536.gitgitgadget@gmail.com>


* ps/ci-test-with-jgit (2024-04-08) 12 commits
 - t0612: add tests to exercise Git/JGit reftable compatibility
 - t0610: fix non-portable variable assignment
 - t06xx: always execute backend-specific tests
 - ci: install JGit dependency
 - ci: make Perforce binaries executable for all users
 - ci: merge scripts which install dependencies
 - ci: merge custom PATH directories
 - ci: convert "install-dependencies.sh" to use "/bin/sh"
 - ci: drop duplicate package installation for "linux-gcc-default"
 - ci: allow skipping sudo on dockerized jobs
 - ci: expose distro name in dockerized GitHub jobs
 - ci: rename "runs_on_pool" to "distro"

 Tests to ensure interoperability between reftable written by jgit
 and our code have been added and enabled in CI.

 Comments?
 source: <cover.1712555682.git.ps@pks.im>


* pw/rebase-i-error-message (2024-04-08) 2 commits
 - rebase -i: improve error message when picking merge
 - rebase -i: pass struct replay_opts to parse_insn_line()

 When the user adds to "git rebase -i" instruction to "pick" a merge
 commit, the error experience is not pleasant.  Such an error is now
 caught earlier in the process that parses the todo list.

 Comments?
 source: <pull.1672.v2.git.1712585787.gitgitgadget@gmail.com>


* tb/make-indent-conditional-with-non-spaces (2024-04-08) 2 commits
 - Makefile(s): do not enforce "all indents must be done with tab"
 - Makefile(s): avoid recipe prefix in conditional statements

 Adjust to an upcoming changes to GNU make that breaks our Makefiles.

 Will merge to 'next'.
 source: <9d14c08ca6cc06cdf8fb4ba33d2470053dca3966.1712591504.git.me@ttaylorr.com>


* ps/t0610-umask-fix (2024-04-09) 2 commits
 - t0610: execute git-pack-refs(1) with specified umask
 - t0610: make `--shared=` tests reusable

 The "shared repository" test in the t0610 reftable test failed
 under restrictive umask setting (e.g. 007), which has been
 corrected.

 Will merge to 'next'.
 source: <cover.1712656576.git.ps@pks.im>

--------------------------------------------------
[Cooking]

* ma/win32-unix-domain-socket (2024-04-03) 1 commit
  (merged to 'next' on 2024-04-09 at b98021a65c)
 + Win32: detect unix socket support at runtime

 Windows binary used to decide the use of unix-domain socket at
 build time, but it learned to make the decision at runtime instead.

 Will merge to 'master'.
 source: <pull.1708.git.1712158923106.gitgitgadget@gmail.com>


* ps/reftable-write-optim (2024-04-08) 11 commits
 - reftable/block: reuse compressed array
 - reftable/block: reuse zstream when writing log blocks
 - reftable/writer: reset `last_key` instead of releasing it
 - reftable/writer: unify releasing memory
 - reftable/writer: refactorings for `writer_flush_nonempty_block()`
 - reftable/writer: refactorings for `writer_add_record()`
 - refs/reftable: don't recompute committer ident
 - reftable: remove name checks
 - refs/reftable: skip duplicate name checks
 - refs/reftable: perform explicit D/F check when writing symrefs
 - refs/reftable: fix D/F conflict error message on ref copy

 Code to write out reftable has seen some optimization and
 simplification.
 source: <cover.1712578837.git.ps@pks.im>


* ds/send-email-per-message-block (2024-04-08) 3 commits
 - send-email: separate the confirmation prompts from the messages
 - send-email: make it easy to discern the messages for each patch
 - send-email: move newline character out of a translatable string

 "git send-email" learned to separate its reports on each message it
 sends out with an extra blank line in between.

 Comments?
 source: <cover.1712486910.git.dsimic@manjaro.org>


* ds/fetch-config-parse-microfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 585dcadd63)
 + fetch: return when parsing submodule.recurse

 A config parser callback function fell through instead of returning
 after recognising and processing a variable, wasting cycles, which
 has been corrected.

 Will merge to 'master'.
 source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>


* rs/apply-lift-path-length-limit (2024-04-05) 2 commits
  (merged to 'next' on 2024-04-09 at 3270d194fd)
 + path: remove mksnpath()
 + apply: avoid fixed-size buffer in create_one_file()

 "git apply" has been updated to lift the hardcoded pathname length
 limit, which in turn allowed a mksnpath() function that is no
 longer used.

 Will merge to 'master'.
 source: <df774306-f29b-4a75-a282-59db89812b9a@web.de>


* rs/apply-reject-fd-leakfix (2024-04-05) 1 commit
  (merged to 'next' on 2024-04-09 at 11efa0543c)
 + apply: don't leak fd on fdopen() error

 A file descriptor leak in an error codepath, used when "git apply
 --reject" fails to create the *.rej file, has been corrected.

 Will merge to 'master'.
 source: <5ba55ee4-94c7-4094-a744-584fc623b391@web.de>


* kn/clarify-update-ref-doc (2024-04-02) 2 commits
  (merged to 'next' on 2024-04-02 at d1b9c5aa67)
 + githooks: use {old,new}-oid instead of {old,new}-value
 + update-ref: use {old,new}-oid instead of {old,new}value

 Doc update, as a preparation to enhance "git update-ref --stdin".

 Will merge to 'master'.
 source: <20240402064915.191104-1-knayak@gitlab.com>


* vs/complete-with-set-u-fix (2024-04-01) 2 commits
  (merged to 'next' on 2024-04-02 at d8f6a511e8)
 + completion: protect prompt against unset SHOWUPSTREAM in nounset mode
 + completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode

 Another "set -u" fix for the bash prompt (in contrib/) script.

 Will merge to 'master'.
 source: <20240401190751.8676-1-ville.skytta@iki.fi>


* ba/osxkeychain-updates (2024-04-01) 4 commits
 - osxkeychain: store new attributes
 - osxkeychain: erase matching passwords only
 - osxkeychain: erase all matching credentials
 - osxkeychain: replace deprecated SecKeychain API

 Update osxkeychain backend with features required for the recent
 credential subsystem.

 Will merge to 'next'?
 source: <pull.1667.git.1708212896.gitgitgadget@gmail.com>


* rs/imap-send-use-xsnprintf (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 789ad853e1)
 + imap-send: use xsnprintf to format command

 Code clean-up and duplicate reduction.

 Will merge to 'master'.
 source: <f9ad9f41-5b9b-474e-9818-f91fc937daae@web.de>


* tb/midx-write (2024-04-01) 5 commits
  (merged to 'next' on 2024-04-05 at b4870116f7)
 + midx-write.c: use `--stdin-packs` when repacking
 + midx-write.c: check count of packs to repack after grouping
 + midx-write.c: factor out common want_included_pack() routine
 + midx-write: move writing-related functions from midx.c
 + Merge branch 'rs/midx-use-strvec-pushf' into tb/midx-write

 Code clean-up by splitting code responsible for writing midx files
 into its own file.

 Will merge to 'master'.
 source: <cover.1712006190.git.me@ttaylorr.com>


* jc/t2104-style-update (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-03 at 0449835479)
 + t2104: style fixes

 Coding style fixes.

 Will merge to 'master'.
 source: <xmqqmsqb4ngg.fsf@gitster.g>


* rs/t-prio-queue-cleanup (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 7961c838ac)
 + t-prio-queue: simplify using compound literals

 t-prio-queue test has been cleaned up by using C99 compound
 literals; this is meant to also serve as a weather-balloon to smoke
 out folks with compilers who have trouble compiling code that uses
 the feature.

 Will merge to 'master'.
 source: <520da361-1b80-4ba3-87b2-86d6fdfc18b5@web.de>


* jk/libcurl-8.7-regression-workaround (2024-04-05) 3 commits
 - remote-curl: add Transfer-Encoding header only for older curl
 - INSTALL: bump libcurl version to 7.21.3
 - http: reset POSTFIELDSIZE when clearing curl handle

 Fix was added to work around a regression in libcURL 8.7.0 (which has
 already been fixed in their tip of the tree).

 Will merge to 'next'.
 source: <20240402200254.GA874754@coredump.intra.peff.net>


* tb/t7700-fixup (2024-04-03) 1 commit
 - t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `

 Test fix.

 Will merge to 'next'.
 source: <7e8d435d58eea19d2aae0be366720f5956d29a5d.1712075189.git.me@ttaylorr.com>


* gt/add-u-commit-i-pathspec-check (2024-04-03) 3 commits
  (merged to 'next' on 2024-04-09 at 1a0c757907)
 + builtin/add: error out when passing untracked path with -u
 + builtin/commit: error out when passing untracked path with -i
 + revision: optionally record matches with pathspec elements

 "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
 diagnose a pathspec element that did not match any files in certain
 situations, unlike "git add <pathspec>" did.

 Will merge to 'master'.
 source: <20240402213640.139682-2-shyamthakkar001@gmail.com>


* jt/reftable-geometric-compaction (2024-04-08) 4 commits
 - reftable/stack: use geometric table compaction
 - reftable/stack: add env to disable autocompaction
 - reftable/stack: expose option to disable auto-compaction
 - Merge branch 'ps/pack-refs-auto' into jt/reftable-geometric-compaction

 The strategy to compact multiple tables of reftables after many
 operations accumulate many entries has been improved to avoid
 accumulating too many tables uncollected.

 Comments?
 source: <pull.1683.v6.git.1712593016.gitgitgadget@gmail.com>


* ds/typofix-core-config-doc (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 79496fcfc4)
 + config: fix some small capitalization issues, as spotted

 Typofix.

 Will merge to 'master'.
 source: <26135b06c48565ee8ac6dcfc1ef5431511e6202c.1711918168.git.dsimic@manjaro.org>


* jc/checkout-detach-wo-tracking-report (2024-03-30) 1 commit
  (merged to 'next' on 2024-04-04 at 161eca247d)
 + checkout: omit "tracking" information on a detached HEAD

 "git checkout/switch --detach foo", after switching to the detached
 HEAD state, gave the tracking information for the 'foo' branch,
 which was pointless.

 Tested-by: M Hickford <mirth.hickford@gmail.com>
 cf. <CAGJzqsmE9FDEBn=u3ge4LA3ha4fDbm4OWiuUbMaztwjELBd7ug@mail.gmail.com>

 Will merge to 'master'.
 source: <xmqqa5mfl7ud.fsf@gitster.g>


* rs/mem-pool-size-t-safety (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 3517d48210)
 + mem-pool: use st_add() in mem_pool_strvfmt()

 size_t arithmetic safety.

 Will merge to 'master'.
 source: <bbe00b9e-64d8-4ec8-a2b9-2c6917c72dbd@web.de>


* ew/khash-to-khashl (2024-03-28) 3 commits
 - khashl: fix ensemble lookups on empty table
 - treewide: switch to khashl for memory savings
 - list-objects-filter: use kh_size API

 The hashtable library "khash.h" has been replaced with "khashl.h"
 that has better memory usage characteristics.

 Needs review.
 source: <20240328101356.300374-1-e@80x24.org>


* ps/reftable-block-iteration-optim (2024-03-27) 9 commits
 - reftable/block: reuse `zstream` state on inflation
 - reftable/block: open-code call to `uncompress2()`
 - reftable/block: reuse uncompressed blocks
 - reftable/reader: iterate to next block in place
 - reftable/block: move ownership of block reader into `struct table_iter`
 - reftable/block: introduce `block_reader_release()`
 - reftable/block: better grouping of functions
 - reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
 - reftable/block: rename `block_reader_start()`

 The code to iterate over reftable blocks has seen some optimization
 to reduce memory allocation and deallocation.

 Needs review.
 source: <cover.1711519925.git.ps@pks.im>


* bc/credential-scheme-enhancement (2024-03-27) 12 commits
 . credential: add support for multistage credential rounds
 . t5563: refactor for multi-stage authentication
 . docs: set a limit on credential line length
 . credential: enable state capability
 . credential: add an argument to keep state
 . http: add support for authtype and credential
 . docs: indicate new credential protocol fields
 . credential: gate new fields on capability
 . credential: add a field for pre-encoded credentials
 . http: use new headers for each object request
 . remote-curl: reset headers on new request
 . credential: add an authtype field

 The credential helper protocol, together with the HTTP layer, have
 been enhanced to support authentication schemes different from
 username & password pair, like Bearer and NTLM.

 Expecting a reroll.
 cf. <ZgSQ5o_KyqDaxz1m@tapette.crustytoothpaste.net>
 source: <20240324011301.1553072-1-sandals@crustytoothpaste.net>


* ps/reftable-binsearch-updates (2024-04-03) 7 commits
  (merged to 'next' on 2024-04-04 at 40e6d5a36b)
 + reftable/block: avoid decoding keys when searching restart points
 + reftable/record: extract function to decode key lengths
 + reftable/block: fix error handling when searching restart points
 + reftable/block: refactor binary search over restart points
 + reftable/refname: refactor binary search over refnames
 + reftable/basics: improve `binsearch()` test
 + reftable/basics: fix return type of `binsearch()` to be `size_t`

 Reftable code clean-up and some bugfixes.

 Will merge to 'master'.
 source: <cover.1712123093.git.ps@pks.im>


* tb/pseudo-merge-reachability-bitmap (2024-03-20) 24 commits
 - t/perf: implement performace tests for pseudo-merge bitmaps
 - pseudo-merge: implement support for finding existing merges
 - ewah: `bitmap_equals_ewah()`
 - pack-bitmap: extra trace2 information
 - pack-bitmap.c: use pseudo-merges during traversal
 - t/test-lib-functions.sh: support `--date` in `test_commit_bulk()`
 - pack-bitmap: implement test helpers for pseudo-merge
 - ewah: implement `ewah_bitmap_popcount()`
 - pseudo-merge: implement support for reading pseudo-merge commits
 - pack-bitmap.c: read pseudo-merge extension
 - pseudo-merge: scaffolding for reads
 - pack-bitmap: extract `read_bitmap()` function
 - pack-bitmap-write.c: write pseudo-merge table
 - pack-bitmap-write.c: select pseudo-merge commits
 - pseudo-merge: implement support for selecting pseudo-merge commits
 - pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` public
 - pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`
 - pack-bitmap-write: support storing pseudo-merge commits
 - pseudo-merge.ch: initial commit
 - pack-bitmap: move some initialization to `bitmap_writer_init()`
 - pack-bitmap: drop unused `max_bitmaps` parameter
 - ewah: implement `ewah_bitmap_is_subset()`
 - config: repo_config_get_expiry()
 - Documentation/technical: describe pseudo-merge bitmaps format

 The pack-bitmap machinery learned to write pseudo-merge bitmaps,
 which act as imaginary octopus merges covering un-bitmapped
 reference tips. This enhances bitmap coverage, and thus,
 performance, for repositories with many references using bitmaps.

 Expecting a reroll.
 cf. <ZfyxCLpjbaScIdWA@nand.local>
 source: <cover.1710972293.git.me@ttaylorr.com>


* la/hide-trailer-info (2024-03-16) 7 commits
 - trailer: retire trailer_info_get() from API
 - trailer: make trailer_info struct private
 - trailer: make parse_trailers() return trailer_info pointer
 - interpret-trailers: access trailer_info with new helpers
 - sequencer: use the trailer iterator
 - trailer: teach iterator about non-trailer lines
 - Merge branch 'la/format-trailer-info' into la/hide-trailer-info
 (this branch uses la/format-trailer-info.)

 The trailer API has been reshuffled a bit.

 Needs review.
 source: <pull.1696.git.1710570428.gitgitgadget@gmail.com>


* ds/doc-config-reflow (2024-03-14) 1 commit
 - config.txt: perform some minor reformatting

 Reflow a paragraph in the documentation source without any effect
 to the formatted text.

 Will discard.
 source: <97bdaf075bf5a68554cca1731eca78aff2662907.1710444774.git.dsimic@manjaro.org>


* la/format-trailer-info (2024-03-15) 5 commits
 - trailer: finish formatting unification
 - trailer: begin formatting unification
 - format_trailer_info(): append newline for non-trailer lines
 - format_trailer_info(): drop redundant unfold_value()
 - format_trailer_info(): use trailer_item objects
 (this branch is used by la/hide-trailer-info.)

 The code to format trailers have been cleaned up.

 Comments?
 source: <pull.1694.git.1710485706.gitgitgadget@gmail.com>


* ie/config-includeif-hostname (2024-03-19) 2 commits
 - config: learn the "hostname:" includeIf condition
 - t: add a test helper for getting hostname

 The conditional inclusion mechanism for configuration files learned
 to switch on the hostname.

 Expecting a reroll.
 cf. <20240319210428.GC1159535@coredump.intra.peff.net>
 cf. <20240320001934.GA903718@coredump.intra.peff.net>
 source: <20240319183722.211300-1-ignacio@iencinas.com>


* js/build-fuzz-more-often (2024-03-05) 3 commits
 - SQUASH???
 - fuzz: link fuzz programs with `make all` on Linux
 - ci: also define CXX environment variable

 In addition to building the objects needed, try to link the objects
 that are used in fuzzer tests, to make sure at least they build
 without bitrot, in Linux CI runs.

 Stalled.
 cf. <xmqq1q7w8xx6.fsf@gitster.g>
 source: <cover.1709673020.git.steadmon@google.com>


* sj/userdiff-c-sharp (2024-04-05) 1 commit
 - userdiff: better method/property matching for C#

 The userdiff patterns for C# has been updated.

 Acked-by: Johannes Sixt <j6t@kdbg.org>
 cf. <c2154457-3f2f-496e-9b8b-c8ea7257027b@kdbg.org>

 Will merge to 'next'.
 source: <pull.1682.v5.git.git.1712180564927.gitgitgadget@gmail.com>


* cw/git-std-lib (2024-02-28) 4 commits
 - SQUASH??? get rid of apparent debugging crufts
 - test-stdlib: show that git-std-lib is independent
 - git-std-lib: introduce Git Standard Library
 - pager: include stdint.h because uintmax_t is used

 Split libgit.a out to a separate git-std-lib tor easier reuse.

 Expecting a reroll.
 source: <cover.1696021277.git.jonathantanmy@google.com>


* js/cmake-with-test-tool (2024-02-23) 2 commits
 - cmake: let `test-tool` run the unit tests, too
 - Merge branch 'js/unit-test-suite-runner' into js/cmake-with-test-tool
 (this branch uses js/unit-test-suite-runner.)

 "test-tool" is now built in CMake build to also run the unit tests.

 May want to roll it into the base topic.
 source: <pull.1666.git.1708038924522.gitgitgadget@gmail.com>


* js/unit-test-suite-runner (2024-02-23) 8 commits
 - ci: use test-tool as unit test runner on Windows
 - t/Makefile: run unit tests alongside shell tests
 - unit tests: add rule for running with test-tool
 - test-tool run-command testsuite: support unit tests
 - test-tool run-command testsuite: remove hardcoded filter
 - test-tool run-command testsuite: get shell from env
 - t0080: turn t-basic unit test into a helper
 - Merge branch 'jk/unit-tests-buildfix' into js/unit-test-suite-runner
 (this branch is used by js/cmake-with-test-tool.)

 The "test-tool" has been taught to run testsuite tests in parallel,
 bypassing the need to use the "prove" tool.

 Needs review.
 source: <cover.1708728717.git.steadmon@google.com>


* bk/complete-dirname-for-am-and-format-patch (2024-01-12) 1 commit
 - completion: dir-type optargs for am, format-patch

 Command line completion support (in contrib/) has been
 updated for a few commands to complete directory names where a
 directory name is expected.

 Expecting a reroll.
 cf. <40c3a824-a961-490b-94d4-4eb23c8f713d@gmail.com>
 cf. <6683f24e-7e56-489d-be2d-8afe1fc38d2b@gmail.com>
 source: <d37781c3-6af2-409b-95a8-660a9b92d20b@smtp-relay.sendinblue.com>


* bk/complete-send-email (2024-01-12) 1 commit
 - completion: don't complete revs when --no-format-patch

 Command line completion support (in contrib/) has been taught to
 avoid offering revision names as candidates to "git send-email" when
 the command is used to send pre-generated files.

 Expecting a reroll.
 cf. <CAC4O8c88Z3ZqxH2VVaNPpEGB3moL5dJcg3cOWuLWwQ_hLrJMtA@mail.gmail.com>
 source: <a718b5ee-afb0-44bd-a299-3208fac43506@smtp-relay.sendinblue.com>


* tb/path-filter-fix (2024-01-31) 16 commits
 - bloom: introduce `deinit_bloom_filters()`
 - commit-graph: reuse existing Bloom filters where possible
 - object.h: fix mis-aligned flag bits table
 - commit-graph: new Bloom filter version that fixes murmur3
 - commit-graph: unconditionally load Bloom filters
 - bloom: prepare to discard incompatible Bloom filters
 - bloom: annotate filters with hash version
 - repo-settings: introduce commitgraph.changedPathsVersion
 - t4216: test changed path filters with high bit paths
 - t/helper/test-read-graph: implement `bloom-filters` mode
 - bloom.h: make `load_bloom_filter_from_graph()` public
 - t/helper/test-read-graph.c: extract `dump_graph_info()`
 - gitformat-commit-graph: describe version 2 of BDAT
 - commit-graph: ensure Bloom filters are read with consistent settings
 - revision.c: consult Bloom filters for root commits
 - t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`

 The Bloom filter used for path limited history traversal was broken
 on systems whose "char" is unsigned; update the implementation and
 bump the format version to 2.

 Waiting for a final ack?
 cf. <ZcFjkfbsBfk7JQIH@nand.local>
 source: <cover.1706741516.git.me@ttaylorr.com>


* jc/rerere-cleanup (2023-08-25) 4 commits
 - rerere: modernize use of empty strbuf
 - rerere: try_merge() should use LL_MERGE_ERROR when it means an error
 - rerere: fix comment on handle_file() helper
 - rerere: simplify check_one_conflict() helper function

 Code clean-up.

 Not ready to be reviewed yet.
 source: <20230824205456.1231371-1-gitster@pobox.com>


^ permalink raw reply	[relevance 1%]

* [PATCH v3 0/8] docs: recommend using contrib/contacts/git-contacts
  2024-04-06  1:22  3% ` [PATCH v2 0/8] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
@ 2024-04-09 21:56  3%   ` Linus Arver via GitGitGadget
  2024-04-11 23:32  2%     ` [PATCH v4 " Linus Arver via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-09 21:56 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Patrick Steinhardt, Matthieu Moy, Linus Arver

Make git-contacts more prominent in our docs.


Notable changes in v3
=====================

 * Refer to GitGitGadget via a link to MyFirstContribution (instead of
   sending readers to GGG's homepage directly)
 * Soften the advice for using git-contacts


Notable changes in v2
=====================

 * Improve existing mention of git-contacts in SubmittingPatches (instead of
   adding a separate, entirely new paragraph)
 * Add example usage of integrating git-contacts with git-send-email with
   the latter's --cc-cmd flag.
 * Various smaller fixes to SubmittingPatches

Linus Arver (8):
  MyFirstContribution: mention contrib/contacts/git-contacts
  SubmittingPatches: make 'git contacts' grep-friendly
  SubmittingPatches: mention GitGitGadget
  SubmittingPatches: quote commands
  SubmittingPatches: discuss reviewers first
  SubmittingPatches: dedupe discussion of security patches
  SubmittingPatches: add heading for format-patch and send-email
  SubmittingPatches: demonstrate using git-contacts with git-send-email

 Documentation/MyFirstContribution.txt |  5 ++
 Documentation/SubmittingPatches       | 68 +++++++++++++++------------
 2 files changed, 43 insertions(+), 30 deletions(-)


base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

Range-diff vs v2:

 1:  3817e7f3cd0 ! 1:  1f65dc5ba3d MyFirstContribution: mention contrib/contacts/git-contacts
     @@ Documentation/MyFirstContribution.txt: $ git send-email --to=target@example.com
       NOTE: Check `git help send-email` for some other options which you may find
       valuable, such as changing the Reply-to address or adding more CC and BCC lines.
       
     -+NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
     -+include in the CC list. In addition, you can do `git send-email --cc-cmd='git
     -+contacts' feature/*.patch` to automatically pass this list of emails to
     -+`send-email`.
     ++NOTE: If you're not sure who to CC, use `contrib/contacts/git-contacts` to get a
     ++list of reviewers you should include in the CC list. In addition, you can do
     ++`git send-email --cc-cmd='git contacts' feature/*.patch` to automatically pass
     ++this list of emails to `send-email`.
      +
       NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
       please don't send your patchset from the tutorial to the real mailing list! For
 2:  82e5e05288d = 2:  6e8b1b50ac5 SubmittingPatches: make 'git contacts' grep-friendly
 3:  6e6950afa6e ! 3:  7f8fdc053f7 SubmittingPatches: mention GitGitGadget
     @@ Documentation/SubmittingPatches: are optimized for the workflow of sending patch
       your existing e-mail client (often optimized for "multipart/*" MIME
       type e-mails) might render your patches unusable.
       
     -+NOTE: You can also use GitGitGadget (https://gitgitgadget.github.io/) to send in
     -+your patches. The discussion here focuses on using `format-patch` and
     -+`send-email`.
     ++NOTE: Here we outline the procedure using `format-patch` and
     ++`send-email`, but you can instead use GitGitGadget to send in your
     ++patches (see link:MyFirstContribution.html[MyFirstContribution]).
      +
       People on the Git mailing list need to be able to read and
       comment on the changes you are submitting.  It is important for
 4:  fb06d5ce247 = 4:  ef26bdb75c5 SubmittingPatches: quote commands
 5:  a8abcf45881 = 5:  6f71b1731f2 SubmittingPatches: discuss reviewers first
 6:  326afe13315 = 6:  f5e44f69941 SubmittingPatches: dedupe discussion of security patches
 7:  09f4e7ad123 = 7:  e64fc5c888a SubmittingPatches: add heading for format-patch and send-email
 8:  b35748f0cf8 = 8:  4fcab0d3319 SubmittingPatches: demonstrate using git-contacts with git-send-email

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [PATCH 0/3] Cleanup rebase signoff tests
@ 2024-04-09 15:27  4% Phillip Wood via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Phillip Wood via GitGitGadget @ 2024-04-09 15:27 UTC (permalink / raw)
  To: git; +Cc: Phillip Wood

This series cleans up the tests for "git rebase --signoff" in preparation
for extending them and fixing a couple of bugs in a future series. The
cleanups are:

 * move test setup into "test_expect_success"
 * stop running git upstream of a pipe
 * restore "git rebase --apply --signoff" coverage

Phillip Wood (3):
  t3428: modernize test setup
  t3428: use test_commit_message
  t3428: restore coverage for "apply" backend

 t/t3428-rebase-signoff.sh | 67 ++++++++++++++++++---------------------
 1 file changed, 30 insertions(+), 37 deletions(-)


base-commit: 19981daefd7c147444462739375462b49412ce33
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1713%2Fphillipwood%2Fcleanup-rebase-signoff-tests-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1713/phillipwood/cleanup-rebase-signoff-tests-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1713
-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* [PATCH] pack-bitmap: gracefully handle missing BTMP chunks
@ 2024-04-09  5:59  3% Patrick Steinhardt
  2024-04-10 15:02  3% ` Taylor Blau
  2024-04-15  6:41  3% ` [PATCH v2] " Patrick Steinhardt
  0 siblings, 2 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-09  5:59 UTC (permalink / raw)
  To: git; +Cc: Taylor Blau

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

In 0fea6b73f1 (Merge branch 'tb/multi-pack-verbatim-reuse', 2024-01-12)
we have introduced multi-pack verbatim reuse of objects. This series has
introduced a new BTMP chunk, which encodes information about bitmapped
objects in the multi-pack index. Starting with dab60934e3 (pack-bitmap:
pass `bitmapped_pack` struct to pack-reuse functions, 2023-12-14) we use
this information to figure out objects which we can reuse from each of
the packfiles.

One thing that we glossed over though is backwards compatibility with
repositories that do not yet have BTMP chunks in their multi-pack index.
In that case, `nth_bitmapped_pack()` would return an error, which causes
us to emit a warning followed by another error message. These warnings
are visible to users that fetch from a repository:

```
$ git fetch
...
remote: error: MIDX does not contain the BTMP chunk
remote: warning: unable to load pack: 'pack-f6bb7bd71d345ea9fe604b60cab9ba9ece54ffbe.idx', disabling pack-reuse
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 40 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
...
```

While the fetch succeeds the user is left wondering what they did wrong.
Furthermore, as visible both from the warning and from the reuse stats,
pack-reuse is completely disabled in such repositories.

What is quite interesting is that this issue can even be triggered in
case `pack.allowPackReuse=single` is set, which is the default value.
One could have expected that in this case we fall back to the old logic,
which is to use the preferred packfile without consulting BTMP chunks at
all. But either we fail with the above error in case they are missing,
or we use the first pack in the multi-pack-index. The former case
disables pack-reuse altogether, whereas the latter case may result in
reusing objects from a suboptimal packfile.

Fix this issue by partially reverting the logic back to what we had
before this patch series landed. Namely, in the case where we have no
BTMP chunks or when `pack.allowPackReuse=single` are set, we use the
preferred pack instead of consulting the BTMP chunks.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 midx.c                        |  7 ++++---
 pack-bitmap.c                 | 36 ++++++++++++++++++-----------------
 t/t5326-multi-pack-bitmaps.sh | 22 +++++++++++++++++++++
 3 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/midx.c b/midx.c
index 41521e019c..6903e9dfd2 100644
--- a/midx.c
+++ b/midx.c
@@ -1661,9 +1661,10 @@ static int write_midx_internal(const char *object_dir,
 		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
 			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
 			  write_midx_revindex);
-		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
-			  bitmapped_packs_concat_len,
-			  write_midx_bitmapped_packs);
+		if (git_env_bool("GIT_TEST_MIDX_WRITE_BTMP", 1))
+			add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+				  bitmapped_packs_concat_len,
+				  write_midx_bitmapped_packs);
 	}
 
 	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 2baeabacee..f286805724 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -2049,7 +2049,25 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 
 	load_reverse_index(r, bitmap_git);
 
-	if (bitmap_is_midx(bitmap_git)) {
+	if (bitmap_is_midx(bitmap_git) &&
+	    (!multi_pack_reuse || !bitmap_git->midx->chunk_bitmapped_packs)) {
+		uint32_t preferred_pack_pos;
+		struct packed_git *pack;
+
+		if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
+			warning(_("unable to compute preferred pack, disabling pack-reuse"));
+			return;
+		}
+
+		pack = bitmap_git->midx->packs[preferred_pack_pos];
+
+		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+		packs[packs_nr].p = pack;
+		packs[packs_nr].bitmap_nr = pack->num_objects;
+		packs[packs_nr].bitmap_pos = 0;
+
+		objects_nr = packs[packs_nr++].bitmap_nr;
+	} else if (bitmap_is_midx(bitmap_git)) {
 		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
 			struct bitmapped_pack pack;
 			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
@@ -2062,26 +2080,10 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 			if (!pack.bitmap_nr)
 				continue;
 
-			if (!multi_pack_reuse && pack.bitmap_pos) {
-				/*
-				 * If we're only reusing a single pack, skip
-				 * over any packs which are not positioned at
-				 * the beginning of the MIDX bitmap.
-				 *
-				 * This is consistent with the existing
-				 * single-pack reuse behavior, which only reuses
-				 * parts of the MIDX's preferred pack.
-				 */
-				continue;
-			}
-
 			ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
 			memcpy(&packs[packs_nr++], &pack, sizeof(pack));
 
 			objects_nr += pack.p->num_objects;
-
-			if (!multi_pack_reuse)
-				break;
 		}
 
 		QSORT(packs, packs_nr, bitmapped_pack_cmp);
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58709..ee3843b239 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -513,4 +513,26 @@ test_expect_success 'corrupt MIDX with bitmap causes fallback' '
 	)
 '
 
+for allow_pack_reuse in single multi
+do
+	test_expect_success "MIDX without BTMP chunk does not complain with $allow_pack_reuse pack reuse" '
+		test_when_finished "rm -rf midx-without-btmp" &&
+		git init midx-without-btmp &&
+		(
+			cd midx-without-btmp &&
+			test_commit initial &&
+
+			# Write a multi-pack index that does have a bitmap, but
+			# no BTMP chunk. Such MIDX files would not be generated
+			# by modern Git anymore, but they were generated by
+			# older Git versions.
+			GIT_TEST_MIDX_WRITE_BTMP=false \
+				git repack -Adbl --write-bitmap-index --write-midx &&
+			git -c pack.allowPackReuse=$allow_pack_reuse \
+				pack-objects --all --use-bitmap-index --stdout </dev/null >/dev/null 2>err &&
+			test_must_be_empty err
+		)
+	'
+done
+
 test_done
-- 
2.44.GIT


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

^ permalink raw reply related	[relevance 3%]

* [PATCH v2 0/3] show-ref: add --symbolic-name option
  @ 2024-04-08 17:38  2% ` John Cai via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: John Cai via GitGitGadget @ 2024-04-08 17:38 UTC (permalink / raw)
  To: git
  Cc: Phillip Wood, Kristoffer Haugsbakk, Jeff King, Patrick Steinhardt,
	Jean-Noël Avila, John Cai

For reftable development, it would be handy to have a tool to provide the
direct value of any ref whether it be a symbolic ref or not. Currently there
is git-symbolic-ref, which only works for symbolic refs, and git-rev-parse,
which will resolve the ref. Let's add a --symbolic-name option that will
print out the value the ref directly points to without dereferencing it.

Changes since V1:

 * changed output format to print out values as a third column
 * made plumbing changes to enable the value of a symbolic ref to be read
   from the iterator
 * changed the name of the flag

John Cai (3):
  refs: keep track of unresolved reference value in iterator
  refs: add referent to each_repo_ref_fn
  show-ref: add --symbolic-name option

 Documentation/git-show-ref.txt | 21 ++++++++++++++++++-
 builtin/replace.c              |  1 +
 builtin/show-ref.c             | 38 ++++++++++++++++++++++++----------
 builtin/submodule--helper.c    |  2 +-
 refs.c                         | 31 ++++++++++++++++++---------
 refs.h                         |  6 ++++--
 refs/files-backend.c           | 20 ++++++++++--------
 refs/iterator.c                |  3 ++-
 refs/ref-cache.c               |  3 +++
 refs/ref-cache.h               |  2 ++
 refs/refs-internal.h           |  1 +
 refs/reftable-backend.c        | 13 ++++++++----
 remote.c                       |  2 +-
 replace-object.c               |  1 +
 sequencer.c                    |  4 ++--
 t/helper/test-ref-store.c      |  2 +-
 t/t1403-show-ref.sh            | 20 ++++++++++++++++++
 worktree.c                     |  4 +++-
 18 files changed, 130 insertions(+), 44 deletions(-)


base-commit: 7774cfed6261ce2900c84e55906da708c711d601
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1684%2Fjohn-cai%2Fjc%2Fshow-ref-direct-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1684/john-cai/jc/show-ref-direct-v2
Pull-Request: https://github.com/git/git/pull/1684

Range-diff vs v1:

 -:  ----------- > 1:  6adc9dd26da refs: keep track of unresolved reference value in iterator
 -:  ----------- > 2:  b60e78560e0 refs: add referent to each_repo_ref_fn
 1:  c32572a8d0b ! 3:  a9e6644327a show-ref: add --unresolved option
     @@ Metadata
      Author: John Cai <johncai86@gmail.com>
      
       ## Commit message ##
     -    show-ref: add --unresolved option
     +    show-ref: add --symbolic-name option
      
          For reftable development, it would be handy to have a tool to provide
          the direct value of any ref whether it be a symbolic ref or not.
          Currently there is git-symbolic-ref, which only works for symbolic refs,
     -    and git-rev-parse, which will resolve the ref. Let's add a --unresolved
     -    option that will only take one ref and return whatever it points to
     -    without dereferencing it.
     +    and git-rev-parse, which will resolve the ref. Let's teach show-ref a
     +    --symbolic-name option that will cause git-show-ref(1) to print out the
     +    value symbolic references points to.
      
          Signed-off-by: John Cai <johncai86@gmail.com>
      
       ## Documentation/git-show-ref.txt ##
      @@ Documentation/git-show-ref.txt: SYNOPSIS
     + [verse]
     + 'git show-ref' [--head] [-d | --dereference]
     + 	     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
     +-	     [--heads] [--] [<pattern>...]
     ++	     [--heads] [--symbolic-name] [--] [<pattern>...]
     + 'git show-ref' --verify [-q | --quiet] [-d | --dereference]
     + 	     [-s | --hash[=<n>]] [--abbrev[=<n>]]
       	     [--] [<ref>...]
     - 'git show-ref' --exclude-existing[=<pattern>]
     - 'git show-ref' --exists <ref>
     -+'git show-ref' --unresolved <ref>
     - 
     - DESCRIPTION
     - -----------
      @@ Documentation/git-show-ref.txt: OPTIONS
     - 	it does, 2 if it is missing, and 1 in case looking up the reference
     - 	failed with an error other than the reference being missing.
     + 	Dereference tags into object IDs as well. They will be shown with `^{}`
     + 	appended.
     + 
     ++--symbolic-name::
     ++
     ++	Print out the value the reference points to without dereferencing. This
     ++	is useful to know the reference that a symbolic ref is pointing to.
     ++
     + -s::
     + --hash[=<n>]::
     + 
     +@@ Documentation/git-show-ref.txt: $ git show-ref --heads --hash
     + ...
     + -----------------------------------------------------------------------------
       
     -+--unresolved::
     ++When using `--symbolic-name`, the output is in the format:
     ++
     ++-----------
     ++<oid> SP <ref> SP <symbolic-name>
     ++-----------
      +
     -+	Prints out what the reference points to without resolving it. Returns
     -+	an exit code of 0 if it does, 2 if it is missing, and 1 in case looking
     -+	up the reference failed with an error other than the reference being
     -+	missing.
     ++For example,
      +
     - --abbrev[=<n>]::
     ++-----------------------------------------------------------------------------
     ++$ git show-ref --symbolic-name
     ++b75428bae1d090f60bdd4b67185f814bc8f0819d refs/heads/SYMBOLIC_REF ref:refs/heads/main
     ++...
     ++-----------------------------------------------------------------------------
     ++
     + EXAMPLES
     + --------
       
     - 	Abbreviate the object name.  When using `--hash`, you do
      
       ## builtin/show-ref.c ##
     -@@ builtin/show-ref.c: static const char * const show_ref_usage[] = {
     +@@
     + static const char * const show_ref_usage[] = {
     + 	N_("git show-ref [--head] [-d | --dereference]\n"
     + 	   "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
     +-	   "             [--heads] [--] [<pattern>...]"),
     ++	   "             [--heads] [--symbolic-name] [--] [<pattern>...]"),
     + 	N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
     + 	   "             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
       	   "             [--] [<ref>...]"),
     - 	N_("git show-ref --exclude-existing[=<pattern>]"),
     - 	N_("git show-ref --exists <ref>"),
     -+	N_("git show-ref --unresolved <ref>"),
     - 	NULL
     +@@ builtin/show-ref.c: struct show_one_options {
     + 	int hash_only;
     + 	int abbrev;
     + 	int deref_tags;
     ++	int symbolic_name;
       };
       
     -@@ builtin/show-ref.c: static int cmd_show_ref__patterns(const struct patterns_options *opts,
     - 	return 0;
     - }
     + static void show_one(const struct show_one_options *opts,
     +-		     const char *refname, const struct object_id *oid)
     ++		     const char *refname,
     ++		     const char *referent,
     ++		     const struct object_id *oid, const int is_symref)
     + {
     + 	const char *hex;
     + 	struct object_id peeled;
     +@@ builtin/show-ref.c: static void show_one(const struct show_one_options *opts,
     + 	hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
     + 	if (opts->hash_only)
     + 		printf("%s\n", hex);
     +-	else
     ++	else if (opts->symbolic_name & is_symref) {
     ++		printf("%s %s ref:%s\n", hex, refname, referent);
     ++	} else
     + 		printf("%s %s\n", hex, refname);
       
     --static int cmd_show_ref__exists(const char **refs)
     -+static int cmd_show_ref__raw(const char **refs, int show)
     + 	if (!opts->deref_tags)
     +@@ builtin/show-ref.c: struct show_ref_data {
     + 	int show_head;
     + };
     + 
     +-static int show_ref(const char *refname, const struct object_id *oid,
     +-		    int flag UNUSED, void *cbdata)
     ++static int show_ref_referent(struct repository *repo UNUSED,
     ++			     const char *refname,
     ++			     const char *referent,
     ++			     const struct object_id *oid,
     ++			     int flag, void *cbdata)
       {
     --	struct strbuf unused_referent = STRBUF_INIT;
     --	struct object_id unused_oid;
     --	unsigned int unused_type;
     -+	struct strbuf referent = STRBUF_INIT;
     -+	struct object_id oid;
     -+	unsigned int type;
     - 	int failure_errno = 0;
     - 	const char *ref;
     - 	int ret = 0;
     -@@ builtin/show-ref.c: static int cmd_show_ref__exists(const char **refs)
     - 		die("--exists requires exactly one reference");
     - 
     - 	if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
     --			      &unused_oid, &unused_referent, &unused_type,
     -+			      &oid, &referent, &type,
     - 			      &failure_errno)) {
     - 		if (failure_errno == ENOENT || failure_errno == EISDIR) {
     - 			error(_("reference does not exist"));
     -@@ builtin/show-ref.c: static int cmd_show_ref__exists(const char **refs)
     - 		goto out;
     - 	}
     + 	struct show_ref_data *data = cbdata;
       
     -+		if (!show)
     -+			goto out;
     -+
     -+		if (type & REF_ISSYMREF)
     -+			printf("ref: %s\n", referent.buf);
     -+		else
     -+			printf("ref: %s\n", oid_to_hex(&oid));
     -+
     - out:
     --	strbuf_release(&unused_referent);
     -+	strbuf_release(&referent);
     - 	return ret;
     +@@ builtin/show-ref.c: static int show_ref(const char *refname, const struct object_id *oid,
     + match:
     + 	data->found_match++;
     + 
     +-	show_one(data->show_one_opts, refname, oid);
     ++	show_one(data->show_one_opts, refname, referent, oid, flag & REF_ISSYMREF);
     + 
     + 	return 0;
       }
       
     ++static int show_ref(const char *refname, const struct object_id *oid,
     ++		    int flag, void *cbdata)
     ++{
     ++	return show_ref_referent(NULL, refname, NULL, oid, flag, cbdata);
     ++}
     ++
     + static int add_existing(const char *refname,
     + 			const struct object_id *oid UNUSED,
     + 			int flag UNUSED, void *cbdata)
     +@@ builtin/show-ref.c: static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
     + 
     + 	while (*refs) {
     + 		struct object_id oid;
     ++		int flags = 0;
     + 
     + 		if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
     +-		    !read_ref(*refs, &oid)) {
     +-			show_one(show_one_opts, *refs, &oid);
     ++		    !read_ref_full(*refs, 0, &oid, &flags)) {
     ++			show_one(show_one_opts, *refs, NULL, &oid, flags & REF_ISSYMREF);
     + 		}
     + 		else if (!show_one_opts->quiet)
     + 			die("'%s' - not a valid ref", *refs);
     +@@ builtin/show-ref.c: static int cmd_show_ref__patterns(const struct patterns_options *opts,
     + 		head_ref(show_ref, &show_ref_data);
     + 	if (opts->heads_only || opts->tags_only) {
     + 		if (opts->heads_only)
     +-			for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
     ++			for_each_ref_all("refs/heads/", show_ref_referent, &show_ref_data);
     + 		if (opts->tags_only)
     +-			for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
     ++			for_each_ref_all("refs/tags/", show_ref_referent, &show_ref_data);
     + 	} else {
     +-		for_each_ref(show_ref, &show_ref_data);
     ++		for_each_ref_all("", show_ref_referent, &show_ref_data);
     + 	}
     + 	if (!show_ref_data.found_match)
     + 		return 1;
      @@ builtin/show-ref.c: int cmd_show_ref(int argc, const char **argv, const char *prefix)
     - 	struct exclude_existing_options exclude_existing_opts = {0};
     - 	struct patterns_options patterns_opts = {0};
     - 	struct show_one_options show_one_opts = {0};
     --	int verify = 0, exists = 0;
     -+	int verify = 0, exists = 0, unresolved = 0;
     - 	const struct option show_ref_options[] = {
       		OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
       		OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
       		OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
     -+		OPT_BOOL(0, "unresolved", &unresolved, N_("print out unresolved value of reference")),
     ++		OPT_BOOL(0, "symbolic-name", &show_one_opts.symbolic_name, N_("print out symbolic reference values")),
       		OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
       			    "requires exact ref path")),
       		OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,
     -@@ builtin/show-ref.c: int cmd_show_ref(int argc, const char **argv, const char *prefix)
     - 	argc = parse_options(argc, argv, prefix, show_ref_options,
     - 			     show_ref_usage, 0);
     - 
     --	die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
     -+	die_for_incompatible_opt4(exclude_existing_opts.enabled, "--exclude-existing",
     - 				  verify, "--verify",
     --				  exists, "--exists");
     -+				  exists, "--exists",
     -+				  unresolved, "--unresolved");
     - 
     - 	if (exclude_existing_opts.enabled)
     - 		return cmd_show_ref__exclude_existing(&exclude_existing_opts);
     - 	else if (verify)
     - 		return cmd_show_ref__verify(&show_one_opts, argv);
     --	else if (exists)
     --		return cmd_show_ref__exists(argv);
     -+	else if (exists || unresolved)
     -+		return cmd_show_ref__raw(argv, unresolved);
     - 	else
     - 		return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);
     - }
      
     - ## t/t1403-show-ref.sh ##
     -@@ t/t1403-show-ref.sh: test_expect_success 'show-ref sub-modes are mutually exclusive' '
     - 	test_must_fail git show-ref --exclude-existing --exists 2>err &&
     - 	grep "exclude-existing" err &&
     - 	grep "exists" err &&
     -+	grep "cannot be used together" err &&
     -+
     -+	test_must_fail git show-ref --exclude-existing --unresolved 2>err &&
     -+	grep "exclude-existing" err &&
     -+	grep "unresolved" err &&
     -+	grep "cannot be used together" err &&
     + ## refs.c ##
     +@@ refs.c: int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat
     + 				    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
     + }
     + 
     ++int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data)
     ++{
     ++	return do_for_each_repo_ref(the_repository, prefix, fn, 0,
     ++				    0, cb_data);
     ++}
      +
     -+	test_must_fail git show-ref --verify --unresolved 2>err &&
     -+	grep "verify" err &&
     -+	grep "unresolved" err &&
     - 	grep "cannot be used together" err
     - '
     + int for_each_namespaced_ref(const char **exclude_patterns,
     + 			    each_ref_fn fn, void *cb_data)
     + {
     +
     + ## refs.h ##
     +@@ refs.h: int refs_for_each_branch_ref(struct ref_store *refs,
     + 			     each_ref_fn fn, void *cb_data);
     + int refs_for_each_remote_ref(struct ref_store *refs,
     + 			     each_ref_fn fn, void *cb_data);
     +-
     + /* just iterates the head ref. */
     + int head_ref(each_ref_fn fn, void *cb_data);
     + 
     +@@ refs.h: int for_each_tag_ref(each_ref_fn fn, void *cb_data);
     + int for_each_branch_ref(each_ref_fn fn, void *cb_data);
     + int for_each_remote_ref(each_ref_fn fn, void *cb_data);
     + int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
     ++int for_each_ref_all(const char *prefix, each_repo_ref_fn fn, void *cb_data);
       
     + /* iterates all refs that match the specified glob pattern. */
     + int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
     +
     + ## t/t1403-show-ref.sh ##
      @@ t/t1403-show-ref.sh: test_expect_success '--exists with existing special ref' '
       	git show-ref --exists FETCH_HEAD
       '
       
     -+test_expect_success '--unresolved with existing reference' '
     ++test_expect_success '--symbolic-name with a non symbolic ref' '
      +	commit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&
      +	cat >expect <<-EOF &&
     -+	ref: $commit_oid
     -+	EOF
     -+	git show-ref --unresolved refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&
     -+	test_cmp expect actual
     -+'
     -+
     -+test_expect_success '--unresolved with symbolic ref' '
     -+	test_when_finished "git symbolic-ref -d SYMBOLIC_REF_A" &&
     -+	cat >expect <<-EOF &&
     -+	ref: refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
     ++	$commit_oid refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      +	EOF
     -+	git symbolic-ref SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
     -+	git show-ref --unresolved SYMBOLIC_REF_A >actual &&
     ++	git show-ref --symbolic-name refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME >actual &&
      +	test_cmp expect actual
      +'
      +
     -+test_expect_success '--unresolved with nonexistent object ID' '
     -+	oid=$(test_oid 002) &&
     -+	test-tool ref-store main update-ref msg refs/heads/missing-oid-2 $oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
     ++test_expect_success '--symbolic-name with symbolic ref' '
     ++	test_when_finished "git symbolic-ref -d refs/heads/SYMBOLIC_REF_A" &&
     ++	commit_oid=$(git rev-parse refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME) &&
      +	cat >expect <<-EOF &&
     -+	ref: $oid
     ++	$commit_oid refs/heads/SYMBOLIC_REF_A ref:refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      +	EOF
     -+	git show-ref --unresolved refs/heads/missing-oid-2 >actual &&
     ++	git symbolic-ref refs/heads/SYMBOLIC_REF_A refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
     ++	git show-ref --symbolic-name SYMBOLIC_REF_A >actual &&
      +	test_cmp expect actual
      +'
     -+
     -+test_expect_success '--unresolved with nonexistent reference' '
     -+	cat >expect <<-EOF &&
     -+	error: reference does not exist
     -+	EOF
     -+	test_expect_code 2 git show-ref --unresolved refs/heads/not-exist 2>err &&
     -+	test_cmp expect err
     -+'
      +
       test_done

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* Re: [PATCH v6 0/3] reftable/stack: use geometric table compaction
  2024-04-08 16:16  3%         ` [PATCH v6 " Justin Tobler via GitGitGadget
@ 2024-04-08 16:20  0%           ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-08 16:20 UTC (permalink / raw)
  To: Justin Tobler via GitGitGadget
  Cc: git, Karthik Nayak, Han-Wen Nienhuys, Justin Tobler

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

On Mon, Apr 08, 2024 at 04:16:52PM +0000, Justin Tobler via GitGitGadget wrote:
> Hello again,
> 
> This is the sixth version my patch series that refactors the reftable
> compaction strategy to instead follow a geometric sequence. Changes compared
> to v5:
> 
>  * Reworded commit message to more clearly explain that the already existing
>    configuration to disable auto-compaction is being exposed to callers of
>    the library.
>  * Simplified expression to set the disable_auto_compact configuration.
> 
> Thanks for taking a look!

Thanks, this version looks good to me!

Patrick

> -Justin
> 
> Justin Tobler (3):
>   reftable/stack: expose option to disable auto-compaction
>   reftable/stack: add env to disable autocompaction
>   reftable/stack: use geometric table compaction
> 
>  refs/reftable-backend.c    |   3 +
>  reftable/reftable-writer.h |   3 +
>  reftable/stack.c           | 125 +++++++++++++++++++------------------
>  reftable/stack.h           |   4 --
>  reftable/stack_test.c      |  77 ++++++-----------------
>  t/t0610-reftable-basics.sh |  71 ++++++++++++++++-----
>  6 files changed, 145 insertions(+), 138 deletions(-)
> 
> 
> base-commit: 4b32163adf4863c6df3bb6b43540fa2ca3494e28
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v6
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v6
> Pull-Request: https://github.com/gitgitgadget/git/pull/1683
> 
> Range-diff vs v5:
> 
>  1:  a7011dbc6aa ! 1:  9c8f6b336ec reftable/stack: allow disabling of auto-compaction
>      @@ Metadata
>       Author: Justin Tobler <jltobler@gmail.com>
>       
>        ## Commit message ##
>      -    reftable/stack: allow disabling of auto-compaction
>      +    reftable/stack: expose option to disable auto-compaction
>      +
>      +    The reftable stack already has a variable to configure whether or not to
>      +    run auto-compaction, but it is inaccessible to users of the library.
>      +    There exist use cases where a caller may want to have more control over
>      +    auto-compaction.
>       
>           Move the `disable_auto_compact` option into `reftable_write_options` to
>      -    allow a stack to be configured with auto-compaction disabled. In a
>      -    subsequent commit, this is used to disable auto-compaction when a
>      -    specific environment variable is set.
>      +    allow external callers to disable auto-compaction. This will be used in
>      +    a subsequent commit.
>       
>           Signed-off-by: Justin Tobler <jltobler@gmail.com>
>       
>  2:  7c4fe0e9ec5 ! 2:  c7bc7346540 reftable/stack: add env to disable autocompaction
>      @@ refs/reftable-backend.c
>        
>        /*
>       @@ refs/reftable-backend.c: static struct ref_store *reftable_be_init(struct repository *repo,
>      + 	refs->write_options.block_size = 4096;
>        	refs->write_options.hash_id = repo->hash_algo->format_id;
>        	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
>      ++	refs->write_options.disable_auto_compact =
>      ++		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
>        
>      -+	if (!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
>      -+		refs->write_options.disable_auto_compact = 1;
>      -+
>        	/*
>        	 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
>      - 	 * This stack contains both the shared and the main worktree refs.
>       
>        ## t/t0610-reftable-basics.sh ##
>       @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause auto-compaction' '
>  3:  8f124acf0f8 = 3:  d75494a88b0 reftable/stack: use geometric table compaction
> 
> -- 
> gitgitgadget

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

^ permalink raw reply	[relevance 0%]

* [PATCH v6 0/3] reftable/stack: use geometric table compaction
  2024-04-04 18:29  3%       ` [PATCH v5 0/3] " Justin Tobler via GitGitGadget
  2024-04-08  6:12  0%         ` Patrick Steinhardt
@ 2024-04-08 16:16  3%         ` Justin Tobler via GitGitGadget
  2024-04-08 16:20  0%           ` Patrick Steinhardt
  1 sibling, 1 reply; 200+ results
From: Justin Tobler via GitGitGadget @ 2024-04-08 16:16 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Karthik Nayak, Han-Wen Nienhuys,
	Justin Tobler

Hello again,

This is the sixth version my patch series that refactors the reftable
compaction strategy to instead follow a geometric sequence. Changes compared
to v5:

 * Reworded commit message to more clearly explain that the already existing
   configuration to disable auto-compaction is being exposed to callers of
   the library.
 * Simplified expression to set the disable_auto_compact configuration.

Thanks for taking a look!

-Justin

Justin Tobler (3):
  reftable/stack: expose option to disable auto-compaction
  reftable/stack: add env to disable autocompaction
  reftable/stack: use geometric table compaction

 refs/reftable-backend.c    |   3 +
 reftable/reftable-writer.h |   3 +
 reftable/stack.c           | 125 +++++++++++++++++++------------------
 reftable/stack.h           |   4 --
 reftable/stack_test.c      |  77 ++++++-----------------
 t/t0610-reftable-basics.sh |  71 ++++++++++++++++-----
 6 files changed, 145 insertions(+), 138 deletions(-)


base-commit: 4b32163adf4863c6df3bb6b43540fa2ca3494e28
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/1683

Range-diff vs v5:

 1:  a7011dbc6aa ! 1:  9c8f6b336ec reftable/stack: allow disabling of auto-compaction
     @@ Metadata
      Author: Justin Tobler <jltobler@gmail.com>
      
       ## Commit message ##
     -    reftable/stack: allow disabling of auto-compaction
     +    reftable/stack: expose option to disable auto-compaction
     +
     +    The reftable stack already has a variable to configure whether or not to
     +    run auto-compaction, but it is inaccessible to users of the library.
     +    There exist use cases where a caller may want to have more control over
     +    auto-compaction.
      
          Move the `disable_auto_compact` option into `reftable_write_options` to
     -    allow a stack to be configured with auto-compaction disabled. In a
     -    subsequent commit, this is used to disable auto-compaction when a
     -    specific environment variable is set.
     +    allow external callers to disable auto-compaction. This will be used in
     +    a subsequent commit.
      
          Signed-off-by: Justin Tobler <jltobler@gmail.com>
      
 2:  7c4fe0e9ec5 ! 2:  c7bc7346540 reftable/stack: add env to disable autocompaction
     @@ refs/reftable-backend.c
       
       /*
      @@ refs/reftable-backend.c: static struct ref_store *reftable_be_init(struct repository *repo,
     + 	refs->write_options.block_size = 4096;
       	refs->write_options.hash_id = repo->hash_algo->format_id;
       	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
     ++	refs->write_options.disable_auto_compact =
     ++		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
       
     -+	if (!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
     -+		refs->write_options.disable_auto_compact = 1;
     -+
       	/*
       	 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
     - 	 * This stack contains both the shared and the main worktree refs.
      
       ## t/t0610-reftable-basics.sh ##
      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause auto-compaction' '
 3:  8f124acf0f8 = 3:  d75494a88b0 reftable/stack: use geometric table compaction

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [PATCH] Makefile(s): avoid recipe prefix in conditional statements
  @ 2024-04-08 15:51  3% ` Taylor Blau
  0 siblings, 0 replies; 200+ results
From: Taylor Blau @ 2024-04-08 15:51 UTC (permalink / raw)
  To: git; +Cc: Dario Gjorgjevski, Jeff King, Junio C Hamano

In GNU Make commit 07fcee35 ([SV 64815] Recipe lines cannot contain
conditional statements, 2023-05-22) and following, conditional
statements may no longer be preceded by a tab character (which Make
refers to as the recipe prefix).

There are a handful of spots in our various Makefile(s) which will break
in a future release of Make containing 07fcee35. For instance, trying to
compile the pre-image of this patch with the tip of make.git results in
the following:

    $ make -v | head -1 && make
    GNU Make 4.4.90
    config.mak.uname:842: *** missing 'endif'.  Stop.

The kernel addressed this issue in 82175d1f9430 (kbuild: Replace tabs
with spaces when followed by conditionals, 2024-01-28). Address the
issues in Git's tree by applying the same strategy.

When a conditional word (ifeq, ifneq, ifdef, etc.) is preceded by one or
more tab characters, replace each tab character with 8 space characters
with the following:

    find . -type f -not -path './.git/*' -name Makefile -or -name '*.mak' |
      xargs perl -i -pe '
        s/(\t+)(ifn?eq|ifn?def|else|endif)/" " x (length($1) * 8) . $2/ge unless /\\$/
      '

The "unless /\\$/" removes any false-positives (like "\telse \"
appearing within a shell script as part of a recipe).

After doing so, Git compiles on newer versions of Make:

    $ make -v | head -1 && make
    GNU Make 4.4.90
    GIT_VERSION = 2.44.0.414.gfac1dc44ca9
    [...]

    $ echo $?
    0

Reported-by: Dario Gjorgjevski <dario.gjorgjevski@gmail.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 Makefile          |  96 ++++++++++++++---------------
 config.mak.uname  | 154 +++++++++++++++++++++++-----------------------
 git-gui/Makefile  |  16 ++---
 gitk-git/Makefile |   4 +-
 4 files changed, 135 insertions(+), 135 deletions(-)

diff --git a/Makefile b/Makefile
index c43c1bd1a05..ac603fb7678 100644
--- a/Makefile
+++ b/Makefile
@@ -1557,23 +1557,23 @@ ifneq (,$(SOCKLEN_T))
 endif
 
 ifeq ($(uname_S),Darwin)
-	ifndef NO_FINK
-		ifeq ($(shell test -d /sw/lib && echo y),y)
+        ifndef NO_FINK
+                ifeq ($(shell test -d /sw/lib && echo y),y)
 			BASIC_CFLAGS += -I/sw/include
 			BASIC_LDFLAGS += -L/sw/lib
-		endif
-	endif
-	ifndef NO_DARWIN_PORTS
-		ifeq ($(shell test -d /opt/local/lib && echo y),y)
+                endif
+        endif
+        ifndef NO_DARWIN_PORTS
+                ifeq ($(shell test -d /opt/local/lib && echo y),y)
 			BASIC_CFLAGS += -I/opt/local/include
 			BASIC_LDFLAGS += -L/opt/local/lib
-		endif
-	endif
-	ifndef NO_APPLE_COMMON_CRYPTO
+                endif
+        endif
+        ifndef NO_APPLE_COMMON_CRYPTO
 		NO_OPENSSL = YesPlease
 		APPLE_COMMON_CRYPTO = YesPlease
 		COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
-	endif
+        endif
 	PTHREAD_LIBS =
 endif
 
@@ -1612,23 +1612,23 @@ ifdef NO_CURL
 	REMOTE_CURL_NAMES =
 	EXCLUDED_PROGRAMS += git-http-fetch git-http-push
 else
-	ifdef CURLDIR
+        ifdef CURLDIR
 		# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
 		CURL_CFLAGS = -I$(CURLDIR)/include
 		CURL_LIBCURL = $(call libpath_template,$(CURLDIR)/$(lib))
-	else
+        else
 		CURL_CFLAGS =
 		CURL_LIBCURL =
-	endif
+        endif
 
-	ifndef CURL_LDFLAGS
+        ifndef CURL_LDFLAGS
 		CURL_LDFLAGS = $(eval CURL_LDFLAGS := $$(shell $$(CURL_CONFIG) --libs))$(CURL_LDFLAGS)
-	endif
+        endif
 	CURL_LIBCURL += $(CURL_LDFLAGS)
 
-	ifndef CURL_CFLAGS
+        ifndef CURL_CFLAGS
 		CURL_CFLAGS = $(eval CURL_CFLAGS := $$(shell $$(CURL_CONFIG) --cflags))$(CURL_CFLAGS)
-	endif
+        endif
 	BASIC_CFLAGS += $(CURL_CFLAGS)
 
 	REMOTE_CURL_PRIMARY = git-remote-http$X
@@ -1636,29 +1636,29 @@ else
 	REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
 	PROGRAM_OBJS += http-fetch.o
 	PROGRAMS += $(REMOTE_CURL_NAMES)
-	ifndef NO_EXPAT
+        ifndef NO_EXPAT
 		PROGRAM_OBJS += http-push.o
-	endif
+        endif
 	curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
-	ifeq "$(curl_check)" "072200"
+        ifeq "$(curl_check)" "072200"
 		USE_CURL_FOR_IMAP_SEND = YesPlease
-	endif
-	ifdef USE_CURL_FOR_IMAP_SEND
+        endif
+        ifdef USE_CURL_FOR_IMAP_SEND
 		BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND
 		IMAP_SEND_BUILDDEPS = http.o
 		IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
-	endif
-	ifndef NO_EXPAT
-		ifdef EXPATDIR
+        endif
+        ifndef NO_EXPAT
+                ifdef EXPATDIR
 			BASIC_CFLAGS += -I$(EXPATDIR)/include
 			EXPAT_LIBEXPAT = $(call libpath_template,$(EXPATDIR)/$(lib)) -lexpat
-		else
+                else
 			EXPAT_LIBEXPAT = -lexpat
-		endif
-		ifdef EXPAT_NEEDS_XMLPARSE_H
+                endif
+                ifdef EXPAT_NEEDS_XMLPARSE_H
 			BASIC_CFLAGS += -DEXPAT_NEEDS_XMLPARSE_H
-		endif
-	endif
+                endif
+        endif
 endif
 IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
@@ -1670,15 +1670,15 @@ EXTLIBS += -lz
 
 ifndef NO_OPENSSL
 	OPENSSL_LIBSSL = -lssl
-	ifdef OPENSSLDIR
+        ifdef OPENSSLDIR
 		BASIC_CFLAGS += -I$(OPENSSLDIR)/include
 		OPENSSL_LINK = $(call libpath_template,$(OPENSSLDIR)/$(lib))
-	else
+        else
 		OPENSSL_LINK =
-	endif
-	ifdef NEEDS_CRYPTO_WITH_SSL
+        endif
+        ifdef NEEDS_CRYPTO_WITH_SSL
 		OPENSSL_LIBSSL += -lcrypto
-	endif
+        endif
 else
 	BASIC_CFLAGS += -DNO_OPENSSL
 	OPENSSL_LIBSSL =
@@ -1696,18 +1696,18 @@ ifdef APPLE_COMMON_CRYPTO
 endif
 endif
 ifndef NO_ICONV
-	ifdef NEEDS_LIBICONV
-		ifdef ICONVDIR
+        ifdef NEEDS_LIBICONV
+                ifdef ICONVDIR
 			BASIC_CFLAGS += -I$(ICONVDIR)/include
 			ICONV_LINK = $(call libpath_template,$(ICONVDIR)/$(lib))
-		else
+                else
 			ICONV_LINK =
-		endif
-		ifdef NEEDS_LIBINTL_BEFORE_LIBICONV
+                endif
+                ifdef NEEDS_LIBINTL_BEFORE_LIBICONV
 			ICONV_LINK += -lintl
-		endif
+                endif
 		EXTLIBS += $(ICONV_LINK) -liconv
-	endif
+        endif
 endif
 ifdef ICONV_OMITS_BOM
 	BASIC_CFLAGS += -DICONV_OMITS_BOM
@@ -1828,10 +1828,10 @@ ifdef NO_MMAP
 	COMPAT_CFLAGS += -DNO_MMAP
 	COMPAT_OBJS += compat/mmap.o
 else
-	ifdef USE_WIN32_MMAP
+        ifdef USE_WIN32_MMAP
 		COMPAT_CFLAGS += -DUSE_WIN32_MMAP
 		COMPAT_OBJS += compat/win32mmap.o
-	endif
+        endif
 endif
 ifdef MMAP_PREVENTS_DELETE
 	BASIC_CFLAGS += -DMMAP_PREVENTS_DELETE
@@ -1956,11 +1956,11 @@ else
 	BASIC_CFLAGS += -DSHA1_DC
 	LIB_OBJS += sha1dc_git.o
 ifdef DC_SHA1_EXTERNAL
-	ifdef DC_SHA1_SUBMODULE
-		ifneq ($(DC_SHA1_SUBMODULE),auto)
+        ifdef DC_SHA1_SUBMODULE
+                ifneq ($(DC_SHA1_SUBMODULE),auto)
 $(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
-		endif
-	endif
+                endif
+        endif
 	BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
 	EXTLIBS += -lsha1detectcoll
 else
diff --git a/config.mak.uname b/config.mak.uname
index d0dcca2ec55..b11408e67b4 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -65,9 +65,9 @@ ifeq ($(uname_S),Linux)
 	HAVE_PLATFORM_PROCINFO = YesPlease
 	COMPAT_OBJS += compat/linux/procinfo.o
 	# centos7/rhel7 provides gcc 4.8.5 and zlib 1.2.7.
-	ifneq ($(findstring .el7.,$(uname_R)),)
+        ifneq ($(findstring .el7.,$(uname_R)),)
 		BASIC_CFLAGS += -std=c99
-	endif
+        endif
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	HAVE_ALLOCA_H = YesPlease
@@ -95,13 +95,13 @@ ifeq ($(uname_S),UnixWare)
 	NO_MEMMEM = YesPlease
 endif
 ifeq ($(uname_S),SCO_SV)
-	ifeq ($(uname_R),3.2)
+        ifeq ($(uname_R),3.2)
 		CFLAGS = -O2
-	endif
-	ifeq ($(uname_R),5)
+        endif
+        ifeq ($(uname_R),5)
 		CC = cc
 		BASIC_CFLAGS += -Kthread
-	endif
+        endif
 	NEEDS_SOCKET = YesPlease
 	NEEDS_NSL = YesPlease
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
@@ -124,19 +124,19 @@ ifeq ($(uname_S),Darwin)
 	# - MacOS 10.0.* and MacOS 10.1.0 = Darwin 1.*
 	# - MacOS 10.x.* = Darwin (x+4).* for (1 <= x)
 	# i.e. "begins with [15678] and a dot" means "10.4.* or older".
-	ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
+        ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
 		OLD_ICONV = UnfortunatelyYes
 		NO_APPLE_COMMON_CRYPTO = YesPlease
-	endif
-	ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
+        endif
+        ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
 		NO_STRLCPY = YesPlease
-	endif
-	ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
+        endif
+        ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
 		HAVE_GETDELIM = YesPlease
-	endif
-	ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
+        endif
+        ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
 		OPEN_RETURNS_EINTR = UnfortunatelyYes
-	endif
+        endif
 	NO_MEMMEM = YesPlease
 	USE_ST_TIMESPEC = YesPlease
 	HAVE_DEV_TTY = YesPlease
@@ -152,12 +152,12 @@ ifeq ($(uname_S),Darwin)
 	# Workaround for `gettext` being keg-only and not even being linked via
 	# `brew link --force gettext`, should be obsolete as of
 	# https://github.com/Homebrew/homebrew-core/pull/53489
-	ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
+        ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
 		BASIC_CFLAGS += -I/usr/local/include -I/usr/local/opt/gettext/include
 		BASIC_LDFLAGS += -L/usr/local/lib -L/usr/local/opt/gettext/lib
-		ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
+                ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
 			MSGFMT = /usr/local/opt/gettext/bin/msgfmt
-		endif
+                endif
 	# On newer ARM-based machines the default installation path has changed to
 	# /opt/homebrew. Include it in our search paths so that the user does not
 	# have to configure this manually.
@@ -165,22 +165,22 @@ ifeq ($(uname_S),Darwin)
 	# Note that we do not employ the same workaround as above where we manually
 	# add gettext. The issue was fixed more than three years ago by now, and at
 	# that point there haven't been any ARM-based Macs yet.
-	else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
+        else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
 		BASIC_CFLAGS += -I/opt/homebrew/include
 		BASIC_LDFLAGS += -L/opt/homebrew/lib
-		ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
+                ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
 			MSGFMT = /opt/homebrew/bin/msgfmt
-		endif
-	endif
+                endif
+        endif
 
 	# The builtin FSMonitor on MacOS builds upon Simple-IPC.  Both require
 	# Unix domain sockets and PThreads.
-	ifndef NO_PTHREADS
-	ifndef NO_UNIX_SOCKETS
+        ifndef NO_PTHREADS
+        ifndef NO_UNIX_SOCKETS
 	FSMONITOR_DAEMON_BACKEND = darwin
 	FSMONITOR_OS_SETTINGS = darwin
-	endif
-	endif
+        endif
+        endif
 
 	BASIC_LDFLAGS += -framework CoreServices
 endif
@@ -196,7 +196,7 @@ ifeq ($(uname_S),SunOS)
 	NO_REGEX = YesPlease
 	NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
 	HAVE_DEV_TTY = YesPlease
-	ifeq ($(uname_R),5.6)
+        ifeq ($(uname_R),5.6)
 		SOCKLEN_T = int
 		NO_HSTRERROR = YesPlease
 		NO_IPV6 = YesPlease
@@ -206,8 +206,8 @@ ifeq ($(uname_S),SunOS)
 		NO_STRLCPY = YesPlease
 		NO_STRTOUMAX = YesPlease
 		GIT_TEST_CMP = cmp
-	endif
-	ifeq ($(uname_R),5.7)
+        endif
+        ifeq ($(uname_R),5.7)
 		NEEDS_RESOLV = YesPlease
 		NO_IPV6 = YesPlease
 		NO_SOCKADDR_STORAGE = YesPlease
@@ -216,25 +216,25 @@ ifeq ($(uname_S),SunOS)
 		NO_STRLCPY = YesPlease
 		NO_STRTOUMAX = YesPlease
 		GIT_TEST_CMP = cmp
-	endif
-	ifeq ($(uname_R),5.8)
+        endif
+        ifeq ($(uname_R),5.8)
 		NO_UNSETENV = YesPlease
 		NO_SETENV = YesPlease
 		NO_STRTOUMAX = YesPlease
 		GIT_TEST_CMP = cmp
-	endif
-	ifeq ($(uname_R),5.9)
+        endif
+        ifeq ($(uname_R),5.9)
 		NO_UNSETENV = YesPlease
 		NO_SETENV = YesPlease
 		NO_STRTOUMAX = YesPlease
 		GIT_TEST_CMP = cmp
-	endif
+        endif
 	INSTALL = /usr/ucb/install
 	TAR = gtar
 	BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
 endif
 ifeq ($(uname_O),Cygwin)
-	ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
+        ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
 		NO_D_TYPE_IN_DIRENT = YesPlease
 		NO_STRCASESTR = YesPlease
 		NO_MEMMEM = YesPlease
@@ -245,9 +245,9 @@ ifeq ($(uname_O),Cygwin)
 		# On some boxes NO_MMAP is needed, and not so elsewhere.
 		# Try commenting this out if you suspect MMAP is more efficient
 		NO_MMAP = YesPlease
-	else
+        else
 		NO_REGEX = UnfortunatelyYes
-	endif
+        endif
 	HAVE_ALLOCA_H = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
@@ -263,25 +263,25 @@ ifeq ($(uname_S),FreeBSD)
 	NEEDS_LIBICONV = YesPlease
 	# Versions up to 10.1 require OLD_ICONV; 10.2 and beyond don't.
 	# A typical version string looks like "10.2-RELEASE".
-	ifeq ($(shell expr "$(uname_R)" : '[1-9]\.'),2)
+        ifeq ($(shell expr "$(uname_R)" : '[1-9]\.'),2)
 		OLD_ICONV = YesPlease
-	endif
-	ifeq ($(firstword $(subst -, ,$(uname_R))),10.0)
+        endif
+        ifeq ($(firstword $(subst -, ,$(uname_R))),10.0)
 		OLD_ICONV = YesPlease
-	endif
-	ifeq ($(firstword $(subst -, ,$(uname_R))),10.1)
+        endif
+        ifeq ($(firstword $(subst -, ,$(uname_R))),10.1)
 		OLD_ICONV = YesPlease
-	endif
+        endif
 	NO_MEMMEM = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
 	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 	USE_ST_TIMESPEC = YesPlease
-	ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
+        ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
 		PTHREAD_LIBS = -pthread
 		NO_UINTMAX_T = YesPlease
 		NO_STRTOUMAX = YesPlease
-	endif
+        endif
 	PYTHON_PATH = /usr/local/bin/python
 	PERL_PATH = /usr/local/bin/perl
 	HAVE_PATHS_H = YesPlease
@@ -317,9 +317,9 @@ ifeq ($(uname_S),MirBSD)
 	CSPRNG_METHOD = arc4random
 endif
 ifeq ($(uname_S),NetBSD)
-	ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
+        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
 		NEEDS_LIBICONV = YesPlease
-	endif
+        endif
 	BASIC_CFLAGS += -I/usr/pkg/include
 	BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
 	USE_ST_TIMESPEC = YesPlease
@@ -343,14 +343,14 @@ ifeq ($(uname_S),AIX)
 	BASIC_CFLAGS += -D_LARGE_FILES
 	FILENO_IS_A_MACRO = UnfortunatelyYes
 	NEED_ACCESS_ROOT_HANDLER = UnfortunatelyYes
-	ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
+        ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
 		NO_PTHREADS = YesPlease
-	else
+        else
 		PTHREAD_LIBS = -lpthread
-	endif
-	ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+        endif
+        ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
 		INLINE = ''
-	endif
+        endif
 	GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),GNU)
@@ -410,29 +410,29 @@ ifeq ($(uname_S),HP-UX)
 	NO_SYS_SELECT_H = YesPlease
 	SNPRINTF_RETURNS_BOGUS = YesPlease
 	NO_NSEC = YesPlease
-	ifeq ($(uname_R),B.11.00)
+        ifeq ($(uname_R),B.11.00)
 		NO_INET_NTOP = YesPlease
 		NO_INET_PTON = YesPlease
-	endif
-	ifeq ($(uname_R),B.10.20)
+        endif
+        ifeq ($(uname_R),B.10.20)
 		# Override HP-UX 11.x setting:
 		INLINE =
 		SOCKLEN_T = size_t
 		NO_PREAD = YesPlease
 		NO_INET_NTOP = YesPlease
 		NO_INET_PTON = YesPlease
-	endif
+        endif
 	GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),Windows)
 	GIT_VERSION := $(GIT_VERSION).MSVC
 	pathsep = ;
 	# Assume that this is built in Git for Windows' SDK
-	ifeq (MINGW32,$(MSYSTEM))
+        ifeq (MINGW32,$(MSYSTEM))
 		prefix = /mingw32
-	else
+        else
 		prefix = /mingw64
-	endif
+        endif
 	# Prepend MSVC 64-bit tool-chain to PATH.
 	#
 	# A regular Git Bash *does not* have cl.exe in its $PATH. As there is a
@@ -550,16 +550,16 @@ ifeq ($(uname_S),Interix)
 	NO_MKDTEMP = YesPlease
 	NO_STRTOUMAX = YesPlease
 	NO_NSEC = YesPlease
-	ifeq ($(uname_R),3.5)
+        ifeq ($(uname_R),3.5)
 		NO_INET_NTOP = YesPlease
 		NO_INET_PTON = YesPlease
 		NO_SOCKADDR_STORAGE = YesPlease
-	endif
-	ifeq ($(uname_R),5.2)
+        endif
+        ifeq ($(uname_R),5.2)
 		NO_INET_NTOP = YesPlease
 		NO_INET_PTON = YesPlease
 		NO_SOCKADDR_STORAGE = YesPlease
-	endif
+        endif
 endif
 ifeq ($(uname_S),Minix)
 	NO_IPV6 = YesPlease
@@ -579,12 +579,12 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
 	# still not compile in c89 mode, due to non-const array initializations.
 	CC = cc -c99
 	# Build down-rev compatible objects that don't use our new getopt_long.
-	ifeq ($(uname_R).$(uname_V),J06.21)
+        ifeq ($(uname_R).$(uname_V),J06.21)
 		CC += -WRVU=J06.20
-	endif
-	ifeq ($(uname_R).$(uname_V),L17.02)
+        endif
+        ifeq ($(uname_R).$(uname_V),L17.02)
 		CC += -WRVU=L16.05
-	endif
+        endif
 	# Disable all optimization, seems to result in bad code, with -O or -O2
 	# or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects
 	# abends on "git push". Needs more investigation.
@@ -651,9 +651,9 @@ ifeq ($(uname_S),OS/390)
 	NEEDS_MODE_TRANSLATION = YesPlease
 endif
 ifeq ($(uname_S),MINGW)
-	ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
+        ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
 		$(error "Building with MSys is no longer supported")
-	endif
+        endif
 	pathsep = ;
 	HAVE_ALLOCA_H = YesPlease
 	NO_PREAD = YesPlease
@@ -712,22 +712,22 @@ ifeq ($(uname_S),MINGW)
 	# Enable DEP
 	BASIC_LDFLAGS += -Wl,--nxcompat
 	# Enable ASLR (unless debugging)
-	ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+        ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
 		BASIC_LDFLAGS += -Wl,--dynamicbase
-	endif
-	ifeq (MINGW32,$(MSYSTEM))
+        endif
+        ifeq (MINGW32,$(MSYSTEM))
 		prefix = /mingw32
 		HOST_CPU = i686
 		BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
-	endif
-	ifeq (MINGW64,$(MSYSTEM))
+        endif
+        ifeq (MINGW64,$(MSYSTEM))
 		prefix = /mingw64
 		HOST_CPU = x86_64
 		BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
-	else
+        else
 		COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
 		BASIC_LDFLAGS += -Wl,--large-address-aware
-	endif
+        endif
 	CC = gcc
 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
 		-fstack-protector-strong
@@ -739,11 +739,11 @@ ifeq ($(uname_S),MINGW)
 	USE_GETTEXT_SCHEME = fallthrough
 	USE_LIBPCRE = YesPlease
 	USE_NED_ALLOCATOR = YesPlease
-	ifeq (/mingw64,$(subst 32,64,$(prefix)))
+        ifeq (/mingw64,$(subst 32,64,$(prefix)))
 		# Move system config into top-level /etc/
 		ETC_GITCONFIG = ../etc/gitconfig
 		ETC_GITATTRIBUTES = ../etc/gitattributes
-	endif
+        endif
 endif
 ifeq ($(uname_S),QNX)
 	COMPAT_CFLAGS += -DSA_RESTART=0
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 3f80435436c..667c39ed564 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -107,12 +107,12 @@ endif
 
 ifeq ($(uname_S),Darwin)
 	TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
-	ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n)
+        ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n)
 		TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish.app
-		ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n)
+                ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n)
 			TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app
-		endif
-	endif
+                endif
+        endif
 	TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app)
 endif
 
@@ -143,9 +143,9 @@ ifeq ($(exedir),$(gg_libdir))
 endif
 gg_libdir_sed_in := $(gg_libdir)
 ifeq ($(uname_S),Darwin)
-	ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
+        ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
 		GITGUI_MACOSXAPP := YesPlease
-	endif
+        endif
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
 ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
@@ -220,9 +220,9 @@ ifdef NO_MSGFMT
 	MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
 	MSGFMT ?= msgfmt
-	ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+        ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
 		MSGFMT := $(TCL_PATH) po/po2msg.sh
-	endif
+        endif
 endif
 
 msgsdir     = $(gg_libdir)/msgs
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index 5bdd52a6ebf..e1f0aff4a19 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -33,9 +33,9 @@ ifdef NO_MSGFMT
 	MSGFMT ?= $(TCL_PATH) po/po2msg.sh
 else
 	MSGFMT ?= msgfmt
-	ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+        ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
 		MSGFMT := $(TCL_PATH) po/po2msg.sh
-	endif
+        endif
 endif
 
 PO_TEMPLATE = po/gitk.pot
-- 
2.44.0.414.g38c9f58cd0c


^ permalink raw reply related	[relevance 3%]

* [PATCH v2 0/2] rebase -i: improve error message when picking merge
    2024-04-03 13:42  0% ` phillip.wood123
  2024-04-04  6:08  0% ` Patrick Steinhardt
@ 2024-04-08 14:16  3% ` Phillip Wood via GitGitGadget
  2 siblings, 0 replies; 200+ results
From: Phillip Wood via GitGitGadget @ 2024-04-08 14:16 UTC (permalink / raw)
  To: git
  Cc: Stefan Haller, Johannes Schindelin, Patrick Steinhardt,
	Rubén Justo, Phillip Wood

If the user tries to pick a merge commit error out when parsing the todo
list rather than complaining when trying to pick the commit.

Thanks to Patrick and Rubén for their comments on V1. Changes since V1:

 * Rebased to avoid a conflict with jk/core-comment-string
 * Patch 1 is a new preparatory change that plumbs 'struct replay_opts'
   through to parse_insn_line()
 * Patch 2 is updated to use is_rebase_i() rather than requiring the caller
   to pass a boolean indicating whether we're rebasing.

Phillip Wood (2):
  rebase -i: pass struct replay_opts to parse_insn_line()
  rebase -i: improve error message when picking merge

 builtin/rebase.c              | 17 +++++++----
 rebase-interactive.c          | 21 ++++++++-----
 rebase-interactive.h          |  9 ++++--
 sequencer.c                   | 57 ++++++++++++++++++++++++++++-------
 sequencer.h                   |  4 +--
 t/t3404-rebase-interactive.sh | 33 ++++++++++++++++++++
 6 files changed, 111 insertions(+), 30 deletions(-)


base-commit: 19981daefd7c147444462739375462b49412ce33
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1672%2Fphillipwood%2Frebase-reject-merges-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1672/phillipwood/rebase-reject-merges-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1672

Range-diff vs v1:

 -:  ----------- > 1:  1bcf92c6105 rebase -i: pass struct replay_opts to parse_insn_line()
 1:  7726c496385 ! 2:  fbc6746e018 rebase -i: improve error message when picking merge
     @@ Commit message
      
              exec git cherry-pick -m1 abc123
      
     -    The change is relatively straight forward but is complicated slightly as
     -    we now need to tell the parser if we're rebasing or not.
     -
          Reported-by: Stefan Haller <lists@haller-berlin.de>
          Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
      
     - ## builtin/rebase.c ##
     -@@ builtin/rebase.c: static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
     - 	else {
     - 		discard_index(&the_index);
     - 		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
     --						&todo_list))
     -+						&todo_list, 1))
     - 			BUG("unusable todo list");
     - 
     - 		ret = complete_action(the_repository, &replay, flags,
     -
     - ## rebase-interactive.c ##
     -@@ rebase-interactive.c: int edit_todo_list(struct repository *r, struct todo_list *todo_list,
     - 	 * it.  If there is an error, we do not return, because the user
     - 	 * might want to fix it in the first place. */
     - 	if (!initial)
     --		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list) |
     -+		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf,
     -+							todo_list, 1) |
     - 			file_exists(rebase_path_dropped());
     - 
     - 	if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
     -@@ rebase-interactive.c: int edit_todo_list(struct repository *r, struct todo_list *todo_list,
     - 	if (initial && new_todo->buf.len == 0)
     - 		return -3;
     - 
     --	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo)) {
     -+	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo, 1)) {
     - 		fprintf(stderr, _(edit_todo_list_advice));
     - 		return -4;
     - 	}
     -@@ rebase-interactive.c: int todo_list_check_against_backup(struct repository *r, struct todo_list *todo_
     - 	int res = 0;
     - 
     - 	if (strbuf_read_file(&backup.buf, rebase_path_todo_backup(), 0) > 0) {
     --		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup);
     -+		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup, 1);
     - 		res = todo_list_check(&backup, todo_list);
     - 	}
     - 
     -
       ## sequencer.c ##
      @@ sequencer.c: static int check_label_or_ref_arg(enum todo_command command, const char *arg)
       	return 0;
       }
       
     +-static int parse_insn_line(struct repository *r, struct replay_opts *opts UNUSED,
      +static int error_merge_commit(enum todo_command command)
      +{
      +	switch(command) {
     @@ sequencer.c: static int check_label_or_ref_arg(enum todo_command command, const
      +	}
      +}
      +
     - static int parse_insn_line(struct repository *r, struct todo_item *item,
     --			   const char *buf, const char *bol, char *eol)
     -+			   const char *buf, const char *bol, char *eol,
     -+			   int rebasing)
     ++static int parse_insn_line(struct repository *r, struct replay_opts *opts,
     + 			   struct todo_item *item, const char *buf,
     + 			   const char *bol, char *eol)
       {
     - 	struct object_id commit_oid;
     - 	char *end_of_object_name;
     -@@ sequencer.c: static int parse_insn_line(struct repository *r, struct todo_item *item,
     +@@ sequencer.c: static int parse_insn_line(struct repository *r, struct replay_opts *opts UNUSED
       		return status;
       
       	item->commit = lookup_commit_reference(r, &commit_oid);
      -	return item->commit ? 0 : -1;
      +	if (!item->commit)
      +		return -1;
     -+	if (rebasing && item->command != TODO_MERGE &&
     ++	if (is_rebase_i(opts) && item->command != TODO_MERGE &&
      +	    item->commit->parents && item->commit->parents->next)
      +		return error_merge_commit(item->command);
      +	return 0;
       }
       
       int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
     -@@ sequencer.c: int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *
     - }
     - 
     - int todo_list_parse_insn_buffer(struct repository *r, char *buf,
     --				struct todo_list *todo_list)
     -+				struct todo_list *todo_list, int rebasing)
     - {
     - 	struct todo_item *item;
     - 	char *p = buf, *next_p;
     -@@ sequencer.c: int todo_list_parse_insn_buffer(struct repository *r, char *buf,
     - 
     - 		item = append_new_todo(todo_list);
     - 		item->offset_in_buf = p - todo_list->buf.buf;
     --		if (parse_insn_line(r, item, buf, p, eol)) {
     -+		if (parse_insn_line(r, item, buf, p, eol, rebasing)) {
     - 			res = error(_("invalid line %d: %.*s"),
     - 				i, (int)(eol - p), p);
     - 			item->command = TODO_COMMENT + 1;
     -@@ sequencer.c: static int read_populate_todo(struct repository *r,
     - 	if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
     - 		return -1;
     - 
     --	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
     -+	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list,
     -+					  is_rebase_i(opts));
     - 	if (res) {
     - 		if (is_rebase_i(opts))
     - 			return error(_("please fix this using "
     -@@ sequencer.c: static int read_populate_todo(struct repository *r,
     - 		struct todo_list done = TODO_LIST_INIT;
     - 
     - 		if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
     --		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
     -+		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done, 1))
     - 			todo_list->done_nr = count_commands(&done);
     - 		else
     - 			todo_list->done_nr = 0;
     -@@ sequencer.c: int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
     - 	strbuf_release(&buf2);
     - 	/* Nothing is done yet, and we're reparsing, so let's reset the count */
     - 	new_todo.total_nr = 0;
     --	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
     -+	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo, 1) < 0)
     - 		BUG("invalid todo list after expanding IDs:\n%s",
     - 		    new_todo.buf.buf);
     - 
     -
     - ## sequencer.h ##
     -@@ sequencer.h: struct todo_list {
     - }
     - 
     - int todo_list_parse_insn_buffer(struct repository *r, char *buf,
     --				struct todo_list *todo_list);
     -+				struct todo_list *todo_list, int rebasing);
     - int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
     - 			    const char *file, const char *shortrevisions,
     - 			    const char *shortonto, int num, unsigned flags);
      
       ## t/t3404-rebase-interactive.sh ##
      @@ t/t3404-rebase-interactive.sh: test_expect_success 'bad labels and refs rejected when parsing todo list' '

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v5 0/3] reftable/stack: use geometric table compaction
  2024-04-04 18:29  3%       ` [PATCH v5 0/3] " Justin Tobler via GitGitGadget
@ 2024-04-08  6:12  0%         ` Patrick Steinhardt
  2024-04-08 16:16  3%         ` [PATCH v6 " Justin Tobler via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-08  6:12 UTC (permalink / raw)
  To: Justin Tobler via GitGitGadget
  Cc: git, Karthik Nayak, Han-Wen Nienhuys, Justin Tobler

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

On Thu, Apr 04, 2024 at 06:29:26PM +0000, Justin Tobler via GitGitGadget wrote:
> Hello again,
> 
> This is the fifth version my patch series that refactors the reftable
> compaction strategy to instead follow a geometric sequence. Changes compared
> to v4:
> 
>  * To fix some failing tests and conflicts, this patch series now depends on
>    the ps/pack-refs-auto series which is currently in next.
>  * Lifted the GIT_TEST_REFTABLE_AUTOCOMPACTION env out of the reftable
>    library and into the reftable backend code.
> 
> Thanks for taking a look!
> 
> -Justin

I've added two additional nits which you may or may not want to address.
But overall this patch series looks good to me. Thanks!

Patrick

> Justin Tobler (3):
>   reftable/stack: allow disabling of auto-compaction
>   reftable/stack: add env to disable autocompaction
>   reftable/stack: use geometric table compaction
> 
>  refs/reftable-backend.c    |   4 ++
>  reftable/reftable-writer.h |   3 +
>  reftable/stack.c           | 125 +++++++++++++++++++------------------
>  reftable/stack.h           |   4 --
>  reftable/stack_test.c      |  77 ++++++-----------------
>  t/t0610-reftable-basics.sh |  71 ++++++++++++++++-----
>  6 files changed, 146 insertions(+), 138 deletions(-)
> 
> 
> base-commit: 4b32163adf4863c6df3bb6b43540fa2ca3494e28
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v5
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v5
> Pull-Request: https://github.com/gitgitgadget/git/pull/1683
> 
> Range-diff vs v4:
> 
>  -:  ----------- > 1:  a7011dbc6aa reftable/stack: allow disabling of auto-compaction
>  1:  2a0421e5f20 ! 2:  7c4fe0e9ec5 reftable/stack: add env to disable autocompaction
>      @@ Commit message
>       
>           Signed-off-by: Justin Tobler <jltobler@gmail.com>
>       
>      - ## reftable/stack.c ##
>      -@@ reftable/stack.c: int reftable_addition_commit(struct reftable_addition *add)
>      - 	if (err)
>      - 		goto done;
>      - 
>      --	if (!add->stack->disable_auto_compact)
>      -+	if (!add->stack->disable_auto_compact &&
>      -+	    git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
>      - 		err = reftable_stack_auto_compact(add->stack);
>      - 
>      - done:
>      -
>      - ## reftable/system.h ##
>      -@@ reftable/system.h: license that can be found in the LICENSE file or at
>      - #include "tempfile.h"
>      - #include "hash-ll.h" /* hash ID, sizes.*/
>      - #include "dir.h" /* remove_dir_recursively, for tests.*/
>      + ## refs/reftable-backend.c ##
>      +@@
>      + #include "../reftable/reftable-merged.h"
>      + #include "../setup.h"
>      + #include "../strmap.h"
>       +#include "parse.h"
>      + #include "refs-internal.h"
>        
>      - int hash_size(uint32_t id);
>      + /*
>      +@@ refs/reftable-backend.c: static struct ref_store *reftable_be_init(struct repository *repo,
>      + 	refs->write_options.hash_id = repo->hash_algo->format_id;
>      + 	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
>        
>      ++	if (!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
>      ++		refs->write_options.disable_auto_compact = 1;
>      ++
>      + 	/*
>      + 	 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
>      + 	 * This stack contains both the shared and the main worktree refs.
>       
>        ## t/t0610-reftable-basics.sh ##
>       @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause auto-compaction' '
>  2:  e0f4d0dbcc1 ! 3:  8f124acf0f8 reftable/stack: use geometric table compaction
>      @@ reftable/stack_test.c: static void test_empty_add(void)
>       +
>        static void test_reftable_stack_auto_compaction(void)
>        {
>      - 	struct reftable_write_options cfg = { 0 };
>      + 	struct reftable_write_options cfg = {
>       @@ reftable/stack_test.c: static void test_reftable_stack_compaction_concurrent_clean(void)
>        int stack_test_main(int argc, const char *argv[])
>        {
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes are syn
>        	EOF
>        '
>        
>      +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: fails gracefully when auto compaction fail
>      + 			done ||
>      + 			exit 1
>      + 		done &&
>      +-		test_line_count = 13 .git/reftable/tables.list
>      ++		test_line_count = 10 .git/reftable/tables.list
>      + 	)
>      + '
>      + 
>       @@ t/t0610-reftable-basics.sh: test_expect_success 'pack-refs: compacts tables' '
>        
>        	test_commit -C repo A &&
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'pack-refs: compacts tables' '
>        
>        	git -C repo pack-refs &&
>        	ls -1 repo/.git/reftable >table-files &&
>      +@@ t/t0610-reftable-basics.sh: test_expect_success "$command: auto compaction" '
>      + 		# The tables should have been auto-compacted, and thus auto
>      + 		# compaction should not have to do anything.
>      + 		ls -1 .git/reftable >tables-expect &&
>      +-		test_line_count = 4 tables-expect &&
>      ++		test_line_count = 3 tables-expect &&
>      + 		git $command --auto &&
>      + 		ls -1 .git/reftable >tables-actual &&
>      + 		test_cmp tables-expect tables-actual &&
>      +@@ t/t0610-reftable-basics.sh: test_expect_success "$command: auto compaction" '
>      + 		git branch B &&
>      + 		git branch C &&
>      + 		rm .git/reftable/*.lock &&
>      +-		test_line_count = 5 .git/reftable/tables.list &&
>      ++		test_line_count = 4 .git/reftable/tables.list &&
>      + 
>      + 		git $command --auto &&
>      + 		test_line_count = 1 .git/reftable/tables.list
>       @@ t/t0610-reftable-basics.sh: do
>        			umask $umask &&
>        			git init --shared=true repo &&
> 
> -- 
> gitgitgadget

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

^ permalink raw reply	[relevance 0%]

* Re: [External] Re: [PATCH] midx: disable replace objects
  2024-04-07 18:02  2%   ` Taylor Blau
@ 2024-04-08  5:45  0%     ` 鑫邢
  0 siblings, 0 replies; 200+ results
From: 鑫邢 @ 2024-04-08  5:45 UTC (permalink / raw)
  To: Taylor Blau; +Cc: blanet via GitGitGadget, git, blanet

> I had a some more time to look into this, and I think that your original
> fix is correct.
>
> The issue is, as you suggest, due to the following (from your original
> patch):
>
> > After some investigation we found that all repositories experiencing
> > failures contain replace references, which seem to be improperly
> > acknowledged by the MIDX bitmap generation logic.
>
> Indeed, the pack-bitmap-write machinery does not itself call
> disable_replace_refs(). So when it generates a reachability bitmap, it
> is doing so with the replace refs in mind. You can see that this is
> indeed the cause of the problem by looking at the output of an
> instrumented version of Git that indicates what bits are being set
> during the bitmap generation phase.
>
> With replace refs (incorrectly) enabled, we get:
>
>     [2, 4, 6, 8, 13, 3, 6, 7, 3, 4, 6, 8]
>
> and doing the same after calling disable_replace_refs(), we instead get:
>
>     [2, 5, 6, 13, 3, 6, 7, 3, 4, 6, 8]
>
> Single pack bitmaps are unaffected by this issue because we generate
> them from within pack-objects, which does call disable_replace_refs().

Thank you for the comprehensive investigation. I have quoted them in the
commit message to provide a clearer explanation of the patch.

> In addition to the test fixes I suggested earlier, I would instead demonstrate
> the bug by showing a clone (which fails with MIDXs, but doesn't without
> MIDXs) like so:
>
> --- 8< ---
> diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
> index 5e4cdef6a8..1fb3b0f9d7 100755
> --- a/t/t5326-multi-pack-bitmaps.sh
> +++ b/t/t5326-multi-pack-bitmaps.sh
> @@ -442,19 +442,16 @@ test_expect_success 'do not follow replace objects for MIDX bitmap' '
>                 cd repo &&
>
>                 test_commit A &&
> -               A=$(git rev-parse HEAD) &&
>                 test_commit B &&
> -               B=$(git rev-parse HEAD) &&
> -               git checkout --orphan=orphan $A &&
> +               git checkout --orphan=orphan A &&
>                 test_commit orphan &&
> -               C=$(git rev-parse HEAD) &&
> -               git rev-list --objects --no-object-names $B |sort >expected &&
>
> -               git replace $A $C &&
> -               git repack -ad &&
> -               git multi-pack-index write --bitmap &&
> -               git rev-list --objects --no-object-names --use-bitmap-index $B |sort >actual &&
> -               test_cmp expected actual
> +               git replace A HEAD &&
> +               git repack -ad --write-midx --write-bitmap-index &&
> +
> +               # generating reachability bitmaps with replace refs
> +               # enabled will result in broken clones
> +               git clone --no-local --bare . clone.git
>         )
>  '
> --- >8 ---
>
> With the change in your patch to call disable_replace_refs() in
> builtin/multi-pack-index.c, this test passes as expected. With that
> change compiled out, we instead get:
>
> [...]
> + git clone --no-local --bare . clone.git
> Cloning into bare repository 'clone.git'...
> remote: Enumerating objects: 8, done.
> remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 8 (from 1)
> Receiving objects: 100% (8/8), done.
> fatal: did not receive expected object da5497437fd67ca928333aab79c4b4b55036ea66
> fatal: fetch-pack: invalid index-pack output
> error: last command exited with $?=128
> not ok 352 - do not follow replace objects for MIDX bitmap
>
> as expected.
>

Applied! The test looks much clearer now, thanks!

Xing Xin


^ permalink raw reply	[relevance 0%]

* [PATCH v2] midx: disable replace objects
  2024-04-07 13:11  4% [PATCH] midx: disable replace objects blanet via GitGitGadget
  2024-04-07 14:16  0% ` Taylor Blau
@ 2024-04-08  5:26  4% ` blanet via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: blanet via GitGitGadget @ 2024-04-08  5:26 UTC (permalink / raw)
  To: git; +Cc: blanet, Xing Xin

From: Xing Xin <xingxin.xx@bytedance.com>

We observed a series of clone failures arose in a specific set of
repositories after we fully enabled the MIDX bitmap feature within our
Codebase service. These failures were accompanied with error messages
such as:

    Cloning into bare repository 'clone.git'...
    remote: Enumerating objects: 8, done.
    remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 8 (from 1)
    Receiving objects: 100% (8/8), done.
    fatal: did not receive expected object ...
    fatal: fetch-pack: invalid index-pack output

Temporarily disabling the MIDX feature eliminated the reported issues.
After some investigation we found that all repositories experiencing
failures contain replace references, which seem to be improperly
acknowledged by the MIDX bitmap generation logic.

A more thorough explanation about the root cause from Taylor Blau says:

Indeed, the pack-bitmap-write machinery does not itself call
disable_replace_refs(). So when it generates a reachability bitmap, it
is doing so with the replace refs in mind. You can see that this is
indeed the cause of the problem by looking at the output of an
instrumented version of Git that indicates what bits are being set
during the bitmap generation phase.

With replace refs (incorrectly) enabled, we get:

    [2, 4, 6, 8, 13, 3, 6, 7, 3, 4, 6, 8]

and doing the same after calling disable_replace_refs(), we instead get:

    [2, 5, 6, 13, 3, 6, 7, 3, 4, 6, 8]

Single pack bitmaps are unaffected by this issue because we generate
them from within pack-objects, which does call disable_replace_refs().

This patch updates the MIDX logic to disable replace objects within the
multi-pack-index builtin, and a test showing a clone (which would fail
with MIDX bitmap) is added to demonstrate the bug.

Helped-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Xing Xin <xingxin.xx@bytedance.com>
---
    midx: disable replace objects
    
    cc: Taylor Blau me@ttaylorr.com

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1711%2Fblanet%2Fxx%2Fmidx-ignore-replace-objects-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1711/blanet/xx/midx-ignore-replace-objects-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1711

Range-diff vs v1:

 1:  b1c838965ab ! 1:  1be25b55c5a midx: disable replace objects
     @@ Commit message
          Codebase service. These failures were accompanied with error messages
          such as:
      
     -      fatal: did not receive expected object ...
     -      fatal: fetch-pack: invalid index-pack output
     +        Cloning into bare repository 'clone.git'...
     +        remote: Enumerating objects: 8, done.
     +        remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 8 (from 1)
     +        Receiving objects: 100% (8/8), done.
     +        fatal: did not receive expected object ...
     +        fatal: fetch-pack: invalid index-pack output
      
          Temporarily disabling the MIDX feature eliminated the reported issues.
          After some investigation we found that all repositories experiencing
          failures contain replace references, which seem to be improperly
     -    acknowledged by the MIDX bitmap generation logic. During cloning or
     -    fetching, git-pack-objects, which may make use of MIDX bitmap to find
     -    objects to pack, would give wrong objects even if we explicitly
     -    specified not to enable replace refs by GIT_NO_REPLACE_OBJECTS=1.
     -    Indeed, this issue appears to have persisted since the introduction of
     -    MIDX.
     +    acknowledged by the MIDX bitmap generation logic.
      
     -    This patch updates the MIDX logic to disable replace objects during
     -    operations, mirroring the handling seen in single pack index scenarios,
     -    i.e. git-index-pack and git-pack-objects. The added test uses
     -    git-rev-list to give a more intuitive check.
     +    A more thorough explanation about the root cause from Taylor Blau says:
      
     +    Indeed, the pack-bitmap-write machinery does not itself call
     +    disable_replace_refs(). So when it generates a reachability bitmap, it
     +    is doing so with the replace refs in mind. You can see that this is
     +    indeed the cause of the problem by looking at the output of an
     +    instrumented version of Git that indicates what bits are being set
     +    during the bitmap generation phase.
     +
     +    With replace refs (incorrectly) enabled, we get:
     +
     +        [2, 4, 6, 8, 13, 3, 6, 7, 3, 4, 6, 8]
     +
     +    and doing the same after calling disable_replace_refs(), we instead get:
     +
     +        [2, 5, 6, 13, 3, 6, 7, 3, 4, 6, 8]
     +
     +    Single pack bitmaps are unaffected by this issue because we generate
     +    them from within pack-objects, which does call disable_replace_refs().
     +
     +    This patch updates the MIDX logic to disable replace objects within the
     +    multi-pack-index builtin, and a test showing a clone (which would fail
     +    with MIDX bitmap) is added to demonstrate the bug.
     +
     +    Helped-by: Taylor Blau <me@ttaylorr.com>
          Signed-off-by: Xing Xin <xingxin.xx@bytedance.com>
      
       ## builtin/multi-pack-index.c ##
     @@ t/t5326-multi-pack-bitmaps.sh: test_expect_success 'tagged commits are selected
      +		cd repo &&
      +
      +		test_commit A &&
     -+		A=$(git rev-parse HEAD) &&
      +		test_commit B &&
     -+		B=$(git rev-parse HEAD) &&
     -+		git checkout --orphan=orphan $A &&
     ++		git checkout --orphan=orphan A &&
      +		test_commit orphan &&
     -+		C=$(git rev-parse HEAD) &&
     -+		git rev-list --objects --no-object-names $B |sort >expected &&
      +
     -+		git replace $A $C &&
     -+		git repack -ad &&
     -+		git multi-pack-index write --bitmap &&
     -+		git rev-list --objects --no-object-names --use-bitmap-index $B |sort >actual &&
     -+		test_cmp expected actual
     ++		git replace A HEAD &&
     ++		git repack -ad --write-midx --write-bitmap-index &&
     ++
     ++		# generating reachability bitmaps with replace refs
     ++		# enabled will result in broken clones
     ++		git clone --no-local --bare . clone.git
      +	)
      +'
      +


 builtin/multi-pack-index.c    |  3 +++
 t/t5326-multi-pack-bitmaps.sh | 21 +++++++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index a72aebecaa2..8360932d2e7 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -8,6 +8,7 @@
 #include "strbuf.h"
 #include "trace2.h"
 #include "object-store-ll.h"
+#include "replace-object.h"
 
 #define BUILTIN_MIDX_WRITE_USAGE \
 	N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -273,6 +274,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
 	};
 	struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
 
+	disable_replace_refs();
+
 	git_config(git_default_config, NULL);
 
 	if (the_repository &&
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58709a..1fb3b0f9d7a 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -434,6 +434,27 @@ test_expect_success 'tagged commits are selected for bitmapping' '
 	)
 '
 
+test_expect_success 'do not follow replace objects for MIDX bitmap' '
+	rm -fr repo &&
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		test_commit A &&
+		test_commit B &&
+		git checkout --orphan=orphan A &&
+		test_commit orphan &&
+
+		git replace A HEAD &&
+		git repack -ad --write-midx --write-bitmap-index &&
+
+		# generating reachability bitmaps with replace refs
+		# enabled will result in broken clones
+		git clone --no-local --bare . clone.git
+	)
+'
+
 corrupt_file () {
 	chmod a+w "$1" &&
 	printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc

base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* Re: [PATCH] midx: disable replace objects
  2024-04-07 14:16  0% ` Taylor Blau
@ 2024-04-07 18:02  2%   ` Taylor Blau
  2024-04-08  5:45  0%     ` [External] " 鑫邢
  0 siblings, 1 reply; 200+ results
From: Taylor Blau @ 2024-04-07 18:02 UTC (permalink / raw)
  To: blanet via GitGitGadget; +Cc: git, blanet, Xing Xin

On Sun, Apr 07, 2024 at 10:16:28AM -0400, Taylor Blau wrote:
> , I can still produce the failure that you are seeing here. So I suspect
> that while it's entirely possible that there is a bug in the MIDX/bitmap
> code, that this test is not exercising it.
>
> I think the first step to demonstrate a bug in the MIDX/bitmap machinery
> would be to provide a reproducer that fails only when using a MIDX
> and/or bitmap.

I had a some more time to look into this, and I think that your original
fix is correct.

The issue is, as you suggest, due to the following (from your original
patch):

> After some investigation we found that all repositories experiencing
> failures contain replace references, which seem to be improperly
> acknowledged by the MIDX bitmap generation logic.

Indeed, the pack-bitmap-write machinery does not itself call
disable_replace_refs(). So when it generates a reachability bitmap, it
is doing so with the replace refs in mind. You can see that this is
indeed the cause of the problem by looking at the output of an
instrumented version of Git that indicates what bits are being set
during the bitmap generation phase.

With replace refs (incorrectly) enabled, we get:

    [2, 4, 6, 8, 13, 3, 6, 7, 3, 4, 6, 8]

and doing the same after calling disable_replace_refs(), we instead get:

    [2, 5, 6, 13, 3, 6, 7, 3, 4, 6, 8]

Single pack bitmaps are unaffected by this issue because we generate
them from within pack-objects, which does call disable_replace_refs().

It is tempting to instead do something like:

--- 8< ---
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index c6c8f94cc5..cbc543caad 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -17,6 +17,7 @@
 #include "trace2.h"
 #include "tree.h"
 #include "tree-walk.h"
+#include "replace-object.h"

 struct bitmapped_commit {
 	struct commit *commit;
@@ -223,6 +224,8 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	memset(bb, 0, sizeof(*bb));
 	init_bb_data(&bb->data);

+	parsed_object_pool_clear(the_repository->parsed_objects);
+
 	reset_revision_walk();
 	repo_init_revisions(writer->to_pack->repo, &revs, NULL);
 	revs.topo_order = 1;
--- >8 ---

But by then it is too late, because the replace refs have already been
taken into account for parsed objects.

An alternative is to clear the parsed_object_pool before (or after)
calling disable_replace_refs(), but I think that approach that feels
sub-optimal for a couple of reasons:

  - We're wasting time re-parsing objects that we've already seen

  - We're banking on the fact that the MIDX generation does not lookup
    objects with the OBJECT_INFO_LOOKUP_REPLACE flag set, which would
    cause the MIDX to be broken in the same way.

So I think that disabling replace refs at the outset within the
multi-pack-index builtin is the right way to go. In addition to the test
fixes I suggested earlier, I would instead demonstrate the bug by
showing a clone (which fails with MIDXs, but doesn't without MIDXs) like
so:

--- 8< ---
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 5e4cdef6a8..1fb3b0f9d7 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -442,19 +442,16 @@ test_expect_success 'do not follow replace objects for MIDX bitmap' '
 		cd repo &&

 		test_commit A &&
-		A=$(git rev-parse HEAD) &&
 		test_commit B &&
-		B=$(git rev-parse HEAD) &&
-		git checkout --orphan=orphan $A &&
+		git checkout --orphan=orphan A &&
 		test_commit orphan &&
-		C=$(git rev-parse HEAD) &&
-		git rev-list --objects --no-object-names $B |sort >expected &&

-		git replace $A $C &&
-		git repack -ad &&
-		git multi-pack-index write --bitmap &&
-		git rev-list --objects --no-object-names --use-bitmap-index $B |sort >actual &&
-		test_cmp expected actual
+		git replace A HEAD &&
+		git repack -ad --write-midx --write-bitmap-index &&
+
+		# generating reachability bitmaps with replace refs
+		# enabled will result in broken clones
+		git clone --no-local --bare . clone.git
 	)
 '
--- >8 ---

With the change in your patch to call disable_replace_refs() in
builtin/multi-pack-index.c, this test passes as expected. With that
change compiled out, we instead get:

[...]
+ git clone --no-local --bare . clone.git
Cloning into bare repository 'clone.git'...
remote: Enumerating objects: 8, done.
remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 8 (from 1)
Receiving objects: 100% (8/8), done.
fatal: did not receive expected object da5497437fd67ca928333aab79c4b4b55036ea66
fatal: fetch-pack: invalid index-pack output
error: last command exited with $?=128
not ok 352 - do not follow replace objects for MIDX bitmap

as expected.

Thanks,
Taylor



^ permalink raw reply related	[relevance 2%]

* Re: [PATCH] midx: disable replace objects
  2024-04-07 13:11  4% [PATCH] midx: disable replace objects blanet via GitGitGadget
@ 2024-04-07 14:16  0% ` Taylor Blau
  2024-04-07 18:02  2%   ` Taylor Blau
  2024-04-08  5:26  4% ` [PATCH v2] " blanet via GitGitGadget
  1 sibling, 1 reply; 200+ results
From: Taylor Blau @ 2024-04-07 14:16 UTC (permalink / raw)
  To: blanet via GitGitGadget; +Cc: git, blanet, Xing Xin

On Sun, Apr 07, 2024 at 01:11:47PM +0000, blanet via GitGitGadget wrote:
> From: Xing Xin <xingxin.xx@bytedance.com>
>
> We observed a series of clone failures arose in a specific set of
> repositories after we fully enabled the MIDX bitmap feature within our
> Codebase service. These failures were accompanied with error messages
> such as:
>
>   fatal: did not receive expected object ...
>   fatal: fetch-pack: invalid index-pack output
>
> Temporarily disabling the MIDX feature eliminated the reported issues.
> After some investigation we found that all repositories experiencing
> failures contain replace references, which seem to be improperly
> acknowledged by the MIDX bitmap generation logic.

I was suspicious that this might be related to the MIDX or MIDX bitmap,
but noticed something curious upon digging in. Applying the following on
top of your patch:

--- 8< ---
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 5e4cdef6a8..8543f8d097 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -451,9 +451,7 @@ test_expect_success 'do not follow replace objects for MIDX bitmap' '
 		git rev-list --objects --no-object-names $B |sort >expected &&

 		git replace $A $C &&
-		git repack -ad &&
-		git multi-pack-index write --bitmap &&
-		git rev-list --objects --no-object-names --use-bitmap-index $B |sort >actual &&
+		git rev-list --objects --no-object-names $B |sort >actual &&
 		test_cmp expected actual
 	)
 '
--- >8 ---

, I can still produce the failure that you are seeing here. So I suspect
that while it's entirely possible that there is a bug in the MIDX/bitmap
code, that this test is not exercising it.

I think the first step to demonstrate a bug in the MIDX/bitmap machinery
would be to provide a reproducer that fails only when using a MIDX
and/or bitmap.

> @@ -273,6 +274,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
>  	};
>  	struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
>
> +	disable_replace_refs();
> +

Supposing for a moment that this issue is in the MIDX, we know that
regardless of what replace refs might be in place, the MIDX should only
be storing the objects that are in the packs being indexed, not the
objects which are their replacements.

Are we storing objects in the MIDX that are replacements? Looking
at midx.c::fill_pack_entry(), I think the answer is "no", since we're
looking up packed objects by calling nth_packed_object_id(), which is
just a table read into the .idx, all of which is beneath the level of
replace refs.

> @@ -434,6 +434,30 @@ test_expect_success 'tagged commits are selected for bitmapping' '
>  	)
>  '
>
> +test_expect_success 'do not follow replace objects for MIDX bitmap' '
> +	rm -fr repo &&
> +	git init repo &&
> +	test_when_finished "rm -fr repo" &&
> +	(
> +		cd repo &&
> +
> +		test_commit A &&
> +		A=$(git rev-parse HEAD) &&

It's possible that much of this will be moot if the current test gets
rewritten, but here are a couple of suggestions for writing tests in
Git's suite:

- test_commit will create a tag for you, so there is no need to store
  "$A", "$B", and "$C".

> +		test_commit B &&
> +		B=$(git rev-parse HEAD) &&
> +		git checkout --orphan=orphan $A &&
> +		test_commit orphan &&
> +		C=$(git rev-parse HEAD) &&
> +		git rev-list --objects --no-object-names $B |sort >expected &&

- We do not allow Git invocations on the left-hand side of a pipe, since
  doing so will squelch its exit code. Instead, try:

    git rev-list --objects --no-object-names B >expect.raw &&
    sort expect.raw >expect &&

Thanks,
Taylor


^ permalink raw reply related	[relevance 0%]

* [PATCH] midx: disable replace objects
@ 2024-04-07 13:11  4% blanet via GitGitGadget
  2024-04-07 14:16  0% ` Taylor Blau
  2024-04-08  5:26  4% ` [PATCH v2] " blanet via GitGitGadget
  0 siblings, 2 replies; 200+ results
From: blanet via GitGitGadget @ 2024-04-07 13:11 UTC (permalink / raw)
  To: git; +Cc: blanet, Xing Xin

From: Xing Xin <xingxin.xx@bytedance.com>

We observed a series of clone failures arose in a specific set of
repositories after we fully enabled the MIDX bitmap feature within our
Codebase service. These failures were accompanied with error messages
such as:

  fatal: did not receive expected object ...
  fatal: fetch-pack: invalid index-pack output

Temporarily disabling the MIDX feature eliminated the reported issues.
After some investigation we found that all repositories experiencing
failures contain replace references, which seem to be improperly
acknowledged by the MIDX bitmap generation logic. During cloning or
fetching, git-pack-objects, which may make use of MIDX bitmap to find
objects to pack, would give wrong objects even if we explicitly
specified not to enable replace refs by GIT_NO_REPLACE_OBJECTS=1.
Indeed, this issue appears to have persisted since the introduction of
MIDX.

This patch updates the MIDX logic to disable replace objects during
operations, mirroring the handling seen in single pack index scenarios,
i.e. git-index-pack and git-pack-objects. The added test uses
git-rev-list to give a more intuitive check.

Signed-off-by: Xing Xin <xingxin.xx@bytedance.com>
---
    midx: disable replace objects

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1711%2Fblanet%2Fxx%2Fmidx-ignore-replace-objects-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1711/blanet/xx/midx-ignore-replace-objects-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1711

 builtin/multi-pack-index.c    |  3 +++
 t/t5326-multi-pack-bitmaps.sh | 24 ++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index a72aebecaa2..8360932d2e7 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -8,6 +8,7 @@
 #include "strbuf.h"
 #include "trace2.h"
 #include "object-store-ll.h"
+#include "replace-object.h"
 
 #define BUILTIN_MIDX_WRITE_USAGE \
 	N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -273,6 +274,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
 	};
 	struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
 
+	disable_replace_refs();
+
 	git_config(git_default_config, NULL);
 
 	if (the_repository &&
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 70d1b58709a..5e4cdef6a8b 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -434,6 +434,30 @@ test_expect_success 'tagged commits are selected for bitmapping' '
 	)
 '
 
+test_expect_success 'do not follow replace objects for MIDX bitmap' '
+	rm -fr repo &&
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		test_commit A &&
+		A=$(git rev-parse HEAD) &&
+		test_commit B &&
+		B=$(git rev-parse HEAD) &&
+		git checkout --orphan=orphan $A &&
+		test_commit orphan &&
+		C=$(git rev-parse HEAD) &&
+		git rev-list --objects --no-object-names $B |sort >expected &&
+
+		git replace $A $C &&
+		git repack -ad &&
+		git multi-pack-index write --bitmap &&
+		git rev-list --objects --no-object-names --use-bitmap-index $B |sort >actual &&
+		test_cmp expected actual
+	)
+'
+
 corrupt_file () {
 	chmod a+w "$1" &&
 	printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc

base-commit: 3c2a3fdc388747b9eaf4a4a4f2035c1c9ddb26d0
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* [PATCH v2] log: add option to search for header or body
  2024-04-05 21:48  3% [PATCH] feat(log): add option to search for header or body to `git log` Max Coplan via GitGitGadget
@ 2024-04-07  3:24  2% ` Max Coplan via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Max Coplan via GitGitGadget @ 2024-04-07  3:24 UTC (permalink / raw)
  To: git
  Cc: Phillip Wood, strager, me, Max Coplan,
	Max 👨🏽‍💻 Coplan

From: =?UTF-8?q?Max=20=F0=9F=91=A8=F0=9F=8F=BD=E2=80=8D=F0=9F=92=BB=20Copl?=
 =?UTF-8?q?an?= <mchcopl@gmail.com>

Summary:
This change adds a new option to `git log` that allows users to search
for commits that match either the author or the commit message. This is
useful for finding commits that were either authored or co-authored by a
specific person.

Currently, the best way to find a commit either authored or co-authored
by a specific person is to use

```sh
$ echo \
    $(git log --author=Torvalds --pretty="%cd,%H\n" --date=iso-strict) \
    $(git log --grep="Co-authored-by: .*Torvalds" --pretty="%cd,%H\n" --date=iso-strict) \
| sort -n --reverse \
| awk -F, '{print $2}' \
| tr '\n' '\t' \
| xargs git show --stat --stdin
```

This is a bit of a pain, so this change adds a new option to `git log`.
Now finding either authors or co-authors is as simple as

```sh
$ git log --author=Torvalds --grep=Torvalds --match-header-or-grep
```

Test plan:
1. create commit authored by A and co-authored-by B
2. create commit authored by B
3. run
```sh
$ git log --author=B --grep="Co-authored-by: B" --match-header-or-grep
```
4. expect to see both commits

Signed-off-by: Max 👨🏽‍💻 Coplan <mchcopl@gmail.com>
---
    log: add option to search for header or body

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1710%2Fvegerot%2Fheader-or-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1710/vegerot/header-or-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1710

Range-diff vs v1:

 1:  dc6b5664159 ! 1:  c7f14e59b4f feat(log): add option to search for header or body to `git log`
     @@ Metadata
      Author: Max 👨🏽‍💻 Coplan <mchcopl@gmail.com>
      
       ## Commit message ##
     -    feat(log): add option to search for header or body to `git log`
     -
     -    Note to reviewer: I hate the name `--header-or`!  Please help me come up
     -    with a better name.
     +    log: add option to search for header or body
      
          Summary:
          This change adds a new option to `git log` that allows users to search
     @@ Commit message
          by a specific person is to use
      
          ```sh
     -    echo \
     +    $ echo \
              $(git log --author=Torvalds --pretty="%cd,%H\n" --date=iso-strict) \
              $(git log --grep="Co-authored-by: .*Torvalds" --pretty="%cd,%H\n" --date=iso-strict) \
          | sort -n --reverse \
     @@ Commit message
          Now finding either authors or co-authors is as simple as
      
          ```sh
     -    git log --author=Torvalds --grep=Torvalds --header-or
     +    $ git log --author=Torvalds --grep=Torvalds --match-header-or-grep
          ```
      
          Test plan:
          1. create commit authored by A and co-authored-by B
          2. create commit authored by B
     -    3. run `git log --author=B --grep="Co-authored-by: B" --header-or`
     +    3. run
     +    ```sh
     +    $ git log --author=B --grep="Co-authored-by: B" --match-header-or-grep
     +    ```
          4. expect to see both commits
      
          Signed-off-by: Max 👨🏽‍💻 Coplan <mchcopl@gmail.com>
      
     + ## Documentation/rev-list-options.txt ##
     +@@ Documentation/rev-list-options.txt: endif::git-rev-list[]
     + 	Limit the commits output to ones that match all given `--grep`,
     + 	instead of ones that match at least one.
     + 
     ++--match-header-or-grep::
     ++	Limit the commits output to ones that match either header patterns
     ++	(`--author`, `--committer`, or `--grep-reflog`) or `--grep`, instead
     ++	of ones that match both the header and grep patterns
     +++
     ++For example, `--author=me --grep=Co-authored-by: me` limits to commits either
     ++authored or co-authored by me.
     ++
     + --invert-grep::
     + 	Limit the commits output to ones with a log message that do not
     + 	match the pattern specified with `--grep=<pattern>`.
     +
     + ## contrib/completion/git-completion.bash ##
     +@@ contrib/completion/git-completion.bash: __git_log_gitk_options="
     + # Options that go well for log and shortlog (not gitk)
     + __git_log_shortlog_options="
     + 	--author= --committer= --grep=
     +-	--all-match --invert-grep
     ++	--all-match --invert-grep --match-header-or-grep
     + "
     + # Options accepted by log and show
     + __git_log_show_options="
     +
       ## grep.c ##
      @@ grep.c: void compile_grep_patterns(struct grep_opt *opt)
     - 	if (opt->no_body_match && opt->pattern_expression)
     - 		opt->pattern_expression = grep_not_expr(opt->pattern_expression);
     - 
     --	if (!header_expr)
     -+	if (!header_expr || opt->header_or)
     - 		return;
       
       	if (!opt->pattern_expression)
     + 		opt->pattern_expression = header_expr;
     +-	else if (opt->all_match)
     ++	else if (opt->all_match || opt->match_header_or_grep)
     + 		opt->pattern_expression = grep_splice_or(header_expr,
     + 							 opt->pattern_expression);
     + 	else
     +@@ grep.c: int grep_source(struct grep_opt *opt, struct grep_source *gs)
     + 	opt->body_hit = 0;
     + 	grep_source_1(opt, gs, 1);
     + 
     +-	if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
     ++	if (!opt->match_header_or_grep && opt->all_match && !chk_hit_marker(opt->pattern_expression))
     + 		return 0;
     + 	if (opt->no_body_match && opt->body_hit)
     + 		return 0;
      
       ## grep.h ##
      @@ grep.h: struct grep_opt {
       	int count;
       	int word_regexp;
       	int all_match;
     -+	int header_or;
     ++	int match_header_or_grep;
       	int no_body_match;
       	int body_hit;
       #define GREP_BINARY_DEFAULT	0
     @@ revision.c: static int handle_revision_opt(struct rev_info *revs, int argc, cons
       		revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
       	} else if (!strcmp(arg, "--all-match")) {
       		revs->grep_filter.all_match = 1;
     -+	// @@@ must-fix: find a better name
     -+	} else if (!strcmp(arg, "--header-or")) {
     -+		revs->grep_filter.header_or = 1;
     ++	} else if (!strcmp(arg, "--match-header-or-grep")) {
     ++		revs->grep_filter.match_header_or_grep = 1;
       	} else if (!strcmp(arg, "--invert-grep")) {
       		revs->grep_filter.no_body_match = 1;
       	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
     @@ t/t7810-grep.sh: test_expect_success 'log --grep --author uses intersection' '
       	test_cmp expect actual
       '
       
     -+test_expect_success 'log --grep --author --header-or uses union' '
     ++test_expect_success 'log --grep --author --match-header-or-grep uses union' '
      +	# grep matches only third and fourth
      +	# author matches only initial and third
     -+	git log --author="A U Thor" --grep=r --header-or --format=%s >actual &&
     -+	{
     -+	    echo fourth && echo third
     -+	} >expect &&
     ++	git log --author="A U Thor" --grep=r --match-header-or-grep --format=%s >actual &&
     ++	test_write_lines fourth third initial >expect &&
      +	test_cmp expect actual
      +'
     -+
      +
       test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
       	# grep matches initial and second but not third
       	# author matches only initial and third
     +@@ t/t7810-grep.sh: test_expect_success 'log --grep --grep --author takes union of greps and interse
     + 	test_cmp expect actual
     + '
     + 
     +-test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
     ++test_expect_success 'log --author --grep --grep --match-header-or-grep takes union of greps and author' '
     ++	# grep matches initial and second but not third
     ++	# author matches only initial and third
     ++	git log --author="A U Thor" --grep=second --grep=initial --match-header-or-grep --format=%s >actual &&
     ++	test_write_lines third second initial >expect &&
     ++	test_cmp expect actual
     ++'
     ++
     ++test_expect_success 'log --author --grep --grep --all-match --match-header-or-grep still takes union of greps and author' '
     ++	# grep matches initial and second but not third
     ++	# author matches only initial and third
     ++	git log --author="A U Thor" --grep=second --grep=initial --all-match --match-header-or-grep --format=%s >actual &&
     ++	test_write_lines third second initial >expect &&
     ++	test_cmp expect actual
     ++'
     ++
     ++test_expect_success 'log --all-match --grep --author --author still takes union of authors and intersects with grep' '
     + 	# grep matches only initial and third
     + 	# author matches all but second
     + 	git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&


 Documentation/rev-list-options.txt     |  8 ++++++++
 contrib/completion/git-completion.bash |  2 +-
 grep.c                                 |  4 ++--
 grep.h                                 |  1 +
 revision.c                             |  2 ++
 t/t7810-grep.sh                        | 26 +++++++++++++++++++++++++-
 6 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 00ccf687441..db0979ac498 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -71,6 +71,14 @@ endif::git-rev-list[]
 	Limit the commits output to ones that match all given `--grep`,
 	instead of ones that match at least one.
 
+--match-header-or-grep::
+	Limit the commits output to ones that match either header patterns
+	(`--author`, `--committer`, or `--grep-reflog`) or `--grep`, instead
+	of ones that match both the header and grep patterns
++
+For example, `--author=me --grep=Co-authored-by: me` limits to commits either
+authored or co-authored by me.
+
 --invert-grep::
 	Limit the commits output to ones with a log message that do not
 	match the pattern specified with `--grep=<pattern>`.
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 75193ded4bd..30fc6ed08bd 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2170,7 +2170,7 @@ __git_log_gitk_options="
 # Options that go well for log and shortlog (not gitk)
 __git_log_shortlog_options="
 	--author= --committer= --grep=
-	--all-match --invert-grep
+	--all-match --invert-grep --match-header-or-grep
 "
 # Options accepted by log and show
 __git_log_show_options="
diff --git a/grep.c b/grep.c
index ac34bfeafb3..72cf599660a 100644
--- a/grep.c
+++ b/grep.c
@@ -802,7 +802,7 @@ void compile_grep_patterns(struct grep_opt *opt)
 
 	if (!opt->pattern_expression)
 		opt->pattern_expression = header_expr;
-	else if (opt->all_match)
+	else if (opt->all_match || opt->match_header_or_grep)
 		opt->pattern_expression = grep_splice_or(header_expr,
 							 opt->pattern_expression);
 	else
@@ -1829,7 +1829,7 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs)
 	opt->body_hit = 0;
 	grep_source_1(opt, gs, 1);
 
-	if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
+	if (!opt->match_header_or_grep && opt->all_match && !chk_hit_marker(opt->pattern_expression))
 		return 0;
 	if (opt->no_body_match && opt->body_hit)
 		return 0;
diff --git a/grep.h b/grep.h
index 926c0875c42..861584dba98 100644
--- a/grep.h
+++ b/grep.h
@@ -147,6 +147,7 @@ struct grep_opt {
 	int count;
 	int word_regexp;
 	int all_match;
+	int match_header_or_grep;
 	int no_body_match;
 	int body_hit;
 #define GREP_BINARY_DEFAULT	0
diff --git a/revision.c b/revision.c
index 7e45f765d9f..786c229f56d 100644
--- a/revision.c
+++ b/revision.c
@@ -2646,6 +2646,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
 	} else if (!strcmp(arg, "--all-match")) {
 		revs->grep_filter.all_match = 1;
+	} else if (!strcmp(arg, "--match-header-or-grep")) {
+		revs->grep_filter.match_header_or_grep = 1;
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 875dcfd98f3..c78ce150f4d 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -961,6 +961,14 @@ test_expect_success 'log --grep --author uses intersection' '
 	test_cmp expect actual
 '
 
+test_expect_success 'log --grep --author --match-header-or-grep uses union' '
+	# grep matches only third and fourth
+	# author matches only initial and third
+	git log --author="A U Thor" --grep=r --match-header-or-grep --format=%s >actual &&
+	test_write_lines fourth third initial >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
 	# grep matches initial and second but not third
 	# author matches only initial and third
@@ -971,7 +979,23 @@ test_expect_success 'log --grep --grep --author takes union of greps and interse
 	test_cmp expect actual
 '
 
-test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' '
+test_expect_success 'log --author --grep --grep --match-header-or-grep takes union of greps and author' '
+	# grep matches initial and second but not third
+	# author matches only initial and third
+	git log --author="A U Thor" --grep=second --grep=initial --match-header-or-grep --format=%s >actual &&
+	test_write_lines third second initial >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --author --grep --grep --all-match --match-header-or-grep still takes union of greps and author' '
+	# grep matches initial and second but not third
+	# author matches only initial and third
+	git log --author="A U Thor" --grep=second --grep=initial --all-match --match-header-or-grep --format=%s >actual &&
+	test_write_lines third second initial >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'log --all-match --grep --author --author still takes union of authors and intersects with grep' '
 	# grep matches only initial and third
 	# author matches all but second
 	git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual &&

base-commit: 7774cfed6261ce2900c84e55906da708c711d601
-- 
gitgitgadget


^ permalink raw reply related	[relevance 2%]

* [PATCH 03/11] config: prefer git_config_string_dup() for temp variables
  2024-04-07  0:56  2% ` [PATCH 0/12] git_config_string() considered harmful Jeff King
@ 2024-04-07  1:00 13%   ` Jeff King
  0 siblings, 0 replies; 200+ results
From: Jeff King @ 2024-04-07  1:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Rubén Justo

In some cases we may use git_config_string() or git_config_pathname() to
read a value into a temporary variable, and then either hand off memory
ownership of the new variable or free it. These are not potential leaks,
since we know that there is no previous value we are overwriting.

However, it's worth converting these to use git_config_string_dup() and
git_config_pathname_dup(). It makes it easier to audit for leaky cases,
and possibly we can get rid of the leak-prone functions in the future.
Plus it lets the const-ness of our variables match their expected memory
ownership, which avoids some casts when calling free().

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin/blame.c        |  4 ++--
 builtin/config.c       |  6 +++---
 builtin/receive-pack.c |  6 +++---
 fetch-pack.c           |  6 +++---
 fsck.c                 |  6 +++---
 remote.c               | 28 ++++++++++++++--------------
 setup.c                |  6 +++---
 7 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index db1f56de61..0b07ceb110 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -718,10 +718,10 @@ static int git_blame_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "blame.ignorerevsfile")) {
-		const char *str;
+		char *str = NULL;
 		int ret;
 
-		ret = git_config_pathname(&str, var, value);
+		ret = git_config_pathname_dup(&str, var, value);
 		if (ret)
 			return ret;
 		string_list_insert(&ignore_revs_file_list, str);
diff --git a/builtin/config.c b/builtin/config.c
index 0015620dde..fc5d96bb4c 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -282,11 +282,11 @@ static int format_config(struct strbuf *buf, const char *key_,
 			else
 				strbuf_addstr(buf, v ? "true" : "false");
 		} else if (type == TYPE_PATH) {
-			const char *v;
-			if (git_config_pathname(&v, key_, value_) < 0)
+			char *v = NULL;
+			if (git_config_pathname_dup(&v, key_, value_) < 0)
 				return -1;
 			strbuf_addstr(buf, v);
-			free((char *)v);
+			free(v);
 		} else if (type == TYPE_EXPIRY_DATE) {
 			timestamp_t t;
 			if (git_config_expiry_date(&t, key_, value_) < 0)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 56d8a77ed7..15ed81a3f6 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.fsck.skiplist") == 0) {
-		const char *path;
+		char *path = NULL;
 
-		if (git_config_pathname(&path, var, value))
+		if (git_config_pathname_dup(&path, var, value))
 			return 1;
 		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
 			fsck_msg_types.len ? ',' : '=', path);
-		free((char *)path);
+		free(path);
 		return 0;
 	}
 
diff --git a/fetch-pack.c b/fetch-pack.c
index 091f9a80a9..fd59c497b4 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1863,13 +1863,13 @@ static int fetch_pack_config_cb(const char *var, const char *value,
 	const char *msg_id;
 
 	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
-		const char *path;
+		char *path = NULL;
 
-		if (git_config_pathname(&path, var, value))
+		if (git_config_pathname_dup(&path, var, value))
 			return 1;
 		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
 			fsck_msg_types.len ? ',' : '=', path);
-		free((char *)path);
+		free(path);
 		return 0;
 	}
 
diff --git a/fsck.c b/fsck.c
index 78af29d264..a9d27a660f 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1274,13 +1274,13 @@ int git_fsck_config(const char *var, const char *value,
 	const char *msg_id;
 
 	if (strcmp(var, "fsck.skiplist") == 0) {
-		const char *path;
+		char *path = NULL;
 		struct strbuf sb = STRBUF_INIT;
 
-		if (git_config_pathname(&path, var, value))
+		if (git_config_pathname_dup(&path, var, value))
 			return 1;
 		strbuf_addf(&sb, "skiplist=%s", path);
-		free((char *)path);
+		free(path);
 		fsck_set_msg_types(options, sb.buf);
 		strbuf_release(&sb);
 		return 0;
diff --git a/remote.c b/remote.c
index 2b650b813b..09912bebf1 100644
--- a/remote.c
+++ b/remote.c
@@ -428,38 +428,38 @@ static int handle_config(const char *key, const char *value,
 	else if (!strcmp(subkey, "prunetags"))
 		remote->prune_tags = git_config_bool(key, value);
 	else if (!strcmp(subkey, "url")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		add_url(remote, v);
 	} else if (!strcmp(subkey, "pushurl")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		add_pushurl(remote, v);
 	} else if (!strcmp(subkey, "push")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		refspec_append(&remote->push, v);
-		free((char *)v);
+		free(v);
 	} else if (!strcmp(subkey, "fetch")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		refspec_append(&remote->fetch, v);
-		free((char *)v);
+		free(v);
 	} else if (!strcmp(subkey, "receivepack")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		if (!remote->receivepack)
 			remote->receivepack = v;
 		else
 			error(_("more than one receivepack given, using the first"));
 	} else if (!strcmp(subkey, "uploadpack")) {
-		const char *v;
-		if (git_config_string(&v, key, value))
+		char *v = NULL;
+		if (git_config_string_dup(&v, key, value))
 			return -1;
 		if (!remote->uploadpack)
 			remote->uploadpack = v;
diff --git a/setup.c b/setup.c
index f4b32f76e3..9f35a27978 100644
--- a/setup.c
+++ b/setup.c
@@ -1176,13 +1176,13 @@ static int safe_directory_cb(const char *key, const char *value,
 	} else if (!strcmp(value, "*")) {
 		data->is_safe = 1;
 	} else {
-		const char *interpolated = NULL;
+		char *interpolated = NULL;
 
-		if (!git_config_pathname(&interpolated, key, value) &&
+		if (!git_config_pathname_dup(&interpolated, key, value) &&
 		    !fspathcmp(data->path, interpolated ? interpolated : value))
 			data->is_safe = 1;
 
-		free((char *)interpolated);
+		free(interpolated);
 	}
 
 	return 0;
-- 
2.44.0.872.g288abe5b5b



^ permalink raw reply related	[relevance 13%]

* [PATCH 0/12] git_config_string() considered harmful
    2024-04-06 19:17  6% ` [WIP] git_config_pathname() leakfix Junio C Hamano
@ 2024-04-07  0:56  2% ` Jeff King
  2024-04-07  1:00 13%   ` [PATCH 03/11] config: prefer git_config_string_dup() for temp variables Jeff King
  1 sibling, 1 reply; 200+ results
From: Jeff King @ 2024-04-07  0:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Rubén Justo

On Sat, Apr 06, 2024 at 11:11:12AM -0700, Junio C Hamano wrote:

> The excludes_file variable is marked "const char *", but all the
> assignments to it are made with a piece of memory allocated just
> for it, and the variable is responsible for owning it.
> 
> When "core.excludesfile" is read, the code just lost the previous
> value, leaking memory.  Plug it.
> 
> The real problem is that the variable is mistyped; our convention
> is to never make a variable that owns the piece of memory pointed
> by it as "const".  Fixing that would reduce the chance of this kind
> of bug happening, and also would make it unnecessary to cast the
> constness away while free()ing it, but that would be a much larger
> follow-up effort.

As you noticed in your follow-up, this is just the tip of the iceberg.
And it's not just git_config_pathname(), but really git_config_string(),
and it is a potential problem for almost every call.

I have a series that I started a few months ago to try to improve this,
but I never sent it in because I didn't have a good solution for the
long tail of variables where we assign a string literal as the default.

But that doesn't mean we can't incrementally make things better. So I
polished it up a bit, and will send the result in a minute.

Looking at your sketch, I think I glossed over the parse-options
OPT_FILENAME_DUP() issue. In practice it's OK because we wouldn't
generally re-read the config after parsing the options. But leaving it
does seem rather ugly, and your solution looks reasonable. I'm not sure
if there's an easy way to get the compiler to point to spots which need
it; the type information is all lost when parse-options passes
everything through a void pointer.

(I remember a while ago looking at retaining type annotations for
parse-options; this could be another use case for that).

I think it would also be useful if we could enable -Wwrite-strings to
catch cases where string literals are assigned to non-const pointers.
But there's some cleanup/refactoring to get that to compile cleanly.

  [01/11]: config: make sequencer.c's git_config_string_dup() public
  [02/11]: config: add git_config_pathname_dup()
  [03/11]: config: prefer git_config_string_dup() for temp variables
  [04/11]: config: use git_config_string_dup() for open-coded equivalents
  [05/11]: config: use git_config_string_dup() to fix leaky open coding
  [06/11]: config: use git_config_string() in easy cases
  [07/11]: config: use git_config_pathname_dup() in easy cases
  [08/11]: http: use git_config_string_dup()
  [09/11]: merge: use git_config_string_dup() for pull strategies
  [10/11]: userdiff: use git_config_string_dup() when we can
  [11/11]: blame: use "dup" string_list for ignore-revs files

 alias.c                |   3 +-
 archive-tar.c          |  10 ++--
 attr.c                 |   2 +-
 attr.h                 |   2 +-
 builtin/blame.c        |   7 +--
 builtin/commit.c       |   8 ++--
 builtin/config.c       |   6 +--
 builtin/help.c         |   7 +--
 builtin/log.c          |  16 +++----
 builtin/merge.c        |  12 ++---
 builtin/receive-pack.c |  10 ++--
 builtin/repack.c       |  16 +++----
 compat/mingw.c         |   7 +--
 config.c               |  48 ++++++++++++-------
 config.h               |  19 ++++++++
 convert.c              |  12 ++---
 delta-islands.c        |   4 +-
 diff.c                 |  12 ++---
 environment.c          |  14 +++---
 environment.h          |  14 +++---
 fetch-pack.c           |   6 +--
 fsck.c                 |   6 +--
 gpg-interface.c        |   9 ++--
 http.c                 | 105 +++++++++++++++++++----------------------
 imap-send.c            |  20 ++++----
 mailmap.c              |   4 +-
 mailmap.h              |   4 +-
 merge-ll.c             |  17 +++----
 pager.c                |   4 +-
 promisor-remote.c      |   2 +-
 promisor-remote.h      |   2 +-
 remote.c               |  45 +++++++++---------
 remote.h               |   8 ++--
 sequencer.c            |  12 +----
 setup.c                |  11 ++---
 upload-pack.c          |   4 +-
 userdiff.c             |   6 +--
 userdiff.h             |   6 +--
 38 files changed, 251 insertions(+), 249 deletions(-)

-Peff


^ permalink raw reply	[relevance 2%]

* [WIP] git_config_pathname() leakfix
  @ 2024-04-06 19:17  6% ` Junio C Hamano
  2024-04-07  0:56  2% ` [PATCH 0/12] git_config_string() considered harmful Jeff King
  1 sibling, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-06 19:17 UTC (permalink / raw)
  To: git

The git_config_pathname() function takes a pointer to "const char *"
that specifies where to store the resulting pathname string, but
always assigns a newly assigned value to it.  The caller has to cast
the constness away if it wants to call free() on it, e.g.,

	const char *str;
	int ret = git_config_pathname(&str, var, value);
	use(str)
	free((const char *)str);

Here is a not-working WIP just to show the scale of fallout from an
exercise to plug leaks of returned value from git_config_pathname().
It revealed a few more issues around memory ownership rules.

 * OPTION_FILENAME of parse-options API assigns the string from the
   command line argument directly to the supplied variable.  The
   variable obviously does not own the value and cannot and does not
   have to free() it.  It however is very common to use the same
   variable to also hold a string read from the config API, via
   git_config_pathname(), which wants the caller to own the string.

   In this sketch, I attempted to introduce OPTION_FILENAME_DUP and
   have the parse-options API to treat the destination variable as
   owning the string (i.e. free the existing contents before
   assigning the value from the command line, and xstrdup() the
   string from the command line option), but I am sure I missed
   some (which will lead to segfault when a caller later tries
   to free() it before calling git_config_pathname()) and/or 
   converted too many (which will lead to leaks when a caller
   later does not free() it).

 * http.c has a private set_from_env() helper function, which lets
   the callers to borrow memory from the environment.  But some
   variables that receive value from this mechanism also receive
   pathnames from configuration via git_config_pathname().

   In this sketch, I attempted to solve it by introducing
   dup_from_env() and changed callers that wants to overwrite the
   variable with a value obtained from git_config_pathname().

Some readers might wonder if it is easier to go in the other
direction, i.e. allowing cllaers of git_config_pathname() to
borrow and not worry about freeing, but this is fundamentally
impossible as expanding ~/.gitconfig into /home/jch/.gitconfig
and the like is the central part of its function.

In any case, I am not going to finish this soonish, but I think I
found some existing leaks that can be fixed _without_ going through
the whole nine yards, so perhaps I'll see if I can salvage small
changes like that as "preliminary clean-up".

 builtin/blame.c        |  3 ++-
 builtin/commit.c       |  4 ++--
 builtin/config.c       |  4 ++--
 builtin/log.c          |  4 ++--
 builtin/receive-pack.c |  4 ++--
 config.c               |  8 ++++----
 config.h               |  8 ++++----
 diff.c                 |  2 +-
 environment.c          |  6 +++---
 environment.h          |  6 +++---
 fetch-pack.c           |  4 ++--
 fsck.c                 |  4 ++--
 fsmonitor-settings.c   |  8 ++++++--
 gpg-interface.c        |  3 ++-
 http.c                 | 29 +++++++++++++++++++----------
 mailmap.c              |  2 +-
 mailmap.h              |  2 +-
 parse-options.c        | 14 ++++++++++++--
 parse-options.h        | 11 ++++++++++-
 setup.c                |  8 ++++----
 20 files changed, 84 insertions(+), 50 deletions(-)

diff --git c/builtin/blame.c w/builtin/blame.c
index db1f56de61..23810323e6 100644
--- c/builtin/blame.c
+++ w/builtin/blame.c
@@ -718,13 +718,14 @@ static int git_blame_config(const char *var, const char *value,
 		return 0;
 	}
 	if (!strcmp(var, "blame.ignorerevsfile")) {
-		const char *str;
+		char *str;
 		int ret;
 
 		ret = git_config_pathname(&str, var, value);
 		if (ret)
 			return ret;
 		string_list_insert(&ignore_revs_file_list, str);
+		free(str);
 		return 0;
 	}
 	if (!strcmp(var, "blame.markunblamablelines")) {
diff --git c/builtin/commit.c w/builtin/commit.c
index 7ba7201cfb..a35c524458 100644
--- c/builtin/commit.c
+++ w/builtin/commit.c
@@ -107,7 +107,7 @@ static enum {
 } commit_style;
 
 static const char *logfile, *force_author;
-static const char *template_file;
+static char *template_file;
 /*
  * The _message variables are commit names from which to take
  * the commit message and/or authorship.
@@ -1319,7 +1319,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
 				  !!use_message, "-C",
 				  !!logfile, "-F");
 	if (use_message || edit_message || logfile ||fixup_message || have_option_m)
-		template_file = NULL;
+		FREE_AND_NULL(template_file);
 	if (edit_message)
 		use_message = edit_message;
 	if (amend && !use_message && !fixup_message)
diff --git c/builtin/config.c w/builtin/config.c
index 0015620dde..739d3beb57 100644
--- c/builtin/config.c
+++ w/builtin/config.c
@@ -282,11 +282,11 @@ static int format_config(struct strbuf *buf, const char *key_,
 			else
 				strbuf_addstr(buf, v ? "true" : "false");
 		} else if (type == TYPE_PATH) {
-			const char *v;
+			char *v;
 			if (git_config_pathname(&v, key_, value_) < 0)
 				return -1;
 			strbuf_addstr(buf, v);
-			free((char *)v);
+			free(v);
 		} else if (type == TYPE_EXPIRY_DATE) {
 			timestamp_t t;
 			if (git_config_expiry_date(&t, key_, value_) < 0)
diff --git c/builtin/log.c w/builtin/log.c
index c0a8bb95e9..e90d3f13e7 100644
--- c/builtin/log.c
+++ w/builtin/log.c
@@ -957,7 +957,7 @@ static int do_signoff;
 static enum auto_base_setting auto_base;
 static char *from;
 static const char *signature = git_version_string;
-static const char *signature_file;
+static char *signature_file;
 static enum cover_setting config_cover_letter;
 static const char *config_output_directory;
 static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
@@ -1981,7 +1981,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"),
 			       N_("add prerequisite tree info to the patch series"),
 			       0, base_callback),
-		OPT_FILENAME(0, "signature-file", &signature_file,
+		OPT_FILENAME_DUP(0, "signature-file", &signature_file,
 				N_("add a signature from a file")),
 		OPT__QUIET(&quiet, N_("don't print the patch filenames")),
 		OPT_BOOL(0, "progress", &show_progress,
diff --git c/builtin/receive-pack.c w/builtin/receive-pack.c
index 56d8a77ed7..f5e55bad51 100644
--- c/builtin/receive-pack.c
+++ w/builtin/receive-pack.c
@@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value,
 	}
 
 	if (strcmp(var, "receive.fsck.skiplist") == 0) {
-		const char *path;
+		char *path;
 
 		if (git_config_pathname(&path, var, value))
 			return 1;
 		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
 			fsck_msg_types.len ? ',' : '=', path);
-		free((char *)path);
+		free(path);
 		return 0;
 	}
 
diff --git c/config.c w/config.c
index ae3652b08f..683acbe235 100644
--- c/config.c
+++ w/config.c
@@ -1345,7 +1345,7 @@ int git_config_string(const char **dest, const char *var, const char *value)
 	return 0;
 }
 
-int git_config_pathname(const char **dest, const char *var, const char *value)
+int git_config_pathname(char **dest, const char *var, const char *value)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -2482,7 +2482,7 @@ int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *d
 		return 1;
 }
 
-int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, char **dest)
 {
 	const char *value;
 	if (!git_configset_get_value(set, key, &value, NULL))
@@ -2627,7 +2627,7 @@ int repo_config_get_maybe_bool(struct repository *repo,
 }
 
 int repo_config_get_pathname(struct repository *repo,
-			     const char *key, const char **dest)
+			     const char *key, char **dest)
 {
 	int ret;
 	git_config_check_init(repo);
@@ -2726,7 +2726,7 @@ int git_config_get_maybe_bool(const char *key, int *dest)
 	return repo_config_get_maybe_bool(the_repository, key, dest);
 }
 
-int git_config_get_pathname(const char *key, const char **dest)
+int git_config_get_pathname(const char *key, char **dest)
 {
 	return repo_config_get_pathname(the_repository, key, dest);
 }
diff --git c/config.h w/config.h
index f4966e3749..868057c77f 100644
--- c/config.h
+++ w/config.h
@@ -286,7 +286,7 @@ int git_config_string(const char **, const char *, const char *);
  * Similar to `git_config_string`, but expands `~` or `~user` into the
  * user's home directory when found at the beginning of the path.
  */
-int git_config_pathname(const char **, const char *, const char *);
+int git_config_pathname(char **, const char *, const char *);
 
 int git_config_expiry_date(timestamp_t *, const char *, const char *);
 int git_config_color(char *, const char *, const char *);
@@ -541,7 +541,7 @@ int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned lon
 int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
 int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
 int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, char **dest);
 
 /* Functions for reading a repository's config */
 struct repository;
@@ -577,7 +577,7 @@ int repo_config_get_bool_or_int(struct repository *repo,
 int repo_config_get_maybe_bool(struct repository *repo,
 			       const char *key, int *dest);
 int repo_config_get_pathname(struct repository *repo,
-			     const char *key, const char **dest);
+			     const char *key, char **dest);
 
 /*
  * Functions for reading protected config. By definition, protected
@@ -687,7 +687,7 @@ int git_config_get_maybe_bool(const char *key, int *dest);
  * Similar to `git_config_get_string`, but expands `~` or `~user` into
  * the user's home directory when found at the beginning of the path.
  */
-int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_pathname(const char *key, char **dest);
 
 int git_config_get_index_threads(int *dest);
 int git_config_get_split_index(void);
diff --git c/diff.c w/diff.c
index 108c187577..e8754888f4 100644
--- c/diff.c
+++ w/diff.c
@@ -58,7 +58,7 @@ static int diff_context_default = 3;
 static int diff_interhunk_context_default;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
-static const char *diff_order_file_cfg;
+static char *diff_order_file_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
diff --git c/environment.c w/environment.c
index a73ba9c12c..279ea3fd5e 100644
--- c/environment.c
+++ w/environment.c
@@ -46,8 +46,8 @@ const char *git_commit_encoding;
 const char *git_log_output_encoding;
 char *apply_default_whitespace;
 char *apply_default_ignorewhitespace;
-const char *git_attributes_file;
-const char *git_hooks_path;
+char *git_attributes_file;
+char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
@@ -60,7 +60,7 @@ size_t delta_base_cache_limit = 96 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
 const char *editor_program;
 const char *askpass_program;
-const char *excludes_file;
+char *excludes_file;
 enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
diff --git c/environment.h w/environment.h
index 05fd94d7be..c0e0c8b1f9 100644
--- c/environment.h
+++ w/environment.h
@@ -124,8 +124,8 @@ extern int warn_ambiguous_refs;
 extern int warn_on_object_refname_ambiguity;
 extern char *apply_default_whitespace;
 extern char *apply_default_ignorewhitespace;
-extern const char *git_attributes_file;
-extern const char *git_hooks_path;
+extern char *git_attributes_file;
+extern char *git_hooks_path;
 extern int zlib_compression_level;
 extern int pack_compression_level;
 extern size_t packed_git_window_size;
@@ -222,7 +222,7 @@ extern const char *git_log_output_encoding;
 
 extern const char *editor_program;
 extern const char *askpass_program;
-extern const char *excludes_file;
+extern char *excludes_file;
 
 /*
  * Should we print an ellipsis after an abbreviated SHA-1 value
diff --git c/fetch-pack.c w/fetch-pack.c
index 091f9a80a9..f7579cb3bc 100644
--- c/fetch-pack.c
+++ w/fetch-pack.c
@@ -1863,13 +1863,13 @@ static int fetch_pack_config_cb(const char *var, const char *value,
 	const char *msg_id;
 
 	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
-		const char *path;
+		char *path;
 
 		if (git_config_pathname(&path, var, value))
 			return 1;
 		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
 			fsck_msg_types.len ? ',' : '=', path);
-		free((char *)path);
+		free(path);
 		return 0;
 	}
 
diff --git c/fsck.c w/fsck.c
index 78af29d264..dd0a33028e 100644
--- c/fsck.c
+++ w/fsck.c
@@ -1274,13 +1274,13 @@ int git_fsck_config(const char *var, const char *value,
 	const char *msg_id;
 
 	if (strcmp(var, "fsck.skiplist") == 0) {
-		const char *path;
+		char *path;
 		struct strbuf sb = STRBUF_INIT;
 
 		if (git_config_pathname(&path, var, value))
 			return 1;
 		strbuf_addf(&sb, "skiplist=%s", path);
-		free((char *)path);
+		free(path);
 		fsck_set_msg_types(options, sb.buf);
 		strbuf_release(&sb);
 		return 0;
diff --git c/fsmonitor-settings.c w/fsmonitor-settings.c
index a6a9e6bc19..20e9347907 100644
--- c/fsmonitor-settings.c
+++ w/fsmonitor-settings.c
@@ -129,8 +129,12 @@ static void lookup_fsmonitor_settings(struct repository *r)
 		break;
 
 	case -1: /* config value set to an arbitrary string */
-		if (repo_config_get_pathname(r, "core.fsmonitor", &const_str))
-			return; /* should not happen */
+		{
+			char *str;
+			if (repo_config_get_pathname(r, "core.fsmonitor", &str))
+				return; /* should not happen */
+			const_str = str;
+		}
 		break;
 
 	default: /* should not happen */
diff --git c/gpg-interface.c w/gpg-interface.c
index b5993385ff..87e214c76e 100644
--- c/gpg-interface.c
+++ w/gpg-interface.c
@@ -27,7 +27,8 @@ static void gpg_interface_lazy_init(void)
 }
 
 static char *configured_signing_key;
-static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
+static const char *ssh_default_key_command;
+static char *ssh_allowed_signers, *ssh_revocation_file;
 static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
 
 struct gpg_format {
diff --git c/http.c w/http.c
index e73b136e58..b03a9a169d 100644
--- c/http.c
+++ w/http.c
@@ -39,7 +39,7 @@ char curl_errorstr[CURL_ERROR_SIZE];
 static int curl_ssl_verify = -1;
 static int curl_ssl_try;
 static const char *curl_http_version = NULL;
-static const char *ssl_cert;
+static char *ssl_cert;
 static const char *ssl_cert_type;
 static const char *ssl_cipherlist;
 static const char *ssl_version;
@@ -59,14 +59,14 @@ static struct {
 	{ "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
 #endif
 };
-static const char *ssl_key;
+static char *ssl_key;
 static const char *ssl_key_type;
-static const char *ssl_capath;
+static char *ssl_capath;
 static const char *curl_no_proxy;
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
-static const char *ssl_pinnedkey;
+static char *ssl_pinnedkey;
 #endif
-static const char *ssl_cainfo;
+static char *ssl_cainfo;
 static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
@@ -108,7 +108,7 @@ static struct {
 
 static struct credential proxy_auth = CREDENTIAL_INIT;
 static const char *curl_proxyuserpwd;
-static const char *curl_cookie_file;
+static char *curl_cookie_file;
 static int curl_save_cookies;
 struct credential http_auth = CREDENTIAL_INIT;
 static int http_proactive_auth;
@@ -1215,6 +1215,15 @@ static void set_from_env(const char **var, const char *envname)
 		*var = val;
 }
 
+static void dup_from_env(char **var, const char *envname)
+{
+	const char *val = getenv(envname);
+	if (val) {
+		free(*var);
+		*var = strdup(val);
+	}
+}
+
 void http_init(struct remote *remote, const char *url, int proactive_auth)
 {
 	char *low_speed_limit;
@@ -1291,12 +1300,12 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 	if (getenv("GIT_SSL_NO_VERIFY"))
 		curl_ssl_verify = 0;
 
-	set_from_env(&ssl_cert, "GIT_SSL_CERT");
+	dup_from_env(&ssl_cert, "GIT_SSL_CERT");
 	set_from_env(&ssl_cert_type, "GIT_SSL_CERT_TYPE");
-	set_from_env(&ssl_key, "GIT_SSL_KEY");
+	dup_from_env(&ssl_key, "GIT_SSL_KEY");
 	set_from_env(&ssl_key_type, "GIT_SSL_KEY_TYPE");
-	set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
-	set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
+	dup_from_env(&ssl_capath, "GIT_SSL_CAPATH");
+	dup_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
 	set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
 
diff --git c/mailmap.c w/mailmap.c
index 3d6a5e9400..044466b043 100644
--- c/mailmap.c
+++ w/mailmap.c
@@ -6,7 +6,7 @@
 #include "object-store-ll.h"
 #include "setup.h"
 
-const char *git_mailmap_file;
+char *git_mailmap_file;
 const char *git_mailmap_blob;
 
 struct mailmap_info {
diff --git c/mailmap.h w/mailmap.h
index 0f8fd2c586..429a760945 100644
--- c/mailmap.h
+++ w/mailmap.h
@@ -3,7 +3,7 @@
 
 struct string_list;
 
-extern const char *git_mailmap_file;
+extern char *git_mailmap_file;
 extern const char *git_mailmap_blob;
 
 int read_mailmap(struct string_list *map);
diff --git c/parse-options.c w/parse-options.c
index 30b9e68f8a..94c1e52b60 100644
--- c/parse-options.c
+++ w/parse-options.c
@@ -64,8 +64,11 @@ static void fix_filename(const char *prefix, char **file)
 {
 	if (!file || !*file)
 		; /* leave as NULL */
-	else
-		*file = prefix_filename_except_for_dash(prefix, *file);
+	else {
+		char *tmp = prefix_filename_except_for_dash(prefix, *file);
+		free(*file);
+		*file = tmp;
+	}
 }
 
 static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
@@ -128,6 +131,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 			return get_arg(p, opt, flags, (const char **)opt->value);
 		return 0;
 
+	case OPTION_FILENAME_DUP:
+		FREE_AND_NULL(*(char **)opt->value);
+		/* fallthru */
 	case OPTION_FILENAME:
 		err = 0;
 		if (unset)
@@ -137,6 +143,8 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 		else
 			err = get_arg(p, opt, flags, (const char **)opt->value);
 
+		if (opt->type == OPTION_FILENAME_DUP)
+			*(char **)opt->value = xstrdup_or_null(*(const char **)opt->value);
 		if (!err)
 			fix_filename(p->prefix, (char **)opt->value);
 		return err;
@@ -649,6 +657,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all,
 		switch (opts->type) {
 		case OPTION_STRING:
 		case OPTION_FILENAME:
+		case OPTION_FILENAME_DUP:
 		case OPTION_INTEGER:
 		case OPTION_MAGNITUDE:
 		case OPTION_CALLBACK:
@@ -701,6 +710,7 @@ static int show_gitcomp(const struct option *opts, int show_all)
 			continue;
 		case OPTION_STRING:
 		case OPTION_FILENAME:
+		case OPTION_FILENAME_DUP:
 		case OPTION_INTEGER:
 		case OPTION_MAGNITUDE:
 		case OPTION_CALLBACK:
diff --git c/parse-options.h w/parse-options.h
index bd62e20268..e78f72f986 100644
--- c/parse-options.h
+++ w/parse-options.h
@@ -26,7 +26,8 @@ enum parse_opt_type {
 	OPTION_MAGNITUDE,
 	OPTION_CALLBACK,
 	OPTION_LOWLEVEL_CALLBACK,
-	OPTION_FILENAME
+	OPTION_FILENAME,
+	OPTION_FILENAME_DUP
 };
 
 enum parse_opt_flags {
@@ -330,6 +331,14 @@ struct option {
 	.argh = N_("file"), \
 	.help = (h), \
 }
+#define OPT_FILENAME_DUP(s, l, v, h) { \
+	.type = OPTION_FILENAME_DUP, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("file"), \
+	.help = (h), \
+}
 #define OPT_COLOR_FLAG(s, l, v, h) { \
 	.type = OPTION_CALLBACK, \
 	.short_name = (s), \
diff --git c/setup.c w/setup.c
index f4b32f76e3..d99856ac81 100644
--- c/setup.c
+++ w/setup.c
@@ -1176,13 +1176,13 @@ static int safe_directory_cb(const char *key, const char *value,
 	} else if (!strcmp(value, "*")) {
 		data->is_safe = 1;
 	} else {
-		const char *interpolated = NULL;
+		char *interpolated = NULL;
 
 		if (!git_config_pathname(&interpolated, key, value) &&
 		    !fspathcmp(data->path, interpolated ? interpolated : value))
 			data->is_safe = 1;
 
-		free((char *)interpolated);
+		free(interpolated);
 	}
 
 	return 0;
@@ -2023,7 +2023,7 @@ static int create_default_files(const char *template_path,
 	char *path;
 	int reinit;
 	int filemode;
-	const char *init_template_dir = NULL;
+	char *init_template_dir = NULL;
 	const char *work_tree = get_git_work_tree();
 
 	/*
@@ -2037,7 +2037,7 @@ static int create_default_files(const char *template_path,
 	 */
 	git_config_get_pathname("init.templatedir", &init_template_dir);
 	copy_templates(template_path, init_template_dir);
-	free((char *)init_template_dir);
+	free(init_template_dir);
 	git_config_clear();
 	reset_shared_repository();
 	git_config(git_default_config, NULL);


^ permalink raw reply related	[relevance 6%]

* Re: What's cooking in git.git (Apr 2024, #03; Fri, 5)
  2024-04-06  5:03  0%   ` Junio C Hamano
@ 2024-04-06  8:37  0%     ` Dragan Simic
  0 siblings, 0 replies; 200+ results
From: Dragan Simic @ 2024-04-06  8:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On 2024-04-06 07:03, Junio C Hamano wrote:
> Dragan Simic <dsimic@manjaro.org> writes:
> 
>>> * ds/fetch-config-parse-microfix (2024-04-05) 1 commit
>>>  - fetch: return when parsing submodule.recurse
>>>  A config parser callback function fell through instead of returning
>>>  after recognising and processing a variable, wasting cycles, which
>>>  has been corrected.
>>>  Will merge to 'next'.
>>>  source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>
>> 
>> Isn't this an example of a prefix collision, i.e. "ds/" points
>> to two different contributors?
> 
> "ls .git/objects/" would tell you that no object is important enough
> to squat on a single "prefix" and exclude others, and two objects in
> 00/ hierarchy are by no means closer together than an object in 00/
> hierarchy and another object in 01/ hiearchy.  The idea to use the
> fan-out in the ref namespace is pretty much the same.

Ah, sorry, I wasn't precise enough, please let me explain.  When
I wrote "prefix", I had its use by humans in mind, e.g. for searching
the "what's cooking" emails by hand, to see the statuses of one's
patches.  I mean, that's what I do -- by searching for "ds/", I can
easily find the statuses of my patches in "what's cooking" emails.
Does that make sense?


^ permalink raw reply	[relevance 0%]

* Re: What's cooking in git.git (Apr 2024, #03; Fri, 5)
  2024-04-06  1:11  0% ` Dragan Simic
@ 2024-04-06  5:03  0%   ` Junio C Hamano
  2024-04-06  8:37  0%     ` Dragan Simic
  0 siblings, 1 reply; 200+ results
From: Junio C Hamano @ 2024-04-06  5:03 UTC (permalink / raw)
  To: Dragan Simic; +Cc: git

Dragan Simic <dsimic@manjaro.org> writes:

>> * ds/fetch-config-parse-microfix (2024-04-05) 1 commit
>>  - fetch: return when parsing submodule.recurse
>>  A config parser callback function fell through instead of returning
>>  after recognising and processing a variable, wasting cycles, which
>>  has been corrected.
>>  Will merge to 'next'.
>>  source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>
>
> Isn't this an example of a prefix collision, i.e. "ds/" points
> to two different contributors?

"ls .git/objects/" would tell you that no object is important enough
to squat on a single "prefix" and exclude others, and two objects in
00/ hierarchy are by no means closer together than an object in 00/
hierarchy and another object in 01/ hiearchy.  The idea to use the
fan-out in the ref namespace is pretty much the same.





^ permalink raw reply	[relevance 0%]

* [PATCH v2 0/8] docs: recommend using contrib/contacts/git-contacts
  2024-04-02  0:20  3% [PATCH] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
                   ` (2 preceding siblings ...)
       [not found]     ` <35192e61-c442-6719-caf0-1019bf3e44c9@live.de>
@ 2024-04-06  1:22  3% ` Linus Arver via GitGitGadget
  2024-04-09 21:56  3%   ` [PATCH v3 " Linus Arver via GitGitGadget
  3 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-06  1:22 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Patrick Steinhardt, Matthieu Moy, Linus Arver

Make git-contacts more prominent in our docs.


Notable changes in v2
=====================

 * Improve existing mention of git-contacts in SubmittingPatches (instead of
   adding a separate, entirely new paragraph)
 * Add example usage of integrating git-contacts with git-send-email with
   the latter's --cc-cmd flag.
 * Various smaller fixes to SubmittingPatches

Linus Arver (8):
  MyFirstContribution: mention contrib/contacts/git-contacts
  SubmittingPatches: make 'git contacts' grep-friendly
  SubmittingPatches: mention GitGitGadget
  SubmittingPatches: quote commands
  SubmittingPatches: discuss reviewers first
  SubmittingPatches: dedupe discussion of security patches
  SubmittingPatches: add heading for format-patch and send-email
  SubmittingPatches: demonstrate using git-contacts with git-send-email

 Documentation/MyFirstContribution.txt |  5 ++
 Documentation/SubmittingPatches       | 68 +++++++++++++++------------
 2 files changed, 43 insertions(+), 30 deletions(-)


base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

Range-diff vs v1:

 1:  7f1ac742008 ! 1:  3817e7f3cd0 docs: recommend using contrib/contacts/git-contacts
     @@ Metadata
      Author: Linus Arver <linusa@google.com>
      
       ## Commit message ##
     -    docs: recommend using contrib/contacts/git-contacts
     +    MyFirstContribution: mention contrib/contacts/git-contacts
      
          Although we've had this script since 4d06402b1b (contrib: add
          git-contacts helper, 2013-07-21), we don't mention it in our
     @@ Documentation/MyFirstContribution.txt: $ git send-email --to=target@example.com
       valuable, such as changing the Reply-to address or adding more CC and BCC lines.
       
      +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
     -+include in the CC list.
     ++include in the CC list. In addition, you can do `git send-email --cc-cmd='git
     ++contacts' feature/*.patch` to automatically pass this list of emails to
     ++`send-email`.
      +
       NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
       please don't send your patchset from the tutorial to the real mailing list! For
       now, you can send it to yourself, to make sure you understand how it will look.
     -
     - ## Documentation/SubmittingPatches ##
     -@@ Documentation/SubmittingPatches: an explanation of changes between each iteration can be kept in
     - Git-notes and inserted automatically following the three-dash
     - line via `git format-patch --notes`.
     - 
     -+[[suggested-reviewers]]
     -+Use `contrib/contacts/git-contacts` to get a list of reviewers you should
     -+include in the CC list.
     -+
     - [[attachment]]
     - Do not attach the patch as a MIME attachment, compressed or not.
     - Do not let your e-mail client send quoted-printable.  Do not let
 -:  ----------- > 2:  82e5e05288d SubmittingPatches: make 'git contacts' grep-friendly
 -:  ----------- > 3:  6e6950afa6e SubmittingPatches: mention GitGitGadget
 -:  ----------- > 4:  fb06d5ce247 SubmittingPatches: quote commands
 -:  ----------- > 5:  a8abcf45881 SubmittingPatches: discuss reviewers first
 -:  ----------- > 6:  326afe13315 SubmittingPatches: dedupe discussion of security patches
 -:  ----------- > 7:  09f4e7ad123 SubmittingPatches: add heading for format-patch and send-email
 -:  ----------- > 8:  b35748f0cf8 SubmittingPatches: demonstrate using git-contacts with git-send-email

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: What's cooking in git.git (Apr 2024, #03; Fri, 5)
  2024-04-05 19:13  1% What's cooking in git.git (Apr 2024, #03; Fri, 5) Junio C Hamano
@ 2024-04-06  1:11  0% ` Dragan Simic
  2024-04-06  5:03  0%   ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Dragan Simic @ 2024-04-06  1:11 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On 2024-04-05 21:13, Junio C Hamano wrote:
> * ds/send-email-per-message-block (2024-04-05) 2 commits
>  - SQUASH??? switch to separator semantics
>  - send-email: make it easy to discern the messages for each patch
> 
>  "git send-email" learned to separate its reports on each message it
>  sends out with an extra blank line in between.
> 
>  Expecting a reroll.
>  cf. <8d47bd687f2ad80bbc1e1c86ae337327@manjaro.org>
>  source: 
> <0e087ed992def0746f3d437253248904c2126464.1712262791.git.dsimic@manjaro.org>

Done, the v3 is on the mailing list. [1]

[1] 
https://lore.kernel.org/git/e3212c0a4ad331685c68c13afcdbced20982ab32.1712364420.git.dsimic@manjaro.org/

> * ds/fetch-config-parse-microfix (2024-04-05) 1 commit
>  - fetch: return when parsing submodule.recurse
> 
>  A config parser callback function fell through instead of returning
>  after recognising and processing a variable, wasting cycles, which
>  has been corrected.
> 
>  Will merge to 'next'.
>  source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>

Isn't this an example of a prefix collision, i.e. "ds/" points
to two different contributors?


^ permalink raw reply	[relevance 0%]

* [PATCH] feat(log): add option to search for header or body to `git log`
@ 2024-04-05 21:48  3% Max Coplan via GitGitGadget
  2024-04-07  3:24  2% ` [PATCH v2] log: add option to search for header or body Max Coplan via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Max Coplan via GitGitGadget @ 2024-04-05 21:48 UTC (permalink / raw)
  To: git; +Cc: Max Coplan, Max 👨🏽‍💻 Coplan

From: =?UTF-8?q?Max=20=F0=9F=91=A8=F0=9F=8F=BD=E2=80=8D=F0=9F=92=BB=20Copl?=
 =?UTF-8?q?an?= <mchcopl@gmail.com>

Note to reviewer: I hate the name `--header-or`!  Please help me come up
with a better name.

Summary:
This change adds a new option to `git log` that allows users to search
for commits that match either the author or the commit message. This is
useful for finding commits that were either authored or co-authored by a
specific person.

Currently, the best way to find a commit either authored or co-authored
by a specific person is to use

```sh
echo \
    $(git log --author=Torvalds --pretty="%cd,%H\n" --date=iso-strict) \
    $(git log --grep="Co-authored-by: .*Torvalds" --pretty="%cd,%H\n" --date=iso-strict) \
| sort -n --reverse \
| awk -F, '{print $2}' \
| tr '\n' '\t' \
| xargs git show --stat --stdin
```

This is a bit of a pain, so this change adds a new option to `git log`.
Now finding either authors or co-authors is as simple as

```sh
git log --author=Torvalds --grep=Torvalds --header-or
```

Test plan:
1. create commit authored by A and co-authored-by B
2. create commit authored by B
3. run `git log --author=B --grep="Co-authored-by: B" --header-or`
4. expect to see both commits

Signed-off-by: Max 👨🏽‍💻 Coplan <mchcopl@gmail.com>
---
    feat(log): add option to search for header or body to git log

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1710%2Fvegerot%2Fheader-or-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1710/vegerot/header-or-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1710

 grep.c          |  2 +-
 grep.h          |  1 +
 revision.c      |  3 +++
 t/t7810-grep.sh | 11 +++++++++++
 4 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/grep.c b/grep.c
index ac34bfeafb3..79ba4810b83 100644
--- a/grep.c
+++ b/grep.c
@@ -797,7 +797,7 @@ void compile_grep_patterns(struct grep_opt *opt)
 	if (opt->no_body_match && opt->pattern_expression)
 		opt->pattern_expression = grep_not_expr(opt->pattern_expression);
 
-	if (!header_expr)
+	if (!header_expr || opt->header_or)
 		return;
 
 	if (!opt->pattern_expression)
diff --git a/grep.h b/grep.h
index 926c0875c42..8cbc4a46194 100644
--- a/grep.h
+++ b/grep.h
@@ -147,6 +147,7 @@ struct grep_opt {
 	int count;
 	int word_regexp;
 	int all_match;
+	int header_or;
 	int no_body_match;
 	int body_hit;
 #define GREP_BINARY_DEFAULT	0
diff --git a/revision.c b/revision.c
index 7e45f765d9f..0858050ece3 100644
--- a/revision.c
+++ b/revision.c
@@ -2646,6 +2646,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
 	} else if (!strcmp(arg, "--all-match")) {
 		revs->grep_filter.all_match = 1;
+	// @@@ must-fix: find a better name
+	} else if (!strcmp(arg, "--header-or")) {
+		revs->grep_filter.header_or = 1;
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 875dcfd98f3..d539b5e88f5 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -961,6 +961,17 @@ test_expect_success 'log --grep --author uses intersection' '
 	test_cmp expect actual
 '
 
+test_expect_success 'log --grep --author --header-or uses union' '
+	# grep matches only third and fourth
+	# author matches only initial and third
+	git log --author="A U Thor" --grep=r --header-or --format=%s >actual &&
+	{
+	    echo fourth && echo third
+	} >expect &&
+	test_cmp expect actual
+'
+
+
 test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' '
 	# grep matches initial and second but not third
 	# author matches only initial and third

base-commit: 7774cfed6261ce2900c84e55906da708c711d601
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* What's cooking in git.git (Apr 2024, #03; Fri, 5)
@ 2024-04-05 19:13  1% Junio C Hamano
  2024-04-06  1:11  0% ` Dragan Simic
  0 siblings, 1 reply; 200+ results
From: Junio C Hamano @ 2024-04-05 19:13 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking in my tree.  Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and are candidate to be in a
future release).  Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all and may be annotated with an URL
to a message that raises issues but they are no means exhaustive.  A
topic without enough support may be discarded after a long period of
no activity (of course they can be resubmit when new interests
arise).

Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors.  Some
repositories have only a subset of branches.

With maint, master, next, seen, todo:

	git://git.kernel.org/pub/scm/git/git.git/
	git://repo.or.cz/alt-git.git/
	https://kernel.googlesource.com/pub/scm/git/git/
	https://github.com/git/git/
	https://gitlab.com/git-scm/git/

With all the integration branches and topics broken out:

	https://github.com/gitster/git/

Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):

	git://git.kernel.org/pub/scm/git/git-htmldocs.git/
	https://github.com/gitster/git-htmldocs.git/

Release tarballs are available at:

	https://www.kernel.org/pub/software/scm/git/

--------------------------------------------------
[Graduated to 'master']

* jk/core-comment-string (2024-03-27) 17 commits
  (merged to 'next' on 2024-03-28 at fbf8eb9331)
 + config: add core.commentString
 + config: allow multi-byte core.commentChar
 + environment: drop comment_line_char compatibility macro
 + wt-status: drop custom comment-char stringification
 + sequencer: handle multi-byte comment characters when writing todo list
 + find multi-byte comment chars in unterminated buffers
 + find multi-byte comment chars in NUL-terminated strings
 + prefer comment_line_str to comment_line_char for printing
 + strbuf: accept a comment string for strbuf_add_commented_lines()
 + strbuf: accept a comment string for strbuf_commented_addf()
 + strbuf: accept a comment string for strbuf_stripspace()
 + environment: store comment_line_char as a string
 + strbuf: avoid shadowing global comment_line_char name
 + commit: refactor base-case of adjust_comment_line_char()
 + strbuf: avoid static variables in strbuf_add_commented_lines()
 + strbuf: simplify comment-handling in add_lines() helper
 + config: forbid newline as core.commentChar

 core.commentChar used to be limited to a single byte, but has been
 updated to allow an arbitrary multi-byte sequence.
 source: <20240312091013.GA95442@coredump.intra.peff.net>
 source: <20240327081922.GA830163@coredump.intra.peff.net>


* rs/config-comment (2024-03-15) 3 commits
  (merged to 'next' on 2024-03-28 at 83eaadc2b6)
 + config: allow tweaking whitespace between value and comment
 + config: fix --comment formatting
 + config: add --comment option to add a comment

 "git config" learned "--comment=<message>" option to leave a
 comment immediately after the "variable = value" on the same line
 in the configuration file.
 source: <pull.1681.v2.git.1709824540636.gitgitgadget@gmail.com>


* rs/retire-mksnpath (2024-04-04) 1 commit
 - apply: replace mksnpath() with a mkpathdup() call

 Replace the only remaining caller of mksnpath() with mkpathdup() to
 lift the hardcoded path length limit, and retire the function.

 Expecting a retitle and reroll?
 cf. <xmqqo7aozuih.fsf@gitster.g>
 source: <df774306-f29b-4a75-a282-59db89812b9a@web.de>

--------------------------------------------------
[New Topics]

* ma/win32-unix-domain-socket (2024-04-03) 1 commit
 - Win32: detect unix socket support at runtime

 Windows binary used to decide the use of unix-domain socket at
 build time, but it learned to make the decision at runtime instead.

 Will merge to 'next'.
 source: <pull.1708.git.1712158923106.gitgitgadget@gmail.com>


* ps/reftable-write-optim (2024-04-03) 11 commits
 - reftable/block: reuse compressed array
 - reftable/block: reuse zstream when writing log blocks
 - reftable/writer: reset `last_key` instead of releasing it
 - reftable/writer: unify releasing memory
 - reftable/writer: refactorings for `writer_flush_nonempty_block()`
 - reftable/writer: refactorings for `writer_add_record()`
 - refs/reftable: don't recompute committer ident
 - reftable: remove name checks
 - refs/reftable: skip duplicate name checks
 - refs/reftable: perform explicit D/F check when writing symrefs
 - refs/reftable: fix D/F conflict error message on ref copy

 Code to write out reftable has seen some optimization and
 simplification.

 Expecting a reroll.
 cf. <Zg6SVcGC8kSGSYh-@tanuki>
 source: <cover.1712209149.git.ps@pks.im>


* ds/send-email-per-message-block (2024-04-05) 2 commits
 - SQUASH??? switch to separator semantics
 - send-email: make it easy to discern the messages for each patch

 "git send-email" learned to separate its reports on each message it
 sends out with an extra blank line in between.

 Expecting a reroll.
 cf. <8d47bd687f2ad80bbc1e1c86ae337327@manjaro.org>
 source: <0e087ed992def0746f3d437253248904c2126464.1712262791.git.dsimic@manjaro.org>


* ds/fetch-config-parse-microfix (2024-04-05) 1 commit
 - fetch: return when parsing submodule.recurse

 A config parser callback function fell through instead of returning
 after recognising and processing a variable, wasting cycles, which
 has been corrected.

 Will merge to 'next'.
 source: <pull.1709.git.1712285542303.gitgitgadget@gmail.com>


* rs/apply-lift-path-length-limit (2024-04-05) 2 commits
 - path: remove mksnpath()
 - apply: avoid fixed-size buffer in create_one_file()

 "git apply" has been updated to lift the hardcoded pathname length
 limit, which in turn allowed a mksnpath() function that is no
 longer used.

 Will merge to 'next'.
 source: <df774306-f29b-4a75-a282-59db89812b9a@web.de>


* rs/apply-reject-fd-leakfix (2024-04-05) 1 commit
 - apply: don't leak fd on fdopen() error

 A file descriptor leak in an error codepath, used when "git apply
 --reject" fails to create the *.rej file, has been corrected.

 Will merge to 'next'.
 source: <5ba55ee4-94c7-4094-a744-584fc623b391@web.de>

--------------------------------------------------
[Cooking]

* kn/clarify-update-ref-doc (2024-04-02) 2 commits
  (merged to 'next' on 2024-04-02 at d1b9c5aa67)
 + githooks: use {old,new}-oid instead of {old,new}-value
 + update-ref: use {old,new}-oid instead of {old,new}value

 Doc update, as a preparation to enhance "git update-ref --stdin".

 Will merge to 'master'.
 source: <20240402064915.191104-1-knayak@gitlab.com>


* vs/complete-with-set-u-fix (2024-04-01) 2 commits
  (merged to 'next' on 2024-04-02 at d8f6a511e8)
 + completion: protect prompt against unset SHOWUPSTREAM in nounset mode
 + completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode

 Another "set -u" fix for the bash prompt (in contrib/) script.

 Will merge to 'master'.
 source: <20240401190751.8676-1-ville.skytta@iki.fi>


* ba/osxkeychain-updates (2024-04-01) 4 commits
 - osxkeychain: store new attributes
 - osxkeychain: erase matching passwords only
 - osxkeychain: erase all matching credentials
 - osxkeychain: replace deprecated SecKeychain API

 Update osxkeychain backend with features required for the recent
 credential subsystem.

 Will merge to 'next'?
 source: <pull.1667.git.1708212896.gitgitgadget@gmail.com>


* rs/imap-send-use-xsnprintf (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 789ad853e1)
 + imap-send: use xsnprintf to format command

 Code clean-up and duplicate reduction.

 Will merge to 'master'.
 source: <f9ad9f41-5b9b-474e-9818-f91fc937daae@web.de>


* tb/midx-write (2024-04-01) 5 commits
  (merged to 'next' on 2024-04-05 at b4870116f7)
 + midx-write.c: use `--stdin-packs` when repacking
 + midx-write.c: check count of packs to repack after grouping
 + midx-write.c: factor out common want_included_pack() routine
 + midx-write: move writing-related functions from midx.c
 + Merge branch 'rs/midx-use-strvec-pushf' into tb/midx-write

 Code clean-up by splitting code responsible for writing midx files
 into its own file.

 Will merge to 'master'.
 source: <cover.1712006190.git.me@ttaylorr.com>


* jc/t2104-style-update (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-03 at 0449835479)
 + t2104: style fixes

 Coding style fixes.

 Will merge to 'master'.
 source: <xmqqmsqb4ngg.fsf@gitster.g>


* rs/t-prio-queue-cleanup (2024-04-02) 1 commit
  (merged to 'next' on 2024-04-04 at 7961c838ac)
 + t-prio-queue: simplify using compound literals

 t-prio-queue test has been cleaned up by using C99 compound
 literals; this is meant to also serve as a weather-balloon to smoke
 out folks with compilers who have trouble compiling code that uses
 the feature.

 Will merge to 'master'.
 source: <520da361-1b80-4ba3-87b2-86d6fdfc18b5@web.de>


* jk/libcurl-8.7-regression-workaround (2024-04-02) 2 commits
 - INSTALL: bump libcurl version to 7.21.3
 - http: reset POSTFIELDSIZE when clearing curl handle

 Fix was added to work around a regression in libcURL 8.7.0 (which has
 already been fixed in their tip of the tree).

 Expecting a reroll.
 cf. <20240403032045.GA1559972@coredump.intra.peff.net>
 source: <20240402200254.GA874754@coredump.intra.peff.net>


* tb/t7700-fixup (2024-04-03) 1 commit
 - t/t7700-repack.sh: fix test breakages with `GIT_TEST_MULTI_PACK_INDEX=1 `

 source: <7e8d435d58eea19d2aae0be366720f5956d29a5d.1712075189.git.me@ttaylorr.com>


* es/test-cron-safety (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at e383c8cfb2)
 + test-lib: fix non-functioning GIT_TEST_MAINT_SCHEDULER fallback

 The test script had an incomplete and ineffective attempt to avoid
 clobbering the testing user's real crontab (and its equivalents),
 which has been completed.

 Will merge to 'master'.
 source: <20240329222703.9343-1-ericsunshine@charter.net>


* gt/add-u-commit-i-pathspec-check (2024-04-03) 3 commits
 - builtin/add: error out when passing untracked path with -u
 - builtin/commit: error out when passing untracked path with -i
 - revision: optionally record matches with pathspec elements

 "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
 diagnose a pathspec element that did not match any files in certain
 situations, unlike "git add <pathspec>" did.

 Will merge to 'next'.
 source: <20240402213640.139682-2-shyamthakkar001@gmail.com>


* jc/advice-sans-trailing-whitespace (2024-03-29) 1 commit
  (merged to 'next' on 2024-04-02 at 3cb0fda1bf)
 + advice: omit trailing whitespace

 The "hint:" messages given by the advice mechanism, when given a
 message with a blank line, left a line with trailing whitespace,
 which has been cleansed.

 Will merge to 'master'.
 source: <xmqq4jcooddp.fsf@gitster.g>


* jt/reftable-geometric-compaction (2024-04-05) 4 commits
 - reftable/stack: use geometric table compaction
 - reftable/stack: add env to disable autocompaction
 - reftable/stack: allow disabling of auto-compaction
 - Merge branch 'ps/pack-refs-auto' into jt/reftable-geometric-compaction
 (this branch uses ps/pack-refs-auto.)

 The strategy to compact multiple tables of reftables after many
 operations accumulate many entries has been improved to avoid
 accumulating too many tables uncollected.

 Comments?
 source: <pull.1683.v5.git.1712255369.gitgitgadget@gmail.com>


* ds/typofix-core-config-doc (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 79496fcfc4)
 + config: fix some small capitalization issues, as spotted

 Typofix.

 Will merge to 'master'.
 source: <26135b06c48565ee8ac6dcfc1ef5431511e6202c.1711918168.git.dsimic@manjaro.org>


* jc/checkout-detach-wo-tracking-report (2024-03-30) 1 commit
  (merged to 'next' on 2024-04-04 at 161eca247d)
 + checkout: omit "tracking" information on a detached HEAD

 "git checkout/switch --detach foo", after switching to the detached
 HEAD state, gave the tracking information for the 'foo' branch,
 which was pointless.

 Tested-by: M Hickford <mirth.hickford@gmail.com>
 cf. <CAGJzqsmE9FDEBn=u3ge4LA3ha4fDbm4OWiuUbMaztwjELBd7ug@mail.gmail.com>

 Will merge to 'master'.
 source: <xmqqa5mfl7ud.fsf@gitster.g>


* rj/use-adv-if-enabled (2024-03-30) 3 commits
  (merged to 'next' on 2024-04-02 at 31d4453035)
 + add: use advise_if_enabled for ADVICE_ADD_EMBEDDED_REPO
 + add: use advise_if_enabled for ADVICE_ADD_EMPTY_PATHSPEC
 + add: use advise_if_enabled for ADVICE_ADD_IGNORED_FILE

 Use advice_if_enabled() API to rewrite a simple pattern to
 call advise() after checking advice_enabled().

 Will merge to 'master'.
 source: <46fba030-d7aa-49d2-88fa-e506850f7b6a@gmail.com>


* rs/mem-pool-size-t-safety (2024-03-31) 1 commit
  (merged to 'next' on 2024-04-02 at 3517d48210)
 + mem-pool: use st_add() in mem_pool_strvfmt()

 size_t arithmetic safety.

 Will merge to 'master'.
 source: <bbe00b9e-64d8-4ec8-a2b9-2c6917c72dbd@web.de>


* ew/khash-to-khashl (2024-03-28) 3 commits
 - khashl: fix ensemble lookups on empty table
 - treewide: switch to khashl for memory savings
 - list-objects-filter: use kh_size API

 The hashtable library "khash.h" has been replaced with "khashl.h"
 that has better memory usage characteristics.

 Needs review.
 source: <20240328101356.300374-1-e@80x24.org>


* ps/reftable-block-iteration-optim (2024-03-27) 9 commits
 - reftable/block: reuse `zstream` state on inflation
 - reftable/block: open-code call to `uncompress2()`
 - reftable/block: reuse uncompressed blocks
 - reftable/reader: iterate to next block in place
 - reftable/block: move ownership of block reader into `struct table_iter`
 - reftable/block: introduce `block_reader_release()`
 - reftable/block: better grouping of functions
 - reftable/block: merge `block_iter_seek()` and `block_reader_seek()`
 - reftable/block: rename `block_reader_start()`

 The code to iterate over reftable blocks has seen some optimization
 to reduce memory allocation and deallocation.

 Needs review.
 source: <cover.1711519925.git.ps@pks.im>


* rj/add-p-explicit-reshow (2024-03-29) 2 commits
  (merged to 'next' on 2024-04-02 at 05c7e930af)
 + add-patch: do not print hunks repeatedly
 + add-patch: introduce 'p' in interactive-patch

 "git add -p" and other "interactive hunk selection" UI has learned to
 skip showing the hunk immediately after it has already been shown, and
 an additional action to explicitly ask to reshow the current hunk.

 Will merge to 'master'.
 source: <a9c515fe-6664-4b5d-abca-d88fdd32a883@gmail.com>


* bc/credential-scheme-enhancement (2024-03-27) 12 commits
 . credential: add support for multistage credential rounds
 . t5563: refactor for multi-stage authentication
 . docs: set a limit on credential line length
 . credential: enable state capability
 . credential: add an argument to keep state
 . http: add support for authtype and credential
 . docs: indicate new credential protocol fields
 . credential: gate new fields on capability
 . credential: add a field for pre-encoded credentials
 . http: use new headers for each object request
 . remote-curl: reset headers on new request
 . credential: add an authtype field

 The credential helper protocol, together with the HTTP layer, have
 been enhanced to support authentication schemes different from
 username & password pair, like Bearer and NTLM.

 Expecting a reroll.
 cf. <ZgSQ5o_KyqDaxz1m@tapette.crustytoothpaste.net>
 source: <20240324011301.1553072-1-sandals@crustytoothpaste.net>


* ja/doc-markup-updates (2024-03-29) 5 commits
  (merged to 'next' on 2024-04-02 at 69b015d7ce)
 + doc: git-clone: do not autoreference the manpage in itself
 + doc: git-clone: apply new documentation formatting guidelines
 + doc: git-init: apply new documentation formatting guidelines
 + doc: allow literal and emphasis format in doc vs help tests
 + doc: rework CodingGuidelines with new formatting rules

 Documentation rules has been explicitly described how to mark-up
 literal parts and a few manual pages have been updated as examples.

 Will merge to 'master'.
 source: <pull.1702.v2.git.1711711181.gitgitgadget@gmail.com>


* mg/editorconfig-makefile (2024-03-23) 1 commit
  (merged to 'next' on 2024-04-02 at 907b55579e)
 + editorconfig: add Makefiles to "text files"

 The .editorconfig file has been taught that a Makefile uses HT
 indentation.

 Will merge to 'master'.
 source: <20240322221813.13019-1-mg@max.gautier.name>


* ps/reftable-binsearch-updates (2024-04-03) 7 commits
  (merged to 'next' on 2024-04-04 at 40e6d5a36b)
 + reftable/block: avoid decoding keys when searching restart points
 + reftable/record: extract function to decode key lengths
 + reftable/block: fix error handling when searching restart points
 + reftable/block: refactor binary search over restart points
 + reftable/refname: refactor binary search over refnames
 + reftable/basics: improve `binsearch()` test
 + reftable/basics: fix return type of `binsearch()` to be `size_t`

 Reftable code clean-up and some bugfixes.

 Will merge to 'master'.
 source: <cover.1712123093.git.ps@pks.im>


* tb/pseudo-merge-reachability-bitmap (2024-03-20) 24 commits
 - t/perf: implement performace tests for pseudo-merge bitmaps
 - pseudo-merge: implement support for finding existing merges
 - ewah: `bitmap_equals_ewah()`
 - pack-bitmap: extra trace2 information
 - pack-bitmap.c: use pseudo-merges during traversal
 - t/test-lib-functions.sh: support `--date` in `test_commit_bulk()`
 - pack-bitmap: implement test helpers for pseudo-merge
 - ewah: implement `ewah_bitmap_popcount()`
 - pseudo-merge: implement support for reading pseudo-merge commits
 - pack-bitmap.c: read pseudo-merge extension
 - pseudo-merge: scaffolding for reads
 - pack-bitmap: extract `read_bitmap()` function
 - pack-bitmap-write.c: write pseudo-merge table
 - pack-bitmap-write.c: select pseudo-merge commits
 - pseudo-merge: implement support for selecting pseudo-merge commits
 - pack-bitmap: make `bitmap_writer_push_bitmapped_commit()` public
 - pack-bitmap: implement `bitmap_writer_has_bitmapped_object_id()`
 - pack-bitmap-write: support storing pseudo-merge commits
 - pseudo-merge.ch: initial commit
 - pack-bitmap: move some initialization to `bitmap_writer_init()`
 - pack-bitmap: drop unused `max_bitmaps` parameter
 - ewah: implement `ewah_bitmap_is_subset()`
 - config: repo_config_get_expiry()
 - Documentation/technical: describe pseudo-merge bitmaps format

 The pack-bitmap machinery learned to write pseudo-merge bitmaps,
 which act as imaginary octopus merges covering un-bitmapped
 reference tips. This enhances bitmap coverage, and thus,
 performance, for repositories with many references using bitmaps.

 Expecting a reroll.
 cf. <ZfyxCLpjbaScIdWA@nand.local>
 source: <cover.1710972293.git.me@ttaylorr.com>


* dg/myfirstobjectwalk-updates (2024-03-27) 5 commits
  (merged to 'next' on 2024-04-02 at effa6a98a6)
 + MyFirstObjectWalk: add stderr to pipe processing
 + MyFirstObjectWalk: fix description for counting omitted objects
 + MyFirstObjectWalk: fix filtered object walk
 + MyFirstObjectWalk: fix misspelled "builtins/"
 + MyFirstObjectWalk: use additional arg in config_fn_t

 Update a more recent tutorial doc.

 Will merge to 'master'.
 source: <cover.1711537370.git.dirk@gouders.net>


* jc/apply-parse-diff-git-header-names-fix (2024-03-29) 3 commits
  (merged to 'next' on 2024-04-02 at d1fa726c41)
 + t4126: fix "funny directory name" test on Windows (again)
  (merged to 'next' on 2024-03-28 at a35de15836)
 + t4126: make sure a directory with SP at the end is usable
  (merged to 'next' on 2024-03-27 at d586367985)
 + apply: parse names out of "diff --git" more carefully

 "git apply" failed to extract the filename the patch applied to,
 when the change was about an empty file created in or deleted from
 a directory whose name ends with a SP, which has been corrected.

 Will merge to 'master'.
 source: <xmqqfrwlltjn.fsf@gitster.g>
 source: <xmqqh6gqt674.fsf_-_@gitster.g>
 source: <xmqq5xx50x8p.fsf_-_@gitster.g>


* la/hide-trailer-info (2024-03-16) 7 commits
 - trailer: retire trailer_info_get() from API
 - trailer: make trailer_info struct private
 - trailer: make parse_trailers() return trailer_info pointer
 - interpret-trailers: access trailer_info with new helpers
 - sequencer: use the trailer iterator
 - trailer: teach iterator about non-trailer lines
 - Merge branch 'la/format-trailer-info' into la/hide-trailer-info
 (this branch uses la/format-trailer-info.)

 The trailer API has been reshuffled a bit.

 Needs review.
 source: <pull.1696.git.1710570428.gitgitgadget@gmail.com>


* ps/pack-refs-auto (2024-03-25) 16 commits
  (merged to 'next' on 2024-04-02 at 1d76dc3648)
 + builtin/gc: pack refs when using `git maintenance run --auto`
 + builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
 + t6500: extract objects with "17" prefix
 + builtin/gc: move `struct maintenance_run_opts`
 + builtin/pack-refs: introduce new "--auto" flag
 + builtin/pack-refs: release allocated memory
 + refs/reftable: expose auto compaction via new flag
 + refs: remove `PACK_REFS_ALL` flag
 + refs: move `struct pack_refs_opts` to where it's used
 + t/helper: drop pack-refs wrapper
 + refs/reftable: print errors on compaction failure
 + reftable/stack: gracefully handle failed auto-compaction due to locks
 + reftable/stack: use error codes when locking fails during compaction
 + reftable/error: discern locked/outdated errors
 + reftable/stack: fix error handling in `reftable_stack_init_addition()`
 + Merge branch 'ps/reftable-stack-tempfile' into ps/pack-refs-auto
 (this branch is used by jt/reftable-geometric-compaction.)

 "git pack-refs" learned the "--auto" option, which is a useful
 addition to be triggered from "git gc --auto".

 Acked-by: Karthik Nayak <karthik.188@gmail.com>
 cf. <CAOLa=ZRAEA7rSUoYL0h-2qfEELdbPHbeGpgBJRqesyhHi9Q6WQ@mail.gmail.com>

 Will merge to 'master'.
 source: <cover.1711360631.git.ps@pks.im>


* ds/doc-config-reflow (2024-03-14) 1 commit
 - config.txt: perform some minor reformatting

 Reflow a paragraph in the documentation source without any effect
 to the formatted text.

 Will discard.
 source: <97bdaf075bf5a68554cca1731eca78aff2662907.1710444774.git.dsimic@manjaro.org>


* la/format-trailer-info (2024-03-15) 5 commits
 - trailer: finish formatting unification
 - trailer: begin formatting unification
 - format_trailer_info(): append newline for non-trailer lines
 - format_trailer_info(): drop redundant unfold_value()
 - format_trailer_info(): use trailer_item objects
 (this branch is used by la/hide-trailer-info.)

 The code to format trailers have been cleaned up.

 Comments?
 source: <pull.1694.git.1710485706.gitgitgadget@gmail.com>


* ie/config-includeif-hostname (2024-03-19) 2 commits
 - config: learn the "hostname:" includeIf condition
 - t: add a test helper for getting hostname

 The conditional inclusion mechanism for configuration files learned
 to switch on the hostname.

 Expecting a reroll.
 cf. <20240319210428.GC1159535@coredump.intra.peff.net>
 cf. <20240320001934.GA903718@coredump.intra.peff.net>
 source: <20240319183722.211300-1-ignacio@iencinas.com>


* js/build-fuzz-more-often (2024-03-05) 3 commits
 - SQUASH???
 - fuzz: link fuzz programs with `make all` on Linux
 - ci: also define CXX environment variable

 In addition to building the objects needed, try to link the objects
 that are used in fuzzer tests, to make sure at least they build
 without bitrot, in Linux CI runs.

 Stalled.
 cf. <xmqq1q7w8xx6.fsf@gitster.g>
 source: <cover.1709673020.git.steadmon@google.com>


* sj/userdiff-c-sharp (2024-04-03) 1 commit
 - userdiff: better method/property matching for C#

 The userdiff patterns for C# has been updated.

 Will merge to 'next'?
 source: <pull.1682.v5.git.git.1712180564927.gitgitgadget@gmail.com>


* cw/git-std-lib (2024-02-28) 4 commits
 - SQUASH??? get rid of apparent debugging crufts
 - test-stdlib: show that git-std-lib is independent
 - git-std-lib: introduce Git Standard Library
 - pager: include stdint.h because uintmax_t is used

 Split libgit.a out to a separate git-std-lib tor easier reuse.

 Expecting a reroll.
 source: <cover.1696021277.git.jonathantanmy@google.com>


* js/cmake-with-test-tool (2024-02-23) 2 commits
 - cmake: let `test-tool` run the unit tests, too
 - Merge branch 'js/unit-test-suite-runner' into js/cmake-with-test-tool
 (this branch uses js/unit-test-suite-runner.)

 "test-tool" is now built in CMake build to also run the unit tests.

 May want to roll it into the base topic.
 source: <pull.1666.git.1708038924522.gitgitgadget@gmail.com>


* js/unit-test-suite-runner (2024-02-23) 8 commits
 - ci: use test-tool as unit test runner on Windows
 - t/Makefile: run unit tests alongside shell tests
 - unit tests: add rule for running with test-tool
 - test-tool run-command testsuite: support unit tests
 - test-tool run-command testsuite: remove hardcoded filter
 - test-tool run-command testsuite: get shell from env
 - t0080: turn t-basic unit test into a helper
 - Merge branch 'jk/unit-tests-buildfix' into js/unit-test-suite-runner
 (this branch is used by js/cmake-with-test-tool.)

 The "test-tool" has been taught to run testsuite tests in parallel,
 bypassing the need to use the "prove" tool.

 Needs review.
 source: <cover.1708728717.git.steadmon@google.com>


* bk/complete-dirname-for-am-and-format-patch (2024-01-12) 1 commit
 - completion: dir-type optargs for am, format-patch

 Command line completion support (in contrib/) has been
 updated for a few commands to complete directory names where a
 directory name is expected.

 Expecting a reroll.
 cf. <40c3a824-a961-490b-94d4-4eb23c8f713d@gmail.com>
 cf. <6683f24e-7e56-489d-be2d-8afe1fc38d2b@gmail.com>
 source: <d37781c3-6af2-409b-95a8-660a9b92d20b@smtp-relay.sendinblue.com>


* bk/complete-send-email (2024-01-12) 1 commit
 - completion: don't complete revs when --no-format-patch

 Command line completion support (in contrib/) has been taught to
 avoid offering revision names as candidates to "git send-email" when
 the command is used to send pre-generated files.

 Expecting a reroll.
 cf. <CAC4O8c88Z3ZqxH2VVaNPpEGB3moL5dJcg3cOWuLWwQ_hLrJMtA@mail.gmail.com>
 source: <a718b5ee-afb0-44bd-a299-3208fac43506@smtp-relay.sendinblue.com>


* tb/path-filter-fix (2024-01-31) 16 commits
 - bloom: introduce `deinit_bloom_filters()`
 - commit-graph: reuse existing Bloom filters where possible
 - object.h: fix mis-aligned flag bits table
 - commit-graph: new Bloom filter version that fixes murmur3
 - commit-graph: unconditionally load Bloom filters
 - bloom: prepare to discard incompatible Bloom filters
 - bloom: annotate filters with hash version
 - repo-settings: introduce commitgraph.changedPathsVersion
 - t4216: test changed path filters with high bit paths
 - t/helper/test-read-graph: implement `bloom-filters` mode
 - bloom.h: make `load_bloom_filter_from_graph()` public
 - t/helper/test-read-graph.c: extract `dump_graph_info()`
 - gitformat-commit-graph: describe version 2 of BDAT
 - commit-graph: ensure Bloom filters are read with consistent settings
 - revision.c: consult Bloom filters for root commits
 - t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`

 The Bloom filter used for path limited history traversal was broken
 on systems whose "char" is unsigned; update the implementation and
 bump the format version to 2.

 Waiting for a final ack?
 cf. <ZcFjkfbsBfk7JQIH@nand.local>
 source: <cover.1706741516.git.me@ttaylorr.com>


* jc/rerere-cleanup (2023-08-25) 4 commits
 - rerere: modernize use of empty strbuf
 - rerere: try_merge() should use LL_MERGE_ERROR when it means an error
 - rerere: fix comment on handle_file() helper
 - rerere: simplify check_one_conflict() helper function

 Code clean-up.

 Not ready to be reviewed yet.
 source: <20230824205456.1231371-1-gitster@pobox.com>


^ permalink raw reply	[relevance 1%]

* Re: [PATCH] fetch: return when parsing submodule.recurse
  2024-04-05  2:52 15% [PATCH] fetch: return when parsing submodule.recurse Derrick Stolee via GitGitGadget
@ 2024-04-05 16:53  5% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-05 16:53 UTC (permalink / raw)
  To: Derrick Stolee via GitGitGadget; +Cc: git, Derrick Stolee

"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Derrick Stolee <stolee@gmail.com>
>
> When parsing config keys, the normal pattern is to return 0 after
> completing the logic for a specific config key, since no other key will
> match. One instance, for "submodule.recurse", was missing this case in
> builtin/fetch.c.
>
> This is a very minor change, and will have minimal impact to
> performance.

True, and the performance impact should be positive, if it is
observable.

Well spotted.  Thanks.

> This particular block was edited recently in 56e8bb4fb4
> (fetch: use `fetch_config` to store "fetch.recurseSubmodules" value,
> 2023-05-17), which led to some hesitation that perhaps this omission was
> on purpose.
>
> However, no later cases within git_fetch_config() will match the key if
> equal to "submodule.recurse" and neither will any key matches within the
> catch-all git_default_config().
>
> Signed-off-by: Derrick Stolee <stolee@gmail.com>
> ---
>     fetch: return when parsing submodule.recurse
>     
>     This is a super-nit that I noticed when looking into this config setting
>     and couldn't help but try to fix.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1709%2Fderrickstolee%2Ffetch-config-parse-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1709/derrickstolee/fetch-config-parse-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1709
>
>  builtin/fetch.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index 46a793411a4..5857d860dbf 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -138,6 +138,7 @@ static int git_fetch_config(const char *k, const char *v,
>  		int r = git_config_bool(k, v) ?
>  			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
>  		fetch_config->recurse_submodules = r;
> +		return 0;
>  	}
>  
>  	if (!strcmp(k, "submodule.fetchjobs")) {
>
> base-commit: 7774cfed6261ce2900c84e55906da708c711d601


^ permalink raw reply	[relevance 5%]

* Re: Git Server
  @ 2024-04-05  9:11  2%     ` Konstantin Khomoutov
  0 siblings, 0 replies; 200+ results
From: Konstantin Khomoutov @ 2024-04-05  9:11 UTC (permalink / raw)
  To: Simon Phai; +Cc: git

(Reformatted to have inline style [1].)

On Thu, Apr 04, 2024 at 06:26:31PM +0800, Simon Phai wrote:

> > > I want to host my own git server, may I understand the server OS can
> > > it be windows?
> >
> > Yes.
> >
> > But note that there exist quite many ways to "host a Git server", so you
> > should maybe explore what's already there and specify your requirements
> > more precisely.
> >
> > In the simplest form (if we forget about just running git-daemon in a
> > console window - providing unprotected R/O access to a given repository),
> > you either set up Git to be accessible via an SSH server or via a web
> > server (IIS works).
> >
> actually I'm quite new to this but I would like to setup my own git
> server so that my fellows can develop our own repository, I find
> online there isn't much guide on using windows server to do it.

Unfortunately, this added not too much information to the original question.
I mean, now we know that you want to host a Git server on a Windows system,
there is a single repository to make access to and that there is going to be
more than a single person to have access to that repository.

OK, let us try to maybe move a bit further.

The first thing to know, is that Git does not provide any "high-level" -
Github-like or GitLab-like, is you want - solution out of the box.
It basically provides three ways to access Git repositories remotely:

 - A thing called "git-daemon" which provides an unprotected and
   unauthenticated access to a Git repository. Because of these properties,
   by default the access it read-only, and even though you can convince it
   to be read-write, it's a bad idea unless you really understand what you're
   doing, and, based on what you described about your prospective setup,
   this is certainly not what you want to do.

 - Two low-level programs (whose names do not matter) which are supposed to be
   used in conjunction with an SSH client and server. This means you need to
   have an SSH server running on the host with a Git repository (or multiple
   repositories), and the clients are to use SSH clients to access those
   repositories. For clients, this happens almost transparently - they do not
   need to manually run SSH clients, it's done by "git push/fetch/pull"
   commands automatically when the URL of a remote repo has the "ssh://"
   scheme, - but the server-side setup has to be rather explicit.

 - More low-level commands intended to serve access to Git repos with the help
   of an HTTP server. This works in a manner quite similar to that of SSH,
   just HTTP (these days, HTTPS is more common) transport is used instead.
   Again, clients handle this automatically if the URL of a remote repository
   to work with has the "http://" or "https://" schemes.

Note that neither of the described solutions provide any user management and
access control facilities, and this is arguably the most complex part of
setting server-side Git up: making an SSH or HTTP server start Git is not too
complex, but putting up user management and access control is harder, and is
different for SSH and HTTP.

To research, you could start with [2] and [3]. These do not present the whole
solutions but at least it's something you can probably start with.

Also note that having an SSH+Git and/or HTTP+Git combo only allows you to
maintain a so-called "rendez-vouz" repository (or a set of them) basically
used by a team to share their developments. Such a solution won't provide you
with a web-browsable access to repositories, code review, issue tracker and so
on and so forth. There will also be quite limited, if any, access to
fine-grained access control - basically to who can push where. If you need
anything of the above, you might have better luck trying a "turn-key" solution
such as [4] or [5].

 1. https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
 2. https://github.com/PowerShell/Win32-OpenSSH/wiki/Setting-up-a-Git-server-on-Windows-using-Git-for-Windows-and-Win32_OpenSSH
 3. https://smalltech.com.au/blog/how-to-run-a-git-server-on-windows-with-iis
 4. https://about.gitea.com/products/gitea/
 5. https://gogs.io



^ permalink raw reply	[relevance 2%]

* [PATCH] fetch: return when parsing submodule.recurse
@ 2024-04-05  2:52 15% Derrick Stolee via GitGitGadget
  2024-04-05 16:53  5% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Derrick Stolee via GitGitGadget @ 2024-04-05  2:52 UTC (permalink / raw)
  To: git; +Cc: Derrick Stolee, Derrick Stolee

From: Derrick Stolee <stolee@gmail.com>

When parsing config keys, the normal pattern is to return 0 after
completing the logic for a specific config key, since no other key will
match. One instance, for "submodule.recurse", was missing this case in
builtin/fetch.c.

This is a very minor change, and will have minimal impact to
performance. This particular block was edited recently in 56e8bb4fb4
(fetch: use `fetch_config` to store "fetch.recurseSubmodules" value,
2023-05-17), which led to some hesitation that perhaps this omission was
on purpose.

However, no later cases within git_fetch_config() will match the key if
equal to "submodule.recurse" and neither will any key matches within the
catch-all git_default_config().

Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
    fetch: return when parsing submodule.recurse
    
    This is a super-nit that I noticed when looking into this config setting
    and couldn't help but try to fix.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1709%2Fderrickstolee%2Ffetch-config-parse-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1709/derrickstolee/fetch-config-parse-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1709

 builtin/fetch.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin/fetch.c b/builtin/fetch.c
index 46a793411a4..5857d860dbf 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -138,6 +138,7 @@ static int git_fetch_config(const char *k, const char *v,
 		int r = git_config_bool(k, v) ?
 			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
 		fetch_config->recurse_submodules = r;
+		return 0;
 	}
 
 	if (!strcmp(k, "submodule.fetchjobs")) {

base-commit: 7774cfed6261ce2900c84e55906da708c711d601
-- 
gitgitgadget


^ permalink raw reply related	[relevance 15%]

* Re: Git log --decorate show prefetch objects
  2024-04-04 16:56  0% ` Junio C Hamano
@ 2024-04-04 21:37  0%   ` Alexandre Badez
  0 siblings, 0 replies; 200+ results
From: Alexandre Badez @ 2024-04-04 21:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Derrick Stolee

On 04/04/2024 18:56, Junio C Hamano wrote:

> Alexandre Badez <alexandre@badez.eu> writes:
>
>> # What did you do before the bug happened? (Steps to reproduce your issue)
>>
>> <on a git repository not fetch from a "long" time so you have missing
>> branches and/or objects>
>>
>> git maintenance run --task=prefetch
>>
>> git log --oneline --decorate --all --graph
> I do not use the prefetch stuff, but unfortunately the person who
> was most familiar with "maintenance" are no longer active on this
> list, so let me take a crack.
>
> I think your complaint is that "--all" really means "all", not just
> "heads" and "tags" but also includes "prefetch", while the prefetch
> hierarchy is not included in the "--decorate" sources.
>
> What does
>
>      $ git log --oneline --branches --tags --decorate --graph
>
> show, and is it closer to what you expected (note: this is not a
> direct suggestion of a workaround---trying to gauge what the
> direction is to move forward)?
>
> Thanks.


Indeed, I missed the --branches (and --remotes) options; they do a 
better job in my case.

Maybe --all have a "legacy behaviour" so it's on me to move to new and 
better options.

Thanks.




^ permalink raw reply	[relevance 0%]

* Re: [PATCH] docs: recommend using contrib/contacts/git-contacts
  2024-04-03  8:42  0% ` Matthias Aßhauer
@ 2024-04-04 20:01  0%   ` Linus Arver
  0 siblings, 0 replies; 200+ results
From: Linus Arver @ 2024-04-04 20:01 UTC (permalink / raw)
  To: Matthias Aßhauer, Linus Arver via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Jonathan Tan,
	Emily Shaffer, Pablo CHABANNE, Nathan BERBEZIER, Corentin BOMPARD,
	Matthieu MOY

Matthias Aßhauer <mha1993@live.de> writes:

> On Tue, 2 Apr 2024, Linus Arver via GitGitGadget wrote:
>
>> From: Linus Arver <linusa@google.com>
>>
>> Although we've had this script since 4d06402b1b (contrib: add
>> git-contacts helper, 2013-07-21), we don't mention it in our
>> introductory docs. Do so now.
>>
>> Signed-off-by: Linus Arver <linusa@google.com>
>> ---
>>    docs: recommend using contrib/contacts/git-contacts
>>
>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v1
>> Pull-Request: https://github.com/gitgitgadget/git/pull/1704
>>
>> Documentation/MyFirstContribution.txt | 3 +++
>> Documentation/SubmittingPatches       | 4 ++++
>> 2 files changed, 7 insertions(+)
>>
>> diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
>> index f06563e9817..eb1e27a82df 100644
>> --- a/Documentation/MyFirstContribution.txt
>> +++ b/Documentation/MyFirstContribution.txt
>> @@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
>> NOTE: Check `git help send-email` for some other options which you may find
>> valuable, such as changing the Reply-to address or adding more CC and BCC lines.
>>
>> +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
>> +include in the CC list.
>> +
>> NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
>> please don't send your patchset from the tutorial to the real mailing list! For
>> now, you can send it to yourself, to make sure you understand how it will look.
>> diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
>> index e734a3f0f17..52d11ff510b 100644
>> --- a/Documentation/SubmittingPatches
>> +++ b/Documentation/SubmittingPatches
>> @@ -459,6 +459,10 @@ an explanation of changes between each iteration can be kept in
>> Git-notes and inserted automatically following the three-dash
>> line via `git format-patch --notes`.
>>
>> +[[suggested-reviewers]]
>> +Use `contrib/contacts/git-contacts` to get a list of reviewers you should
>> +include in the CC list.
>> +
>
> There is already a paragraph about this in Documentation/SubmittingPatches 
> just a few paragraphs below.
>
>> Send your patch with "To:" set to the mailing list, with "cc:" listing
>> people who are involved in the area you are touching (the `git
>> contacts` command in `contrib/contacts/` can help to
>> identify them), to solicit comments and reviews.  Also, when you made
>> trial merges of your topic to `next` and `seen`, you may have noticed
>> work by others conflicting with your changes.  There is a good possibility
>> that these people may know the area you are touching well.
>
> Could we improve the existing paragraph instead of duplicating this 
> information?

Ah, yes of course (somehow I missed that existing guidance). Will update.


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] docs: recommend using contrib/contacts/git-contacts
  2024-04-02  6:28  0% ` Patrick Steinhardt
@ 2024-04-04 20:00  0%   ` Linus Arver
  0 siblings, 0 replies; 200+ results
From: Linus Arver @ 2024-04-04 20:00 UTC (permalink / raw)
  To: Patrick Steinhardt, Linus Arver via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Jonathan Tan,
	Emily Shaffer, Pablo CHABANNE, Nathan BERBEZIER, Corentin BOMPARD,
	Matthieu MOY

Patrick Steinhardt <ps@pks.im> writes:

> On Tue, Apr 02, 2024 at 12:20:05AM +0000, Linus Arver via GitGitGadget wrote:
>> From: Linus Arver <linusa@google.com>
>> 
>> Although we've had this script since 4d06402b1b (contrib: add
>> git-contacts helper, 2013-07-21), we don't mention it in our
>> introductory docs. Do so now.
>> 
>> Signed-off-by: Linus Arver <linusa@google.com>
>> ---
>>     docs: recommend using contrib/contacts/git-contacts
>> 
>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v1
>> Pull-Request: https://github.com/gitgitgadget/git/pull/1704
>> 
>>  Documentation/MyFirstContribution.txt | 3 +++
>>  Documentation/SubmittingPatches       | 4 ++++
>>  2 files changed, 7 insertions(+)
>> 
>> diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
>> index f06563e9817..eb1e27a82df 100644
>> --- a/Documentation/MyFirstContribution.txt
>> +++ b/Documentation/MyFirstContribution.txt
>> @@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
>>  NOTE: Check `git help send-email` for some other options which you may find
>>  valuable, such as changing the Reply-to address or adding more CC and BCC lines.
>>  
>> +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
>> +include in the CC list.
>> +
>
> Should we mention that the script can be passed to git-send-email(1) via
> `--cc-cmd=`?

Ack, will do. I think I can just copy/paste the existing guidance from
git-contact.txt which has this example:

    git send-email --cc-cmd='git contacts' feature/*.patch


^ permalink raw reply	[relevance 0%]

* [PATCH v5 0/3] reftable/stack: use geometric table compaction
  2024-04-03  0:20  2%     ` [PATCH v4 0/2] " Justin Tobler via GitGitGadget
  2024-04-03  4:47  0%       ` Patrick Steinhardt
@ 2024-04-04 18:29  3%       ` Justin Tobler via GitGitGadget
  2024-04-08  6:12  0%         ` Patrick Steinhardt
  2024-04-08 16:16  3%         ` [PATCH v6 " Justin Tobler via GitGitGadget
  1 sibling, 2 replies; 200+ results
From: Justin Tobler via GitGitGadget @ 2024-04-04 18:29 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Karthik Nayak, Han-Wen Nienhuys,
	Justin Tobler

Hello again,

This is the fifth version my patch series that refactors the reftable
compaction strategy to instead follow a geometric sequence. Changes compared
to v4:

 * To fix some failing tests and conflicts, this patch series now depends on
   the ps/pack-refs-auto series which is currently in next.
 * Lifted the GIT_TEST_REFTABLE_AUTOCOMPACTION env out of the reftable
   library and into the reftable backend code.

Thanks for taking a look!

-Justin

Justin Tobler (3):
  reftable/stack: allow disabling of auto-compaction
  reftable/stack: add env to disable autocompaction
  reftable/stack: use geometric table compaction

 refs/reftable-backend.c    |   4 ++
 reftable/reftable-writer.h |   3 +
 reftable/stack.c           | 125 +++++++++++++++++++------------------
 reftable/stack.h           |   4 --
 reftable/stack_test.c      |  77 ++++++-----------------
 t/t0610-reftable-basics.sh |  71 ++++++++++++++++-----
 6 files changed, 146 insertions(+), 138 deletions(-)


base-commit: 4b32163adf4863c6df3bb6b43540fa2ca3494e28
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/1683

Range-diff vs v4:

 -:  ----------- > 1:  a7011dbc6aa reftable/stack: allow disabling of auto-compaction
 1:  2a0421e5f20 ! 2:  7c4fe0e9ec5 reftable/stack: add env to disable autocompaction
     @@ Commit message
      
          Signed-off-by: Justin Tobler <jltobler@gmail.com>
      
     - ## reftable/stack.c ##
     -@@ reftable/stack.c: int reftable_addition_commit(struct reftable_addition *add)
     - 	if (err)
     - 		goto done;
     - 
     --	if (!add->stack->disable_auto_compact)
     -+	if (!add->stack->disable_auto_compact &&
     -+	    git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
     - 		err = reftable_stack_auto_compact(add->stack);
     - 
     - done:
     -
     - ## reftable/system.h ##
     -@@ reftable/system.h: license that can be found in the LICENSE file or at
     - #include "tempfile.h"
     - #include "hash-ll.h" /* hash ID, sizes.*/
     - #include "dir.h" /* remove_dir_recursively, for tests.*/
     + ## refs/reftable-backend.c ##
     +@@
     + #include "../reftable/reftable-merged.h"
     + #include "../setup.h"
     + #include "../strmap.h"
      +#include "parse.h"
     + #include "refs-internal.h"
       
     - int hash_size(uint32_t id);
     + /*
     +@@ refs/reftable-backend.c: static struct ref_store *reftable_be_init(struct repository *repo,
     + 	refs->write_options.hash_id = repo->hash_algo->format_id;
     + 	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
       
     ++	if (!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
     ++		refs->write_options.disable_auto_compact = 1;
     ++
     + 	/*
     + 	 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
     + 	 * This stack contains both the shared and the main worktree refs.
      
       ## t/t0610-reftable-basics.sh ##
      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause auto-compaction' '
 2:  e0f4d0dbcc1 ! 3:  8f124acf0f8 reftable/stack: use geometric table compaction
     @@ reftable/stack_test.c: static void test_empty_add(void)
      +
       static void test_reftable_stack_auto_compaction(void)
       {
     - 	struct reftable_write_options cfg = { 0 };
     + 	struct reftable_write_options cfg = {
      @@ reftable/stack_test.c: static void test_reftable_stack_compaction_concurrent_clean(void)
       int stack_test_main(int argc, const char *argv[])
       {
     @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes are syn
       	EOF
       '
       
     +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: fails gracefully when auto compaction fail
     + 			done ||
     + 			exit 1
     + 		done &&
     +-		test_line_count = 13 .git/reftable/tables.list
     ++		test_line_count = 10 .git/reftable/tables.list
     + 	)
     + '
     + 
      @@ t/t0610-reftable-basics.sh: test_expect_success 'pack-refs: compacts tables' '
       
       	test_commit -C repo A &&
     @@ t/t0610-reftable-basics.sh: test_expect_success 'pack-refs: compacts tables' '
       
       	git -C repo pack-refs &&
       	ls -1 repo/.git/reftable >table-files &&
     +@@ t/t0610-reftable-basics.sh: test_expect_success "$command: auto compaction" '
     + 		# The tables should have been auto-compacted, and thus auto
     + 		# compaction should not have to do anything.
     + 		ls -1 .git/reftable >tables-expect &&
     +-		test_line_count = 4 tables-expect &&
     ++		test_line_count = 3 tables-expect &&
     + 		git $command --auto &&
     + 		ls -1 .git/reftable >tables-actual &&
     + 		test_cmp tables-expect tables-actual &&
     +@@ t/t0610-reftable-basics.sh: test_expect_success "$command: auto compaction" '
     + 		git branch B &&
     + 		git branch C &&
     + 		rm .git/reftable/*.lock &&
     +-		test_line_count = 5 .git/reftable/tables.list &&
     ++		test_line_count = 4 .git/reftable/tables.list &&
     + 
     + 		git $command --auto &&
     + 		test_line_count = 1 .git/reftable/tables.list
      @@ t/t0610-reftable-basics.sh: do
       			umask $umask &&
       			git init --shared=true repo &&

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* Re: Git log --decorate show prefetch objects
  2024-04-04 10:16  4% Git log --decorate show prefetch objects Alexandre Badez
@ 2024-04-04 16:56  0% ` Junio C Hamano
  2024-04-04 21:37  0%   ` Alexandre Badez
  0 siblings, 1 reply; 200+ results
From: Junio C Hamano @ 2024-04-04 16:56 UTC (permalink / raw)
  To: Alexandre Badez; +Cc: git, Derrick Stolee

Alexandre Badez <alexandre@badez.eu> writes:

> # What did you do before the bug happened? (Steps to reproduce your issue)
>
> <on a git repository not fetch from a "long" time so you have missing
> branches and/or objects>
>
> git maintenance run --task=prefetch
>
> git log --oneline --decorate --all --graph

I do not use the prefetch stuff, but unfortunately the person who
was most familiar with "maintenance" are no longer active on this
list, so let me take a crack.

I think your complaint is that "--all" really means "all", not just
"heads" and "tags" but also includes "prefetch", while the prefetch
hierarchy is not included in the "--decorate" sources.

What does

    $ git log --oneline --branches --tags --decorate --graph

show, and is it closer to what you expected (note: this is not a
direct suggestion of a workaround---trying to gauge what the
direction is to move forward)?

Thanks.


^ permalink raw reply	[relevance 0%]

* Git log --decorate show prefetch objects
@ 2024-04-04 10:16  4% Alexandre Badez
  2024-04-04 16:56  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Alexandre Badez @ 2024-04-04 10:16 UTC (permalink / raw)
  To: git

# What did you do before the bug happened? (Steps to reproduce your issue)

<on a git repository not fetch from a "long" time so you have missing 
branches and/or objects>

git maintenance run --task=prefetch

git log --oneline --decorate --all --graph

NOTE: here:
     - xxxxxxx commits are local commits,
     - yyyyyyy commits are remote commits fetch previously,
     - zzzzzzz commits are remote commits prefetch.

     * xxxxxxx (HEAD -> current-dev, origin/master, origin/HEAD) feat
     * xxxxxxx feat
     | * yyyyyyy (origin/feature) feat
     |/
     * xxxxxxx feat
     | * yyyyyyy (origin/a-fix) fix
     |/
     * xxxxxxx fix
     | * zzzzzzz feat
     | * zzzzzzz feat
     | * zzzzzzz feat
     |/
     * xxxxxxx feat
     | * zzzzzzz feat
     |/
     * xxxxxxx fix


According to the git-log  documentation (cf: 
https://git-scm.com/docs/git-log#Documentation/git-log.txt---all ):

     --all Pretend as if all the refs in refs/, along with HEAD, are 
listed on the command line as <commit>.

But the --decorate (cf: 
https://git-scm.com/docs/git-log#Documentation/git-log.txt---decorateshortfullautono 
):

     --decorate
     Print out the ref names of any commits that are shown. If short is 
specified,
     the ref name prefixes refs/heads/, refs/tags/ and refs/remotes/ 
will not be printed.
     [...]
     The option --decorate is short-hand for --decorate=short.

Documentation of maintenant prefetch (cf: 
https://git-scm.com/docs/git-maintenance#Documentation/git-maintenance.txt-prefetch 
):

     The prefetch task updates the object directory with the latest 
objects from all registered remotes.
     For each remote, a git fetch command is run.
     The configured refspec is modified to place all requested refs 
within refs/prefetch/.
     Also, tags are not updated.



# What did you expect to happen? (Expected behavior)

I would have prefer this output (prefetch not shown):

     * xxxxxxx (HEAD -> current-dev, origin/master, origin/HEAD) feat
     * xxxxxxx feat
     | * yyyyyyy (origin/feature) feat
     |/
     * xxxxxxx feat
     | * yyyyyyy (origin/a-fix) fix
     |/
     * xxxxxxx fix
     * xxxxxxx feat
     * xxxxxxx fix

Or this output (prefetch properly decorated):

     * xxxxxxx (HEAD -> current-dev, origin/master, origin/HEAD) feat
     * xxxxxxx feat
     | * yyyyyyy (origin/feature) feat
     |/
     * xxxxxxx feat
     | * yyyyyyy (origin/a-fix) fix
     |/
     * xxxxxxx fix
     | * zzzzzzz (refs/prefetch/remotes/origin/anotherfeat) feat
     | * zzzzzzz feat
     | * zzzzzzz feat
     |/
     * xxxxxxx feat
     | * zzzzzzz (refs/prefetch/remotes/origin/anotherotherfeat) feat
     |/
     * xxxxxxx fix


# What happened instead? (Actual behavior)

Prefetch object are return and not properly decorated.



# What's different between what you expected and what actually happened?

I would have preferd prefetched objects not displayed


# Information

[System Info]
git version: git version 2.44.0
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/bash
bash version: 5.2.26
uname: Linux 6.8.1 #1-NixOS SMP PREEMPT_DYNAMIC Fri Mar 15 18:19:29 UTC 
2024 x86_64
compiler info: gnuc: 13.2
libc info: glibc: 2.39
$SHELL (typically, interactive shell): /bin/fish



^ permalink raw reply	[relevance 4%]

* [PATCH] sequencer: honor signoff opt in run_git_commit
@ 2024-04-04  9:39  3% David Bimmler via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: David Bimmler via GitGitGadget @ 2024-04-04  9:39 UTC (permalink / raw)
  To: git; +Cc: David Bimmler, David Bimmler

From: David Bimmler <david.bimmler@isovalent.com>

When rebasing interactively, --signoff would not take effect for commits
which conflict. That is, commits applying cleanly would be signed off,
but commits requiring intervention would miss the sign off trailer.

The reason is that run_git_commit did not check for the signoff replay
opt, and hence even though the option was picked up and passed
correctly, the actual committing dropped the ball.

The patch adds a test specifically for this case, as well as amending a
squash test which codified the broken behaviour.

Signed-off-by: David Bimmler <david.bimmler@isovalent.com>
---
    sequencer: honor signoff opt in run_git_commit

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1707%2Fbimmlerd%2Fsignoff-conflicting-commits-in-rebase-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1707/bimmlerd/signoff-conflicting-commits-in-rebase-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1707

 sequencer.c                     |  2 ++
 t/t3428-rebase-signoff.sh       | 33 +++++++++++++++++++++++++++++++++
 t/t3437/expected-squash-message |  2 ++
 3 files changed, 37 insertions(+)

diff --git a/sequencer.c b/sequencer.c
index fa838f264f5..16595e26a17 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1121,6 +1121,8 @@ static int run_git_commit(const char *defmsg,
 		strvec_pushf(&cmd.args, "-S%s", opts->gpg_sign);
 	else
 		strvec_push(&cmd.args, "--no-gpg-sign");
+	if (opts->signoff)
+		strvec_push(&cmd.args, "--signoff");
 	if (defmsg)
 		strvec_pushl(&cmd.args, "-F", defmsg, NULL);
 	else if (!(flags & EDIT_MSG))
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index e1b1e947647..fcecdf41978 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -27,6 +27,13 @@ first
 Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
 EOF
 
+# Expected signed off message after resolving the conflict
+cat >expected-signed-after-conflict <<EOF
+update file on side
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
 # Expected commit message after rebase without --signoff (or with --no-signoff)
 cat >expected-unsigned <<EOF
 first
@@ -82,4 +89,30 @@ test_expect_success 'rebase -m --signoff fails' '
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	test_cmp expected-signed actual
 '
+
+test_expect_success 'rebase -i signs commits even if a conflict occurs' '
+	git branch -M main &&
+
+	git branch side &&
+	echo "b" >file &&
+	git add file &&
+	git commit -m"update file" &&
+	test_tick &&
+
+	git checkout side &&
+	echo "side" >file &&
+	git add file &&
+	git commit -m"update file on side" &&
+	test_tick &&
+
+	test_must_fail git rebase -i --signoff main &&
+
+	echo "merged" >file &&
+	git add file &&
+	git rebase --continue &&
+
+	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+	test_cmp expected-signed-after-conflict actual
+'
+
 test_done
diff --git a/t/t3437/expected-squash-message b/t/t3437/expected-squash-message
index ab2434f90ed..d74af0bcf6b 100644
--- a/t/t3437/expected-squash-message
+++ b/t/t3437/expected-squash-message
@@ -48,4 +48,6 @@ edited 1
 edited 2
 
 edited 3
+
+Signed-off-by: Rebase Committer <rebase.committer@example.com>
 squashed

base-commit: 7774cfed6261ce2900c84e55906da708c711d601
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* Re: [PATCH] rebase -i: improve error message when picking merge
    2024-04-03 13:42  0% ` phillip.wood123
@ 2024-04-04  6:08  0% ` Patrick Steinhardt
  2024-04-08 14:16  3% ` [PATCH v2 0/2] " Phillip Wood via GitGitGadget
  2 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-04  6:08 UTC (permalink / raw)
  To: Phillip Wood via GitGitGadget
  Cc: git, Stefan Haller, Johannes Schindelin, Phillip Wood

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

On Mon, Feb 26, 2024 at 10:58:07AM +0000, Phillip Wood via GitGitGadget wrote:
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> The only todo commands that accept a merge commit are "merge" and
> "reset". All the other commands like "pick" or "reword" fail when they
> try to pick a a merge commit and print the message
> 
>     error: commit abc123 is a merge but no -m option was given.
> 
> followed by a hint about the command being rescheduled. This message is
> designed to help the user when they cherry-pick a merge and forget to
> pass "-m". For users who are rebasing the message is confusing as there
> is no way for rebase to cherry-pick the merge.
> 
> Improve the user experience by detecting the error when the todo list is
> parsed rather than waiting for the "pick" command to fail and print a
> message recommending the "merge" command instead. We recommend "merge"
> rather than "exec git cherry-pick -m ..." on the assumption that
> cherry-picking merges is relatively rare and it is more likely that the
> user chose "pick" by a mistake.
> 
> It would be possible to support cherry-picking merges by allowing the
> user to pass "-m" to "pick" commands but that adds complexity to do
> something that can already be achieved with
> 
>     exec git cherry-pick -m1 abc123

Okay. I think it's reasonable to abort early and give some advice here.
It's certainly a lot more user friendly than to abort during the rebase.
And if we ever gain the ability to e.g. `pick -m1` in the instruction
sheet directly we can adapt accordingly.

> The change is relatively straight forward but is complicated slightly as
> we now need to tell the parser if we're rebasing or not.
> 
> Reported-by: Stefan Haller <lists@haller-berlin.de>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>     rebase -i: improve error message when picking merge
> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1672%2Fphillipwood%2Frebase-reject-merges-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1672/phillipwood/rebase-reject-merges-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1672
> 
>  builtin/rebase.c              |  2 +-
>  rebase-interactive.c          |  7 ++---
>  sequencer.c                   | 49 ++++++++++++++++++++++++++++++-----
>  sequencer.h                   |  2 +-
>  t/t3404-rebase-interactive.sh | 33 +++++++++++++++++++++++
>  5 files changed, 81 insertions(+), 12 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 5b086f651a6..a33e41c44da 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -297,7 +297,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
>  	else {
>  		discard_index(&the_index);
>  		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
> -						&todo_list))
> +						&todo_list, 1))

I think these booleans are somewhat hard to read. I may be
overengineering this, so please feel free to push back, but would it
make more sense to introduce a `unsigned flags` field and then have a
`PARSE_INSN_IS_REBASING` flag?

>  			BUG("unusable todo list");
>  
>  		ret = complete_action(the_repository, &replay, flags,
> diff --git a/rebase-interactive.c b/rebase-interactive.c
> index d9718409b3d..78d5ed1a41d 100644
> --- a/rebase-interactive.c
> +++ b/rebase-interactive.c
> @@ -114,7 +114,8 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
>  	 * it.  If there is an error, we do not return, because the user
>  	 * might want to fix it in the first place. */
>  	if (!initial)
> -		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list) |
> +		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf,
> +							todo_list, 1) |
>  			file_exists(rebase_path_dropped());
>  
>  	if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
> @@ -134,7 +135,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
>  	if (initial && new_todo->buf.len == 0)
>  		return -3;
>  
> -	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo)) {
> +	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo, 1)) {
>  		fprintf(stderr, _(edit_todo_list_advice));
>  		return -4;
>  	}
> @@ -234,7 +235,7 @@ int todo_list_check_against_backup(struct repository *r, struct todo_list *todo_
>  	int res = 0;
>  
>  	if (strbuf_read_file(&backup.buf, rebase_path_todo_backup(), 0) > 0) {
> -		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup);
> +		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup, 1);
>  		res = todo_list_check(&backup, todo_list);
>  	}
>  
> diff --git a/sequencer.c b/sequencer.c
> index 91de546b323..cf808c24d20 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -2550,8 +2550,37 @@ static int check_label_or_ref_arg(enum todo_command command, const char *arg)
>  	return 0;
>  }
>  
> +static int error_merge_commit(enum todo_command command)
> +{
> +	switch(command) {
> +	case TODO_PICK:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s'"),
> +			     todo_command_info[command].str, "merge -C");

I wonder how actionable these commands are. Can we give the full command
that the user can use instead, including the commit ID?

That raises another question though: how exactly is the user supposed to
perform the merge? Should they merge the merge commit, resulting in two
merge commits? Should they pick one of the sides, and if so, which one?
I guess the answer is "it depends", which makes it harder for us to come
up with actionable advice here.

> +	case TODO_REWORD:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s'"),
> +			     todo_command_info[command].str, "merge -c");

I was about to suggest that the above two cases should be merged. But
then I realized that it's "merge -c" here, but "merge -C" in the first
case.

Patrick

> +	case TODO_EDIT:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s' followed by '%s'"),
> +			     todo_command_info[command].str,
> +			     "merge -C", "break");
> +
> +	case TODO_FIXUP:
> +	case TODO_SQUASH:
> +		return error(_("cannot squash merge commit into another commit"));
> +
> +	default:
> +		BUG("unexpected todo_command");
> +	}
> +}
> +
>  static int parse_insn_line(struct repository *r, struct todo_item *item,
> -			   const char *buf, const char *bol, char *eol)
> +			   const char *buf, const char *bol, char *eol,
> +			   int rebasing)
>  {
>  	struct object_id commit_oid;
>  	char *end_of_object_name;
> @@ -2655,7 +2684,12 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
>  		return status;
>  
>  	item->commit = lookup_commit_reference(r, &commit_oid);
> -	return item->commit ? 0 : -1;
> +	if (!item->commit)
> +		return -1;
> +	if (rebasing && item->command != TODO_MERGE &&
> +	    item->commit->parents && item->commit->parents->next)
> +		return error_merge_commit(item->command);
> +	return 0;
>  }
>  
>  int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
> @@ -2686,7 +2720,7 @@ int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *
>  }
>  
>  int todo_list_parse_insn_buffer(struct repository *r, char *buf,
> -				struct todo_list *todo_list)
> +				struct todo_list *todo_list, int rebasing)
>  {
>  	struct todo_item *item;
>  	char *p = buf, *next_p;
> @@ -2704,7 +2738,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
>  
>  		item = append_new_todo(todo_list);
>  		item->offset_in_buf = p - todo_list->buf.buf;
> -		if (parse_insn_line(r, item, buf, p, eol)) {
> +		if (parse_insn_line(r, item, buf, p, eol, rebasing)) {
>  			res = error(_("invalid line %d: %.*s"),
>  				i, (int)(eol - p), p);
>  			item->command = TODO_COMMENT + 1;
> @@ -2852,7 +2886,8 @@ static int read_populate_todo(struct repository *r,
>  	if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
>  		return -1;
>  
> -	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
> +	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list,
> +					  is_rebase_i(opts));
>  	if (res) {
>  		if (is_rebase_i(opts))
>  			return error(_("please fix this using "
> @@ -2882,7 +2917,7 @@ static int read_populate_todo(struct repository *r,
>  		struct todo_list done = TODO_LIST_INIT;
>  
>  		if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
> -		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
> +		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done, 1))
>  			todo_list->done_nr = count_commands(&done);
>  		else
>  			todo_list->done_nr = 0;
> @@ -6286,7 +6321,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
>  	strbuf_release(&buf2);
>  	/* Nothing is done yet, and we're reparsing, so let's reset the count */
>  	new_todo.total_nr = 0;
> -	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
> +	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo, 1) < 0)
>  		BUG("invalid todo list after expanding IDs:\n%s",
>  		    new_todo.buf.buf);
>  
> diff --git a/sequencer.h b/sequencer.h
> index dcef7bb99c0..ed2c4b38514 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -136,7 +136,7 @@ struct todo_list {
>  }
>  
>  int todo_list_parse_insn_buffer(struct repository *r, char *buf,
> -				struct todo_list *todo_list);
> +				struct todo_list *todo_list, int rebasing);
>  int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
>  			    const char *file, const char *shortrevisions,
>  			    const char *shortonto, int num, unsigned flags);
> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
> index 64b641002e4..20b8589ad07 100755
> --- a/t/t3404-rebase-interactive.sh
> +++ b/t/t3404-rebase-interactive.sh
> @@ -2203,6 +2203,39 @@ test_expect_success 'bad labels and refs rejected when parsing todo list' '
>  	test_path_is_missing execed
>  '
>  
> +test_expect_success 'non-merge commands reject merge commits' '
> +	test_when_finished "test_might_fail git rebase --abort" &&
> +	git checkout E &&
> +	git merge I &&
> +	oid=$(git rev-parse HEAD) &&
> +	cat >todo <<-EOF &&
> +	pick $oid
> +	reword $oid
> +	edit $oid
> +	fixup $oid
> +	squash $oid
> +	EOF
> +	(
> +		set_replace_editor todo &&
> +		test_must_fail git rebase -i HEAD 2>actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	error: ${SQ}pick${SQ} does not accept merge commits, please use ${SQ}merge -C${SQ}
> +	error: invalid line 1: pick $oid
> +	error: ${SQ}reword${SQ} does not accept merge commits, please use ${SQ}merge -c${SQ}
> +	error: invalid line 2: reword $oid
> +	error: ${SQ}edit${SQ} does not accept merge commits, please use ${SQ}merge -C${SQ} followed by ${SQ}break${SQ}
> +	error: invalid line 3: edit $oid
> +	error: cannot squash merge commit into another commit
> +	error: invalid line 4: fixup $oid
> +	error: cannot squash merge commit into another commit
> +	error: invalid line 5: squash $oid
> +	You can fix this with ${SQ}git rebase --edit-todo${SQ} and then run ${SQ}git rebase --continue${SQ}.
> +	Or you can abort the rebase with ${SQ}git rebase --abort${SQ}.
> +	EOF
> +	test_cmp expect actual
> +'
> +
>  # This must be the last test in this file
>  test_expect_success '$EDITOR and friends are unchanged' '
>  	test_editor_unchanged
> 
> base-commit: c875e0b8e036c12cfbf6531962108a063c7a821c
> -- 
> gitgitgadget
> 

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

^ permalink raw reply	[relevance 0%]

* [PATCH v5] userdiff: better method/property matching for C#
  2024-03-28 19:14  1%     ` [PATCH v4] " Steven Jeuris via GitGitGadget
@ 2024-04-03 21:42  1%       ` Steven Jeuris via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Steven Jeuris via GitGitGadget @ 2024-04-03 21:42 UTC (permalink / raw)
  To: git
  Cc: Ævar Arnfjörð Bjarmason, Jeff King, Linus Arver,
	Johannes Sixt, Steven Jeuris, Steven Jeuris

From: Steven Jeuris <steven.jeuris@3shape.com>

- Support multi-line methods by not requiring closing parenthesis.
- Support multiple generics (comma was missing before).
- Add missing `foreach`, `lock` and  `fixed` keywords to skip over.
- Remove `instanceof` keyword, which isn't C#.
- Also detect non-method keywords not positioned at the start of a line.
- Added tests; none existed before.

The overall strategy is to focus more on what isn't expected for
method/property definitions, instead of what is, but is fully optional.

Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com>
---
    userdiff: better method/property matching for C#
    
    Change since v1: I removed "from" from the list of keywords to skip.
    First, I considered adding "await", but I discovered both "await" and
    "from" are "contextual keywords", which unlike the other keywords
    currently listed, aren't reserved, and can thus cause false negatives.
    I.e., it is valid to have a method named "await" or "from". In edge
    cases, this may lead to false positives, but a different exclusion rule
    will need to be added to handle these.
    
    Change since v2:
    
     * Corrected comment formatting.
     * Added csharp-property-skip-body test.
     * Added comments in test code to explain sections not part of the test.
     * Elaborated regex comments.
     * Excluded math operators (+-*/%) in method pattern to not catch
       multiline operations, and tested for this in the -skip-body tests.
       Catching "-" only worked when it was defined at the end of the
       exclusion block for some reason. The regex matcher seems quite
       bugged.
    
    Change since v3:
    
     * Changed regex to better handle whitespace in types, making use of the
       fact that it only appears after commas.
     * Split regex into multiple lines with comments explaining structure.
     * Split the "skip body" tests into more narrow csharp-exclude- tests.
     * Added a test for generic methods:
       csharp-exclude-generic-method-calls.
     * Added a test for array types used in methods: csharp-method-array.
     * Added an addition property test: csharp-property-braces-same-line.
     * Included a test for "( func(x)" case identified by Johannes in
       csharp-exclude-assignments.
    
    As before, I ran into many regex limitations (no possessive quantifiers,
    no lookahead). It also seems different regex evaluators are used on
    different test runs. Which one does git diff use? Maybe it is about time
    to update this? E.g., if speed is a concern, possessive quantifiers can
    speed up search.
    
    Change since v4:
    
     * Better matching of at least two "words".
     * Better handling of generics by restricting commas within < ... >.
     * Allow any spaces around commas in generics.
     * Because of stricter use of comma, Johannes' identified failing cases
       now pass.
     * Updated tests to cover all of the above.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1682%2FWhathecode%2Fmaster-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1682/Whathecode/master-v5
Pull-Request: https://github.com/git/git/pull/1682

Range-diff vs v4:

 1:  2feb84beaa0 ! 1:  00191ef695a userdiff: better method/property matching for C#
     @@ t/t4018/csharp-exclude-method-calls (new)
      +        MethodCall(1, 2);
      +        MethodCall(
      +            1, 2);
     ++        MethodCall(
     ++            1, 2,
     ++            3);
     ++        MethodCall(
     ++            1, MethodCall(),
     ++            2);
      +
      +        return "ChangeMe";
      +    }
      +
     -+    string MethodCall(int a = 0, int b = 0) => "test";
     -+    string GenericMethodCall<T, T2>() => "test";
     ++    int MethodCall(int a = 0, int b = 0, int c = 0) => 42;
      +}
      
       ## t/t4018/csharp-exclude-other (new) ##
     @@ t/t4018/csharp-method-generics (new)
      +        // ChangeMe
      +        return null;
      +    }
     ++}
     +
     + ## t/t4018/csharp-method-generics-alternate-spaces (new) ##
     +@@
     ++class Example<T1, T2>
     ++{
     ++    Example<int,string> Method<TA ,TB>(TA RIGHT, TB b)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        return null;
     ++    }
      +}
      
       ## t/t4018/csharp-method-modifiers (new) ##
     @@ userdiff.c: PATTERNS("cpp",
      +	  */
      +	 "^[ \t]*" /* Remove leading whitespace. */
      +		"(" /* Start chunk header capture. */
     -+		"(" /* Group of keywords/type/names. */
     -+		"([][[:alnum:]@_<>.]|, [ |\t]*)+" /* Space only allowed after ",". */
     -+		"[ \t]+" /* One required space forces a minimum of two items. */
     -+		"([][[:alnum:]@_<>.]|, [ |\t]*)+"
     -+		"[ \t]*" /* Optional space before parameters start. */
     ++		"(" /* First group. */
     ++			"[][[:alnum:]@_.]" /* Name. */
     ++			"(<[][[:alnum:]@_, \t<>]+>)?" /* Optional generic parameters. */
      +		")+"
     ++		"([ \t]+" /* Subsequent groups, prepended with space. */
     ++			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
     ++		")+"
     ++		"[ \t]*" /* Optional space before parameters start. */
      +		"\\(" /* Start of method parameters. */
      +		"[^;]*" /* Allow complex parameters, but exclude statements (;). */
      +		")$\n" /* Close chunk header capture. */
     @@ userdiff.c: PATTERNS("cpp",
      +	  * defined, since they don't have a parameter list.
      +	  */
      +	 "^[ \t]*("
     -+		"("
     -+		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]+"
     -+		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]*"
     ++		"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
     ++		"([ \t]+"
     ++			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
      +		")+" /* Up to here, same as methods regex. */
      +		"[^;=:,()]*" /* Compared to methods, no parameter list allowed. */
      +		")$\n"


 t/t4018/csharp-exclude-assignments            | 20 ++++++++
 t/t4018/csharp-exclude-control-statements     | 34 +++++++++++++
 t/t4018/csharp-exclude-exceptions             | 29 +++++++++++
 t/t4018/csharp-exclude-generic-method-calls   | 12 +++++
 t/t4018/csharp-exclude-init-dispose           | 22 +++++++++
 t/t4018/csharp-exclude-iterations             | 26 ++++++++++
 t/t4018/csharp-exclude-method-calls           | 20 ++++++++
 t/t4018/csharp-exclude-other                  | 18 +++++++
 t/t4018/csharp-method                         | 10 ++++
 t/t4018/csharp-method-array                   | 10 ++++
 t/t4018/csharp-method-explicit                | 12 +++++
 t/t4018/csharp-method-generics                | 11 +++++
 .../csharp-method-generics-alternate-spaces   | 11 +++++
 t/t4018/csharp-method-modifiers               | 13 +++++
 t/t4018/csharp-method-multiline               | 10 ++++
 t/t4018/csharp-method-params                  | 10 ++++
 t/t4018/csharp-method-special-chars           | 11 +++++
 t/t4018/csharp-method-with-spacing            | 10 ++++
 t/t4018/csharp-property                       | 11 +++++
 t/t4018/csharp-property-braces-same-line      | 10 ++++
 userdiff.c                                    | 48 ++++++++++++++++---
 21 files changed, 352 insertions(+), 6 deletions(-)
 create mode 100644 t/t4018/csharp-exclude-assignments
 create mode 100644 t/t4018/csharp-exclude-control-statements
 create mode 100644 t/t4018/csharp-exclude-exceptions
 create mode 100644 t/t4018/csharp-exclude-generic-method-calls
 create mode 100644 t/t4018/csharp-exclude-init-dispose
 create mode 100644 t/t4018/csharp-exclude-iterations
 create mode 100644 t/t4018/csharp-exclude-method-calls
 create mode 100644 t/t4018/csharp-exclude-other
 create mode 100644 t/t4018/csharp-method
 create mode 100644 t/t4018/csharp-method-array
 create mode 100644 t/t4018/csharp-method-explicit
 create mode 100644 t/t4018/csharp-method-generics
 create mode 100644 t/t4018/csharp-method-generics-alternate-spaces
 create mode 100644 t/t4018/csharp-method-modifiers
 create mode 100644 t/t4018/csharp-method-multiline
 create mode 100644 t/t4018/csharp-method-params
 create mode 100644 t/t4018/csharp-method-special-chars
 create mode 100644 t/t4018/csharp-method-with-spacing
 create mode 100644 t/t4018/csharp-property
 create mode 100644 t/t4018/csharp-property-braces-same-line

diff --git a/t/t4018/csharp-exclude-assignments b/t/t4018/csharp-exclude-assignments
new file mode 100644
index 00000000000..239f312963b
--- /dev/null
+++ b/t/t4018/csharp-exclude-assignments
@@ -0,0 +1,20 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        var constantAssignment = "test";
+        var methodAssignment = MethodCall();
+        var multiLineMethodAssignment = MethodCall(
+        );
+        var multiLine = "first"
+            + MethodCall()
+            +
+            ( MethodCall()
+            )
+            + MethodCall();
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-control-statements b/t/t4018/csharp-exclude-control-statements
new file mode 100644
index 00000000000..3a0f404ee10
--- /dev/null
+++ b/t/t4018/csharp-exclude-control-statements
@@ -0,0 +1,34 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        if (false)
+        {
+            return "out";
+        }
+        else { }
+        if (true) MethodCall(
+        );
+        else MethodCall(
+        );
+        switch ("test")
+        {
+            case "one":
+            return MethodCall(
+            );
+            case "two":
+            break;
+        }
+        (int, int) tuple = (1, 4);
+        switch (tuple)
+        {
+            case (1, 4):
+              MethodCall();
+		      break;
+        }
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-exceptions b/t/t4018/csharp-exclude-exceptions
new file mode 100644
index 00000000000..b1e64256cfe
--- /dev/null
+++ b/t/t4018/csharp-exclude-exceptions
@@ -0,0 +1,29 @@
+using System;
+
+class Example
+{
+    string Method(int RIGHT)
+    {
+        try
+        {
+            throw new Exception("fail");
+        }
+        catch (Exception)
+        {
+        }
+        finally
+        {
+        }
+        try { } catch (Exception) {}
+        try
+        {
+            throw GetException(
+            );
+        }
+        catch (Exception) { }
+
+        return "ChangeMe";
+    }
+
+    Exception GetException() => new Exception("fail");
+}
diff --git a/t/t4018/csharp-exclude-generic-method-calls b/t/t4018/csharp-exclude-generic-method-calls
new file mode 100644
index 00000000000..31af546665d
--- /dev/null
+++ b/t/t4018/csharp-exclude-generic-method-calls
@@ -0,0 +1,12 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        GenericMethodCall<int, int>(
+            );
+
+        return "ChangeMe";
+    }
+
+    string GenericMethodCall<T, T2>() => "test";
+}
diff --git a/t/t4018/csharp-exclude-init-dispose b/t/t4018/csharp-exclude-init-dispose
new file mode 100644
index 00000000000..2bc8e194e20
--- /dev/null
+++ b/t/t4018/csharp-exclude-init-dispose
@@ -0,0 +1,22 @@
+using System;
+
+class Example : IDisposable
+{
+    string Method(int RIGHT)
+    {
+        new Example();
+        new Example(
+            );
+        new Example { };
+        using (this)
+        {
+        }
+        var def =
+            this is default(
+                Example);
+
+        return "ChangeMe";
+    }
+
+    public void Dispose() {}
+}
diff --git a/t/t4018/csharp-exclude-iterations b/t/t4018/csharp-exclude-iterations
new file mode 100644
index 00000000000..960aa182ae2
--- /dev/null
+++ b/t/t4018/csharp-exclude-iterations
@@ -0,0 +1,26 @@
+using System.Linq;
+
+class Example
+{
+    string Method(int RIGHT)
+    {
+        do { } while (true);
+        do MethodCall(
+        ); while (true);
+        while (true);
+        while (true) {
+            break;
+        }
+        for (int i = 0; i < 10; ++i)
+        {
+        }
+        foreach (int i in Enumerable.Range(0, 10))
+        {
+        }
+        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-method-calls b/t/t4018/csharp-exclude-method-calls
new file mode 100644
index 00000000000..51e2dc20407
--- /dev/null
+++ b/t/t4018/csharp-exclude-method-calls
@@ -0,0 +1,20 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        MethodCall();
+        MethodCall(1, 2);
+        MethodCall(
+            1, 2);
+        MethodCall(
+            1, 2,
+            3);
+        MethodCall(
+            1, MethodCall(),
+            2);
+
+        return "ChangeMe";
+    }
+
+    int MethodCall(int a = 0, int b = 0, int c = 0) => 42;
+}
diff --git a/t/t4018/csharp-exclude-other b/t/t4018/csharp-exclude-other
new file mode 100644
index 00000000000..4d5581cf3e1
--- /dev/null
+++ b/t/t4018/csharp-exclude-other
@@ -0,0 +1,18 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        lock (this)
+        {
+        }
+        unsafe
+        {
+            byte[] bytes = [1, 2, 3];
+            fixed (byte* pointerToFirst = bytes)
+            {
+            }
+        }
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
new file mode 100644
index 00000000000..16b367aca2b
--- /dev/null
+++ b/t/t4018/csharp-method
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-array b/t/t4018/csharp-method-array
new file mode 100644
index 00000000000..1126de8201d
--- /dev/null
+++ b/t/t4018/csharp-method-array
@@ -0,0 +1,10 @@
+class Example
+{
+    string[] Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+
+        return ["ChangeMe"];
+    }
+}
diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
new file mode 100644
index 00000000000..5a710116cc4
--- /dev/null
+++ b/t/t4018/csharp-method-explicit
@@ -0,0 +1,12 @@
+using System;
+
+class Example : IDisposable
+{
+    void IDisposable.Dispose() // RIGHT
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+    }
+}
diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
new file mode 100644
index 00000000000..b3216bfb2a7
--- /dev/null
+++ b/t/t4018/csharp-method-generics
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return null;
+    }
+}
diff --git a/t/t4018/csharp-method-generics-alternate-spaces b/t/t4018/csharp-method-generics-alternate-spaces
new file mode 100644
index 00000000000..95836217430
--- /dev/null
+++ b/t/t4018/csharp-method-generics-alternate-spaces
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+    Example<int,string> Method<TA ,TB>(TA RIGHT, TB b)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return null;
+    }
+}
diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
new file mode 100644
index 00000000000..caefa8ee99c
--- /dev/null
+++ b/t/t4018/csharp-method-modifiers
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+class Example
+{
+    static internal async Task Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        await Task.Delay(1);
+    }
+}
diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
new file mode 100644
index 00000000000..3983ff42f51
--- /dev/null
+++ b/t/t4018/csharp-method-multiline
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method_RIGHT(
+        int a,
+        int b,
+        int c)
+    {
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
new file mode 100644
index 00000000000..3f00410ba1f
--- /dev/null
+++ b/t/t4018/csharp-method-params
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT, int b, int c = 42)
+    {
+        // Filler
+        // Filler
+        
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
new file mode 100644
index 00000000000..e6c7bc01a18
--- /dev/null
+++ b/t/t4018/csharp-method-special-chars
@@ -0,0 +1,11 @@
+class @Some_Type
+{
+    @Some_Type @Method_With_Underscore(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return new @Some_Type();
+    }
+}
diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
new file mode 100644
index 00000000000..233bb976cc2
--- /dev/null
+++ b/t/t4018/csharp-method-with-spacing
@@ -0,0 +1,10 @@
+class Example
+{
+    	string   Method 	( int 	RIGHT )
+    {
+        // Filler
+        // Filler
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
new file mode 100644
index 00000000000..e56dfce34c1
--- /dev/null
+++ b/t/t4018/csharp-property
@@ -0,0 +1,11 @@
+class Example
+{
+    public bool RIGHT
+    {
+        get { return true; }
+        set
+        {
+            // ChangeMe
+        }
+    }
+}
diff --git a/t/t4018/csharp-property-braces-same-line b/t/t4018/csharp-property-braces-same-line
new file mode 100644
index 00000000000..608131d3d31
--- /dev/null
+++ b/t/t4018/csharp-property-braces-same-line
@@ -0,0 +1,10 @@
+class Example
+{
+    public bool RIGHT {
+        get { return true; }
+        set
+        {
+            // ChangeMe
+        }
+    }
+}
diff --git a/userdiff.c b/userdiff.c
index e399543823b..0d667a1f5a6 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -89,12 +89,48 @@ PATTERNS("cpp",
 	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
 PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /*
+	  * Jump over reserved keywords which are illegal method names, but which
+	  * can be followed by parentheses without special characters in between,
+	  * making them look like methods.
+	  */
+	 "!(^|[ \t]+)" /* Start of line or whitespace. */
+		"(do|while|for|foreach|if|else|new|default|return|switch|case|throw"
+		"|catch|using|lock|fixed)"
+		"([ \t(]+|$)\n" /* Whitespace, "(", or end of line. */
+	 /*
+	  * Methods/constructors:
+	  * The strategy is to identify a minimum of two groups (any combination
+	  * of keywords/type/name) before the opening parenthesis, and without
+	  * final unexpected characters, normally only used in ordinary statements.
+	  */
+	 "^[ \t]*" /* Remove leading whitespace. */
+		"(" /* Start chunk header capture. */
+		"(" /* First group. */
+			"[][[:alnum:]@_.]" /* Name. */
+			"(<[][[:alnum:]@_, \t<>]+>)?" /* Optional generic parameters. */
+		")+"
+		"([ \t]+" /* Subsequent groups, prepended with space. */
+			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+		")+"
+		"[ \t]*" /* Optional space before parameters start. */
+		"\\(" /* Start of method parameters. */
+		"[^;]*" /* Allow complex parameters, but exclude statements (;). */
+		")$\n" /* Close chunk header capture. */
+	 /*
+	  * Properties:
+	  * As with methods, expect a minimum of two groups. But, more trivial than
+	  * methods, the vast majority of properties long enough to be worth
+	  * showing a chunk header for don't include "=:;,()" on the line they are
+	  * defined, since they don't have a parameter list.
+	  */
+	 "^[ \t]*("
+		"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+		"([ \t]+"
+			"([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+		")+" /* Up to here, same as methods regex. */
+		"[^;=:,()]*" /* Compared to methods, no parameter list allowed. */
+		")$\n"
 	 /* Type definitions */
 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
 	 /* Namespace */

base-commit: f41f85c9ec8d4d46de0fd5fded88db94d3ec8c11
-- 
gitgitgadget


^ permalink raw reply related	[relevance 1%]

* Re: Worktree shares a common remote with main checkout
       [not found]             ` <17c2d5148e3afb10.70b1dd9aae081c6e.203dcd72f6563036@zivdesk>
@ 2024-04-03 18:29  4%           ` Bill Wallace
  0 siblings, 0 replies; 200+ results
From: Bill Wallace @ 2024-04-03 18:29 UTC (permalink / raw)
  To: Brian Lyles; +Cc: phillip.wood, Kristoffer Haugsbakk, git, Rubén Justo

Each branch has a parent origin/remote repository that it is directly
tracking (or not tracking), but there are other operations that
presume the name "origin" as the name - the two that I care about are:

git fetch
- this should be able to default fetch a remote associated with the worktree

git push (on a new branch)
says:
  git push --set-upstream origin testBranch

I want this to say
  git push --set-upstream project1-remote testBranch

instead.  Ideally, adding this automatically should set the upstream
remote to the specified upstream branch if that is configured (which I
never do, since I often switch the upstream).

Ideally supposing there are two branches  origin/main and
project1-remote/main, then doing:
git checkout main
in worktree project1 would checkout project1-remote/main
while doing it in the original checkout would checkout  origin/main

There is no change to the functionality of worktree branches, the only
change is the ability to store configuration values associated with a
worktree (not a branch, but the worktree itself), plus getting the
name "origin" from configuration instead of defaulting to "origin"

The setting `checkout.defaultRemote` is close - but I want that to be
something like:
worktree.<name>.remote=<value>
eg
worktree.project1.remote=project1-remote

and then:
git fetch
will fetch from project1-remote when in project1 worktree

With git push having auto setup remote, this will default to pushing a
branch testBranch to
project1-remote/testBranch
and without it being auto setup, will show the message:
"git push --set-upstream project1-remote testBranch"
as specified above.

git checkout nonLocalBranch
will first try to get nonLocalBranch from project1-remote, and only if
it is ambiguous on other systems would it complain.

Bill

On Wed, 3 Apr 2024 at 13:24, Brian Lyles <brianmlyles@gmail.com> wrote:
>
> Hi Bill,
>
> Phillip Wood wrote:
>
> > You can set a different default remote for "git pull" for each branch by
> > setting an upstream branch with "git branch --set-upstream-to" which
> > sets "branch.<name>.remote" and "branch.<name>.merge" or
> > "branch.<name>.rebase". You can also set a different remote to push to
> > by setting "branch.<name>.pushRemote" - see the "git config"
> > documentation. Would that help?
>
> Bill Wallace wrote:
>
> > It helps, and I do that, but I really want it associated with the
> > worktree so that I can work on projects for different vendors based on
> > a common open source framework.  What I'm trying to avoid is
> > accidentally committing a branch to the wrong vendor's stream or
> > mixing changes between streams.
>
> My understanding of worktrees has always been that they are simply a
> checkout of an arbitrary branch, and that branches themselves define
> their remote as Phillip noted.
>
> I'm struggling to reconcile this fact of a branch defining its remote
> with having a worktree also define the remote. How would this be
> expected to behave if you try to switch a worktree with some defined
> remote to a branch based on another remote? A warning or error?
>
> I am also wondering if an adjustment to your branch creation/management
> would solve this problem without needing such a significant change to
> the definition of a worktree.
>
> At the point of branch creation, the branch will track a specific remote
> based on the ref it's created from (assuming that ref is a remote
> tracking ref, and based on your `branch.autoSetupMerge`/whether
> `--track`/`--no-track` is used). The remote can be corrected after
> creation as Phillip noted.
>
> For example: My understanding is that if you were to set your
> `branch.autoSetupMerge` to `inherit`, then when you create a new branch
> it would inherit the remote tracking ref (and therefore the remote) from
> the branch that you create it from. This would be the case whether you
> specify a ref manually at the time of creation, or just omit it to
> create the branch from the current checked out branch. If the latter,
> then by creating new branches from within the worktree that you
> designated for a specific vendor/stream, you ensure that the new branch
> uses that same remote.
>
> Have you tried that approach? `branch.autoSetupMerge` has
> several other values to control this behavior as well though `inherit`
> seems closest to what you're looking for to me. If this is still
> insufficient, I'd be curious to hear more specifics about where it
> breaks down.
>
> --
> Thank you,
> Brian Lyles


^ permalink raw reply	[relevance 4%]

* Re: git-merge: --no-commit is not respected on a fresh repository
  2024-04-03 13:26  4% git-merge: --no-commit is not respected on a fresh repository Jasmin Oster
@ 2024-04-03 17:29  0% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-03 17:29 UTC (permalink / raw)
  To: Jasmin Oster; +Cc: git@vger.kernel.org

Jasmin Oster <JOster@anexia.com> writes:

> 1. Create a new repository: $ git init
> 2. Add a remote: $ git remote add foo ../<path-to-another-repo>
> 3. Fetch everything: $ git fetch --all
> 4. Initiate a subtree merge: $ git merge -s our --no-commit foo/main

This is expected.  We are not creating any new commit, so it is
following the --no-commit option to the letter.  Obviously it is
different from what you expected to see, though ;-)

The "merge into void" first came to "git pull $URL $branch" (no
other options) where people tried to pull into a freshly created
repository, where it was very clear that they wanted to have a copy
of the upstream branch.  The "git pull" implementation of the
"pulling into void" feature forgot that curious users may give
"--no-commit", and just always fast-forwarded to the merged commit
without checking if such an option was given.

The behaviour you are seeing was inherited into "git merge".  It
also always fast-forwards to the merged commit, ignoring the option.

In either case, there is no new commit created.  The history you are
seeing is the exact history of the upstream.  If you want to
dissociate your history from theirs and start your history anew:

    $ git merge foo/main
    $ git checkout --orphan master

would give you an unborn branch "master", with the merge result in
the working tree and in the index, without any history behind it.
If you wanted to, you can then rename it to main from this state
with:

    $ git branch -M master main

We could make it more explicit with a patch like the following, but
it probably is not worth it.  I dunno.

 builtin/merge.c | 2 ++
 builtin/pull.c  | 2 ++
 2 files changed, 4 insertions(+)

diff --git c/builtin/merge.c w/builtin/merge.c
index 1cbd6a899c..1c3165e99a 100644
--- c/builtin/merge.c
+++ w/builtin/merge.c
@@ -1434,6 +1434,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		struct object_id *remote_head_oid;
 		if (squash)
 			die(_("Squash commit into empty head not supported yet"));
+		if (!option_commit)
+			die(_("--no-commit into empty head not supported"));
 		if (fast_forward == FF_NO)
 			die(_("Non-fast-forward commit does not make sense into "
 			    "an empty head"));
diff --git c/builtin/pull.c w/builtin/pull.c
index 72cbb76d52..98c11ecc55 100644
--- c/builtin/pull.c
+++ w/builtin/pull.c
@@ -1097,6 +1097,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (is_null_oid(&orig_head)) {
 		if (merge_heads.nr > 1)
 			die(_("Cannot merge multiple branches into empty head."));
+		if (opt_commit && !strcmp("--no-commit", opt_commit))
+			die(_("--no-commit into empty head not supported"));
 		ret = pull_into_void(merge_heads.oid, &curr_head);
 		goto cleanup;
 	}


^ permalink raw reply related	[relevance 0%]

* Re: Worktree shares a common remote with main checkout
  2024-04-03 15:30  0%     ` Phillip Wood
@ 2024-04-03 16:18  0%       ` Bill Wallace
       [not found]             ` <17c2d5148e3afb10.70b1dd9aae081c6e.203dcd72f6563036@zivdesk>
  0 siblings, 1 reply; 200+ results
From: Bill Wallace @ 2024-04-03 16:18 UTC (permalink / raw)
  To: phillip.wood; +Cc: Kristoffer Haugsbakk, git, Rubén Justo

It helps, and I do that, but I really want it associated with the
worktree so that I can work on projects for different vendors based on
a common open source framework.  What I'm trying to avoid is
accidentally committing a branch to the wrong vendor's stream or
mixing changes between streams.

Bill.

On Wed, 3 Apr 2024 at 11:30, Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Bill
>
> On 03/04/2024 15:26, Bill Wallace wrote:
> > The problem is that one wants different default remotes for different
> > worktrees - for example, suppose I'm creating a worktree for two
> > different projects, plus the base repository is on the "origin"
> > default remote.  I then have:
> > base_repository - a directory with the base/default origin (call it
> > 'origin' for now as the remote name)
> > project1 - currently checked out with 'feat/1'
> > project2 - current checkout out with 'feat/2'
> >
> > Now, project1 is being developed against a remote repository
> > 'project1-origin' and project2 is being developed against a remote
> > repository 'project2-origin'
> > However, both are getting merges from origin/main on their own
> > projectX-origin/main branches
> >
> > Now, when I'm the directory for project1 and I do any of:
> >     git fetch
> >     git checkout X
> >     git push
> >
> > I want the correct default to be chosen for the remote - for the
> > base_repository that should be 'origin', while for project1 it should
> > be 'project1-origin' etc.
>
> You can set a different default remote for "git pull" for each branch by
> setting an upstream branch with "git branch --set-upstream-to" which
> sets "branch.<name>.remote" and "branch.<name>.merge" or
> "branch.<name>.rebase". You can also set a different remote to push to
> by setting "branch.<name>.pushRemote" - see the "git config"
> documentation. Would that help?
>
> Best Wishes
>
> Phillip
>
> > I KNOW I can specify those manually, and git push will give a
> > suggestion, but I WANT all of them to default to the correct remote
> > associated with that worktree so that I don't accidentally pick the
> > wrong one or forget to update the correct repository.  This is to fix
> > dumb fingers that sometimes do the wrong thing without thinking, and
> > to try to reduce the number of things that don't get done
> > accidentally.
> >
> > What I'm doing now is to create a new non-worktree version against the
> > projects directories, but that then doesn't share any data.
> >
> > git remote add ... has nothing to do with this, but I want something like:
> >
> > git worktree add project1 --default-remote project1-origin
> >
> > The idea is to make the expectations of what happens to be consistent
> > with cloning a new directory, or at least as close as possible to
> > that.
> >
> > Bill.
> >
> > On Fri, 22 Mar 2024 at 13:29, Kristoffer Haugsbakk <code@khaugsbakk.name> wrote:
> >>
> >> On Fri, Mar 22, 2024, at 15:50, Bill Wallace wrote:
> >>> This issue is just to fix an easy to make mistake when working with
> >>> multiple remote origins and worktrees, where it is too easy to push to
> >>> the wrong remote origin because one can't set the default origin on a
> >>> per-worktree basis.
> >>>
> >>> What did you do before the bug happened? (Steps to reproduce your issue)
> >>> Used
> >>> * git worktree to create a worktree
> >>> * git remote add to add a custom repository
> >>> * git commit/push to try to push changes
> >>>
> >>> What did you expect to happen? (Expected behavior)
> >>> Expected to have the git push recommend a remote origin that matched
> >>> the worktree, but it defaults to 'origin' all
> >>> the time, which means I need to checkout a clean clone from the
> >>> specific origin I'm making changes for so that I don't accidentally
> >>> push to the default origin.
> >>>
> >>> What happened instead? (Actual behavior)
> >>> Suggests 'origin' as the default origin - which is CORRECT for the
> >>> main git branch, but I want to use worktrees to allow working against
> >>> several remote origins, with the default being determined by which
> >>> worktree I'm in.
> >>>
> >>> What's different between what you expected and what actually happened?
> >>> Suggested 'origin' for the --set-default rather than allowing me to
> >>> define the origin I want, for example 'wayfarer' as teh name of my own
> >>> remote that I have cloned on github.  The default origin is still
> >>> supposed to be 'origin' for pulls/naming, but when I push, it needs to
> >>> recommend the matching origin.
> >>>
> >>> Anything else you want to add:
> >>> This is a bit of feature request, but the reason I'm listing it as a
> >>> bug is it makes it very easy to make a mistake by pushing to the wrong
> >>> origin for a new branch.
> >>
> >> I don’t understand the expectation. git-worktree(1) just gives you a new
> >> worktree to work on a branch, do a bisect, maybe a rebase and so on. I
> >> expect `git remote add <remote>` to have nothing to do with the current
> >> worktree that I am in. A remote ref is for the repository, not
> >> per-worktree.
> >>
> >> If you are creating a local branch based on this so-called
> >> worktree-specific remote and this branch exists on this remote (and
> >> *only* on that one) then you can use `git worktree --add --guess-remote`
> >> to automatically track the remote branch.
> >


^ permalink raw reply	[relevance 0%]

* [PATCH] Win32: detect unix socket support at runtime
@ 2024-04-03 15:42  3% Matthias Aßhauer via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Matthias Aßhauer via GitGitGadget @ 2024-04-03 15:42 UTC (permalink / raw)
  To: git
  Cc: Eric Wong, Leslie Cheng, Johannes Schindelin, M Hickford,
	Carlo Marcelo Arenas Belón, Denton Liu, SZEDER Gábor,
	Jeff King, Matthias Aßhauer, Matthias Aßhauer

From: =?UTF-8?q?Matthias=20A=C3=9Fhauer?= <mha1993@live.de>

Windows 10 build 17063 introduced support for unix sockets to Windows.
bb390b1 (git-compat-util: include declaration for unix sockets in
windows, 2021-09-14) introduced a way to build git with unix socket
support on Windows, but you still had to decide at build time which
Windows version the compiled executable was supposed to run on.

We can detect at runtime wether the operating system supports unix
sockets and act accordingly for all supported Windows versions.

This fixes https://github.com/git-for-windows/git/issues/3892

Signed-off-by: Matthias Aßhauer <mha1993@live.de>
---
    Win32: detect unix socket support at runtime
    
    Microsoft recommends checking for unix socket support on the command
    line trough the sc command. [1][2]
    
    > Check whether your Windows build has support for unix socket by
    > running “sc query afunix” from a Windows admin command prompt.
    
    That command queries wether the Service afunix exists and what it's
    current status is. [2]
    
    Using OpenSCManagerA() [3], OpenServiceA() [4], QueryServiceStatusEx()
    [5] and CloseServiceHandle() [6] we can query the same information
    without spawning a new process and parsing the output.
    
    All the used APIs are available Windows XP/Server 2003. [3][4][5][6].
    They're also available on Nano Server images [7] and should thus be
    available in docker containers.
    
    A quick test with time git credential-cache exit shows a negligible
    startup penalty of 2ms.
    
    I've decided against introducing a third behaviour (disabled at compile
    time/dynamic detection/enabled at compile time), because the main
    difference would be a more cryptic error message on unsupported systems
    and the aforementioned ~2ms startup time difference.
    
    This conflicts slightly with the patch series at [8], but rebasing onto
    v2 made little sense with a v3 seemongly in the making.
    
    [1] https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
    [2]
    https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/sc-query
    [3]
    https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-openscmanagera
    [4]
    https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-openservicea
    [5]
    https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryservicestatusex
    [6]
    https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-closeservicehandle
    [7]
    https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/mt588480(v=vs.85)
    [8]
    https://lore.kernel.org/git/pull.1681.git.git.1708506863243.gitgitgadget@gmail.com/

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1708%2Frimrul%2Fwin32-unix-socket-runtime-check-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1708/rimrul/win32-unix-socket-runtime-check-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1708

 builtin/credential-cache--daemon.c |  2 ++
 builtin/credential-cache.c         |  3 +++
 compat/mingw.c                     | 19 +++++++++++++++++++
 compat/mingw.h                     |  6 ++++++
 config.mak.uname                   |  2 --
 git-compat-util.h                  | 12 ++++++++++++
 t/t0301-credential-cache.sh        |  8 ++++++++
 7 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 3a6a750a8eb..17f929dede3 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -294,6 +294,8 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 	socket_path = argv[0];
 
+	if (!have_unix_sockets())
+		die(_("credential-cache--daemon unavailable; no unix socket support"));
 	if (!socket_path)
 		usage_with_options(usage, options);
 
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index bba96d4ffd6..bef120b5375 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -149,6 +149,9 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix)
 		usage_with_options(usage, options);
 	op = argv[0];
 
+	if (!have_unix_sockets())
+		die(_("credential-cache unavailable; no unix socket support"));
+
 	if (!socket_path)
 		socket_path = get_socket_path();
 	if (!socket_path)
diff --git a/compat/mingw.c b/compat/mingw.c
index 320fb99a90e..4876344b5b8 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -3158,3 +3158,22 @@ int uname(struct utsname *buf)
 		  "%u", (v >> 16) & 0x7fff);
 	return 0;
 }
+
+int mingw_have_unix_sockets(void)
+{
+	SC_HANDLE scm, srvc;
+	SERVICE_STATUS_PROCESS status;
+	DWORD bytes;
+	int ret = 0;
+	scm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
+	if (scm) {
+		srvc = OpenServiceA(scm, "afunix", SERVICE_QUERY_STATUS);
+		if (srvc) {
+			if(QueryServiceStatusEx(srvc, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes))
+				ret = status.dwCurrentState == SERVICE_RUNNING;
+			CloseServiceHandle(srvc);
+		}
+		CloseServiceHandle(scm);
+	}
+	return ret;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 6aec50e4124..27b61284f46 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -631,3 +631,9 @@ void open_in_gdb(void);
  * Used by Pthread API implementation for Windows
  */
 int err_win_to_posix(DWORD winerr);
+
+#ifndef NO_UNIX_SOCKETS
+int mingw_have_unix_sockets(void);
+#undef have_unix_sockets
+#define have_unix_sockets mingw_have_unix_sockets
+#endif
diff --git a/config.mak.uname b/config.mak.uname
index d0dcca2ec55..fcf3e2d785a 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -447,7 +447,6 @@ ifeq ($(uname_S),Windows)
 	NO_POLL = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
 	NO_IPV6 = YesPlease
-	NO_UNIX_SOCKETS = YesPlease
 	NO_SETENV = YesPlease
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
@@ -661,7 +660,6 @@ ifeq ($(uname_S),MINGW)
 	NO_LIBGEN_H = YesPlease
 	NO_POLL = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
-	NO_UNIX_SOCKETS = YesPlease
 	NO_SETENV = YesPlease
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
diff --git a/git-compat-util.h b/git-compat-util.h
index 7c2a6538e5a..044f87454a2 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -218,6 +218,18 @@ struct strbuf;
 #define GIT_WINDOWS_NATIVE
 #endif
 
+#if defined(NO_UNIX_SOCKETS) || !defined(GIT_WINDOWS_NATIVE)
+static inline int _have_unix_sockets(void)
+{
+#if defined(NO_UNIX_SOCKETS)
+	return 0;
+#else
+	return 1;
+#endif
+}
+#define have_unix_sockets _have_unix_sockets
+#endif
+
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index 8300faadea9..f2c146fa2a1 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -8,6 +8,14 @@ test -z "$NO_UNIX_SOCKETS" || {
 	skip_all='skipping credential-cache tests, unix sockets not available'
 	test_done
 }
+if test_have_prereq MINGW
+then
+	service_running=$(sc query afunix | grep "4  RUNNING")
+	test -z "$service_running" || {
+		skip_all='skipping credential-cache tests, unix sockets not available'
+		test_done
+	}
+fi
 
 uname_s=$(uname -s)
 case $uname_s in

base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* Re: Worktree shares a common remote with main checkout
  2024-04-03 14:26  2%   ` Bill Wallace
@ 2024-04-03 15:30  0%     ` Phillip Wood
  2024-04-03 16:18  0%       ` Bill Wallace
  0 siblings, 1 reply; 200+ results
From: Phillip Wood @ 2024-04-03 15:30 UTC (permalink / raw)
  To: Bill Wallace, Kristoffer Haugsbakk; +Cc: git, Rubén Justo

Hi Bill

On 03/04/2024 15:26, Bill Wallace wrote:
> The problem is that one wants different default remotes for different
> worktrees - for example, suppose I'm creating a worktree for two
> different projects, plus the base repository is on the "origin"
> default remote.  I then have:
> base_repository - a directory with the base/default origin (call it
> 'origin' for now as the remote name)
> project1 - currently checked out with 'feat/1'
> project2 - current checkout out with 'feat/2'
> 
> Now, project1 is being developed against a remote repository
> 'project1-origin' and project2 is being developed against a remote
> repository 'project2-origin'
> However, both are getting merges from origin/main on their own
> projectX-origin/main branches
> 
> Now, when I'm the directory for project1 and I do any of:
>     git fetch
>     git checkout X
>     git push
> 
> I want the correct default to be chosen for the remote - for the
> base_repository that should be 'origin', while for project1 it should
> be 'project1-origin' etc.

You can set a different default remote for "git pull" for each branch by 
setting an upstream branch with "git branch --set-upstream-to" which 
sets "branch.<name>.remote" and "branch.<name>.merge" or 
"branch.<name>.rebase". You can also set a different remote to push to 
by setting "branch.<name>.pushRemote" - see the "git config" 
documentation. Would that help?

Best Wishes

Phillip

> I KNOW I can specify those manually, and git push will give a
> suggestion, but I WANT all of them to default to the correct remote
> associated with that worktree so that I don't accidentally pick the
> wrong one or forget to update the correct repository.  This is to fix
> dumb fingers that sometimes do the wrong thing without thinking, and
> to try to reduce the number of things that don't get done
> accidentally.
> 
> What I'm doing now is to create a new non-worktree version against the
> projects directories, but that then doesn't share any data.
> 
> git remote add ... has nothing to do with this, but I want something like:
> 
> git worktree add project1 --default-remote project1-origin
> 
> The idea is to make the expectations of what happens to be consistent
> with cloning a new directory, or at least as close as possible to
> that.
> 
> Bill.
> 
> On Fri, 22 Mar 2024 at 13:29, Kristoffer Haugsbakk <code@khaugsbakk.name> wrote:
>>
>> On Fri, Mar 22, 2024, at 15:50, Bill Wallace wrote:
>>> This issue is just to fix an easy to make mistake when working with
>>> multiple remote origins and worktrees, where it is too easy to push to
>>> the wrong remote origin because one can't set the default origin on a
>>> per-worktree basis.
>>>
>>> What did you do before the bug happened? (Steps to reproduce your issue)
>>> Used
>>> * git worktree to create a worktree
>>> * git remote add to add a custom repository
>>> * git commit/push to try to push changes
>>>
>>> What did you expect to happen? (Expected behavior)
>>> Expected to have the git push recommend a remote origin that matched
>>> the worktree, but it defaults to 'origin' all
>>> the time, which means I need to checkout a clean clone from the
>>> specific origin I'm making changes for so that I don't accidentally
>>> push to the default origin.
>>>
>>> What happened instead? (Actual behavior)
>>> Suggests 'origin' as the default origin - which is CORRECT for the
>>> main git branch, but I want to use worktrees to allow working against
>>> several remote origins, with the default being determined by which
>>> worktree I'm in.
>>>
>>> What's different between what you expected and what actually happened?
>>> Suggested 'origin' for the --set-default rather than allowing me to
>>> define the origin I want, for example 'wayfarer' as teh name of my own
>>> remote that I have cloned on github.  The default origin is still
>>> supposed to be 'origin' for pulls/naming, but when I push, it needs to
>>> recommend the matching origin.
>>>
>>> Anything else you want to add:
>>> This is a bit of feature request, but the reason I'm listing it as a
>>> bug is it makes it very easy to make a mistake by pushing to the wrong
>>> origin for a new branch.
>>
>> I don’t understand the expectation. git-worktree(1) just gives you a new
>> worktree to work on a branch, do a bisect, maybe a rebase and so on. I
>> expect `git remote add <remote>` to have nothing to do with the current
>> worktree that I am in. A remote ref is for the repository, not
>> per-worktree.
>>
>> If you are creating a local branch based on this so-called
>> worktree-specific remote and this branch exists on this remote (and
>> *only* on that one) then you can use `git worktree --add --guess-remote`
>> to automatically track the remote branch.
> 


^ permalink raw reply	[relevance 0%]

* Re: Worktree shares a common remote with main checkout
  @ 2024-04-03 14:26  2%   ` Bill Wallace
  2024-04-03 15:30  0%     ` Phillip Wood
  0 siblings, 1 reply; 200+ results
From: Bill Wallace @ 2024-04-03 14:26 UTC (permalink / raw)
  To: Kristoffer Haugsbakk; +Cc: git, Rubén Justo

The problem is that one wants different default remotes for different
worktrees - for example, suppose I'm creating a worktree for two
different projects, plus the base repository is on the "origin"
default remote.  I then have:
base_repository - a directory with the base/default origin (call it
'origin' for now as the remote name)
project1 - currently checked out with 'feat/1'
project2 - current checkout out with 'feat/2'

Now, project1 is being developed against a remote repository
'project1-origin' and project2 is being developed against a remote
repository 'project2-origin'
However, both are getting merges from origin/main on their own
projectX-origin/main branches

Now, when I'm the directory for project1 and I do any of:
   git fetch
   git checkout X
   git push

I want the correct default to be chosen for the remote - for the
base_repository that should be 'origin', while for project1 it should
be 'project1-origin' etc.
I KNOW I can specify those manually, and git push will give a
suggestion, but I WANT all of them to default to the correct remote
associated with that worktree so that I don't accidentally pick the
wrong one or forget to update the correct repository.  This is to fix
dumb fingers that sometimes do the wrong thing without thinking, and
to try to reduce the number of things that don't get done
accidentally.

What I'm doing now is to create a new non-worktree version against the
projects directories, but that then doesn't share any data.

git remote add ... has nothing to do with this, but I want something like:

git worktree add project1 --default-remote project1-origin

The idea is to make the expectations of what happens to be consistent
with cloning a new directory, or at least as close as possible to
that.

Bill.

On Fri, 22 Mar 2024 at 13:29, Kristoffer Haugsbakk <code@khaugsbakk.name> wrote:
>
> On Fri, Mar 22, 2024, at 15:50, Bill Wallace wrote:
> > This issue is just to fix an easy to make mistake when working with
> > multiple remote origins and worktrees, where it is too easy to push to
> > the wrong remote origin because one can't set the default origin on a
> > per-worktree basis.
> >
> > What did you do before the bug happened? (Steps to reproduce your issue)
> > Used
> > * git worktree to create a worktree
> > * git remote add to add a custom repository
> > * git commit/push to try to push changes
> >
> > What did you expect to happen? (Expected behavior)
> > Expected to have the git push recommend a remote origin that matched
> > the worktree, but it defaults to 'origin' all
> > the time, which means I need to checkout a clean clone from the
> > specific origin I'm making changes for so that I don't accidentally
> > push to the default origin.
> >
> > What happened instead? (Actual behavior)
> > Suggests 'origin' as the default origin - which is CORRECT for the
> > main git branch, but I want to use worktrees to allow working against
> > several remote origins, with the default being determined by which
> > worktree I'm in.
> >
> > What's different between what you expected and what actually happened?
> > Suggested 'origin' for the --set-default rather than allowing me to
> > define the origin I want, for example 'wayfarer' as teh name of my own
> > remote that I have cloned on github.  The default origin is still
> > supposed to be 'origin' for pulls/naming, but when I push, it needs to
> > recommend the matching origin.
> >
> > Anything else you want to add:
> > This is a bit of feature request, but the reason I'm listing it as a
> > bug is it makes it very easy to make a mistake by pushing to the wrong
> > origin for a new branch.
>
> I don’t understand the expectation. git-worktree(1) just gives you a new
> worktree to work on a branch, do a bisect, maybe a rebase and so on. I
> expect `git remote add <remote>` to have nothing to do with the current
> worktree that I am in. A remote ref is for the repository, not
> per-worktree.
>
> If you are creating a local branch based on this so-called
> worktree-specific remote and this branch exists on this remote (and
> *only* on that one) then you can use `git worktree --add --guess-remote`
> to automatically track the remote branch.


^ permalink raw reply	[relevance 2%]

* Re: [PATCH] rebase -i: improve error message when picking merge
  @ 2024-04-03 13:42  0% ` phillip.wood123
  2024-04-04  6:08  0% ` Patrick Steinhardt
  2024-04-08 14:16  3% ` [PATCH v2 0/2] " Phillip Wood via GitGitGadget
  2 siblings, 0 replies; 200+ results
From: phillip.wood123 @ 2024-04-03 13:42 UTC (permalink / raw)
  To: Phillip Wood via GitGitGadget, git
  Cc: Stefan Haller, Johannes Schindelin, Phillip Wood, Philippe Blain,
	Brian Lyles

If anyone has time to review this I'd be very grateful. I've added a 
couple of people to the cc list who have recently contributed to the 
sequencer but if anyone else fancies taking a look please do.

Thanks

Phillip

On 26/02/2024 10:58, Phillip Wood via GitGitGadget wrote:
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> The only todo commands that accept a merge commit are "merge" and
> "reset". All the other commands like "pick" or "reword" fail when they
> try to pick a a merge commit and print the message
> 
>      error: commit abc123 is a merge but no -m option was given.
> 
> followed by a hint about the command being rescheduled. This message is
> designed to help the user when they cherry-pick a merge and forget to
> pass "-m". For users who are rebasing the message is confusing as there
> is no way for rebase to cherry-pick the merge.
> 
> Improve the user experience by detecting the error when the todo list is
> parsed rather than waiting for the "pick" command to fail and print a
> message recommending the "merge" command instead. We recommend "merge"
> rather than "exec git cherry-pick -m ..." on the assumption that
> cherry-picking merges is relatively rare and it is more likely that the
> user chose "pick" by a mistake.
> 
> It would be possible to support cherry-picking merges by allowing the
> user to pass "-m" to "pick" commands but that adds complexity to do
> something that can already be achieved with
> 
>      exec git cherry-pick -m1 abc123
> 
> The change is relatively straight forward but is complicated slightly as
> we now need to tell the parser if we're rebasing or not.
> 
> Reported-by: Stefan Haller <lists@haller-berlin.de>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>      rebase -i: improve error message when picking merge
> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1672%2Fphillipwood%2Frebase-reject-merges-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1672/phillipwood/rebase-reject-merges-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1672
> 
>   builtin/rebase.c              |  2 +-
>   rebase-interactive.c          |  7 ++---
>   sequencer.c                   | 49 ++++++++++++++++++++++++++++++-----
>   sequencer.h                   |  2 +-
>   t/t3404-rebase-interactive.sh | 33 +++++++++++++++++++++++
>   5 files changed, 81 insertions(+), 12 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 5b086f651a6..a33e41c44da 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -297,7 +297,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
>   	else {
>   		discard_index(&the_index);
>   		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
> -						&todo_list))
> +						&todo_list, 1))
>   			BUG("unusable todo list");
>   
>   		ret = complete_action(the_repository, &replay, flags,
> diff --git a/rebase-interactive.c b/rebase-interactive.c
> index d9718409b3d..78d5ed1a41d 100644
> --- a/rebase-interactive.c
> +++ b/rebase-interactive.c
> @@ -114,7 +114,8 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
>   	 * it.  If there is an error, we do not return, because the user
>   	 * might want to fix it in the first place. */
>   	if (!initial)
> -		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list) |
> +		incorrect = todo_list_parse_insn_buffer(r, todo_list->buf.buf,
> +							todo_list, 1) |
>   			file_exists(rebase_path_dropped());
>   
>   	if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
> @@ -134,7 +135,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
>   	if (initial && new_todo->buf.len == 0)
>   		return -3;
>   
> -	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo)) {
> +	if (todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo, 1)) {
>   		fprintf(stderr, _(edit_todo_list_advice));
>   		return -4;
>   	}
> @@ -234,7 +235,7 @@ int todo_list_check_against_backup(struct repository *r, struct todo_list *todo_
>   	int res = 0;
>   
>   	if (strbuf_read_file(&backup.buf, rebase_path_todo_backup(), 0) > 0) {
> -		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup);
> +		todo_list_parse_insn_buffer(r, backup.buf.buf, &backup, 1);
>   		res = todo_list_check(&backup, todo_list);
>   	}
>   
> diff --git a/sequencer.c b/sequencer.c
> index 91de546b323..cf808c24d20 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -2550,8 +2550,37 @@ static int check_label_or_ref_arg(enum todo_command command, const char *arg)
>   	return 0;
>   }
>   
> +static int error_merge_commit(enum todo_command command)
> +{
> +	switch(command) {
> +	case TODO_PICK:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s'"),
> +			     todo_command_info[command].str, "merge -C");
> +
> +	case TODO_REWORD:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s'"),
> +			     todo_command_info[command].str, "merge -c");
> +
> +	case TODO_EDIT:
> +		return error(_("'%s' does not accept merge commits, "
> +			       "please use '%s' followed by '%s'"),
> +			     todo_command_info[command].str,
> +			     "merge -C", "break");
> +
> +	case TODO_FIXUP:
> +	case TODO_SQUASH:
> +		return error(_("cannot squash merge commit into another commit"));
> +
> +	default:
> +		BUG("unexpected todo_command");
> +	}
> +}
> +
>   static int parse_insn_line(struct repository *r, struct todo_item *item,
> -			   const char *buf, const char *bol, char *eol)
> +			   const char *buf, const char *bol, char *eol,
> +			   int rebasing)
>   {
>   	struct object_id commit_oid;
>   	char *end_of_object_name;
> @@ -2655,7 +2684,12 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
>   		return status;
>   
>   	item->commit = lookup_commit_reference(r, &commit_oid);
> -	return item->commit ? 0 : -1;
> +	if (!item->commit)
> +		return -1;
> +	if (rebasing && item->command != TODO_MERGE &&
> +	    item->commit->parents && item->commit->parents->next)
> +		return error_merge_commit(item->command);
> +	return 0;
>   }
>   
>   int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
> @@ -2686,7 +2720,7 @@ int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *
>   }
>   
>   int todo_list_parse_insn_buffer(struct repository *r, char *buf,
> -				struct todo_list *todo_list)
> +				struct todo_list *todo_list, int rebasing)
>   {
>   	struct todo_item *item;
>   	char *p = buf, *next_p;
> @@ -2704,7 +2738,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
>   
>   		item = append_new_todo(todo_list);
>   		item->offset_in_buf = p - todo_list->buf.buf;
> -		if (parse_insn_line(r, item, buf, p, eol)) {
> +		if (parse_insn_line(r, item, buf, p, eol, rebasing)) {
>   			res = error(_("invalid line %d: %.*s"),
>   				i, (int)(eol - p), p);
>   			item->command = TODO_COMMENT + 1;
> @@ -2852,7 +2886,8 @@ static int read_populate_todo(struct repository *r,
>   	if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
>   		return -1;
>   
> -	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
> +	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list,
> +					  is_rebase_i(opts));
>   	if (res) {
>   		if (is_rebase_i(opts))
>   			return error(_("please fix this using "
> @@ -2882,7 +2917,7 @@ static int read_populate_todo(struct repository *r,
>   		struct todo_list done = TODO_LIST_INIT;
>   
>   		if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
> -		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
> +		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done, 1))
>   			todo_list->done_nr = count_commands(&done);
>   		else
>   			todo_list->done_nr = 0;
> @@ -6286,7 +6321,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
>   	strbuf_release(&buf2);
>   	/* Nothing is done yet, and we're reparsing, so let's reset the count */
>   	new_todo.total_nr = 0;
> -	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
> +	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo, 1) < 0)
>   		BUG("invalid todo list after expanding IDs:\n%s",
>   		    new_todo.buf.buf);
>   
> diff --git a/sequencer.h b/sequencer.h
> index dcef7bb99c0..ed2c4b38514 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -136,7 +136,7 @@ struct todo_list {
>   }
>   
>   int todo_list_parse_insn_buffer(struct repository *r, char *buf,
> -				struct todo_list *todo_list);
> +				struct todo_list *todo_list, int rebasing);
>   int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
>   			    const char *file, const char *shortrevisions,
>   			    const char *shortonto, int num, unsigned flags);
> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
> index 64b641002e4..20b8589ad07 100755
> --- a/t/t3404-rebase-interactive.sh
> +++ b/t/t3404-rebase-interactive.sh
> @@ -2203,6 +2203,39 @@ test_expect_success 'bad labels and refs rejected when parsing todo list' '
>   	test_path_is_missing execed
>   '
>   
> +test_expect_success 'non-merge commands reject merge commits' '
> +	test_when_finished "test_might_fail git rebase --abort" &&
> +	git checkout E &&
> +	git merge I &&
> +	oid=$(git rev-parse HEAD) &&
> +	cat >todo <<-EOF &&
> +	pick $oid
> +	reword $oid
> +	edit $oid
> +	fixup $oid
> +	squash $oid
> +	EOF
> +	(
> +		set_replace_editor todo &&
> +		test_must_fail git rebase -i HEAD 2>actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	error: ${SQ}pick${SQ} does not accept merge commits, please use ${SQ}merge -C${SQ}
> +	error: invalid line 1: pick $oid
> +	error: ${SQ}reword${SQ} does not accept merge commits, please use ${SQ}merge -c${SQ}
> +	error: invalid line 2: reword $oid
> +	error: ${SQ}edit${SQ} does not accept merge commits, please use ${SQ}merge -C${SQ} followed by ${SQ}break${SQ}
> +	error: invalid line 3: edit $oid
> +	error: cannot squash merge commit into another commit
> +	error: invalid line 4: fixup $oid
> +	error: cannot squash merge commit into another commit
> +	error: invalid line 5: squash $oid
> +	You can fix this with ${SQ}git rebase --edit-todo${SQ} and then run ${SQ}git rebase --continue${SQ}.
> +	Or you can abort the rebase with ${SQ}git rebase --abort${SQ}.
> +	EOF
> +	test_cmp expect actual
> +'
> +
>   # This must be the last test in this file
>   test_expect_success '$EDITOR and friends are unchanged' '
>   	test_editor_unchanged
> 
> base-commit: c875e0b8e036c12cfbf6531962108a063c7a821c


^ permalink raw reply	[relevance 0%]

* git-merge: --no-commit is not respected on a fresh repository
@ 2024-04-03 13:26  4% Jasmin Oster
  2024-04-03 17:29  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Jasmin Oster @ 2024-04-03 13:26 UTC (permalink / raw)
  To: git@vger.kernel.org

## What did you do before the bug happened? (Steps to reproduce your issue)

When trying to perform a subtree merge, especially following the blog 
post on https://nuclearsquid.com/writings/subtree-merging-and-you/,
I noticed that Git does not respect the --no-commit option if the 
repository does not have a history.

1. Create a new repository: $ git init
2. Add a remote: $ git remote add foo ../<path-to-another-repo>
3. Fetch everything: $ git fetch --all
4. Initiate a subtree merge: $ git merge -s our --no-commit foo/main

## What did you expect to happen? (Expected behavior)

The repository should've been in the "merging" state, in order to 
rewrite the paths using:

$ git read-tree --prefix=/project/ -u foo/main

It's only a minor annoyance that can be fixed by adding an empty commit 
in advance of performing the merge, yet it took me some minutes to 
figure out why Git does not behave as intended.

## What happened instead? (Actual behavior)

A merge commit was created.

## What's different between what you expected and what actually happened?

Said merge commit, even though the `--no-commit` option was set.

[System Info]
git version:
git version 2.44.0
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
uname: Linux 6.7.10-200.fc39.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Mar 18 
18:56:52 UTC 2024 x86_64
compiler info: gnuc: 13.2
libc info: glibc: 2.38
$SHELL (typically, interactive shell): /usr/bin/fish

-- 
Mit freundlichen Grüßen / kind regards

*Jasmin Oster*
Software Developer

ANEXIA Internetdienstleistungs GmbH

E-Mail: JOster@anexia.com <mailto:DName@anexia.com>
Web: anexia.com <https://anexia.com/>

Anschrift Hauptsitz Klagenfurt: Feldkirchner Straße 140, 9020 Klagenfurt
Geschäftsführer: Alexander Windbichler
Firmenbuch: FN 289918a | Gerichtsstand: Klagenfurt | UID-Nummer: AT 
U63216601

^ permalink raw reply	[relevance 4%]

* [RFC] git-contacts: exclude list (was: Re: [PATCH] docs: recommend using contrib/contacts/git-contacts)
       [not found]     ` <35192e61-c442-6719-caf0-1019bf3e44c9@live.de>
@ 2024-04-03 10:11  0%   ` Matthias Aßhauer
  0 siblings, 0 replies; 200+ results
From: Matthias Aßhauer @ 2024-04-03 10:11 UTC (permalink / raw)
  To: Matthias Aßhauer
  Cc: Linus Arver via GitGitGadget, git, Junio C Hamano,
	Johannes Schindelin, Jonathan Tan, Emily Shaffer, Matthieu MOY,
	Linus Arver



On Wed, 3 Apr 2024, Matthias Aßhauer wrote:

>

After sending my previous message I've noticed that all of the 
etu.univ-lyon1.fr recipients bounced with the 
message

> 550 5.5.0 Requested actions not taken as the mailbox is unavailable

After running https://etu.univ-lyon1.fr/ through a machine translation 
service it seems like that subdomain is used for mailboxes of current 
students, whereas staff like Matthieu get a mailbox on the main domain.
With Corentin, Nathan and Pablo presumably being former students, it's 
probably unlikely that these mailboxes will become active again.

Would it make sense to have a way to teach `git-contacts` to exclude a 
user defined list of known-bad recipient adresses? This could potentiallly 
be an extension of mailmap or a separate file.

>
> On Tue, 2 Apr 2024, Linus Arver via GitGitGadget wrote:
>
>> From: Linus Arver <linusa@google.com>
>> 
>> Although we've had this script since 4d06402b1b (contrib: add
>> git-contacts helper, 2013-07-21), we don't mention it in our
>> introductory docs. Do so now.
>> 
>> Signed-off-by: Linus Arver <linusa@google.com>
>> ---
>>    docs: recommend using contrib/contacts/git-contacts
>> 
>> Published-As: 
>> https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git 
>> pr-1704/listx/reviewers-v1
>> Pull-Request: https://github.com/gitgitgadget/git/pull/1704
>> 
>> Documentation/MyFirstContribution.txt | 3 +++
>> Documentation/SubmittingPatches       | 4 ++++
>> 2 files changed, 7 insertions(+)
>> 
>> diff --git a/Documentation/MyFirstContribution.txt 
>> b/Documentation/MyFirstContribution.txt
>> index f06563e9817..eb1e27a82df 100644
>> --- a/Documentation/MyFirstContribution.txt
>> +++ b/Documentation/MyFirstContribution.txt
>> @@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
>> NOTE: Check `git help send-email` for some other options which you may find
>> valuable, such as changing the Reply-to address or adding more CC and BCC 
>> lines.
>> 
>> +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you 
>> should
>> +include in the CC list.
>> +
>> NOTE: When you are sending a real patch, it will go to git@vger.kernel.org 
>> - but
>> please don't send your patchset from the tutorial to the real mailing list! 
>> For
>> now, you can send it to yourself, to make sure you understand how it will 
>> look.
>> diff --git a/Documentation/SubmittingPatches 
>> b/Documentation/SubmittingPatches
>> index e734a3f0f17..52d11ff510b 100644
>> --- a/Documentation/SubmittingPatches
>> +++ b/Documentation/SubmittingPatches
>> @@ -459,6 +459,10 @@ an explanation of changes between each iteration can 
>> be kept in
>> Git-notes and inserted automatically following the three-dash
>> line via `git format-patch --notes`.
>> 
>> +[[suggested-reviewers]]
>> +Use `contrib/contacts/git-contacts` to get a list of reviewers you should
>> +include in the CC list.
>> +
>
> There is already a paragraph about this in Documentation/SubmittingPatches 
> just a few paragraphs below.
>
>> Send your patch with "To:" set to the mailing list, with "cc:" listing
>> people who are involved in the area you are touching (the `git
>> contacts` command in `contrib/contacts/` can help to
>> identify them), to solicit comments and reviews.  Also, when you made
>> trial merges of your topic to `next` and `seen`, you may have noticed
>> work by others conflicting with your changes.  There is a good possibility
>> that these people may know the area you are touching well.
>
> Could we improve the existing paragraph instead of duplicating this 
> information?
>
>> [[attachment]]
>> Do not attach the patch as a MIME attachment, compressed or not.
>> Do not let your e-mail client send quoted-printable.  Do not let
>> 
>> base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
>> -- 
>> gitgitgadget
>> 
>


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] docs: recommend using contrib/contacts/git-contacts
  2024-04-02  0:20  3% [PATCH] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
  2024-04-02  6:28  0% ` Patrick Steinhardt
@ 2024-04-03  8:42  0% ` Matthias Aßhauer
  2024-04-04 20:01  0%   ` Linus Arver
       [not found]     ` <35192e61-c442-6719-caf0-1019bf3e44c9@live.de>
  2024-04-06  1:22  3% ` [PATCH v2 0/8] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
  3 siblings, 1 reply; 200+ results
From: Matthias Aßhauer @ 2024-04-03  8:42 UTC (permalink / raw)
  To: Linus Arver via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Jonathan Tan,
	Emily Shaffer, Pablo CHABANNE, Nathan BERBEZIER, Corentin BOMPARD,
	Matthieu MOY, Linus Arver, Linus Arver



On Tue, 2 Apr 2024, Linus Arver via GitGitGadget wrote:

> From: Linus Arver <linusa@google.com>
>
> Although we've had this script since 4d06402b1b (contrib: add
> git-contacts helper, 2013-07-21), we don't mention it in our
> introductory docs. Do so now.
>
> Signed-off-by: Linus Arver <linusa@google.com>
> ---
>    docs: recommend using contrib/contacts/git-contacts
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1704
>
> Documentation/MyFirstContribution.txt | 3 +++
> Documentation/SubmittingPatches       | 4 ++++
> 2 files changed, 7 insertions(+)
>
> diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
> index f06563e9817..eb1e27a82df 100644
> --- a/Documentation/MyFirstContribution.txt
> +++ b/Documentation/MyFirstContribution.txt
> @@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
> NOTE: Check `git help send-email` for some other options which you may find
> valuable, such as changing the Reply-to address or adding more CC and BCC lines.
>
> +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
> +include in the CC list.
> +
> NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
> please don't send your patchset from the tutorial to the real mailing list! For
> now, you can send it to yourself, to make sure you understand how it will look.
> diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
> index e734a3f0f17..52d11ff510b 100644
> --- a/Documentation/SubmittingPatches
> +++ b/Documentation/SubmittingPatches
> @@ -459,6 +459,10 @@ an explanation of changes between each iteration can be kept in
> Git-notes and inserted automatically following the three-dash
> line via `git format-patch --notes`.
>
> +[[suggested-reviewers]]
> +Use `contrib/contacts/git-contacts` to get a list of reviewers you should
> +include in the CC list.
> +

There is already a paragraph about this in Documentation/SubmittingPatches 
just a few paragraphs below.

> Send your patch with "To:" set to the mailing list, with "cc:" listing
> people who are involved in the area you are touching (the `git
> contacts` command in `contrib/contacts/` can help to
> identify them), to solicit comments and reviews.  Also, when you made
> trial merges of your topic to `next` and `seen`, you may have noticed
> work by others conflicting with your changes.  There is a good possibility
> that these people may know the area you are touching well.

Could we improve the existing paragraph instead of duplicating this 
information?

> [[attachment]]
> Do not attach the patch as a MIME attachment, compressed or not.
> Do not let your e-mail client send quoted-printable.  Do not let
>
> base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
> -- 
> gitgitgadget
>


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v4 0/2] reftable/stack: use geometric table compaction
  2024-04-03  0:20  2%     ` [PATCH v4 0/2] " Justin Tobler via GitGitGadget
@ 2024-04-03  4:47  0%       ` Patrick Steinhardt
  2024-04-04 18:29  3%       ` [PATCH v5 0/3] " Justin Tobler via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-04-03  4:47 UTC (permalink / raw)
  To: Justin Tobler via GitGitGadget; +Cc: git, Karthik Nayak, Justin Tobler

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

On Wed, Apr 03, 2024 at 12:20:34AM +0000, Justin Tobler via GitGitGadget wrote:
> Hello again,
> 
> This is the fourth version my patch series that refactors the reftable
> compaction strategy to instead follow a geometric sequence. Changes compared
> to v3:
> 
>  * Changed env name from GIT_TEST_REFTABLE_NO_AUTOCOMPACTION to
>    GIT_TEST_REFTABLE_AUTOCOMPACTION and set the default to false. This

You probably mean true here, not false :)

>    should hopefully be a bit more intuitive since it avoids the double
>    negative.
>  * Updated the corresponding env var test in t0610-reftable-basics.sh to
>    assert on the number of tables added and be overall less fragile.
>  * Folded lines that were too long.
>  * Updated some comments in stack.c to more accurately explain that table
>    segment end is exclusive.
>  * Dropped reftable/stack: make segment end inclusive commit to keep segment
>    end exclusive and better follow expectations.
> 
> Thanks for taking a look!

This version looks good to me, thanks!

Patrick

> -Justin
> 
> Justin Tobler (2):
>   reftable/stack: add env to disable autocompaction
>   reftable/stack: use geometric table compaction
> 
>  reftable/stack.c           | 126 +++++++++++++++++++------------------
>  reftable/stack.h           |   3 -
>  reftable/stack_test.c      |  66 ++++---------------
>  reftable/system.h          |   1 +
>  t/t0610-reftable-basics.sh |  65 +++++++++++++++----
>  5 files changed, 132 insertions(+), 129 deletions(-)
> 
> 
> base-commit: c75fd8d8150afdf836b63a8e0534d9b9e3e111ba
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v4
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v4
> Pull-Request: https://github.com/gitgitgadget/git/pull/1683
> 
> Range-diff vs v3:
> 
>  1:  2fdd8ea1133 ! 1:  2a0421e5f20 reftable/stack: add env to disable autocompaction
>      @@ Commit message
>       
>           In future tests it will be neccesary to create repositories with a set
>           number of tables. To make this easier, introduce the
>      -    `GIT_TEST_REFTABLE_NO_AUTOCOMPACTION` environment variable that, when
>      -    set, disables autocompaction of reftables.
>      +    `GIT_TEST_REFTABLE_AUTOCOMPACTION` environment variable that, when set
>      +    to false, disables autocompaction of reftables.
>       
>           Signed-off-by: Justin Tobler <jltobler@gmail.com>
>       
>      @@ reftable/stack.c: int reftable_addition_commit(struct reftable_addition *add)
>        		goto done;
>        
>       -	if (!add->stack->disable_auto_compact)
>      -+	if (!add->stack->disable_auto_compact && !git_env_bool("GIT_TEST_REFTABLE_NO_AUTOCOMPACTION", 0))
>      ++	if (!add->stack->disable_auto_compact &&
>      ++	    git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
>        		err = reftable_stack_auto_compact(add->stack);
>        
>        done:
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
>        	test_line_count = 1 repo/.git/reftable/tables.list
>        '
>        
>      -+test_expect_success 'ref transaction: environment variable disables auto-compaction' '
>      ++test_expect_success 'ref transaction: env var disables compaction' '
>       +	test_when_finished "rm -rf repo" &&
>       +
>       +	git init repo &&
>       +	test_commit -C repo A &&
>      -+	for i in $(test_seq 20)
>      ++
>      ++	start=$(wc -l <repo/.git/reftable/tables.list) &&
>      ++	iterations=5 &&
>      ++	expected=$((start + iterations)) &&
>      ++
>      ++	for i in $(test_seq $iterations)
>       +	do
>      -+		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
>      ++		GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      ++		git -C repo update-ref branch-$i HEAD || return 1
>       +	done &&
>      -+	test_line_count = 23 repo/.git/reftable/tables.list &&
>      ++	test_line_count = $expected repo/.git/reftable/tables.list &&
>       +
>       +	git -C repo update-ref foo HEAD &&
>      -+	test_line_count = 1 repo/.git/reftable/tables.list
>      ++	test_line_count -lt $expected repo/.git/reftable/tables.list
>       +'
>       +
>        check_fsync_events () {
>  2:  7e62c2286ae ! 2:  e0f4d0dbcc1 reftable/stack: use geometric table compaction
>      @@ reftable/stack.c: static int segment_size(struct segment *s)
>       -		if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev]))
>       +	/*
>       +	 * Find the ending table of the compaction segment needed to restore the
>      -+	 * geometric sequence.
>      ++	 * geometric sequence. Note that the segment end is exclusive.
>       +	 *
>       +	 * To do so, we iterate backwards starting from the most recent table
>       +	 * until a valid segment end is found. If the preceding table is smaller
>       +	 * than the current table multiplied by the geometric factor (2), the
>      -+	 * current table is set as the compaction segment end.
>      ++	 * compaction segment end has been identified.
>       +	 *
>       +	 * Tables after the ending point are not added to the byte count because
>       +	 * they are already valid members of the geometric sequence. Due to the
>      @@ reftable/stack.c: static int segment_size(struct segment *s)
>       +	 * Example table size sequence requiring no compaction:
>       +	 * 	64, 32, 16, 8, 4, 2, 1
>       +	 *
>      -+	 * Example compaction segment end set to table with size 3:
>      ++	 * Example table size sequence where compaction segment end is set to
>      ++	 * the last table. Since the segment end is exclusive, the last table is
>      ++	 * excluded during subsequent compaction and the table with size 3 is
>      ++	 * the final table included:
>       +	 * 	64, 32, 16, 8, 4, 3, 1
>       +	 */
>       +	for (i = n - 1; i > 0; i--) {
>      @@ reftable/stack_test.c: static void test_empty_add(void)
>       +	int l = 0;
>       +	if (sz == 0)
>       +		return 0;
>      -+	for (; sz; sz /= 2) {
>      ++	for (; sz; sz /= 2)
>       +		l++;
>      -+	}
>       +	return l - 1;
>       +}
>       +
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
>        
>        	test_commit -C repo --no-tag B &&
>        	test_line_count = 1 repo/.git/reftable/tables.list
>      -@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: environment variable disables auto-compact
>      - 	do
>      - 		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
>      - 	done &&
>      --	test_line_count = 23 repo/.git/reftable/tables.list &&
>      -+	test_line_count = 22 repo/.git/reftable/tables.list &&
>      - 
>      - 	git -C repo update-ref foo HEAD &&
>      - 	test_line_count = 1 repo/.git/reftable/tables.list
>      +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: env var disables compaction' '
>      + 	test_line_count -lt $expected repo/.git/reftable/tables.list
>        '
>        
>       +test_expect_success 'ref transaction: alternating table sizes are compacted' '
>       +	test_when_finished "rm -rf repo" &&
>      ++
>       +	git init repo &&
>       +	test_commit -C repo A &&
>      -+	for i in $(test_seq 20)
>      ++	for i in $(test_seq 5)
>       +	do
>       +		git -C repo branch -f foo &&
>       +		git -C repo branch -d foo || return 1
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in main rep
>        	test_when_finished "rm -rf repo worktree" &&
>        	git init repo &&
>        	test_commit -C repo A &&
>      --	git -C repo worktree add ../worktree &&
>      -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
>      -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
>      ++
>      ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      + 	git -C repo worktree add ../worktree &&
>      ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      ++	git -C worktree update-ref refs/worktree/per-worktree HEAD &&
>        
>       -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
>       -	test_line_count = 4 repo/.git/reftable/tables.list &&
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in worktree
>        	test_when_finished "rm -rf repo worktree" &&
>        	git init repo &&
>        	test_commit -C repo A &&
>      --	git -C repo worktree add ../worktree &&
>      -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
>      -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
>      ++
>      ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      + 	git -C repo worktree add ../worktree &&
>      ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      ++	git -C worktree update-ref refs/worktree/per-worktree HEAD &&
>        
>       -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
>       -	test_line_count = 4 repo/.git/reftable/tables.list &&
>      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in worktree
>        '
>        
>        test_expect_success 'worktree: creating shared ref updates main stack' '
>      - 	test_when_finished "rm -rf repo worktree" &&
>      - 	git init repo &&
>      - 	test_commit -C repo A &&
>      -+	test_commit -C repo B &&
>      - 
>      - 	git -C repo worktree add ../worktree &&
>      - 	git -C repo pack-refs &&
>       @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: creating shared ref updates main stack' '
>        	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
>        	test_line_count = 1 repo/.git/reftable/tables.list &&
>        
>      --	git -C worktree update-ref refs/heads/shared HEAD &&
>      -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/heads/shared HEAD &&
>      ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
>      + 	git -C worktree update-ref refs/heads/shared HEAD &&
>        	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
>        	test_line_count = 2 repo/.git/reftable/tables.list
>      - '
>  3:  9a33914c852 < -:  ----------- reftable/stack: make segment end inclusive
> 
> -- 
> gitgitgadget

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

^ permalink raw reply	[relevance 0%]

* [PATCH v4 0/2] reftable/stack: use geometric table compaction
  2024-03-29  4:16  3%   ` [PATCH v3 " Justin Tobler via GitGitGadget
@ 2024-04-03  0:20  2%     ` Justin Tobler via GitGitGadget
  2024-04-03  4:47  0%       ` Patrick Steinhardt
  2024-04-04 18:29  3%       ` [PATCH v5 0/3] " Justin Tobler via GitGitGadget
  0 siblings, 2 replies; 200+ results
From: Justin Tobler via GitGitGadget @ 2024-04-03  0:20 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Karthik Nayak, Justin Tobler

Hello again,

This is the fourth version my patch series that refactors the reftable
compaction strategy to instead follow a geometric sequence. Changes compared
to v3:

 * Changed env name from GIT_TEST_REFTABLE_NO_AUTOCOMPACTION to
   GIT_TEST_REFTABLE_AUTOCOMPACTION and set the default to false. This
   should hopefully be a bit more intuitive since it avoids the double
   negative.
 * Updated the corresponding env var test in t0610-reftable-basics.sh to
   assert on the number of tables added and be overall less fragile.
 * Folded lines that were too long.
 * Updated some comments in stack.c to more accurately explain that table
   segment end is exclusive.
 * Dropped reftable/stack: make segment end inclusive commit to keep segment
   end exclusive and better follow expectations.

Thanks for taking a look!

-Justin

Justin Tobler (2):
  reftable/stack: add env to disable autocompaction
  reftable/stack: use geometric table compaction

 reftable/stack.c           | 126 +++++++++++++++++++------------------
 reftable/stack.h           |   3 -
 reftable/stack_test.c      |  66 ++++---------------
 reftable/system.h          |   1 +
 t/t0610-reftable-basics.sh |  65 +++++++++++++++----
 5 files changed, 132 insertions(+), 129 deletions(-)


base-commit: c75fd8d8150afdf836b63a8e0534d9b9e3e111ba
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1683

Range-diff vs v3:

 1:  2fdd8ea1133 ! 1:  2a0421e5f20 reftable/stack: add env to disable autocompaction
     @@ Commit message
      
          In future tests it will be neccesary to create repositories with a set
          number of tables. To make this easier, introduce the
     -    `GIT_TEST_REFTABLE_NO_AUTOCOMPACTION` environment variable that, when
     -    set, disables autocompaction of reftables.
     +    `GIT_TEST_REFTABLE_AUTOCOMPACTION` environment variable that, when set
     +    to false, disables autocompaction of reftables.
      
          Signed-off-by: Justin Tobler <jltobler@gmail.com>
      
     @@ reftable/stack.c: int reftable_addition_commit(struct reftable_addition *add)
       		goto done;
       
      -	if (!add->stack->disable_auto_compact)
     -+	if (!add->stack->disable_auto_compact && !git_env_bool("GIT_TEST_REFTABLE_NO_AUTOCOMPACTION", 0))
     ++	if (!add->stack->disable_auto_compact &&
     ++	    git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1))
       		err = reftable_stack_auto_compact(add->stack);
       
       done:
     @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
       	test_line_count = 1 repo/.git/reftable/tables.list
       '
       
     -+test_expect_success 'ref transaction: environment variable disables auto-compaction' '
     ++test_expect_success 'ref transaction: env var disables compaction' '
      +	test_when_finished "rm -rf repo" &&
      +
      +	git init repo &&
      +	test_commit -C repo A &&
     -+	for i in $(test_seq 20)
     ++
     ++	start=$(wc -l <repo/.git/reftable/tables.list) &&
     ++	iterations=5 &&
     ++	expected=$((start + iterations)) &&
     ++
     ++	for i in $(test_seq $iterations)
      +	do
     -+		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
     ++		GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     ++		git -C repo update-ref branch-$i HEAD || return 1
      +	done &&
     -+	test_line_count = 23 repo/.git/reftable/tables.list &&
     ++	test_line_count = $expected repo/.git/reftable/tables.list &&
      +
      +	git -C repo update-ref foo HEAD &&
     -+	test_line_count = 1 repo/.git/reftable/tables.list
     ++	test_line_count -lt $expected repo/.git/reftable/tables.list
      +'
      +
       check_fsync_events () {
 2:  7e62c2286ae ! 2:  e0f4d0dbcc1 reftable/stack: use geometric table compaction
     @@ reftable/stack.c: static int segment_size(struct segment *s)
      -		if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev]))
      +	/*
      +	 * Find the ending table of the compaction segment needed to restore the
     -+	 * geometric sequence.
     ++	 * geometric sequence. Note that the segment end is exclusive.
      +	 *
      +	 * To do so, we iterate backwards starting from the most recent table
      +	 * until a valid segment end is found. If the preceding table is smaller
      +	 * than the current table multiplied by the geometric factor (2), the
     -+	 * current table is set as the compaction segment end.
     ++	 * compaction segment end has been identified.
      +	 *
      +	 * Tables after the ending point are not added to the byte count because
      +	 * they are already valid members of the geometric sequence. Due to the
     @@ reftable/stack.c: static int segment_size(struct segment *s)
      +	 * Example table size sequence requiring no compaction:
      +	 * 	64, 32, 16, 8, 4, 2, 1
      +	 *
     -+	 * Example compaction segment end set to table with size 3:
     ++	 * Example table size sequence where compaction segment end is set to
     ++	 * the last table. Since the segment end is exclusive, the last table is
     ++	 * excluded during subsequent compaction and the table with size 3 is
     ++	 * the final table included:
      +	 * 	64, 32, 16, 8, 4, 3, 1
      +	 */
      +	for (i = n - 1; i > 0; i--) {
     @@ reftable/stack_test.c: static void test_empty_add(void)
      +	int l = 0;
      +	if (sz == 0)
      +		return 0;
     -+	for (; sz; sz /= 2) {
     ++	for (; sz; sz /= 2)
      +		l++;
     -+	}
      +	return l - 1;
      +}
      +
     @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
       
       	test_commit -C repo --no-tag B &&
       	test_line_count = 1 repo/.git/reftable/tables.list
     -@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: environment variable disables auto-compact
     - 	do
     - 		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
     - 	done &&
     --	test_line_count = 23 repo/.git/reftable/tables.list &&
     -+	test_line_count = 22 repo/.git/reftable/tables.list &&
     - 
     - 	git -C repo update-ref foo HEAD &&
     - 	test_line_count = 1 repo/.git/reftable/tables.list
     +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: env var disables compaction' '
     + 	test_line_count -lt $expected repo/.git/reftable/tables.list
       '
       
      +test_expect_success 'ref transaction: alternating table sizes are compacted' '
      +	test_when_finished "rm -rf repo" &&
     ++
      +	git init repo &&
      +	test_commit -C repo A &&
     -+	for i in $(test_seq 20)
     ++	for i in $(test_seq 5)
      +	do
      +		git -C repo branch -f foo &&
      +		git -C repo branch -d foo || return 1
     @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in main rep
       	test_when_finished "rm -rf repo worktree" &&
       	git init repo &&
       	test_commit -C repo A &&
     --	git -C repo worktree add ../worktree &&
     -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
     -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
     ++
     ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     + 	git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     ++	git -C worktree update-ref refs/worktree/per-worktree HEAD &&
       
      -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
      -	test_line_count = 4 repo/.git/reftable/tables.list &&
     @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in worktree
       	test_when_finished "rm -rf repo worktree" &&
       	git init repo &&
       	test_commit -C repo A &&
     --	git -C repo worktree add ../worktree &&
     -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
     -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
     ++
     ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     + 	git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     ++	git -C worktree update-ref refs/worktree/per-worktree HEAD &&
       
      -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
      -	test_line_count = 4 repo/.git/reftable/tables.list &&
     @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in worktree
       '
       
       test_expect_success 'worktree: creating shared ref updates main stack' '
     - 	test_when_finished "rm -rf repo worktree" &&
     - 	git init repo &&
     - 	test_commit -C repo A &&
     -+	test_commit -C repo B &&
     - 
     - 	git -C repo worktree add ../worktree &&
     - 	git -C repo pack-refs &&
      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: creating shared ref updates main stack' '
       	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
       	test_line_count = 1 repo/.git/reftable/tables.list &&
       
     --	git -C worktree update-ref refs/heads/shared HEAD &&
     -+	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/heads/shared HEAD &&
     ++	GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
     + 	git -C worktree update-ref refs/heads/shared HEAD &&
       	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
       	test_line_count = 2 repo/.git/reftable/tables.list
     - '
 3:  9a33914c852 < -:  ----------- reftable/stack: make segment end inclusive

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* Re: bug report: spurious "cannot delete branch '%s' used by worktree"
  2024-04-02 10:10  0%       ` Phillip Wood
@ 2024-04-02 10:26  0%         ` Tamir Duberstein
  0 siblings, 0 replies; 200+ results
From: Tamir Duberstein @ 2024-04-02 10:26 UTC (permalink / raw)
  To: phillip.wood; +Cc: Eric Sunshine, git

Hi Phillip

On Tue, Apr 2, 2024 at 11:10 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Tamir
>
> On 31/03/2024 07:49, Eric Sunshine wrote:
> > [please reply inline rather than top-posting; I've moved your reply
> > inline for this response]
> >
> > On Thu, Mar 28, 2024 at 1:40 PM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> >> On Thu, Mar 28, 2024 at 5:24 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> >>> On Thu, Mar 28, 2024 at 10:54 AM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> >>>> % git branch -d cleanup
> >>>> error: cannot delete branch 'cleanup' used by worktree at '<my source dir>'
> >>>> % git worktree list
> >>>> <my source dir>  dc46f6d5e [main]
> >>>> % git branch
> >>>>    cleanup
> >>>> * main
> >>>
> >>> Is this error persistent once it arises? That is, if you invoke `git
> >>> branch -d cleanup` again immediately after (or a little while after)
> >>> the above sequence, does the problem persist? Or does it "clear up" on
> >>> its own at some point?
> >>
> >> Yes, the problem is persistent. The branch is never deleted.
> >
> > I'd guess that there may be some sort of "ref" still pointing at the
> > "cleanup" branch which presumably was, at some point, checked out at
> > "<my source dir>". Digging through the code[1,2,3] suggests that you
> > might have some stale state from a rebase, bisect, or other sequencer
> > operation which still references the "cleanup" branch.
> >
> > [Cc'ing Phillip who is probably much more familiar with this code than am I.]
>
> Thanks Eric. I'd have thought that "git worktree list" would say
> something about the branch being rebased if there was enough state lying
> around to prevent the branch being deleted, but lets see. What does
>
>      ls $(git rev-parse --git-path rebase-merge) $(git rev-parse
> --git-path rebase-apply)
>
> show when you run it in <my source dir>? Also is <my source dir> the
> only worktree?

% ls $(git rev-parse --git-path rebase-merge) $(git rev-parse
--git-path rebase-apply)
ls: .git/rebase-apply: No such file or directory
ls: .git/rebase-merge: No such file or directory

Yes, it's the only worktree.

> Best Wishes
>
> Phillip
>
> > By the way, it's not clear from your initial report what you mean when
> > you say "then the remote deleted the branch". Also, did you fetch
> > and/or pull from the remote after that?
> >
> > [1]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L454
> > [2]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L386
> > [3]: https://github.com/git/git/blob/d6fd04375f91/sequencer.c#L6551


^ permalink raw reply	[relevance 0%]

* Re: bug report: spurious "cannot delete branch '%s' used by worktree"
  2024-03-31  6:49  3%     ` Eric Sunshine
  2024-03-31  7:45  3%       ` Tamir Duberstein
@ 2024-04-02 10:10  0%       ` Phillip Wood
  2024-04-02 10:26  0%         ` Tamir Duberstein
  1 sibling, 1 reply; 200+ results
From: Phillip Wood @ 2024-04-02 10:10 UTC (permalink / raw)
  To: Eric Sunshine, Tamir Duberstein; +Cc: git

Hi Tamir

On 31/03/2024 07:49, Eric Sunshine wrote:
> [please reply inline rather than top-posting; I've moved your reply
> inline for this response]
> 
> On Thu, Mar 28, 2024 at 1:40 PM Tamir Duberstein <tamird@fuseenergy.com> wrote:
>> On Thu, Mar 28, 2024 at 5:24 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>>> On Thu, Mar 28, 2024 at 10:54 AM Tamir Duberstein <tamird@fuseenergy.com> wrote:
>>>> % git branch -d cleanup
>>>> error: cannot delete branch 'cleanup' used by worktree at '<my source dir>'
>>>> % git worktree list
>>>> <my source dir>  dc46f6d5e [main]
>>>> % git branch
>>>>    cleanup
>>>> * main
>>>
>>> Is this error persistent once it arises? That is, if you invoke `git
>>> branch -d cleanup` again immediately after (or a little while after)
>>> the above sequence, does the problem persist? Or does it "clear up" on
>>> its own at some point?
>>
>> Yes, the problem is persistent. The branch is never deleted.
> 
> I'd guess that there may be some sort of "ref" still pointing at the
> "cleanup" branch which presumably was, at some point, checked out at
> "<my source dir>". Digging through the code[1,2,3] suggests that you
> might have some stale state from a rebase, bisect, or other sequencer
> operation which still references the "cleanup" branch.
> 
> [Cc'ing Phillip who is probably much more familiar with this code than am I.]

Thanks Eric. I'd have thought that "git worktree list" would say 
something about the branch being rebased if there was enough state lying 
around to prevent the branch being deleted, but lets see. What does

     ls $(git rev-parse --git-path rebase-merge) $(git rev-parse 
--git-path rebase-apply)

show when you run it in <my source dir>? Also is <my source dir> the 
only worktree?

Best Wishes

Phillip

> By the way, it's not clear from your initial report what you mean when
> you say "then the remote deleted the branch". Also, did you fetch
> and/or pull from the remote after that?
> 
> [1]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L454
> [2]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L386
> [3]: https://github.com/git/git/blob/d6fd04375f91/sequencer.c#L6551


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] docs: recommend using contrib/contacts/git-contacts
  2024-04-02  0:20  3% [PATCH] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
@ 2024-04-02  6:28  0% ` Patrick Steinhardt
  2024-04-04 20:00  0%   ` Linus Arver
  2024-04-03  8:42  0% ` Matthias Aßhauer
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 200+ results
From: Patrick Steinhardt @ 2024-04-02  6:28 UTC (permalink / raw)
  To: Linus Arver via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Jonathan Tan,
	Emily Shaffer, Pablo CHABANNE, Nathan BERBEZIER, Corentin BOMPARD,
	Matthieu MOY, Linus Arver

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

On Tue, Apr 02, 2024 at 12:20:05AM +0000, Linus Arver via GitGitGadget wrote:
> From: Linus Arver <linusa@google.com>
> 
> Although we've had this script since 4d06402b1b (contrib: add
> git-contacts helper, 2013-07-21), we don't mention it in our
> introductory docs. Do so now.
> 
> Signed-off-by: Linus Arver <linusa@google.com>
> ---
>     docs: recommend using contrib/contacts/git-contacts
> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1704
> 
>  Documentation/MyFirstContribution.txt | 3 +++
>  Documentation/SubmittingPatches       | 4 ++++
>  2 files changed, 7 insertions(+)
> 
> diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
> index f06563e9817..eb1e27a82df 100644
> --- a/Documentation/MyFirstContribution.txt
> +++ b/Documentation/MyFirstContribution.txt
> @@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
>  NOTE: Check `git help send-email` for some other options which you may find
>  valuable, such as changing the Reply-to address or adding more CC and BCC lines.
>  
> +NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
> +include in the CC list.
> +

Should we mention that the script can be passed to git-send-email(1) via
`--cc-cmd=`?

Thanks!

Patrick

>  NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
>  please don't send your patchset from the tutorial to the real mailing list! For
>  now, you can send it to yourself, to make sure you understand how it will look.
> diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
> index e734a3f0f17..52d11ff510b 100644
> --- a/Documentation/SubmittingPatches
> +++ b/Documentation/SubmittingPatches
> @@ -459,6 +459,10 @@ an explanation of changes between each iteration can be kept in
>  Git-notes and inserted automatically following the three-dash
>  line via `git format-patch --notes`.
>  
> +[[suggested-reviewers]]
> +Use `contrib/contacts/git-contacts` to get a list of reviewers you should
> +include in the CC list.
> +
>  [[attachment]]
>  Do not attach the patch as a MIME attachment, compressed or not.
>  Do not let your e-mail client send quoted-printable.  Do not let
> 
> base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
> -- 
> gitgitgadget
> 

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

^ permalink raw reply	[relevance 0%]

* [PATCH] docs: recommend using contrib/contacts/git-contacts
@ 2024-04-02  0:20  3% Linus Arver via GitGitGadget
  2024-04-02  6:28  0% ` Patrick Steinhardt
                   ` (3 more replies)
  0 siblings, 4 replies; 200+ results
From: Linus Arver via GitGitGadget @ 2024-04-02  0:20 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Jonathan Tan, Emily Shaffer,
	Pablo CHABANNE, Nathan BERBEZIER, Corentin BOMPARD, Matthieu MOY,
	Linus Arver, Linus Arver

From: Linus Arver <linusa@google.com>

Although we've had this script since 4d06402b1b (contrib: add
git-contacts helper, 2013-07-21), we don't mention it in our
introductory docs. Do so now.

Signed-off-by: Linus Arver <linusa@google.com>
---
    docs: recommend using contrib/contacts/git-contacts

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1704%2Flistx%2Freviewers-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1704/listx/reviewers-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1704

 Documentation/MyFirstContribution.txt | 3 +++
 Documentation/SubmittingPatches       | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index f06563e9817..eb1e27a82df 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1116,6 +1116,9 @@ $ git send-email --to=target@example.com psuh/*.patch
 NOTE: Check `git help send-email` for some other options which you may find
 valuable, such as changing the Reply-to address or adding more CC and BCC lines.
 
+NOTE: Use `contrib/contacts/git-contacts` to get a list of reviewers you should
+include in the CC list.
+
 NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
 please don't send your patchset from the tutorial to the real mailing list! For
 now, you can send it to yourself, to make sure you understand how it will look.
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index e734a3f0f17..52d11ff510b 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -459,6 +459,10 @@ an explanation of changes between each iteration can be kept in
 Git-notes and inserted automatically following the three-dash
 line via `git format-patch --notes`.
 
+[[suggested-reviewers]]
+Use `contrib/contacts/git-contacts` to get a list of reviewers you should
+include in the CC list.
+
 [[attachment]]
 Do not attach the patch as a MIME attachment, compressed or not.
 Do not let your e-mail client send quoted-printable.  Do not let

base-commit: c2cbfbd2e28cbe27c194d62183b42f27a6a5bb87
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* Re: [PATCH 08/13] credential: add an argument to keep state
  @ 2024-04-01 22:14  3%     ` brian m. carlson
  0 siblings, 0 replies; 200+ results
From: brian m. carlson @ 2024-04-01 22:14 UTC (permalink / raw)
  To: mirth hickford; +Cc: git, Junio C Hamano, Matthew John Cheetham

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

On 2024-04-01 at 21:05:28, mirth hickford wrote:
> On Sun, Mar 24, 2024 at 1:13 AM brian m. carlson
> <sandals@crustytoothpaste.net> wrote:
> >
> > Until now, our credential code has mostly deal with usernames and
> > passwords and we've let libcurl deal with the variant of authentication
> > to be used.  However, now that we have the credential value, the
> > credential helper can take control of the authentication, so the value
> > provided might be something that's generated, such as a Digest hash
> > value.
> >
> > In such a case, it would be helpful for a credential helper that gets an
> > erase or store command to be able to keep track of an identifier for the
> > original secret that went into the computation.  Furthermore, some types
> > of authentication, such as NTLM and Kerberos, actually need two round
> > trips to authenticate, which will require that the credential helper
> > keep some state.
> >
> > In order to allow for these use cases and others, allow storing state in
> > a field called "state[]".  This value is passed back to the credential
> > helper that created it, which avoids confusion caused by parsing values
> > from different helpers.
> >
> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> > ---
> >  Documentation/git-credential.txt | 29 ++++++++++++++++++-----------
> >  credential.c                     | 20 +++++++++++++++++---
> >  credential.h                     |  7 +++++++
> >  t/t0300-credentials.sh           | 29 +++++++++++++++++++++++++++++
> >  4 files changed, 71 insertions(+), 14 deletions(-)
> >
> > diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
> > index f3ed3a82fa..ef30c89c00 100644
> > --- a/Documentation/git-credential.txt
> > +++ b/Documentation/git-credential.txt
> > @@ -196,6 +196,15 @@ provided on input.
> >  This value should not be sent unless the appropriate capability (see below) is
> >  provided on input.
> >
> > +`state[]`::
> > +       This value provides an opaque state that will be passed back to this helper
> > +       if it is called again.  Each different credential helper may specify this
> > +       once.  The value should include a prefix unique to the credential helper and
> > +       should ignore values that don't match its prefix.
> 
> Does Git ever populate state[] in 'store' or 'erase' requests,  or
> only 'get' requests? It might be worthwhile to spell this out.

Yes, it's populated with whatever the last state value was from `get`.

> This seems somewhat different to other multi-valued attributes,
> particularly the "set at most one value" constraint. As an
> alternative, how about a single-valued attribute stored independently
> for each helper (vector length equal to the number of configured
> helpers)? Then in repeat requests send the "nth state to the nth
> helper". This would avoid the complexity of the prefix mechanism.

I originally tried that approach, but if you have external callers of
`git credential` (like Git LFS), that doesn't work, since you need to
make two separate calls: one (with `get`) to fetch the credentials that
returns multiple state values, and one (with `store` or `erase`) that
sends the data back to accept or reject the credentials.  Since there's
no internal state in Git between the two calls, it's not possible to
only send certain data to certain helpers.
-- 
brian m. carlson (they/them or he/him)
Toronto, Ontario, CA

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

^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2 4/4] midx-write.c: use `--stdin-packs` when repacking
  @ 2024-04-01 21:45  3%     ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-04-01 21:45 UTC (permalink / raw)
  To: Taylor Blau; +Cc: git, Jeff King, René Scharfe

Taylor Blau <me@ttaylorr.com> writes:

> When constructing a new pack `git multi-pack-index repack` provides a
> list of objects which is the union of objects in all MIDX'd packs which
> were "included" in the repack.
>
> Though correct, this typically yields a poorly structured pack, since
> providing the objects list over stdin does not give pack-objects a
> chance to discover the namehash values for each object, leading to
> sub-optimal delta selection.
>
> We can use `--stdin-packs` instead, which has a couple of benefits:
>
>   - it does a supplemental walk over objects in the supplied list of
>     packs to discover their namehash, leading to higher-quality delta
>     selection
>
>   - it requires us to list far less data over stdin; instead of listing
>     each object in the resulting pack, we need only list the
>     constituent packs from which those objects were selected in the MIDX
>
> Of course, this comes at a slight cost: though we save time on listing
> packs versus objects over stdin[^1] (around ~650 milliseconds), we add a
> non-trivial amount of time walking over the given objects in order to
> find better deltas.
>
> In general, this is likely to more closely match the user's expectations
> (i.e. that packs generated via `git multi-pack-index repack` are written
> with high-quality deltas). But if not, we can always introduce a new
> option in pack-objects to disable the supplemental object walk, which
> would yield a pure CPU-time savings, at the cost of the on-disk size of
> the resulting pack.
>
> [^1]: In a patched version of Git that doesn't perform the supplemental
>   object walk in `pack-objects --stdin-packs`, we save around ~650ms
>   (from 5.968 to 5.325 seconds) when running `git multi-pack-index
>   repack --batch-size=0` on git.git with all objects packed, and all
>   packs in a MIDX.

There are some measures in the mind of readers' who have read the
explanation so far.

 - So, this gives us a resulting pack with better delta selection.
   How much better would it get in a sample repository?  10%?  40%?

 - Of course, the better delta selection comes with cost.  How much
   more time do we spend?  20%?  150%?

 - As we do not enumerate all the object names, we save some time.
   Around 0.65 seconds in a sample repository.

I think among the three, the first two are more interesting numbers,
no?

I wonder if we can leverage the trick that reuses existing packdata
when we stream packs to feed the "git fetch" clients---we rely on
the fact that existing packs are tightly packed with good delta
selection, and using bitmap stream contiguous section(s) as much as
possible without disturbing the existing delta chain.  Wouldn't the
"we have many packs, let's repack them into one" workload benefit
the same way?

> -	strvec_push(&cmd.args, "pack-objects");
> +	strvec_pushl(&cmd.args, "pack-objects", "--stdin-packs", "--non-empty",
> +		     NULL);
>  
>  	strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
>  
> @@ -1498,16 +1499,15 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
>  	}
>  
>  	cmd_in = xfdopen(cmd.in, "w");
> -
> -	for (i = 0; i < m->num_objects; i++) {
> -		struct object_id oid;
> -		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
> -
> -		if (!include_pack[pack_int_id])
> +	for (i = 0; i < m->num_packs; i++) {
> +		struct packed_git *p = m->packs[i];
> +		if (!p)
>  			continue;
>  
> -		nth_midxed_object_oid(&oid, m, i);
> -		fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
> +		if (include_pack[i])
> +			fprintf(cmd_in, "%s\n", pack_basename(p));
> +		else
> +			fprintf(cmd_in, "^%s\n", pack_basename(p));
>  	}
>  	fclose(cmd_in);

Looking very straight-forward.  Will queue.

Thanks.


^ permalink raw reply	[relevance 3%]

* Re: [PATCH 0/4] osxkeychain: bring in line with other credential helpers
  @ 2024-04-01 21:40  0% ` M Hickford
  0 siblings, 0 replies; 200+ results
From: M Hickford @ 2024-04-01 21:40 UTC (permalink / raw)
  To: gitgitgadget; +Cc: git, mail

> From: "Bo Anderson via GitGitGadget" <gitgitgadget@gmail.com>
> To: git@vger.kernel.org
> Cc: Bo Anderson <mail@boanderson.me>
> Subject: [PATCH 0/4] osxkeychain: bring in line with other credential helpers
> Date: Sat, 17 Feb 2024 23:34:52 +0000	[thread overview]
> Message-ID: <pull.1667.git.1708212896.gitgitgadget@gmail.com> (raw)
> 
> git-credential-osxkeychain has largely fallen behind other external
> credential helpers in the features it supports, and hasn't received any
> functional changes since 2013. As it stood, osxkeychain failed seven tests
> in the external credential helper test suite:
> 
> not ok 8 - helper (osxkeychain) overwrites on store
> not ok 9 - helper (osxkeychain) can forget host
> not ok 11 - helper (osxkeychain) does not erase a password distinct from input
> not ok 15 - helper (osxkeychain) erases all matching credentials
> not ok 18 - helper (osxkeychain) gets password_expiry_utc
> not ok 19 - helper (osxkeychain) overwrites when password_expiry_utc changes
> not ok 21 - helper (osxkeychain) gets oauth_refresh_token
> 
> 
> osxkeychain also made use of macOS APIs that had been deprecated since 2014.
> Replacement API was able to be used without regressing the minimum supported
> macOS established in 5747c8072b (contrib/credential: avoid fixed-size buffer
> in osxkeychain, 2023-05-01).
> 
> After this set of patches, osxkeychain passes all tests in the external
> credential helper test suite.
> 
> Bo Anderson (4):
>   osxkeychain: replace deprecated SecKeychain API
>   osxkeychain: erase all matching credentials
>   osxkeychain: erase matching passwords only
>   osxkeychain: store new attributes
> 
>  contrib/credential/osxkeychain/Makefile       |   3 +-
>  .../osxkeychain/git-credential-osxkeychain.c  | 376 ++++++++++++++----
>  2 files changed, 310 insertions(+), 69 deletions(-)
> 
> 
> base-commit: 3e0d3cd5c7def4808247caf168e17f2bbf47892b
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1667%2FBo98%2Fosxkeychain-update-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1667/Bo98/osxkeychain-update-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1667
> -- 
> gitgitgadget

Hi. Is this patch ready to cook in seen?


^ permalink raw reply	[relevance 0%]

* [PATCH v2 1/4] midx-write: move writing-related functions from midx.c
  @ 2024-04-01 21:16  1%   ` Taylor Blau
    1 sibling, 0 replies; 200+ results
From: Taylor Blau @ 2024-04-01 21:16 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, René Scharfe

Introduce a new midx-write.c source file, which holds all of the
functionality from the MIDX sub-system related to writing new MIDX files.

Similar to the relationship between "pack-bitmap.c" and
"pack-bitmap-write.c", this source file will hold code that is specific
to writing MIDX files as opposed to reading them (the latter will remain
in midx.c).

Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 Makefile     |    1 +
 midx-write.c | 1521 ++++++++++++++++++++++++++++++++++++++++++++++++
 midx.c       | 1553 +-------------------------------------------------
 midx.h       |   19 +
 4 files changed, 1555 insertions(+), 1539 deletions(-)
 create mode 100644 midx-write.c

diff --git a/Makefile b/Makefile
index 4e255c81f2..cf44a964c0 100644
--- a/Makefile
+++ b/Makefile
@@ -1072,6 +1072,7 @@ LIB_OBJS += merge-ort-wrappers.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += merge.o
 LIB_OBJS += midx.o
+LIB_OBJS += midx-write.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/noop.o
diff --git a/midx-write.c b/midx-write.c
new file mode 100644
index 0000000000..5242d2a724
--- /dev/null
+++ b/midx-write.c
@@ -0,0 +1,1521 @@
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "config.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "packfile.h"
+#include "object-file.h"
+#include "hash-lookup.h"
+#include "midx.h"
+#include "progress.h"
+#include "trace2.h"
+#include "run-command.h"
+#include "chunk-format.h"
+#include "pack-bitmap.h"
+#include "refs.h"
+#include "revision.h"
+#include "list-objects.h"
+
+#define PACK_EXPIRED UINT_MAX
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
+#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+
+extern int midx_checksum_valid(struct multi_pack_index *m);
+extern void clear_midx_files_ext(const char *object_dir, const char *ext,
+				 unsigned char *keep_hash);
+extern int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+				const char *idx_name);
+
+static size_t write_midx_header(struct hashfile *f,
+				unsigned char num_chunks,
+				uint32_t num_packs)
+{
+	hashwrite_be32(f, MIDX_SIGNATURE);
+	hashwrite_u8(f, MIDX_VERSION);
+	hashwrite_u8(f, oid_version(the_hash_algo));
+	hashwrite_u8(f, num_chunks);
+	hashwrite_u8(f, 0); /* unused */
+	hashwrite_be32(f, num_packs);
+
+	return MIDX_HEADER_SIZE;
+}
+
+struct pack_info {
+	uint32_t orig_pack_int_id;
+	char *pack_name;
+	struct packed_git *p;
+
+	uint32_t bitmap_pos;
+	uint32_t bitmap_nr;
+
+	unsigned expired : 1;
+};
+
+static void fill_pack_info(struct pack_info *info,
+			   struct packed_git *p, const char *pack_name,
+			   uint32_t orig_pack_int_id)
+{
+	memset(info, 0, sizeof(struct pack_info));
+
+	info->orig_pack_int_id = orig_pack_int_id;
+	info->pack_name = xstrdup(pack_name);
+	info->p = p;
+	info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
+static int pack_info_compare(const void *_a, const void *_b)
+{
+	struct pack_info *a = (struct pack_info *)_a;
+	struct pack_info *b = (struct pack_info *)_b;
+	return strcmp(a->pack_name, b->pack_name);
+}
+
+static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
+{
+	const char *pack_name = _va;
+	const struct pack_info *compar = _vb;
+
+	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
+}
+
+struct write_midx_context {
+	struct pack_info *info;
+	size_t nr;
+	size_t alloc;
+	struct multi_pack_index *m;
+	struct progress *progress;
+	unsigned pack_paths_checked;
+
+	struct pack_midx_entry *entries;
+	size_t entries_nr;
+
+	uint32_t *pack_perm;
+	uint32_t *pack_order;
+	unsigned large_offsets_needed:1;
+	uint32_t num_large_offsets;
+
+	int preferred_pack_idx;
+
+	struct string_list *to_include;
+};
+
+static void add_pack_to_midx(const char *full_path, size_t full_path_len,
+			     const char *file_name, void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct packed_git *p;
+
+	if (ends_with(file_name, ".idx")) {
+		display_progress(ctx->progress, ++ctx->pack_paths_checked);
+		/*
+		 * Note that at most one of ctx->m and ctx->to_include are set,
+		 * so we are testing midx_contains_pack() and
+		 * string_list_has_string() independently (guarded by the
+		 * appropriate NULL checks).
+		 *
+		 * We could support passing to_include while reusing an existing
+		 * MIDX, but don't currently since the reuse process drags
+		 * forward all packs from an existing MIDX (without checking
+		 * whether or not they appear in the to_include list).
+		 *
+		 * If we added support for that, these next two conditional
+		 * should be performed independently (likely checking
+		 * to_include before the existing MIDX).
+		 */
+		if (ctx->m && midx_contains_pack(ctx->m, file_name))
+			return;
+		else if (ctx->to_include &&
+			 !string_list_has_string(ctx->to_include, file_name))
+			return;
+
+		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+		p = add_packed_git(full_path, full_path_len, 0);
+		if (!p) {
+			warning(_("failed to add packfile '%s'"),
+				full_path);
+			return;
+		}
+
+		if (open_pack_index(p)) {
+			warning(_("failed to open pack-index '%s'"),
+				full_path);
+			close_pack(p);
+			free(p);
+			return;
+		}
+
+		fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
+		ctx->nr++;
+	}
+}
+
+struct pack_midx_entry {
+	struct object_id oid;
+	uint32_t pack_int_id;
+	time_t pack_mtime;
+	uint64_t offset;
+	unsigned preferred : 1;
+};
+
+static int midx_oid_compare(const void *_a, const void *_b)
+{
+	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
+	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
+	int cmp = oidcmp(&a->oid, &b->oid);
+
+	if (cmp)
+		return cmp;
+
+	/* Sort objects in a preferred pack first when multiple copies exist. */
+	if (a->preferred > b->preferred)
+		return -1;
+	if (a->preferred < b->preferred)
+		return 1;
+
+	if (a->pack_mtime > b->pack_mtime)
+		return -1;
+	else if (a->pack_mtime < b->pack_mtime)
+		return 1;
+
+	return a->pack_int_id - b->pack_int_id;
+}
+
+static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
+				      struct pack_midx_entry *e,
+				      uint32_t pos)
+{
+	if (pos >= m->num_objects)
+		return 1;
+
+	nth_midxed_object_oid(&e->oid, m, pos);
+	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
+	e->offset = nth_midxed_offset(m, pos);
+
+	/* consider objects in midx to be from "old" packs */
+	e->pack_mtime = 0;
+	return 0;
+}
+
+static void fill_pack_entry(uint32_t pack_int_id,
+			    struct packed_git *p,
+			    uint32_t cur_object,
+			    struct pack_midx_entry *entry,
+			    int preferred)
+{
+	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
+		die(_("failed to locate object %d in packfile"), cur_object);
+
+	entry->pack_int_id = pack_int_id;
+	entry->pack_mtime = p->mtime;
+
+	entry->offset = nth_packed_object_offset(p, cur_object);
+	entry->preferred = !!preferred;
+}
+
+struct midx_fanout {
+	struct pack_midx_entry *entries;
+	size_t nr, alloc;
+};
+
+static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
+{
+	if (nr < fanout->nr)
+		BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
+		    (uintmax_t)nr, (uintmax_t)fanout->nr);
+	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
+}
+
+static void midx_fanout_sort(struct midx_fanout *fanout)
+{
+	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
+}
+
+static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
+					struct multi_pack_index *m,
+					uint32_t cur_fanout,
+					int preferred_pack)
+{
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
+	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		if ((preferred_pack > -1) &&
+		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
+			/*
+			 * Objects from preferred packs are added
+			 * separately.
+			 */
+			continue;
+		}
+
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		nth_midxed_pack_midx_entry(m,
+					   &fanout->entries[fanout->nr],
+					   cur_object);
+		fanout->entries[fanout->nr].preferred = 0;
+		fanout->nr++;
+	}
+}
+
+static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
+					struct pack_info *info,
+					uint32_t cur_pack,
+					int preferred,
+					uint32_t cur_fanout)
+{
+	struct packed_git *pack = info[cur_pack].p;
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = get_pack_fanout(pack, cur_fanout - 1);
+	end = get_pack_fanout(pack, cur_fanout);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		fill_pack_entry(cur_pack,
+				info[cur_pack].p,
+				cur_object,
+				&fanout->entries[fanout->nr],
+				preferred);
+		fanout->nr++;
+	}
+}
+
+/*
+ * It is possible to artificially get into a state where there are many
+ * duplicate copies of objects. That can create high memory pressure if
+ * we are to create a list of all objects before de-duplication. To reduce
+ * this memory pressure without a significant performance drop, automatically
+ * group objects by the first byte of their object id. Use the IDX fanout
+ * tables to group the data, copy to a local array, then sort.
+ *
+ * Copy only the de-duplicated entries (selected by most-recent modified time
+ * of a packfile containing the object).
+ */
+static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
+						  struct pack_info *info,
+						  uint32_t nr_packs,
+						  size_t *nr_objects,
+						  int preferred_pack)
+{
+	uint32_t cur_fanout, cur_pack, cur_object;
+	size_t alloc_objects, total_objects = 0;
+	struct midx_fanout fanout = { 0 };
+	struct pack_midx_entry *deduplicated_entries = NULL;
+	uint32_t start_pack = m ? m->num_packs : 0;
+
+	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+		total_objects = st_add(total_objects,
+				       info[cur_pack].p->num_objects);
+
+	/*
+	 * As we de-duplicate by fanout value, we expect the fanout
+	 * slices to be evenly distributed, with some noise. Hence,
+	 * allocate slightly more than one 256th.
+	 */
+	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
+
+	ALLOC_ARRAY(fanout.entries, fanout.alloc);
+	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
+	*nr_objects = 0;
+
+	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
+		fanout.nr = 0;
+
+		if (m)
+			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
+						    preferred_pack);
+
+		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
+			int preferred = cur_pack == preferred_pack;
+			midx_fanout_add_pack_fanout(&fanout,
+						    info, cur_pack,
+						    preferred, cur_fanout);
+		}
+
+		if (-1 < preferred_pack && preferred_pack < start_pack)
+			midx_fanout_add_pack_fanout(&fanout, info,
+						    preferred_pack, 1,
+						    cur_fanout);
+
+		midx_fanout_sort(&fanout);
+
+		/*
+		 * The batch is now sorted by OID and then mtime (descending).
+		 * Take only the first duplicate.
+		 */
+		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
+			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
+						&fanout.entries[cur_object].oid))
+				continue;
+
+			ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+				   alloc_objects);
+			memcpy(&deduplicated_entries[*nr_objects],
+			       &fanout.entries[cur_object],
+			       sizeof(struct pack_midx_entry));
+			(*nr_objects)++;
+		}
+	}
+
+	free(fanout.entries);
+	return deduplicated_entries;
+}
+
+static int write_midx_pack_names(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
+	size_t written = 0;
+
+	for (i = 0; i < ctx->nr; i++) {
+		size_t writelen;
+
+		if (ctx->info[i].expired)
+			continue;
+
+		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
+			BUG("incorrect pack-file order: %s before %s",
+			    ctx->info[i - 1].pack_name,
+			    ctx->info[i].pack_name);
+
+		writelen = strlen(ctx->info[i].pack_name) + 1;
+		hashwrite(f, ctx->info[i].pack_name, writelen);
+		written += writelen;
+	}
+
+	/* add padding to be aligned */
+	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
+	if (i < MIDX_CHUNK_ALIGNMENT) {
+		memset(padding, 0, sizeof(padding));
+		hashwrite(f, padding, i);
+	}
+
+	return 0;
+}
+
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	size_t i;
+
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[i];
+		if (pack->expired)
+			continue;
+
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+			BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+			    pack->pack_name, pack->bitmap_nr);
+
+		hashwrite_be32(f, pack->bitmap_pos);
+		hashwrite_be32(f, pack->bitmap_nr);
+	}
+	return 0;
+}
+
+static int write_midx_oid_fanout(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
+	uint32_t count = 0;
+	uint32_t i;
+
+	/*
+	* Write the first-level table (the list is sorted,
+	* but we use a 256-entry lookup to be able to avoid
+	* having to do eight extra binary search iterations).
+	*/
+	for (i = 0; i < 256; i++) {
+		struct pack_midx_entry *next = list;
+
+		while (next < last && next->oid.hash[0] == i) {
+			count++;
+			next++;
+		}
+
+		hashwrite_be32(f, count);
+		list = next;
+	}
+
+	return 0;
+}
+
+static int write_midx_oid_lookup(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	unsigned char hash_len = the_hash_algo->rawsz;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (i < ctx->entries_nr - 1) {
+			struct pack_midx_entry *next = list;
+			if (oidcmp(&obj->oid, &next->oid) >= 0)
+				BUG("OIDs not in order: %s >= %s",
+				    oid_to_hex(&obj->oid),
+				    oid_to_hex(&next->oid));
+		}
+
+		hashwrite(f, obj->oid.hash, (int)hash_len);
+	}
+
+	return 0;
+}
+
+static int write_midx_object_offsets(struct hashfile *f,
+				     void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i, nr_large_offset = 0;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
+			BUG("object %s is in an expired pack with int-id %d",
+			    oid_to_hex(&obj->oid),
+			    obj->pack_int_id);
+
+		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
+
+		if (ctx->large_offsets_needed && obj->offset >> 31)
+			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
+		else if (!ctx->large_offsets_needed && obj->offset >> 32)
+			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
+			    oid_to_hex(&obj->oid),
+			    obj->offset);
+		else
+			hashwrite_be32(f, (uint32_t)obj->offset);
+	}
+
+	return 0;
+}
+
+static int write_midx_large_offsets(struct hashfile *f,
+				    void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
+	uint32_t nr_large_offset = ctx->num_large_offsets;
+
+	while (nr_large_offset) {
+		struct pack_midx_entry *obj;
+		uint64_t offset;
+
+		if (list >= end)
+			BUG("too many large-offset objects");
+
+		obj = list++;
+		offset = obj->offset;
+
+		if (!(offset >> 31))
+			continue;
+
+		hashwrite_be64(f, offset);
+
+		nr_large_offset--;
+	}
+
+	return 0;
+}
+
+static int write_midx_revindex(struct hashfile *f,
+			       void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++)
+		hashwrite_be32(f, ctx->pack_order[i]);
+
+	return 0;
+}
+
+struct midx_pack_order_data {
+	uint32_t nr;
+	uint32_t pack;
+	off_t offset;
+};
+
+static int midx_pack_order_cmp(const void *va, const void *vb)
+{
+	const struct midx_pack_order_data *a = va, *b = vb;
+	if (a->pack < b->pack)
+		return -1;
+	else if (a->pack > b->pack)
+		return 1;
+	else if (a->offset < b->offset)
+		return -1;
+	else if (a->offset > b->offset)
+		return 1;
+	else
+		return 0;
+}
+
+static uint32_t *midx_pack_order(struct write_midx_context *ctx)
+{
+	struct midx_pack_order_data *data;
+	uint32_t *pack_order;
+	uint32_t i;
+
+	trace2_region_enter("midx", "midx_pack_order", the_repository);
+
+	ALLOC_ARRAY(data, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[i];
+		data[i].nr = i;
+		data[i].pack = ctx->pack_perm[e->pack_int_id];
+		if (!e->preferred)
+			data[i].pack |= (1U << 31);
+		data[i].offset = e->offset;
+	}
+
+	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
+
+	ALLOC_ARRAY(pack_order, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = i;
+		pack->bitmap_nr++;
+		pack_order[i] = data[i].nr;
+	}
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = 0;
+	}
+	free(data);
+
+	trace2_region_leave("midx", "midx_pack_order", the_repository);
+
+	return pack_order;
+}
+
+static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
+				     struct write_midx_context *ctx)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *tmp_file;
+
+	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
+
+	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
+
+	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
+					midx_hash, WRITE_REV);
+
+	if (finalize_object_file(tmp_file, buf.buf))
+		die(_("cannot store reverse index file"));
+
+	strbuf_release(&buf);
+
+	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
+}
+
+static void prepare_midx_packing_data(struct packing_data *pdata,
+				      struct write_midx_context *ctx)
+{
+	uint32_t i;
+
+	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
+
+	memset(pdata, 0, sizeof(struct packing_data));
+	prepare_packing_data(the_repository, pdata);
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
+		struct object_entry *to = packlist_alloc(pdata, &from->oid);
+
+		oe_set_in_pack(pdata, to,
+			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
+	}
+
+	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
+}
+
+static int add_ref_to_pending(const char *refname,
+			      const struct object_id *oid,
+			      int flag, void *cb_data)
+{
+	struct rev_info *revs = (struct rev_info*)cb_data;
+	struct object_id peeled;
+	struct object *object;
+
+	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
+		warning("symbolic ref is dangling: %s", refname);
+		return 0;
+	}
+
+	if (!peel_iterated_oid(oid, &peeled))
+		oid = &peeled;
+
+	object = parse_object_or_die(oid, refname);
+	if (object->type != OBJ_COMMIT)
+		return 0;
+
+	add_pending_object(revs, object, "");
+	if (bitmap_is_preferred_refname(revs->repo, refname))
+		object->flags |= NEEDS_BITMAP;
+	return 0;
+}
+
+struct bitmap_commit_cb {
+	struct commit **commits;
+	size_t commits_nr, commits_alloc;
+
+	struct write_midx_context *ctx;
+};
+
+static const struct object_id *bitmap_oid_access(size_t index,
+						 const void *_entries)
+{
+	const struct pack_midx_entry *entries = _entries;
+	return &entries[index].oid;
+}
+
+static void bitmap_show_commit(struct commit *commit, void *_data)
+{
+	struct bitmap_commit_cb *data = _data;
+	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
+			  data->ctx->entries_nr,
+			  bitmap_oid_access);
+	if (pos < 0)
+		return;
+
+	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
+	data->commits[data->commits_nr++] = commit;
+}
+
+static int read_refs_snapshot(const char *refs_snapshot,
+			      struct rev_info *revs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+	FILE *f = xfopen(refs_snapshot, "r");
+
+	while (strbuf_getline(&buf, f) != EOF) {
+		struct object *object;
+		int preferred = 0;
+		char *hex = buf.buf;
+		const char *end = NULL;
+
+		if (buf.len && *buf.buf == '+') {
+			preferred = 1;
+			hex = &buf.buf[1];
+		}
+
+		if (parse_oid_hex(hex, &oid, &end) < 0)
+			die(_("could not parse line: %s"), buf.buf);
+		if (*end)
+			die(_("malformed line: %s"), buf.buf);
+
+		object = parse_object_or_die(&oid, NULL);
+		if (preferred)
+			object->flags |= NEEDS_BITMAP;
+
+		add_pending_object(revs, object, "");
+	}
+
+	fclose(f);
+	strbuf_release(&buf);
+	return 0;
+}
+static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
+						    const char *refs_snapshot,
+						    struct write_midx_context *ctx)
+{
+	struct rev_info revs;
+	struct bitmap_commit_cb cb = {0};
+
+	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	cb.ctx = ctx;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	if (refs_snapshot) {
+		read_refs_snapshot(refs_snapshot, &revs);
+	} else {
+		setup_revisions(0, NULL, &revs, NULL);
+		for_each_ref(add_ref_to_pending, &revs);
+	}
+
+	/*
+	 * Skipping promisor objects here is intentional, since it only excludes
+	 * them from the list of reachable commits that we want to select from
+	 * when computing the selection of MIDX'd commits to receive bitmaps.
+	 *
+	 * Reachability bitmaps do require that their objects be closed under
+	 * reachability, but fetching any objects missing from promisors at this
+	 * point is too late. But, if one of those objects can be reached from
+	 * an another object that is included in the bitmap, then we will
+	 * complain later that we don't have reachability closure (and fail
+	 * appropriately).
+	 */
+	fetch_if_missing = 0;
+	revs.exclude_promisor_objects = 1;
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+
+	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
+	if (indexed_commits_nr_p)
+		*indexed_commits_nr_p = cb.commits_nr;
+
+	release_revisions(&revs);
+
+	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	return cb.commits;
+}
+
+static int write_midx_bitmap(const char *midx_name,
+			     const unsigned char *midx_hash,
+			     struct packing_data *pdata,
+			     struct commit **commits,
+			     uint32_t commits_nr,
+			     uint32_t *pack_order,
+			     unsigned flags)
+{
+	int ret, i;
+	uint16_t options = 0;
+	struct pack_idx_entry **index;
+	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
+					hash_to_hex(midx_hash));
+
+	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
+
+	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
+		options |= BITMAP_OPT_HASH_CACHE;
+
+	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
+		options |= BITMAP_OPT_LOOKUP_TABLE;
+
+	/*
+	 * Build the MIDX-order index based on pdata.objects (which is already
+	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
+	 * this order).
+	 */
+	ALLOC_ARRAY(index, pdata->nr_objects);
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[i] = &pdata->objects[i].idx;
+
+	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
+	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+
+	/*
+	 * bitmap_writer_finish expects objects in lex order, but pack_order
+	 * gives us exactly that. use it directly instead of re-sorting the
+	 * array.
+	 *
+	 * This changes the order of objects in 'index' between
+	 * bitmap_writer_build_type_index and bitmap_writer_finish.
+	 *
+	 * The same re-ordering takes place in the single-pack bitmap code via
+	 * write_idx_file(), which is called by finish_tmp_packfile(), which
+	 * happens between bitmap_writer_build_type_index() and
+	 * bitmap_writer_finish().
+	 */
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[pack_order[i]] = &pdata->objects[i].idx;
+
+	bitmap_writer_select_commits(commits, commits_nr, -1);
+	ret = bitmap_writer_build(pdata);
+	if (ret < 0)
+		goto cleanup;
+
+	bitmap_writer_set_checksum(midx_hash);
+	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+
+cleanup:
+	free(index);
+	free(bitmap_name);
+
+	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
+
+	return ret;
+}
+
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+							const char *object_dir)
+{
+	struct multi_pack_index *result = NULL;
+	struct multi_pack_index *cur;
+	char *obj_dir_real = real_pathdup(object_dir, 1);
+	struct strbuf cur_path_real = STRBUF_INIT;
+
+	/* Ensure the given object_dir is local, or a known alternate. */
+	find_odb(r, obj_dir_real);
+
+	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
+		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
+			result = cur;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	free(obj_dir_real);
+	strbuf_release(&cur_path_real);
+	return result;
+}
+
+static int write_midx_internal(const char *object_dir,
+			       struct string_list *packs_to_include,
+			       struct string_list *packs_to_drop,
+			       const char *preferred_pack_name,
+			       const char *refs_snapshot,
+			       unsigned flags)
+{
+	struct strbuf midx_name = STRBUF_INIT;
+	unsigned char midx_hash[GIT_MAX_RAWSZ];
+	uint32_t i;
+	struct hashfile *f = NULL;
+	struct lock_file lk;
+	struct write_midx_context ctx = { 0 };
+	int bitmapped_packs_concat_len = 0;
+	int pack_name_concat_len = 0;
+	int dropped_packs = 0;
+	int result = 0;
+	struct chunkfile *cf;
+
+	trace2_region_enter("midx", "write_midx_internal", the_repository);
+
+	get_midx_filename(&midx_name, object_dir);
+	if (safe_create_leading_directories(midx_name.buf))
+		die_errno(_("unable to create leading directories of %s"),
+			  midx_name.buf);
+
+	if (!packs_to_include) {
+		/*
+		 * Only reference an existing MIDX when not filtering which
+		 * packs to include, since all packs and objects are copied
+		 * blindly from an existing MIDX if one is present.
+		 */
+		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
+	}
+
+	if (ctx.m && !midx_checksum_valid(ctx.m)) {
+		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
+		ctx.m = NULL;
+	}
+
+	ctx.nr = 0;
+	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
+	ctx.info = NULL;
+	ALLOC_ARRAY(ctx.info, ctx.alloc);
+
+	if (ctx.m) {
+		for (i = 0; i < ctx.m->num_packs; i++) {
+			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
+
+			if (flags & MIDX_WRITE_REV_INDEX) {
+				/*
+				 * If generating a reverse index, need to have
+				 * packed_git's loaded to compare their
+				 * mtimes and object count.
+				 */
+				if (prepare_midx_pack(the_repository, ctx.m, i)) {
+					error(_("could not load pack"));
+					result = 1;
+					goto cleanup;
+				}
+
+				if (open_pack_index(ctx.m->packs[i]))
+					die(_("could not open index for %s"),
+					    ctx.m->packs[i]->pack_name);
+			}
+
+			fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+				       ctx.m->pack_names[i], i);
+		}
+	}
+
+	ctx.pack_paths_checked = 0;
+	if (flags & MIDX_PROGRESS)
+		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
+	else
+		ctx.progress = NULL;
+
+	ctx.to_include = packs_to_include;
+
+	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
+	stop_progress(&ctx.progress);
+
+	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
+	    !(packs_to_include || packs_to_drop)) {
+		struct bitmap_index *bitmap_git;
+		int bitmap_exists;
+		int want_bitmap = flags & MIDX_WRITE_BITMAP;
+
+		bitmap_git = prepare_midx_bitmap_git(ctx.m);
+		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
+		free_bitmap_index(bitmap_git);
+
+		if (bitmap_exists || !want_bitmap) {
+			/*
+			 * The correct MIDX already exists, and so does a
+			 * corresponding bitmap (or one wasn't requested).
+			 */
+			if (!want_bitmap)
+				clear_midx_files_ext(object_dir, ".bitmap",
+						     NULL);
+			goto cleanup;
+		}
+	}
+
+	if (preferred_pack_name) {
+		ctx.preferred_pack_idx = -1;
+
+		for (i = 0; i < ctx.nr; i++) {
+			if (!cmp_idx_or_pack_name(preferred_pack_name,
+						  ctx.info[i].pack_name)) {
+				ctx.preferred_pack_idx = i;
+				break;
+			}
+		}
+
+		if (ctx.preferred_pack_idx == -1)
+			warning(_("unknown preferred pack: '%s'"),
+				preferred_pack_name);
+	} else if (ctx.nr &&
+		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
+		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
+		ctx.preferred_pack_idx = 0;
+
+		if (packs_to_drop && packs_to_drop->nr)
+			BUG("cannot write a MIDX bitmap during expiration");
+
+		/*
+		 * set a preferred pack when writing a bitmap to ensure that
+		 * the pack from which the first object is selected in pseudo
+		 * pack-order has all of its objects selected from that pack
+		 * (and not another pack containing a duplicate)
+		 */
+		for (i = 1; i < ctx.nr; i++) {
+			struct packed_git *p = ctx.info[i].p;
+
+			if (!oldest->num_objects || p->mtime < oldest->mtime) {
+				oldest = p;
+				ctx.preferred_pack_idx = i;
+			}
+		}
+
+		if (!oldest->num_objects) {
+			/*
+			 * If all packs are empty; unset the preferred index.
+			 * This is acceptable since there will be no duplicate
+			 * objects to resolve, so the preferred value doesn't
+			 * matter.
+			 */
+			ctx.preferred_pack_idx = -1;
+		}
+	} else {
+		/*
+		 * otherwise don't mark any pack as preferred to avoid
+		 * interfering with expiration logic below
+		 */
+		ctx.preferred_pack_idx = -1;
+	}
+
+	if (ctx.preferred_pack_idx > -1) {
+		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
+		if (!preferred->num_objects) {
+			error(_("cannot select preferred pack %s with no objects"),
+			      preferred->pack_name);
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
+					 ctx.preferred_pack_idx);
+
+	ctx.large_offsets_needed = 0;
+	for (i = 0; i < ctx.entries_nr; i++) {
+		if (ctx.entries[i].offset > 0x7fffffff)
+			ctx.num_large_offsets++;
+		if (ctx.entries[i].offset > 0xffffffff)
+			ctx.large_offsets_needed = 1;
+	}
+
+	QSORT(ctx.info, ctx.nr, pack_info_compare);
+
+	if (packs_to_drop && packs_to_drop->nr) {
+		int drop_index = 0;
+		int missing_drops = 0;
+
+		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
+			int cmp = strcmp(ctx.info[i].pack_name,
+					 packs_to_drop->items[drop_index].string);
+
+			if (!cmp) {
+				drop_index++;
+				ctx.info[i].expired = 1;
+			} else if (cmp > 0) {
+				error(_("did not see pack-file %s to drop"),
+				      packs_to_drop->items[drop_index].string);
+				drop_index++;
+				missing_drops++;
+				i--;
+			} else {
+				ctx.info[i].expired = 0;
+			}
+		}
+
+		if (missing_drops) {
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * pack_perm stores a permutation between pack-int-ids from the
+	 * previous multi-pack-index to the new one we are writing:
+	 *
+	 * pack_perm[old_id] = new_id
+	 */
+	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired) {
+			dropped_packs++;
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
+		} else {
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
+		}
+	}
+
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired)
+			continue;
+		pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+		bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
+	}
+
+	/* Check that the preferred pack wasn't expired (if given). */
+	if (preferred_pack_name) {
+		struct pack_info *preferred = bsearch(preferred_pack_name,
+						      ctx.info, ctx.nr,
+						      sizeof(*ctx.info),
+						      idx_or_pack_name_cmp);
+		if (preferred) {
+			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
+			if (perm == PACK_EXPIRED)
+				warning(_("preferred pack '%s' is expired"),
+					preferred_pack_name);
+		}
+	}
+
+	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
+		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
+					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
+
+	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
+	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+
+	if (ctx.nr - dropped_packs == 0) {
+		error(_("no pack files to index."));
+		result = 1;
+		goto cleanup;
+	}
+
+	if (!ctx.entries_nr) {
+		if (flags & MIDX_WRITE_BITMAP)
+			warning(_("refusing to write multi-pack .bitmap without any objects"));
+		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
+	}
+
+	cf = init_chunkfile(f);
+
+	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
+		  write_midx_pack_names);
+	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
+		  write_midx_oid_fanout);
+	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
+		  st_mult(ctx.entries_nr, the_hash_algo->rawsz),
+		  write_midx_oid_lookup);
+	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
+		  st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
+		  write_midx_object_offsets);
+
+	if (ctx.large_offsets_needed)
+		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
+			st_mult(ctx.num_large_offsets,
+				MIDX_CHUNK_LARGE_OFFSET_WIDTH),
+			write_midx_large_offsets);
+
+	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
+		ctx.pack_order = midx_pack_order(&ctx);
+		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
+			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
+			  write_midx_revindex);
+		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+			  bitmapped_packs_concat_len,
+			  write_midx_bitmapped_packs);
+	}
+
+	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
+	write_chunkfile(cf, &ctx);
+
+	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
+			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
+	free_chunkfile(cf);
+
+	if (flags & MIDX_WRITE_REV_INDEX &&
+	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
+		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+
+	if (flags & MIDX_WRITE_BITMAP) {
+		struct packing_data pdata;
+		struct commit **commits;
+		uint32_t commits_nr;
+
+		if (!ctx.entries_nr)
+			BUG("cannot write a bitmap without any objects");
+
+		prepare_midx_packing_data(&pdata, &ctx);
+
+		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
+
+		/*
+		 * The previous steps translated the information from
+		 * 'entries' into information suitable for constructing
+		 * bitmaps. We no longer need that array, so clear it to
+		 * reduce memory pressure.
+		 */
+		FREE_AND_NULL(ctx.entries);
+		ctx.entries_nr = 0;
+
+		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
+				      commits, commits_nr, ctx.pack_order,
+				      flags) < 0) {
+			error(_("could not write multi-pack bitmap"));
+			result = 1;
+			clear_packing_data(&pdata);
+			free(commits);
+			goto cleanup;
+		}
+
+		clear_packing_data(&pdata);
+		free(commits);
+	}
+	/*
+	 * NOTE: Do not use ctx.entries beyond this point, since it might
+	 * have been freed in the previous if block.
+	 */
+
+	if (ctx.m)
+		close_object_store(the_repository->objects);
+
+	if (commit_lock_file(&lk) < 0)
+		die_errno(_("could not write multi-pack-index"));
+
+	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
+	clear_midx_files_ext(object_dir, ".rev", midx_hash);
+
+cleanup:
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].p) {
+			close_pack(ctx.info[i].p);
+			free(ctx.info[i].p);
+		}
+		free(ctx.info[i].pack_name);
+	}
+
+	free(ctx.info);
+	free(ctx.entries);
+	free(ctx.pack_perm);
+	free(ctx.pack_order);
+	strbuf_release(&midx_name);
+
+	trace2_region_leave("midx", "write_midx_internal", the_repository);
+
+	return result;
+}
+
+int write_midx_file(const char *object_dir,
+		    const char *preferred_pack_name,
+		    const char *refs_snapshot,
+		    unsigned flags)
+{
+	return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
+				   refs_snapshot, flags);
+}
+
+int write_midx_file_only(const char *object_dir,
+			 struct string_list *packs_to_include,
+			 const char *preferred_pack_name,
+			 const char *refs_snapshot,
+			 unsigned flags)
+{
+	return write_midx_internal(object_dir, packs_to_include, NULL,
+				   preferred_pack_name, refs_snapshot, flags);
+}
+
+int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
+{
+	uint32_t i, *count, result = 0;
+	struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
+	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+	struct progress *progress = NULL;
+
+	if (!m)
+		return 0;
+
+	CALLOC_ARRAY(count, m->num_packs);
+
+	if (flags & MIDX_PROGRESS)
+		progress = start_delayed_progress(_("Counting referenced objects"),
+					  m->num_objects);
+	for (i = 0; i < m->num_objects; i++) {
+		int pack_int_id = nth_midxed_pack_int_id(m, i);
+		count[pack_int_id]++;
+		display_progress(progress, i + 1);
+	}
+	stop_progress(&progress);
+
+	if (flags & MIDX_PROGRESS)
+		progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
+					  m->num_packs);
+	for (i = 0; i < m->num_packs; i++) {
+		char *pack_name;
+		display_progress(progress, i + 1);
+
+		if (count[i])
+			continue;
+
+		if (prepare_midx_pack(r, m, i))
+			continue;
+
+		if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
+			continue;
+
+		pack_name = xstrdup(m->packs[i]->pack_name);
+		close_pack(m->packs[i]);
+
+		string_list_insert(&packs_to_drop, m->pack_names[i]);
+		unlink_pack_path(pack_name, 0);
+		free(pack_name);
+	}
+	stop_progress(&progress);
+
+	free(count);
+
+	if (packs_to_drop.nr)
+		result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
+
+	string_list_clear(&packs_to_drop, 0);
+
+	return result;
+}
+
+struct repack_info {
+	timestamp_t mtime;
+	uint32_t referenced_objects;
+	uint32_t pack_int_id;
+};
+
+static int compare_by_mtime(const void *a_, const void *b_)
+{
+	const struct repack_info *a, *b;
+
+	a = (const struct repack_info *)a_;
+	b = (const struct repack_info *)b_;
+
+	if (a->mtime < b->mtime)
+		return -1;
+	if (a->mtime > b->mtime)
+		return 1;
+	return 0;
+}
+
+static int fill_included_packs_all(struct repository *r,
+				   struct multi_pack_index *m,
+				   unsigned char *include_pack)
+{
+	uint32_t i, count = 0;
+	int pack_kept_objects = 0;
+
+	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+	for (i = 0; i < m->num_packs; i++) {
+		if (prepare_midx_pack(r, m, i))
+			continue;
+		if (!pack_kept_objects && m->packs[i]->pack_keep)
+			continue;
+		if (m->packs[i]->is_cruft)
+			continue;
+
+		include_pack[i] = 1;
+		count++;
+	}
+
+	return count < 2;
+}
+
+static int fill_included_packs_batch(struct repository *r,
+				     struct multi_pack_index *m,
+				     unsigned char *include_pack,
+				     size_t batch_size)
+{
+	uint32_t i, packs_to_repack;
+	size_t total_size;
+	struct repack_info *pack_info;
+	int pack_kept_objects = 0;
+
+	CALLOC_ARRAY(pack_info, m->num_packs);
+
+	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+	for (i = 0; i < m->num_packs; i++) {
+		pack_info[i].pack_int_id = i;
+
+		if (prepare_midx_pack(r, m, i))
+			continue;
+
+		pack_info[i].mtime = m->packs[i]->mtime;
+	}
+
+	for (i = 0; i < m->num_objects; i++) {
+		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
+		pack_info[pack_int_id].referenced_objects++;
+	}
+
+	QSORT(pack_info, m->num_packs, compare_by_mtime);
+
+	total_size = 0;
+	packs_to_repack = 0;
+	for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
+		int pack_int_id = pack_info[i].pack_int_id;
+		struct packed_git *p = m->packs[pack_int_id];
+		size_t expected_size;
+
+		if (!p)
+			continue;
+		if (!pack_kept_objects && p->pack_keep)
+			continue;
+		if (p->is_cruft)
+			continue;
+		if (open_pack_index(p) || !p->num_objects)
+			continue;
+
+		expected_size = st_mult(p->pack_size,
+					pack_info[i].referenced_objects);
+		expected_size /= p->num_objects;
+
+		if (expected_size >= batch_size)
+			continue;
+
+		packs_to_repack++;
+		total_size += expected_size;
+		include_pack[pack_int_id] = 1;
+	}
+
+	free(pack_info);
+
+	if (packs_to_repack < 2)
+		return 1;
+
+	return 0;
+}
+
+int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
+{
+	int result = 0;
+	uint32_t i;
+	unsigned char *include_pack;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	FILE *cmd_in;
+	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+
+	/*
+	 * When updating the default for these configuration
+	 * variables in builtin/repack.c, these must be adjusted
+	 * to match.
+	 */
+	int delta_base_offset = 1;
+	int use_delta_islands = 0;
+
+	if (!m)
+		return 0;
+
+	CALLOC_ARRAY(include_pack, m->num_packs);
+
+	if (batch_size) {
+		if (fill_included_packs_batch(r, m, include_pack, batch_size))
+			goto cleanup;
+	} else if (fill_included_packs_all(r, m, include_pack))
+		goto cleanup;
+
+	repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
+	repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
+
+	strvec_push(&cmd.args, "pack-objects");
+
+	strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
+
+	if (delta_base_offset)
+		strvec_push(&cmd.args, "--delta-base-offset");
+	if (use_delta_islands)
+		strvec_push(&cmd.args, "--delta-islands");
+
+	if (flags & MIDX_PROGRESS)
+		strvec_push(&cmd.args, "--progress");
+	else
+		strvec_push(&cmd.args, "-q");
+
+	cmd.git_cmd = 1;
+	cmd.in = cmd.out = -1;
+
+	if (start_command(&cmd)) {
+		error(_("could not start pack-objects"));
+		result = 1;
+		goto cleanup;
+	}
+
+	cmd_in = xfdopen(cmd.in, "w");
+
+	for (i = 0; i < m->num_objects; i++) {
+		struct object_id oid;
+		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
+
+		if (!include_pack[pack_int_id])
+			continue;
+
+		nth_midxed_object_oid(&oid, m, i);
+		fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
+	}
+	fclose(cmd_in);
+
+	if (finish_command(&cmd)) {
+		error(_("could not finish pack-objects"));
+		result = 1;
+		goto cleanup;
+	}
+
+	result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
+
+cleanup:
+	free(include_pack);
+	return result;
+}
diff --git a/midx.c b/midx.c
index 41521e019c..ae3b49166c 100644
--- a/midx.c
+++ b/midx.c
@@ -1,52 +1,22 @@
 #include "git-compat-util.h"
-#include "abspath.h"
 #include "config.h"
-#include "csum-file.h"
 #include "dir.h"
-#include "gettext.h"
 #include "hex.h"
-#include "lockfile.h"
 #include "packfile.h"
 #include "object-file.h"
-#include "object-store-ll.h"
 #include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
 #include "trace2.h"
-#include "run-command.h"
-#include "repository.h"
 #include "chunk-format.h"
-#include "pack.h"
 #include "pack-bitmap.h"
-#include "refs.h"
-#include "revision.h"
-#include "list-objects.h"
 #include "pack-revindex.h"
 
-#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
-#define MIDX_VERSION 1
-#define MIDX_BYTE_FILE_VERSION 4
-#define MIDX_BYTE_HASH_VERSION 5
-#define MIDX_BYTE_NUM_CHUNKS 6
-#define MIDX_BYTE_NUM_PACKS 8
-#define MIDX_HEADER_SIZE 12
-#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
-
-#define MIDX_CHUNK_ALIGNMENT 4
-#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
-#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
-#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
-#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
-#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
-#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
-#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
-#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
-#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
-#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
-#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
-#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
-
-#define PACK_EXPIRED UINT_MAX
+int midx_checksum_valid(struct multi_pack_index *m);
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash);
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name);
 
 const unsigned char *get_midx_checksum(struct multi_pack_index *m)
 {
@@ -115,6 +85,8 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
 	return 0;
 }
 
+#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
+
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local)
 {
 	struct multi_pack_index *m = NULL;
@@ -294,6 +266,8 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t
 	return 0;
 }
 
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
+
 int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
 		       struct bitmapped_pack *bp, uint32_t pack_int_id)
 {
@@ -400,8 +374,8 @@ int fill_midx_entry(struct repository *r,
 }
 
 /* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */
-static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
-				const char *idx_name)
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name)
 {
 	/* Skip past any initial matching prefix. */
 	while (*idx_name && *idx_name == *idx_or_pack_name) {
@@ -508,1262 +482,11 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
 	return 0;
 }
 
-static size_t write_midx_header(struct hashfile *f,
-				unsigned char num_chunks,
-				uint32_t num_packs)
-{
-	hashwrite_be32(f, MIDX_SIGNATURE);
-	hashwrite_u8(f, MIDX_VERSION);
-	hashwrite_u8(f, oid_version(the_hash_algo));
-	hashwrite_u8(f, num_chunks);
-	hashwrite_u8(f, 0); /* unused */
-	hashwrite_be32(f, num_packs);
-
-	return MIDX_HEADER_SIZE;
-}
-
-#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
-
-struct pack_info {
-	uint32_t orig_pack_int_id;
-	char *pack_name;
-	struct packed_git *p;
-
-	uint32_t bitmap_pos;
-	uint32_t bitmap_nr;
-
-	unsigned expired : 1;
-};
-
-static void fill_pack_info(struct pack_info *info,
-			   struct packed_git *p, const char *pack_name,
-			   uint32_t orig_pack_int_id)
-{
-	memset(info, 0, sizeof(struct pack_info));
-
-	info->orig_pack_int_id = orig_pack_int_id;
-	info->pack_name = xstrdup(pack_name);
-	info->p = p;
-	info->bitmap_pos = BITMAP_POS_UNKNOWN;
-}
-
-static int pack_info_compare(const void *_a, const void *_b)
-{
-	struct pack_info *a = (struct pack_info *)_a;
-	struct pack_info *b = (struct pack_info *)_b;
-	return strcmp(a->pack_name, b->pack_name);
-}
-
-static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
-{
-	const char *pack_name = _va;
-	const struct pack_info *compar = _vb;
-
-	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
-}
-
-struct write_midx_context {
-	struct pack_info *info;
-	size_t nr;
-	size_t alloc;
-	struct multi_pack_index *m;
-	struct progress *progress;
-	unsigned pack_paths_checked;
-
-	struct pack_midx_entry *entries;
-	size_t entries_nr;
-
-	uint32_t *pack_perm;
-	uint32_t *pack_order;
-	unsigned large_offsets_needed:1;
-	uint32_t num_large_offsets;
-
-	int preferred_pack_idx;
-
-	struct string_list *to_include;
-};
-
-static void add_pack_to_midx(const char *full_path, size_t full_path_len,
-			     const char *file_name, void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct packed_git *p;
-
-	if (ends_with(file_name, ".idx")) {
-		display_progress(ctx->progress, ++ctx->pack_paths_checked);
-		/*
-		 * Note that at most one of ctx->m and ctx->to_include are set,
-		 * so we are testing midx_contains_pack() and
-		 * string_list_has_string() independently (guarded by the
-		 * appropriate NULL checks).
-		 *
-		 * We could support passing to_include while reusing an existing
-		 * MIDX, but don't currently since the reuse process drags
-		 * forward all packs from an existing MIDX (without checking
-		 * whether or not they appear in the to_include list).
-		 *
-		 * If we added support for that, these next two conditional
-		 * should be performed independently (likely checking
-		 * to_include before the existing MIDX).
-		 */
-		if (ctx->m && midx_contains_pack(ctx->m, file_name))
-			return;
-		else if (ctx->to_include &&
-			 !string_list_has_string(ctx->to_include, file_name))
-			return;
-
-		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
-		p = add_packed_git(full_path, full_path_len, 0);
-		if (!p) {
-			warning(_("failed to add packfile '%s'"),
-				full_path);
-			return;
-		}
-
-		if (open_pack_index(p)) {
-			warning(_("failed to open pack-index '%s'"),
-				full_path);
-			close_pack(p);
-			free(p);
-			return;
-		}
-
-		fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
-		ctx->nr++;
-	}
-}
-
-struct pack_midx_entry {
-	struct object_id oid;
-	uint32_t pack_int_id;
-	time_t pack_mtime;
-	uint64_t offset;
-	unsigned preferred : 1;
-};
-
-static int midx_oid_compare(const void *_a, const void *_b)
-{
-	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
-	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
-	int cmp = oidcmp(&a->oid, &b->oid);
-
-	if (cmp)
-		return cmp;
-
-	/* Sort objects in a preferred pack first when multiple copies exist. */
-	if (a->preferred > b->preferred)
-		return -1;
-	if (a->preferred < b->preferred)
-		return 1;
-
-	if (a->pack_mtime > b->pack_mtime)
-		return -1;
-	else if (a->pack_mtime < b->pack_mtime)
-		return 1;
-
-	return a->pack_int_id - b->pack_int_id;
-}
-
-static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
-				      struct pack_midx_entry *e,
-				      uint32_t pos)
-{
-	if (pos >= m->num_objects)
-		return 1;
-
-	nth_midxed_object_oid(&e->oid, m, pos);
-	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
-	e->offset = nth_midxed_offset(m, pos);
-
-	/* consider objects in midx to be from "old" packs */
-	e->pack_mtime = 0;
-	return 0;
-}
-
-static void fill_pack_entry(uint32_t pack_int_id,
-			    struct packed_git *p,
-			    uint32_t cur_object,
-			    struct pack_midx_entry *entry,
-			    int preferred)
-{
-	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
-		die(_("failed to locate object %d in packfile"), cur_object);
-
-	entry->pack_int_id = pack_int_id;
-	entry->pack_mtime = p->mtime;
-
-	entry->offset = nth_packed_object_offset(p, cur_object);
-	entry->preferred = !!preferred;
-}
-
-struct midx_fanout {
-	struct pack_midx_entry *entries;
-	size_t nr, alloc;
-};
-
-static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
-{
-	if (nr < fanout->nr)
-		BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
-		    (uintmax_t)nr, (uintmax_t)fanout->nr);
-	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
-}
-
-static void midx_fanout_sort(struct midx_fanout *fanout)
-{
-	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
-}
-
-static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
-					struct multi_pack_index *m,
-					uint32_t cur_fanout,
-					int preferred_pack)
-{
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
-	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		if ((preferred_pack > -1) &&
-		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
-			/*
-			 * Objects from preferred packs are added
-			 * separately.
-			 */
-			continue;
-		}
-
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		nth_midxed_pack_midx_entry(m,
-					   &fanout->entries[fanout->nr],
-					   cur_object);
-		fanout->entries[fanout->nr].preferred = 0;
-		fanout->nr++;
-	}
-}
-
-static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
-					struct pack_info *info,
-					uint32_t cur_pack,
-					int preferred,
-					uint32_t cur_fanout)
-{
-	struct packed_git *pack = info[cur_pack].p;
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = get_pack_fanout(pack, cur_fanout - 1);
-	end = get_pack_fanout(pack, cur_fanout);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		fill_pack_entry(cur_pack,
-				info[cur_pack].p,
-				cur_object,
-				&fanout->entries[fanout->nr],
-				preferred);
-		fanout->nr++;
-	}
-}
-
-/*
- * It is possible to artificially get into a state where there are many
- * duplicate copies of objects. That can create high memory pressure if
- * we are to create a list of all objects before de-duplication. To reduce
- * this memory pressure without a significant performance drop, automatically
- * group objects by the first byte of their object id. Use the IDX fanout
- * tables to group the data, copy to a local array, then sort.
- *
- * Copy only the de-duplicated entries (selected by most-recent modified time
- * of a packfile containing the object).
- */
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
-						  struct pack_info *info,
-						  uint32_t nr_packs,
-						  size_t *nr_objects,
-						  int preferred_pack)
-{
-	uint32_t cur_fanout, cur_pack, cur_object;
-	size_t alloc_objects, total_objects = 0;
-	struct midx_fanout fanout = { 0 };
-	struct pack_midx_entry *deduplicated_entries = NULL;
-	uint32_t start_pack = m ? m->num_packs : 0;
-
-	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
-		total_objects = st_add(total_objects,
-				       info[cur_pack].p->num_objects);
-
-	/*
-	 * As we de-duplicate by fanout value, we expect the fanout
-	 * slices to be evenly distributed, with some noise. Hence,
-	 * allocate slightly more than one 256th.
-	 */
-	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
-
-	ALLOC_ARRAY(fanout.entries, fanout.alloc);
-	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
-	*nr_objects = 0;
-
-	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
-		fanout.nr = 0;
-
-		if (m)
-			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
-						    preferred_pack);
-
-		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
-			int preferred = cur_pack == preferred_pack;
-			midx_fanout_add_pack_fanout(&fanout,
-						    info, cur_pack,
-						    preferred, cur_fanout);
-		}
-
-		if (-1 < preferred_pack && preferred_pack < start_pack)
-			midx_fanout_add_pack_fanout(&fanout, info,
-						    preferred_pack, 1,
-						    cur_fanout);
-
-		midx_fanout_sort(&fanout);
-
-		/*
-		 * The batch is now sorted by OID and then mtime (descending).
-		 * Take only the first duplicate.
-		 */
-		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
-			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
-						&fanout.entries[cur_object].oid))
-				continue;
-
-			ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
-				   alloc_objects);
-			memcpy(&deduplicated_entries[*nr_objects],
-			       &fanout.entries[cur_object],
-			       sizeof(struct pack_midx_entry));
-			(*nr_objects)++;
-		}
-	}
-
-	free(fanout.entries);
-	return deduplicated_entries;
-}
-
-static int write_midx_pack_names(struct hashfile *f, void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
-	size_t written = 0;
-
-	for (i = 0; i < ctx->nr; i++) {
-		size_t writelen;
-
-		if (ctx->info[i].expired)
-			continue;
-
-		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
-			BUG("incorrect pack-file order: %s before %s",
-			    ctx->info[i - 1].pack_name,
-			    ctx->info[i].pack_name);
-
-		writelen = strlen(ctx->info[i].pack_name) + 1;
-		hashwrite(f, ctx->info[i].pack_name, writelen);
-		written += writelen;
-	}
-
-	/* add padding to be aligned */
-	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
-	if (i < MIDX_CHUNK_ALIGNMENT) {
-		memset(padding, 0, sizeof(padding));
-		hashwrite(f, padding, i);
-	}
-
-	return 0;
-}
-
-static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
-{
-	struct write_midx_context *ctx = data;
-	size_t i;
-
-	for (i = 0; i < ctx->nr; i++) {
-		struct pack_info *pack = &ctx->info[i];
-		if (pack->expired)
-			continue;
-
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
-			BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
-			    pack->pack_name, pack->bitmap_nr);
-
-		hashwrite_be32(f, pack->bitmap_pos);
-		hashwrite_be32(f, pack->bitmap_nr);
-	}
-	return 0;
-}
-
-static int write_midx_oid_fanout(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
-	uint32_t count = 0;
-	uint32_t i;
-
-	/*
-	* Write the first-level table (the list is sorted,
-	* but we use a 256-entry lookup to be able to avoid
-	* having to do eight extra binary search iterations).
-	*/
-	for (i = 0; i < 256; i++) {
-		struct pack_midx_entry *next = list;
-
-		while (next < last && next->oid.hash[0] == i) {
-			count++;
-			next++;
-		}
-
-		hashwrite_be32(f, count);
-		list = next;
-	}
-
-	return 0;
-}
-
-static int write_midx_oid_lookup(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	unsigned char hash_len = the_hash_algo->rawsz;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (i < ctx->entries_nr - 1) {
-			struct pack_midx_entry *next = list;
-			if (oidcmp(&obj->oid, &next->oid) >= 0)
-				BUG("OIDs not in order: %s >= %s",
-				    oid_to_hex(&obj->oid),
-				    oid_to_hex(&next->oid));
-		}
-
-		hashwrite(f, obj->oid.hash, (int)hash_len);
-	}
-
-	return 0;
-}
-
-static int write_midx_object_offsets(struct hashfile *f,
-				     void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i, nr_large_offset = 0;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
-			BUG("object %s is in an expired pack with int-id %d",
-			    oid_to_hex(&obj->oid),
-			    obj->pack_int_id);
-
-		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
-
-		if (ctx->large_offsets_needed && obj->offset >> 31)
-			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
-		else if (!ctx->large_offsets_needed && obj->offset >> 32)
-			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
-			    oid_to_hex(&obj->oid),
-			    obj->offset);
-		else
-			hashwrite_be32(f, (uint32_t)obj->offset);
-	}
-
-	return 0;
-}
-
-static int write_midx_large_offsets(struct hashfile *f,
-				    void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
-	uint32_t nr_large_offset = ctx->num_large_offsets;
-
-	while (nr_large_offset) {
-		struct pack_midx_entry *obj;
-		uint64_t offset;
-
-		if (list >= end)
-			BUG("too many large-offset objects");
-
-		obj = list++;
-		offset = obj->offset;
-
-		if (!(offset >> 31))
-			continue;
-
-		hashwrite_be64(f, offset);
-
-		nr_large_offset--;
-	}
-
-	return 0;
-}
-
-static int write_midx_revindex(struct hashfile *f,
-			       void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++)
-		hashwrite_be32(f, ctx->pack_order[i]);
-
-	return 0;
-}
-
-struct midx_pack_order_data {
-	uint32_t nr;
-	uint32_t pack;
-	off_t offset;
-};
-
-static int midx_pack_order_cmp(const void *va, const void *vb)
-{
-	const struct midx_pack_order_data *a = va, *b = vb;
-	if (a->pack < b->pack)
-		return -1;
-	else if (a->pack > b->pack)
-		return 1;
-	else if (a->offset < b->offset)
-		return -1;
-	else if (a->offset > b->offset)
-		return 1;
-	else
-		return 0;
-}
-
-static uint32_t *midx_pack_order(struct write_midx_context *ctx)
-{
-	struct midx_pack_order_data *data;
-	uint32_t *pack_order;
-	uint32_t i;
-
-	trace2_region_enter("midx", "midx_pack_order", the_repository);
-
-	ALLOC_ARRAY(data, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *e = &ctx->entries[i];
-		data[i].nr = i;
-		data[i].pack = ctx->pack_perm[e->pack_int_id];
-		if (!e->preferred)
-			data[i].pack |= (1U << 31);
-		data[i].offset = e->offset;
-	}
-
-	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
-
-	ALLOC_ARRAY(pack_order, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *e = &ctx->entries[data[i].nr];
-		struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
-			pack->bitmap_pos = i;
-		pack->bitmap_nr++;
-		pack_order[i] = data[i].nr;
-	}
-	for (i = 0; i < ctx->nr; i++) {
-		struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
-			pack->bitmap_pos = 0;
-	}
-	free(data);
-
-	trace2_region_leave("midx", "midx_pack_order", the_repository);
-
-	return pack_order;
-}
-
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
-				     struct write_midx_context *ctx)
-{
-	struct strbuf buf = STRBUF_INIT;
-	const char *tmp_file;
-
-	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
-
-	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
-
-	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
-					midx_hash, WRITE_REV);
-
-	if (finalize_object_file(tmp_file, buf.buf))
-		die(_("cannot store reverse index file"));
-
-	strbuf_release(&buf);
-
-	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
-}
-
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash);
-
-static int midx_checksum_valid(struct multi_pack_index *m)
+int midx_checksum_valid(struct multi_pack_index *m)
 {
 	return hashfile_checksum_valid(m->data, m->data_len);
 }
 
-static void prepare_midx_packing_data(struct packing_data *pdata,
-				      struct write_midx_context *ctx)
-{
-	uint32_t i;
-
-	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
-
-	memset(pdata, 0, sizeof(struct packing_data));
-	prepare_packing_data(the_repository, pdata);
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
-		struct object_entry *to = packlist_alloc(pdata, &from->oid);
-
-		oe_set_in_pack(pdata, to,
-			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
-	}
-
-	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
-}
-
-static int add_ref_to_pending(const char *refname,
-			      const struct object_id *oid,
-			      int flag, void *cb_data)
-{
-	struct rev_info *revs = (struct rev_info*)cb_data;
-	struct object_id peeled;
-	struct object *object;
-
-	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
-		warning("symbolic ref is dangling: %s", refname);
-		return 0;
-	}
-
-	if (!peel_iterated_oid(oid, &peeled))
-		oid = &peeled;
-
-	object = parse_object_or_die(oid, refname);
-	if (object->type != OBJ_COMMIT)
-		return 0;
-
-	add_pending_object(revs, object, "");
-	if (bitmap_is_preferred_refname(revs->repo, refname))
-		object->flags |= NEEDS_BITMAP;
-	return 0;
-}
-
-struct bitmap_commit_cb {
-	struct commit **commits;
-	size_t commits_nr, commits_alloc;
-
-	struct write_midx_context *ctx;
-};
-
-static const struct object_id *bitmap_oid_access(size_t index,
-						 const void *_entries)
-{
-	const struct pack_midx_entry *entries = _entries;
-	return &entries[index].oid;
-}
-
-static void bitmap_show_commit(struct commit *commit, void *_data)
-{
-	struct bitmap_commit_cb *data = _data;
-	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
-			  data->ctx->entries_nr,
-			  bitmap_oid_access);
-	if (pos < 0)
-		return;
-
-	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
-	data->commits[data->commits_nr++] = commit;
-}
-
-static int read_refs_snapshot(const char *refs_snapshot,
-			      struct rev_info *revs)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct object_id oid;
-	FILE *f = xfopen(refs_snapshot, "r");
-
-	while (strbuf_getline(&buf, f) != EOF) {
-		struct object *object;
-		int preferred = 0;
-		char *hex = buf.buf;
-		const char *end = NULL;
-
-		if (buf.len && *buf.buf == '+') {
-			preferred = 1;
-			hex = &buf.buf[1];
-		}
-
-		if (parse_oid_hex(hex, &oid, &end) < 0)
-			die(_("could not parse line: %s"), buf.buf);
-		if (*end)
-			die(_("malformed line: %s"), buf.buf);
-
-		object = parse_object_or_die(&oid, NULL);
-		if (preferred)
-			object->flags |= NEEDS_BITMAP;
-
-		add_pending_object(revs, object, "");
-	}
-
-	fclose(f);
-	strbuf_release(&buf);
-	return 0;
-}
-
-static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
-						    const char *refs_snapshot,
-						    struct write_midx_context *ctx)
-{
-	struct rev_info revs;
-	struct bitmap_commit_cb cb = {0};
-
-	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	cb.ctx = ctx;
-
-	repo_init_revisions(the_repository, &revs, NULL);
-	if (refs_snapshot) {
-		read_refs_snapshot(refs_snapshot, &revs);
-	} else {
-		setup_revisions(0, NULL, &revs, NULL);
-		for_each_ref(add_ref_to_pending, &revs);
-	}
-
-	/*
-	 * Skipping promisor objects here is intentional, since it only excludes
-	 * them from the list of reachable commits that we want to select from
-	 * when computing the selection of MIDX'd commits to receive bitmaps.
-	 *
-	 * Reachability bitmaps do require that their objects be closed under
-	 * reachability, but fetching any objects missing from promisors at this
-	 * point is too late. But, if one of those objects can be reached from
-	 * an another object that is included in the bitmap, then we will
-	 * complain later that we don't have reachability closure (and fail
-	 * appropriately).
-	 */
-	fetch_if_missing = 0;
-	revs.exclude_promisor_objects = 1;
-
-	if (prepare_revision_walk(&revs))
-		die(_("revision walk setup failed"));
-
-	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
-	if (indexed_commits_nr_p)
-		*indexed_commits_nr_p = cb.commits_nr;
-
-	release_revisions(&revs);
-
-	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	return cb.commits;
-}
-
-static int write_midx_bitmap(const char *midx_name,
-			     const unsigned char *midx_hash,
-			     struct packing_data *pdata,
-			     struct commit **commits,
-			     uint32_t commits_nr,
-			     uint32_t *pack_order,
-			     unsigned flags)
-{
-	int ret, i;
-	uint16_t options = 0;
-	struct pack_idx_entry **index;
-	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
-					hash_to_hex(midx_hash));
-
-	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
-
-	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
-		options |= BITMAP_OPT_HASH_CACHE;
-
-	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
-		options |= BITMAP_OPT_LOOKUP_TABLE;
-
-	/*
-	 * Build the MIDX-order index based on pdata.objects (which is already
-	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
-	 * this order).
-	 */
-	ALLOC_ARRAY(index, pdata->nr_objects);
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[i] = &pdata->objects[i].idx;
-
-	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
-	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
-
-	/*
-	 * bitmap_writer_finish expects objects in lex order, but pack_order
-	 * gives us exactly that. use it directly instead of re-sorting the
-	 * array.
-	 *
-	 * This changes the order of objects in 'index' between
-	 * bitmap_writer_build_type_index and bitmap_writer_finish.
-	 *
-	 * The same re-ordering takes place in the single-pack bitmap code via
-	 * write_idx_file(), which is called by finish_tmp_packfile(), which
-	 * happens between bitmap_writer_build_type_index() and
-	 * bitmap_writer_finish().
-	 */
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[pack_order[i]] = &pdata->objects[i].idx;
-
-	bitmap_writer_select_commits(commits, commits_nr, -1);
-	ret = bitmap_writer_build(pdata);
-	if (ret < 0)
-		goto cleanup;
-
-	bitmap_writer_set_checksum(midx_hash);
-	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
-
-cleanup:
-	free(index);
-	free(bitmap_name);
-
-	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
-
-	return ret;
-}
-
-static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
-							const char *object_dir)
-{
-	struct multi_pack_index *result = NULL;
-	struct multi_pack_index *cur;
-	char *obj_dir_real = real_pathdup(object_dir, 1);
-	struct strbuf cur_path_real = STRBUF_INIT;
-
-	/* Ensure the given object_dir is local, or a known alternate. */
-	find_odb(r, obj_dir_real);
-
-	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
-		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
-		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
-			result = cur;
-			goto cleanup;
-		}
-	}
-
-cleanup:
-	free(obj_dir_real);
-	strbuf_release(&cur_path_real);
-	return result;
-}
-
-static int write_midx_internal(const char *object_dir,
-			       struct string_list *packs_to_include,
-			       struct string_list *packs_to_drop,
-			       const char *preferred_pack_name,
-			       const char *refs_snapshot,
-			       unsigned flags)
-{
-	struct strbuf midx_name = STRBUF_INIT;
-	unsigned char midx_hash[GIT_MAX_RAWSZ];
-	uint32_t i;
-	struct hashfile *f = NULL;
-	struct lock_file lk;
-	struct write_midx_context ctx = { 0 };
-	int bitmapped_packs_concat_len = 0;
-	int pack_name_concat_len = 0;
-	int dropped_packs = 0;
-	int result = 0;
-	struct chunkfile *cf;
-
-	trace2_region_enter("midx", "write_midx_internal", the_repository);
-
-	get_midx_filename(&midx_name, object_dir);
-	if (safe_create_leading_directories(midx_name.buf))
-		die_errno(_("unable to create leading directories of %s"),
-			  midx_name.buf);
-
-	if (!packs_to_include) {
-		/*
-		 * Only reference an existing MIDX when not filtering which
-		 * packs to include, since all packs and objects are copied
-		 * blindly from an existing MIDX if one is present.
-		 */
-		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
-	}
-
-	if (ctx.m && !midx_checksum_valid(ctx.m)) {
-		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
-		ctx.m = NULL;
-	}
-
-	ctx.nr = 0;
-	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
-	ctx.info = NULL;
-	ALLOC_ARRAY(ctx.info, ctx.alloc);
-
-	if (ctx.m) {
-		for (i = 0; i < ctx.m->num_packs; i++) {
-			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
-			if (flags & MIDX_WRITE_REV_INDEX) {
-				/*
-				 * If generating a reverse index, need to have
-				 * packed_git's loaded to compare their
-				 * mtimes and object count.
-				 */
-				if (prepare_midx_pack(the_repository, ctx.m, i)) {
-					error(_("could not load pack"));
-					result = 1;
-					goto cleanup;
-				}
-
-				if (open_pack_index(ctx.m->packs[i]))
-					die(_("could not open index for %s"),
-					    ctx.m->packs[i]->pack_name);
-			}
-
-			fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
-				       ctx.m->pack_names[i], i);
-		}
-	}
-
-	ctx.pack_paths_checked = 0;
-	if (flags & MIDX_PROGRESS)
-		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
-	else
-		ctx.progress = NULL;
-
-	ctx.to_include = packs_to_include;
-
-	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
-	stop_progress(&ctx.progress);
-
-	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
-	    !(packs_to_include || packs_to_drop)) {
-		struct bitmap_index *bitmap_git;
-		int bitmap_exists;
-		int want_bitmap = flags & MIDX_WRITE_BITMAP;
-
-		bitmap_git = prepare_midx_bitmap_git(ctx.m);
-		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
-		free_bitmap_index(bitmap_git);
-
-		if (bitmap_exists || !want_bitmap) {
-			/*
-			 * The correct MIDX already exists, and so does a
-			 * corresponding bitmap (or one wasn't requested).
-			 */
-			if (!want_bitmap)
-				clear_midx_files_ext(object_dir, ".bitmap",
-						     NULL);
-			goto cleanup;
-		}
-	}
-
-	if (preferred_pack_name) {
-		ctx.preferred_pack_idx = -1;
-
-		for (i = 0; i < ctx.nr; i++) {
-			if (!cmp_idx_or_pack_name(preferred_pack_name,
-						  ctx.info[i].pack_name)) {
-				ctx.preferred_pack_idx = i;
-				break;
-			}
-		}
-
-		if (ctx.preferred_pack_idx == -1)
-			warning(_("unknown preferred pack: '%s'"),
-				preferred_pack_name);
-	} else if (ctx.nr &&
-		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
-		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
-		ctx.preferred_pack_idx = 0;
-
-		if (packs_to_drop && packs_to_drop->nr)
-			BUG("cannot write a MIDX bitmap during expiration");
-
-		/*
-		 * set a preferred pack when writing a bitmap to ensure that
-		 * the pack from which the first object is selected in pseudo
-		 * pack-order has all of its objects selected from that pack
-		 * (and not another pack containing a duplicate)
-		 */
-		for (i = 1; i < ctx.nr; i++) {
-			struct packed_git *p = ctx.info[i].p;
-
-			if (!oldest->num_objects || p->mtime < oldest->mtime) {
-				oldest = p;
-				ctx.preferred_pack_idx = i;
-			}
-		}
-
-		if (!oldest->num_objects) {
-			/*
-			 * If all packs are empty; unset the preferred index.
-			 * This is acceptable since there will be no duplicate
-			 * objects to resolve, so the preferred value doesn't
-			 * matter.
-			 */
-			ctx.preferred_pack_idx = -1;
-		}
-	} else {
-		/*
-		 * otherwise don't mark any pack as preferred to avoid
-		 * interfering with expiration logic below
-		 */
-		ctx.preferred_pack_idx = -1;
-	}
-
-	if (ctx.preferred_pack_idx > -1) {
-		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
-		if (!preferred->num_objects) {
-			error(_("cannot select preferred pack %s with no objects"),
-			      preferred->pack_name);
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
-					 ctx.preferred_pack_idx);
-
-	ctx.large_offsets_needed = 0;
-	for (i = 0; i < ctx.entries_nr; i++) {
-		if (ctx.entries[i].offset > 0x7fffffff)
-			ctx.num_large_offsets++;
-		if (ctx.entries[i].offset > 0xffffffff)
-			ctx.large_offsets_needed = 1;
-	}
-
-	QSORT(ctx.info, ctx.nr, pack_info_compare);
-
-	if (packs_to_drop && packs_to_drop->nr) {
-		int drop_index = 0;
-		int missing_drops = 0;
-
-		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
-			int cmp = strcmp(ctx.info[i].pack_name,
-					 packs_to_drop->items[drop_index].string);
-
-			if (!cmp) {
-				drop_index++;
-				ctx.info[i].expired = 1;
-			} else if (cmp > 0) {
-				error(_("did not see pack-file %s to drop"),
-				      packs_to_drop->items[drop_index].string);
-				drop_index++;
-				missing_drops++;
-				i--;
-			} else {
-				ctx.info[i].expired = 0;
-			}
-		}
-
-		if (missing_drops) {
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	/*
-	 * pack_perm stores a permutation between pack-int-ids from the
-	 * previous multi-pack-index to the new one we are writing:
-	 *
-	 * pack_perm[old_id] = new_id
-	 */
-	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].expired) {
-			dropped_packs++;
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
-		} else {
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
-		}
-	}
-
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].expired)
-			continue;
-		pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
-		bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
-	}
-
-	/* Check that the preferred pack wasn't expired (if given). */
-	if (preferred_pack_name) {
-		struct pack_info *preferred = bsearch(preferred_pack_name,
-						      ctx.info, ctx.nr,
-						      sizeof(*ctx.info),
-						      idx_or_pack_name_cmp);
-		if (preferred) {
-			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
-			if (perm == PACK_EXPIRED)
-				warning(_("preferred pack '%s' is expired"),
-					preferred_pack_name);
-		}
-	}
-
-	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
-		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
-					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
-
-	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
-	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
-
-	if (ctx.nr - dropped_packs == 0) {
-		error(_("no pack files to index."));
-		result = 1;
-		goto cleanup;
-	}
-
-	if (!ctx.entries_nr) {
-		if (flags & MIDX_WRITE_BITMAP)
-			warning(_("refusing to write multi-pack .bitmap without any objects"));
-		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
-	}
-
-	cf = init_chunkfile(f);
-
-	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
-		  write_midx_pack_names);
-	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
-		  write_midx_oid_fanout);
-	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
-		  st_mult(ctx.entries_nr, the_hash_algo->rawsz),
-		  write_midx_oid_lookup);
-	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
-		  st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
-		  write_midx_object_offsets);
-
-	if (ctx.large_offsets_needed)
-		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
-			st_mult(ctx.num_large_offsets,
-				MIDX_CHUNK_LARGE_OFFSET_WIDTH),
-			write_midx_large_offsets);
-
-	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
-		ctx.pack_order = midx_pack_order(&ctx);
-		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
-			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
-			  write_midx_revindex);
-		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
-			  bitmapped_packs_concat_len,
-			  write_midx_bitmapped_packs);
-	}
-
-	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
-	write_chunkfile(cf, &ctx);
-
-	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
-			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
-	free_chunkfile(cf);
-
-	if (flags & MIDX_WRITE_REV_INDEX &&
-	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
-		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
-
-	if (flags & MIDX_WRITE_BITMAP) {
-		struct packing_data pdata;
-		struct commit **commits;
-		uint32_t commits_nr;
-
-		if (!ctx.entries_nr)
-			BUG("cannot write a bitmap without any objects");
-
-		prepare_midx_packing_data(&pdata, &ctx);
-
-		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
-
-		/*
-		 * The previous steps translated the information from
-		 * 'entries' into information suitable for constructing
-		 * bitmaps. We no longer need that array, so clear it to
-		 * reduce memory pressure.
-		 */
-		FREE_AND_NULL(ctx.entries);
-		ctx.entries_nr = 0;
-
-		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
-				      commits, commits_nr, ctx.pack_order,
-				      flags) < 0) {
-			error(_("could not write multi-pack bitmap"));
-			result = 1;
-			clear_packing_data(&pdata);
-			free(commits);
-			goto cleanup;
-		}
-
-		clear_packing_data(&pdata);
-		free(commits);
-	}
-	/*
-	 * NOTE: Do not use ctx.entries beyond this point, since it might
-	 * have been freed in the previous if block.
-	 */
-
-	if (ctx.m)
-		close_object_store(the_repository->objects);
-
-	if (commit_lock_file(&lk) < 0)
-		die_errno(_("could not write multi-pack-index"));
-
-	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
-	clear_midx_files_ext(object_dir, ".rev", midx_hash);
-
-cleanup:
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].p) {
-			close_pack(ctx.info[i].p);
-			free(ctx.info[i].p);
-		}
-		free(ctx.info[i].pack_name);
-	}
-
-	free(ctx.info);
-	free(ctx.entries);
-	free(ctx.pack_perm);
-	free(ctx.pack_order);
-	strbuf_release(&midx_name);
-
-	trace2_region_leave("midx", "write_midx_internal", the_repository);
-
-	return result;
-}
-
-int write_midx_file(const char *object_dir,
-		    const char *preferred_pack_name,
-		    const char *refs_snapshot,
-		    unsigned flags)
-{
-	return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
-				   refs_snapshot, flags);
-}
-
-int write_midx_file_only(const char *object_dir,
-			 struct string_list *packs_to_include,
-			 const char *preferred_pack_name,
-			 const char *refs_snapshot,
-			 unsigned flags)
-{
-	return write_midx_internal(object_dir, packs_to_include, NULL,
-				   preferred_pack_name, refs_snapshot, flags);
-}
-
 struct clear_midx_data {
 	char *keep;
 	const char *ext;
@@ -1784,8 +507,8 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS
 		die_errno(_("failed to remove %s"), full_path);
 }
 
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash)
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash)
 {
 	struct clear_midx_data data;
 	memset(&data, 0, sizeof(struct clear_midx_data));
@@ -1988,251 +711,3 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
 
 	return verify_midx_error;
 }
-
-int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
-{
-	uint32_t i, *count, result = 0;
-	struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
-	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
-	struct progress *progress = NULL;
-
-	if (!m)
-		return 0;
-
-	CALLOC_ARRAY(count, m->num_packs);
-
-	if (flags & MIDX_PROGRESS)
-		progress = start_delayed_progress(_("Counting referenced objects"),
-					  m->num_objects);
-	for (i = 0; i < m->num_objects; i++) {
-		int pack_int_id = nth_midxed_pack_int_id(m, i);
-		count[pack_int_id]++;
-		display_progress(progress, i + 1);
-	}
-	stop_progress(&progress);
-
-	if (flags & MIDX_PROGRESS)
-		progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
-					  m->num_packs);
-	for (i = 0; i < m->num_packs; i++) {
-		char *pack_name;
-		display_progress(progress, i + 1);
-
-		if (count[i])
-			continue;
-
-		if (prepare_midx_pack(r, m, i))
-			continue;
-
-		if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
-			continue;
-
-		pack_name = xstrdup(m->packs[i]->pack_name);
-		close_pack(m->packs[i]);
-
-		string_list_insert(&packs_to_drop, m->pack_names[i]);
-		unlink_pack_path(pack_name, 0);
-		free(pack_name);
-	}
-	stop_progress(&progress);
-
-	free(count);
-
-	if (packs_to_drop.nr)
-		result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
-
-	string_list_clear(&packs_to_drop, 0);
-
-	return result;
-}
-
-struct repack_info {
-	timestamp_t mtime;
-	uint32_t referenced_objects;
-	uint32_t pack_int_id;
-};
-
-static int compare_by_mtime(const void *a_, const void *b_)
-{
-	const struct repack_info *a, *b;
-
-	a = (const struct repack_info *)a_;
-	b = (const struct repack_info *)b_;
-
-	if (a->mtime < b->mtime)
-		return -1;
-	if (a->mtime > b->mtime)
-		return 1;
-	return 0;
-}
-
-static int fill_included_packs_all(struct repository *r,
-				   struct multi_pack_index *m,
-				   unsigned char *include_pack)
-{
-	uint32_t i, count = 0;
-	int pack_kept_objects = 0;
-
-	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
-	for (i = 0; i < m->num_packs; i++) {
-		if (prepare_midx_pack(r, m, i))
-			continue;
-		if (!pack_kept_objects && m->packs[i]->pack_keep)
-			continue;
-		if (m->packs[i]->is_cruft)
-			continue;
-
-		include_pack[i] = 1;
-		count++;
-	}
-
-	return count < 2;
-}
-
-static int fill_included_packs_batch(struct repository *r,
-				     struct multi_pack_index *m,
-				     unsigned char *include_pack,
-				     size_t batch_size)
-{
-	uint32_t i, packs_to_repack;
-	size_t total_size;
-	struct repack_info *pack_info;
-	int pack_kept_objects = 0;
-
-	CALLOC_ARRAY(pack_info, m->num_packs);
-
-	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
-	for (i = 0; i < m->num_packs; i++) {
-		pack_info[i].pack_int_id = i;
-
-		if (prepare_midx_pack(r, m, i))
-			continue;
-
-		pack_info[i].mtime = m->packs[i]->mtime;
-	}
-
-	for (i = 0; i < m->num_objects; i++) {
-		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
-		pack_info[pack_int_id].referenced_objects++;
-	}
-
-	QSORT(pack_info, m->num_packs, compare_by_mtime);
-
-	total_size = 0;
-	packs_to_repack = 0;
-	for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
-		int pack_int_id = pack_info[i].pack_int_id;
-		struct packed_git *p = m->packs[pack_int_id];
-		size_t expected_size;
-
-		if (!p)
-			continue;
-		if (!pack_kept_objects && p->pack_keep)
-			continue;
-		if (p->is_cruft)
-			continue;
-		if (open_pack_index(p) || !p->num_objects)
-			continue;
-
-		expected_size = st_mult(p->pack_size,
-					pack_info[i].referenced_objects);
-		expected_size /= p->num_objects;
-
-		if (expected_size >= batch_size)
-			continue;
-
-		packs_to_repack++;
-		total_size += expected_size;
-		include_pack[pack_int_id] = 1;
-	}
-
-	free(pack_info);
-
-	if (packs_to_repack < 2)
-		return 1;
-
-	return 0;
-}
-
-int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
-{
-	int result = 0;
-	uint32_t i;
-	unsigned char *include_pack;
-	struct child_process cmd = CHILD_PROCESS_INIT;
-	FILE *cmd_in;
-	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
-
-	/*
-	 * When updating the default for these configuration
-	 * variables in builtin/repack.c, these must be adjusted
-	 * to match.
-	 */
-	int delta_base_offset = 1;
-	int use_delta_islands = 0;
-
-	if (!m)
-		return 0;
-
-	CALLOC_ARRAY(include_pack, m->num_packs);
-
-	if (batch_size) {
-		if (fill_included_packs_batch(r, m, include_pack, batch_size))
-			goto cleanup;
-	} else if (fill_included_packs_all(r, m, include_pack))
-		goto cleanup;
-
-	repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
-	repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
-
-	strvec_push(&cmd.args, "pack-objects");
-
-	strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
-
-	if (delta_base_offset)
-		strvec_push(&cmd.args, "--delta-base-offset");
-	if (use_delta_islands)
-		strvec_push(&cmd.args, "--delta-islands");
-
-	if (flags & MIDX_PROGRESS)
-		strvec_push(&cmd.args, "--progress");
-	else
-		strvec_push(&cmd.args, "-q");
-
-	cmd.git_cmd = 1;
-	cmd.in = cmd.out = -1;
-
-	if (start_command(&cmd)) {
-		error(_("could not start pack-objects"));
-		result = 1;
-		goto cleanup;
-	}
-
-	cmd_in = xfdopen(cmd.in, "w");
-
-	for (i = 0; i < m->num_objects; i++) {
-		struct object_id oid;
-		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
-
-		if (!include_pack[pack_int_id])
-			continue;
-
-		nth_midxed_object_oid(&oid, m, i);
-		fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
-	}
-	fclose(cmd_in);
-
-	if (finish_command(&cmd)) {
-		error(_("could not finish pack-objects"));
-		result = 1;
-		goto cleanup;
-	}
-
-	result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
-
-cleanup:
-	free(include_pack);
-	return result;
-}
diff --git a/midx.h b/midx.h
index b374a7afaf..dc477dff44 100644
--- a/midx.h
+++ b/midx.h
@@ -8,6 +8,25 @@ struct pack_entry;
 struct repository;
 struct bitmapped_pack;
 
+#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
+#define MIDX_VERSION 1
+#define MIDX_BYTE_FILE_VERSION 4
+#define MIDX_BYTE_HASH_VERSION 5
+#define MIDX_BYTE_NUM_CHUNKS 6
+#define MIDX_BYTE_NUM_PACKS 8
+#define MIDX_HEADER_SIZE 12
+
+#define MIDX_CHUNK_ALIGNMENT 4
+#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
+#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
+#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
+#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
+#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
+#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
+#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
+#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
+
 #define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
 #define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
 	"GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP"
-- 
2.44.0.330.g158d2a670b4



^ permalink raw reply related	[relevance 1%]

* Fwd: "reverse proxy" remote-helper
       [not found]     <CAEK6H9o2QQGLQQZ0=C=1CjRGg6UiD_0fv7MwQujv1Sa_v41TAw@mail.gmail.com>
@ 2024-04-01 17:37  3% ` Guga Figueiredo
  0 siblings, 0 replies; 200+ results
From: Guga Figueiredo @ 2024-04-01 17:37 UTC (permalink / raw)
  To: git

Hello,

I am playing around with git remote helpers trying to write a "reverse
proxy" sort of custom helper to work with a personal server and custom
protocol.
I am doing this because git, natively, already does 99% of the work
that I need, and I would like to avoid reinventing the wheel. I just
want to create a barely customized interface for a custom server.
I know I can achieve this with hooks, but I would rather keep this
configuration out of the repos.

conceptually, the command call flow should be something like this:
```
git <cmd> custom::<repo-url>
└──>git-remote-custom <repo-url> <cmd>
    └──>git <native-remote-helper> <repo-url> <cmd>
```

I have been able to implement a git remote helper in go that works on
locally stored remotes following this article by rovaugn and the git
documentation on helpers.

And have stripped it down to bare essentials to try to invoke the
native/builtin remote-helpers directly:
```go
package main

import (
    "errors"
    "fmt"
    "log"
    "net/url"
    "os"
    "os/exec"
    "regexp"
)

func Main() (er error) {

    if len(os.Args) < 3 {
       return fmt.Errorf("Usage: git-remote-custom remote-name remote-url")
    }

    remoteName := os.Args[1]
    remoteUrl := os.Args[2]

    scheme, err := parseRemoteURL(remoteUrl)
    if err != nil {
       return fmt.Errorf("Invalid remote URL %s: %v", remoteUrl, err)
    }

    // do custom stuff here ...

    // proxy through to native git remote helper
    cmd := exec.Command("git", fmt.Sprintf("remote-%s", scheme),
remoteName, remoteUrl)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    if err := cmd.Run(); err != nil {
       return err
    }

    return nil
}

func main() {
    if err := Main(); err != nil {
       log.Fatal(err)
    }
}

func parseRemoteURL(remoteUrl string) (string, error) {
    if u, err := url.Parse(remoteUrl); err == nil && u.Scheme != "" {
       return u.Scheme, nil
    }

    // Check for SCP-like SSH URLs first because they don't have a "scheme://"
    scpRe := regexp.MustCompile(`^[a-zA-Z0-9._@/+~-]+@[a-zA-Z0-9._@/+~-]+:[a-zA-Z0-9._@/+~-]+(\.git)?$`)
    if scpRe.MatchString(remoteUrl) {
       return "ssh", nil
    }

    return "", errors.New("invalid Git URL scheme")
}
```

and this worked for http(s):
```
$ GIT_TRACE=1 git clone custom::https://github.com/rovaughn/git-remote-grave.git
11:17:52.117674 git.c:455               trace: built-in: git clone
custom::https://github.com/rovaughn/git-remote-grave.git
Cloning into 'git-remote-grave'...
11:17:52.120021 run-command.c:668       trace: run_command: git
remote-custom origin https://github.com/rovaughn/git-remote-grave.git
11:17:52.121757 git.c:742               trace: exec: git-remote-custom
origin https://github.com/rovaughn/git-remote-grave.git
11:17:52.121792 run-command.c:668       trace: run_command:
git-remote-custom origin
https://github.com/rovaughn/git-remote-grave.git
11:17:52.666930 run-command.c:668       trace: run_command: git
index-pack --stdin -v --fix-thin '--keep=fetch-pack 21547 on pop-os'
--check-self-contained-and-connected
remote: Enumerating objects: 71, done.
11:17:52.669349 git.c:455               trace: built-in: git
index-pack --stdin -v --fix-thin '--keep=fetch-pack 21547 on pop-os'
--check-self-contained-and-connected
remote: Total 71 (delta 0), reused 0 (delta 0), pack-reused 71
Receiving objects: 100% (71/71), 48.92 KiB | 1.11 MiB/s, done.
Resolving deltas: 100% (28/28), done.
11:17:52.716950 run-command.c:668       trace: run_command: git
rev-list --objects --stdin --not --all --quiet --alternate-refs
'--progress=Checking connectivity'
11:17:52.718721 git.c:455               trace: built-in: git rev-list
--objects --stdin --not --all --quiet --alternate-refs
'--progress=Checking connectivity'
```

but `ssh` doesn't have a corresponding remote-ssh helper.

Is there a plug and play way to wrap/proxy the `ssh` protocols in a
similar way with a remote-helper? Or are there better approaches to
achieve this?

Any feedback is appreciated,
Kind regards

Guga


^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2] bisect: Honor log.date
  @ 2024-03-31 22:58  3%     ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-03-31 22:58 UTC (permalink / raw)
  To: Peter Krefting; +Cc: git, Osipov, Michael (IN IT IN), Jeff King

Peter Krefting <peter@softwolves.pp.se> writes:

> There are a lot of parameters to show that I have haven't used in my
> 14+ years of using Git, --summary is one of them. That's why I didn't
> add it.

Yup, that is semi-understandable, but especially given that it is
one of the options used by the original "diff-tree"'s invocation,
and that we are trying to replace it with "show" from the same
family of commands, it is a bit of disappointment.

We know we used to drive "diff-tree" with a known set of options,
and we are replacing the command to use "show" with some other set
of options.  I expected it to be fairly straight-forward and natural
to feed randomly picked commits to the two commands and compare
their output while deciding what that "some other set of options"
should be.  It is exactly the reason why I mentioned v1.0.0^0 is a
good test case.

Again, the output from them do not have to be identical---we are
primarily after catching unintended loss of informatino in such a
comparison, while gaining more confidence that it is a better
approach to use "show" output to produce output for end-user
consumption.

We have changed the bisect output before, as recent as in 2019 with
b02be8b9 (bisect: make diff-tree output prettier, 2019-02-22), and
heard nobody complain, so once we get to a reasonable set of options
and land this patch, maybe we can try improving on it safely.

FYI, attached is a comparison between the diff-tree output and
output from show with my choice of options for "show" picked from
the top of my head.  I do not think I personally like the --stat
output applied to a merge (--stat and --summary do not work N-way
like --cc does for patch text), but I think these options are the
closest parallel to what we have been giving to "diff-tree".

Thanks.

---------------------- >8 ----------------------
$ git diff-tree --pretty --stat --summary --cc v1.0.0^0
commit c2f3bf071ee90b01f2d629921bb04c4f798f02fa
Merge: 1ed91937e5cd59fdbdfa5f15f6fac132d2b21ce0 41f93a2c903a45167b26c2dc93d45ffa9a9bbd49
Author: Junio C Hamano <junkio@cox.net>
Date:   Wed Dec 21 00:01:00 2005 -0800

    GIT 1.0.0
    
    Signed-off-by: Junio C Hamano <junkio@cox.net>

 .gitignore                                       |   1 -
 Documentation/diff-options.txt                   |   8 +
 ...
 tar-tree.c                                       |   4 +-
 unpack-objects.c                                 |  13 +-
 66 files changed, 778 insertions(+), 617 deletions(-)
 delete mode 100644 Documentation/git-octopus.txt
 ...
 mode change 100644 => 100755 t/t5500-fetch-pack.sh
 mode change 100644 => 100755 t/t6101-rev-parse-parents.sh

---------------------- >8 ----------------------
$ git show -s --stat --summary --first-parent v1.0.0^0
commit c2f3bf071ee90b01f2d629921bb04c4f798f02fa
Merge: 1ed91937e5 41f93a2c90
Author: Junio C Hamano <gitster@pobox.com>
Date:   Wed Dec 21 00:01:00 2005 -0800

    GIT 1.0.0
    
    Signed-off-by: Junio C Hamano <junkio@cox.net>

 .gitignore                                       |   1 -
 Documentation/diff-options.txt                   |   8 +
 ...
 tar-tree.c                                       |   4 +-
 unpack-objects.c                                 |  13 +-
 66 files changed, 778 insertions(+), 617 deletions(-)
 delete mode 100644 Documentation/git-octopus.txt
 ...
 mode change 100644 => 100755 t/t5500-fetch-pack.sh
 mode change 100644 => 100755 t/t6101-rev-parse-parents.sh


^ permalink raw reply	[relevance 3%]

* Re: bug report: spurious "cannot delete branch '%s' used by worktree"
  2024-03-31  6:49  3%     ` Eric Sunshine
@ 2024-03-31  7:45  3%       ` Tamir Duberstein
  2024-04-02 10:10  0%       ` Phillip Wood
  1 sibling, 0 replies; 200+ results
From: Tamir Duberstein @ 2024-03-31  7:45 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: git, Phillip Wood

On Sun, Mar 31, 2024 at 7:49 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> [please reply inline rather than top-posting; I've moved your reply
> inline for this response]
>
> On Thu, Mar 28, 2024 at 1:40 PM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> > On Thu, Mar 28, 2024 at 5:24 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > > On Thu, Mar 28, 2024 at 10:54 AM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> > > > % git branch -d cleanup
> > > > error: cannot delete branch 'cleanup' used by worktree at '<my source dir>'
> > > > % git worktree list
> > > > <my source dir>  dc46f6d5e [main]
> > > > % git branch
> > > >   cleanup
> > > > * main
> > >
> > > Is this error persistent once it arises? That is, if you invoke `git
> > > branch -d cleanup` again immediately after (or a little while after)
> > > the above sequence, does the problem persist? Or does it "clear up" on
> > > its own at some point?
> >
> > Yes, the problem is persistent. The branch is never deleted.
>
> I'd guess that there may be some sort of "ref" still pointing at the
> "cleanup" branch which presumably was, at some point, checked out at
> "<my source dir>". Digging through the code[1,2,3] suggests that you
> might have some stale state from a rebase, bisect, or other sequencer
> operation which still references the "cleanup" branch.
>
> [Cc'ing Phillip who is probably much more familiar with this code than am I.]
>
> By the way, it's not clear from your initial report what you mean when
> you say "then the remote deleted the branch". Also, did you fetch
> and/or pull from the remote after that?
>
> [1]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L454
> [2]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L386
> [3]: https://github.com/git/git/blob/d6fd04375f91/sequencer.c#L6551

Thanks, I wasn't aware of the etiquette.

I had used the branch to send a pull request on GitHub, and then
deleted the remote branch from GitHub after merging the pull request.
Yes, I have fetched from the remote following that. I've also manually
removed the remote tracking branch. Symptoms remain unchanged.

If you can suggest where to go looking for stale state, I'm happy to
provide more information. I'll take a look at the code references as
well.


^ permalink raw reply	[relevance 3%]

* Re: bug report: spurious "cannot delete branch '%s' used by worktree"
  @ 2024-03-31  6:49  3%     ` Eric Sunshine
  2024-03-31  7:45  3%       ` Tamir Duberstein
  2024-04-02 10:10  0%       ` Phillip Wood
  0 siblings, 2 replies; 200+ results
From: Eric Sunshine @ 2024-03-31  6:49 UTC (permalink / raw)
  To: Tamir Duberstein; +Cc: git, Phillip Wood

[please reply inline rather than top-posting; I've moved your reply
inline for this response]

On Thu, Mar 28, 2024 at 1:40 PM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> On Thu, Mar 28, 2024 at 5:24 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > On Thu, Mar 28, 2024 at 10:54 AM Tamir Duberstein <tamird@fuseenergy.com> wrote:
> > > % git branch -d cleanup
> > > error: cannot delete branch 'cleanup' used by worktree at '<my source dir>'
> > > % git worktree list
> > > <my source dir>  dc46f6d5e [main]
> > > % git branch
> > >   cleanup
> > > * main
> >
> > Is this error persistent once it arises? That is, if you invoke `git
> > branch -d cleanup` again immediately after (or a little while after)
> > the above sequence, does the problem persist? Or does it "clear up" on
> > its own at some point?
>
> Yes, the problem is persistent. The branch is never deleted.

I'd guess that there may be some sort of "ref" still pointing at the
"cleanup" branch which presumably was, at some point, checked out at
"<my source dir>". Digging through the code[1,2,3] suggests that you
might have some stale state from a rebase, bisect, or other sequencer
operation which still references the "cleanup" branch.

[Cc'ing Phillip who is probably much more familiar with this code than am I.]

By the way, it's not clear from your initial report what you mean when
you say "then the remote deleted the branch". Also, did you fetch
and/or pull from the remote after that?

[1]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L454
[2]: https://github.com/git/git/blob/d6fd04375f91/branch.c#L386
[3]: https://github.com/git/git/blob/d6fd04375f91/sequencer.c#L6551


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] advice: omit trailing whitespace
  2024-03-29 23:23  0% ` Rubén Justo
@ 2024-03-31  6:17  0%   ` Rubén Justo
  0 siblings, 0 replies; 200+ results
From: Rubén Justo @ 2024-03-31  6:17 UTC (permalink / raw)
  To: Junio C Hamano, git

On Sat, Mar 30, 2024 at 12:23:41AM +0100, Rubén Justo wrote:
> On Fri, Mar 29, 2024 at 03:57:06PM -0700, Junio C Hamano wrote:
> > Git tools all consistently encourage users to avoid whitespaces at
> > the end of line by giving them features like "git diff --check" and
> > "git am --whitespace=fix".  Make sure that the advice messages we
> > give users avoid trailing whitespaces.  We shouldn't be wasting
> > vertical screen real estate by adding blank lines in advice messages
> > that are supposed to be concise hints, but as long as we write such
> > blank line in our "hints", we should do it right.
> > 
> > A test that expects the current behaviour of leaving trailing
> > whitespaces has been adjusted.
> > 
> > Signed-off-by: Junio C Hamano <gitster@pobox.com>
> > ---
> 
> Yeah, ACK.  This is obviously a good thing.  I'll base my other series
> in this.  Thanks.
> 
> >  advice.c          | 3 ++-
> >  t/t3200-branch.sh | 4 ++--
> >  2 files changed, 4 insertions(+), 3 deletions(-)
> > 
> > diff --git c/advice.c w/advice.c
> > index d19648b7f8..75111191ad 100644
> > --- c/advice.c
> > +++ w/advice.c
> > @@ -105,8 +105,9 @@ static void vadvise(const char *advice, int display_instructions,
> >  
> >  	for (cp = buf.buf; *cp; cp = np) {
> >  		np = strchrnul(cp, '\n');
> > -		fprintf(stderr,	_("%shint: %.*s%s\n"),
> > +		fprintf(stderr,	_("%shint:%s%.*s%s\n"),
> >  			advise_get_color(ADVICE_COLOR_HINT),
> > +			(np == cp) ? "" : " ",
> >  			(int)(np - cp), cp,
> >  			advise_get_color(ADVICE_COLOR_RESET));

Thinking again on this I wonder, while we're here, if we could go further
and move the "hint" literal to the args, to ease the translation work:

--- >8 ---
diff --git a/advice.c b/advice.c
index a18bfe776f..5897e62541 100644
--- a/advice.c
+++ b/advice.c
@@ -104,8 +104,9 @@ static void vadvise(const char *advice, int display_instructions,

        for (cp = buf.buf; *cp; cp = np) {
                np = strchrnul(cp, '\n');
-               fprintf(stderr, _("%shint:%s%.*s%s\n"),
+               fprintf(stderr, "%s%s:%s%.*s%s\n",
                        advise_get_color(ADVICE_COLOR_HINT),
+                       _("hint"),
                        (np == cp) ? "" : " ",
                        (int)(np - cp), cp,
                        advise_get_color(ADVICE_COLOR_RESET));
--- 8< ---

Of course, this is completely optional.

> >  		if (*np)
> > diff --git c/t/t3200-branch.sh w/t/t3200-branch.sh
> > index d3bbd00b81..ccfa6a720d 100755
> > --- c/t/t3200-branch.sh
> > +++ w/t/t3200-branch.sh
> > @@ -1154,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
> >  	hint: tracking ref '\''refs/heads/main'\'':
> >  	hint:   ambi1
> >  	hint:   ambi2
> > -	hint: ''
> > +	hint:
> >  	hint: This is typically a configuration error.
> > -	hint: ''
> > +	hint:
> >  	hint: To support setting up tracking branches, ensure that
> >  	hint: different remotes'\'' fetch refspecs map into different
> >  	hint: tracking namespaces.
> 
> 
> A quick run tells me that this step also needs, I think:
> 
> --- >8 ---
> 
> diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
> index b41a47eb94..03bb4af7d6 100755
> --- a/t/t7004-tag.sh
> +++ b/t/t7004-tag.sh
> @@ -1780,7 +1780,7 @@ test_expect_success 'recursive tagging should give advice' '
>         sed -e "s/|$//" <<-EOF >expect &&
>         hint: You have created a nested tag. The object referred to by your new tag is
>         hint: already a tag. If you meant to tag the object that it points to, use:
> -       hint: |
> +       hint:
>         hint:   git tag -f nested annotated-v4.0^{}
>         hint: Disable this message with "git config advice.nestedTag false"
>         EOF
> 
> 

Your queued version already fixed this.  So, nothing to worry about.


^ permalink raw reply related	[relevance 0%]

* [PATCH 4/8] refs: accept symref in `ref_transaction_add_update`
  2024-03-30 22:46  2% [PATCH 0/8] update-ref: add support for update-symref option Karthik Nayak
@ 2024-03-30 22:46  8% ` Karthik Nayak
  2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
  1 sibling, 0 replies; 200+ results
From: Karthik Nayak @ 2024-03-30 22:46 UTC (permalink / raw)
  To: git; +Cc: ps, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

The `ref_transaction_add_update` function obtains ref information and
flags to create a `ref_update` and add it to the transaction at hand.

To extend symref support in transactions, we need to also accept the
symref and process it. While we handle the processing in upcoming
commits. In this commit, let's add the paramater to the function.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 branch.c                |  2 +-
 builtin/fast-import.c   |  6 +++---
 builtin/fetch.c         |  2 +-
 builtin/receive-pack.c  |  2 +-
 builtin/replace.c       |  2 +-
 builtin/tag.c           |  2 +-
 builtin/update-ref.c    |  2 +-
 refs.c                  | 16 ++++++++--------
 refs.h                  |  9 +++++++--
 refs/files-backend.c    | 14 +++++++-------
 refs/refs-internal.h    | 12 +++++++++---
 refs/reftable-backend.c |  4 ++--
 sequencer.c             |  6 +++---
 walker.c                |  2 +-
 14 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/branch.c b/branch.c
index 621019fcf4..266dcb6a89 100644
--- a/branch.c
+++ b/branch.c
@@ -627,7 +627,7 @@ void create_branch(struct repository *r,
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					0, msg, NULL, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 71a195ca22..96c3fd5a30 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1648,7 +1648,7 @@ static int update_branch(struct branch *b)
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   0, msg, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1688,8 +1688,8 @@ static void dump_tags(void)
 		strbuf_reset(&ref_name);
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
-		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+		if (ref_transaction_update(transaction, ref_name.buf, &t->oid,
+					   NULL, 0, msg, NULL, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 46a793411a..5a8a58b6fa 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -667,7 +667,7 @@ static int s_update_ref(const char *action,
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     0, msg, NULL, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 56d8a77ed7..5318bc4b58 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1596,7 +1596,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 					   namespaced_name,
 					   new_oid, old_oid,
 					   0, "push",
-					   &err)) {
+					   NULL, &err)) {
 			rp_error("%s", err.buf);
 			ret = "failed to update ref";
 		} else {
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..b3281758d0 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
 	transaction = ref_transaction_begin(&err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   0, NULL, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index 19a7e06bf4..8de32535de 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -624,7 +624,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
-				   reflog_msg.buf, &err) ||
+				   reflog_msg.buf, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		if (path)
 			fprintf(stderr,
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 61338a01ec..3807cf4106 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -205,7 +205,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
 				   update_flags | create_reflog_flag,
-				   msg, &err))
+				   msg, NULL, &err))
 		die("%s", err.buf);
 
 	update_flags = default_flags;
diff --git a/refs.c b/refs.c
index 55d2e0b2cb..69b89a1aa2 100644
--- a/refs.c
+++ b/refs.c
@@ -1228,7 +1228,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
-		const char *msg)
+		const char *msg, const char *symref)
 {
 	struct ref_update *update;
 
@@ -1254,7 +1254,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
 			   unsigned int flags, const char *msg,
-			   struct strbuf *err)
+			   const char *symref, struct strbuf *err)
 {
 	assert(err);
 
@@ -1280,7 +1280,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 	flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
 	ref_transaction_add_update(transaction, refname, flags,
-				   new_oid, old_oid, msg);
+				   new_oid, old_oid, msg, symref);
 	return 0;
 }
 
@@ -1295,7 +1295,7 @@ int ref_transaction_create(struct ref_transaction *transaction,
 		return 1;
 	}
 	return ref_transaction_update(transaction, refname, new_oid,
-				      null_oid(), flags, msg, err);
+				      null_oid(), flags, msg, NULL, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1308,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 		BUG("delete called with old_oid set to zeros");
 	return ref_transaction_update(transaction, refname,
 				      null_oid(), old_oid,
-				      flags, msg, err);
+				      flags, msg, NULL, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1320,8 +1320,8 @@ int ref_transaction_verify(struct ref_transaction *transaction,
 	if (!old_oid)
 		BUG("verify called with old_oid set to NULL");
 	return ref_transaction_update(transaction, refname,
-				      NULL, old_oid,
-				      flags, NULL, err);
+				      NULL, old_oid, flags,
+				      NULL, NULL, err);
 }
 
 int refs_update_ref(struct ref_store *refs, const char *msg,
@@ -1336,7 +1336,7 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 	t = ref_store_transaction_begin(refs, &err);
 	if (!t ||
 	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-				   &err) ||
+				   NULL, &err) ||
 	    ref_transaction_commit(t, &err)) {
 		ret = 1;
 		ref_transaction_free(t);
diff --git a/refs.h b/refs.h
index 298caf6c61..073653d1a4 100644
--- a/refs.h
+++ b/refs.h
@@ -694,13 +694,18 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  */
 #define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
 
+/*
+ * Used to denote a symbolic reference update.
+ */
+#define REF_UPDATE_SYMREF (1 << 12)
+
 /*
  * Bitmask of all of the flags that are allowed to be passed in to
  * ref_transaction_update() and friends:
  */
 #define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS                                  \
 	(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
-	 REF_SKIP_REFNAME_VERIFICATION)
+	 REF_SKIP_REFNAME_VERIFICATION | REF_UPDATE_SYMREF)
 
 /*
  * Add a reference update to transaction. `new_oid` is the value that
@@ -721,7 +726,7 @@ int ref_transaction_update(struct ref_transaction *transaction,
 			   const struct object_id *new_oid,
 			   const struct object_id *old_oid,
 			   unsigned int flags, const char *msg,
-			   struct strbuf *err);
+			   const char *symref, struct strbuf *err);
 
 /*
  * Add a reference creation to transaction. new_oid is the value that
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 3f0f9521cb..4dbe73c106 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1198,7 +1198,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(), &r->oid, NULL);
+			null_oid(), &r->oid, NULL, NULL);
 	if (ref_transaction_commit(transaction, &err))
 		goto cleanup;
 
@@ -1292,8 +1292,8 @@ static int files_pack_refs(struct ref_store *ref_store,
 		 * packed-refs transaction:
 		 */
 		if (ref_transaction_update(transaction, iter->refname,
-					   iter->oid, NULL,
-					   REF_NO_DEREF, NULL, &err))
+					   iter->oid, NULL, REF_NO_DEREF,
+					   NULL, NULL, &err))
 			die("failure preparing to create packed reference %s: %s",
 			    iter->refname, err.buf);
 
@@ -2323,7 +2323,7 @@ static int split_head_update(struct ref_update *update,
 			transaction, "HEAD",
 			update->flags | REF_LOG_ONLY | REF_NO_DEREF,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			update->msg, NULL);
 
 	/*
 	 * Add "HEAD". This insertion is O(N) in the transaction
@@ -2386,7 +2386,7 @@ static int split_symref_update(struct ref_update *update,
 	new_update = ref_transaction_add_update(
 			transaction, referent, new_flags,
 			&update->new_oid, &update->old_oid,
-			update->msg);
+			update->msg, NULL);
 
 	new_update->parent_update = update;
 
@@ -2777,7 +2777,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 					packed_transaction, update->refname,
 					REF_HAVE_NEW | REF_NO_DEREF,
 					&update->new_oid, NULL,
-					NULL);
+					NULL, NULL);
 		}
 	}
 
@@ -3062,7 +3062,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
 		ref_transaction_add_update(packed_transaction, update->refname,
 					   update->flags & ~REF_HAVE_OLD,
 					   &update->new_oid, &update->old_oid,
-					   NULL);
+					   NULL, NULL);
 	}
 
 	if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 56641aa57a..3fccf784d4 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -134,6 +134,12 @@ struct ref_update {
 	unsigned int type;
 	char *msg;
 
+	/*
+	 * If (flags & REF_UPDATE_SYMREF), we update the reference to be a
+	 * symbolic reference and the value is taken from this field.
+	 */
+	char *symref_target;
+
 	/*
 	 * If this ref_update was split off of a symref update via
 	 * split_symref_update(), then this member points at that
@@ -164,8 +170,8 @@ int ref_update_reject_duplicates(struct string_list *refnames,
 /*
  * Add a ref_update with the specified properties to transaction, and
  * return a pointer to the new object. This function does not verify
- * that refname is well-formed. new_oid and old_oid are only
- * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits,
+ * that refname is well-formed. new_oid, old_oid, symref are only
+ * dereferenced if the REF_HAVE_NEW, REF_HAVE_OLD and REF_UPDATE_SYMREF bits,
  * respectively, are set in flags.
  */
 struct ref_update *ref_transaction_add_update(
@@ -173,7 +179,7 @@ struct ref_update *ref_transaction_add_update(
 		const char *refname, unsigned int flags,
 		const struct object_id *new_oid,
 		const struct object_id *old_oid,
-		const char *msg);
+		const char *msg, const char *symref);
 
 /*
  * Transaction states.
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 9b53d42541..92f2803e90 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -884,7 +884,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 			new_update = ref_transaction_add_update(
 					transaction, "HEAD",
 					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
-					&u->new_oid, &u->old_oid, u->msg);
+					&u->new_oid, &u->old_oid, u->msg, NULL);
 			string_list_insert(&affected_refnames, new_update->refname);
 		}
 
@@ -963,7 +963,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
 				 */
 				new_update = ref_transaction_add_update(
 						transaction, referent.buf, new_flags,
-						&u->new_oid, &u->old_oid, u->msg);
+						&u->new_oid, &u->old_oid, u->msg, NULL);
 				new_update->parent_update = u;
 
 				/*
diff --git a/sequencer.c b/sequencer.c
index 4e14fa6541..668ea683ee 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -614,7 +614,7 @@ static int fast_forward_to(struct repository *r,
 	    ref_transaction_update(transaction, "HEAD",
 				   to, unborn && !is_rebase_i(opts) ?
 				   null_oid() : from,
-				   0, sb.buf, &err) ||
+				   0, sb.buf, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1232,7 +1232,7 @@ int update_head_with_reflog(const struct commit *old_head,
 	if (!transaction ||
 	    ref_transaction_update(transaction, "HEAD", new_head,
 				   old_head ? &old_head->object.oid : null_oid(),
-				   0, sb.buf, err) ||
+				   0, sb.buf, NULL, err) ||
 	    ref_transaction_commit(transaction, err)) {
 		ret = -1;
 	}
@@ -3750,7 +3750,7 @@ static int do_label(struct repository *r, const char *name, int len)
 		error(_("could not read HEAD"));
 		ret = -1;
 	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
-					  NULL, 0, msg.buf, &err) < 0 ||
+					  NULL, 0, msg.buf, NULL, &err) < 0 ||
 		   ref_transaction_commit(transaction, &err)) {
 		error("%s", err.buf);
 		ret = -1;
diff --git a/walker.c b/walker.c
index 65002a7220..33eef703ce 100644
--- a/walker.c
+++ b/walker.c
@@ -326,7 +326,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 		if (ref_transaction_update(transaction, refname.buf,
 					   oids + i, NULL, 0,
 					   msg ? msg : "fetch (unknown)",
-					   &err)) {
+					   NULL, &err)) {
 			error("%s", err.buf);
 			goto done;
 		}
-- 
2.43.GIT



^ permalink raw reply related	[relevance 8%]

* [PATCH 0/8] update-ref: add support for update-symref option
@ 2024-03-30 22:46  2% Karthik Nayak
  2024-03-30 22:46  8% ` [PATCH 4/8] refs: accept symref in `ref_transaction_add_update` Karthik Nayak
  2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
  0 siblings, 2 replies; 200+ results
From: Karthik Nayak @ 2024-03-30 22:46 UTC (permalink / raw)
  To: git; +Cc: ps, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

The 'git-update-ref(1)' command allows transactional reference updates.
But currently only supports regular reference updates. Meaning, if one
wants to update HEAD (symbolic ref) in a transaction, there is no tool
to do so.

One option to obtain transactional updates for the HEAD ref is to
manually create the HEAD.lock file and commit. This is intrusive, where
the user needs to mimic internal git behavior. Also, this only works
when using the files backend.

At GitLab, we've been using the manual process till date, to allow users
to set and change their default branch. But with the introduction of
reftables as a reference backend, this becomes a necessity to be solved
within git.

This patch series goes about introducing 'update-symref' command for
'git-update-ref(1)'. This command allows the user to create symref in a
transaction similar to the 'update' command of 'git-update-ref(1)'. This
command also works well with the existing 'no-deref' option.

We don't create a dedicated 'create-symref' since 'update-symref' can
also create symrefs. We don't create 'delete-symref', since the 'delete'
command can also delete symrefs.

Karthik Nayak (8):
  files-backend: extract out `create_symref_lock`
  reftable-backend: extract out `write_symref_with_log`
  reftable-backend: move `write_symref_with_log` up
  refs: accept symref in `ref_transaction_add_update`
  refs/files-backend: add support for symref updates
  refs/reftable-backend: add support for symref updates
  refs: add 'update-symref' command to 'update-ref'
  refs: support symrefs in 'reference-transaction' hook

 Documentation/git-update-ref.txt |  11 ++-
 Documentation/githooks.txt       |  13 ++-
 branch.c                         |   2 +-
 builtin/fast-import.c            |   6 +-
 builtin/fetch.c                  |   2 +-
 builtin/receive-pack.c           |   2 +-
 builtin/replace.c                |   2 +-
 builtin/tag.c                    |   2 +-
 builtin/update-ref.c             |  63 +++++++++++---
 refs.c                           |  37 ++++++---
 refs.h                           |   9 +-
 refs/files-backend.c             |  95 ++++++++++++++++-----
 refs/refs-internal.h             |  12 ++-
 refs/reftable-backend.c          | 136 ++++++++++++++++++-------------
 sequencer.c                      |   6 +-
 t/t0600-reffiles-backend.sh      |  30 +++++++
 t/t1400-update-ref.sh            | 127 +++++++++++++++++++++++++++++
 t/t1416-ref-transaction-hooks.sh |  27 ++++++
 walker.c                         |   2 +-
 19 files changed, 464 insertions(+), 120 deletions(-)

-- 
2.43.GIT



^ permalink raw reply	[relevance 2%]

* Re: [PATCH] advice: omit trailing whitespace
  2024-03-29 22:57  5% [PATCH] advice: omit trailing whitespace Junio C Hamano
  2024-03-29 23:23  0% ` Rubén Justo
@ 2024-03-29 23:35  0% ` Dragan Simic
  1 sibling, 0 replies; 200+ results
From: Dragan Simic @ 2024-03-29 23:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On 2024-03-29 23:57, Junio C Hamano wrote:
> Git tools all consistently encourage users to avoid whitespaces at
> the end of line by giving them features like "git diff --check" and
> "git am --whitespace=fix".  Make sure that the advice messages we
> give users avoid trailing whitespaces.  We shouldn't be wasting
> vertical screen real estate by adding blank lines in advice messages
> that are supposed to be concise hints, but as long as we write such
> blank line in our "hints", we should do it right.
> 
> A test that expects the current behaviour of leaving trailing
> whitespaces has been adjusted.
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Looking good to me.  Consistency is always good.

Reviewed-by: Dragan Simic <dsimic@manjaro.org>

> ---
>  advice.c          | 3 ++-
>  t/t3200-branch.sh | 4 ++--
>  2 files changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git c/advice.c w/advice.c
> index d19648b7f8..75111191ad 100644
> --- c/advice.c
> +++ w/advice.c
> @@ -105,8 +105,9 @@ static void vadvise(const char *advice, int
> display_instructions,
> 
>  	for (cp = buf.buf; *cp; cp = np) {
>  		np = strchrnul(cp, '\n');
> -		fprintf(stderr,	_("%shint: %.*s%s\n"),
> +		fprintf(stderr,	_("%shint:%s%.*s%s\n"),
>  			advise_get_color(ADVICE_COLOR_HINT),
> +			(np == cp) ? "" : " ",
>  			(int)(np - cp), cp,
>  			advise_get_color(ADVICE_COLOR_RESET));
>  		if (*np)
> diff --git c/t/t3200-branch.sh w/t/t3200-branch.sh
> index d3bbd00b81..ccfa6a720d 100755
> --- c/t/t3200-branch.sh
> +++ w/t/t3200-branch.sh
> @@ -1154,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and 
> advise' '
>  	hint: tracking ref '\''refs/heads/main'\'':
>  	hint:   ambi1
>  	hint:   ambi2
> -	hint: ''
> +	hint:
>  	hint: This is typically a configuration error.
> -	hint: ''
> +	hint:
>  	hint: To support setting up tracking branches, ensure that
>  	hint: different remotes'\'' fetch refspecs map into different
>  	hint: tracking namespaces.


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] advice: omit trailing whitespace
  2024-03-29 22:57  5% [PATCH] advice: omit trailing whitespace Junio C Hamano
@ 2024-03-29 23:23  0% ` Rubén Justo
  2024-03-31  6:17  0%   ` Rubén Justo
  2024-03-29 23:35  0% ` Dragan Simic
  1 sibling, 1 reply; 200+ results
From: Rubén Justo @ 2024-03-29 23:23 UTC (permalink / raw)
  To: Junio C Hamano, git

On Fri, Mar 29, 2024 at 03:57:06PM -0700, Junio C Hamano wrote:
> Git tools all consistently encourage users to avoid whitespaces at
> the end of line by giving them features like "git diff --check" and
> "git am --whitespace=fix".  Make sure that the advice messages we
> give users avoid trailing whitespaces.  We shouldn't be wasting
> vertical screen real estate by adding blank lines in advice messages
> that are supposed to be concise hints, but as long as we write such
> blank line in our "hints", we should do it right.
> 
> A test that expects the current behaviour of leaving trailing
> whitespaces has been adjusted.
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---

Yeah, ACK.  This is obviously a good thing.  I'll base my other series
in this.  Thanks.

>  advice.c          | 3 ++-
>  t/t3200-branch.sh | 4 ++--
>  2 files changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git c/advice.c w/advice.c
> index d19648b7f8..75111191ad 100644
> --- c/advice.c
> +++ w/advice.c
> @@ -105,8 +105,9 @@ static void vadvise(const char *advice, int display_instructions,
>  
>  	for (cp = buf.buf; *cp; cp = np) {
>  		np = strchrnul(cp, '\n');
> -		fprintf(stderr,	_("%shint: %.*s%s\n"),
> +		fprintf(stderr,	_("%shint:%s%.*s%s\n"),
>  			advise_get_color(ADVICE_COLOR_HINT),
> +			(np == cp) ? "" : " ",
>  			(int)(np - cp), cp,
>  			advise_get_color(ADVICE_COLOR_RESET));
>  		if (*np)
> diff --git c/t/t3200-branch.sh w/t/t3200-branch.sh
> index d3bbd00b81..ccfa6a720d 100755
> --- c/t/t3200-branch.sh
> +++ w/t/t3200-branch.sh
> @@ -1154,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
>  	hint: tracking ref '\''refs/heads/main'\'':
>  	hint:   ambi1
>  	hint:   ambi2
> -	hint: ''
> +	hint:
>  	hint: This is typically a configuration error.
> -	hint: ''
> +	hint:
>  	hint: To support setting up tracking branches, ensure that
>  	hint: different remotes'\'' fetch refspecs map into different
>  	hint: tracking namespaces.


A quick run tells me that this step also needs, I think:

--- >8 ---

diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index b41a47eb94..03bb4af7d6 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1780,7 +1780,7 @@ test_expect_success 'recursive tagging should give advice' '
        sed -e "s/|$//" <<-EOF >expect &&
        hint: You have created a nested tag. The object referred to by your new tag is
        hint: already a tag. If you meant to tag the object that it points to, use:
-       hint: |
+       hint:
        hint:   git tag -f nested annotated-v4.0^{}
        hint: Disable this message with "git config advice.nestedTag false"
        EOF




^ permalink raw reply related	[relevance 0%]

* [PATCH] advice: omit trailing whitespace
@ 2024-03-29 22:57  5% Junio C Hamano
  2024-03-29 23:23  0% ` Rubén Justo
  2024-03-29 23:35  0% ` Dragan Simic
  0 siblings, 2 replies; 200+ results
From: Junio C Hamano @ 2024-03-29 22:57 UTC (permalink / raw)
  To: git

Git tools all consistently encourage users to avoid whitespaces at
the end of line by giving them features like "git diff --check" and
"git am --whitespace=fix".  Make sure that the advice messages we
give users avoid trailing whitespaces.  We shouldn't be wasting
vertical screen real estate by adding blank lines in advice messages
that are supposed to be concise hints, but as long as we write such
blank line in our "hints", we should do it right.

A test that expects the current behaviour of leaving trailing
whitespaces has been adjusted.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 advice.c          | 3 ++-
 t/t3200-branch.sh | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git c/advice.c w/advice.c
index d19648b7f8..75111191ad 100644
--- c/advice.c
+++ w/advice.c
@@ -105,8 +105,9 @@ static void vadvise(const char *advice, int display_instructions,
 
 	for (cp = buf.buf; *cp; cp = np) {
 		np = strchrnul(cp, '\n');
-		fprintf(stderr,	_("%shint: %.*s%s\n"),
+		fprintf(stderr,	_("%shint:%s%.*s%s\n"),
 			advise_get_color(ADVICE_COLOR_HINT),
+			(np == cp) ? "" : " ",
 			(int)(np - cp), cp,
 			advise_get_color(ADVICE_COLOR_RESET));
 		if (*np)
diff --git c/t/t3200-branch.sh w/t/t3200-branch.sh
index d3bbd00b81..ccfa6a720d 100755
--- c/t/t3200-branch.sh
+++ w/t/t3200-branch.sh
@@ -1154,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
 	hint: tracking ref '\''refs/heads/main'\'':
 	hint:   ambi1
 	hint:   ambi2
-	hint: ''
+	hint:
 	hint: This is typically a configuration error.
-	hint: ''
+	hint:
 	hint: To support setting up tracking branches, ensure that
 	hint: different remotes'\'' fetch refspecs map into different
 	hint: tracking namespaces.


^ permalink raw reply related	[relevance 5%]

* [PATCH v2 4/5] doc: git-clone: apply new documentation formatting guidelines
  2024-03-29 11:19  2% ` [PATCH v2 0/5] Doc new guidelines Jean-Noël Avila via GitGitGadget
@ 2024-03-29 11:19  6%   ` Jean-Noël Avila via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Jean-Noël Avila via GitGitGadget @ 2024-03-29 11:19 UTC (permalink / raw)
  To: git
  Cc: Martin Ågren, Jeff King, Eric Sunshine, Jean-Noël Avila,
	Jean-Noël Avila

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/clone.txt |   6 +-
 Documentation/git-clone.txt    | 120 ++++++++++++++++-----------------
 Documentation/urls.txt         |  44 ++++++------
 3 files changed, 86 insertions(+), 84 deletions(-)

diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
index d037b57f729..71a967f262d 100644
--- a/Documentation/config/clone.txt
+++ b/Documentation/config/clone.txt
@@ -1,13 +1,13 @@
-clone.defaultRemoteName::
+`clone.defaultRemoteName`::
 	The name of the remote to create when cloning a repository.  Defaults to
 	`origin`, and can be overridden by passing the `--origin` command-line
 	option to linkgit:git-clone[1].
 
-clone.rejectShallow::
+`clone.rejectShallow`::
 	Reject cloning a repository if it is a shallow one; this can be overridden by
 	passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
 
-clone.filterSubmodules::
+`clone.filterSubmodules`::
 	If a partial clone filter is provided (see `--filter` in
 	linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
 	the filter to submodules.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index f90977a8519..5de18de2ab8 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,15 +9,15 @@ git-clone - Clone a repository into a new directory
 SYNOPSIS
 --------
 [verse]
-'git clone' [--template=<template-directory>]
-	  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
-	  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--dissociate] [--separate-git-dir <git-dir>]
-	  [--depth <depth>] [--[no-]single-branch] [--no-tags]
-	  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
-	  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
-	  [--filter=<filter> [--also-filter-submodules]] [--] <repository>
-	  [<directory>]
+`git clone` [++--template=++__<template-directory>__]
+	  [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
+	  [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
+	  [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
+	  [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
+	  [++--recurse-submodules++[++=++__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
+	  [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
+	  [++--filter=++__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
+	  [_<directory>_]
 
 DESCRIPTION
 -----------
@@ -31,7 +31,7 @@ currently active branch.
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
 arguments will in addition merge the remote master branch into the
-current master branch, if any (this is untrue when "--single-branch"
+current master branch, if any (this is untrue when `--single-branch`
 is given; see below).
 
 This default configuration is achieved by creating references to
@@ -42,12 +42,12 @@ configuration variables.
 
 OPTIONS
 -------
--l::
---local::
+`-l`::
+`--local`::
 	When the repository to clone from is on a local machine,
 	this flag bypasses the normal "Git aware" transport
 	mechanism and clones the repository by making a copy of
-	HEAD and everything under objects and refs directories.
+	`HEAD` and everything under objects and refs directories.
 	The files under `.git/objects/` directory are hardlinked
 	to save space when possible.
 +
@@ -67,14 +67,14 @@ links.
 source repository, similar to running `cp -r src dst` while modifying
 `src`.
 
---no-hardlinks::
+`--no-hardlinks`::
 	Force the cloning process from a repository on a local
 	filesystem to copy the files under the `.git/objects`
 	directory instead of using hardlinks. This may be desirable
 	if you are trying to make a back-up of your repository.
 
--s::
---shared::
+`-s`::
+`--shared`::
 	When the repository to clone is on the local machine,
 	instead of using hard links, automatically setup
 	`.git/objects/info/alternates` to share the objects
@@ -101,7 +101,7 @@ If you want to break the dependency of a repository cloned with `--shared` on
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
---reference[-if-able] <repository>::
+`--reference`[`-if-able`] _<repository>_::
 	If the reference _<repository>_ is on the local machine,
 	automatically setup `.git/objects/info/alternates` to
 	obtain objects from the reference _<repository>_.  Using
@@ -115,7 +115,7 @@ objects from the source repository into a pack in the cloned repository.
 *NOTE*: see the NOTE for the `--shared` option, and also the
 `--dissociate` option.
 
---dissociate::
+`--dissociate`::
 	Borrow the objects from reference repositories specified
 	with the `--reference` options only to reduce network
 	transfer, and stop borrowing from them after a clone is made
@@ -126,43 +126,43 @@ objects from the source repository into a pack in the cloned repository.
 	same repository, and this option can be used to stop the
 	borrowing.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Operate quietly.  Progress is not reported to the standard
 	error stream.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Run verbosely. Does not affect the reporting of progress status
 	to the standard error stream.
 
---progress::
+`--progress`::
 	Progress status is reported on the standard error stream
 	by default when it is attached to a terminal, unless `--quiet`
 	is specified. This flag forces progress status even if the
 	standard error stream is not directed to a terminal.
 
---server-option=<option>::
+++--server-option=++__<option>__::
 	Transmit the given string to the server when communicating using
 	protocol version 2.  The given string must not contain a NUL or LF
 	character.  The server's handling of server options, including
 	unknown ones, is server-specific.
-	When multiple `--server-option=<option>` are given, they are all
+	When multiple ++--server-option=++__<option>__ are given, they are all
 	sent to the other side in the order listed on the command line.
 
--n::
---no-checkout::
+`-n`::
+`--no-checkout`::
 	No checkout of HEAD is performed after the clone is complete.
 
---[no-]reject-shallow::
+`--`[`no-`]`reject-shallow`::
 	Fail if the source repository is a shallow repository.
 	The `clone.rejectShallow` configuration variable can be used to
 	specify the default.
 
---bare::
+`--bare`::
 	Make a 'bare' Git repository.  That is, instead of
 	creating _<directory>_ and placing the administrative
-	files in `<directory>/.git`, make the _<directory>_
+	files in _<directory>_`/.git`, make the _<directory>_
 	itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
 	because there is nowhere to check out the working tree.
 	Also the branch heads at the remote are copied directly
@@ -171,28 +171,28 @@ objects from the source repository into a pack in the cloned repository.
 	used, neither remote-tracking branches nor the related
 	configuration variables are created.
 
---sparse::
+`--sparse`::
 	Employ a sparse-checkout, with only files in the toplevel
 	directory initially being present.  The
 	linkgit:git-sparse-checkout[1] command can be used to grow the
 	working directory as needed.
 
---filter=<filter-spec>::
+++--filter=++__<filter-spec>__::
 	Use the partial clone feature and request that the server sends
 	a subset of reachable objects according to a given object filter.
 	When using `--filter`, the supplied _<filter-spec>_ is used for
 	the partial clone filter. For example, `--filter=blob:none` will
 	filter out all blobs (file contents) until needed by Git. Also,
-	`--filter=blob:limit=<size>` will filter out all blobs of size
+	++--filter=blob:limit=++__<size>__ will filter out all blobs of size
 	at least _<size>_. For more details on filter specifications, see
 	the `--filter` option in linkgit:git-rev-list[1].
 
---also-filter-submodules::
+`--also-filter-submodules`::
 	Also apply the partial clone filter to any submodules in the repository.
 	Requires `--filter` and `--recurse-submodules`. This can be turned on by
 	default by setting the `clone.filterSubmodules` config option.
 
---mirror::
+`--mirror`::
 	Set up a mirror of the source repository.  This implies `--bare`.
 	Compared to `--bare`, `--mirror` not only maps local branches of the
 	source to local branches of the target, it maps all refs (including
@@ -200,14 +200,14 @@ objects from the source repository into a pack in the cloned repository.
 	that all these refs are overwritten by a `git remote update` in the
 	target repository.
 
--o <name>::
---origin <name>::
+`-o` _<name>_::
+`--origin` _<name>_::
 	Instead of using the remote name `origin` to keep track of the upstream
 	repository, use _<name>_.  Overrides `clone.defaultRemoteName` from the
 	config.
 
--b <name>::
---branch <name>::
+`-b` _<name>_::
+`--branch` _<name>_::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to _<name>_ branch
 	instead. In a non-bare repository, this is the branch that will
@@ -215,18 +215,18 @@ objects from the source repository into a pack in the cloned repository.
 	`--branch` can also take tags and detaches the HEAD at that commit
 	in the resulting repository.
 
--u <upload-pack>::
---upload-pack <upload-pack>::
+`-u` _<upload-pack>_::
+`--upload-pack` _<upload-pack>_::
 	When given, and the repository to clone from is accessed
 	via ssh, this specifies a non-default path for the command
 	run on the other end.
 
---template=<template-directory>::
+++--template=++__<template-directory>__::
 	Specify the directory from which templates will be used;
 	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
--c <key>=<value>::
---config <key>=<value>::
+`-c` __<key>__++=++__<value>__::
+`--config` __<key>__++=++__<value>__::
 	Set a configuration variable in the newly-created repository;
 	this takes effect immediately after the repository is
 	initialized, but before the remote history is fetched or any
@@ -239,25 +239,25 @@ objects from the source repository into a pack in the cloned repository.
 Due to limitations of the current implementation, some configuration
 variables do not take effect until after the initial fetch and checkout.
 Configuration variables known to not take effect are:
-`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
+++remote.++__<name>__++.mirror++ and ++remote.++__<name>__++.tagOpt++.  Use the
 corresponding `--mirror` and `--no-tags` options instead.
 
---depth <depth>::
+`--depth` _<depth>_::
 	Create a 'shallow' clone with a history truncated to the
 	specified number of commits. Implies `--single-branch` unless
 	`--no-single-branch` is given to fetch the histories near the
 	tips of all branches. If you want to clone submodules shallowly,
 	also pass `--shallow-submodules`.
 
---shallow-since=<date>::
+++--shallow-since=++__<date>__::
 	Create a shallow clone with a history after the specified time.
 
---shallow-exclude=<revision>::
+++--shallow-exclude=++__<revision>__::
 	Create a shallow clone with a history, excluding commits
 	reachable from a specified remote branch or tag.  This option
 	can be specified multiple times.
 
---[no-]single-branch::
+`--`[`no-`]`single-branch`::
 	Clone only the history leading to the tip of a single branch,
 	either specified by the `--branch` option or the primary
 	branch remote's `HEAD` points at.
@@ -267,7 +267,7 @@ corresponding `--mirror` and `--no-tags` options instead.
 	branch when `--single-branch` clone was made, no remote-tracking
 	branch is created.
 
---no-tags::
+`--no-tags`::
 	Don't clone any tags, and set
 	`remote.<remote>.tagOpt=--no-tags` in the config, ensuring
 	that future `git pull` and `git fetch` operations won't follow
@@ -279,7 +279,7 @@ maintain a branch with no references other than a single cloned
 branch. This is useful e.g. to maintain minimal clones of the default
 branch of some repository for search indexing.
 
---recurse-submodules[=<pathspec>]::
+`--recurse-submodules`[`=`{empty}__<pathspec>__]::
 	After the clone is created, initialize and clone submodules
 	within based on the provided _<pathspec>_.  If no _=<pathspec>_ is
 	provided, all submodules are initialized and cloned.
@@ -295,46 +295,46 @@ the clone is finished. This option is ignored if the cloned repository does
 not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`,
 or `--mirror` is given)
 
---[no-]shallow-submodules::
+`--`[`no-`]`shallow-submodules`::
 	All submodules which are cloned will be shallow with a depth of 1.
 
---[no-]remote-submodules::
+`--`[`no-`]`remote-submodules`::
 	All submodules which are cloned will use the status of the submodule's
 	remote-tracking branch to update the submodule, rather than the
 	superproject's recorded SHA-1. Equivalent to passing `--remote` to
 	`git submodule update`.
 
---separate-git-dir=<git-dir>::
+`--separate-git-dir=`{empty}__<git-dir>__::
 	Instead of placing the cloned repository where it is supposed
 	to be, place the cloned repository at the specified directory,
 	then make a filesystem-agnostic Git symbolic link to there.
 	The result is Git repository can be separated from working
 	tree.
 
---ref-format=<ref-format>::
+`--ref-format=`{empty}__<ref-format>__::
 
 Specify the given ref storage format for the repository. The valid values are:
 +
 include::ref-storage-format.txt[]
 
--j <n>::
---jobs <n>::
+`-j` _<n>_::
+`--jobs` _<n>_::
 	The number of submodules fetched at the same time.
 	Defaults to the `submodule.fetchJobs` option.
 
-<repository>::
+_<repository>_::
 	The (possibly remote) _<repository>_ to clone from.  See the
 	<<URLS,GIT URLS>> section below for more information on specifying
 	repositories.
 
-<directory>::
+_<directory>_::
 	The name of a new directory to clone into.  The "humanish"
 	part of the source repository is used if no _<directory>_ is
 	explicitly given (`repo` for `/path/to/repo.git` and `foo`
 	for `host.xz:foo/.git`).  Cloning into an existing directory
 	is only allowed if the directory is empty.
 
---bundle-uri=<uri>::
+`--bundle-uri=`{empty}__<uri>__::
 	Before fetching from the remote, fetch a bundle from the given
 	_<uri>_ and unbundle the data into the local repository. The refs
 	in the bundle will be stored under the hidden `refs/bundle/*`
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 0b9e0c4302d..7cec85aef17 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -15,14 +15,14 @@ should be used with caution on unsecured networks.
 
 The following syntaxes may be used with them:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}:__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++http++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++ftp++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
 
 An alternative scp-like syntax may also be used with the ssh protocol:
 
-- {startsb}user@{endsb}host.xz:path/to/repo.git/
+- {startsb}__<user>__++@++{endsb}__<host>__++:/++__<path-to-git-repo>__
 
 This syntax is only recognized if there are no slashes before the
 first colon. This helps differentiate a local path that contains a
@@ -30,17 +30,17 @@ colon. For example the local path `foo:bar` could be specified as an
 absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
 url.
 
-The ssh and git protocols additionally support ~username expansion:
+The ssh and git protocols additionally support ++~++__<username>__ expansion:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- {startsb}__<user>__++@++{endsb}__<host>__++:~++__<user>__++/++__<path-to-git-repo>__
 
 For local repositories, also supported by Git natively, the following
 syntaxes may be used:
 
-- /path/to/repo.git/
-- \file:///path/to/repo.git/
+- `/path/to/repo.git/`
+- ++file:///path/to/repo.git/++
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
@@ -57,11 +57,11 @@ endif::git-clone[]
 accept a suitable bundle file. See linkgit:git-bundle[1].
 
 When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the `remote-<transport>` remote helper, if one
+attempts to use the `remote-`{empty}__<transport>__ remote helper, if one
 exists. To explicitly request a remote helper, the following syntax
 may be used:
 
-- _<transport>_::_<address>_
+- _<transport>_::__<address>__
 
 where _<address>_ may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
@@ -72,10 +72,11 @@ you want to use a different format for them (such that the URLs you
 use will be rewritten into URLs that work), you can create a
 configuration section of the form:
 
-------------
-	[url "<actual-url-base>"]
-		insteadOf = <other-url-base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		insteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
@@ -91,10 +92,11 @@ rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
 If you want to rewrite URLs for push only, you can create a
 configuration section of the form:
 
-------------
-	[url "<actual-url-base>"]
-		pushInsteadOf = <other-url-base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		pushInsteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
-- 
gitgitgadget



^ permalink raw reply related	[relevance 6%]

* [PATCH v2 0/5] Doc new guidelines
  2024-03-24 22:18  4% [PATCH 0/4] Doc new guidelines Jean-Noël Avila via GitGitGadget
  2024-03-24 22:18  6% ` [PATCH 4/4] doc: git-clone: apply new documentation guidelines Jean-Noël Avila via GitGitGadget
@ 2024-03-29 11:19  2% ` Jean-Noël Avila via GitGitGadget
  2024-03-29 11:19  6%   ` [PATCH v2 4/5] doc: git-clone: apply new documentation formatting guidelines Jean-Noël Avila via GitGitGadget
  1 sibling, 1 reply; 200+ results
From: Jean-Noël Avila via GitGitGadget @ 2024-03-29 11:19 UTC (permalink / raw)
  To: git; +Cc: Martin Ågren, Jeff King, Eric Sunshine, Jean-Noël Avila

This version is a simplification of the markup. The behavior is variable,
depending on versions of asciidoc[tor], but at least, it works both on my
station and in github-ci. @Martin, please check on your platform.

Changes since v1:

 * use unconstrained markup ++ for verbatim stuck to emphasis, instead of
   {empty} trickery
 * document and apply markup on man page references
 * split git-clone commit into markup and non-autoreference

Jean-Noël Avila (5):
  doc: rework CodingGuidelines with new formatting rules
  doc: allow literal and emphasis format in doc vs help tests
  doc: git-init: apply new documentation formatting guidelines
  doc: git-clone: apply new documentation formatting guidelines
  doc: git-clone: do not autoreference the manpage in itself

 Documentation/CodingGuidelines | 153 ++++++++++++++++++---------------
 Documentation/config/clone.txt |  20 +++--
 Documentation/config/init.txt  |   4 +-
 Documentation/git-clone.txt    | 120 +++++++++++++-------------
 Documentation/git-init.txt     |  56 ++++++------
 Documentation/urls.txt         |  44 +++++-----
 t/t0450-txt-doc-vs-help.sh     |   4 +-
 7 files changed, 216 insertions(+), 185 deletions(-)


base-commit: 11c821f2f2a31e70fb5cc449f9a29401c333aad2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1702%2Fjnavila%2Fdoc_new_Guidelines-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1702/jnavila/doc_new_Guidelines-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1702

Range-diff vs v1:

 1:  d08e8bfd9bc ! 1:  8a25ab1c3b6 doc: rework CodingGuidelines with new formatting rules
     @@ Documentation/CodingGuidelines: Writing Documentation:
      +   `.git/config`
      +   `GIT_DIR`
      +   `HEAD`
     ++   `umask`(2)
      +
      + An environment variable must be prefixed with "$" only when referring to its
      + value and not when referring to the variable itself, in this case there is
     @@ Documentation/CodingGuidelines: Writing Documentation:
      +   _<key-id>_
      +
      + When literal and placeholders are mixed, each markup is applied for
     -+ each sub-entity. If they are stuck, a special markup, with an {empty}
     -+ entity is needed:
     ++ each sub-entity. If they are stuck, a special markup, called
     ++ unconstrained formatting is required.
     ++ Unconstrained formating for placeholders is __<like-this>__
     ++ Unconstrained formatting for literal formatting is ++like this++
      +   `--jobs` _<n>_
     -+   `--sort=`{empty}__<key>__
     -+   __<directory>__{empty}`/.git`
     -+   `remote.`{empty}__<name>__{empty}`.mirror`
     ++   ++--sort=++__<key>__
     ++   __<directory>__++/.git++
     ++   ++remote.++__<name>__++.mirror++
      +
     -+Synopsis Syntax
     ++ caveat: ++ unconstrained format is not verbatim and may expand
     ++ content. Use Asciidoc escapes inside them.
       
      - When a placeholder is cited in text paragraph, it is enclosed in angle
      - brackets to remind the reader the reference in the synopsis section.
      - For better visibility, the placeholder is typeset in italics:
      -   The _<file>_ to be added.
     ++Synopsis Syntax
     ++
      + Syntax grammar is formatted neither as literal nor as placeholder.
      +
      + A few commented examples follow to provide reference when writing or
     @@ Documentation/CodingGuidelines: Writing Documentation:
          (Zero or more of <file>.)
       
      -   --exec-path[=<path>]
     -+   `--exec-path`[`=`{empty}__<path>__]
     ++   ++--exec-path++[++=++__<path>__]
          (Option with an optional argument.  Note that the "=" is inside the
          brackets.)
       
     @@ Documentation/CodingGuidelines: Writing Documentation:
        alternate arguments of an option:
      -    Do: --track[=(direct|inherit)]
      -    Don't: --track[=(direct | inherit)]
     -+    Do: `--track`[`=`(`direct`|`inherit`)]`
     -+    Don't: `--track`[`=`(`direct` | `inherit`)]
     ++    Do: ++--track++[++=++(`direct`|`inherit`)]`
     ++    Don't: ++--track++[++=++(`direct` | `inherit`)]
       
        Parentheses are used for grouping:
      -   [(<rev> | <range>)...]
 2:  202ed891463 ! 2:  1a4feff2aea doc: allow literal and emphasis format in doc vs help tests
     @@ t/t0450-txt-doc-vs-help.sh: txt_to_synopsis () {
       			/^$/d;
       			/^\[verse\]$/d;
      -
     -+			s/{empty}\|_\|`//g;
     ++			s/_//g;
     ++			s/++//g;
     ++			s/`//g;
       			s/{litdd}/--/g;
       			s/'\''\(git[ a-z-]*\)'\''/\1/g;
       
 3:  310f09fc81c ! 3:  ad7986e4c39 doc: git-init: apply new documentation formatting guidelines
     @@ Documentation/git-init.txt: git-init - Create an empty Git repository or reiniti
      -	  [--ref-format=<format>]
      -	  [-b <branch-name> | --initial-branch=<branch-name>]
      -	  [--shared[=<permissions>]] [<directory>]
     -+`git init` [`-q` | `--quiet`] [`--bare`] [`--template=`{empty}__<template-directory>__]
     -+	  [`--separate-git-dir` _<git-dir>_] [`--object-format=`{empty}__<format>__]
     -+	  [`--ref-format=`{empty}__<format>__]
     -+	  [`-b` _<branch-name>_ | `--initial-branch=`{empty}__<branch-name>__]
     -+	  [`--shared`[`=`{empty}__<permissions>__]] [_<directory>_]
     ++`git init` [`-q` | `--quiet`] [`--bare`] [++--template=++__<template-directory>__]
     ++	  [`--separate-git-dir` _<git-dir>_] [++--object-format=++__<format>__]
     ++	  [++--ref-format=++__<format>__]
     ++	  [`-b` _<branch-name>_ | ++--initial-branch=++__<branch-name>__]
     ++	  [++--shared++[++=++__<permissions>__]] [_<directory>_]
       
       
       DESCRIPTION
     @@ Documentation/git-init.txt: the repository to another place if `--separate-git-d
       current working directory.
       
      ---object-format=<format>::
     -+`--object-format=`{empty}__<format>__::
     ++++--object-format=++__<format>__::
       
       Specify the given object _<format>_ (hash algorithm) for the repository.  The valid
       values are `sha1` and (if enabled) `sha256`.  `sha1` is the default.
     @@ Documentation/git-init.txt: the repository to another place if `--separate-git-d
       include::object-format-disclaimer.txt[]
       
      ---ref-format=<format>::
     -+`--ref-format=`{empty}__<format>__::
     ++++--ref-format=++__<format>__::
       
       Specify the given ref storage _<format>_ for the repository. The valid values are:
       +
       include::ref-storage-format.txt[]
       
      ---template=<template-directory>::
     -+`--template=`{empty}__<template-directory>__::
     ++++--template=++__<template-directory>__::
       
       Specify the directory from which templates will be used.  (See the "TEMPLATE
       DIRECTORY" section below.)
       
      ---separate-git-dir=<git-dir>::
     -+`--separate-git-dir=`{empty}__<git-dir>__::
     ++++--separate-git-dir=++__<git-dir>__::
       
       Instead of initializing the repository as a directory to either `$GIT_DIR` or
       `./.git/`, create a text file there containing the path to the actual
     @@ Documentation/git-init.txt: repository.
      --b <branch-name>::
      ---initial-branch=<branch-name>::
      +`-b` _<branch-name>_::
     -+`--initial-branch=`{empty}__<branch-name>__::
     ++++--initial-branch=++__<branch-name>__::
       
       Use _<branch-name>_ for the initial branch in the newly created
       repository.  If not specified, fall back to the default name (currently
     @@ Documentation/git-init.txt: repository.
       customized via the `init.defaultBranch` configuration variable).
       
      ---shared[=(false|true|umask|group|all|world|everybody|<perm>)]::
     -+`--shared`[`=`(`false`|`true`|`umask`|`group`|`all`|`world`|`everybody`|_<perm>_)]::
     ++++--shared++[++=++(`false`|`true`|`umask`|`group`|`all`|`world`|`everybody`|_<perm>_)]::
       
       Specify that the Git repository is to be shared amongst several users.  This
       allows users belonging to the same group to push into that
     -@@ Documentation/git-init.txt: The option can have the following values, defaulting to `group` if no value
     + repository.  When specified, the config variable `core.sharedRepository` is
     + set so that files and directories under `$GIT_DIR` are created with the
     + requested permissions.  When not specified, Git will use permissions reported
     +-by `umask(2)`.
     ++by `umask`(2).
     + +
     + The option can have the following values, defaulting to `group` if no value
       is given:
       +
       --
     @@ Documentation/git-init.txt: The option can have the following values, defaulting
      +`umask`::
      +`false`::
       
     - Use permissions reported by umask(2). The default, when `--shared` is not
     +-Use permissions reported by umask(2). The default, when `--shared` is not
     ++Use permissions reported by `umask`(2). The default, when `--shared` is not
       specified.
       
      -group::
     @@ Documentation/git-init.txt: The option can have the following values, defaulting
      +`group`::
      +`true`::
       
     - Make the repository group-writable, (and g+sx, since the git group may not be
     +-Make the repository group-writable, (and g+sx, since the git group may not be
     ++Make the repository group-writable, (and `g+sx`, since the git group may not be
       the primary group of all users). This is used to loosen the permissions of an
     -@@ Documentation/git-init.txt: permission bits (e.g. if umask is `0022`, using `group` will not remove read
     +-otherwise safe umask(2) value. Note that the umask still applies to the other
     ++otherwise safe `umask`(2) value. Note that the umask still applies to the other
     + permission bits (e.g. if umask is `0022`, using `group` will not remove read
       privileges from other (non-group) users). See `0xxx` for how to exactly specify
       the repository permissions.
       
     @@ Documentation/git-init.txt: permission bits (e.g. if umask is `0022`, using `gro
      +_<perm>_::
       
       _<perm>_ is a 3-digit octal number prefixed with `0` and each file
     - will have mode _<perm>_. _<perm>_ will override users'`umask(2)`
     +-will have mode _<perm>_. _<perm>_ will override users'`umask(2)`
     ++will have mode _<perm>_. _<perm>_ will override users' `umask`(2)
     + value (and not only loosen permissions as `group` and `all`
     + do). `0640` will create a repository which is group-readable, but
     + not group-writable or accessible to others. `0660` will create a repo
 4:  5ae83d3f799 ! 4:  54c2012429d doc: git-clone: apply new documentation guidelines
     @@ Metadata
      Author: Jean-Noël Avila <jn.avila@free.fr>
      
       ## Commit message ##
     -    doc: git-clone: apply new documentation guidelines
     -
     -    Heavily apply literal and placeholder markup everywhere.
     +    doc: git-clone: apply new documentation formatting guidelines
      
          Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
      
     @@ Documentation/config/clone.txt
      -clone.defaultRemoteName::
      +`clone.defaultRemoteName`::
       	The name of the remote to create when cloning a repository.  Defaults to
     --	`origin`, and can be overridden by passing the `--origin` command-line
     -+	`origin`.
     -+ifdef::git-clone[]
     -+	It can be overridden by passing the `--origin` command-line
     -+	option.
     -+endif::[]
     -+ifndef::git-clone[]
     -+	It can be overridden by passing the `--origin` command-line
     + 	`origin`, and can be overridden by passing the `--origin` command-line
       	option to linkgit:git-clone[1].
     -+endif::[]
     -+`clone.rejectShallow`::
     -+	Reject cloning a repository if it is a shallow one. This can be overridden by
     -+	passing the `--reject-shallow` option on the command line.
     -+ifndef::git-clone[]
     -+	See linkgit:git-clone[1]
     -+endif::[]
       
      -clone.rejectShallow::
     --	Reject cloning a repository if it is a shallow one; this can be overridden by
     --	passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
     --
     ++`clone.rejectShallow`::
     + 	Reject cloning a repository if it is a shallow one; this can be overridden by
     + 	passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
     + 
      -clone.filterSubmodules::
      +`clone.filterSubmodules`::
       	If a partial clone filter is provided (see `--filter` in
     @@ Documentation/git-clone.txt: git-clone - Clone a repository into a new directory
      -	  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
      -	  [--filter=<filter> [--also-filter-submodules]] [--] <repository>
      -	  [<directory>]
     -+`git clone` [`--template=`{empty}__<template-directory>__]
     ++`git clone` [++--template=++__<template-directory>__]
      +	  [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
      +	  [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
      +	  [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
      +	  [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
     -+	  [`--recurse-submodules`[`=`{empty}__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
     ++	  [++--recurse-submodules++[++=++__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
      +	  [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
     -+	  [`--filter=`{empty}__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
     ++	  [++--filter=++__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
      +	  [_<directory>_]
       
       DESCRIPTION
     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t
       	standard error stream is not directed to a terminal.
       
      ---server-option=<option>::
     -+`--server-option=`{empty}__<option>__::
     ++++--server-option=++__<option>__::
       	Transmit the given string to the server when communicating using
       	protocol version 2.  The given string must not contain a NUL or LF
       	character.  The server's handling of server options, including
       	unknown ones, is server-specific.
      -	When multiple `--server-option=<option>` are given, they are all
     -+	When multiple `--server-option=`{empty}__<option>__ are given, they are all
     ++	When multiple ++--server-option=++__<option>__ are given, they are all
       	sent to the other side in the order listed on the command line.
       
      --n::
     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t
       	working directory as needed.
       
      ---filter=<filter-spec>::
     -+`--filter=`{empty}__<filter-spec>__::
     ++++--filter=++__<filter-spec>__::
       	Use the partial clone feature and request that the server sends
       	a subset of reachable objects according to a given object filter.
       	When using `--filter`, the supplied _<filter-spec>_ is used for
       	the partial clone filter. For example, `--filter=blob:none` will
       	filter out all blobs (file contents) until needed by Git. Also,
      -	`--filter=blob:limit=<size>` will filter out all blobs of size
     -+	`--filter=blob:limit=`{empty}__<size>__ will filter out all blobs of size
     ++	++--filter=blob:limit=++__<size>__ will filter out all blobs of size
       	at least _<size>_. For more details on filter specifications, see
       	the `--filter` option in linkgit:git-rev-list[1].
       
     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t
       	run on the other end.
       
      ---template=<template-directory>::
     -+`--template=`{empty}__<template-directory>__::
     ++++--template=++__<template-directory>__::
       	Specify the directory from which templates will be used;
       	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
       
      --c <key>=<value>::
      ---config <key>=<value>::
     -+`-c` __<key>__{empty}`=`{empty}__<value>__::
     -+`--config` __<key>__{empty}`=`{empty}__<value>__::
     ++`-c` __<key>__++=++__<value>__::
     ++`--config` __<key>__++=++__<value>__::
       	Set a configuration variable in the newly-created repository;
       	this takes effect immediately after the repository is
       	initialized, but before the remote history is fetched or any
     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t
       variables do not take effect until after the initial fetch and checkout.
       Configuration variables known to not take effect are:
      -`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
     -+`remote.`{empty}__<name>__{empty}`.mirror` and `remote.`{empty}__<name>__{empty}`.tagOpt`.  Use the
     ++++remote.++__<name>__++.mirror++ and ++remote.++__<name>__++.tagOpt++.  Use the
       corresponding `--mirror` and `--no-tags` options instead.
       
      ---depth <depth>::
     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t
       	also pass `--shallow-submodules`.
       
      ---shallow-since=<date>::
     -+`--shallow-since=`{empty}__<date>__::
     ++++--shallow-since=++__<date>__::
       	Create a shallow clone with a history after the specified time.
       
      ---shallow-exclude=<revision>::
     -+`--shallow-exclude=`{empty}__<revision>__::
     ++++--shallow-exclude=++__<revision>__::
       	Create a shallow clone with a history, excluding commits
       	reachable from a specified remote branch or tag.  This option
       	can be specified multiple times.
     @@ Documentation/git-clone.txt: the clone is finished. This option is ignored if th
       	in the bundle will be stored under the hidden `refs/bundle/*`
      
       ## Documentation/urls.txt ##
     +@@ Documentation/urls.txt: should be used with caution on unsecured networks.
     + 
     + The following syntaxes may be used with them:
     + 
     +-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
     +-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
     +-- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
     +-- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
     ++- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
     ++- ++git://++__<host>__{startsb}:__<port>__{endsb}++/++__<path-to-git-repo>__
     ++- ++http++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
     ++- ++ftp++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
     + 
     + An alternative scp-like syntax may also be used with the ssh protocol:
     + 
     +-- {startsb}user@{endsb}host.xz:path/to/repo.git/
     ++- {startsb}__<user>__++@++{endsb}__<host>__++:/++__<path-to-git-repo>__
     + 
     + This syntax is only recognized if there are no slashes before the
     + first colon. This helps differentiate a local path that contains a
     +@@ Documentation/urls.txt: colon. For example the local path `foo:bar` could be specified as an
     + absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
     + url.
     + 
     +-The ssh and git protocols additionally support ~username expansion:
     ++The ssh and git protocols additionally support ++~++__<username>__ expansion:
     + 
     +-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
     +-- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
     +-- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
     ++- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
     ++- ++git://++__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
     ++- {startsb}__<user>__++@++{endsb}__<host>__++:~++__<user>__++/++__<path-to-git-repo>__
     + 
     + For local repositories, also supported by Git natively, the following
     + syntaxes may be used:
     + 
     +-- /path/to/repo.git/
     +-- \file:///path/to/repo.git/
     ++- `/path/to/repo.git/`
     ++- ++file:///path/to/repo.git/++
     + 
     + ifndef::git-clone[]
     + These two syntaxes are mostly equivalent, except when cloning, when
      @@ Documentation/urls.txt: endif::git-clone[]
       accept a suitable bundle file. See linkgit:git-bundle[1].
       
 -:  ----------- > 5:  38bd78c92eb doc: git-clone: do not autoreference the manpage in itself

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* [PATCH v3 0/3] reftable/stack: use geometric table compaction
  2024-03-21 22:40  2% ` [PATCH v2 0/3] " Justin Tobler via GitGitGadget
@ 2024-03-29  4:16  3%   ` Justin Tobler via GitGitGadget
  2024-04-03  0:20  2%     ` [PATCH v4 0/2] " Justin Tobler via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Justin Tobler via GitGitGadget @ 2024-03-29  4:16 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Karthik Nayak, Justin Tobler

Hello again,

This is the third version my patch series that refactors the reftable
compaction strategy to instead follow a geometric sequence. Changes compared
to v2:

 * Added test to validate the GIT_TEST_REFTABLE_NO_AUTOCOMPACTION
   environment variable works as expected.
 * Added additional clarifying comments and examples to explain how the new
   compaction strategy works.
 * Removed outdated comment from stack_test.c test

Thanks for taking a look!

-Justin

Justin Tobler (3):
  reftable/stack: add env to disable autocompaction
  reftable/stack: use geometric table compaction
  reftable/stack: make segment end inclusive

 reftable/stack.c           | 124 ++++++++++++++++++-------------------
 reftable/stack.h           |   3 -
 reftable/stack_test.c      |  67 +++++---------------
 reftable/system.h          |   1 +
 t/t0610-reftable-basics.sh |  58 ++++++++++++-----
 5 files changed, 120 insertions(+), 133 deletions(-)


base-commit: c75fd8d8150afdf836b63a8e0534d9b9e3e111ba
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1683

Range-diff vs v2:

 1:  cb6b152e5c8 ! 1:  2fdd8ea1133 reftable/stack: add env to disable autocompaction
     @@ reftable/stack.c: int reftable_addition_commit(struct reftable_addition *add)
      
       ## reftable/system.h ##
      @@ reftable/system.h: license that can be found in the LICENSE file or at
     - #include "strbuf.h"
     + #include "tempfile.h"
       #include "hash-ll.h" /* hash ID, sizes.*/
       #include "dir.h" /* remove_dir_recursively, for tests.*/
      +#include "parse.h"
       
       int hash_size(uint32_t id);
       
     +
     + ## t/t0610-reftable-basics.sh ##
     +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause auto-compaction' '
     + 	test_line_count = 1 repo/.git/reftable/tables.list
     + '
     + 
     ++test_expect_success 'ref transaction: environment variable disables auto-compaction' '
     ++	test_when_finished "rm -rf repo" &&
     ++
     ++	git init repo &&
     ++	test_commit -C repo A &&
     ++	for i in $(test_seq 20)
     ++	do
     ++		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
     ++	done &&
     ++	test_line_count = 23 repo/.git/reftable/tables.list &&
     ++
     ++	git -C repo update-ref foo HEAD &&
     ++	test_line_count = 1 repo/.git/reftable/tables.list
     ++'
     ++
     + check_fsync_events () {
     + 	local trace="$1" &&
     + 	shift &&
 2:  def70084523 ! 2:  7e62c2286ae reftable/stack: use geometric table compaction
     @@ Commit message
      
          Instead, to avoid unbounded growth of the table list, the compaction
          strategy is updated to ensure tables follow a geometric sequence after
     -    each operation. This is done by walking the table list in reverse index
     -    order to identify the compaction segment start and end. The compaction
     -    segment end is found by identifying the first table which has a
     -    preceding table size less than twice the current table. Next, the
     -    compaction segment start is found iterating through the remaining tables
     -    in the list checking if the previous table size is less than twice the
     -    cumulative of tables from the segment end. This ensures the correct
     -    segment start is found and that the newly compacted table does not
     -    violate the geometric sequence.
     +    each operation by individually evaluating each table in reverse index
     +    order. This strategy results in a much simpler and more robust algorithm
     +    compared to the previous one while also maintaining a minimal ordered
     +    set of tables on-disk.
      
          When creating 10 thousand references, the new strategy has no
          performance impact:
     @@ reftable/stack.c: static int segment_size(struct segment *s)
      +	 * they are already valid members of the geometric sequence. Due to the
      +	 * properties of a geometric sequence, it is not possible for the sum of
      +	 * these tables to exceed the value of the ending point table.
     ++	 *
     ++	 * Example table size sequence requiring no compaction:
     ++	 * 	64, 32, 16, 8, 4, 2, 1
     ++	 *
     ++	 * Example compaction segment end set to table with size 3:
     ++	 * 	64, 32, 16, 8, 4, 3, 1
      +	 */
      +	for (i = n - 1; i > 0; i--) {
      +		if (sizes[i - 1] < sizes[i] * 2) {
     @@ reftable/stack.c: static int segment_size(struct segment *s)
       			break;
      +		}
      +	}
     -+
     + 
     +-		min_seg.start = prev;
     +-		min_seg.bytes += sizes[prev];
      +	/*
      +	 * Find the starting table of the compaction segment by iterating
      +	 * through the remaining tables and keeping track of the accumulated
     -+	 * size of all tables seen from the segment end table.
     ++	 * size of all tables seen from the segment end table. The previous
     ++	 * table is compared to the accumulated size because the tables from the
     ++	 * segment end are merged backwards recursively.
      +	 *
      +	 * Note that we keep iterating even after we have found the first
      +	 * starting point. This is because there may be tables in the stack
      +	 * preceding that first starting point which violate the geometric
      +	 * sequence.
     ++	 *
     ++	 * Example compaction segment start set to table with size 32:
     ++	 * 	128, 32, 16, 8, 4, 3, 1
      +	 */
      +	for (; i > 0; i--) {
      +		uint64_t curr = bytes;
      +		bytes += sizes[i - 1];
     - 
     --		min_seg.start = prev;
     --		min_seg.bytes += sizes[prev];
     ++
      +		if (sizes[i - 1] < curr * 2) {
      +			seg.start = i - 1;
      +			seg.bytes = bytes;
     @@ reftable/stack_test.c: static void test_reftable_stack_hash_id(void)
       static void test_suggest_compaction_segment(void)
       {
      -	uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
     +-	/* .................0    1    2  3   4  5  6 */
      +	uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
     - 	/* .................0    1    2  3   4  5  6 */
       	struct segment min =
       		suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
      -	EXPECT(min.start == 2);
     @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
       
       	test_commit -C repo --no-tag B &&
       	test_line_count = 1 repo/.git/reftable/tables.list
     +@@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: environment variable disables auto-compact
     + 	do
     + 		GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo update-ref branch-$i HEAD || return 1
     + 	done &&
     +-	test_line_count = 23 repo/.git/reftable/tables.list &&
     ++	test_line_count = 22 repo/.git/reftable/tables.list &&
     + 
     + 	git -C repo update-ref foo HEAD &&
     + 	test_line_count = 1 repo/.git/reftable/tables.list
       '
       
      +test_expect_success 'ref transaction: alternating table sizes are compacted' '
 3:  a23e3fc6972 ! 3:  9a33914c852 reftable/segment: make segment end inclusive
     @@ Metadata
      Author: Justin Tobler <jltobler@gmail.com>
      
       ## Commit message ##
     -    reftable/segment: make segment end inclusive
     +    reftable/stack: make segment end inclusive
      
          For a reftable segment, the start of the range is inclusive and the end
          is exclusive. In practice we increment the end when creating the

-- 
gitgitgadget


^ permalink raw reply	[relevance 3%]

* [PATCH v4] userdiff: better method/property matching for C#
  2024-03-28  8:07  1%   ` [PATCH v3] " Steven Jeuris via GitGitGadget
@ 2024-03-28 19:14  1%     ` Steven Jeuris via GitGitGadget
  2024-04-03 21:42  1%       ` [PATCH v5] " Steven Jeuris via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Steven Jeuris via GitGitGadget @ 2024-03-28 19:14 UTC (permalink / raw)
  To: git
  Cc: Ævar Arnfjörð Bjarmason, Jeff King, Linus Arver,
	Johannes Sixt, Steven Jeuris, Steven Jeuris

From: Steven Jeuris <steven.jeuris@3shape.com>

- Support multi-line methods by not requiring closing parenthesis.
- Support multiple generics (comma was missing before).
- Add missing `foreach`, `lock` and  `fixed` keywords to skip over.
- Remove `instanceof` keyword, which isn't C#.
- Also detect non-method keywords not positioned at the start of a line.
- Added tests; none existed before.

The overall strategy is to focus more on what isn't expected for
method/property definitions, instead of what is, but is fully optional.

Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com>
---
    userdiff: better method/property matching for C#
    
    Change since v1: I removed "from" from the list of keywords to skip.
    First, I considered adding "await", but I discovered both "await" and
    "from" are "contextual keywords", which unlike the other keywords
    currently listed, aren't reserved, and can thus cause false negatives.
    I.e., it is valid to have a method named "await" or "from". In edge
    cases, this may lead to false positives, but a different exclusion rule
    will need to be added to handle these.
    
    Change since v2:
    
     * Corrected comment formatting.
     * Added csharp-property-skip-body test.
     * Added comments in test code to explain sections not part of the test.
     * Elaborated regex comments.
     * Excluded math operators (+-*/%) in method pattern to not catch
       multiline operations, and tested for this in the -skip-body tests.
       Catching "-" only worked when it was defined at the end of the
       exclusion block for some reason. The regex matcher seems quite
       bugged.
    
    Change since v3:
    
     * Changed regex to better handle whitespace in types, making use of the
       fact that it only appears after commas.
     * Split regex into multiple lines with comments explaining structure.
     * Split the "skip body" tests into more narrow csharp-exclude- tests.
     * Added a test for generic methods:
       csharp-exclude-generic-method-calls.
     * Added a test for array types used in methods: csharp-method-array.
     * Added an addition property test: csharp-property-braces-same-line.
     * Included a test for "( func(x)" case identified by Johannes in
       csharp-exclude-assignments.
    
    As before, I ran into many regex limitations (no possessive quantifiers,
    no lookahead). It also seems different regex evaluators are used on
    different test runs. Which one does git diff use? Maybe it is about time
    to update this? E.g., if speed is a concern, possessive quantifiers can
    speed up search.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1682%2FWhathecode%2Fmaster-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1682/Whathecode/master-v4
Pull-Request: https://github.com/git/git/pull/1682

Range-diff vs v3:

 1:  9b6c76f5342 ! 1:  2feb84beaa0 userdiff: better method/property matching for C#
     @@ Commit message
      
          Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com>
      
     - ## t/t4018/csharp-method (new) ##
     + ## t/t4018/csharp-exclude-assignments (new) ##
      @@
      +class Example
      +{
      +    string Method(int RIGHT)
      +    {
     -+        // Filler
     -+        // Filler
     ++        var constantAssignment = "test";
     ++        var methodAssignment = MethodCall();
     ++        var multiLineMethodAssignment = MethodCall(
     ++        );
     ++        var multiLine = "first"
     ++            + MethodCall()
     ++            +
     ++            ( MethodCall()
     ++            )
     ++            + MethodCall();
      +
      +        return "ChangeMe";
      +    }
     -+}
     -
     - ## t/t4018/csharp-method-explicit (new) ##
     -@@
     -+using System;
      +
     -+class Example : IDisposable
     -+{
     -+    void IDisposable.Dispose() // RIGHT
     -+    {
     -+        // Filler
     -+        // Filler
     -+        
     -+        // ChangeMe
     -+    }
     -+}
     -
     - ## t/t4018/csharp-method-generics (new) ##
     -@@
     -+class Example<T1, T2>
     -+{
     -+    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
     -+    {
     -+        // Filler
     -+        // Filler
     -+        
     -+        // ChangeMe
     -+        return null;
     -+    }
     ++    string MethodCall(int a = 0, int b = 0) => "test";
      +}
      
     - ## t/t4018/csharp-method-modifiers (new) ##
     + ## t/t4018/csharp-exclude-control-statements (new) ##
      @@
     -+using System.Threading.Tasks;
     -+
      +class Example
      +{
     -+    static internal async Task Method(int RIGHT)
     ++    string Method(int RIGHT)
      +    {
     -+        // Filler
     -+        // Filler
     -+        
     -+        // ChangeMe
     -+        await Task.Delay(1);
     ++        if (false)
     ++        {
     ++            return "out";
     ++        }
     ++        else { }
     ++        if (true) MethodCall(
     ++        );
     ++        else MethodCall(
     ++        );
     ++        switch ("test")
     ++        {
     ++            case "one":
     ++            return MethodCall(
     ++            );
     ++            case "two":
     ++            break;
     ++        }
     ++        (int, int) tuple = (1, 4);
     ++        switch (tuple)
     ++        {
     ++            case (1, 4):
     ++              MethodCall();
     ++		      break;
     ++        }
     ++
     ++        return "ChangeMe";
      +    }
     ++
     ++    string MethodCall(int a = 0, int b = 0) => "test";
      +}
      
     - ## t/t4018/csharp-method-multiline (new) ##
     + ## t/t4018/csharp-exclude-exceptions (new) ##
      @@
     ++using System;
     ++
      +class Example
      +{
     -+    string Method_RIGHT(
     -+        int a,
     -+        int b,
     -+        int c)
     ++    string Method(int RIGHT)
      +    {
     ++        try
     ++        {
     ++            throw new Exception("fail");
     ++        }
     ++        catch (Exception)
     ++        {
     ++        }
     ++        finally
     ++        {
     ++        }
     ++        try { } catch (Exception) {}
     ++        try
     ++        {
     ++            throw GetException(
     ++            );
     ++        }
     ++        catch (Exception) { }
     ++
      +        return "ChangeMe";
      +    }
     ++
     ++    Exception GetException() => new Exception("fail");
      +}
      
     - ## t/t4018/csharp-method-params (new) ##
     + ## t/t4018/csharp-exclude-generic-method-calls (new) ##
      @@
      +class Example
      +{
     -+    string Method(int RIGHT, int b, int c = 42)
     ++    string Method(int RIGHT)
      +    {
     -+        // Filler
     -+        // Filler
     -+        
     ++        GenericMethodCall<int, int>(
     ++            );
     ++
      +        return "ChangeMe";
      +    }
     ++
     ++    string GenericMethodCall<T, T2>() => "test";
      +}
      
     - ## t/t4018/csharp-method-skip-body (new) ##
     + ## t/t4018/csharp-exclude-init-dispose (new) ##
      @@
     -+using System.Linq;
      +using System;
      +
      +class Example : IDisposable
      +{
      +    string Method(int RIGHT)
      +    {
     -+        // Method calls
     -+        MethodCall();
     -+        MethodCall(1, 2);
     -+        MethodCall(
     -+            1, 2);
     -+
     -+        // Assignments
     -+        var constantAssignment = "test";
     -+        var methodAssignment = MethodCall();
     -+        var multiLineMethodAssignment = MethodCall(
     -+        );
     -+        var multiLine = "first"
     -+            + MethodCall()
     -+            - MethodCall()
     -+            + MethodCall();
     -+
     -+        // Initializations/disposal
      +        new Example();
      +        new Example(
      +            );
     @@ t/t4018/csharp-method-skip-body (new)
      +            this is default(
      +                Example);
      +
     -+        // Iteration statements
     ++        return "ChangeMe";
     ++    }
     ++
     ++    public void Dispose() {}
     ++}
     +
     + ## t/t4018/csharp-exclude-iterations (new) ##
     +@@
     ++using System.Linq;
     ++
     ++class Example
     ++{
     ++    string Method(int RIGHT)
     ++    {
      +        do { } while (true);
      +        do MethodCall(
      +        ); while (true);
     @@ t/t4018/csharp-method-skip-body (new)
      +        }
      +        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
      +
     -+        // Control
     -+        if (false)
     -+        {
     -+            return "out";
     -+        }
     -+        else { }
     -+        if (true) MethodCall(
     -+        );
     -+        else MethodCall(
     -+        );
     -+        switch ("test")
     -+        {
     -+            case "one":
     -+            return MethodCall(
     -+            );
     -+            case "two":
     -+            break;
     -+        }
     -+        (int, int) tuple = (1, 4);
     -+        switch (tuple)
     -+        {
     -+            case (1, 4):
     -+            MethodCall();
     -+        }
     ++        return "ChangeMe";
     ++    }
      +
     -+        // Exceptions
     -+        try
     -+        {
     -+            throw new Exception("fail");
     -+        }
     -+        catch (Exception)
     -+        {
     -+        }
     -+        finally
     -+        {
     -+        }
     -+        try { } catch (Exception) {}
     -+        try
     -+        {
     -+            throw GetException(
     -+            );
     -+        }
     -+        catch (Exception) { }
     ++    string MethodCall(int a = 0, int b = 0) => "test";
     ++}
     +
     + ## t/t4018/csharp-exclude-method-calls (new) ##
     +@@
     ++class Example
     ++{
     ++    string Method(int RIGHT)
     ++    {
     ++        MethodCall();
     ++        MethodCall(1, 2);
     ++        MethodCall(
     ++            1, 2);
      +
     -+        // Others
     ++        return "ChangeMe";
     ++    }
     ++
     ++    string MethodCall(int a = 0, int b = 0) => "test";
     ++    string GenericMethodCall<T, T2>() => "test";
     ++}
     +
     + ## t/t4018/csharp-exclude-other (new) ##
     +@@
     ++class Example
     ++{
     ++    string Method(int RIGHT)
     ++    {
      +        lock (this)
      +        {
      +        }
     @@ t/t4018/csharp-method-skip-body (new)
      +
      +        return "ChangeMe";
      +    }
     ++}
     +
     + ## t/t4018/csharp-method (new) ##
     +@@
     ++class Example
     ++{
     ++    string Method(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
      +
     -+    // Supporting methods to make the tested method above valid C#.
     -+    public void Dispose() {}
     -+    string MethodCall(int a = 0, int b = 0) => "test";
     -+    Exception GetException() => new Exception("fail");
     -+    int[] Numbers() => [0, 1];
     ++        return "ChangeMe";
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-array (new) ##
     +@@
     ++class Example
     ++{
     ++    string[] Method(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
     ++
     ++        return ["ChangeMe"];
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-explicit (new) ##
     +@@
     ++using System;
     ++
     ++class Example : IDisposable
     ++{
     ++    void IDisposable.Dispose() // RIGHT
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-generics (new) ##
     +@@
     ++class Example<T1, T2>
     ++{
     ++    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        return null;
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-modifiers (new) ##
     +@@
     ++using System.Threading.Tasks;
     ++
     ++class Example
     ++{
     ++    static internal async Task Method(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        await Task.Delay(1);
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-multiline (new) ##
     +@@
     ++class Example
     ++{
     ++    string Method_RIGHT(
     ++        int a,
     ++        int b,
     ++        int c)
     ++    {
     ++        return "ChangeMe";
     ++    }
     ++}
     +
     + ## t/t4018/csharp-method-params (new) ##
     +@@
     ++class Example
     ++{
     ++    string Method(int RIGHT, int b, int c = 42)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        return "ChangeMe";
     ++    }
      +}
      
       ## t/t4018/csharp-method-special-chars (new) ##
     @@ t/t4018/csharp-method-with-spacing (new)
      @@
      +class Example
      +{
     -+		string   Method 	( int 	RIGHT )
     -+	{
     ++    	string   Method 	( int 	RIGHT )
     ++    {
      +        // Filler
      +        // Filler
     -+		
     ++
      +        return "ChangeMe";
      +    }
      +}
     @@ t/t4018/csharp-property (new)
      +    }
      +}
      
     - ## t/t4018/csharp-property-skip-body (new) ##
     + ## t/t4018/csharp-property-braces-same-line (new) ##
      @@
     -+using System.Linq;
     -+using System;
     -+
     -+class Example : IDisposable
     ++class Example
      +{
     -+    public string RIGHT
     -+    {
     -+        get
     ++    public bool RIGHT {
     ++        get { return true; }
     ++        set
      +        {
     -+            // Method calls
     -+            MethodCall();
     -+            MethodCall(1, 2);
     -+            MethodCall(
     -+                1, 2);
     -+
     -+            // Assignments
     -+            var constantAssignment = "test";
     -+            var methodAssignment = MethodCall();
     -+            var multiLineMethodAssignment = MethodCall(
     -+                );
     -+            var multiLine = "first"
     -+                + MethodCall()
     -+                - MethodCall()
     -+                + MethodCall();
     -+
     -+            // Initializations/disposal
     -+            new Example();
     -+            new Example(
     -+                );
     -+            new Example { };
     -+            using (this)
     -+            {
     -+            }
     -+            var def =
     -+                this is default(
     -+                    Example);
     -+
     -+            // Iteration statements
     -+            do { } while (true);
     -+            do MethodCall(
     -+            ); while (true);
     -+            while (true);
     -+            while (true) {
     -+                break;
     -+            }
     -+            for (int i = 0; i < 10; ++i)
     -+            {
     -+            }
     -+            foreach (int i in Enumerable.Range(0, 10))
     -+            {
     -+            }
     -+            int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
     -+
     -+            // Control
     -+            if (false)
     -+            {
     -+                return "out";
     -+            }
     -+            else { }
     -+            if (true) MethodCall(
     -+            );
     -+            else MethodCall(
     -+            );
     -+            switch ("test")
     -+            {
     -+                case "one":
     -+                return MethodCall(
     -+                );
     -+                case "two":
     -+                break;
     -+            }
     -+            (int, int) tuple = (1, 4);
     -+            switch (tuple)
     -+            {
     -+                case (1, 4):
     -+                MethodCall();
     -+            }
     -+
     -+            // Exceptions
     -+            try
     -+            {
     -+                throw new Exception("fail");
     -+            }
     -+            catch (Exception)
     -+            {
     -+            }
     -+            finally
     -+            {
     -+            }
     -+            try { } catch (Exception) {}
     -+            try
     -+            {
     -+                throw GetException(
     -+                );
     -+            }
     -+            catch (Exception) { }
     -+
     -+            // Others
     -+            lock (this)
     -+            {
     -+            }
     -+            unsafe
     -+            {
     -+                byte[] bytes = [1, 2, 3];
     -+                fixed (byte* pointerToFirst = bytes)
     -+                {
     -+                }
     -+            }
     -+
     -+            return "ChangeMe";
     ++            // ChangeMe
      +        }
     -+        set { }
      +    }
     -+
     -+    // Supporting methods to make the tested property above valid C#.
     -+    public void Dispose() {}
     -+    string MethodCall(int a = 0, int b = 0) => "test";
     -+    Exception GetException() => new Exception("fail");
     -+    int[] Numbers() => [0, 1];
      +}
      
       ## userdiff.c ##
     @@ userdiff.c: PATTERNS("cpp",
      +	  * can be followed by parentheses without special characters in between,
      +	  * making them look like methods.
      +	  */
     -+	 "!(^|[ \t]+)(do|while|for|foreach|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
     ++	 "!(^|[ \t]+)" /* Start of line or whitespace. */
     ++		"(do|while|for|foreach|if|else|new|default|return|switch|case|throw"
     ++		"|catch|using|lock|fixed)"
     ++		"([ \t(]+|$)\n" /* Whitespace, "(", or end of line. */
      +	 /*
      +	  * Methods/constructors:
      +	  * The strategy is to identify a minimum of two groups (any combination
     -+	  * of keywords/type/name), without intermediate characters which can't be
     -+	  * part of method definitions before the opening parenthesis, and without
     ++	  * of keywords/type/name) before the opening parenthesis, and without
      +	  * final unexpected characters, normally only used in ordinary statements.
     -+	  * "=" is excluded to ignore assignments, but as a result rules out
     -+	  * methods with expression bodies. However, since those fit on 1-2 lines,
     -+	  * a chunk header isn't useful either way.
      +	  */
     -+	 "^[ \t]*(([][[:alnum:]@_<>.,]*[^=:{ \t+*%\\/\\-][ \t]+[][[:alnum:]@_<>.,]*)+\\([^;]*)$\n"
     ++	 "^[ \t]*" /* Remove leading whitespace. */
     ++		"(" /* Start chunk header capture. */
     ++		"(" /* Group of keywords/type/names. */
     ++		"([][[:alnum:]@_<>.]|, [ |\t]*)+" /* Space only allowed after ",". */
     ++		"[ \t]+" /* One required space forces a minimum of two items. */
     ++		"([][[:alnum:]@_<>.]|, [ |\t]*)+"
     ++		"[ \t]*" /* Optional space before parameters start. */
     ++		")+"
     ++		"\\(" /* Start of method parameters. */
     ++		"[^;]*" /* Allow complex parameters, but exclude statements (;). */
     ++		")$\n" /* Close chunk header capture. */
      +	 /*
      +	  * Properties:
     -+	  * As with methods, expect a minimum of two groups. And, the vast majority
     -+	  * of properties long enough to be worth showing a chunk header for don't
     -+	  * include "=:;,()" on the line they are defined.
     ++	  * As with methods, expect a minimum of two groups. But, more trivial than
     ++	  * methods, the vast majority of properties long enough to be worth
     ++	  * showing a chunk header for don't include "=:;,()" on the line they are
     ++	  * defined, since they don't have a parameter list.
      +	  */
     -+	 "^[ \t]*([][[:alnum:]@_<>.,]+[ \t]+[][[:alnum:]@_.]+[^=:;,()]*)$\n"
     ++	 "^[ \t]*("
     ++		"("
     ++		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]+"
     ++		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]*"
     ++		")+" /* Up to here, same as methods regex. */
     ++		"[^;=:,()]*" /* Compared to methods, no parameter list allowed. */
     ++		")$\n"
       	 /* Type definitions */
       	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
       	 /* Namespace */


 t/t4018/csharp-exclude-assignments          | 20 +++++++++
 t/t4018/csharp-exclude-control-statements   | 34 +++++++++++++++
 t/t4018/csharp-exclude-exceptions           | 29 +++++++++++++
 t/t4018/csharp-exclude-generic-method-calls | 12 ++++++
 t/t4018/csharp-exclude-init-dispose         | 22 ++++++++++
 t/t4018/csharp-exclude-iterations           | 26 ++++++++++++
 t/t4018/csharp-exclude-method-calls         | 15 +++++++
 t/t4018/csharp-exclude-other                | 18 ++++++++
 t/t4018/csharp-method                       | 10 +++++
 t/t4018/csharp-method-array                 | 10 +++++
 t/t4018/csharp-method-explicit              | 12 ++++++
 t/t4018/csharp-method-generics              | 11 +++++
 t/t4018/csharp-method-modifiers             | 13 ++++++
 t/t4018/csharp-method-multiline             | 10 +++++
 t/t4018/csharp-method-params                | 10 +++++
 t/t4018/csharp-method-special-chars         | 11 +++++
 t/t4018/csharp-method-with-spacing          | 10 +++++
 t/t4018/csharp-property                     | 11 +++++
 t/t4018/csharp-property-braces-same-line    | 10 +++++
 userdiff.c                                  | 46 ++++++++++++++++++---
 20 files changed, 334 insertions(+), 6 deletions(-)
 create mode 100644 t/t4018/csharp-exclude-assignments
 create mode 100644 t/t4018/csharp-exclude-control-statements
 create mode 100644 t/t4018/csharp-exclude-exceptions
 create mode 100644 t/t4018/csharp-exclude-generic-method-calls
 create mode 100644 t/t4018/csharp-exclude-init-dispose
 create mode 100644 t/t4018/csharp-exclude-iterations
 create mode 100644 t/t4018/csharp-exclude-method-calls
 create mode 100644 t/t4018/csharp-exclude-other
 create mode 100644 t/t4018/csharp-method
 create mode 100644 t/t4018/csharp-method-array
 create mode 100644 t/t4018/csharp-method-explicit
 create mode 100644 t/t4018/csharp-method-generics
 create mode 100644 t/t4018/csharp-method-modifiers
 create mode 100644 t/t4018/csharp-method-multiline
 create mode 100644 t/t4018/csharp-method-params
 create mode 100644 t/t4018/csharp-method-special-chars
 create mode 100644 t/t4018/csharp-method-with-spacing
 create mode 100644 t/t4018/csharp-property
 create mode 100644 t/t4018/csharp-property-braces-same-line

diff --git a/t/t4018/csharp-exclude-assignments b/t/t4018/csharp-exclude-assignments
new file mode 100644
index 00000000000..239f312963b
--- /dev/null
+++ b/t/t4018/csharp-exclude-assignments
@@ -0,0 +1,20 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        var constantAssignment = "test";
+        var methodAssignment = MethodCall();
+        var multiLineMethodAssignment = MethodCall(
+        );
+        var multiLine = "first"
+            + MethodCall()
+            +
+            ( MethodCall()
+            )
+            + MethodCall();
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-control-statements b/t/t4018/csharp-exclude-control-statements
new file mode 100644
index 00000000000..3a0f404ee10
--- /dev/null
+++ b/t/t4018/csharp-exclude-control-statements
@@ -0,0 +1,34 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        if (false)
+        {
+            return "out";
+        }
+        else { }
+        if (true) MethodCall(
+        );
+        else MethodCall(
+        );
+        switch ("test")
+        {
+            case "one":
+            return MethodCall(
+            );
+            case "two":
+            break;
+        }
+        (int, int) tuple = (1, 4);
+        switch (tuple)
+        {
+            case (1, 4):
+              MethodCall();
+		      break;
+        }
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-exceptions b/t/t4018/csharp-exclude-exceptions
new file mode 100644
index 00000000000..b1e64256cfe
--- /dev/null
+++ b/t/t4018/csharp-exclude-exceptions
@@ -0,0 +1,29 @@
+using System;
+
+class Example
+{
+    string Method(int RIGHT)
+    {
+        try
+        {
+            throw new Exception("fail");
+        }
+        catch (Exception)
+        {
+        }
+        finally
+        {
+        }
+        try { } catch (Exception) {}
+        try
+        {
+            throw GetException(
+            );
+        }
+        catch (Exception) { }
+
+        return "ChangeMe";
+    }
+
+    Exception GetException() => new Exception("fail");
+}
diff --git a/t/t4018/csharp-exclude-generic-method-calls b/t/t4018/csharp-exclude-generic-method-calls
new file mode 100644
index 00000000000..31af546665d
--- /dev/null
+++ b/t/t4018/csharp-exclude-generic-method-calls
@@ -0,0 +1,12 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        GenericMethodCall<int, int>(
+            );
+
+        return "ChangeMe";
+    }
+
+    string GenericMethodCall<T, T2>() => "test";
+}
diff --git a/t/t4018/csharp-exclude-init-dispose b/t/t4018/csharp-exclude-init-dispose
new file mode 100644
index 00000000000..2bc8e194e20
--- /dev/null
+++ b/t/t4018/csharp-exclude-init-dispose
@@ -0,0 +1,22 @@
+using System;
+
+class Example : IDisposable
+{
+    string Method(int RIGHT)
+    {
+        new Example();
+        new Example(
+            );
+        new Example { };
+        using (this)
+        {
+        }
+        var def =
+            this is default(
+                Example);
+
+        return "ChangeMe";
+    }
+
+    public void Dispose() {}
+}
diff --git a/t/t4018/csharp-exclude-iterations b/t/t4018/csharp-exclude-iterations
new file mode 100644
index 00000000000..960aa182ae2
--- /dev/null
+++ b/t/t4018/csharp-exclude-iterations
@@ -0,0 +1,26 @@
+using System.Linq;
+
+class Example
+{
+    string Method(int RIGHT)
+    {
+        do { } while (true);
+        do MethodCall(
+        ); while (true);
+        while (true);
+        while (true) {
+            break;
+        }
+        for (int i = 0; i < 10; ++i)
+        {
+        }
+        foreach (int i in Enumerable.Range(0, 10))
+        {
+        }
+        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-method-calls b/t/t4018/csharp-exclude-method-calls
new file mode 100644
index 00000000000..8afe2a54638
--- /dev/null
+++ b/t/t4018/csharp-exclude-method-calls
@@ -0,0 +1,15 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        MethodCall();
+        MethodCall(1, 2);
+        MethodCall(
+            1, 2);
+
+        return "ChangeMe";
+    }
+
+    string MethodCall(int a = 0, int b = 0) => "test";
+    string GenericMethodCall<T, T2>() => "test";
+}
diff --git a/t/t4018/csharp-exclude-other b/t/t4018/csharp-exclude-other
new file mode 100644
index 00000000000..4d5581cf3e1
--- /dev/null
+++ b/t/t4018/csharp-exclude-other
@@ -0,0 +1,18 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        lock (this)
+        {
+        }
+        unsafe
+        {
+            byte[] bytes = [1, 2, 3];
+            fixed (byte* pointerToFirst = bytes)
+            {
+            }
+        }
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
new file mode 100644
index 00000000000..16b367aca2b
--- /dev/null
+++ b/t/t4018/csharp-method
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-array b/t/t4018/csharp-method-array
new file mode 100644
index 00000000000..1126de8201d
--- /dev/null
+++ b/t/t4018/csharp-method-array
@@ -0,0 +1,10 @@
+class Example
+{
+    string[] Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+
+        return ["ChangeMe"];
+    }
+}
diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
new file mode 100644
index 00000000000..5a710116cc4
--- /dev/null
+++ b/t/t4018/csharp-method-explicit
@@ -0,0 +1,12 @@
+using System;
+
+class Example : IDisposable
+{
+    void IDisposable.Dispose() // RIGHT
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+    }
+}
diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
new file mode 100644
index 00000000000..b3216bfb2a7
--- /dev/null
+++ b/t/t4018/csharp-method-generics
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return null;
+    }
+}
diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
new file mode 100644
index 00000000000..caefa8ee99c
--- /dev/null
+++ b/t/t4018/csharp-method-modifiers
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+class Example
+{
+    static internal async Task Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        await Task.Delay(1);
+    }
+}
diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
new file mode 100644
index 00000000000..3983ff42f51
--- /dev/null
+++ b/t/t4018/csharp-method-multiline
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method_RIGHT(
+        int a,
+        int b,
+        int c)
+    {
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
new file mode 100644
index 00000000000..3f00410ba1f
--- /dev/null
+++ b/t/t4018/csharp-method-params
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT, int b, int c = 42)
+    {
+        // Filler
+        // Filler
+        
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
new file mode 100644
index 00000000000..e6c7bc01a18
--- /dev/null
+++ b/t/t4018/csharp-method-special-chars
@@ -0,0 +1,11 @@
+class @Some_Type
+{
+    @Some_Type @Method_With_Underscore(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return new @Some_Type();
+    }
+}
diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
new file mode 100644
index 00000000000..233bb976cc2
--- /dev/null
+++ b/t/t4018/csharp-method-with-spacing
@@ -0,0 +1,10 @@
+class Example
+{
+    	string   Method 	( int 	RIGHT )
+    {
+        // Filler
+        // Filler
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
new file mode 100644
index 00000000000..e56dfce34c1
--- /dev/null
+++ b/t/t4018/csharp-property
@@ -0,0 +1,11 @@
+class Example
+{
+    public bool RIGHT
+    {
+        get { return true; }
+        set
+        {
+            // ChangeMe
+        }
+    }
+}
diff --git a/t/t4018/csharp-property-braces-same-line b/t/t4018/csharp-property-braces-same-line
new file mode 100644
index 00000000000..608131d3d31
--- /dev/null
+++ b/t/t4018/csharp-property-braces-same-line
@@ -0,0 +1,10 @@
+class Example
+{
+    public bool RIGHT {
+        get { return true; }
+        set
+        {
+            // ChangeMe
+        }
+    }
+}
diff --git a/userdiff.c b/userdiff.c
index e399543823b..35e0e1183f7 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -89,12 +89,46 @@ PATTERNS("cpp",
 	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
 PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /*
+	  * Jump over reserved keywords which are illegal method names, but which
+	  * can be followed by parentheses without special characters in between,
+	  * making them look like methods.
+	  */
+	 "!(^|[ \t]+)" /* Start of line or whitespace. */
+		"(do|while|for|foreach|if|else|new|default|return|switch|case|throw"
+		"|catch|using|lock|fixed)"
+		"([ \t(]+|$)\n" /* Whitespace, "(", or end of line. */
+	 /*
+	  * Methods/constructors:
+	  * The strategy is to identify a minimum of two groups (any combination
+	  * of keywords/type/name) before the opening parenthesis, and without
+	  * final unexpected characters, normally only used in ordinary statements.
+	  */
+	 "^[ \t]*" /* Remove leading whitespace. */
+		"(" /* Start chunk header capture. */
+		"(" /* Group of keywords/type/names. */
+		"([][[:alnum:]@_<>.]|, [ |\t]*)+" /* Space only allowed after ",". */
+		"[ \t]+" /* One required space forces a minimum of two items. */
+		"([][[:alnum:]@_<>.]|, [ |\t]*)+"
+		"[ \t]*" /* Optional space before parameters start. */
+		")+"
+		"\\(" /* Start of method parameters. */
+		"[^;]*" /* Allow complex parameters, but exclude statements (;). */
+		")$\n" /* Close chunk header capture. */
+	 /*
+	  * Properties:
+	  * As with methods, expect a minimum of two groups. But, more trivial than
+	  * methods, the vast majority of properties long enough to be worth
+	  * showing a chunk header for don't include "=:;,()" on the line they are
+	  * defined, since they don't have a parameter list.
+	  */
+	 "^[ \t]*("
+		"("
+		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]+"
+		"([][[:alnum:]@_<>.]|, [ |\t]*)+[ \t]*"
+		")+" /* Up to here, same as methods regex. */
+		"[^;=:,()]*" /* Compared to methods, no parameter list allowed. */
+		")$\n"
 	 /* Type definitions */
 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
 	 /* Namespace */

base-commit: f41f85c9ec8d4d46de0fd5fded88db94d3ec8c11
-- 
gitgitgadget


^ permalink raw reply related	[relevance 1%]

* [PATCH v3] userdiff: better method/property matching for C#
    2024-03-27  7:30  0%   ` Johannes Sixt
@ 2024-03-28  8:07  1%   ` Steven Jeuris via GitGitGadget
  2024-03-28 19:14  1%     ` [PATCH v4] " Steven Jeuris via GitGitGadget
  1 sibling, 1 reply; 200+ results
From: Steven Jeuris via GitGitGadget @ 2024-03-28  8:07 UTC (permalink / raw)
  To: git
  Cc: Ævar Arnfjörð Bjarmason, Jeff King, Linus Arver,
	Johannes Sixt, Steven Jeuris, Steven Jeuris

From: Steven Jeuris <steven.jeuris@3shape.com>

- Support multi-line methods by not requiring closing parenthesis.
- Support multiple generics (comma was missing before).
- Add missing `foreach`, `lock` and  `fixed` keywords to skip over.
- Remove `instanceof` keyword, which isn't C#.
- Also detect non-method keywords not positioned at the start of a line.
- Added tests; none existed before.

The overall strategy is to focus more on what isn't expected for
method/property definitions, instead of what is, but is fully optional.

Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com>
---
    userdiff: better method/property matching for C#
    
    Change since v1: I removed "from" from the list of keywords to skip.
    First, I considered adding "await", but I discovered both "await" and
    "from" are "contextual keywords", which unlike the other keywords
    currently listed, aren't reserved, and can thus cause false negatives.
    I.e., it is valid to have a method named "await" or "from". In edge
    cases, this may lead to false positives, but a different exclusion rule
    will need to be added to handle these.
    
    Change since v2:
    
     * Corrected comment formatting.
     * Added csharp-property-skip-body test.
     * Added comments in test code to explain sections not part of the test.
     * Elaborated regex comments.
     * Excluded math operators (+-*/%) in method pattern to not catch
       multiline operations, and tested for this in the -skip-body tests.
       Catching "-" only worked when it was defined at the end of the
       exclusion block for some reason. The regex matcher seems quite
       bugged.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1682%2FWhathecode%2Fmaster-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1682/Whathecode/master-v3
Pull-Request: https://github.com/git/git/pull/1682

Range-diff vs v2:

 1:  00315519014 ! 1:  9b6c76f5342 userdiff: better method/property matching for C#
     @@ t/t4018/csharp-method (new)
      @@
      +class Example
      +{
     -+	string Method(int RIGHT)
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		return "ChangeMe";
     -+	}
     ++    string Method(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
     ++
     ++        return "ChangeMe";
     ++    }
      +}
      
       ## t/t4018/csharp-method-explicit (new) ##
     @@ t/t4018/csharp-method-explicit (new)
      +
      +class Example : IDisposable
      +{
     -+	void IDisposable.Dispose() // RIGHT
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		// ChangeMe
     -+	}
     ++    void IDisposable.Dispose() // RIGHT
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++    }
      +}
      
       ## t/t4018/csharp-method-generics (new) ##
      @@
      +class Example<T1, T2>
      +{
     -+	Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		// ChangeMe
     -+		return null;
     -+	}
     ++    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        return null;
     ++    }
      +}
      
       ## t/t4018/csharp-method-modifiers (new) ##
     @@ t/t4018/csharp-method-modifiers (new)
      +
      +class Example
      +{
     -+	static internal async Task Method(int RIGHT)
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		// ChangeMe
     -+		await Task.Delay(1);
     -+	}
     ++    static internal async Task Method(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        await Task.Delay(1);
     ++    }
      +}
      
       ## t/t4018/csharp-method-multiline (new) ##
      @@
      +class Example
      +{
     -+	string Method_RIGHT(
     -+		int a,
     -+		int b,
     -+		int c)
     -+	{
     -+		return "ChangeMe";
     -+	}
     ++    string Method_RIGHT(
     ++        int a,
     ++        int b,
     ++        int c)
     ++    {
     ++        return "ChangeMe";
     ++    }
      +}
      
       ## t/t4018/csharp-method-params (new) ##
      @@
      +class Example
      +{
     -+	string Method(int RIGHT, int b, int c = 42)
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		return "ChangeMe";
     -+	}
     ++    string Method(int RIGHT, int b, int c = 42)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        return "ChangeMe";
     ++    }
      +}
      
       ## t/t4018/csharp-method-skip-body (new) ##
     @@ t/t4018/csharp-method-skip-body (new)
      +
      +class Example : IDisposable
      +{
     -+	string Method(int RIGHT)
     -+	{
     -+		// Method calls
     -+		MethodCall();
     -+		MethodCall(1, 2);
     -+		MethodCall(
     -+			1, 2);
     -+		
     -+		// Assignments
     -+		var constantAssignment = "test";
     -+		var methodAssignment = MethodCall();
     -+		var multiLineMethodAssignment = MethodCall(
     -+			);
     -+		
     -+		// Initializations/disposal
     -+		new Example();
     -+		new Example(
     -+			);
     -+		new Example { };
     -+		using (this) 
     -+		{
     -+		}
     -+		var def =
     -+			this is default(
     -+				Example);
     -+		
     -+		// Iteration statements
     -+		do { } while (true);
     -+		do MethodCall(
     -+			); while (true);
     -+		while (true);
     -+		while (true) {
     -+			break;
     -+		}
     -+		for (int i = 0; i < 10; ++i)
     -+		{
     -+		}
     -+		foreach (int i in Enumerable.Range(0, 10))
     -+		{
     -+		}
     -+		int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
     -+		
     -+		// Control
     -+		if (false)
     -+		{
     -+			return "out";
     -+		}
     -+		else { }
     -+		if (true) MethodCall(
     -+			);
     -+		else MethodCall(
     -+			);
     -+		switch ("test")
     -+		{
     -+			case "one":
     -+				return MethodCall(
     -+					);
     -+			case "two":
     -+				break;
     -+		}
     -+		(int, int) tuple = (1, 4);
     -+		switch (tuple)
     -+		{
     -+			case (1, 4):
     -+				MethodCall();
     -+		}
     -+		
     -+		// Exceptions
     -+		try
     -+		{
     -+			throw new Exception("fail");
     -+		}
     -+		catch (Exception)
     -+		{
     -+		}
     -+		finally
     -+		{
     -+		}
     -+		try { } catch (Exception) {}
     -+		try
     -+		{
     -+			throw GetException(
     -+				);
     -+		}
     -+		catch (Exception) { }
     -+		
     -+		// Others
     -+		lock (this)
     -+		{
     -+		}
     -+		unsafe
     -+		{
     -+			byte[] bytes = [1, 2, 3];
     -+			fixed (byte* pointerToFirst = bytes)
     -+			{
     -+			}
     -+		}
     -+		
     -+		return "ChangeMe";
     -+	}
     -+	
     -+	public void Dispose() {}
     -+	
     -+	string MethodCall(int a = 0, int b = 0) => "test";
     -+	Exception GetException() => new Exception("fail");
     -+	int[] Numbers() => [0, 1];
     ++    string Method(int RIGHT)
     ++    {
     ++        // Method calls
     ++        MethodCall();
     ++        MethodCall(1, 2);
     ++        MethodCall(
     ++            1, 2);
     ++
     ++        // Assignments
     ++        var constantAssignment = "test";
     ++        var methodAssignment = MethodCall();
     ++        var multiLineMethodAssignment = MethodCall(
     ++        );
     ++        var multiLine = "first"
     ++            + MethodCall()
     ++            - MethodCall()
     ++            + MethodCall();
     ++
     ++        // Initializations/disposal
     ++        new Example();
     ++        new Example(
     ++            );
     ++        new Example { };
     ++        using (this)
     ++        {
     ++        }
     ++        var def =
     ++            this is default(
     ++                Example);
     ++
     ++        // Iteration statements
     ++        do { } while (true);
     ++        do MethodCall(
     ++        ); while (true);
     ++        while (true);
     ++        while (true) {
     ++            break;
     ++        }
     ++        for (int i = 0; i < 10; ++i)
     ++        {
     ++        }
     ++        foreach (int i in Enumerable.Range(0, 10))
     ++        {
     ++        }
     ++        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
     ++
     ++        // Control
     ++        if (false)
     ++        {
     ++            return "out";
     ++        }
     ++        else { }
     ++        if (true) MethodCall(
     ++        );
     ++        else MethodCall(
     ++        );
     ++        switch ("test")
     ++        {
     ++            case "one":
     ++            return MethodCall(
     ++            );
     ++            case "two":
     ++            break;
     ++        }
     ++        (int, int) tuple = (1, 4);
     ++        switch (tuple)
     ++        {
     ++            case (1, 4):
     ++            MethodCall();
     ++        }
     ++
     ++        // Exceptions
     ++        try
     ++        {
     ++            throw new Exception("fail");
     ++        }
     ++        catch (Exception)
     ++        {
     ++        }
     ++        finally
     ++        {
     ++        }
     ++        try { } catch (Exception) {}
     ++        try
     ++        {
     ++            throw GetException(
     ++            );
     ++        }
     ++        catch (Exception) { }
     ++
     ++        // Others
     ++        lock (this)
     ++        {
     ++        }
     ++        unsafe
     ++        {
     ++            byte[] bytes = [1, 2, 3];
     ++            fixed (byte* pointerToFirst = bytes)
     ++            {
     ++            }
     ++        }
     ++
     ++        return "ChangeMe";
     ++    }
     ++
     ++    // Supporting methods to make the tested method above valid C#.
     ++    public void Dispose() {}
     ++    string MethodCall(int a = 0, int b = 0) => "test";
     ++    Exception GetException() => new Exception("fail");
     ++    int[] Numbers() => [0, 1];
      +}
      
       ## t/t4018/csharp-method-special-chars (new) ##
      @@
      +class @Some_Type
      +{
     -+	@Some_Type @Method_With_Underscore(int RIGHT)
     -+	{
     -+		// Filler
     -+		// Filler
     -+		
     -+		// ChangeMe
     -+		return new @Some_Type();
     -+	}
     ++    @Some_Type @Method_With_Underscore(int RIGHT)
     ++    {
     ++        // Filler
     ++        // Filler
     ++        
     ++        // ChangeMe
     ++        return new @Some_Type();
     ++    }
      +}
      
       ## t/t4018/csharp-method-with-spacing (new) ##
     @@ t/t4018/csharp-method-with-spacing (new)
      +{
      +		string   Method 	( int 	RIGHT )
      +	{
     -+		// Filler
     -+		// Filler
     ++        // Filler
     ++        // Filler
      +		
     -+		return "ChangeMe";
     -+	}
     ++        return "ChangeMe";
     ++    }
      +}
      
       ## t/t4018/csharp-property (new) ##
      @@
      +class Example
      +{
     -+	public bool RIGHT
     ++    public bool RIGHT
      +    {
      +        get { return true; }
      +        set
     @@ t/t4018/csharp-property (new)
      +            // ChangeMe
      +        }
      +    }
     ++}
     +
     + ## t/t4018/csharp-property-skip-body (new) ##
     +@@
     ++using System.Linq;
     ++using System;
     ++
     ++class Example : IDisposable
     ++{
     ++    public string RIGHT
     ++    {
     ++        get
     ++        {
     ++            // Method calls
     ++            MethodCall();
     ++            MethodCall(1, 2);
     ++            MethodCall(
     ++                1, 2);
     ++
     ++            // Assignments
     ++            var constantAssignment = "test";
     ++            var methodAssignment = MethodCall();
     ++            var multiLineMethodAssignment = MethodCall(
     ++                );
     ++            var multiLine = "first"
     ++                + MethodCall()
     ++                - MethodCall()
     ++                + MethodCall();
     ++
     ++            // Initializations/disposal
     ++            new Example();
     ++            new Example(
     ++                );
     ++            new Example { };
     ++            using (this)
     ++            {
     ++            }
     ++            var def =
     ++                this is default(
     ++                    Example);
     ++
     ++            // Iteration statements
     ++            do { } while (true);
     ++            do MethodCall(
     ++            ); while (true);
     ++            while (true);
     ++            while (true) {
     ++                break;
     ++            }
     ++            for (int i = 0; i < 10; ++i)
     ++            {
     ++            }
     ++            foreach (int i in Enumerable.Range(0, 10))
     ++            {
     ++            }
     ++            int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
     ++
     ++            // Control
     ++            if (false)
     ++            {
     ++                return "out";
     ++            }
     ++            else { }
     ++            if (true) MethodCall(
     ++            );
     ++            else MethodCall(
     ++            );
     ++            switch ("test")
     ++            {
     ++                case "one":
     ++                return MethodCall(
     ++                );
     ++                case "two":
     ++                break;
     ++            }
     ++            (int, int) tuple = (1, 4);
     ++            switch (tuple)
     ++            {
     ++                case (1, 4):
     ++                MethodCall();
     ++            }
     ++
     ++            // Exceptions
     ++            try
     ++            {
     ++                throw new Exception("fail");
     ++            }
     ++            catch (Exception)
     ++            {
     ++            }
     ++            finally
     ++            {
     ++            }
     ++            try { } catch (Exception) {}
     ++            try
     ++            {
     ++                throw GetException(
     ++                );
     ++            }
     ++            catch (Exception) { }
     ++
     ++            // Others
     ++            lock (this)
     ++            {
     ++            }
     ++            unsafe
     ++            {
     ++                byte[] bytes = [1, 2, 3];
     ++                fixed (byte* pointerToFirst = bytes)
     ++                {
     ++                }
     ++            }
     ++
     ++            return "ChangeMe";
     ++        }
     ++        set { }
     ++    }
     ++
     ++    // Supporting methods to make the tested property above valid C#.
     ++    public void Dispose() {}
     ++    string MethodCall(int a = 0, int b = 0) => "test";
     ++    Exception GetException() => new Exception("fail");
     ++    int[] Numbers() => [0, 1];
      +}
      
       ## userdiff.c ##
     @@ userdiff.c: PATTERNS("cpp",
      -	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
      -	 /* Methods and constructors */
      -	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
     +-	 /* Properties */
     +-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
      +	 /*
     -+	  * Jump over keywords not used by methods which can be followed by parentheses without special characters in between,
     ++	  * Jump over reserved keywords which are illegal method names, but which
     ++	  * can be followed by parentheses without special characters in between,
      +	  * making them look like methods.
      +	  */
      +	 "!(^|[ \t]+)(do|while|for|foreach|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
     -+	 /* Methods/constructors:
     -+	  * the strategy is to identify a minimum of two groups (any combination of keywords/type/name),
     -+	  * without intermediate or final characters which can't be part of method definitions before the opening parenthesis.
     ++	 /*
     ++	  * Methods/constructors:
     ++	  * The strategy is to identify a minimum of two groups (any combination
     ++	  * of keywords/type/name), without intermediate characters which can't be
     ++	  * part of method definitions before the opening parenthesis, and without
     ++	  * final unexpected characters, normally only used in ordinary statements.
     ++	  * "=" is excluded to ignore assignments, but as a result rules out
     ++	  * methods with expression bodies. However, since those fit on 1-2 lines,
     ++	  * a chunk header isn't useful either way.
      +	  */
     -+	 "^[ \t]*(([][[:alnum:]@_<>.,]*[^=:{ \t][ \t]+[][[:alnum:]@_<>.,]*)+\\([^;]*)$\n"
     - 	 /* Properties */
     --	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
     -+	 "^[ \t]*((([][[:alnum:]@_<>.,]+)[ \t]+[][[:alnum:]@_]*)+[^=:;,()]*)$\n"
     ++	 "^[ \t]*(([][[:alnum:]@_<>.,]*[^=:{ \t+*%\\/\\-][ \t]+[][[:alnum:]@_<>.,]*)+\\([^;]*)$\n"
     ++	 /*
     ++	  * Properties:
     ++	  * As with methods, expect a minimum of two groups. And, the vast majority
     ++	  * of properties long enough to be worth showing a chunk header for don't
     ++	  * include "=:;,()" on the line they are defined.
     ++	  */
     ++	 "^[ \t]*([][[:alnum:]@_<>.,]+[ \t]+[][[:alnum:]@_.]+[^=:;,()]*)$\n"
       	 /* Type definitions */
       	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
       	 /* Namespace */


 t/t4018/csharp-method               |  10 +++
 t/t4018/csharp-method-explicit      |  12 +++
 t/t4018/csharp-method-generics      |  11 +++
 t/t4018/csharp-method-modifiers     |  13 +++
 t/t4018/csharp-method-multiline     |  10 +++
 t/t4018/csharp-method-params        |  10 +++
 t/t4018/csharp-method-skip-body     | 116 +++++++++++++++++++++++++++
 t/t4018/csharp-method-special-chars |  11 +++
 t/t4018/csharp-method-with-spacing  |  10 +++
 t/t4018/csharp-property             |  11 +++
 t/t4018/csharp-property-skip-body   | 120 ++++++++++++++++++++++++++++
 userdiff.c                          |  30 +++++--
 12 files changed, 358 insertions(+), 6 deletions(-)
 create mode 100644 t/t4018/csharp-method
 create mode 100644 t/t4018/csharp-method-explicit
 create mode 100644 t/t4018/csharp-method-generics
 create mode 100644 t/t4018/csharp-method-modifiers
 create mode 100644 t/t4018/csharp-method-multiline
 create mode 100644 t/t4018/csharp-method-params
 create mode 100644 t/t4018/csharp-method-skip-body
 create mode 100644 t/t4018/csharp-method-special-chars
 create mode 100644 t/t4018/csharp-method-with-spacing
 create mode 100644 t/t4018/csharp-property
 create mode 100644 t/t4018/csharp-property-skip-body

diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
new file mode 100644
index 00000000000..16b367aca2b
--- /dev/null
+++ b/t/t4018/csharp-method
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
new file mode 100644
index 00000000000..5a710116cc4
--- /dev/null
+++ b/t/t4018/csharp-method-explicit
@@ -0,0 +1,12 @@
+using System;
+
+class Example : IDisposable
+{
+    void IDisposable.Dispose() // RIGHT
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+    }
+}
diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
new file mode 100644
index 00000000000..b3216bfb2a7
--- /dev/null
+++ b/t/t4018/csharp-method-generics
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+    Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return null;
+    }
+}
diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
new file mode 100644
index 00000000000..caefa8ee99c
--- /dev/null
+++ b/t/t4018/csharp-method-modifiers
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+class Example
+{
+    static internal async Task Method(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        await Task.Delay(1);
+    }
+}
diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
new file mode 100644
index 00000000000..3983ff42f51
--- /dev/null
+++ b/t/t4018/csharp-method-multiline
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method_RIGHT(
+        int a,
+        int b,
+        int c)
+    {
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
new file mode 100644
index 00000000000..3f00410ba1f
--- /dev/null
+++ b/t/t4018/csharp-method-params
@@ -0,0 +1,10 @@
+class Example
+{
+    string Method(int RIGHT, int b, int c = 42)
+    {
+        // Filler
+        // Filler
+        
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-method-skip-body b/t/t4018/csharp-method-skip-body
new file mode 100644
index 00000000000..b3c7194eb74
--- /dev/null
+++ b/t/t4018/csharp-method-skip-body
@@ -0,0 +1,116 @@
+using System.Linq;
+using System;
+
+class Example : IDisposable
+{
+    string Method(int RIGHT)
+    {
+        // Method calls
+        MethodCall();
+        MethodCall(1, 2);
+        MethodCall(
+            1, 2);
+
+        // Assignments
+        var constantAssignment = "test";
+        var methodAssignment = MethodCall();
+        var multiLineMethodAssignment = MethodCall(
+        );
+        var multiLine = "first"
+            + MethodCall()
+            - MethodCall()
+            + MethodCall();
+
+        // Initializations/disposal
+        new Example();
+        new Example(
+            );
+        new Example { };
+        using (this)
+        {
+        }
+        var def =
+            this is default(
+                Example);
+
+        // Iteration statements
+        do { } while (true);
+        do MethodCall(
+        ); while (true);
+        while (true);
+        while (true) {
+            break;
+        }
+        for (int i = 0; i < 10; ++i)
+        {
+        }
+        foreach (int i in Enumerable.Range(0, 10))
+        {
+        }
+        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+        // Control
+        if (false)
+        {
+            return "out";
+        }
+        else { }
+        if (true) MethodCall(
+        );
+        else MethodCall(
+        );
+        switch ("test")
+        {
+            case "one":
+            return MethodCall(
+            );
+            case "two":
+            break;
+        }
+        (int, int) tuple = (1, 4);
+        switch (tuple)
+        {
+            case (1, 4):
+            MethodCall();
+        }
+
+        // Exceptions
+        try
+        {
+            throw new Exception("fail");
+        }
+        catch (Exception)
+        {
+        }
+        finally
+        {
+        }
+        try { } catch (Exception) {}
+        try
+        {
+            throw GetException(
+            );
+        }
+        catch (Exception) { }
+
+        // Others
+        lock (this)
+        {
+        }
+        unsafe
+        {
+            byte[] bytes = [1, 2, 3];
+            fixed (byte* pointerToFirst = bytes)
+            {
+            }
+        }
+
+        return "ChangeMe";
+    }
+
+    // Supporting methods to make the tested method above valid C#.
+    public void Dispose() {}
+    string MethodCall(int a = 0, int b = 0) => "test";
+    Exception GetException() => new Exception("fail");
+    int[] Numbers() => [0, 1];
+}
diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
new file mode 100644
index 00000000000..e6c7bc01a18
--- /dev/null
+++ b/t/t4018/csharp-method-special-chars
@@ -0,0 +1,11 @@
+class @Some_Type
+{
+    @Some_Type @Method_With_Underscore(int RIGHT)
+    {
+        // Filler
+        // Filler
+        
+        // ChangeMe
+        return new @Some_Type();
+    }
+}
diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
new file mode 100644
index 00000000000..da0c9b7e60c
--- /dev/null
+++ b/t/t4018/csharp-method-with-spacing
@@ -0,0 +1,10 @@
+class Example
+{
+		string   Method 	( int 	RIGHT )
+	{
+        // Filler
+        // Filler
+		
+        return "ChangeMe";
+    }
+}
diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
new file mode 100644
index 00000000000..e56dfce34c1
--- /dev/null
+++ b/t/t4018/csharp-property
@@ -0,0 +1,11 @@
+class Example
+{
+    public bool RIGHT
+    {
+        get { return true; }
+        set
+        {
+            // ChangeMe
+        }
+    }
+}
diff --git a/t/t4018/csharp-property-skip-body b/t/t4018/csharp-property-skip-body
new file mode 100644
index 00000000000..ad9d96007a8
--- /dev/null
+++ b/t/t4018/csharp-property-skip-body
@@ -0,0 +1,120 @@
+using System.Linq;
+using System;
+
+class Example : IDisposable
+{
+    public string RIGHT
+    {
+        get
+        {
+            // Method calls
+            MethodCall();
+            MethodCall(1, 2);
+            MethodCall(
+                1, 2);
+
+            // Assignments
+            var constantAssignment = "test";
+            var methodAssignment = MethodCall();
+            var multiLineMethodAssignment = MethodCall(
+                );
+            var multiLine = "first"
+                + MethodCall()
+                - MethodCall()
+                + MethodCall();
+
+            // Initializations/disposal
+            new Example();
+            new Example(
+                );
+            new Example { };
+            using (this)
+            {
+            }
+            var def =
+                this is default(
+                    Example);
+
+            // Iteration statements
+            do { } while (true);
+            do MethodCall(
+            ); while (true);
+            while (true);
+            while (true) {
+                break;
+            }
+            for (int i = 0; i < 10; ++i)
+            {
+            }
+            foreach (int i in Enumerable.Range(0, 10))
+            {
+            }
+            int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+            // Control
+            if (false)
+            {
+                return "out";
+            }
+            else { }
+            if (true) MethodCall(
+            );
+            else MethodCall(
+            );
+            switch ("test")
+            {
+                case "one":
+                return MethodCall(
+                );
+                case "two":
+                break;
+            }
+            (int, int) tuple = (1, 4);
+            switch (tuple)
+            {
+                case (1, 4):
+                MethodCall();
+            }
+
+            // Exceptions
+            try
+            {
+                throw new Exception("fail");
+            }
+            catch (Exception)
+            {
+            }
+            finally
+            {
+            }
+            try { } catch (Exception) {}
+            try
+            {
+                throw GetException(
+                );
+            }
+            catch (Exception) { }
+
+            // Others
+            lock (this)
+            {
+            }
+            unsafe
+            {
+                byte[] bytes = [1, 2, 3];
+                fixed (byte* pointerToFirst = bytes)
+                {
+                }
+            }
+
+            return "ChangeMe";
+        }
+        set { }
+    }
+
+    // Supporting methods to make the tested property above valid C#.
+    public void Dispose() {}
+    string MethodCall(int a = 0, int b = 0) => "test";
+    Exception GetException() => new Exception("fail");
+    int[] Numbers() => [0, 1];
+}
diff --git a/userdiff.c b/userdiff.c
index e399543823b..5440ccf2de5 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -89,12 +89,30 @@ PATTERNS("cpp",
 	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
 	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
 PATTERNS("csharp",
-	 /* Keywords */
-	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
-	 /* Methods and constructors */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
-	 /* Properties */
-	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+	 /*
+	  * Jump over reserved keywords which are illegal method names, but which
+	  * can be followed by parentheses without special characters in between,
+	  * making them look like methods.
+	  */
+	 "!(^|[ \t]+)(do|while|for|foreach|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
+	 /*
+	  * Methods/constructors:
+	  * The strategy is to identify a minimum of two groups (any combination
+	  * of keywords/type/name), without intermediate characters which can't be
+	  * part of method definitions before the opening parenthesis, and without
+	  * final unexpected characters, normally only used in ordinary statements.
+	  * "=" is excluded to ignore assignments, but as a result rules out
+	  * methods with expression bodies. However, since those fit on 1-2 lines,
+	  * a chunk header isn't useful either way.
+	  */
+	 "^[ \t]*(([][[:alnum:]@_<>.,]*[^=:{ \t+*%\\/\\-][ \t]+[][[:alnum:]@_<>.,]*)+\\([^;]*)$\n"
+	 /*
+	  * Properties:
+	  * As with methods, expect a minimum of two groups. And, the vast majority
+	  * of properties long enough to be worth showing a chunk header for don't
+	  * include "=:;,()" on the line they are defined.
+	  */
+	 "^[ \t]*([][[:alnum:]@_<>.,]+[ \t]+[][[:alnum:]@_.]+[^=:;,()]*)$\n"
 	 /* Type definitions */
 	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
 	 /* Namespace */

base-commit: f41f85c9ec8d4d46de0fd5fded88db94d3ec8c11
-- 
gitgitgadget


^ permalink raw reply related	[relevance 1%]

* Re: [PATCH v2] userdiff: better method/property matching for C#
  @ 2024-03-27  7:30  0%   ` Johannes Sixt
  2024-03-28  8:07  1%   ` [PATCH v3] " Steven Jeuris via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Johannes Sixt @ 2024-03-27  7:30 UTC (permalink / raw)
  To: Steven Jeuris via GitGitGadget
  Cc: Ævar Arnfjörð Bjarmason, Jeff King, Steven Jeuris,
	Steven Jeuris, git

Am 06.03.24 um 21:21 schrieb Steven Jeuris via GitGitGadget:
> From: Steven Jeuris <steven.jeuris@3shape.com>
> 
> - Support multi-line methods by not requiring closing parenthesis.
> - Support multiple generics (comma was missing before).
> - Add missing `foreach`, `lock` and  `fixed` keywords to skip over.
> - Remove `instanceof` keyword, which isn't C#.
> - Also detect non-method keywords not positioned at the start of a line.
> - Added tests; none existed before.
> 
> The overall strategy is to focus more on what isn't expected for
> method/property definitions, instead of what is, but is fully optional.
> 
> Signed-off-by: Steven Jeuris <steven.jeuris@gmail.com>
> ---

I like the comprehensive test cases that are added. However, I found one
major point in the patterns that must be considered. See below.

>     userdiff: better method/property matching for C#
>     
>     Change since v1: I removed "from" from the list of keywords to skip.
>     First, I considered adding "await", but I discovered both "await" and
>     "from" are "contextual keywords", which unlike the other keywords
>     currently listed, aren't reserved, and can thus cause false negatives.
>     I.e., it is valid to have a method named "await" or "from". In edge
>     cases, this may lead to false positives, but a different exclusion rule
>     will need to be added to handle these.

So, this patch makes the choice to have some false positives instead of
some false negatives. I have no experience in C#, so I trust your
judgement that users are better served with this choice rather than the
opposite.

> 
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1682%2FWhathecode%2Fmaster-v2
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1682/Whathecode/master-v2
> Pull-Request: https://github.com/git/git/pull/1682
> 
> Range-diff vs v1:
> 
>  1:  cdd8dd4d871 ! 1:  00315519014 userdiff: better method/property matching for C#
>      @@ Commit message
>       
>           - Support multi-line methods by not requiring closing parenthesis.
>           - Support multiple generics (comma was missing before).
>      -    - Add missing `foreach`, `from`, `lock` and  `fixed` keywords to skip over.
>      +    - Add missing `foreach`, `lock` and  `fixed` keywords to skip over.
>           - Remove `instanceof` keyword, which isn't C#.
>           - Also detect non-method keywords not positioned at the start of a line.
>           - Added tests; none existed before.
>      @@ t/t4018/csharp-method-skip-body (new)
>       +		{
>       +		}
>       +		int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
>      -+		var test =
>      -+			from num in Numbers(
>      -+		 	) select num;
>       +		
>       +		// Control
>       +		if (false)
>      @@ userdiff.c: PATTERNS("cpp",
>       +	  * Jump over keywords not used by methods which can be followed by parentheses without special characters in between,
>       +	  * making them look like methods.
>       +	  */
>      -+	 "!(^|[ \t]+)(do|while|for|foreach|from|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
>      ++	 "!(^|[ \t]+)(do|while|for|foreach|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
>       +	 /* Methods/constructors:
>       +	  * the strategy is to identify a minimum of two groups (any combination of keywords/type/name),
>       +	  * without intermediate or final characters which can't be part of method definitions before the opening parenthesis.
> 
> 
>  t/t4018/csharp-method               |  10 +++
>  t/t4018/csharp-method-explicit      |  12 +++
>  t/t4018/csharp-method-generics      |  11 +++
>  t/t4018/csharp-method-modifiers     |  13 ++++
>  t/t4018/csharp-method-multiline     |  10 +++
>  t/t4018/csharp-method-params        |  10 +++
>  t/t4018/csharp-method-skip-body     | 112 ++++++++++++++++++++++++++++
>  t/t4018/csharp-method-special-chars |  11 +++
>  t/t4018/csharp-method-with-spacing  |  10 +++
>  t/t4018/csharp-property             |  11 +++
>  userdiff.c                          |  16 ++--
>  11 files changed, 221 insertions(+), 5 deletions(-)
>  create mode 100644 t/t4018/csharp-method
>  create mode 100644 t/t4018/csharp-method-explicit
>  create mode 100644 t/t4018/csharp-method-generics
>  create mode 100644 t/t4018/csharp-method-modifiers
>  create mode 100644 t/t4018/csharp-method-multiline
>  create mode 100644 t/t4018/csharp-method-params
>  create mode 100644 t/t4018/csharp-method-skip-body
>  create mode 100644 t/t4018/csharp-method-special-chars
>  create mode 100644 t/t4018/csharp-method-with-spacing
>  create mode 100644 t/t4018/csharp-property
> 
> diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
> new file mode 100644
> index 00000000000..85ff0cb8b5b
> --- /dev/null
> +++ b/t/t4018/csharp-method
> @@ -0,0 +1,10 @@
> +class Example
> +{
> +	string Method(int RIGHT)
> +	{
> +		// Filler
> +		// Filler
> +		
> +		return "ChangeMe";
> +	}
> +}
> diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
> new file mode 100644
> index 00000000000..083aa094ce2
> --- /dev/null
> +++ b/t/t4018/csharp-method-explicit
> @@ -0,0 +1,12 @@
> +using System;
> +
> +class Example : IDisposable
> +{
> +	void IDisposable.Dispose() // RIGHT
> +	{
> +		// Filler
> +		// Filler
> +		
> +		// ChangeMe
> +	}
> +}
> diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
> new file mode 100644
> index 00000000000..c472d4a18df
> --- /dev/null
> +++ b/t/t4018/csharp-method-generics
> @@ -0,0 +1,11 @@
> +class Example<T1, T2>
> +{
> +	Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
> +	{
> +		// Filler
> +		// Filler
> +		
> +		// ChangeMe
> +		return null;
> +	}
> +}
> diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
> new file mode 100644
> index 00000000000..f1c008a4749
> --- /dev/null
> +++ b/t/t4018/csharp-method-modifiers
> @@ -0,0 +1,13 @@
> +using System.Threading.Tasks;
> +
> +class Example
> +{
> +	static internal async Task Method(int RIGHT)
> +	{
> +		// Filler
> +		// Filler
> +		
> +		// ChangeMe
> +		await Task.Delay(1);
> +	}
> +}
> diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
> new file mode 100644
> index 00000000000..0a20b0cb49c
> --- /dev/null
> +++ b/t/t4018/csharp-method-multiline
> @@ -0,0 +1,10 @@
> +class Example
> +{
> +	string Method_RIGHT(
> +		int a,
> +		int b,
> +		int c)
> +	{
> +		return "ChangeMe";
> +	}
> +}
> diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
> new file mode 100644
> index 00000000000..18598449008
> --- /dev/null
> +++ b/t/t4018/csharp-method-params
> @@ -0,0 +1,10 @@
> +class Example
> +{
> +	string Method(int RIGHT, int b, int c = 42)
> +	{
> +		// Filler
> +		// Filler
> +		
> +		return "ChangeMe";
> +	}
> +}
> diff --git a/t/t4018/csharp-method-skip-body b/t/t4018/csharp-method-skip-body
> new file mode 100644
> index 00000000000..c8c9621634d
> --- /dev/null
> +++ b/t/t4018/csharp-method-skip-body
> @@ -0,0 +1,112 @@
> +using System.Linq;
> +using System;
> +
> +class Example : IDisposable
> +{
> +	string Method(int RIGHT)
> +	{
> +		// Method calls
> +		MethodCall();
> +		MethodCall(1, 2);
> +		MethodCall(
> +			1, 2);
> +		
> +		// Assignments
> +		var constantAssignment = "test";
> +		var methodAssignment = MethodCall();
> +		var multiLineMethodAssignment = MethodCall(
> +			);
> +		
> +		// Initializations/disposal
> +		new Example();
> +		new Example(
> +			);
> +		new Example { };
> +		using (this) 
> +		{
> +		}
> +		var def =
> +			this is default(
> +				Example);
> +		
> +		// Iteration statements
> +		do { } while (true);
> +		do MethodCall(
> +			); while (true);
> +		while (true);
> +		while (true) {
> +			break;
> +		}
> +		for (int i = 0; i < 10; ++i)
> +		{
> +		}
> +		foreach (int i in Enumerable.Range(0, 10))
> +		{
> +		}
> +		int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
> +		
> +		// Control
> +		if (false)
> +		{
> +			return "out";
> +		}
> +		else { }
> +		if (true) MethodCall(
> +			);
> +		else MethodCall(
> +			);
> +		switch ("test")
> +		{
> +			case "one":
> +				return MethodCall(
> +					);
> +			case "two":
> +				break;
> +		}
> +		(int, int) tuple = (1, 4);
> +		switch (tuple)
> +		{
> +			case (1, 4):
> +				MethodCall();
> +		}
> +		
> +		// Exceptions
> +		try
> +		{
> +			throw new Exception("fail");
> +		}
> +		catch (Exception)
> +		{
> +		}
> +		finally
> +		{
> +		}
> +		try { } catch (Exception) {}
> +		try
> +		{
> +			throw GetException(
> +				);
> +		}
> +		catch (Exception) { }
> +		
> +		// Others
> +		lock (this)
> +		{
> +		}
> +		unsafe
> +		{
> +			byte[] bytes = [1, 2, 3];
> +			fixed (byte* pointerToFirst = bytes)
> +			{
> +			}
> +		}
> +		
> +		return "ChangeMe";
> +	}

Nice! This tests the exlusion patterns.

> +	
> +	public void Dispose() {}
> +	
> +	string MethodCall(int a = 0, int b = 0) => "test";
> +	Exception GetException() => new Exception("fail");
> +	int[] Numbers() => [0, 1];

Are these also patterns that should not be picked up? If so, should the
"ChangeMe" not be later in the file? As written, they would never be
candidates because there is no diff hunk later in the file.

> +}
> diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
> new file mode 100644
> index 00000000000..ec3565fd000
> --- /dev/null
> +++ b/t/t4018/csharp-method-special-chars
> @@ -0,0 +1,11 @@
> +class @Some_Type
> +{
> +	@Some_Type @Method_With_Underscore(int RIGHT)
> +	{
> +		// Filler
> +		// Filler
> +		
> +		// ChangeMe
> +		return new @Some_Type();
> +	}
> +}
> diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
> new file mode 100644
> index 00000000000..4143929a711
> --- /dev/null
> +++ b/t/t4018/csharp-method-with-spacing
> @@ -0,0 +1,10 @@
> +class Example
> +{
> +		string   Method 	( int 	RIGHT )
> +	{
> +		// Filler
> +		// Filler
> +		
> +		return "ChangeMe";
> +	}
> +}
> diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
> new file mode 100644
> index 00000000000..1792117f964
> --- /dev/null
> +++ b/t/t4018/csharp-property
> @@ -0,0 +1,11 @@
> +class Example
> +{
> +	public bool RIGHT
> +    {
> +        get { return true; }
> +        set
> +        {
> +            // ChangeMe
> +        }
> +    }
> +}
> diff --git a/userdiff.c b/userdiff.c
> index e399543823b..5a9e8a0ef55 100644
> --- a/userdiff.c
> +++ b/userdiff.c
> @@ -89,12 +89,18 @@ PATTERNS("cpp",
>  	 "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
>  	 "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
>  PATTERNS("csharp",
> -	 /* Keywords */
> -	 "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
> -	 /* Methods and constructors */
> -	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
> +	 /*
> +	  * Jump over keywords not used by methods which can be followed by parentheses without special characters in between,
> +	  * making them look like methods.
> +	  */
> +	 "!(^|[ \t]+)(do|while|for|foreach|if|else|new|default|return|switch|case|throw|catch|using|lock|fixed)([ \t(]+|$)\n"
> +	 /* Methods/constructors:
> +	  * the strategy is to identify a minimum of two groups (any combination of keywords/type/name),
> +	  * without intermediate or final characters which can't be part of method definitions before the opening parenthesis.
> +	  */

I would have appreciated if the comment lines were not so long. We
already have many long lines of code in this file, but comment lines
generally stay below the 80 character limit. Also watch out for the
multi-line comment style (put the opening /* on a line by itself, like
you did in the comment above).

> +	 "^[ \t]*(([][[:alnum:]@_<>.,]*[^=:{ \t][ \t]+[][[:alnum:]@_<>.,]*)+\\([^;]*)$\n"

I am not 100% sure about the meaning of this expression. But would it
not match this line, which looks like part of a multi-line expression:

	+ func(x)

and produce a false positive? [Testing myself...] Yes it does. I do not
know if it is worth fixing as I do not do C#. But if this were patterns
for C or C++, I would consider it a major problem.

>  	 /* Properties */
> -	 "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
> +	 "^[ \t]*((([][[:alnum:]@_<>.,]+)[ \t]+[][[:alnum:]@_]*)+[^=:;,()]*)$\n"

OK. Since the second of the two words is optional and almost the same as
the first, I have a gut feeling that the pattern can be expensive to
match. But since I do not have data to back up my suspicion, let's leave
it as it is.

>  	 /* Type definitions */
>  	 "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
>  	 /* Namespace */>
> base-commit: f41f85c9ec8d4d46de0fd5fded88db94d3ec8c11

-- Hannes



^ permalink raw reply	[relevance 0%]

* Re: [PATCH 03/13] http: use new headers for each object request
  2024-03-24  1:12  4% ` [PATCH 03/13] http: use new headers for each object request brian m. carlson
@ 2024-03-27  8:02  0%   ` Patrick Steinhardt
  0 siblings, 0 replies; 200+ results
From: Patrick Steinhardt @ 2024-03-27  8:02 UTC (permalink / raw)
  To: brian m. carlson; +Cc: git, Junio C Hamano, Matthew John Cheetham, M Hickford

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

On Sun, Mar 24, 2024 at 01:12:51AM +0000, brian m. carlson wrote:
> Currently we create one set of headers for all object requests and reuse
> it.  However, we'll need to adjust the headers for authentication
> purposes in the future, so let's create a new set for each request so
> that we can adjust them if the authentication changes.
> 
> Note that the cost of allocation here is tiny compared to the fact that
> we're making a network call, not to mention probably a full TLS
> connection, so this shouldn't have a significant impact on performance.
> Moreover, nobody who cares about performance is using the dumb HTTP
> protocol anyway, since it often makes huge numbers of requests compared
> to the smart protocol.
> 
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---
>  http.c | 19 +++++++++++--------
>  http.h |  2 ++
>  2 files changed, 13 insertions(+), 8 deletions(-)
> 
> diff --git a/http.c b/http.c
> index e73b136e58..1c2200da77 100644
> --- a/http.c
> +++ b/http.c
> @@ -128,7 +128,6 @@ static unsigned long empty_auth_useless =
>  	| CURLAUTH_DIGEST;
>  
>  static struct curl_slist *pragma_header;
> -static struct curl_slist *no_pragma_header;
>  static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
>  
>  static struct curl_slist *host_resolutions;

Nice to see that this also allows us to get rid of one more global
variable.

> @@ -299,6 +298,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
>  	return nmemb;
>  }
>  
> +static struct curl_slist *object_request_headers(void)
> +{
> +	return curl_slist_append(http_copy_default_headers(), "Pragma:");
> +}
> +
>  static void closedown_active_slot(struct active_request_slot *slot)
>  {
>  	active_requests--;
> @@ -1275,8 +1279,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
>  
>  	pragma_header = curl_slist_append(http_copy_default_headers(),
>  		"Pragma: no-cache");
> -	no_pragma_header = curl_slist_append(http_copy_default_headers(),
> -		"Pragma:");
>  
>  	{
>  		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
> @@ -1360,8 +1362,6 @@ void http_cleanup(void)
>  	curl_slist_free_all(pragma_header);
>  	pragma_header = NULL;
>  
> -	curl_slist_free_all(no_pragma_header);
> -	no_pragma_header = NULL;
>  

Nit: this leaves behind two consecutive empty lines.

Patrick

>  	curl_slist_free_all(host_resolutions);
>  	host_resolutions = NULL;
> @@ -2370,6 +2370,7 @@ void release_http_pack_request(struct http_pack_request *preq)
>  	}
>  	preq->slot = NULL;
>  	strbuf_release(&preq->tmpfile);
> +	curl_slist_free_all(preq->headers);
>  	free(preq->url);
>  	free(preq);
>  }
> @@ -2454,11 +2455,11 @@ struct http_pack_request *new_direct_http_pack_request(
>  	}
>  
>  	preq->slot = get_active_slot();
> +	preq->headers = object_request_headers();
>  	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
>  	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
>  	curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
> -	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
> -		no_pragma_header);
> +	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
>  
>  	/*
>  	 * If there is data present from a previous transfer attempt,
> @@ -2624,13 +2625,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
>  	}
>  
>  	freq->slot = get_active_slot();
> +	freq->headers = object_request_headers();
>  
>  	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
>  	curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
>  	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
>  	curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
>  	curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
> -	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
> +	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
>  
>  	/*
>  	 * If we have successfully processed data from a previous fetch
> @@ -2718,5 +2720,6 @@ void release_http_object_request(struct http_object_request *freq)
>  		release_active_slot(freq->slot);
>  		freq->slot = NULL;
>  	}
> +	curl_slist_free_all(freq->headers);
>  	strbuf_release(&freq->tmpfile);
>  }
> diff --git a/http.h b/http.h
> index 3af19a8bf5..c5f8cc4620 100644
> --- a/http.h
> +++ b/http.h
> @@ -196,6 +196,7 @@ struct http_pack_request {
>  	FILE *packfile;
>  	struct strbuf tmpfile;
>  	struct active_request_slot *slot;
> +	struct curl_slist *headers;
>  };
>  
>  struct http_pack_request *new_http_pack_request(
> @@ -229,6 +230,7 @@ struct http_object_request {
>  	int zret;
>  	int rename;
>  	struct active_request_slot *slot;
> +	struct curl_slist *headers;
>  };
>  
>  struct http_object_request *new_http_object_request(
> 

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

^ permalink raw reply	[relevance 0%]

* [PATCH 07/11] midx: move `write_midx_internal` (and related functions) to midx-write.c
  @ 2024-03-25 17:24  1% ` Taylor Blau
    1 sibling, 0 replies; 200+ results
From: Taylor Blau @ 2024-03-25 17:24 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

Move the last writing-related function from midx.c to midx-write.c. This
patch moves `write_midx_internal()`, along with all of the functions
used to implement it into midx-write.c.

Like previous patch, this patch too does not introduce any functional
changes, and is best moved with `--color-moved`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 midx-write.c | 1243 ++++++++++++++++++++++++++++++++++++++++++++++-
 midx.c       | 1296 +-------------------------------------------------
 midx.h       |   19 +
 3 files changed, 1272 insertions(+), 1286 deletions(-)

diff --git a/midx-write.c b/midx-write.c
index 3d7697d8a2..c812156cbd 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1,22 +1,1257 @@
 #include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
 #include "hex.h"
+#include "lockfile.h"
 #include "packfile.h"
+#include "object-file.h"
+#include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
+#include "trace2.h"
 #include "run-command.h"
+#include "chunk-format.h"
 #include "pack-bitmap.h"
+#include "refs.h"
 #include "revision.h"
+#include "list-objects.h"
 
-extern int write_midx_internal(const char *object_dir,
+#define PACK_EXPIRED UINT_MAX
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
+#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+
+extern int midx_checksum_valid(struct multi_pack_index *m);
+extern void clear_midx_files_ext(const char *object_dir, const char *ext,
+				 unsigned char *keep_hash);
+extern int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+				const char *idx_name);
+
+static size_t write_midx_header(struct hashfile *f,
+				unsigned char num_chunks,
+				uint32_t num_packs)
+{
+	hashwrite_be32(f, MIDX_SIGNATURE);
+	hashwrite_u8(f, MIDX_VERSION);
+	hashwrite_u8(f, oid_version(the_hash_algo));
+	hashwrite_u8(f, num_chunks);
+	hashwrite_u8(f, 0); /* unused */
+	hashwrite_be32(f, num_packs);
+
+	return MIDX_HEADER_SIZE;
+}
+
+struct pack_info {
+	uint32_t orig_pack_int_id;
+	char *pack_name;
+	struct packed_git *p;
+
+	uint32_t bitmap_pos;
+	uint32_t bitmap_nr;
+
+	unsigned expired : 1;
+};
+
+static void fill_pack_info(struct pack_info *info,
+			   struct packed_git *p, const char *pack_name,
+			   uint32_t orig_pack_int_id)
+{
+	memset(info, 0, sizeof(struct pack_info));
+
+	info->orig_pack_int_id = orig_pack_int_id;
+	info->pack_name = xstrdup(pack_name);
+	info->p = p;
+	info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
+static int pack_info_compare(const void *_a, const void *_b)
+{
+	struct pack_info *a = (struct pack_info *)_a;
+	struct pack_info *b = (struct pack_info *)_b;
+	return strcmp(a->pack_name, b->pack_name);
+}
+
+static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
+{
+	const char *pack_name = _va;
+	const struct pack_info *compar = _vb;
+
+	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
+}
+
+struct write_midx_context {
+	struct pack_info *info;
+	size_t nr;
+	size_t alloc;
+	struct multi_pack_index *m;
+	struct progress *progress;
+	unsigned pack_paths_checked;
+
+	struct pack_midx_entry *entries;
+	size_t entries_nr;
+
+	uint32_t *pack_perm;
+	uint32_t *pack_order;
+	unsigned large_offsets_needed:1;
+	uint32_t num_large_offsets;
+
+	int preferred_pack_idx;
+
+	struct string_list *to_include;
+};
+
+static void add_pack_to_midx(const char *full_path, size_t full_path_len,
+			     const char *file_name, void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct packed_git *p;
+
+	if (ends_with(file_name, ".idx")) {
+		display_progress(ctx->progress, ++ctx->pack_paths_checked);
+		/*
+		 * Note that at most one of ctx->m and ctx->to_include are set,
+		 * so we are testing midx_contains_pack() and
+		 * string_list_has_string() independently (guarded by the
+		 * appropriate NULL checks).
+		 *
+		 * We could support passing to_include while reusing an existing
+		 * MIDX, but don't currently since the reuse process drags
+		 * forward all packs from an existing MIDX (without checking
+		 * whether or not they appear in the to_include list).
+		 *
+		 * If we added support for that, these next two conditional
+		 * should be performed independently (likely checking
+		 * to_include before the existing MIDX).
+		 */
+		if (ctx->m && midx_contains_pack(ctx->m, file_name))
+			return;
+		else if (ctx->to_include &&
+			 !string_list_has_string(ctx->to_include, file_name))
+			return;
+
+		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+		p = add_packed_git(full_path, full_path_len, 0);
+		if (!p) {
+			warning(_("failed to add packfile '%s'"),
+				full_path);
+			return;
+		}
+
+		if (open_pack_index(p)) {
+			warning(_("failed to open pack-index '%s'"),
+				full_path);
+			close_pack(p);
+			free(p);
+			return;
+		}
+
+		fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
+		ctx->nr++;
+	}
+}
+
+struct pack_midx_entry {
+	struct object_id oid;
+	uint32_t pack_int_id;
+	time_t pack_mtime;
+	uint64_t offset;
+	unsigned preferred : 1;
+};
+
+static int midx_oid_compare(const void *_a, const void *_b)
+{
+	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
+	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
+	int cmp = oidcmp(&a->oid, &b->oid);
+
+	if (cmp)
+		return cmp;
+
+	/* Sort objects in a preferred pack first when multiple copies exist. */
+	if (a->preferred > b->preferred)
+		return -1;
+	if (a->preferred < b->preferred)
+		return 1;
+
+	if (a->pack_mtime > b->pack_mtime)
+		return -1;
+	else if (a->pack_mtime < b->pack_mtime)
+		return 1;
+
+	return a->pack_int_id - b->pack_int_id;
+}
+
+static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
+				      struct pack_midx_entry *e,
+				      uint32_t pos)
+{
+	if (pos >= m->num_objects)
+		return 1;
+
+	nth_midxed_object_oid(&e->oid, m, pos);
+	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
+	e->offset = nth_midxed_offset(m, pos);
+
+	/* consider objects in midx to be from "old" packs */
+	e->pack_mtime = 0;
+	return 0;
+}
+
+static void fill_pack_entry(uint32_t pack_int_id,
+			    struct packed_git *p,
+			    uint32_t cur_object,
+			    struct pack_midx_entry *entry,
+			    int preferred)
+{
+	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
+		die(_("failed to locate object %d in packfile"), cur_object);
+
+	entry->pack_int_id = pack_int_id;
+	entry->pack_mtime = p->mtime;
+
+	entry->offset = nth_packed_object_offset(p, cur_object);
+	entry->preferred = !!preferred;
+}
+
+struct midx_fanout {
+	struct pack_midx_entry *entries;
+	size_t nr, alloc;
+};
+
+static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
+{
+	if (nr < fanout->nr)
+		BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
+		    (uintmax_t)nr, (uintmax_t)fanout->nr);
+	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
+}
+
+static void midx_fanout_sort(struct midx_fanout *fanout)
+{
+	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
+}
+
+static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
+					struct multi_pack_index *m,
+					uint32_t cur_fanout,
+					int preferred_pack)
+{
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
+	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		if ((preferred_pack > -1) &&
+		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
+			/*
+			 * Objects from preferred packs are added
+			 * separately.
+			 */
+			continue;
+		}
+
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		nth_midxed_pack_midx_entry(m,
+					   &fanout->entries[fanout->nr],
+					   cur_object);
+		fanout->entries[fanout->nr].preferred = 0;
+		fanout->nr++;
+	}
+}
+
+static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
+					struct pack_info *info,
+					uint32_t cur_pack,
+					int preferred,
+					uint32_t cur_fanout)
+{
+	struct packed_git *pack = info[cur_pack].p;
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = get_pack_fanout(pack, cur_fanout - 1);
+	end = get_pack_fanout(pack, cur_fanout);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		fill_pack_entry(cur_pack,
+				info[cur_pack].p,
+				cur_object,
+				&fanout->entries[fanout->nr],
+				preferred);
+		fanout->nr++;
+	}
+}
+
+/*
+ * It is possible to artificially get into a state where there are many
+ * duplicate copies of objects. That can create high memory pressure if
+ * we are to create a list of all objects before de-duplication. To reduce
+ * this memory pressure without a significant performance drop, automatically
+ * group objects by the first byte of their object id. Use the IDX fanout
+ * tables to group the data, copy to a local array, then sort.
+ *
+ * Copy only the de-duplicated entries (selected by most-recent modified time
+ * of a packfile containing the object).
+ */
+static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
+						  struct pack_info *info,
+						  uint32_t nr_packs,
+						  size_t *nr_objects,
+						  int preferred_pack)
+{
+	uint32_t cur_fanout, cur_pack, cur_object;
+	size_t alloc_objects, total_objects = 0;
+	struct midx_fanout fanout = { 0 };
+	struct pack_midx_entry *deduplicated_entries = NULL;
+	uint32_t start_pack = m ? m->num_packs : 0;
+
+	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+		total_objects = st_add(total_objects,
+				       info[cur_pack].p->num_objects);
+
+	/*
+	 * As we de-duplicate by fanout value, we expect the fanout
+	 * slices to be evenly distributed, with some noise. Hence,
+	 * allocate slightly more than one 256th.
+	 */
+	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
+
+	ALLOC_ARRAY(fanout.entries, fanout.alloc);
+	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
+	*nr_objects = 0;
+
+	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
+		fanout.nr = 0;
+
+		if (m)
+			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
+						    preferred_pack);
+
+		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
+			int preferred = cur_pack == preferred_pack;
+			midx_fanout_add_pack_fanout(&fanout,
+						    info, cur_pack,
+						    preferred, cur_fanout);
+		}
+
+		if (-1 < preferred_pack && preferred_pack < start_pack)
+			midx_fanout_add_pack_fanout(&fanout, info,
+						    preferred_pack, 1,
+						    cur_fanout);
+
+		midx_fanout_sort(&fanout);
+
+		/*
+		 * The batch is now sorted by OID and then mtime (descending).
+		 * Take only the first duplicate.
+		 */
+		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
+			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
+						&fanout.entries[cur_object].oid))
+				continue;
+
+			ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+				   alloc_objects);
+			memcpy(&deduplicated_entries[*nr_objects],
+			       &fanout.entries[cur_object],
+			       sizeof(struct pack_midx_entry));
+			(*nr_objects)++;
+		}
+	}
+
+	free(fanout.entries);
+	return deduplicated_entries;
+}
+
+static int write_midx_pack_names(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
+	size_t written = 0;
+
+	for (i = 0; i < ctx->nr; i++) {
+		size_t writelen;
+
+		if (ctx->info[i].expired)
+			continue;
+
+		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
+			BUG("incorrect pack-file order: %s before %s",
+			    ctx->info[i - 1].pack_name,
+			    ctx->info[i].pack_name);
+
+		writelen = strlen(ctx->info[i].pack_name) + 1;
+		hashwrite(f, ctx->info[i].pack_name, writelen);
+		written += writelen;
+	}
+
+	/* add padding to be aligned */
+	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
+	if (i < MIDX_CHUNK_ALIGNMENT) {
+		memset(padding, 0, sizeof(padding));
+		hashwrite(f, padding, i);
+	}
+
+	return 0;
+}
+
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	size_t i;
+
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[i];
+		if (pack->expired)
+			continue;
+
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+			BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+			    pack->pack_name, pack->bitmap_nr);
+
+		hashwrite_be32(f, pack->bitmap_pos);
+		hashwrite_be32(f, pack->bitmap_nr);
+	}
+	return 0;
+}
+
+static int write_midx_oid_fanout(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
+	uint32_t count = 0;
+	uint32_t i;
+
+	/*
+	* Write the first-level table (the list is sorted,
+	* but we use a 256-entry lookup to be able to avoid
+	* having to do eight extra binary search iterations).
+	*/
+	for (i = 0; i < 256; i++) {
+		struct pack_midx_entry *next = list;
+
+		while (next < last && next->oid.hash[0] == i) {
+			count++;
+			next++;
+		}
+
+		hashwrite_be32(f, count);
+		list = next;
+	}
+
+	return 0;
+}
+
+static int write_midx_oid_lookup(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	unsigned char hash_len = the_hash_algo->rawsz;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (i < ctx->entries_nr - 1) {
+			struct pack_midx_entry *next = list;
+			if (oidcmp(&obj->oid, &next->oid) >= 0)
+				BUG("OIDs not in order: %s >= %s",
+				    oid_to_hex(&obj->oid),
+				    oid_to_hex(&next->oid));
+		}
+
+		hashwrite(f, obj->oid.hash, (int)hash_len);
+	}
+
+	return 0;
+}
+
+static int write_midx_object_offsets(struct hashfile *f,
+				     void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i, nr_large_offset = 0;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
+			BUG("object %s is in an expired pack with int-id %d",
+			    oid_to_hex(&obj->oid),
+			    obj->pack_int_id);
+
+		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
+
+		if (ctx->large_offsets_needed && obj->offset >> 31)
+			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
+		else if (!ctx->large_offsets_needed && obj->offset >> 32)
+			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
+			    oid_to_hex(&obj->oid),
+			    obj->offset);
+		else
+			hashwrite_be32(f, (uint32_t)obj->offset);
+	}
+
+	return 0;
+}
+
+static int write_midx_large_offsets(struct hashfile *f,
+				    void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
+	uint32_t nr_large_offset = ctx->num_large_offsets;
+
+	while (nr_large_offset) {
+		struct pack_midx_entry *obj;
+		uint64_t offset;
+
+		if (list >= end)
+			BUG("too many large-offset objects");
+
+		obj = list++;
+		offset = obj->offset;
+
+		if (!(offset >> 31))
+			continue;
+
+		hashwrite_be64(f, offset);
+
+		nr_large_offset--;
+	}
+
+	return 0;
+}
+
+static int write_midx_revindex(struct hashfile *f,
+			       void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++)
+		hashwrite_be32(f, ctx->pack_order[i]);
+
+	return 0;
+}
+
+struct midx_pack_order_data {
+	uint32_t nr;
+	uint32_t pack;
+	off_t offset;
+};
+
+static int midx_pack_order_cmp(const void *va, const void *vb)
+{
+	const struct midx_pack_order_data *a = va, *b = vb;
+	if (a->pack < b->pack)
+		return -1;
+	else if (a->pack > b->pack)
+		return 1;
+	else if (a->offset < b->offset)
+		return -1;
+	else if (a->offset > b->offset)
+		return 1;
+	else
+		return 0;
+}
+
+static uint32_t *midx_pack_order(struct write_midx_context *ctx)
+{
+	struct midx_pack_order_data *data;
+	uint32_t *pack_order;
+	uint32_t i;
+
+	trace2_region_enter("midx", "midx_pack_order", the_repository);
+
+	ALLOC_ARRAY(data, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[i];
+		data[i].nr = i;
+		data[i].pack = ctx->pack_perm[e->pack_int_id];
+		if (!e->preferred)
+			data[i].pack |= (1U << 31);
+		data[i].offset = e->offset;
+	}
+
+	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
+
+	ALLOC_ARRAY(pack_order, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = i;
+		pack->bitmap_nr++;
+		pack_order[i] = data[i].nr;
+	}
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = 0;
+	}
+	free(data);
+
+	trace2_region_leave("midx", "midx_pack_order", the_repository);
+
+	return pack_order;
+}
+
+static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
+				     struct write_midx_context *ctx)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *tmp_file;
+
+	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
+
+	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
+
+	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
+					midx_hash, WRITE_REV);
+
+	if (finalize_object_file(tmp_file, buf.buf))
+		die(_("cannot store reverse index file"));
+
+	strbuf_release(&buf);
+
+	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
+}
+
+static void prepare_midx_packing_data(struct packing_data *pdata,
+				      struct write_midx_context *ctx)
+{
+	uint32_t i;
+
+	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
+
+	memset(pdata, 0, sizeof(struct packing_data));
+	prepare_packing_data(the_repository, pdata);
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
+		struct object_entry *to = packlist_alloc(pdata, &from->oid);
+
+		oe_set_in_pack(pdata, to,
+			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
+	}
+
+	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
+}
+
+static int add_ref_to_pending(const char *refname,
+			      const struct object_id *oid,
+			      int flag, void *cb_data)
+{
+	struct rev_info *revs = (struct rev_info*)cb_data;
+	struct object_id peeled;
+	struct object *object;
+
+	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
+		warning("symbolic ref is dangling: %s", refname);
+		return 0;
+	}
+
+	if (!peel_iterated_oid(oid, &peeled))
+		oid = &peeled;
+
+	object = parse_object_or_die(oid, refname);
+	if (object->type != OBJ_COMMIT)
+		return 0;
+
+	add_pending_object(revs, object, "");
+	if (bitmap_is_preferred_refname(revs->repo, refname))
+		object->flags |= NEEDS_BITMAP;
+	return 0;
+}
+
+struct bitmap_commit_cb {
+	struct commit **commits;
+	size_t commits_nr, commits_alloc;
+
+	struct write_midx_context *ctx;
+};
+
+static const struct object_id *bitmap_oid_access(size_t index,
+						 const void *_entries)
+{
+	const struct pack_midx_entry *entries = _entries;
+	return &entries[index].oid;
+}
+
+static void bitmap_show_commit(struct commit *commit, void *_data)
+{
+	struct bitmap_commit_cb *data = _data;
+	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
+			  data->ctx->entries_nr,
+			  bitmap_oid_access);
+	if (pos < 0)
+		return;
+
+	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
+	data->commits[data->commits_nr++] = commit;
+}
+
+static int read_refs_snapshot(const char *refs_snapshot,
+			      struct rev_info *revs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+	FILE *f = xfopen(refs_snapshot, "r");
+
+	while (strbuf_getline(&buf, f) != EOF) {
+		struct object *object;
+		int preferred = 0;
+		char *hex = buf.buf;
+		const char *end = NULL;
+
+		if (buf.len && *buf.buf == '+') {
+			preferred = 1;
+			hex = &buf.buf[1];
+		}
+
+		if (parse_oid_hex(hex, &oid, &end) < 0)
+			die(_("could not parse line: %s"), buf.buf);
+		if (*end)
+			die(_("malformed line: %s"), buf.buf);
+
+		object = parse_object_or_die(&oid, NULL);
+		if (preferred)
+			object->flags |= NEEDS_BITMAP;
+
+		add_pending_object(revs, object, "");
+	}
+
+	fclose(f);
+	strbuf_release(&buf);
+	return 0;
+}
+static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
+						    const char *refs_snapshot,
+						    struct write_midx_context *ctx)
+{
+	struct rev_info revs;
+	struct bitmap_commit_cb cb = {0};
+
+	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	cb.ctx = ctx;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	if (refs_snapshot) {
+		read_refs_snapshot(refs_snapshot, &revs);
+	} else {
+		setup_revisions(0, NULL, &revs, NULL);
+		for_each_ref(add_ref_to_pending, &revs);
+	}
+
+	/*
+	 * Skipping promisor objects here is intentional, since it only excludes
+	 * them from the list of reachable commits that we want to select from
+	 * when computing the selection of MIDX'd commits to receive bitmaps.
+	 *
+	 * Reachability bitmaps do require that their objects be closed under
+	 * reachability, but fetching any objects missing from promisors at this
+	 * point is too late. But, if one of those objects can be reached from
+	 * an another object that is included in the bitmap, then we will
+	 * complain later that we don't have reachability closure (and fail
+	 * appropriately).
+	 */
+	fetch_if_missing = 0;
+	revs.exclude_promisor_objects = 1;
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+
+	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
+	if (indexed_commits_nr_p)
+		*indexed_commits_nr_p = cb.commits_nr;
+
+	release_revisions(&revs);
+
+	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	return cb.commits;
+}
+
+static int write_midx_bitmap(const char *midx_name,
+			     const unsigned char *midx_hash,
+			     struct packing_data *pdata,
+			     struct commit **commits,
+			     uint32_t commits_nr,
+			     uint32_t *pack_order,
+			     unsigned flags)
+{
+	int ret, i;
+	uint16_t options = 0;
+	struct pack_idx_entry **index;
+	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
+					hash_to_hex(midx_hash));
+
+	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
+
+	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
+		options |= BITMAP_OPT_HASH_CACHE;
+
+	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
+		options |= BITMAP_OPT_LOOKUP_TABLE;
+
+	/*
+	 * Build the MIDX-order index based on pdata.objects (which is already
+	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
+	 * this order).
+	 */
+	ALLOC_ARRAY(index, pdata->nr_objects);
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[i] = &pdata->objects[i].idx;
+
+	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
+	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+
+	/*
+	 * bitmap_writer_finish expects objects in lex order, but pack_order
+	 * gives us exactly that. use it directly instead of re-sorting the
+	 * array.
+	 *
+	 * This changes the order of objects in 'index' between
+	 * bitmap_writer_build_type_index and bitmap_writer_finish.
+	 *
+	 * The same re-ordering takes place in the single-pack bitmap code via
+	 * write_idx_file(), which is called by finish_tmp_packfile(), which
+	 * happens between bitmap_writer_build_type_index() and
+	 * bitmap_writer_finish().
+	 */
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[pack_order[i]] = &pdata->objects[i].idx;
+
+	bitmap_writer_select_commits(commits, commits_nr, -1);
+	ret = bitmap_writer_build(pdata);
+	if (ret < 0)
+		goto cleanup;
+
+	bitmap_writer_set_checksum(midx_hash);
+	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+
+cleanup:
+	free(index);
+	free(bitmap_name);
+
+	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
+
+	return ret;
+}
+
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+							const char *object_dir)
+{
+	struct multi_pack_index *result = NULL;
+	struct multi_pack_index *cur;
+	char *obj_dir_real = real_pathdup(object_dir, 1);
+	struct strbuf cur_path_real = STRBUF_INIT;
+
+	/* Ensure the given object_dir is local, or a known alternate. */
+	find_odb(r, obj_dir_real);
+
+	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
+		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
+			result = cur;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	free(obj_dir_real);
+	strbuf_release(&cur_path_real);
+	return result;
+}
+
+static int write_midx_internal(const char *object_dir,
 			       struct string_list *packs_to_include,
 			       struct string_list *packs_to_drop,
 			       const char *preferred_pack_name,
 			       const char *refs_snapshot,
-			       unsigned flags);
+			       unsigned flags)
+{
+	struct strbuf midx_name = STRBUF_INIT;
+	unsigned char midx_hash[GIT_MAX_RAWSZ];
+	uint32_t i;
+	struct hashfile *f = NULL;
+	struct lock_file lk;
+	struct write_midx_context ctx = { 0 };
+	int bitmapped_packs_concat_len = 0;
+	int pack_name_concat_len = 0;
+	int dropped_packs = 0;
+	int result = 0;
+	struct chunkfile *cf;
 
-extern struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
-							const char *object_dir);
+	trace2_region_enter("midx", "write_midx_internal", the_repository);
+
+	get_midx_filename(&midx_name, object_dir);
+	if (safe_create_leading_directories(midx_name.buf))
+		die_errno(_("unable to create leading directories of %s"),
+			  midx_name.buf);
+
+	if (!packs_to_include) {
+		/*
+		 * Only reference an existing MIDX when not filtering which
+		 * packs to include, since all packs and objects are copied
+		 * blindly from an existing MIDX if one is present.
+		 */
+		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
+	}
+
+	if (ctx.m && !midx_checksum_valid(ctx.m)) {
+		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
+		ctx.m = NULL;
+	}
+
+	ctx.nr = 0;
+	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
+	ctx.info = NULL;
+	ALLOC_ARRAY(ctx.info, ctx.alloc);
+
+	if (ctx.m) {
+		for (i = 0; i < ctx.m->num_packs; i++) {
+			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
+
+			if (flags & MIDX_WRITE_REV_INDEX) {
+				/*
+				 * If generating a reverse index, need to have
+				 * packed_git's loaded to compare their
+				 * mtimes and object count.
+				 */
+				if (prepare_midx_pack(the_repository, ctx.m, i)) {
+					error(_("could not load pack"));
+					result = 1;
+					goto cleanup;
+				}
+
+				if (open_pack_index(ctx.m->packs[i]))
+					die(_("could not open index for %s"),
+					    ctx.m->packs[i]->pack_name);
+			}
+
+			fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+				       ctx.m->pack_names[i], i);
+		}
+	}
+
+	ctx.pack_paths_checked = 0;
+	if (flags & MIDX_PROGRESS)
+		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
+	else
+		ctx.progress = NULL;
+
+	ctx.to_include = packs_to_include;
+
+	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
+	stop_progress(&ctx.progress);
+
+	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
+	    !(packs_to_include || packs_to_drop)) {
+		struct bitmap_index *bitmap_git;
+		int bitmap_exists;
+		int want_bitmap = flags & MIDX_WRITE_BITMAP;
+
+		bitmap_git = prepare_midx_bitmap_git(ctx.m);
+		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
+		free_bitmap_index(bitmap_git);
+
+		if (bitmap_exists || !want_bitmap) {
+			/*
+			 * The correct MIDX already exists, and so does a
+			 * corresponding bitmap (or one wasn't requested).
+			 */
+			if (!want_bitmap)
+				clear_midx_files_ext(object_dir, ".bitmap",
+						     NULL);
+			goto cleanup;
+		}
+	}
+
+	if (preferred_pack_name) {
+		ctx.preferred_pack_idx = -1;
+
+		for (i = 0; i < ctx.nr; i++) {
+			if (!cmp_idx_or_pack_name(preferred_pack_name,
+						  ctx.info[i].pack_name)) {
+				ctx.preferred_pack_idx = i;
+				break;
+			}
+		}
+
+		if (ctx.preferred_pack_idx == -1)
+			warning(_("unknown preferred pack: '%s'"),
+				preferred_pack_name);
+	} else if (ctx.nr &&
+		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
+		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
+		ctx.preferred_pack_idx = 0;
+
+		if (packs_to_drop && packs_to_drop->nr)
+			BUG("cannot write a MIDX bitmap during expiration");
+
+		/*
+		 * set a preferred pack when writing a bitmap to ensure that
+		 * the pack from which the first object is selected in pseudo
+		 * pack-order has all of its objects selected from that pack
+		 * (and not another pack containing a duplicate)
+		 */
+		for (i = 1; i < ctx.nr; i++) {
+			struct packed_git *p = ctx.info[i].p;
+
+			if (!oldest->num_objects || p->mtime < oldest->mtime) {
+				oldest = p;
+				ctx.preferred_pack_idx = i;
+			}
+		}
+
+		if (!oldest->num_objects) {
+			/*
+			 * If all packs are empty; unset the preferred index.
+			 * This is acceptable since there will be no duplicate
+			 * objects to resolve, so the preferred value doesn't
+			 * matter.
+			 */
+			ctx.preferred_pack_idx = -1;
+		}
+	} else {
+		/*
+		 * otherwise don't mark any pack as preferred to avoid
+		 * interfering with expiration logic below
+		 */
+		ctx.preferred_pack_idx = -1;
+	}
+
+	if (ctx.preferred_pack_idx > -1) {
+		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
+		if (!preferred->num_objects) {
+			error(_("cannot select preferred pack %s with no objects"),
+			      preferred->pack_name);
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
+					 ctx.preferred_pack_idx);
+
+	ctx.large_offsets_needed = 0;
+	for (i = 0; i < ctx.entries_nr; i++) {
+		if (ctx.entries[i].offset > 0x7fffffff)
+			ctx.num_large_offsets++;
+		if (ctx.entries[i].offset > 0xffffffff)
+			ctx.large_offsets_needed = 1;
+	}
+
+	QSORT(ctx.info, ctx.nr, pack_info_compare);
+
+	if (packs_to_drop && packs_to_drop->nr) {
+		int drop_index = 0;
+		int missing_drops = 0;
+
+		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
+			int cmp = strcmp(ctx.info[i].pack_name,
+					 packs_to_drop->items[drop_index].string);
+
+			if (!cmp) {
+				drop_index++;
+				ctx.info[i].expired = 1;
+			} else if (cmp > 0) {
+				error(_("did not see pack-file %s to drop"),
+				      packs_to_drop->items[drop_index].string);
+				drop_index++;
+				missing_drops++;
+				i--;
+			} else {
+				ctx.info[i].expired = 0;
+			}
+		}
+
+		if (missing_drops) {
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * pack_perm stores a permutation between pack-int-ids from the
+	 * previous multi-pack-index to the new one we are writing:
+	 *
+	 * pack_perm[old_id] = new_id
+	 */
+	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired) {
+			dropped_packs++;
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
+		} else {
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
+		}
+	}
+
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired)
+			continue;
+		pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+		bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
+	}
+
+	/* Check that the preferred pack wasn't expired (if given). */
+	if (preferred_pack_name) {
+		struct pack_info *preferred = bsearch(preferred_pack_name,
+						      ctx.info, ctx.nr,
+						      sizeof(*ctx.info),
+						      idx_or_pack_name_cmp);
+		if (preferred) {
+			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
+			if (perm == PACK_EXPIRED)
+				warning(_("preferred pack '%s' is expired"),
+					preferred_pack_name);
+		}
+	}
+
+	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
+		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
+					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
+
+	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
+	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+
+	if (ctx.nr - dropped_packs == 0) {
+		error(_("no pack files to index."));
+		result = 1;
+		goto cleanup;
+	}
+
+	if (!ctx.entries_nr) {
+		if (flags & MIDX_WRITE_BITMAP)
+			warning(_("refusing to write multi-pack .bitmap without any objects"));
+		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
+	}
+
+	cf = init_chunkfile(f);
+
+	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
+		  write_midx_pack_names);
+	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
+		  write_midx_oid_fanout);
+	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
+		  st_mult(ctx.entries_nr, the_hash_algo->rawsz),
+		  write_midx_oid_lookup);
+	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
+		  st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
+		  write_midx_object_offsets);
+
+	if (ctx.large_offsets_needed)
+		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
+			st_mult(ctx.num_large_offsets,
+				MIDX_CHUNK_LARGE_OFFSET_WIDTH),
+			write_midx_large_offsets);
+
+	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
+		ctx.pack_order = midx_pack_order(&ctx);
+		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
+			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
+			  write_midx_revindex);
+		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+			  bitmapped_packs_concat_len,
+			  write_midx_bitmapped_packs);
+	}
+
+	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
+	write_chunkfile(cf, &ctx);
+
+	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
+			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
+	free_chunkfile(cf);
+
+	if (flags & MIDX_WRITE_REV_INDEX &&
+	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
+		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+
+	if (flags & MIDX_WRITE_BITMAP) {
+		struct packing_data pdata;
+		struct commit **commits;
+		uint32_t commits_nr;
+
+		if (!ctx.entries_nr)
+			BUG("cannot write a bitmap without any objects");
+
+		prepare_midx_packing_data(&pdata, &ctx);
+
+		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
+
+		/*
+		 * The previous steps translated the information from
+		 * 'entries' into information suitable for constructing
+		 * bitmaps. We no longer need that array, so clear it to
+		 * reduce memory pressure.
+		 */
+		FREE_AND_NULL(ctx.entries);
+		ctx.entries_nr = 0;
+
+		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
+				      commits, commits_nr, ctx.pack_order,
+				      flags) < 0) {
+			error(_("could not write multi-pack bitmap"));
+			result = 1;
+			clear_packing_data(&pdata);
+			free(commits);
+			goto cleanup;
+		}
+
+		clear_packing_data(&pdata);
+		free(commits);
+	}
+	/*
+	 * NOTE: Do not use ctx.entries beyond this point, since it might
+	 * have been freed in the previous if block.
+	 */
+
+	if (ctx.m)
+		close_object_store(the_repository->objects);
+
+	if (commit_lock_file(&lk) < 0)
+		die_errno(_("could not write multi-pack-index"));
+
+	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
+	clear_midx_files_ext(object_dir, ".rev", midx_hash);
+
+cleanup:
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].p) {
+			close_pack(ctx.info[i].p);
+			free(ctx.info[i].p);
+		}
+		free(ctx.info[i].pack_name);
+	}
+
+	free(ctx.info);
+	free(ctx.entries);
+	free(ctx.pack_perm);
+	free(ctx.pack_order);
+	strbuf_release(&midx_name);
+
+	trace2_region_leave("midx", "write_midx_internal", the_repository);
+
+	return result;
+}
 
 int write_midx_file(const char *object_dir,
 		    const char *preferred_pack_name,
diff --git a/midx.c b/midx.c
index 39b5c86736..ae3b49166c 100644
--- a/midx.c
+++ b/midx.c
@@ -1,62 +1,22 @@
 #include "git-compat-util.h"
-#include "abspath.h"
 #include "config.h"
-#include "csum-file.h"
 #include "dir.h"
-#include "gettext.h"
 #include "hex.h"
-#include "lockfile.h"
 #include "packfile.h"
 #include "object-file.h"
-#include "object-store-ll.h"
 #include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
 #include "trace2.h"
-#include "run-command.h"
-#include "repository.h"
 #include "chunk-format.h"
-#include "pack.h"
 #include "pack-bitmap.h"
-#include "refs.h"
-#include "revision.h"
-#include "list-objects.h"
 #include "pack-revindex.h"
 
-struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
-							const char *object_dir);
-
-int write_midx_internal(const char *object_dir,
-			       struct string_list *packs_to_include,
-			       struct string_list *packs_to_drop,
-			       const char *preferred_pack_name,
-			       const char *refs_snapshot,
-			       unsigned flags);
-
-#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
-#define MIDX_VERSION 1
-#define MIDX_BYTE_FILE_VERSION 4
-#define MIDX_BYTE_HASH_VERSION 5
-#define MIDX_BYTE_NUM_CHUNKS 6
-#define MIDX_BYTE_NUM_PACKS 8
-#define MIDX_HEADER_SIZE 12
-#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
-
-#define MIDX_CHUNK_ALIGNMENT 4
-#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
-#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
-#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
-#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
-#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
-#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
-#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
-#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
-#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
-#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
-#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
-#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
-
-#define PACK_EXPIRED UINT_MAX
+int midx_checksum_valid(struct multi_pack_index *m);
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash);
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name);
 
 const unsigned char *get_midx_checksum(struct multi_pack_index *m)
 {
@@ -125,6 +85,8 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
 	return 0;
 }
 
+#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
+
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local)
 {
 	struct multi_pack_index *m = NULL;
@@ -304,6 +266,8 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t
 	return 0;
 }
 
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
+
 int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
 		       struct bitmapped_pack *bp, uint32_t pack_int_id)
 {
@@ -410,8 +374,8 @@ int fill_midx_entry(struct repository *r,
 }
 
 /* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */
-static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
-				const char *idx_name)
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name)
 {
 	/* Skip past any initial matching prefix. */
 	while (*idx_name && *idx_name == *idx_or_pack_name) {
@@ -518,1243 +482,11 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
 	return 0;
 }
 
-static size_t write_midx_header(struct hashfile *f,
-				unsigned char num_chunks,
-				uint32_t num_packs)
-{
-	hashwrite_be32(f, MIDX_SIGNATURE);
-	hashwrite_u8(f, MIDX_VERSION);
-	hashwrite_u8(f, oid_version(the_hash_algo));
-	hashwrite_u8(f, num_chunks);
-	hashwrite_u8(f, 0); /* unused */
-	hashwrite_be32(f, num_packs);
-
-	return MIDX_HEADER_SIZE;
-}
-
-#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
-
-struct pack_info {
-	uint32_t orig_pack_int_id;
-	char *pack_name;
-	struct packed_git *p;
-
-	uint32_t bitmap_pos;
-	uint32_t bitmap_nr;
-
-	unsigned expired : 1;
-};
-
-static void fill_pack_info(struct pack_info *info,
-			   struct packed_git *p, const char *pack_name,
-			   uint32_t orig_pack_int_id)
-{
-	memset(info, 0, sizeof(struct pack_info));
-
-	info->orig_pack_int_id = orig_pack_int_id;
-	info->pack_name = xstrdup(pack_name);
-	info->p = p;
-	info->bitmap_pos = BITMAP_POS_UNKNOWN;
-}
-
-static int pack_info_compare(const void *_a, const void *_b)
-{
-	struct pack_info *a = (struct pack_info *)_a;
-	struct pack_info *b = (struct pack_info *)_b;
-	return strcmp(a->pack_name, b->pack_name);
-}
-
-static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
-{
-	const char *pack_name = _va;
-	const struct pack_info *compar = _vb;
-
-	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
-}
-
-struct write_midx_context {
-	struct pack_info *info;
-	size_t nr;
-	size_t alloc;
-	struct multi_pack_index *m;
-	struct progress *progress;
-	unsigned pack_paths_checked;
-
-	struct pack_midx_entry *entries;
-	size_t entries_nr;
-
-	uint32_t *pack_perm;
-	uint32_t *pack_order;
-	unsigned large_offsets_needed:1;
-	uint32_t num_large_offsets;
-
-	int preferred_pack_idx;
-
-	struct string_list *to_include;
-};
-
-static void add_pack_to_midx(const char *full_path, size_t full_path_len,
-			     const char *file_name, void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct packed_git *p;
-
-	if (ends_with(file_name, ".idx")) {
-		display_progress(ctx->progress, ++ctx->pack_paths_checked);
-		/*
-		 * Note that at most one of ctx->m and ctx->to_include are set,
-		 * so we are testing midx_contains_pack() and
-		 * string_list_has_string() independently (guarded by the
-		 * appropriate NULL checks).
-		 *
-		 * We could support passing to_include while reusing an existing
-		 * MIDX, but don't currently since the reuse process drags
-		 * forward all packs from an existing MIDX (without checking
-		 * whether or not they appear in the to_include list).
-		 *
-		 * If we added support for that, these next two conditional
-		 * should be performed independently (likely checking
-		 * to_include before the existing MIDX).
-		 */
-		if (ctx->m && midx_contains_pack(ctx->m, file_name))
-			return;
-		else if (ctx->to_include &&
-			 !string_list_has_string(ctx->to_include, file_name))
-			return;
-
-		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
-		p = add_packed_git(full_path, full_path_len, 0);
-		if (!p) {
-			warning(_("failed to add packfile '%s'"),
-				full_path);
-			return;
-		}
-
-		if (open_pack_index(p)) {
-			warning(_("failed to open pack-index '%s'"),
-				full_path);
-			close_pack(p);
-			free(p);
-			return;
-		}
-
-		fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
-		ctx->nr++;
-	}
-}
-
-struct pack_midx_entry {
-	struct object_id oid;
-	uint32_t pack_int_id;
-	time_t pack_mtime;
-	uint64_t offset;
-	unsigned preferred : 1;
-};
-
-static int midx_oid_compare(const void *_a, const void *_b)
-{
-	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
-	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
-	int cmp = oidcmp(&a->oid, &b->oid);
-
-	if (cmp)
-		return cmp;
-
-	/* Sort objects in a preferred pack first when multiple copies exist. */
-	if (a->preferred > b->preferred)
-		return -1;
-	if (a->preferred < b->preferred)
-		return 1;
-
-	if (a->pack_mtime > b->pack_mtime)
-		return -1;
-	else if (a->pack_mtime < b->pack_mtime)
-		return 1;
-
-	return a->pack_int_id - b->pack_int_id;
-}
-
-static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
-				      struct pack_midx_entry *e,
-				      uint32_t pos)
-{
-	if (pos >= m->num_objects)
-		return 1;
-
-	nth_midxed_object_oid(&e->oid, m, pos);
-	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
-	e->offset = nth_midxed_offset(m, pos);
-
-	/* consider objects in midx to be from "old" packs */
-	e->pack_mtime = 0;
-	return 0;
-}
-
-static void fill_pack_entry(uint32_t pack_int_id,
-			    struct packed_git *p,
-			    uint32_t cur_object,
-			    struct pack_midx_entry *entry,
-			    int preferred)
-{
-	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
-		die(_("failed to locate object %d in packfile"), cur_object);
-
-	entry->pack_int_id = pack_int_id;
-	entry->pack_mtime = p->mtime;
-
-	entry->offset = nth_packed_object_offset(p, cur_object);
-	entry->preferred = !!preferred;
-}
-
-struct midx_fanout {
-	struct pack_midx_entry *entries;
-	size_t nr, alloc;
-};
-
-static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
-{
-	if (nr < fanout->nr)
-		BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
-		    (uintmax_t)nr, (uintmax_t)fanout->nr);
-	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
-}
-
-static void midx_fanout_sort(struct midx_fanout *fanout)
-{
-	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
-}
-
-static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
-					struct multi_pack_index *m,
-					uint32_t cur_fanout,
-					int preferred_pack)
-{
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
-	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		if ((preferred_pack > -1) &&
-		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
-			/*
-			 * Objects from preferred packs are added
-			 * separately.
-			 */
-			continue;
-		}
-
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		nth_midxed_pack_midx_entry(m,
-					   &fanout->entries[fanout->nr],
-					   cur_object);
-		fanout->entries[fanout->nr].preferred = 0;
-		fanout->nr++;
-	}
-}
-
-static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
-					struct pack_info *info,
-					uint32_t cur_pack,
-					int preferred,
-					uint32_t cur_fanout)
-{
-	struct packed_git *pack = info[cur_pack].p;
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = get_pack_fanout(pack, cur_fanout - 1);
-	end = get_pack_fanout(pack, cur_fanout);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		fill_pack_entry(cur_pack,
-				info[cur_pack].p,
-				cur_object,
-				&fanout->entries[fanout->nr],
-				preferred);
-		fanout->nr++;
-	}
-}
-
-/*
- * It is possible to artificially get into a state where there are many
- * duplicate copies of objects. That can create high memory pressure if
- * we are to create a list of all objects before de-duplication. To reduce
- * this memory pressure without a significant performance drop, automatically
- * group objects by the first byte of their object id. Use the IDX fanout
- * tables to group the data, copy to a local array, then sort.
- *
- * Copy only the de-duplicated entries (selected by most-recent modified time
- * of a packfile containing the object).
- */
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
-						  struct pack_info *info,
-						  uint32_t nr_packs,
-						  size_t *nr_objects,
-						  int preferred_pack)
-{
-	uint32_t cur_fanout, cur_pack, cur_object;
-	size_t alloc_objects, total_objects = 0;
-	struct midx_fanout fanout = { 0 };
-	struct pack_midx_entry *deduplicated_entries = NULL;
-	uint32_t start_pack = m ? m->num_packs : 0;
-
-	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
-		total_objects = st_add(total_objects,
-				       info[cur_pack].p->num_objects);
-
-	/*
-	 * As we de-duplicate by fanout value, we expect the fanout
-	 * slices to be evenly distributed, with some noise. Hence,
-	 * allocate slightly more than one 256th.
-	 */
-	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
-
-	ALLOC_ARRAY(fanout.entries, fanout.alloc);
-	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
-	*nr_objects = 0;
-
-	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
-		fanout.nr = 0;
-
-		if (m)
-			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
-						    preferred_pack);
-
-		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
-			int preferred = cur_pack == preferred_pack;
-			midx_fanout_add_pack_fanout(&fanout,
-						    info, cur_pack,
-						    preferred, cur_fanout);
-		}
-
-		if (-1 < preferred_pack && preferred_pack < start_pack)
-			midx_fanout_add_pack_fanout(&fanout, info,
-						    preferred_pack, 1,
-						    cur_fanout);
-
-		midx_fanout_sort(&fanout);
-
-		/*
-		 * The batch is now sorted by OID and then mtime (descending).
-		 * Take only the first duplicate.
-		 */
-		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
-			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
-						&fanout.entries[cur_object].oid))
-				continue;
-
-			ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
-				   alloc_objects);
-			memcpy(&deduplicated_entries[*nr_objects],
-			       &fanout.entries[cur_object],
-			       sizeof(struct pack_midx_entry));
-			(*nr_objects)++;
-		}
-	}
-
-	free(fanout.entries);
-	return deduplicated_entries;
-}
-
-static int write_midx_pack_names(struct hashfile *f, void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
-	size_t written = 0;
-
-	for (i = 0; i < ctx->nr; i++) {
-		size_t writelen;
-
-		if (ctx->info[i].expired)
-			continue;
-
-		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
-			BUG("incorrect pack-file order: %s before %s",
-			    ctx->info[i - 1].pack_name,
-			    ctx->info[i].pack_name);
-
-		writelen = strlen(ctx->info[i].pack_name) + 1;
-		hashwrite(f, ctx->info[i].pack_name, writelen);
-		written += writelen;
-	}
-
-	/* add padding to be aligned */
-	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
-	if (i < MIDX_CHUNK_ALIGNMENT) {
-		memset(padding, 0, sizeof(padding));
-		hashwrite(f, padding, i);
-	}
-
-	return 0;
-}
-
-static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
-{
-	struct write_midx_context *ctx = data;
-	size_t i;
-
-	for (i = 0; i < ctx->nr; i++) {
-		struct pack_info *pack = &ctx->info[i];
-		if (pack->expired)
-			continue;
-
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
-			BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
-			    pack->pack_name, pack->bitmap_nr);
-
-		hashwrite_be32(f, pack->bitmap_pos);
-		hashwrite_be32(f, pack->bitmap_nr);
-	}
-	return 0;
-}
-
-static int write_midx_oid_fanout(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
-	uint32_t count = 0;
-	uint32_t i;
-
-	/*
-	* Write the first-level table (the list is sorted,
-	* but we use a 256-entry lookup to be able to avoid
-	* having to do eight extra binary search iterations).
-	*/
-	for (i = 0; i < 256; i++) {
-		struct pack_midx_entry *next = list;
-
-		while (next < last && next->oid.hash[0] == i) {
-			count++;
-			next++;
-		}
-
-		hashwrite_be32(f, count);
-		list = next;
-	}
-
-	return 0;
-}
-
-static int write_midx_oid_lookup(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	unsigned char hash_len = the_hash_algo->rawsz;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (i < ctx->entries_nr - 1) {
-			struct pack_midx_entry *next = list;
-			if (oidcmp(&obj->oid, &next->oid) >= 0)
-				BUG("OIDs not in order: %s >= %s",
-				    oid_to_hex(&obj->oid),
-				    oid_to_hex(&next->oid));
-		}
-
-		hashwrite(f, obj->oid.hash, (int)hash_len);
-	}
-
-	return 0;
-}
-
-static int write_midx_object_offsets(struct hashfile *f,
-				     void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i, nr_large_offset = 0;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
-			BUG("object %s is in an expired pack with int-id %d",
-			    oid_to_hex(&obj->oid),
-			    obj->pack_int_id);
-
-		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
-
-		if (ctx->large_offsets_needed && obj->offset >> 31)
-			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
-		else if (!ctx->large_offsets_needed && obj->offset >> 32)
-			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
-			    oid_to_hex(&obj->oid),
-			    obj->offset);
-		else
-			hashwrite_be32(f, (uint32_t)obj->offset);
-	}
-
-	return 0;
-}
-
-static int write_midx_large_offsets(struct hashfile *f,
-				    void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
-	uint32_t nr_large_offset = ctx->num_large_offsets;
-
-	while (nr_large_offset) {
-		struct pack_midx_entry *obj;
-		uint64_t offset;
-
-		if (list >= end)
-			BUG("too many large-offset objects");
-
-		obj = list++;
-		offset = obj->offset;
-
-		if (!(offset >> 31))
-			continue;
-
-		hashwrite_be64(f, offset);
-
-		nr_large_offset--;
-	}
-
-	return 0;
-}
-
-static int write_midx_revindex(struct hashfile *f,
-			       void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++)
-		hashwrite_be32(f, ctx->pack_order[i]);
-
-	return 0;
-}
-
-struct midx_pack_order_data {
-	uint32_t nr;
-	uint32_t pack;
-	off_t offset;
-};
-
-static int midx_pack_order_cmp(const void *va, const void *vb)
-{
-	const struct midx_pack_order_data *a = va, *b = vb;
-	if (a->pack < b->pack)
-		return -1;
-	else if (a->pack > b->pack)
-		return 1;
-	else if (a->offset < b->offset)
-		return -1;
-	else if (a->offset > b->offset)
-		return 1;
-	else
-		return 0;
-}
-
-static uint32_t *midx_pack_order(struct write_midx_context *ctx)
-{
-	struct midx_pack_order_data *data;
-	uint32_t *pack_order;
-	uint32_t i;
-
-	trace2_region_enter("midx", "midx_pack_order", the_repository);
-
-	ALLOC_ARRAY(data, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *e = &ctx->entries[i];
-		data[i].nr = i;
-		data[i].pack = ctx->pack_perm[e->pack_int_id];
-		if (!e->preferred)
-			data[i].pack |= (1U << 31);
-		data[i].offset = e->offset;
-	}
-
-	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
-
-	ALLOC_ARRAY(pack_order, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *e = &ctx->entries[data[i].nr];
-		struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
-			pack->bitmap_pos = i;
-		pack->bitmap_nr++;
-		pack_order[i] = data[i].nr;
-	}
-	for (i = 0; i < ctx->nr; i++) {
-		struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
-		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
-			pack->bitmap_pos = 0;
-	}
-	free(data);
-
-	trace2_region_leave("midx", "midx_pack_order", the_repository);
-
-	return pack_order;
-}
-
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
-				     struct write_midx_context *ctx)
-{
-	struct strbuf buf = STRBUF_INIT;
-	const char *tmp_file;
-
-	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
-
-	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
-
-	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
-					midx_hash, WRITE_REV);
-
-	if (finalize_object_file(tmp_file, buf.buf))
-		die(_("cannot store reverse index file"));
-
-	strbuf_release(&buf);
-
-	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
-}
-
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash);
-
-static int midx_checksum_valid(struct multi_pack_index *m)
+int midx_checksum_valid(struct multi_pack_index *m)
 {
 	return hashfile_checksum_valid(m->data, m->data_len);
 }
 
-static void prepare_midx_packing_data(struct packing_data *pdata,
-				      struct write_midx_context *ctx)
-{
-	uint32_t i;
-
-	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
-
-	memset(pdata, 0, sizeof(struct packing_data));
-	prepare_packing_data(the_repository, pdata);
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
-		struct object_entry *to = packlist_alloc(pdata, &from->oid);
-
-		oe_set_in_pack(pdata, to,
-			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
-	}
-
-	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
-}
-
-static int add_ref_to_pending(const char *refname,
-			      const struct object_id *oid,
-			      int flag, void *cb_data)
-{
-	struct rev_info *revs = (struct rev_info*)cb_data;
-	struct object_id peeled;
-	struct object *object;
-
-	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
-		warning("symbolic ref is dangling: %s", refname);
-		return 0;
-	}
-
-	if (!peel_iterated_oid(oid, &peeled))
-		oid = &peeled;
-
-	object = parse_object_or_die(oid, refname);
-	if (object->type != OBJ_COMMIT)
-		return 0;
-
-	add_pending_object(revs, object, "");
-	if (bitmap_is_preferred_refname(revs->repo, refname))
-		object->flags |= NEEDS_BITMAP;
-	return 0;
-}
-
-struct bitmap_commit_cb {
-	struct commit **commits;
-	size_t commits_nr, commits_alloc;
-
-	struct write_midx_context *ctx;
-};
-
-static const struct object_id *bitmap_oid_access(size_t index,
-						 const void *_entries)
-{
-	const struct pack_midx_entry *entries = _entries;
-	return &entries[index].oid;
-}
-
-static void bitmap_show_commit(struct commit *commit, void *_data)
-{
-	struct bitmap_commit_cb *data = _data;
-	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
-			  data->ctx->entries_nr,
-			  bitmap_oid_access);
-	if (pos < 0)
-		return;
-
-	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
-	data->commits[data->commits_nr++] = commit;
-}
-
-static int read_refs_snapshot(const char *refs_snapshot,
-			      struct rev_info *revs)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct object_id oid;
-	FILE *f = xfopen(refs_snapshot, "r");
-
-	while (strbuf_getline(&buf, f) != EOF) {
-		struct object *object;
-		int preferred = 0;
-		char *hex = buf.buf;
-		const char *end = NULL;
-
-		if (buf.len && *buf.buf == '+') {
-			preferred = 1;
-			hex = &buf.buf[1];
-		}
-
-		if (parse_oid_hex(hex, &oid, &end) < 0)
-			die(_("could not parse line: %s"), buf.buf);
-		if (*end)
-			die(_("malformed line: %s"), buf.buf);
-
-		object = parse_object_or_die(&oid, NULL);
-		if (preferred)
-			object->flags |= NEEDS_BITMAP;
-
-		add_pending_object(revs, object, "");
-	}
-
-	fclose(f);
-	strbuf_release(&buf);
-	return 0;
-}
-
-static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
-						    const char *refs_snapshot,
-						    struct write_midx_context *ctx)
-{
-	struct rev_info revs;
-	struct bitmap_commit_cb cb = {0};
-
-	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	cb.ctx = ctx;
-
-	repo_init_revisions(the_repository, &revs, NULL);
-	if (refs_snapshot) {
-		read_refs_snapshot(refs_snapshot, &revs);
-	} else {
-		setup_revisions(0, NULL, &revs, NULL);
-		for_each_ref(add_ref_to_pending, &revs);
-	}
-
-	/*
-	 * Skipping promisor objects here is intentional, since it only excludes
-	 * them from the list of reachable commits that we want to select from
-	 * when computing the selection of MIDX'd commits to receive bitmaps.
-	 *
-	 * Reachability bitmaps do require that their objects be closed under
-	 * reachability, but fetching any objects missing from promisors at this
-	 * point is too late. But, if one of those objects can be reached from
-	 * an another object that is included in the bitmap, then we will
-	 * complain later that we don't have reachability closure (and fail
-	 * appropriately).
-	 */
-	fetch_if_missing = 0;
-	revs.exclude_promisor_objects = 1;
-
-	if (prepare_revision_walk(&revs))
-		die(_("revision walk setup failed"));
-
-	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
-	if (indexed_commits_nr_p)
-		*indexed_commits_nr_p = cb.commits_nr;
-
-	release_revisions(&revs);
-
-	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	return cb.commits;
-}
-
-static int write_midx_bitmap(const char *midx_name,
-			     const unsigned char *midx_hash,
-			     struct packing_data *pdata,
-			     struct commit **commits,
-			     uint32_t commits_nr,
-			     uint32_t *pack_order,
-			     unsigned flags)
-{
-	int ret, i;
-	uint16_t options = 0;
-	struct pack_idx_entry **index;
-	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
-					hash_to_hex(midx_hash));
-
-	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
-
-	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
-		options |= BITMAP_OPT_HASH_CACHE;
-
-	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
-		options |= BITMAP_OPT_LOOKUP_TABLE;
-
-	/*
-	 * Build the MIDX-order index based on pdata.objects (which is already
-	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
-	 * this order).
-	 */
-	ALLOC_ARRAY(index, pdata->nr_objects);
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[i] = &pdata->objects[i].idx;
-
-	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
-	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
-
-	/*
-	 * bitmap_writer_finish expects objects in lex order, but pack_order
-	 * gives us exactly that. use it directly instead of re-sorting the
-	 * array.
-	 *
-	 * This changes the order of objects in 'index' between
-	 * bitmap_writer_build_type_index and bitmap_writer_finish.
-	 *
-	 * The same re-ordering takes place in the single-pack bitmap code via
-	 * write_idx_file(), which is called by finish_tmp_packfile(), which
-	 * happens between bitmap_writer_build_type_index() and
-	 * bitmap_writer_finish().
-	 */
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[pack_order[i]] = &pdata->objects[i].idx;
-
-	bitmap_writer_select_commits(commits, commits_nr, -1);
-	ret = bitmap_writer_build(pdata);
-	if (ret < 0)
-		goto cleanup;
-
-	bitmap_writer_set_checksum(midx_hash);
-	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
-
-cleanup:
-	free(index);
-	free(bitmap_name);
-
-	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
-
-	return ret;
-}
-
-struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
-							const char *object_dir)
-{
-	struct multi_pack_index *result = NULL;
-	struct multi_pack_index *cur;
-	char *obj_dir_real = real_pathdup(object_dir, 1);
-	struct strbuf cur_path_real = STRBUF_INIT;
-
-	/* Ensure the given object_dir is local, or a known alternate. */
-	find_odb(r, obj_dir_real);
-
-	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
-		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
-		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
-			result = cur;
-			goto cleanup;
-		}
-	}
-
-cleanup:
-	free(obj_dir_real);
-	strbuf_release(&cur_path_real);
-	return result;
-}
-
-int write_midx_internal(const char *object_dir,
-			struct string_list *packs_to_include,
-			struct string_list *packs_to_drop,
-			const char *preferred_pack_name,
-			const char *refs_snapshot,
-			unsigned flags)
-{
-	struct strbuf midx_name = STRBUF_INIT;
-	unsigned char midx_hash[GIT_MAX_RAWSZ];
-	uint32_t i;
-	struct hashfile *f = NULL;
-	struct lock_file lk;
-	struct write_midx_context ctx = { 0 };
-	int bitmapped_packs_concat_len = 0;
-	int pack_name_concat_len = 0;
-	int dropped_packs = 0;
-	int result = 0;
-	struct chunkfile *cf;
-
-	trace2_region_enter("midx", "write_midx_internal", the_repository);
-
-	get_midx_filename(&midx_name, object_dir);
-	if (safe_create_leading_directories(midx_name.buf))
-		die_errno(_("unable to create leading directories of %s"),
-			  midx_name.buf);
-
-	if (!packs_to_include) {
-		/*
-		 * Only reference an existing MIDX when not filtering which
-		 * packs to include, since all packs and objects are copied
-		 * blindly from an existing MIDX if one is present.
-		 */
-		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
-	}
-
-	if (ctx.m && !midx_checksum_valid(ctx.m)) {
-		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
-		ctx.m = NULL;
-	}
-
-	ctx.nr = 0;
-	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
-	ctx.info = NULL;
-	ALLOC_ARRAY(ctx.info, ctx.alloc);
-
-	if (ctx.m) {
-		for (i = 0; i < ctx.m->num_packs; i++) {
-			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
-			if (flags & MIDX_WRITE_REV_INDEX) {
-				/*
-				 * If generating a reverse index, need to have
-				 * packed_git's loaded to compare their
-				 * mtimes and object count.
-				 */
-				if (prepare_midx_pack(the_repository, ctx.m, i)) {
-					error(_("could not load pack"));
-					result = 1;
-					goto cleanup;
-				}
-
-				if (open_pack_index(ctx.m->packs[i]))
-					die(_("could not open index for %s"),
-					    ctx.m->packs[i]->pack_name);
-			}
-
-			fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
-				       ctx.m->pack_names[i], i);
-		}
-	}
-
-	ctx.pack_paths_checked = 0;
-	if (flags & MIDX_PROGRESS)
-		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
-	else
-		ctx.progress = NULL;
-
-	ctx.to_include = packs_to_include;
-
-	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
-	stop_progress(&ctx.progress);
-
-	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
-	    !(packs_to_include || packs_to_drop)) {
-		struct bitmap_index *bitmap_git;
-		int bitmap_exists;
-		int want_bitmap = flags & MIDX_WRITE_BITMAP;
-
-		bitmap_git = prepare_midx_bitmap_git(ctx.m);
-		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
-		free_bitmap_index(bitmap_git);
-
-		if (bitmap_exists || !want_bitmap) {
-			/*
-			 * The correct MIDX already exists, and so does a
-			 * corresponding bitmap (or one wasn't requested).
-			 */
-			if (!want_bitmap)
-				clear_midx_files_ext(object_dir, ".bitmap",
-						     NULL);
-			goto cleanup;
-		}
-	}
-
-	if (preferred_pack_name) {
-		ctx.preferred_pack_idx = -1;
-
-		for (i = 0; i < ctx.nr; i++) {
-			if (!cmp_idx_or_pack_name(preferred_pack_name,
-						  ctx.info[i].pack_name)) {
-				ctx.preferred_pack_idx = i;
-				break;
-			}
-		}
-
-		if (ctx.preferred_pack_idx == -1)
-			warning(_("unknown preferred pack: '%s'"),
-				preferred_pack_name);
-	} else if (ctx.nr &&
-		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
-		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
-		ctx.preferred_pack_idx = 0;
-
-		if (packs_to_drop && packs_to_drop->nr)
-			BUG("cannot write a MIDX bitmap during expiration");
-
-		/*
-		 * set a preferred pack when writing a bitmap to ensure that
-		 * the pack from which the first object is selected in pseudo
-		 * pack-order has all of its objects selected from that pack
-		 * (and not another pack containing a duplicate)
-		 */
-		for (i = 1; i < ctx.nr; i++) {
-			struct packed_git *p = ctx.info[i].p;
-
-			if (!oldest->num_objects || p->mtime < oldest->mtime) {
-				oldest = p;
-				ctx.preferred_pack_idx = i;
-			}
-		}
-
-		if (!oldest->num_objects) {
-			/*
-			 * If all packs are empty; unset the preferred index.
-			 * This is acceptable since there will be no duplicate
-			 * objects to resolve, so the preferred value doesn't
-			 * matter.
-			 */
-			ctx.preferred_pack_idx = -1;
-		}
-	} else {
-		/*
-		 * otherwise don't mark any pack as preferred to avoid
-		 * interfering with expiration logic below
-		 */
-		ctx.preferred_pack_idx = -1;
-	}
-
-	if (ctx.preferred_pack_idx > -1) {
-		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
-		if (!preferred->num_objects) {
-			error(_("cannot select preferred pack %s with no objects"),
-			      preferred->pack_name);
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
-					 ctx.preferred_pack_idx);
-
-	ctx.large_offsets_needed = 0;
-	for (i = 0; i < ctx.entries_nr; i++) {
-		if (ctx.entries[i].offset > 0x7fffffff)
-			ctx.num_large_offsets++;
-		if (ctx.entries[i].offset > 0xffffffff)
-			ctx.large_offsets_needed = 1;
-	}
-
-	QSORT(ctx.info, ctx.nr, pack_info_compare);
-
-	if (packs_to_drop && packs_to_drop->nr) {
-		int drop_index = 0;
-		int missing_drops = 0;
-
-		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
-			int cmp = strcmp(ctx.info[i].pack_name,
-					 packs_to_drop->items[drop_index].string);
-
-			if (!cmp) {
-				drop_index++;
-				ctx.info[i].expired = 1;
-			} else if (cmp > 0) {
-				error(_("did not see pack-file %s to drop"),
-				      packs_to_drop->items[drop_index].string);
-				drop_index++;
-				missing_drops++;
-				i--;
-			} else {
-				ctx.info[i].expired = 0;
-			}
-		}
-
-		if (missing_drops) {
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	/*
-	 * pack_perm stores a permutation between pack-int-ids from the
-	 * previous multi-pack-index to the new one we are writing:
-	 *
-	 * pack_perm[old_id] = new_id
-	 */
-	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].expired) {
-			dropped_packs++;
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
-		} else {
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
-		}
-	}
-
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].expired)
-			continue;
-		pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
-		bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
-	}
-
-	/* Check that the preferred pack wasn't expired (if given). */
-	if (preferred_pack_name) {
-		struct pack_info *preferred = bsearch(preferred_pack_name,
-						      ctx.info, ctx.nr,
-						      sizeof(*ctx.info),
-						      idx_or_pack_name_cmp);
-		if (preferred) {
-			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
-			if (perm == PACK_EXPIRED)
-				warning(_("preferred pack '%s' is expired"),
-					preferred_pack_name);
-		}
-	}
-
-	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
-		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
-					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
-
-	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
-	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
-
-	if (ctx.nr - dropped_packs == 0) {
-		error(_("no pack files to index."));
-		result = 1;
-		goto cleanup;
-	}
-
-	if (!ctx.entries_nr) {
-		if (flags & MIDX_WRITE_BITMAP)
-			warning(_("refusing to write multi-pack .bitmap without any objects"));
-		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
-	}
-
-	cf = init_chunkfile(f);
-
-	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
-		  write_midx_pack_names);
-	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
-		  write_midx_oid_fanout);
-	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
-		  st_mult(ctx.entries_nr, the_hash_algo->rawsz),
-		  write_midx_oid_lookup);
-	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
-		  st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
-		  write_midx_object_offsets);
-
-	if (ctx.large_offsets_needed)
-		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
-			st_mult(ctx.num_large_offsets,
-				MIDX_CHUNK_LARGE_OFFSET_WIDTH),
-			write_midx_large_offsets);
-
-	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
-		ctx.pack_order = midx_pack_order(&ctx);
-		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
-			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
-			  write_midx_revindex);
-		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
-			  bitmapped_packs_concat_len,
-			  write_midx_bitmapped_packs);
-	}
-
-	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
-	write_chunkfile(cf, &ctx);
-
-	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
-			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
-	free_chunkfile(cf);
-
-	if (flags & MIDX_WRITE_REV_INDEX &&
-	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
-		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
-
-	if (flags & MIDX_WRITE_BITMAP) {
-		struct packing_data pdata;
-		struct commit **commits;
-		uint32_t commits_nr;
-
-		if (!ctx.entries_nr)
-			BUG("cannot write a bitmap without any objects");
-
-		prepare_midx_packing_data(&pdata, &ctx);
-
-		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
-
-		/*
-		 * The previous steps translated the information from
-		 * 'entries' into information suitable for constructing
-		 * bitmaps. We no longer need that array, so clear it to
-		 * reduce memory pressure.
-		 */
-		FREE_AND_NULL(ctx.entries);
-		ctx.entries_nr = 0;
-
-		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
-				      commits, commits_nr, ctx.pack_order,
-				      flags) < 0) {
-			error(_("could not write multi-pack bitmap"));
-			result = 1;
-			clear_packing_data(&pdata);
-			free(commits);
-			goto cleanup;
-		}
-
-		clear_packing_data(&pdata);
-		free(commits);
-	}
-	/*
-	 * NOTE: Do not use ctx.entries beyond this point, since it might
-	 * have been freed in the previous if block.
-	 */
-
-	if (ctx.m)
-		close_object_store(the_repository->objects);
-
-	if (commit_lock_file(&lk) < 0)
-		die_errno(_("could not write multi-pack-index"));
-
-	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
-	clear_midx_files_ext(object_dir, ".rev", midx_hash);
-
-cleanup:
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].p) {
-			close_pack(ctx.info[i].p);
-			free(ctx.info[i].p);
-		}
-		free(ctx.info[i].pack_name);
-	}
-
-	free(ctx.info);
-	free(ctx.entries);
-	free(ctx.pack_perm);
-	free(ctx.pack_order);
-	strbuf_release(&midx_name);
-
-	trace2_region_leave("midx", "write_midx_internal", the_repository);
-
-	return result;
-}
-
 struct clear_midx_data {
 	char *keep;
 	const char *ext;
@@ -1775,8 +507,8 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS
 		die_errno(_("failed to remove %s"), full_path);
 }
 
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash)
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash)
 {
 	struct clear_midx_data data;
 	memset(&data, 0, sizeof(struct clear_midx_data));
diff --git a/midx.h b/midx.h
index b374a7afaf..dc477dff44 100644
--- a/midx.h
+++ b/midx.h
@@ -8,6 +8,25 @@ struct pack_entry;
 struct repository;
 struct bitmapped_pack;
 
+#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
+#define MIDX_VERSION 1
+#define MIDX_BYTE_FILE_VERSION 4
+#define MIDX_BYTE_HASH_VERSION 5
+#define MIDX_BYTE_NUM_CHUNKS 6
+#define MIDX_BYTE_NUM_PACKS 8
+#define MIDX_HEADER_SIZE 12
+
+#define MIDX_CHUNK_ALIGNMENT 4
+#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
+#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
+#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
+#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
+#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
+#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
+#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
+#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
+
 #define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
 #define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
 	"GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP"
-- 
2.44.0.290.g736be63234b



^ permalink raw reply related	[relevance 1%]

* Re: [PATCH v3 0/2] Allow disabling advice shown after merge conflicts
  @ 2024-03-25 10:48  0%   ` Phillip Wood
  0 siblings, 0 replies; 200+ results
From: Phillip Wood @ 2024-03-25 10:48 UTC (permalink / raw)
  To: Philippe Blain via GitGitGadget, git
  Cc: Elijah Newren, Phillip Wood, Johannes Schindelin, ZheNing Hu,
	Kristoffer Haugsbakk, Rubén Justo, Philippe Blain

Hi Philippe

On 16/03/2024 21:16, Philippe Blain via GitGitGadget wrote:
> Changes since v2:
> 
>   * expanded the commit messages to explain why the tests for 'git rebase' do
>     not need to be adjusted
>   * adjusted the wording of the new 'advice.mergeConflict' in the doc, as
>     suggested by Kristoffer for uniformity with his series which is already
>     merged to 'master' (b09a8839a4 (Merge branch
>     'kh/branch-ref-syntax-advice', 2024-03-15)).
>   * checked all new output manually and consequently adjusted the code in 1/2
>     to avoid a lonely 'hint: ' line.
>   * adjusted the addition in advice.h in 1/2 to put the new enum
>     alphabetically, as noticed by Rubén.
>   * added misssing newlines in 2/2 as noticed by Phillip and tweaked by
>     Junio.
>   * rebased on master (2953d95d40 (The eighth batch, 2024-03-15)), to avoid
>     conflicts in 'Documentation/config/advice.txt' due to Kristoffer's merged >     series
> [...] 
> Note that the code path where 'git rebase --apply' stops because of
> conflicts is not covered by the tests but I tested it manually using this
> diff:
> 
> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
> index 47534f1062..34eac2e6f4 100755
> --- a/t/t5520-pull.sh
> +++ b/t/t5520-pull.sh
> @@ -374,7 +374,7 @@ test_pull_autostash_fail ()
>       echo conflicting >>seq.txt &&
>       test_tick &&
>       git commit -m "Create conflict" seq.txt &&
> -	test_must_fail git pull --rebase . seq 2>err >out &&
> +	test_must_fail git -c rebase.backend=apply pull --rebase . seq 2>err >out &&
>       test_grep "Resolve all conflicts manually" err
>   '

Thanks for being so thorough, this version looks good to me

Best Wishes

Phillip

> 
> Philippe Blain (2):
>    sequencer: allow disabling conflict advice
>    builtin/am: allow disabling conflict advice
> 
>   Documentation/config/advice.txt |  2 ++
>   advice.c                        |  1 +
>   advice.h                        |  1 +
>   builtin/am.c                    | 14 +++++++++-----
>   sequencer.c                     | 33 ++++++++++++++++++---------------
>   t/t3501-revert-cherry-pick.sh   |  1 +
>   t/t3507-cherry-pick-conflict.sh |  2 ++
>   t/t4150-am.sh                   |  8 ++++----
>   t/t4254-am-corrupt.sh           |  2 +-
>   9 files changed, 39 insertions(+), 25 deletions(-)
> 
> 
> base-commit: 2953d95d402b6bff1a59c4712f4d46f1b9ea137f
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1682%2Fphil-blain%2Fsequencer-conflict-advice-v3
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1682/phil-blain/sequencer-conflict-advice-v3
> Pull-Request: https://github.com/gitgitgadget/git/pull/1682
> 
> Range-diff vs v2:
> 
>   1:  a2ce6fd24c2 ! 1:  6005c1e9890 sequencer: allow disabling conflict advice
>       @@ Commit message
>            merge conflict through a new config 'advice.mergeConflict', which is
>            named generically such that it can be used by other commands eventually.
>        
>       +    Remove that final '\n' in the first hunk in sequencer.c to avoid an
>       +    otherwise empty 'hint: ' line before the line 'hint: Disable this
>       +    message with "git config advice.mergeConflict false"' which is
>       +    automatically added by 'advise_if_enabled'.
>       +
>            Note that we use 'advise_if_enabled' for each message in the second hunk
>            in sequencer.c, instead of using 'if (show_hints &&
>            advice_enabled(...)', because the former instructs the user how to
>       @@ Commit message
>        
>            Update the tests accordingly. Note that the body of the second test in
>            t3507-cherry-pick-conflict.sh is enclosed in double quotes, so we must
>       -    escape them in the added line.
>       +    escape them in the added line. Note that t5520-pull.sh, which checks
>       +    that we display the advice for 'git rebase' (via 'git pull --rebase')
>       +    does not have to be updated because it only greps for a specific line in
>       +    the advice message.
>        
>            Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
>        
>         ## Documentation/config/advice.txt ##
>        @@ Documentation/config/advice.txt: advice.*::
>       - 		Advice on how to set your identity configuration when
>       - 		your information is guessed from the system username and
>       - 		domain name.
>       + 		Shown when the user's information is guessed from the
>       + 		system username and domain name, to tell the user how to
>       + 		set their identity configuration.
>        +	mergeConflict::
>       -+		Advice shown when various commands stop because of conflicts.
>       ++		Shown when various commands stop because of conflicts.
>         	nestedTag::
>       - 		Advice shown if a user attempts to recursively tag a tag object.
>       + 		Shown when a user attempts to recursively tag a tag object.
>         	pushAlreadyExists::
>        
>         ## advice.c ##
>       @@ advice.c: static struct {
>        
>         ## advice.h ##
>        @@ advice.h: enum advice_type {
>       + 	ADVICE_GRAFT_FILE_DEPRECATED,
>         	ADVICE_IGNORED_HOOK,
>         	ADVICE_IMPLICIT_IDENTITY,
>       - 	ADVICE_NESTED_TAG,
>        +	ADVICE_MERGE_CONFLICT,
>       + 	ADVICE_NESTED_TAG,
>         	ADVICE_OBJECT_NAME_WARNING,
>         	ADVICE_PUSH_ALREADY_EXISTS,
>       - 	ADVICE_PUSH_FETCH_FIRST,
>        
>         ## sequencer.c ##
>        @@ sequencer.c: static void print_advice(struct repository *r, int show_hint,
>       @@ sequencer.c: static void print_advice(struct repository *r, int show_hint,
>         
>         	if (msg) {
>        -		advise("%s\n", msg);
>       -+		advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s\n", msg);
>       ++		advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
>         		/*
>         		 * A conflict has occurred but the porcelain
>         		 * (typically rebase --interactive) wants to take care
>   2:  3235542cc6f ! 2:  73d07c8b6a7 builtin/am: allow disabling conflict advice
>       @@ Commit message
>            on stderr instead of stdout. In t4150, redirect stdout to 'out' and
>            stderr to 'err', since this is less confusing. In t4254, as we are
>            testing a specific failure mode of 'git am', simply disable the advice.
>       +    Note that we are not testing that this advice is shown in 'git rebase'
>       +    for the apply backend since 2ac0d6273f (rebase: change the default
>       +    backend from "am" to "merge", 2020-02-15).
>        
>       +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>       +    Helped-by: Junio C Hamano <gitster@pobox.com>
>            Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
>        
>         ## builtin/am.c ##
>       @@ builtin/am.c: static const char *msgnum(const struct am_state *state)
>         
>        -		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
>        -		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
>       -+		strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\"."), cmdline);
>       -+		strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
>       ++		strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline);
>       ++		strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline);
>         
>         		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
>         		    is_empty_or_missing_file(am_path(state, "patch")) &&
>         		    !repo_index_has_changes(the_repository, NULL, NULL))
>        -			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
>       -+			strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
>       ++			strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline);
>         
>        -		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
>        +		strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
> 



^ permalink raw reply	[relevance 0%]

* [PATCH 4/4] doc: git-clone: apply new documentation guidelines
  2024-03-24 22:18  4% [PATCH 0/4] Doc new guidelines Jean-Noël Avila via GitGitGadget
@ 2024-03-24 22:18  6% ` Jean-Noël Avila via GitGitGadget
  2024-03-29 11:19  2% ` [PATCH v2 0/5] Doc new guidelines Jean-Noël Avila via GitGitGadget
  1 sibling, 0 replies; 200+ results
From: Jean-Noël Avila via GitGitGadget @ 2024-03-24 22:18 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Heavily apply literal and placeholder markup everywhere.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/clone.txt |  23 +++++--
 Documentation/git-clone.txt    | 120 ++++++++++++++++-----------------
 Documentation/urls.txt         |  22 +++---
 3 files changed, 88 insertions(+), 77 deletions(-)

diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
index d037b57f729..0e0a8a1ae4a 100644
--- a/Documentation/config/clone.txt
+++ b/Documentation/config/clone.txt
@@ -1,13 +1,22 @@
-clone.defaultRemoteName::
+`clone.defaultRemoteName`::
 	The name of the remote to create when cloning a repository.  Defaults to
-	`origin`, and can be overridden by passing the `--origin` command-line
+	`origin`.
+ifdef::git-clone[]
+	It can be overridden by passing the `--origin` command-line
+	option.
+endif::[]
+ifndef::git-clone[]
+	It can be overridden by passing the `--origin` command-line
 	option to linkgit:git-clone[1].
+endif::[]
+`clone.rejectShallow`::
+	Reject cloning a repository if it is a shallow one. This can be overridden by
+	passing the `--reject-shallow` option on the command line.
+ifndef::git-clone[]
+	See linkgit:git-clone[1]
+endif::[]
 
-clone.rejectShallow::
-	Reject cloning a repository if it is a shallow one; this can be overridden by
-	passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
-
-clone.filterSubmodules::
+`clone.filterSubmodules`::
 	If a partial clone filter is provided (see `--filter` in
 	linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
 	the filter to submodules.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index f90977a8519..134b5278ca1 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,15 +9,15 @@ git-clone - Clone a repository into a new directory
 SYNOPSIS
 --------
 [verse]
-'git clone' [--template=<template-directory>]
-	  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
-	  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--dissociate] [--separate-git-dir <git-dir>]
-	  [--depth <depth>] [--[no-]single-branch] [--no-tags]
-	  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
-	  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
-	  [--filter=<filter> [--also-filter-submodules]] [--] <repository>
-	  [<directory>]
+`git clone` [`--template=`{empty}__<template-directory>__]
+	  [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
+	  [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
+	  [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
+	  [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
+	  [`--recurse-submodules`[`=`{empty}__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
+	  [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
+	  [`--filter=`{empty}__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
+	  [_<directory>_]
 
 DESCRIPTION
 -----------
@@ -31,7 +31,7 @@ currently active branch.
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
 arguments will in addition merge the remote master branch into the
-current master branch, if any (this is untrue when "--single-branch"
+current master branch, if any (this is untrue when `--single-branch`
 is given; see below).
 
 This default configuration is achieved by creating references to
@@ -42,12 +42,12 @@ configuration variables.
 
 OPTIONS
 -------
--l::
---local::
+`-l`::
+`--local`::
 	When the repository to clone from is on a local machine,
 	this flag bypasses the normal "Git aware" transport
 	mechanism and clones the repository by making a copy of
-	HEAD and everything under objects and refs directories.
+	`HEAD` and everything under objects and refs directories.
 	The files under `.git/objects/` directory are hardlinked
 	to save space when possible.
 +
@@ -67,14 +67,14 @@ links.
 source repository, similar to running `cp -r src dst` while modifying
 `src`.
 
---no-hardlinks::
+`--no-hardlinks`::
 	Force the cloning process from a repository on a local
 	filesystem to copy the files under the `.git/objects`
 	directory instead of using hardlinks. This may be desirable
 	if you are trying to make a back-up of your repository.
 
--s::
---shared::
+`-s`::
+`--shared`::
 	When the repository to clone is on the local machine,
 	instead of using hard links, automatically setup
 	`.git/objects/info/alternates` to share the objects
@@ -101,7 +101,7 @@ If you want to break the dependency of a repository cloned with `--shared` on
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
---reference[-if-able] <repository>::
+`--reference`[`-if-able`] _<repository>_::
 	If the reference _<repository>_ is on the local machine,
 	automatically setup `.git/objects/info/alternates` to
 	obtain objects from the reference _<repository>_.  Using
@@ -115,7 +115,7 @@ objects from the source repository into a pack in the cloned repository.
 *NOTE*: see the NOTE for the `--shared` option, and also the
 `--dissociate` option.
 
---dissociate::
+`--dissociate`::
 	Borrow the objects from reference repositories specified
 	with the `--reference` options only to reduce network
 	transfer, and stop borrowing from them after a clone is made
@@ -126,43 +126,43 @@ objects from the source repository into a pack in the cloned repository.
 	same repository, and this option can be used to stop the
 	borrowing.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Operate quietly.  Progress is not reported to the standard
 	error stream.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Run verbosely. Does not affect the reporting of progress status
 	to the standard error stream.
 
---progress::
+`--progress`::
 	Progress status is reported on the standard error stream
 	by default when it is attached to a terminal, unless `--quiet`
 	is specified. This flag forces progress status even if the
 	standard error stream is not directed to a terminal.
 
---server-option=<option>::
+`--server-option=`{empty}__<option>__::
 	Transmit the given string to the server when communicating using
 	protocol version 2.  The given string must not contain a NUL or LF
 	character.  The server's handling of server options, including
 	unknown ones, is server-specific.
-	When multiple `--server-option=<option>` are given, they are all
+	When multiple `--server-option=`{empty}__<option>__ are given, they are all
 	sent to the other side in the order listed on the command line.
 
--n::
---no-checkout::
+`-n`::
+`--no-checkout`::
 	No checkout of HEAD is performed after the clone is complete.
 
---[no-]reject-shallow::
+`--`[`no-`]`reject-shallow`::
 	Fail if the source repository is a shallow repository.
 	The `clone.rejectShallow` configuration variable can be used to
 	specify the default.
 
---bare::
+`--bare`::
 	Make a 'bare' Git repository.  That is, instead of
 	creating _<directory>_ and placing the administrative
-	files in `<directory>/.git`, make the _<directory>_
+	files in _<directory>_`/.git`, make the _<directory>_
 	itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
 	because there is nowhere to check out the working tree.
 	Also the branch heads at the remote are copied directly
@@ -171,28 +171,28 @@ objects from the source repository into a pack in the cloned repository.
 	used, neither remote-tracking branches nor the related
 	configuration variables are created.
 
---sparse::
+`--sparse`::
 	Employ a sparse-checkout, with only files in the toplevel
 	directory initially being present.  The
 	linkgit:git-sparse-checkout[1] command can be used to grow the
 	working directory as needed.
 
---filter=<filter-spec>::
+`--filter=`{empty}__<filter-spec>__::
 	Use the partial clone feature and request that the server sends
 	a subset of reachable objects according to a given object filter.
 	When using `--filter`, the supplied _<filter-spec>_ is used for
 	the partial clone filter. For example, `--filter=blob:none` will
 	filter out all blobs (file contents) until needed by Git. Also,
-	`--filter=blob:limit=<size>` will filter out all blobs of size
+	`--filter=blob:limit=`{empty}__<size>__ will filter out all blobs of size
 	at least _<size>_. For more details on filter specifications, see
 	the `--filter` option in linkgit:git-rev-list[1].
 
---also-filter-submodules::
+`--also-filter-submodules`::
 	Also apply the partial clone filter to any submodules in the repository.
 	Requires `--filter` and `--recurse-submodules`. This can be turned on by
 	default by setting the `clone.filterSubmodules` config option.
 
---mirror::
+`--mirror`::
 	Set up a mirror of the source repository.  This implies `--bare`.
 	Compared to `--bare`, `--mirror` not only maps local branches of the
 	source to local branches of the target, it maps all refs (including
@@ -200,14 +200,14 @@ objects from the source repository into a pack in the cloned repository.
 	that all these refs are overwritten by a `git remote update` in the
 	target repository.
 
--o <name>::
---origin <name>::
+`-o` _<name>_::
+`--origin` _<name>_::
 	Instead of using the remote name `origin` to keep track of the upstream
 	repository, use _<name>_.  Overrides `clone.defaultRemoteName` from the
 	config.
 
--b <name>::
---branch <name>::
+`-b` _<name>_::
+`--branch` _<name>_::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to _<name>_ branch
 	instead. In a non-bare repository, this is the branch that will
@@ -215,18 +215,18 @@ objects from the source repository into a pack in the cloned repository.
 	`--branch` can also take tags and detaches the HEAD at that commit
 	in the resulting repository.
 
--u <upload-pack>::
---upload-pack <upload-pack>::
+`-u` _<upload-pack>_::
+`--upload-pack` _<upload-pack>_::
 	When given, and the repository to clone from is accessed
 	via ssh, this specifies a non-default path for the command
 	run on the other end.
 
---template=<template-directory>::
+`--template=`{empty}__<template-directory>__::
 	Specify the directory from which templates will be used;
 	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
--c <key>=<value>::
---config <key>=<value>::
+`-c` __<key>__{empty}`=`{empty}__<value>__::
+`--config` __<key>__{empty}`=`{empty}__<value>__::
 	Set a configuration variable in the newly-created repository;
 	this takes effect immediately after the repository is
 	initialized, but before the remote history is fetched or any
@@ -239,25 +239,25 @@ objects from the source repository into a pack in the cloned repository.
 Due to limitations of the current implementation, some configuration
 variables do not take effect until after the initial fetch and checkout.
 Configuration variables known to not take effect are:
-`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
+`remote.`{empty}__<name>__{empty}`.mirror` and `remote.`{empty}__<name>__{empty}`.tagOpt`.  Use the
 corresponding `--mirror` and `--no-tags` options instead.
 
---depth <depth>::
+`--depth` _<depth>_::
 	Create a 'shallow' clone with a history truncated to the
 	specified number of commits. Implies `--single-branch` unless
 	`--no-single-branch` is given to fetch the histories near the
 	tips of all branches. If you want to clone submodules shallowly,
 	also pass `--shallow-submodules`.
 
---shallow-since=<date>::
+`--shallow-since=`{empty}__<date>__::
 	Create a shallow clone with a history after the specified time.
 
---shallow-exclude=<revision>::
+`--shallow-exclude=`{empty}__<revision>__::
 	Create a shallow clone with a history, excluding commits
 	reachable from a specified remote branch or tag.  This option
 	can be specified multiple times.
 
---[no-]single-branch::
+`--`[`no-`]`single-branch`::
 	Clone only the history leading to the tip of a single branch,
 	either specified by the `--branch` option or the primary
 	branch remote's `HEAD` points at.
@@ -267,7 +267,7 @@ corresponding `--mirror` and `--no-tags` options instead.
 	branch when `--single-branch` clone was made, no remote-tracking
 	branch is created.
 
---no-tags::
+`--no-tags`::
 	Don't clone any tags, and set
 	`remote.<remote>.tagOpt=--no-tags` in the config, ensuring
 	that future `git pull` and `git fetch` operations won't follow
@@ -279,7 +279,7 @@ maintain a branch with no references other than a single cloned
 branch. This is useful e.g. to maintain minimal clones of the default
 branch of some repository for search indexing.
 
---recurse-submodules[=<pathspec>]::
+`--recurse-submodules`[`=`{empty}__<pathspec>__]::
 	After the clone is created, initialize and clone submodules
 	within based on the provided _<pathspec>_.  If no _=<pathspec>_ is
 	provided, all submodules are initialized and cloned.
@@ -295,46 +295,46 @@ the clone is finished. This option is ignored if the cloned repository does
 not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`,
 or `--mirror` is given)
 
---[no-]shallow-submodules::
+`--`[`no-`]`shallow-submodules`::
 	All submodules which are cloned will be shallow with a depth of 1.
 
---[no-]remote-submodules::
+`--`[`no-`]`remote-submodules`::
 	All submodules which are cloned will use the status of the submodule's
 	remote-tracking branch to update the submodule, rather than the
 	superproject's recorded SHA-1. Equivalent to passing `--remote` to
 	`git submodule update`.
 
---separate-git-dir=<git-dir>::
+`--separate-git-dir=`{empty}__<git-dir>__::
 	Instead of placing the cloned repository where it is supposed
 	to be, place the cloned repository at the specified directory,
 	then make a filesystem-agnostic Git symbolic link to there.
 	The result is Git repository can be separated from working
 	tree.
 
---ref-format=<ref-format>::
+`--ref-format=`{empty}__<ref-format>__::
 
 Specify the given ref storage format for the repository. The valid values are:
 +
 include::ref-storage-format.txt[]
 
--j <n>::
---jobs <n>::
+`-j` _<n>_::
+`--jobs` _<n>_::
 	The number of submodules fetched at the same time.
 	Defaults to the `submodule.fetchJobs` option.
 
-<repository>::
+_<repository>_::
 	The (possibly remote) _<repository>_ to clone from.  See the
 	<<URLS,GIT URLS>> section below for more information on specifying
 	repositories.
 
-<directory>::
+_<directory>_::
 	The name of a new directory to clone into.  The "humanish"
 	part of the source repository is used if no _<directory>_ is
 	explicitly given (`repo` for `/path/to/repo.git` and `foo`
 	for `host.xz:foo/.git`).  Cloning into an existing directory
 	is only allowed if the directory is empty.
 
---bundle-uri=<uri>::
+`--bundle-uri=`{empty}__<uri>__::
 	Before fetching from the remote, fetch a bundle from the given
 	_<uri>_ and unbundle the data into the local repository. The refs
 	in the bundle will be stored under the hidden `refs/bundle/*`
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 0b9e0c4302d..f1a0c275ee1 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -57,11 +57,11 @@ endif::git-clone[]
 accept a suitable bundle file. See linkgit:git-bundle[1].
 
 When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the `remote-<transport>` remote helper, if one
+attempts to use the `remote-`{empty}__<transport>__ remote helper, if one
 exists. To explicitly request a remote helper, the following syntax
 may be used:
 
-- _<transport>_::_<address>_
+- _<transport>_::__<address>__
 
 where _<address>_ may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
@@ -72,10 +72,11 @@ you want to use a different format for them (such that the URLs you
 use will be rewritten into URLs that work), you can create a
 configuration section of the form:
 
-------------
-	[url "<actual-url-base>"]
-		insteadOf = <other-url-base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		insteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
@@ -91,10 +92,11 @@ rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
 If you want to rewrite URLs for push only, you can create a
 configuration section of the form:
 
-------------
-	[url "<actual-url-base>"]
-		pushInsteadOf = <other-url-base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		pushInsteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
-- 
gitgitgadget


^ permalink raw reply related	[relevance 6%]

* [PATCH 0/4] Doc new guidelines
@ 2024-03-24 22:18  4% Jean-Noël Avila via GitGitGadget
  2024-03-24 22:18  6% ` [PATCH 4/4] doc: git-clone: apply new documentation guidelines Jean-Noël Avila via GitGitGadget
  2024-03-29 11:19  2% ` [PATCH v2 0/5] Doc new guidelines Jean-Noël Avila via GitGitGadget
  0 siblings, 2 replies; 200+ results
From: Jean-Noël Avila via GitGitGadget @ 2024-03-24 22:18 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila

This is hopefully the final rehearsal of documentation style rework before
the reroll on the all the man pages.

Following some more research on how man pages are usually formatted and on
our track record of editing our documentation, this series states as the new
standard that all literal text is now formatted in verbatim and that
placeholders are emphasized, wherever they appear.

The markup is a bit more involved in some cases, but the output is more
consistent and follows the principle of least surprise for readers
accustomed to reading man pages.

Jean-Noël Avila (4):
  doc: rework CodingGuidelines with new formatting rules
  doc: allow literal and emphasis format in doc vs help tests
  doc: git-init: apply new documentation formatting guidelines
  doc: git-clone: apply new documentation guidelines

 Documentation/CodingGuidelines | 147 ++++++++++++++++++---------------
 Documentation/config/clone.txt |  23 ++++--
 Documentation/config/init.txt  |   4 +-
 Documentation/git-clone.txt    | 120 +++++++++++++--------------
 Documentation/git-init.txt     |  46 +++++------
 Documentation/urls.txt         |  22 ++---
 t/t0450-txt-doc-vs-help.sh     |   2 +-
 7 files changed, 193 insertions(+), 171 deletions(-)


base-commit: 11c821f2f2a31e70fb5cc449f9a29401c333aad2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1702%2Fjnavila%2Fdoc_new_Guidelines-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1702/jnavila/doc_new_Guidelines-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1702
-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] t/README: mention test files are make targets
  2024-03-24 15:14  4% [PATCH] t/README: mention test files are make targets Philippe Blain via GitGitGadget
@ 2024-03-24 16:10  0% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-03-24 16:10 UTC (permalink / raw)
  To: Philippe Blain via GitGitGadget; +Cc: git, Philippe Blain

"Philippe Blain via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Philippe Blain <levraiphilippeblain@gmail.com>
>
> Since 23fc63bf8f (make tests ignorable with "make -i", 2005-11-08), each
> test file defines a target in the test Makefile, such that one can
> invoke:
>
> 	make *checkout*
>
> to run all tests with 'checkout' in their filename. This is useful to
> run a subset of tests when you have a good idea of what part of the code
> is touched by the changes your are testing.

While I agree with the patch that this is a useful "feature" of
t/Makefile, I've always felt it was ugly to use a file itself that
we do not consider a build product, rather a source, as the target
to trigger some action.  Are we comfortable casting this behaviour
in stone by documenting it here?  Just checking by asking others, as
you are obviously comfortable enough to write this patch ;-)

Thanks.

> Document that in t/README to help new (or more seasoned) contributors
> that might not be aware.
>
> Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
> ---
>     t/README: mention test files are make targets
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1701%2Fphil-blain%2Ftests-makefile-targets-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1701/phil-blain/tests-makefile-targets-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/1701
>
>  t/README | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/t/README b/t/README
> index 621d3b8c095..71211109338 100644
> --- a/t/README
> +++ b/t/README
> @@ -32,6 +32,13 @@ the tests.
>      ok 2 - plain with GIT_WORK_TREE
>      ok 3 - plain bare
>  
> +t/Makefile defines a target for each test file, such that you can also use
> +shell pattern matching to run a subset of the tests:
> +
> +    make *checkout*
> +
> +will run all tests with 'checkout' in their filename.
> +
>  Since the tests all output TAP (see https://testanything.org) they can
>  be run with any TAP harness. Here's an example of parallel testing
>  powered by a recent version of prove(1):
>
> base-commit: 3e0d3cd5c7def4808247caf168e17f2bbf47892b


^ permalink raw reply	[relevance 0%]

* [PATCH] t/README: mention test files are make targets
@ 2024-03-24 15:14  4% Philippe Blain via GitGitGadget
  2024-03-24 16:10  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Philippe Blain via GitGitGadget @ 2024-03-24 15:14 UTC (permalink / raw)
  To: git; +Cc: Philippe Blain, Philippe Blain

From: Philippe Blain <levraiphilippeblain@gmail.com>

Since 23fc63bf8f (make tests ignorable with "make -i", 2005-11-08), each
test file defines a target in the test Makefile, such that one can
invoke:

	make *checkout*

to run all tests with 'checkout' in their filename. This is useful to
run a subset of tests when you have a good idea of what part of the code
is touched by the changes your are testing.

Document that in t/README to help new (or more seasoned) contributors
that might not be aware.

Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
---
    t/README: mention test files are make targets

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1701%2Fphil-blain%2Ftests-makefile-targets-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1701/phil-blain/tests-makefile-targets-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1701

 t/README | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/t/README b/t/README
index 621d3b8c095..71211109338 100644
--- a/t/README
+++ b/t/README
@@ -32,6 +32,13 @@ the tests.
     ok 2 - plain with GIT_WORK_TREE
     ok 3 - plain bare
 
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+    make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
 Since the tests all output TAP (see https://testanything.org) they can
 be run with any TAP harness. Here's an example of parallel testing
 powered by a recent version of prove(1):

base-commit: 3e0d3cd5c7def4808247caf168e17f2bbf47892b
-- 
gitgitgadget


^ permalink raw reply related	[relevance 4%]

* Re: Allow git bisect to auto-skip
  @ 2024-03-24  7:47  3%         ` Olliver Schinagl
  0 siblings, 0 replies; 200+ results
From: Olliver Schinagl @ 2024-03-24  7:47 UTC (permalink / raw)
  To: Junio C Hamano, Stefan Haller; +Cc: git

On 23-03-2024 21:51, Olliver Schinagl wrote:
> On 23-03-2024 19:43, Junio C Hamano wrote:
>> Stefan Haller <lists@haller-berlin.de> writes:
>>
>>> On 22.03.24 23:31, Junio C Hamano wrote:
>>>> It often is discovered that a commit
>>>> breaks bisection after the fact and it is not feasible to rebase
>>>> all the history after the commit.
>>>
>>> This reminds me of a similar problem with git blame, for which we have
>>> the blame.ignoreRevsFile config to work around it. Couldn't there be a
>>> similar mechanism for bisect, e.g. bisect.skipRevsFile?
>>
>> A Very good point.  If a breakage of a commit is "this does not even
>> build" kind of breakage, such a mechanism would be an excellent fit.
>>
>> But if a breakage is "only this particular test fails and we know
>> the reason why it fails has nothing to do with the bug we are
>> chasing", then compiling such a fixed list of commits, or pointing
>> at such a list with a configuration variable, would not work very
>> well, I am afraid.
> 
> This changes my view of the issue a little bit. Building vs testing, 
> though in reality its the same of course.
> 
> I can totally see that a user would need a special bisect script to 
> handle these cases 'this commit breaks test 54, 43 and 12; but the rest 
> work'. This is too specific to handle generically. Probably in using a 
> similar trick, store in the notes what works and does not work.

P.S. One (imo big) issue to me is, that notes aren't automatically 
fetched or pushed according to what I found so far online (haven't 
personally tested it yet).

If that is the case, this makes the feature to be used in combination 
with bisect much more difficult for the un-informed. having a 
`refs/notes/<bisect-or-like-related-name>` notes tree is great, but only 
if it gets automatically pulled.

I think this also might be the reason it is a very undervalued thing maybe?

Olliver

> 
> I think it still stands that having a 'generic' way of telling bisect to 
> skip the entire commit is still reasonable. Kind of like how you can do 
> `git push -o ci.skip=true` with GitLab.
> 
> Olliver
> 
>>
>> Thanks.


^ permalink raw reply	[relevance 3%]

* [PATCH 03/13] http: use new headers for each object request
  @ 2024-03-24  1:12  4% ` brian m. carlson
  2024-03-27  8:02  0%   ` Patrick Steinhardt
  2024-03-24  1:12  2% ` [PATCH 07/13] http: add support for authtype and credential brian m. carlson
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 200+ results
From: brian m. carlson @ 2024-03-24  1:12 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Matthew John Cheetham, M Hickford

Currently we create one set of headers for all object requests and reuse
it.  However, we'll need to adjust the headers for authentication
purposes in the future, so let's create a new set for each request so
that we can adjust them if the authentication changes.

Note that the cost of allocation here is tiny compared to the fact that
we're making a network call, not to mention probably a full TLS
connection, so this shouldn't have a significant impact on performance.
Moreover, nobody who cares about performance is using the dumb HTTP
protocol anyway, since it often makes huge numbers of requests compared
to the smart protocol.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 http.c | 19 +++++++++++--------
 http.h |  2 ++
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/http.c b/http.c
index e73b136e58..1c2200da77 100644
--- a/http.c
+++ b/http.c
@@ -128,7 +128,6 @@ static unsigned long empty_auth_useless =
 	| CURLAUTH_DIGEST;
 
 static struct curl_slist *pragma_header;
-static struct curl_slist *no_pragma_header;
 static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct curl_slist *host_resolutions;
@@ -299,6 +298,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
 	return nmemb;
 }
 
+static struct curl_slist *object_request_headers(void)
+{
+	return curl_slist_append(http_copy_default_headers(), "Pragma:");
+}
+
 static void closedown_active_slot(struct active_request_slot *slot)
 {
 	active_requests--;
@@ -1275,8 +1279,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 
 	pragma_header = curl_slist_append(http_copy_default_headers(),
 		"Pragma: no-cache");
-	no_pragma_header = curl_slist_append(http_copy_default_headers(),
-		"Pragma:");
 
 	{
 		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
@@ -1360,8 +1362,6 @@ void http_cleanup(void)
 	curl_slist_free_all(pragma_header);
 	pragma_header = NULL;
 
-	curl_slist_free_all(no_pragma_header);
-	no_pragma_header = NULL;
 
 	curl_slist_free_all(host_resolutions);
 	host_resolutions = NULL;
@@ -2370,6 +2370,7 @@ void release_http_pack_request(struct http_pack_request *preq)
 	}
 	preq->slot = NULL;
 	strbuf_release(&preq->tmpfile);
+	curl_slist_free_all(preq->headers);
 	free(preq->url);
 	free(preq);
 }
@@ -2454,11 +2455,11 @@ struct http_pack_request *new_direct_http_pack_request(
 	}
 
 	preq->slot = get_active_slot();
+	preq->headers = object_request_headers();
 	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
 	curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
-	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
-		no_pragma_header);
+	curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
 
 	/*
 	 * If there is data present from a previous transfer attempt,
@@ -2624,13 +2625,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
 	}
 
 	freq->slot = get_active_slot();
+	freq->headers = object_request_headers();
 
 	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
 	curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
-	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+	curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
 
 	/*
 	 * If we have successfully processed data from a previous fetch
@@ -2718,5 +2720,6 @@ void release_http_object_request(struct http_object_request *freq)
 		release_active_slot(freq->slot);
 		freq->slot = NULL;
 	}
+	curl_slist_free_all(freq->headers);
 	strbuf_release(&freq->tmpfile);
 }
diff --git a/http.h b/http.h
index 3af19a8bf5..c5f8cc4620 100644
--- a/http.h
+++ b/http.h
@@ -196,6 +196,7 @@ struct http_pack_request {
 	FILE *packfile;
 	struct strbuf tmpfile;
 	struct active_request_slot *slot;
+	struct curl_slist *headers;
 };
 
 struct http_pack_request *new_http_pack_request(
@@ -229,6 +230,7 @@ struct http_object_request {
 	int zret;
 	int rename;
 	struct active_request_slot *slot;
+	struct curl_slist *headers;
 };
 
 struct http_object_request *new_http_object_request(


^ permalink raw reply related	[relevance 4%]

* [PATCH 07/13] http: add support for authtype and credential
    2024-03-24  1:12  4% ` [PATCH 03/13] http: use new headers for each object request brian m. carlson
@ 2024-03-24  1:12  2% ` brian m. carlson
      3 siblings, 0 replies; 200+ results
From: brian m. carlson @ 2024-03-24  1:12 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Matthew John Cheetham, M Hickford

Now that we have the credential helper code set up to handle arbitrary
authentications schemes, let's add support for this in the HTTP code,
where we really want to use it.  If we're using this new functionality,
don't set a username and password, and instead set a header wherever
we'd normally do so, including for proxy authentication.

Since we can now handle this case, ask the credential helper to enable
the appropriate capabilities.

Finally, if we're using the authtype value, set "Expect: 100-continue".
Any type of authentication that requires multiple rounds (such as NTLM
or Kerberos) requires a 100 Continue (if we're larger than
http.postBuffer) because otherwise we send the pack data before we're
authenticated, the push gets a 401 response, and we can't rewind the
stream.  We don't know for certain what other custom schemes might
require this, the HTTP/1.1 standard has required handling this since
1999, the broken HTTP server for which we disabled this (Google's) is
now fixed and has been for some time, and libcurl has a 1-second
fallback in case the HTTP server is still broken.  In addition, it is
not unreasonable to require compliance with a 25-year old standard to
use new Git features.  For all of these reasons, do so here.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 http.c                      |  48 ++++++++++---
 http.h                      |   3 +
 remote-curl.c               |   4 +-
 t/t5563-simple-http-auth.sh | 133 ++++++++++++++++++++++++++++++++++++
 4 files changed, 176 insertions(+), 12 deletions(-)

diff --git a/http.c b/http.c
index 4f5df6fb14..f98c520924 100644
--- a/http.c
+++ b/http.c
@@ -561,18 +561,34 @@ static int curl_empty_auth_enabled(void)
 	return 0;
 }
 
+struct curl_slist *http_append_auth_header(const struct credential *c,
+					   struct curl_slist *headers)
+{
+	if (c->authtype && c->credential) {
+		struct strbuf auth = STRBUF_INIT;
+		strbuf_addf(&auth, "Authorization: %s %s",
+			    c->authtype, c->credential);
+		headers = curl_slist_append(headers, auth.buf);
+		strbuf_release(&auth);
+	}
+	return headers;
+}
+
 static void init_curl_http_auth(CURL *result)
 {
-	if (!http_auth.username || !*http_auth.username) {
+	if ((!http_auth.username || !*http_auth.username) &&
+	    (!http_auth.credential || !*http_auth.credential)) {
 		if (curl_empty_auth_enabled())
 			curl_easy_setopt(result, CURLOPT_USERPWD, ":");
 		return;
 	}
 
-	credential_fill(&http_auth, 0);
+	credential_fill(&http_auth, 1);
 
-	curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
-	curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+	if (http_auth.password) {
+		curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+		curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+	}
 }
 
 /* *var must be free-able */
@@ -586,17 +602,22 @@ static void var_override(const char **var, char *value)
 
 static void set_proxyauth_name_password(CURL *result)
 {
+	if (proxy_auth.password) {
 		curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
 			proxy_auth.username);
 		curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
 			proxy_auth.password);
+	} else if (proxy_auth.authtype && proxy_auth.credential) {
+		curl_easy_setopt(result, CURLOPT_PROXYHEADER,
+				 http_append_auth_header(&proxy_auth, NULL));
+	}
 }
 
 static void init_curl_proxy_auth(CURL *result)
 {
 	if (proxy_auth.username) {
-		if (!proxy_auth.password)
-			credential_fill(&proxy_auth, 0);
+		if (!proxy_auth.password && !proxy_auth.credential)
+			credential_fill(&proxy_auth, 1);
 		set_proxyauth_name_password(result);
 	}
 
@@ -1469,7 +1490,7 @@ struct active_request_slot *get_active_slot(void)
 
 	curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
-	if (http_auth.password || curl_empty_auth_enabled())
+	if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
 		init_curl_http_auth(slot->curl);
 
 	return slot;
@@ -1758,7 +1779,8 @@ static int handle_curl_result(struct slot_results *results)
 	} else if (missing_target(results))
 		return HTTP_MISSING_TARGET;
 	else if (results->http_code == 401) {
-		if (http_auth.username && http_auth.password) {
+		if ((http_auth.username && http_auth.password) ||\
+		    (http_auth.authtype && http_auth.credential)) {
 			credential_reject(&http_auth);
 			return HTTP_NOAUTH;
 		} else {
@@ -2066,11 +2088,15 @@ static int http_request(const char *url,
 	/* Add additional headers here */
 	if (options && options->extra_headers) {
 		const struct string_list_item *item;
-		for_each_string_list_item(item, options->extra_headers) {
-			headers = curl_slist_append(headers, item->string);
+		if (options && options->extra_headers) {
+			for_each_string_list_item(item, options->extra_headers) {
+				headers = curl_slist_append(headers, item->string);
+			}
 		}
 	}
 
+	headers = http_append_auth_header(&http_auth, headers);
+
 	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
 	curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2191,7 +2217,7 @@ static int http_request_reauth(const char *url,
 		BUG("Unknown http_request target");
 	}
 
-	credential_fill(&http_auth, 0);
+	credential_fill(&http_auth, 1);
 
 	return http_request(url, result, target, options);
 }
diff --git a/http.h b/http.h
index c5f8cc4620..a516ca4a9a 100644
--- a/http.h
+++ b/http.h
@@ -175,6 +175,9 @@ int http_get_file(const char *url, const char *filename,
 
 int http_fetch_ref(const char *base, struct ref *ref);
 
+struct curl_slist *http_append_auth_header(const struct credential *c,
+					   struct curl_slist *headers);
+
 /* Helpers for fetching packs */
 int http_get_info_packs(const char *base_url,
 			struct packed_git **packs_head);
diff --git a/remote-curl.c b/remote-curl.c
index f96bda2431..1c5416812a 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -931,7 +931,7 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 		if (err != HTTP_OK)
 			return -1;
 
-		if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
+		if (results.auth_avail & CURLAUTH_GSSNEGOTIATE || http_auth.authtype)
 			needs_100_continue = 1;
 	}
 
@@ -942,6 +942,8 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
 	headers = curl_slist_append(headers, needs_100_continue ?
 		"Expect: 100-continue" : "Expect:");
 
+	headers = http_append_auth_header(&http_auth, headers);
+
 	/* Add Accept-Language header */
 	if (rpc->hdr_accept_language)
 		headers = curl_slist_append(headers, rpc->hdr_accept_language);
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index ab8a721ccc..b3ed0d9fc2 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -74,6 +74,7 @@ test_expect_success 'access using basic auth' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=Basic realm="example.com"
@@ -87,6 +88,43 @@ test_expect_success 'access using basic auth' '
 	EOF
 '
 
+test_expect_success 'access using basic auth via authtype' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Basic
+	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	# Basic base64(alice:secret-passwd)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	capability[]=authtype
+	authtype=Basic
+	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	protocol=http
+	host=$HTTPD_DEST
+	EOF
+'
+
 test_expect_success 'access using basic auth invalid credentials' '
 	test_when_finished "per_test_cleanup" &&
 
@@ -108,6 +146,7 @@ test_expect_success 'access using basic auth invalid credentials' '
 	test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=Basic realm="example.com"
@@ -145,6 +184,7 @@ test_expect_success 'access using basic auth with extra challenges' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -183,6 +223,7 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' '
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=foobar param1="value1" param2="value2"
@@ -226,6 +267,7 @@ test_expect_success 'access using basic auth with wwwauth header continuations'
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -271,6 +313,7 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -312,6 +355,7 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
 	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
 
 	expect_credential_query get <<-EOF &&
+	capability[]=authtype
 	protocol=http
 	host=$HTTPD_DEST
 	wwwauth[]=FooBar param1="value1" param2="value2"
@@ -326,4 +370,93 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
 	EOF
 '
 
+test_expect_success 'access using bearer auth' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Bearer
+	credential=YS1naXQtdG9rZW4=
+	EOF
+
+	# Basic base64(a-git-token)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Bearer YS1naXQtdG9rZW4=
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1" param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	capability[]=authtype
+	authtype=Bearer
+	credential=YS1naXQtdG9rZW4=
+	protocol=http
+	host=$HTTPD_DEST
+	EOF
+'
+
+test_expect_success 'access using bearer auth with invalid credentials' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	capability[]=authtype
+	authtype=Bearer
+	credential=incorrect-token
+	EOF
+
+	# Basic base64(a-git-token)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Bearer YS1naXQtdG9rZW4=
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1" param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	capability[]=authtype
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query erase <<-EOF
+	capability[]=authtype
+	authtype=Bearer
+	credential=incorrect-token
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+'
+
 test_done


^ permalink raw reply related	[relevance 2%]

* Re: [PATCH] RFC: add MAINTAINERS file
  2024-03-23  3:27  3% [PATCH] RFC: add MAINTAINERS file Linus Arver via GitGitGadget
@ 2024-03-23 19:19  0% ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-03-23 19:19 UTC (permalink / raw)
  To: Linus Arver via GitGitGadget; +Cc: git, Linus Arver

"Linus Arver via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Linus Arver <linusa@google.com>
>
> This patch is designed to spur discussion about adding an official
> MAINTAINERS file to our project. The hope is that it could be used as a
> reference in (at least) the following scenarios:
>
>   (1) [CC list] patch authors want to know who to CC on their
>       submissions, without resorting to git-blame-level of precision;
>
>   (2) [escalation path] patch authors have been waiting 1+ weeks for
>       review comments, but are not sure who to escalate to (other than
>       Junio);
>
>   (3) [status tracking] record former maintainers/reviewers who are now
>       inactive.
>
> In addition having a MAINTAINERS file could give a more official sense
> of ownership in the codebase.

OK.  They are understandable goals.

As to the format of the actual file, I do not have much opinion.
What works for the kernel may or may not work for us, as the project
size is very different, but I am fairly confident that we can agree
on something usable.

I am more worried about how the file is used and maintained.  Some
things to think about while in the "spurred discussion" I can think
of are:

 - Is the project big enough to require this (especially for the
   purpose of (1)), or would

   $ git shortlog -n --no-merges --since=24.months -- path-to-file

   be sufficient and more importantly the value that it will keep
   current automatically outweigh the benefit of having this file
   that can go stale?  To answer this question, we'd need to know
   the turnover rates of past project contributors, of course.  If
   it is too high, having such a list may help for (1) and (3)
   above.

 - How binding is it for a contributor to be on this list as an area
   expert?  Will there be concrete "expected response time"?  It can
   be different for each area expert, of course.  I'd expect better
   from those who work on Git as a major part of their job and
   contributes some part of their work product back to the upstream,
   than from folks who do Git as a hobby.  Is each contributer
   expected to volunteer to be on this list, with self declared
   service level target?

 - With many good reviewer candidates being employed in companies
   and doing Git as part of their job, how would we handle folks
   getting transferred out of the Git ecosystem?  Unlike in a
   corporate environment, nominating successors who have no track
   record in the community by the current area expert would not work
   at all.  The successors themselves have to earn respect by
   demonstrating their own competence, which would take time.

There may be many others.

Thanks.

> The MAINTAINERS file here is stolen from the one used in the Linux
> Kernel. We do not have to follow its format at all; it is merely added
> here as a reference for comparison and prior art.
>
> Signed-off-by: Linus Arver <linusa@google.com>
> ---
>     RFC: add MAINTAINERS file
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1694%2Flistx%2Fmaintainers-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1694/listx/maintainers-v1
> Pull-Request: https://github.com/git/git/pull/1694
>
>  MAINTAINERS | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 85 insertions(+)
>  create mode 100644 MAINTAINERS
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> new file mode 100644
> index 00000000000..34fa3baf3a5
> --- /dev/null
> +++ b/MAINTAINERS
> @@ -0,0 +1,85 @@
> +List of maintainers
> +===================
> +
> +Descriptions of section entries and preferred order
> +---------------------------------------------------
> +
> +	M: *Mail* patches to: FullName <address@domain>
> +	R: Designated *Reviewer*: FullName <address@domain>
> +	   These reviewers should be CCed on patches.
> +	L: *Mailing list* that is relevant to this area
> +	S: *Status*, one of the following:
> +	   Supported:	Someone is actually paid to look after this.
> +	   Maintained:	Someone actually looks after it.
> +	   Odd Fixes:	It has a maintainer but they don't have time to do
> +			much other than throw the odd patch in. See below..
> +	   Orphan:	No current maintainer [but maybe you could take the
> +			role as you write your new code].
> +	   Obsolete:	Old code. Something tagged obsolete generally means
> +			it has been replaced by a better system and you
> +			should be using that.
> +	W: *Web-page* with status/info
> +	Q: *Patchwork* web based patch tracking system site
> +	B: URI for where to file *bugs*. A web-page with detailed bug
> +	   filing info, a direct bug tracker link, or a mailto: URI.
> +	C: URI for *chat* protocol, server and channel where developers
> +	   usually hang out, for example irc://server/channel.
> +	P: *Subsystem Profile* document for more details submitting
> +	   patches to the given subsystem. This is either an in-tree file,
> +	   or a URI. See Documentation/maintainer/maintainer-entry-profile.rst
> +	   for details.
> +	T: *SCM* tree type and location.
> +	   Type is one of: git, hg, quilt, stgit, topgit
> +	F: *Files* and directories wildcard patterns.
> +	   A trailing slash includes all files and subdirectory files.
> +	   F:	drivers/net/	all files in and below drivers/net
> +	   F:	drivers/net/*	all files in drivers/net, but not below
> +	   F:	*/net/*		all files in "any top level directory"/net
> +	   One pattern per line.  Multiple F: lines acceptable.
> +	X: *Excluded* files and directories that are NOT maintained, same
> +	   rules as F:. Files exclusions are tested before file matches.
> +	   Can be useful for excluding a specific subdirectory, for instance:
> +	   F:	net/
> +	   X:	net/ipv6/
> +	   matches all files in and below net excluding net/ipv6/
> +	N: Files and directories *Regex* patterns.
> +	   N:	[^a-z]tegra	all files whose path contains tegra
> +	                        (not including files like integrator)
> +	   One pattern per line.  Multiple N: lines acceptable.
> +	   scripts/get_maintainer.pl has different behavior for files that
> +	   match F: pattern and matches of N: patterns.  By default,
> +	   get_maintainer will not look at git log history when an F: pattern
> +	   match occurs.  When an N: match occurs, git log history is used
> +	   to also notify the people that have git commit signatures.
> +	K: *Content regex* (perl extended) pattern match in a patch or file.
> +	   For instance:
> +	   K: of_get_profile
> +	      matches patches or files that contain "of_get_profile"
> +	   K: \b(printk|pr_(info|err))\b
> +	      matches patches or files that contain one or more of the words
> +	      printk, pr_info or pr_err
> +	   One regex pattern per line.  Multiple K: lines acceptable.
> +
> +Maintainers List
> +----------------
> +
> +.. note:: When reading this list, please look for the most precise areas
> +          first. When adding to this list, please keep the entries in
> +          alphabetical order.
> +
> +3C59X NETWORK DRIVER
> +M:	Steffen Klassert <klassert@kernel.org>
> +L:	netdev@vger.kernel.org
> +S:	Odd Fixes
> +F:	Documentation/networking/device_drivers/ethernet/3com/vortex.rst
> +F:	drivers/net/ethernet/3com/3c59x.c
> +
> +...
> +
> +THE REST
> +M:	Linus Torvalds <torvalds@linux-foundation.org>
> +L:	linux-kernel@vger.kernel.org
> +S:	Buried alive in reporters
> +T:	git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
> +F:	*
> +F:	*/
>
> base-commit: 11c821f2f2a31e70fb5cc449f9a29401c333aad2


^ permalink raw reply	[relevance 0%]

* [PATCH] RFC: add MAINTAINERS file
@ 2024-03-23  3:27  3% Linus Arver via GitGitGadget
  2024-03-23 19:19  0% ` Junio C Hamano
  0 siblings, 1 reply; 200+ results
From: Linus Arver via GitGitGadget @ 2024-03-23  3:27 UTC (permalink / raw)
  To: git; +Cc: Linus Arver, Linus Arver

From: Linus Arver <linusa@google.com>

This patch is designed to spur discussion about adding an official
MAINTAINERS file to our project. The hope is that it could be used as a
reference in (at least) the following scenarios:

  (1) [CC list] patch authors want to know who to CC on their
      submissions, without resorting to git-blame-level of precision;

  (2) [escalation path] patch authors have been waiting 1+ weeks for
      review comments, but are not sure who to escalate to (other than
      Junio);

  (3) [status tracking] record former maintainers/reviewers who are now
      inactive.

In addition having a MAINTAINERS file could give a more official sense
of ownership in the codebase.

The MAINTAINERS file here is stolen from the one used in the Linux
Kernel. We do not have to follow its format at all; it is merely added
here as a reference for comparison and prior art.

Signed-off-by: Linus Arver <linusa@google.com>
---
    RFC: add MAINTAINERS file

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1694%2Flistx%2Fmaintainers-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1694/listx/maintainers-v1
Pull-Request: https://github.com/git/git/pull/1694

 MAINTAINERS | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 MAINTAINERS

diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 00000000000..34fa3baf3a5
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,85 @@
+List of maintainers
+===================
+
+Descriptions of section entries and preferred order
+---------------------------------------------------
+
+	M: *Mail* patches to: FullName <address@domain>
+	R: Designated *Reviewer*: FullName <address@domain>
+	   These reviewers should be CCed on patches.
+	L: *Mailing list* that is relevant to this area
+	S: *Status*, one of the following:
+	   Supported:	Someone is actually paid to look after this.
+	   Maintained:	Someone actually looks after it.
+	   Odd Fixes:	It has a maintainer but they don't have time to do
+			much other than throw the odd patch in. See below..
+	   Orphan:	No current maintainer [but maybe you could take the
+			role as you write your new code].
+	   Obsolete:	Old code. Something tagged obsolete generally means
+			it has been replaced by a better system and you
+			should be using that.
+	W: *Web-page* with status/info
+	Q: *Patchwork* web based patch tracking system site
+	B: URI for where to file *bugs*. A web-page with detailed bug
+	   filing info, a direct bug tracker link, or a mailto: URI.
+	C: URI for *chat* protocol, server and channel where developers
+	   usually hang out, for example irc://server/channel.
+	P: *Subsystem Profile* document for more details submitting
+	   patches to the given subsystem. This is either an in-tree file,
+	   or a URI. See Documentation/maintainer/maintainer-entry-profile.rst
+	   for details.
+	T: *SCM* tree type and location.
+	   Type is one of: git, hg, quilt, stgit, topgit
+	F: *Files* and directories wildcard patterns.
+	   A trailing slash includes all files and subdirectory files.
+	   F:	drivers/net/	all files in and below drivers/net
+	   F:	drivers/net/*	all files in drivers/net, but not below
+	   F:	*/net/*		all files in "any top level directory"/net
+	   One pattern per line.  Multiple F: lines acceptable.
+	X: *Excluded* files and directories that are NOT maintained, same
+	   rules as F:. Files exclusions are tested before file matches.
+	   Can be useful for excluding a specific subdirectory, for instance:
+	   F:	net/
+	   X:	net/ipv6/
+	   matches all files in and below net excluding net/ipv6/
+	N: Files and directories *Regex* patterns.
+	   N:	[^a-z]tegra	all files whose path contains tegra
+	                        (not including files like integrator)
+	   One pattern per line.  Multiple N: lines acceptable.
+	   scripts/get_maintainer.pl has different behavior for files that
+	   match F: pattern and matches of N: patterns.  By default,
+	   get_maintainer will not look at git log history when an F: pattern
+	   match occurs.  When an N: match occurs, git log history is used
+	   to also notify the people that have git commit signatures.
+	K: *Content regex* (perl extended) pattern match in a patch or file.
+	   For instance:
+	   K: of_get_profile
+	      matches patches or files that contain "of_get_profile"
+	   K: \b(printk|pr_(info|err))\b
+	      matches patches or files that contain one or more of the words
+	      printk, pr_info or pr_err
+	   One regex pattern per line.  Multiple K: lines acceptable.
+
+Maintainers List
+----------------
+
+.. note:: When reading this list, please look for the most precise areas
+          first. When adding to this list, please keep the entries in
+          alphabetical order.
+
+3C59X NETWORK DRIVER
+M:	Steffen Klassert <klassert@kernel.org>
+L:	netdev@vger.kernel.org
+S:	Odd Fixes
+F:	Documentation/networking/device_drivers/ethernet/3com/vortex.rst
+F:	drivers/net/ethernet/3com/3c59x.c
+
+...
+
+THE REST
+M:	Linus Torvalds <torvalds@linux-foundation.org>
+L:	linux-kernel@vger.kernel.org
+S:	Buried alive in reporters
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+F:	*
+F:	*/

base-commit: 11c821f2f2a31e70fb5cc449f9a29401c333aad2
-- 
gitgitgadget


^ permalink raw reply related	[relevance 3%]

* [PATCH v2 0/3] reftable/stack: use geometric table compaction
  @ 2024-03-21 22:40  2% ` Justin Tobler via GitGitGadget
  2024-03-29  4:16  3%   ` [PATCH v3 " Justin Tobler via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Justin Tobler via GitGitGadget @ 2024-03-21 22:40 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Justin Tobler

Hello again,

This is the second version my patch series that refactors the reftable
compaction strategy to instead follow a geometric sequence. Changes compared
to v1:

 * Added GIT_TEST_REFTABLE_NO_AUTOCOMPACTION environment variable to disable
   reftable compaction when testing.
 * Refactored worktree tests in t0610-reftable-basics.sh to properly assert
   git-pack-refs(1) works as expected.
 * Added test to validate that alternating table sizes are compacted.
 * Added benchmark to compare compaction strategies.
 * Moved change that made compaction segment end inclusive to its own
   commit.
 * Added additional explanation in commits and comments and fixed typos.

Thanks for taking a look!

Justin

Justin Tobler (3):
  reftable/stack: add env to disable autocompaction
  reftable/stack: use geometric table compaction
  reftable/segment: make segment end inclusive

 reftable/stack.c           | 113 ++++++++++++++++---------------------
 reftable/stack.h           |   3 -
 reftable/stack_test.c      |  66 +++++-----------------
 reftable/system.h          |   1 +
 t/t0610-reftable-basics.sh |  43 +++++++++-----
 5 files changed, 94 insertions(+), 132 deletions(-)


base-commit: 3bd955d26919e149552f34aacf8a4e6368c26cec
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1683%2Fjltobler%2Fjt%2Freftable-geometric-compaction-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1683/jltobler/jt/reftable-geometric-compaction-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1683

Range-diff vs v1:

 -:  ----------- > 1:  cb6b152e5c8 reftable/stack: add env to disable autocompaction
 1:  7a518853a10 ! 2:  def70084523 reftable/stack: use geometric table compaction
     @@ Commit message
          occurring until a separate operation produces a table matching the
          previous table log value.
      
     -    To avoid unbounded growth of the table list, walk through each table and
     -    evaluate if it needs to be included in the compaction segment to restore
     -    a geometric sequence.
     +    Instead, to avoid unbounded growth of the table list, the compaction
     +    strategy is updated to ensure tables follow a geometric sequence after
     +    each operation. This is done by walking the table list in reverse index
     +    order to identify the compaction segment start and end. The compaction
     +    segment end is found by identifying the first table which has a
     +    preceding table size less than twice the current table. Next, the
     +    compaction segment start is found iterating through the remaining tables
     +    in the list checking if the previous table size is less than twice the
     +    cumulative of tables from the segment end. This ensures the correct
     +    segment start is found and that the newly compacted table does not
     +    violate the geometric sequence.
     +
     +    When creating 10 thousand references, the new strategy has no
     +    performance impact:
     +
     +    Benchmark 1: update-ref: create refs sequentially (revision = HEAD~)
     +      Time (mean ± σ):     26.516 s ±  0.047 s    [User: 17.864 s, System: 8.491 s]
     +      Range (min … max):   26.447 s … 26.569 s    10 runs
     +
     +    Benchmark 2: update-ref: create refs sequentially (revision = HEAD)
     +      Time (mean ± σ):     26.417 s ±  0.028 s    [User: 17.738 s, System: 8.500 s]
     +      Range (min … max):   26.366 s … 26.444 s    10 runs
     +
     +    Summary
     +      update-ref: create refs sequentially (revision = HEAD) ran
     +        1.00 ± 0.00 times faster than update-ref: create refs sequentially (revision = HEAD~)
      
          Some tests in `t0610-reftable-basics.sh` assert the on-disk state of
          tables and are therefore updated to specify the correct new table count.
     @@ reftable/stack.c: static int segment_size(struct segment *s)
      +	 * until a valid segment end is found. If the preceding table is smaller
      +	 * than the current table multiplied by the geometric factor (2), the
      +	 * current table is set as the compaction segment end.
     ++	 *
     ++	 * Tables after the ending point are not added to the byte count because
     ++	 * they are already valid members of the geometric sequence. Due to the
     ++	 * properties of a geometric sequence, it is not possible for the sum of
     ++	 * these tables to exceed the value of the ending point table.
      +	 */
      +	for (i = n - 1; i > 0; i--) {
      +		if (sizes[i - 1] < sizes[i] * 2) {
     -+			seg.end = i;
     ++			seg.end = i + 1;
      +			bytes = sizes[i];
       			break;
      +		}
     @@ reftable/stack.c: static int segment_size(struct segment *s)
      +
      +	/*
      +	 * Find the starting table of the compaction segment by iterating
     -+	 * through the remaing tables and keeping track of the accumulated size
     -+	 * of all tables seen from the segment end table.
     ++	 * through the remaining tables and keeping track of the accumulated
     ++	 * size of all tables seen from the segment end table.
      +	 *
      +	 * Note that we keep iterating even after we have found the first
     -+	 * first starting point. This is because there may be tables in the
     -+	 * stack preceding that first starting point which violate the geometric
     ++	 * starting point. This is because there may be tables in the stack
     ++	 * preceding that first starting point which violate the geometric
      +	 * sequence.
      +	 */
      +	for (; i > 0; i--) {
     @@ reftable/stack.c: static int segment_size(struct segment *s)
       }
       
       static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
     -@@ reftable/stack.c: int reftable_stack_auto_compact(struct reftable_stack *st)
     - 		suggest_compaction_segment(sizes, st->merged->stack_len);
     - 	reftable_free(sizes);
     - 	if (segment_size(&seg) > 0)
     --		return stack_compact_range_stats(st, seg.start, seg.end - 1,
     -+		return stack_compact_range_stats(st, seg.start, seg.end,
     - 						 NULL);
     - 
     - 	return 0;
      
       ## reftable/stack.h ##
      @@ reftable/stack.h: int read_lines(const char *filename, char ***lines);
     @@ reftable/stack_test.c: static void test_reftable_stack_hash_id(void)
      -	EXPECT(min.start == 2);
      -	EXPECT(min.end == 7);
      +	EXPECT(min.start == 1);
     -+	EXPECT(min.end == 9);
     ++	EXPECT(min.end == 10);
       }
       
       static void test_suggest_compaction_segment_nothing(void)
     @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes cause a
       
       	test_commit -C repo --no-tag B &&
       	test_line_count = 1 repo/.git/reftable/tables.list
     + '
     + 
     ++test_expect_success 'ref transaction: alternating table sizes are compacted' '
     ++	test_when_finished "rm -rf repo" &&
     ++	git init repo &&
     ++	test_commit -C repo A &&
     ++	for i in $(test_seq 20)
     ++	do
     ++		git -C repo branch -f foo &&
     ++		git -C repo branch -d foo || return 1
     ++	done &&
     ++	test_line_count = 2 repo/.git/reftable/tables.list
     ++'
     ++
     + check_fsync_events () {
     + 	local trace="$1" &&
     + 	shift &&
      @@ t/t0610-reftable-basics.sh: test_expect_success 'ref transaction: writes are synced' '
       		git -C repo -c core.fsync=reference \
       		-c core.fsyncMethod=fsync update-ref refs/heads/branch HEAD &&
     @@ t/t0610-reftable-basics.sh: do
       		git -C repo pack-refs &&
       		test_expect_perms "-rw-rw-r--" repo/.git/reftable/tables.list &&
      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in main repo packs main refs' '
     + 	test_when_finished "rm -rf repo worktree" &&
     + 	git init repo &&
       	test_commit -C repo A &&
     - 	git -C repo worktree add ../worktree &&
     +-	git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
       
      -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
      -	test_line_count = 4 repo/.git/reftable/tables.list &&
     -+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
     -+	test_line_count = 1 repo/.git/reftable/tables.list &&
     ++	test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
     ++	test_line_count = 3 repo/.git/reftable/tables.list &&
       	git -C repo pack-refs &&
      -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
     -+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
     ++	test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
       	test_line_count = 1 repo/.git/reftable/tables.list
       '
       
      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
     + 	test_when_finished "rm -rf repo worktree" &&
     + 	git init repo &&
       	test_commit -C repo A &&
     - 	git -C repo worktree add ../worktree &&
     +-	git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C repo worktree add ../worktree &&
     ++	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/worktree/per-worktree HEAD &&
       
      -	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
      -	test_line_count = 4 repo/.git/reftable/tables.list &&
     -+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
     -+	test_line_count = 1 repo/.git/reftable/tables.list &&
     ++	test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
     ++	test_line_count = 3 repo/.git/reftable/tables.list &&
       	git -C worktree pack-refs &&
       	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
      -	test_line_count = 4 repo/.git/reftable/tables.list
     -+	test_line_count = 1 repo/.git/reftable/tables.list
     ++	test_line_count = 3 repo/.git/reftable/tables.list
       '
       
       test_expect_success 'worktree: creating shared ref updates main stack' '
     + 	test_when_finished "rm -rf repo worktree" &&
     + 	git init repo &&
     + 	test_commit -C repo A &&
     ++	test_commit -C repo B &&
     + 
     + 	git -C repo worktree add ../worktree &&
     + 	git -C repo pack-refs &&
      @@ t/t0610-reftable-basics.sh: test_expect_success 'worktree: creating shared ref updates main stack' '
     + 	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
     + 	test_line_count = 1 repo/.git/reftable/tables.list &&
       
     - 	git -C worktree update-ref refs/heads/shared HEAD &&
     +-	git -C worktree update-ref refs/heads/shared HEAD &&
     ++	GIT_TEST_REFTABLE_NO_AUTOCOMPACTION=true git -C worktree update-ref refs/heads/shared HEAD &&
       	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
     --	test_line_count = 2 repo/.git/reftable/tables.list
     -+	test_line_count = 1 repo/.git/reftable/tables.list
     + 	test_line_count = 2 repo/.git/reftable/tables.list
       '
     - 
     - test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
 -:  ----------- > 3:  a23e3fc6972 reftable/segment: make segment end inclusive

-- 
gitgitgadget


^ permalink raw reply	[relevance 2%]

* [PATCH v2 0/2] t9803-git-p4-shell-metachars.sh: update to use test_path_* functions
  2024-03-21 19:39  4% [PATCH 0/2] t9803-git-p4-shell-metachars.sh: update to use test_path_* functions Sanchit Jindal via GitGitGadget
@ 2024-03-21 20:29  4% ` Sanchit Jindal via GitGitGadget
  0 siblings, 0 replies; 200+ results
From: Sanchit Jindal via GitGitGadget @ 2024-03-21 20:29 UTC (permalink / raw)
  To: git; +Cc: Eric Sunshine, Sanchit Jindal

Correct Typo in Branch name

Sanchit Jindal (1):
  t9803: update commit messages and description

sanchit1053 (1):
  t9803-git-p4-shell-metachars.sh: update to use test_path_* functions

 t/t9803-git-p4-shell-metachars.sh | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)


base-commit: 3bd955d26919e149552f34aacf8a4e6368c26cec
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1691%2Fsanchit1053%2Fsj%2Ft9803_use_path_helper_fn-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1691/sanchit1053/sj/t9803_use_path_helper_fn-v2
Pull-Request: https://github.com/git/git/pull/1691

Range-diff vs v1:

 1:  b541ce7a49c = 1:  b541ce7a49c t9803-git-p4-shell-metachars.sh: update to use test_path_* functions
 2:  b8d0620d410 < -:  ----------- t9803: update commit messages and description
 -:  ----------- > 2:  e25b5f928a9 t9803: update commit messages and description

-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* [PATCH 0/2] t9803-git-p4-shell-metachars.sh: update to use test_path_* functions
@ 2024-03-21 19:39  4% Sanchit Jindal via GitGitGadget
  2024-03-21 20:29  4% ` [PATCH v2 " Sanchit Jindal via GitGitGadget
  0 siblings, 1 reply; 200+ results
From: Sanchit Jindal via GitGitGadget @ 2024-03-21 19:39 UTC (permalink / raw)
  To: git; +Cc: Sanchit Jindal

Correct Typo in Branch name

Sanchit Jindal (1):
  t9803: update commit messages and description

sanchit1053 (1):
  t9803-git-p4-shell-metachars.sh: update to use test_path_* functions

 t/t9803-git-p4-shell-metachars.sh | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)


base-commit: 3bd955d26919e149552f34aacf8a4e6368c26cec
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1691%2Fsanchit1053%2Fsj%2Ft9803_use_path_helper_fn-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1691/sanchit1053/sj/t9803_use_path_helper_fn-v1
Pull-Request: https://github.com/git/git/pull/1691
-- 
gitgitgadget


^ permalink raw reply	[relevance 4%]

* Re: [GSoC] Discuss: Implement support for reftables in ‘dumb’ HTTP transport
  @ 2024-03-21 16:59  0%     ` Junio C Hamano
  0 siblings, 0 replies; 200+ results
From: Junio C Hamano @ 2024-03-21 16:59 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Aryan Gupta, git, karthik nayak

Patrick Steinhardt <ps@pks.im> writes:

> The reason why I added this project is that I found it to be interesting
> as a thought experiment. The reason why we have "info/refs" is to help
> out clients of the dumb HTTP transport to figure out actual refs in the
> repository because they typically live in many separate files. But what
> I realized is that we don't actually need it with the reftable format
> anymore because we basically already have a definitive list of all files
> that a client needs to download to acquire all refs: "tables.list".

Yes, YES, YES!!!

> So theoretically speaking we can implement support for dumb HTTP with
> reftables by having the client download "tables.list" and then fetch all
> the "*.ref" files listed in it. Whether that is sensible may be a
> different question.

It is not even theoretical---I think it is the RIGHT way to do the
dumb HTTP walker if we had reftable from the beginning.  Having a
file at a known location where we can start "walking" from is
a very powerful thing [*1*].

If we do not have to change the server side at all, which I think
what you are saying, that removes the major part of the problem I
was having with this proposal.  I do not even have to worry about
speaking with the first-world bias and hurting folks with needs that
are only served by the dumb HTTP walker, either.

By the way, about two years ago, we have talked about making the
first step to deprecate dumb HTTP walker [*2*], but given that
nothing concrete materialized, we are ready to move further, yet.


[Footnote]

*1* With the filesystem backed refs, because there was no standard
    and widely supported way to ask a dumb HTTP server "what are the
    files and directories under this hierarchy?" and that was here
    the ugly "info/refs" hack came from.

*2* https://lore.kernel.org/git/f8639116d2d384a6d285c75830c52d8a8230ae6b.1647243509.git.ps@pks.im/


^ permalink raw reply	[relevance 0%]

* Re: why does git set X in LESS env var?
  @ 2024-03-21 15:53  0%                       ` Dragan Simic
  0 siblings, 0 replies; 200+ results
From: Dragan Simic @ 2024-03-21 15:53 UTC (permalink / raw)
  To: Thomas Guyot; +Cc: Junio C Hamano, Jeff King, Christoph Anton Mitterer, git

Hello all,

On 2023-11-06 04:47, Dragan Simic wrote:
> On 2023-11-02 15:19, Dragan Simic wrote:
>> On 2023-11-02 14:19, Thomas Guyot wrote:
>>> On 2023-11-02 02:48, Dragan Simic wrote:
>>>> We've basically reached some kind of an agreement about the need for 
>>>> a
>>>> good solution, which turned out to be rather complex as a result of
>>>> being quite universal and extensible, which was required for it to,
>>>> hopefully, be accepted into less(1).  Also, the author of less(1) 
>>>> seems
>>>> to be quite busy with some other things, and he prefers to implement 
>>>> new
>>>> features himself.
>>>> 
>>>> We've also agreed on another new feature for less(1), hopefully, 
>>>> which
>>>> isn't exactly related, but should be quite useful.  It's about the
>>>> secure mode for less(1).
>>> 
>>> Feel free to cc me on your next correspondence. If there are mailing
>>> lists archives for the thread I'll fetch them as needed. We have at
>>> least one working term/switch combination, which IMO is a better 
>>> start
>>> than nothing :)
>> 
>> Please test the "--redraw-on-quit" option, AFAICT that's all we need
>> (plus the already mentioned other improvements to less(1), to avoid
>> the version-dependent workarounds), and the distributions will
>> eventually catch up with the newer versions of less(1).  If the whole
>> thing has worked for decades as-is, it can continue working that way
>> for a year or two until the packages get updated.
>> 
>> There's actually no two-way mailing list for less(1), the entire
>> project is pretty much a one-man show, so to speak.  There's a GitHub
>> page that allows issues to be submitted, but I didn't use that, so I
>> exchanged a few private email messages instead with the author.  I've
>> already summed up the important parts of those messages.
> 
> Good news! :)  The author of less(1) has implemented a couple of new
> features that should resolve our issues with the pagination.  The
> improvements for the secure mode of less(1) have also been
> implemented.  I'll test all that in detail, and I'll move forward with
> implementing the required changes in Git.
> 
> It seems that a new version of less(1) may also be released rather
> soon, so we might be on a good way to have these longstanding issues
> resolved in the upcoming releases of Git and less(1).  It will take
> time for the Linux distributions to catch up with their package
> versions, but also the rolling-release distributions will get the new
> versions with no delays.

Good news, new beta version 653 of less(1) has been released! [1]

Version 653 contains new pagination-related features (in particular,
LESSKEY_CONTENT and LESS_UNSUPPORT) I asked the less(1) author for,
which will finally resolve age-old pagination issues in git and
a few other upstream projects.

I'll test the new beta version, after which I'll start working on
the required patches for git and a few other upstream projects.

Looking forward to resolving those age-old pagination issues! :)

[1] https://greenwoodsoftware.com/less/news.653.html


^ permalink raw reply	[relevance 0%]

Results 1-200 of ~80000   | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2023-02-01  8:26     [PATCH] git-gui: fix inability to quit after closing another instance Orgad Shaneh via GitGitGadget
2023-02-01 17:22     ` Junio C Hamano
2024-04-20 21:58  0%   ` Orgad Shaneh
2023-10-11 22:19     why does git set X in LESS env var? Christoph Anton Mitterer
2023-10-11 22:23     ` Junio C Hamano
2023-10-12  0:04       ` Jeff King
2023-10-12  1:39         ` Junio C Hamano
2023-10-12  5:30           ` Dragan Simic
2023-10-12 16:19             ` Junio C Hamano
2023-10-13 20:12               ` Dragan Simic
     [not found]                 ` <cfbe174f-23ac-4a35-8db4-66bdfdfdc14e@gmail.com>
2023-11-02  6:48                   ` Dragan Simic
2023-11-02 13:19                     ` Thomas Guyot
2023-11-02 14:19                       ` Dragan Simic
2023-11-06  3:47                         ` Dragan Simic
2024-03-21 15:53  0%                       ` Dragan Simic
2024-02-09 11:51     [PATCH] doc: git.txt-Fix inconsistency param description 秃头灯笼鱼 via GitGitGadget
2024-04-11  7:56  4% ` [PATCH v2 0/2] Fix the inconsistency in the description of the namespace option's parameter 秃头灯笼鱼 via GitGitGadget
2024-02-17 23:34     [PATCH 0/4] osxkeychain: bring in line with other credential helpers Bo Anderson via GitGitGadget
2024-04-01 21:40  0% ` M Hickford
2024-02-25 17:33     [PATCH] userdiff: better method/property matching for C# Steven Jeuris via GitGitGadget
2024-03-06 20:21     ` [PATCH v2] " Steven Jeuris via GitGitGadget
2024-03-27  7:30  0%   ` Johannes Sixt
2024-03-28  8:07  1%   ` [PATCH v3] " Steven Jeuris via GitGitGadget
2024-03-28 19:14  1%     ` [PATCH v4] " Steven Jeuris via GitGitGadget
2024-04-03 21:42  1%       ` [PATCH v5] " Steven Jeuris via GitGitGadget
2024-02-26 10:58     [PATCH] rebase -i: improve error message when picking merge Phillip Wood via GitGitGadget
2024-04-03 13:42  0% ` phillip.wood123
2024-04-04  6:08  0% ` Patrick Steinhardt
2024-04-08 14:16  3% ` [PATCH v2 0/2] " Phillip Wood via GitGitGadget
2024-03-04 22:51     [PATCH] show-ref: add --unresolved option John Cai via GitGitGadget
2024-04-08 17:38  2% ` [PATCH v2 0/3] show-ref: add --symbolic-name option John Cai via GitGitGadget
2024-03-05 20:03     [PATCH] reftable/stack: use geometric table compaction Justin Tobler via GitGitGadget
2024-03-21 22:40  2% ` [PATCH v2 0/3] " Justin Tobler via GitGitGadget
2024-03-29  4:16  3%   ` [PATCH v3 " Justin Tobler via GitGitGadget
2024-04-03  0:20  2%     ` [PATCH v4 0/2] " Justin Tobler via GitGitGadget
2024-04-03  4:47  0%       ` Patrick Steinhardt
2024-04-04 18:29  3%       ` [PATCH v5 0/3] " Justin Tobler via GitGitGadget
2024-04-08  6:12  0%         ` Patrick Steinhardt
2024-04-08 16:16  3%         ` [PATCH v6 " Justin Tobler via GitGitGadget
2024-04-08 16:20  0%           ` Patrick Steinhardt
2024-03-10 19:50     [PATCH v2 0/2] Allow disabling advice shown after merge conflicts Philippe Blain via GitGitGadget
2024-03-16 21:16     ` [PATCH v3 " Philippe Blain via GitGitGadget
2024-03-25 10:48  0%   ` Phillip Wood
2024-03-13 21:09     [GSoC] Discuss: Implement support for reftables in ‘dumb’ HTTP transport Aryan Gupta
2024-03-13 21:39     ` Junio C Hamano
2024-03-21 13:02       ` Patrick Steinhardt
2024-03-21 16:59  0%     ` Junio C Hamano
2024-03-16  6:27     [PATCH 0/6] Make trailer_info struct private (plus sequencer cleanup) Linus Arver via GitGitGadget
2024-04-19  5:22  3% ` [PATCH v2 0/8] " Linus Arver via GitGitGadget
2024-04-26  0:26  2%   ` [PATCH v3 00/10] " Linus Arver via GitGitGadget
2024-05-02  4:54  2%     ` [PATCH v4 " Linus Arver via GitGitGadget
2024-03-21 19:39  4% [PATCH 0/2] t9803-git-p4-shell-metachars.sh: update to use test_path_* functions Sanchit Jindal via GitGitGadget
2024-03-21 20:29  4% ` [PATCH v2 " Sanchit Jindal via GitGitGadget
2024-03-22 14:50     Worktree shares a common remote with main checkout Bill Wallace
2024-03-22 17:28     ` Kristoffer Haugsbakk
2024-04-03 14:26  2%   ` Bill Wallace
2024-04-03 15:30  0%     ` Phillip Wood
2024-04-03 16:18  0%       ` Bill Wallace
     [not found]             ` <17c2d5148e3afb10.70b1dd9aae081c6e.203dcd72f6563036@zivdesk>
2024-04-03 18:29  4%           ` Bill Wallace
2024-03-22 22:18     Allow git bisect to auto-skip Olliver Schinagl
2024-03-22 22:31     ` Junio C Hamano
2024-03-23 13:51       ` Stefan Haller
2024-03-23 18:43         ` Junio C Hamano
2024-03-23 20:51           ` Olliver Schinagl
2024-03-24  7:47  3%         ` Olliver Schinagl
2024-03-23  3:27  3% [PATCH] RFC: add MAINTAINERS file Linus Arver via GitGitGadget
2024-03-23 19:19  0% ` Junio C Hamano
2024-03-24  1:12     [PATCH 00/13] Support for arbitrary schemes in credentials brian m. carlson
2024-03-24  1:12  4% ` [PATCH 03/13] http: use new headers for each object request brian m. carlson
2024-03-27  8:02  0%   ` Patrick Steinhardt
2024-03-24  1:12  2% ` [PATCH 07/13] http: add support for authtype and credential brian m. carlson
2024-03-24  1:12     ` [PATCH 08/13] credential: add an argument to keep state brian m. carlson
2024-04-01 21:05       ` mirth hickford
2024-04-01 22:14  3%     ` brian m. carlson
2024-04-17  0:02     ` [PATCH v2 00/16] Support for arbitrary schemes in credentials brian m. carlson
2024-04-17  0:02  4%   ` [PATCH v2 03/16] http: use new headers for each object request brian m. carlson
2024-04-17  0:02  2%   ` [PATCH v2 08/16] http: add support for authtype and credential brian m. carlson
2024-03-24 15:14  4% [PATCH] t/README: mention test files are make targets Philippe Blain via GitGitGadget
2024-03-24 16:10  0% ` Junio C Hamano
2024-03-24 22:18  4% [PATCH 0/4] Doc new guidelines Jean-Noël Avila via GitGitGadget
2024-03-24 22:18  6% ` [PATCH 4/4] doc: git-clone: apply new documentation guidelines Jean-Noël Avila via GitGitGadget
2024-03-29 11:19  2% ` [PATCH v2 0/5] Doc new guidelines Jean-Noël Avila via GitGitGadget
2024-03-29 11:19  6%   ` [PATCH v2 4/5] doc: git-clone: apply new documentation formatting guidelines Jean-Noël Avila via GitGitGadget
2024-03-25 17:24     [PATCH 00/11] midx: split MIDX writing routines into midx-write.c, cleanup Taylor Blau
2024-03-25 17:24  1% ` [PATCH 07/11] midx: move `write_midx_internal` (and related functions) to midx-write.c Taylor Blau
2024-04-01 21:16     ` [PATCH v2 0/4] midx: split MIDX writing routines into midx-write.c, cleanup Taylor Blau
2024-04-01 21:16  1%   ` [PATCH v2 1/4] midx-write: move writing-related functions from midx.c Taylor Blau
2024-04-01 21:16       ` [PATCH v2 4/4] midx-write.c: use `--stdin-packs` when repacking Taylor Blau
2024-04-01 21:45  3%     ` Junio C Hamano
2024-03-28 14:53     bug report: spurious "cannot delete branch '%s' used by worktree" Tamir Duberstein
2024-03-28 17:24     ` Eric Sunshine
2024-03-28 17:39       ` Tamir Duberstein
2024-03-31  6:49  3%     ` Eric Sunshine
2024-03-31  7:45  3%       ` Tamir Duberstein
2024-04-02 10:10  0%       ` Phillip Wood
2024-04-02 10:26  0%         ` Tamir Duberstein
2024-03-29 22:57  5% [PATCH] advice: omit trailing whitespace Junio C Hamano
2024-03-29 23:23  0% ` Rubén Justo
2024-03-31  6:17  0%   ` Rubén Justo
2024-03-29 23:35  0% ` Dragan Simic
2024-03-30 22:46  2% [PATCH 0/8] update-ref: add support for update-symref option Karthik Nayak
2024-03-30 22:46  8% ` [PATCH 4/8] refs: accept symref in `ref_transaction_add_update` Karthik Nayak
2024-04-12  9:59  2% ` [PATCH v2 0/7] update-ref: add symref oriented commands Karthik Nayak
2024-04-12  9:59  8%   ` [PATCH v2 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
2024-04-12  9:59 12%   ` [PATCH v2 3/7] update-ref: add support for symref-delete Karthik Nayak
2024-04-23 21:28  1%   ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Karthik Nayak
2024-04-23 21:28  7%     ` [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
2024-04-23 21:28 12%     ` [PATCH v3 5/8] update-ref: support symrefs in the delete command Karthik Nayak
2024-04-23 22:03  2%     ` [PATCH v3 0/8] refs: add symref support to 'git-update-ref' Jeff King
2024-04-24 16:25  0%       ` Karthik Nayak
2024-04-26 15:24  1%     ` [PATCH v4 0/7] add symref-* commands to 'git-update-ref --stdin' Karthik Nayak
2024-04-26 15:24  7%       ` [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update` Karthik Nayak
2024-04-26 15:24 11%       ` [PATCH v4 4/7] update-ref: add support for 'symref-delete' command Karthik Nayak
2024-05-01 20:22  1%       ` [PATCH v5 0/7] refs: add support for transactional symref updates Karthik Nayak
2024-05-01 20:22  7%         ` [PATCH v5 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
2024-05-03 12:41  1%         ` [PATCH v6 0/7] refs: add support for transactional symref updates Karthik Nayak
2024-05-03 12:41  7%           ` [PATCH v6 1/7] refs: accept symref values in `ref_transaction_update()` Karthik Nayak
2024-05-04 15:18  0%             ` Phillip Wood
2024-03-30 23:10     [PATCH v2] bisect: Honor log.date Peter Krefting
2024-03-31  2:14     ` Junio C Hamano
2024-03-31 17:10       ` Peter Krefting
2024-03-31 22:58  3%     ` Junio C Hamano
     [not found]     <CAEK6H9o2QQGLQQZ0=C=1CjRGg6UiD_0fv7MwQujv1Sa_v41TAw@mail.gmail.com>
2024-04-01 17:37  3% ` Fwd: "reverse proxy" remote-helper Guga Figueiredo
2024-04-02  0:20  3% [PATCH] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
2024-04-02  6:28  0% ` Patrick Steinhardt
2024-04-04 20:00  0%   ` Linus Arver
2024-04-03  8:42  0% ` Matthias Aßhauer
2024-04-04 20:01  0%   ` Linus Arver
     [not found]     ` <35192e61-c442-6719-caf0-1019bf3e44c9@live.de>
2024-04-03 10:11  0%   ` [RFC] git-contacts: exclude list (was: Re: [PATCH] docs: recommend using contrib/contacts/git-contacts) Matthias Aßhauer
2024-04-06  1:22  3% ` [PATCH v2 0/8] docs: recommend using contrib/contacts/git-contacts Linus Arver via GitGitGadget
2024-04-09 21:56  3%   ` [PATCH v3 " Linus Arver via GitGitGadget
2024-04-11 23:32  2%     ` [PATCH v4 " Linus Arver via GitGitGadget
2024-04-16 23:01  2%       ` [PATCH v5 " Linus Arver via GitGitGadget
2024-04-18 21:51  3%         ` [PATCH v6 " Linus Arver via GitGitGadget
2024-04-03 13:26  4% git-merge: --no-commit is not respected on a fresh repository Jasmin Oster
2024-04-03 17:29  0% ` Junio C Hamano
2024-04-03 15:42  3% [PATCH] Win32: detect unix socket support at runtime Matthias Aßhauer via GitGitGadget
2024-04-04  9:39  3% [PATCH] sequencer: honor signoff opt in run_git_commit David Bimmler via GitGitGadget
2024-04-04  9:54     Git Server Simon Phai
2024-04-04 10:08     ` Konstantin Khomoutov
2024-04-04 10:26       ` Simon Phai
2024-04-05  9:11  2%     ` Konstantin Khomoutov
2024-04-04 10:16  4% Git log --decorate show prefetch objects Alexandre Badez
2024-04-04 16:56  0% ` Junio C Hamano
2024-04-04 21:37  0%   ` Alexandre Badez
2024-04-05  2:52 15% [PATCH] fetch: return when parsing submodule.recurse Derrick Stolee via GitGitGadget
2024-04-05 16:53  5% ` Junio C Hamano
2024-04-05 19:13  1% What's cooking in git.git (Apr 2024, #03; Fri, 5) Junio C Hamano
2024-04-06  1:11  0% ` Dragan Simic
2024-04-06  5:03  0%   ` Junio C Hamano
2024-04-06  8:37  0%     ` Dragan Simic
2024-04-05 21:48  3% [PATCH] feat(log): add option to search for header or body to `git log` Max Coplan via GitGitGadget
2024-04-07  3:24  2% ` [PATCH v2] log: add option to search for header or body Max Coplan via GitGitGadget
2024-04-06 18:11     [PATCH] config: do not leak excludes_file Junio C Hamano
2024-04-06 19:17  6% ` [WIP] git_config_pathname() leakfix Junio C Hamano
2024-04-07  0:56  2% ` [PATCH 0/12] git_config_string() considered harmful Jeff King
2024-04-07  1:00 13%   ` [PATCH 03/11] config: prefer git_config_string_dup() for temp variables Jeff King
2024-04-07 13:11  4% [PATCH] midx: disable replace objects blanet via GitGitGadget
2024-04-07 14:16  0% ` Taylor Blau
2024-04-07 18:02  2%   ` Taylor Blau
2024-04-08  5:45  0%     ` [External] " 鑫邢
2024-04-08  5:26  4% ` [PATCH v2] " blanet via GitGitGadget
2024-04-08 10:44     Makefiles are broken as of GNU Make commit 07fcee35f058a876447c8a021f9eb1943f902534 Dario Gjorgjevski
2024-04-08 15:51  3% ` [PATCH] Makefile(s): avoid recipe prefix in conditional statements Taylor Blau
2024-04-09  5:59  3% [PATCH] pack-bitmap: gracefully handle missing BTMP chunks Patrick Steinhardt
2024-04-10 15:02  3% ` Taylor Blau
2024-04-15  6:41  3% ` [PATCH v2] " Patrick Steinhardt
2024-04-15  8:51  0%   ` Patrick Steinhardt
2024-04-09 15:27  4% [PATCH 0/3] Cleanup rebase signoff tests Phillip Wood via GitGitGadget
2024-04-09 23:31  1% What's cooking in git.git (Apr 2024, #04; Tue, 9) Junio C Hamano
2024-04-11 17:16     Short form of --force-with-lease Wyatt Carpenter
2024-04-11 17:36     ` Kipras Melnikovas
2024-04-11 19:21       ` rsbecker
2024-04-11 21:30  3%     ` Junio C Hamano
2024-04-12  8:51  4% [PATCH] Documentation: fix typos describing date format blanet via GitGitGadget
2024-04-12 12:10  4% [PATCH] merge-tree: fix argument type of the `--merge-base` option Johannes Schindelin via GitGitGadget
2024-04-12 16:09  0% ` Junio C Hamano
2024-04-13  1:36  1% What's cooking in git.git (Apr 2024, #05; Fri, 12) Junio C Hamano
2024-04-14 22:08  4% [PATCH] Documentation: fix linkgit reference Yehezkel Bernat via GitGitGadget
2024-04-15  7:22  0% ` Patrick Steinhardt
2024-04-15 18:02  0%   ` Junio C Hamano
2024-04-15 11:42     [PATCH 0/5] global: drop external `the_index` variable Patrick Steinhardt
2024-04-15 11:42  1% ` [PATCH 2/5] builtin: stop using `the_index` Patrick Steinhardt
2024-04-18 12:14     ` [PATCH v2 0/6] global: drop `the_index` variable Patrick Steinhardt
2024-04-18 12:14  1%   ` [PATCH v2 2/6] builtin: stop using `the_index` Patrick Steinhardt
2024-04-16 23:21  4% [PATCH] mailmap: change primary address for Linus Arver Linus Arver via GitGitGadget
2024-04-17  6:16     [PATCH 0/2] builtin/receive-pack: convert to use git-maintenance(1) Patrick Steinhardt
2024-04-17  6:16  2% ` [PATCH 2/2] " Patrick Steinhardt
2024-04-17 16:50  0%   ` Karthik Nayak
2024-04-17  8:28  4% [PATCH 0/2] Use a "best effort" strategy in scheduled maintenance Johannes Schindelin via GitGitGadget
2024-04-18 12:53  3% ` [PATCH v2 " Johannes Schindelin via GitGitGadget
2024-04-24 16:14  3%   ` [PATCH v3 " Johannes Schindelin via GitGitGadget
2024-04-18  0:25  1% What's cooking in git.git (Apr 2024, #06; Wed, 17) Junio C Hamano
2024-04-18 13:14  3% [PATCH 0/5] rebase -m: fix --signoff with conflicts Phillip Wood
2024-04-18 18:40  3% [PATCH 0/4] upload-pack: support a missing-action Christian Couder
2024-04-18 18:40 10% ` [PATCH 4/4] upload-pack: allow configuring " Christian Couder
2024-04-18 19:21  5% ` [PATCH 0/4] upload-pack: support " Junio C Hamano
2024-04-19  9:51  2% [PATCH 00/11] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
2024-04-19  9:51 10% ` [PATCH 05/11] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
2024-04-23  5:07  2% ` [PATCH v2 00/12] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
2024-04-23  5:07 10%   ` [PATCH v2 06/12] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
2024-04-29  6:34  2% ` [PATCH v3 00/13] Stop relying on SHA1 fallback for `the_hash_algo` Patrick Steinhardt
2024-04-29  6:34 10%   ` [PATCH v3 06/13] remote-curl: fix parsing of detached SHA256 heads Patrick Steinhardt
2024-04-19 17:14  2% [ANNOUNCE] Git v2.45.0-rc0 Junio C Hamano
2024-04-20 19:51 10% [PATCH] docs: remove duplicate entry and fix typo in 2.45 changelog Orgad Shaneh via GitGitGadget
2024-04-22 10:28  3% [PATCH] stash: fix "--staged" with binary files Adam Johnson via GitGitGadget
2024-04-24  3:58     [PATCH 0/2] advice: add "all" option to disable all hints James Liu
2024-04-29  1:09     ` [PATCH v2 0/1] advice: add --no-advice global option James Liu
2024-04-29  1:09       ` [PATCH v2 1/1] " James Liu
2024-04-29  4:15         ` Dragan Simic
2024-04-29  5:01           ` James Liu
2024-04-29  6:40  4%         ` Jeff King
2024-04-29  6:55  0%           ` Dragan Simic
2024-04-30  0:56  0%           ` James Liu
2024-04-30  1:47  2%   ` [PATCH v3 0/1] " James Liu
2024-04-30  1:47         ` [PATCH v3 1/1] " James Liu
2024-04-30 16:29  2%       ` Junio C Hamano
2024-05-03  7:17         ` [PATCH v4 0/3] advice: add "all" option to disable all hints James Liu
2024-05-03  7:17  6%       ` [PATCH v4 1/3] doc: clean up usage documentation for --no-* opts James Liu
2024-05-03 17:30  0%         ` Junio C Hamano
2024-05-03  7:17 12%       ` [PATCH v4 2/3] doc: add spacing around paginate options James Liu
2024-05-03 14:32  0%         ` Karthik Nayak
2024-05-03 17:36  0%           ` Junio C Hamano
2024-05-03  7:17  5%       ` [PATCH v4 3/3] advice: add --no-advice global option James Liu
2024-04-24 16:27 12% [PATCH] Documentation/RelNotes/2.45.0.txt: fix typo Taylor Blau
2024-04-24 17:07  2% [ANNOUNCE] Git v2.45.0-rc1 Junio C Hamano
2024-04-25 16:19     Use of Git with local folders Felipe Bustamante
2024-04-26 10:16     ` Karthik Nayak
2024-04-26 15:56       ` Felipe Bustamante
2024-04-27 16:08  3%     ` Beat Bolli
2024-04-25 18:59  4% [PATCH] completion: fix zsh parsing $GIT_PS1_SHOWUPSTREAM Thomas via GitGitGadget
2024-04-28 22:30  3% [PATCH 00/13] builtin: implement, document and test url-parse Matheus Moreira via GitGitGadget
2024-04-29 20:53  3% ` Torsten Bögershausen
2024-04-29 22:04  0%   ` Reply to community feedback Matheus Afonso Martins Moreira
2024-04-30  6:51  0%     ` Torsten Bögershausen
2024-04-29  4:31  2% [PATCH] builtin/tag.c: add --trailer arg John Passaro via GitGitGadget
2024-04-29 16:53  1% ` [PATCH v2] " John Passaro via GitGitGadget
2024-04-29 18:54  3%   ` [PATCH v3 0/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
2024-04-30 14:41  3%     ` [PATCH v4 " John Passaro via GitGitGadget
2024-04-29  7:41  2% [GIT PULL] l10n updates for 2.45.0 Jiang Xin
2024-04-29 14:37  0% ` Junio C Hamano
2024-04-29 13:41     [PATCH 0/3] Clarify pseudo-ref terminology Patrick Steinhardt
2024-04-29 13:41  6% ` [PATCH 2/3] refs: do not label special refs as pseudo refs Patrick Steinhardt
2024-04-29 15:12  0%   ` Phillip Wood
2024-04-29 22:52  0%   ` Justin Tobler
2024-04-30  7:29  0%     ` Patrick Steinhardt
2024-04-30 12:26  2% ` [PATCH v2 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
2024-04-30 12:26 19%   ` [PATCH v2 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
2024-04-30 12:49  0%     ` Karthik Nayak
2024-04-30 12:27 28%   ` [PATCH v2 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
2024-05-02  8:17  3% ` [PATCH v3 00/10] Clarify pseudo-ref terminology Patrick Steinhardt
2024-05-02  8:17 18%   ` [PATCH v3 01/10] Documentation/glossary: redefine pseudorefs as special refs Patrick Steinhardt
2024-05-02  8:17 28%   ` [PATCH v3 10/10] refs: refuse to write pseudorefs Patrick Steinhardt
2024-04-29 17:12  2% [ANNOUNCE] Git v2.45.0 Junio C Hamano
2024-05-02  5:56     ` t4216-log-bloom.sh broken ? Torsten Bögershausen
2024-05-02 16:06       ` Junio C Hamano
2024-05-02 18:59         ` Torsten Bögershausen
2024-05-02 19:26           ` SZEDER Gábor
2024-05-02 19:55             ` Junio C Hamano
2024-05-02 20:05  3%           ` Junio C Hamano
2024-04-30 14:50  4% [PATCH] refs: return conflict error when checking packed refs Ivan Tse via GitGitGadget
2024-05-02 12:38  0% ` Patrick Steinhardt
2024-05-03  4:50 19% ` [PATCH v2] " Ivan Tse via GitGitGadget
2024-05-03  6:38  3%   ` Patrick Steinhardt
2024-05-04  3:04 19%   ` [PATCH v3] " Ivan Tse via GitGitGadget
2024-04-30 16:58  3% [PATCH] scalar: avoid segfault in reconfigure --all Derrick Stolee via GitGitGadget
2024-04-30 21:53  3% [PATCH] completion: zsh: stop leaking local cache variable D. Ben Knoble via GitGitGadget
2024-05-01  5:26     Help troubleshoot performance regression cloning with depth: git 2.44 vs git 2.42 Dhruva Krishnamurthy
2024-05-01 22:00     ` using tree as attribute source is slow, was " Jeff King
2024-05-02  0:33       ` Taylor Blau
2024-05-02 17:33         ` Taylor Blau
2024-05-02 17:44           ` Junio C Hamano
2024-05-02 17:55             ` Taylor Blau
2024-05-02 19:01               ` Karthik Nayak
2024-05-02 21:08                 ` Junio C Hamano
2024-05-03  5:37                   ` Dhruva Krishnamurthy
2024-05-03 15:34  2%                 ` Re* " Junio C Hamano
2024-05-02  6:51     [PATCH 00/11] reftable: expose write options as config Patrick Steinhardt
2024-05-02  6:52  2% ` [PATCH 09/11] refs/reftable: allow disabling writing the object index Patrick Steinhardt
2024-05-03  6:27  3% [PATCH 0/5] refs: remove functions without ref store Patrick Steinhardt
2024-05-03  6:28  6% ` [PATCH 4/5] cocci: apply rules to rewrite callers of "refs" interfaces Patrick Steinhardt
2024-05-03 18:48  0%   ` Taylor Blau
2024-05-04  5:57  3% [PATCH] Bug fix: ensure P4 "err" is displayed when exception is raised Fahad Alrashed via GitGitGadget

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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