From: Karthik Nayak <karthik.188@gmail.com>
To: git@vger.kernel.org
Cc: ps@pks.im, toon@iotcl.com, Karthik Nayak <karthik.188@gmail.com>
Subject: [PATCH v4 0/9] refs: move some of the generic logic out of the backends
Date: Mon, 04 May 2026 19:44:04 +0200 [thread overview]
Message-ID: <20260504-refs-move-to-generic-layer-v4-0-936ac2f0b1a3@gmail.com> (raw)
In-Reply-To: <20260420-refs-move-to-generic-layer-v1-0-513e354f376b@gmail.com>
This series came together while I was working on other reference related
code and realized that some of the individual logic implemented with the
reference backends can be moved to the generic layer.
Moving code to the generic layer, simplifies the responsibility of
individual backends and avoids deviation in logic between the backends.
The biggest changes are related to moving out usage of `parse_object()`
and `peel_object()` from reference transactions. The former is used to
validate that the OID provided points to a commit object. The latter is
an optimization technique where the packed/reftable backend store the
peeled OID whenever available, so reading such references provides the
peeled OID without having to call the ODB.
Moving object parsing to the generic layout involves moving it out of
the prepare stage of the transaction and into `ref_transaction_update()`
where every added update is checked. As such, this also involves
modifying update-ref(1) and receive-pack(1) to follow this paradigm.
---
Changes in v4:
- Fix a bug in the error handling code, move it to a function and also
add missing call sites. Add tests to ensure we catch this.
- Link to v3: https://patch.msgid.link/20260427-refs-move-to-generic-layer-v3-0-e4638dfb7897@gmail.com
Changes in v3:
- Remove an unwanted change which creeped up during a rebase.
- Add information in the commit message around how the order of errors
in git-update-ref(1) will change while maintaining functionality.
- Change up the order of an `if..else` to make it clearer.
- Other small typos and fixes.
- Link to v2: https://patch.msgid.link/20260423-refs-move-to-generic-layer-v2-0-ae5a4f146d7d@gmail.com
Changes in v2:
- Split the second commit into two: one introducing
`ref_store_init_options` and the second to use it for reflog config.
- Use opts as the variable name consistently.
- A bunch of grammar fixes.
- Link to v1: https://patch.msgid.link/20260420-refs-move-to-generic-layer-v1-0-513e354f376b@gmail.com
---
builtin/receive-pack.c | 22 +++---
builtin/update-ref.c | 182 +++++++++++++++++++++++++++++++-----------------
refs.c | 60 ++++++++++++----
refs.h | 16 ++---
refs/files-backend.c | 58 ++++++---------
refs/packed-backend.c | 10 ++-
refs/packed-backend.h | 3 +-
refs/refs-internal.h | 35 ++++++++--
refs/reftable-backend.c | 40 +++--------
t/t1400-update-ref.sh | 14 ++++
10 files changed, 266 insertions(+), 174 deletions(-)
Karthik Nayak (9):
refs: remove unused typedef 'ref_transaction_commit_fn'
refs: introduce `ref_store_init_options`
refs: extract out reflog config to generic layer
refs: return `ref_transaction_error` from `ref_transaction_update()`
update-ref: move `print_rejected_refs()` up
update-ref: handle rejections while adding updates
refs: move object parsing to the generic layer
refs: add peeled object ID to the `ref_update` struct
refs: use peeled tag values in reference backends
Range-diff versus v3:
1: b89e273583 = 1: cf5fa53f34 refs: remove unused typedef 'ref_transaction_commit_fn'
2: 8f7f8dadc8 = 2: afd10fd7a3 refs: introduce `ref_store_init_options`
3: 9cdc35f5b6 = 3: 4eb6950cdc refs: extract out reflog config to generic layer
4: 36c0c86a31 = 4: dd8177a243 refs: return `ref_transaction_error` from `ref_transaction_update()`
5: 1b7eca9353 = 5: eb86178ae5 update-ref: move `print_rejected_refs()` up
6: aef1529054 ! 6: 464c6371a4 update-ref: handle rejections while adding updates
@@ builtin/update-ref.c: static unsigned int default_flags;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
* and append the result to arg. Return a pointer to the terminator.
+@@ builtin/update-ref.c: static void print_rejected_refs(const char *refname,
+ strbuf_release(&sb);
+ }
+
++/*
++ * Handle transaction errors. If we're using batches updates, we want to only
++ * die for generic errors and print the remaining to the user.
++ */
++static void handle_ref_transaction_error(const char *refname,
++ struct object_id *new_oid,
++ struct object_id *old_oid,
++ const char *new_target,
++ const char *old_target,
++ enum ref_transaction_error tx_err,
++ struct strbuf *err,
++ struct command_options *opts)
++{
++ if (!tx_err)
++ return;
++
++ if (tx_err != REF_TRANSACTION_ERROR_GENERIC && opts->allow_update_failures) {
++ print_rejected_refs(refname, old_oid, new_oid, old_target,
++ new_target, tx_err, err->buf, NULL);
++ return;
++ }
++
++ die("%s", err->buf);
++}
++
+ /*
+ * The following five parse_cmd_*() functions parse the corresponding
+ * command. In each case, next points at the character following the
@@ builtin/update-ref.c: static void print_rejected_refs(const char *refname,
*/
@@ builtin/update-ref.c: static void parse_cmd_update(struct ref_transaction *trans
- NULL, NULL,
- update_flags | create_reflog_flag,
- msg, &err))
+- die("%s", err.buf);
+ tx_err = ref_transaction_update(transaction, refname,
+ &new_oid, have_old ? &old_oid : NULL,
+ NULL, NULL,
+ update_flags | create_reflog_flag,
+ msg, &err);
++ handle_ref_transaction_error(refname, &new_oid, have_old ? &old_oid : NULL,
++ NULL, NULL, tx_err, &err, opts);
+
-+ /*
-+ * Generic errors are non-recoverable, so we cannot skip the update
-+ * or mark it as rejected.
-+ */
-+ if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
- die("%s", err.buf);
-+ if (tx_err && opts->allow_update_failures)
-+ print_rejected_refs(refname, have_old ? &old_oid : NULL,
-+ &new_oid, NULL, NULL, tx_err, err.buf,
-+ NULL);
-+
update_flags = default_flags;
free(refname);
- strbuf_release(&err);
+@@ builtin/update-ref.c: static void parse_cmd_update(struct ref_transaction *transaction,
}
static void parse_cmd_symref_update(struct ref_transaction *transaction,
@@ builtin/update-ref.c: static void parse_cmd_symref_update(struct ref_transaction
- have_old_oid ? NULL : old_target,
- update_flags | create_reflog_flag,
- msg, &err))
+- die("%s", err.buf);
+ tx_err = ref_transaction_update(transaction, refname, NULL,
+ have_old_oid ? &old_oid : NULL,
+ new_target,
+ have_old_oid ? NULL : old_target,
+ update_flags | create_reflog_flag,
+ msg, &err);
-+
-+ /*
-+ * Generic errors are non-recoverable, so we cannot skip the update
-+ * or mark it as rejected.
-+ */
-+ if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
- die("%s", err.buf);
++ handle_ref_transaction_error(refname, NULL, have_old_oid ? &old_oid : NULL,
++ new_target, have_old_oid ? NULL : old_target,
++ tx_err, &err, opts);
-+ if (tx_err && opts->allow_update_failures)
-+ print_rejected_refs(refname, have_old_oid ? &old_oid : NULL,
-+ NULL, have_old_oid ? NULL : old_target,
-+ new_target, tx_err, err.buf, NULL);
-+
update_flags = default_flags;
free(refname);
- free(old_arg);
@@ builtin/update-ref.c: static void parse_cmd_symref_update(struct ref_transaction *transaction,
}
static void parse_cmd_create(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end,
-+ struct command_options *opts UNUSED)
++ struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname;
+ struct object_id new_oid;
++ enum ref_transaction_error tx_err;
+
+ refname = parse_refname(&next);
+ if (!refname)
@@ builtin/update-ref.c: static void parse_cmd_create(struct ref_transaction *transaction,
+ if (*next != line_termination)
+ die("create %s: extra input: %s", refname, next);
+
+- if (ref_transaction_create(transaction, refname, &new_oid, NULL,
+- update_flags | create_reflog_flag,
+- msg, &err))
+- die("%s", err.buf);
++ tx_err = ref_transaction_create(transaction, refname, &new_oid, NULL,
++ update_flags | create_reflog_flag,
++ msg, &err);
++ handle_ref_transaction_error(refname, &new_oid, NULL, NULL, NULL, tx_err,
++ &err, opts);
+
+ update_flags = default_flags;
+ free(refname);
strbuf_release(&err);
}
@@ builtin/update-ref.c: static void parse_cmd_create(struct ref_transaction *trans
static void parse_cmd_symref_create(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
-+ struct command_options *opts UNUSED)
++ struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname, *new_target;
++ enum ref_transaction_error tx_err;
+
+ refname = parse_refname(&next);
+ if (!refname)
+@@ builtin/update-ref.c: static void parse_cmd_symref_create(struct ref_transaction *transaction,
+ if (*next != line_termination)
+ die("symref-create %s: extra input: %s", refname, next);
+
+- if (ref_transaction_create(transaction, refname, NULL, new_target,
+- update_flags | create_reflog_flag,
+- msg, &err))
+- die("%s", err.buf);
++ tx_err = ref_transaction_create(transaction, refname, NULL, new_target,
++ update_flags | create_reflog_flag,
++ msg, &err);
++ handle_ref_transaction_error(refname, NULL, NULL, new_target, NULL,
++ tx_err, &err, opts);
+
+ update_flags = default_flags;
+ free(refname);
@@ builtin/update-ref.c: static void parse_cmd_symref_create(struct ref_transaction *transaction,
}
7: 4b225f4f4d ! 7: f971226d89 refs: move object parsing to the generic layer
@@ refs/reftable-backend.c: static enum ref_transaction_error prepare_single_update
/*
* When we update the reference that HEAD points to we enqueue
* a second log-only update for HEAD so that its reflog is
+
+ ## t/t1400-update-ref.sh ##
+@@ t/t1400-update-ref.sh: test_expect_success 'stdin -z create ref fails with empty new value' '
+ test_must_fail git rev-parse --verify -q $c
+ '
+
++test_expect_success 'stdin -z create ref fails with non commit object' '
++ printf $F "create $c" "$(test_oid 001)" >stdin &&
++ test_must_fail git update-ref -z --stdin <stdin 2>err &&
++ grep "fatal: trying to write ref ${SQ}$c${SQ} with nonexistent object" err &&
++ test_must_fail git rev-parse --verify -q $c
++'
++
++test_expect_success 'stdin -z update ref fails with non commit object' '
++ printf $F "update $b" "$(test_oid 001)" "" >stdin &&
++ test_must_fail git update-ref -z --stdin <stdin 2>err &&
++ grep "fatal: trying to write ref ${SQ}$b${SQ} with nonexistent object" err &&
++ test_must_fail git rev-parse --verify -q $c
++'
++
+ test_expect_success 'stdin -z update ref works with right old value' '
+ printf $F "update $b" "$m~1" "$m" >stdin &&
+ git update-ref -z --stdin <stdin &&
8: 944af6a454 = 8: d271167077 refs: add peeled object ID to the `ref_update` struct
9: 47653fdde9 = 9: ed88b9e2ce refs: use peeled tag values in reference backends
base-commit: f65aba1e87db64413b6d1ed5ae5a45b5a84a0997
change-id: 20260417-refs-move-to-generic-layer-f7525c5e8764
Thanks
- Karthik
next prev parent reply other threads:[~2026-05-04 17:44 UTC|newest]
Thread overview: 68+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-20 10:11 [PATCH 0/8] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-20 10:11 ` [PATCH 1/8] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 12:20 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 2/8] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:13 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 3/8] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:14 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 4/8] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:16 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 5/8] update-ref: handle rejections while adding updates Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 14:13 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 6/8] refs: move object parsing to the generic layer Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 15:03 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 7/8] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-20 10:12 ` [PATCH 8/8] refs: use peeled tag values in reference backends Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 0/9] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-24 9:34 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-23 8:40 ` [PATCH v2 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-24 11:01 ` Toon Claes
2026-04-23 8:40 ` [PATCH v2 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 6/9] update-ref: handle rejections while adding updates Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-24 9:35 ` Karthik Nayak
2026-04-24 11:22 ` Toon Claes
2026-04-27 8:47 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-04-24 12:06 ` Toon Claes
2026-04-27 9:32 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-24 16:44 ` Toon Claes
2026-04-27 9:33 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 9/9] refs: use peeled tag values in reference backends Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 0/9] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 6/9] update-ref: handle rejections while adding updates Karthik Nayak
2026-04-29 12:24 ` Toon Claes
2026-04-30 9:52 ` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 9/9] refs: use peeled tag values in reference backends Karthik Nayak
2026-05-04 17:44 ` Karthik Nayak [this message]
2026-05-04 17:44 ` [PATCH v4 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 6/9] update-ref: handle rejections while adding updates Karthik Nayak
2026-05-05 5:52 ` Patrick Steinhardt
2026-05-05 8:23 ` Karthik Nayak
2026-05-06 19:44 ` Toon Claes
2026-05-04 17:44 ` [PATCH v4 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 9/9] refs: use peeled tag values in reference backends Karthik Nayak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260504-refs-move-to-generic-layer-v4-0-936ac2f0b1a3@gmail.com \
--to=karthik.188@gmail.com \
--cc=git@vger.kernel.org \
--cc=ps@pks.im \
--cc=toon@iotcl.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox