diff mbox series

[v2,6/8] refs: introduce the `ref_transaction_update_reflog` function

Message ID 20241213-320-git-refs-migrate-reflogs-v2-6-f28312cdb6c0@gmail.com (mailing list archive)
State Superseded
Headers show
Series refs: add reflog support to `git refs migrate` | expand

Commit Message

Karthik Nayak Dec. 13, 2024, 10:36 a.m. UTC
Introduce a new function `ref_transaction_update_reflog`, for clients to
add a reflog update to a transaction. While the existing function
`ref_transaction_update` also allows clients to add a reflog entry, this
function does a few things more, It:
  - Enforces that only a reflog entry is added and does not update the
  ref itself.
  - Allows the users to also provide the committer information. This
  means clients can add reflog entries with custom committer
  information.

A follow up commit will utilize this function to add reflog support to
`git refs migrate`.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 refs.c               | 44 ++++++++++++++++++++++++++++++++++++++------
 refs.h               | 12 ++++++++++++
 refs/files-backend.c | 24 ++++++++++++++++--------
 3 files changed, 66 insertions(+), 14 deletions(-)

Comments

Christian Couder Dec. 13, 2024, 11:44 a.m. UTC | #1
On Fri, Dec 13, 2024 at 11:36 AM Karthik Nayak <karthik.188@gmail.com> wrote:

> +int ref_transaction_update_reflog(struct ref_transaction *transaction,
> +                                 const char *refname,
> +                                 const struct object_id *new_oid,
> +                                 const struct object_id *old_oid,
> +                                 const char *committer_info, unsigned int flags,
> +                                 const char *msg, unsigned int index,
> +                                 struct strbuf *err)
> +{
> +       struct ref_update *update;
> +
> +       assert(err);
> +
> +       if (!transaction_refname_valid(refname, new_oid, flags, 1, err))
> +               return -1;
> +
> +       flags |= REF_LOG_ONLY | REF_NO_DEREF;

If we could switch the above lines like this:

      flags |= REF_LOG_ONLY | REF_NO_DEREF;

      if (!transaction_refname_valid(refname, new_oid, flags, 1, err))
               return -1;

maybe we wouldn't need transaction_refname_valid() to take an
'unsigned int reflog' argument and we could instead use 'flags &
REF_LOG_ONLY' inside that function?

> +       update = ref_transaction_add_update(transaction, refname, flags,
> +                                           new_oid, old_oid, NULL, NULL,
> +                                           committer_info, msg);
> +       /*
> +        * While we do set the old_oid value, we unset the flag to skip
> +        * old_oid verification which only makes sense for refs.
> +        */
> +       update->flags &= ~REF_HAVE_OLD;
> +       update->index = index;
> +
>         return 0;
>  }
Patrick Steinhardt Dec. 13, 2024, 12:24 p.m. UTC | #2
On Fri, Dec 13, 2024 at 11:36:51AM +0100, Karthik Nayak wrote:
> diff --git a/refs.h b/refs.h
> index a5bedf48cf6de91005a7e8d0bf58ca98350397a6..67f8b3eef3f2101409e5cc6eb2241d99e9f7d95c 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -727,6 +727,18 @@ int ref_transaction_update(struct ref_transaction *transaction,
>  			   unsigned int flags, const char *msg,
>  			   struct strbuf *err);
>  
> +/*
> + * Similar to`ref_transaction_update`, but this function is only for adding
> + * a reflog update. Supports providing custom committer information.
> + */
> +int ref_transaction_update_reflog(struct ref_transaction *transaction,
> +				  const char *refname,
> +				  const struct object_id *new_oid,
> +				  const struct object_id *old_oid,
> +				  const char *committer_info, unsigned int flags,
> +				  const char *msg, unsigned int index,
> +				  struct strbuf *err);
> +

Nit: it would be great to explain what the index does in the doc, as it
is completely non-obvious.

Patrick
Karthik Nayak Dec. 13, 2024, 7:49 p.m. UTC | #3
Christian Couder <christian.couder@gmail.com> writes:

> On Fri, Dec 13, 2024 at 11:36 AM Karthik Nayak <karthik.188@gmail.com> wrote:
>
>> +int ref_transaction_update_reflog(struct ref_transaction *transaction,
>> +                                 const char *refname,
>> +                                 const struct object_id *new_oid,
>> +                                 const struct object_id *old_oid,
>> +                                 const char *committer_info, unsigned int flags,
>> +                                 const char *msg, unsigned int index,
>> +                                 struct strbuf *err)
>> +{
>> +       struct ref_update *update;
>> +
>> +       assert(err);
>> +
>> +       if (!transaction_refname_valid(refname, new_oid, flags, 1, err))
>> +               return -1;
>> +
>> +       flags |= REF_LOG_ONLY | REF_NO_DEREF;
>
> If we could switch the above lines like this:
>
>       flags |= REF_LOG_ONLY | REF_NO_DEREF;
>
>       if (!transaction_refname_valid(refname, new_oid, flags, 1, err))
>                return -1;
>
> maybe we wouldn't need transaction_refname_valid() to take an
> 'unsigned int reflog' argument and we could instead use 'flags &
> REF_LOG_ONLY' inside that function?
>

The issue is the that this changes existing behavior, since
`ref_transaction_update()` can also be called with the `REF_LOG_ONLY`
flag set.

But, I think it is a worthwhile change, because earlier even for reflog
updates we would show 'refusing to update ref ...' as the message. I'll
add this in and also make a note in the commit message.

Thanks

>> +       update = ref_transaction_add_update(transaction, refname, flags,
>> +                                           new_oid, old_oid, NULL, NULL,
>> +                                           committer_info, msg);
>> +       /*
>> +        * While we do set the old_oid value, we unset the flag to skip
>> +        * old_oid verification which only makes sense for refs.
>> +        */
>> +       update->flags &= ~REF_HAVE_OLD;
>> +       update->index = index;
>> +
>>         return 0;
>>  }
diff mbox series

Patch

diff --git a/refs.c b/refs.c
index 428ca256f3e5860554e9a7fa42a8368bb2689b31..9f539369bc94a25594adc3e95847f2fe72f58a08 100644
--- a/refs.c
+++ b/refs.c
@@ -1203,20 +1203,21 @@  struct ref_update *ref_transaction_add_update(
 
 static int transaction_refname_valid(const char *refname,
 				     const struct object_id *new_oid,
-				     unsigned int flags, struct strbuf *err)
+				     unsigned int flags, unsigned int reflog,
+				     struct strbuf *err)
 {
 	if (flags & REF_SKIP_REFNAME_VERIFICATION)
 		return 1;
 
 	if (is_pseudo_ref(refname)) {
-		strbuf_addf(err, _("refusing to update pseudoref '%s'"),
-			    refname);
+		const char *what = reflog ? "reflog for pseudoref" : "pseudoref";
+		strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
 		return 0;
 	} else if ((new_oid && !is_null_oid(new_oid)) ?
 		 check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
 		 !refname_is_safe(refname)) {
-		strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
-			    refname);
+		const char *what = reflog ? "reflog with bad name" : "ref with bad name";
+		strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
 		return 0;
 	}
 
@@ -1240,7 +1241,7 @@  int ref_transaction_update(struct ref_transaction *transaction,
 		return -1;
 	}
 
-	if (!transaction_refname_valid(refname, new_oid, flags, err))
+	if (!transaction_refname_valid(refname, new_oid, flags, 0, err))
 		return -1;
 
 	if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
@@ -1259,6 +1260,37 @@  int ref_transaction_update(struct ref_transaction *transaction,
 	ref_transaction_add_update(transaction, refname, flags,
 				   new_oid, old_oid, new_target,
 				   old_target, NULL, msg);
+
+	return 0;
+}
+
+int ref_transaction_update_reflog(struct ref_transaction *transaction,
+				  const char *refname,
+				  const struct object_id *new_oid,
+				  const struct object_id *old_oid,
+				  const char *committer_info, unsigned int flags,
+				  const char *msg, unsigned int index,
+				  struct strbuf *err)
+{
+	struct ref_update *update;
+
+	assert(err);
+
+	if (!transaction_refname_valid(refname, new_oid, flags, 1, err))
+		return -1;
+
+	flags |= REF_LOG_ONLY | REF_NO_DEREF;
+
+	update = ref_transaction_add_update(transaction, refname, flags,
+					    new_oid, old_oid, NULL, NULL,
+					    committer_info, msg);
+	/*
+	 * While we do set the old_oid value, we unset the flag to skip
+	 * old_oid verification which only makes sense for refs.
+	 */
+	update->flags &= ~REF_HAVE_OLD;
+	update->index = index;
+
 	return 0;
 }
 
diff --git a/refs.h b/refs.h
index a5bedf48cf6de91005a7e8d0bf58ca98350397a6..67f8b3eef3f2101409e5cc6eb2241d99e9f7d95c 100644
--- a/refs.h
+++ b/refs.h
@@ -727,6 +727,18 @@  int ref_transaction_update(struct ref_transaction *transaction,
 			   unsigned int flags, const char *msg,
 			   struct strbuf *err);
 
+/*
+ * Similar to`ref_transaction_update`, but this function is only for adding
+ * a reflog update. Supports providing custom committer information.
+ */
+int ref_transaction_update_reflog(struct ref_transaction *transaction,
+				  const char *refname,
+				  const struct object_id *new_oid,
+				  const struct object_id *old_oid,
+				  const char *committer_info, unsigned int flags,
+				  const char *msg, unsigned int index,
+				  struct strbuf *err);
+
 /*
  * Add a reference creation to transaction. new_oid is the value that
  * the reference should have after the update; it must not be
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 255fed8354cae982f785b1b85340e2a1eeecf2a6..c11213f52065bcf2fa7612df8f9500692ee2d02c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3080,10 +3080,12 @@  static int files_transaction_finish_initial(struct files_ref_store *refs,
 		}
 
 		/*
-		 * packed-refs don't support symbolic refs and root refs, so we
-		 * have to queue these references via the loose transaction.
+		 * packed-refs don't support symbolic refs, root refs and reflogs,
+		 * so we have to queue these references via the loose transaction.
 		 */
-		if (update->new_target || is_root_ref(update->refname)) {
+		if (update->new_target ||
+		    is_root_ref(update->refname) ||
+		    (update->flags & REF_LOG_ONLY)) {
 			if (!loose_transaction) {
 				loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
 				if (!loose_transaction) {
@@ -3092,11 +3094,17 @@  static int files_transaction_finish_initial(struct files_ref_store *refs,
 				}
 			}
 
-			ref_transaction_add_update(loose_transaction, update->refname,
-						   update->flags & ~REF_HAVE_OLD,
-						   update->new_target ? NULL : &update->new_oid, NULL,
-						   update->new_target, NULL, update->committer_info,
-						   NULL);
+			if (update->flags & REF_LOG_ONLY)
+				ref_transaction_add_update(loose_transaction, update->refname,
+							   update->flags, &update->new_oid,
+							   &update->old_oid, NULL, NULL,
+							   update->committer_info, update->msg);
+			else
+				ref_transaction_add_update(loose_transaction, update->refname,
+							   update->flags & ~REF_HAVE_OLD,
+							   update->new_target ? NULL : &update->new_oid, NULL,
+							   update->new_target, NULL, update->committer_info,
+							   NULL);
 		} else {
 			ref_transaction_add_update(packed_transaction, update->refname,
 						   update->flags & ~REF_HAVE_OLD,