From 58799e2e3c15b8fdc755f27dd28af8ef1f199454 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 27 Feb 2024 13:44:31 +0100 Subject: [PATCH 1/3] files-backend: extract out `create_symref_lock` The function `create_symref_locked` creates a symref by creating a '.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)`. Signed-off-by: Karthik Nayak --- refs/files-backend.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index a098d14ea00..3f0f9521cb8 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1920,26 +1920,39 @@ static void update_symref_reflog(struct files_ref_store *refs, } } -static int create_symref_locked(struct files_ref_store *refs, - struct ref_lock *lock, const char *refname, - 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) { + if (!fdopen_lock_file(&lock->lk, "w")) + 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); + return 0; +} + +static int create_and_commit_symref(struct files_ref_store *refs, + struct ref_lock *lock, const char *refname, + const char *target, const char *logmsg) +{ + int ret; + if (prefer_symlink_refs && !create_ref_symlink(lock, target)) { update_symref_reflog(refs, lock, refname, target, logmsg); return 0; } - 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); + if (!ret) { + update_symref_reflog(refs, lock, refname, target, logmsg); - 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)); + } - /* no error check; commit_ref will check ferror */ - fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target); - if (commit_ref(lock) < 0) - return error("unable to write symref for %s: %s", refname, - strerror(errno)); return 0; } @@ -1960,7 +1973,8 @@ static int files_create_symref(struct ref_store *ref_store, return -1; } - ret = create_symref_locked(refs, lock, refname, target, logmsg); + ret = create_and_commit_symref(refs, lock, refname, target, logmsg); + unlock_ref(lock); return ret; } -- GitLab From 85cb51b73e767ecbf0127b6a7307102e54cff7f1 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 27 Feb 2024 13:47:15 +0100 Subject: [PATCH 2/3] refs: accept symref in `ref_transaction_add_update` 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 --- 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 6719a181bd1..dd64f506694 100644 --- a/branch.c +++ b/branch.c @@ -623,7 +623,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 92eda20683c..525e7a4872e 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1644,7 +1644,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); @@ -1684,8 +1684,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 0a7a1a34765..0e7033cba9f 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 db656074857..19f5db2650b 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1592,7 +1592,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 da59600ad22..b3281758d09 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 19a7e06bf41..8de32535de7 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 61338a01ecf..3807cf4106d 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 55d2e0b2cb9..69b89a1aa29 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 298caf6c618..073653d1a47 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 3f0f9521cb8..4dbe73c106d 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 56641aa57a1..3fccf784d49 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 eaa82967ee9..6208457273b 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -840,7 +840,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); } @@ -919,7 +919,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 f49a871ac06..bfe1d24bb03 100644 --- a/sequencer.c +++ b/sequencer.c @@ -602,7 +602,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); @@ -1218,7 +1218,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; } @@ -3736,7 +3736,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 65002a7220a..33eef703cec 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; } -- GitLab From 7087b84a83ad833e4522bec1a65f5cd4c6e5a35a Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 27 Feb 2024 13:48:18 +0100 Subject: [PATCH 3/3] refs: add 'update-symref' command to 'update-ref' 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. To allow users to update the HEAD ref in a transaction, we introduce '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. Signed-off-by: Karthik Nayak --- builtin/update-ref.c | 61 ++++++++++++++++++++++++++++++++++++------- refs.c | 9 +++++++ refs/files-backend.c | 12 +++++++-- t/t1400-update-ref.sh | 61 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 11 deletions(-) diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 3807cf4106d..d5c08306c03 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -213,6 +213,48 @@ static void parse_cmd_update(struct ref_transaction *transaction, strbuf_release(&err); } +static void parse_cmd_update_symref(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + char *refname, *symref; + + refname = parse_refname(&next); + if (!refname) + die("update-symbolic: missing "); + + if (line_termination) { + /* Without -z, consume SP and use next argument */ + if (!*next || *next == line_termination) + die("update-symbolic %s: missing ", refname); + if (*next != ' ') + die("update-symbolic %s: expected SP but got: %s", + refname, next); + } else { + /* With -z, read the next NUL-terminated line */ + if (*next) + die("update-symbolic %s: missing ", refname); + } + next++; + + symref = parse_refname(&next); + if (!symref) + die("update-symbolic %s: missing ", refname); + + if (*next != line_termination) + die("update-symbolic %s: extra input: %s", refname, next); + + if (ref_transaction_update(transaction, refname, NULL, NULL, + update_flags | create_reflog_flag | REF_UPDATE_SYMREF, + msg, symref, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(symref); + free(refname); + strbuf_release(&err); +} + static void parse_cmd_create(struct ref_transaction *transaction, const char *next, const char *end) { @@ -379,15 +421,16 @@ static const struct parse_cmd { unsigned args; enum update_refs_state state; } command[] = { - { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, - { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, - { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, - { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, - { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, - { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, - { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, - { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, - { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, + { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, + { "update-symref", parse_cmd_update_symref, 3, UPDATE_REFS_OPEN }, + { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, + { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, + { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, + { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, + { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, + { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, + { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, }; static void update_refs_stdin(void) diff --git a/refs.c b/refs.c index 69b89a1aa29..a049809dc7a 100644 --- a/refs.c +++ b/refs.c @@ -29,6 +29,7 @@ #include "date.h" #include "commit.h" #include "wildmatch.h" +#include "wrapper.h" /* * List of all available backends @@ -1216,6 +1217,7 @@ void ref_transaction_free(struct ref_transaction *transaction) } for (i = 0; i < transaction->nr; i++) { + free(transaction->updates[i]->symref_target); free(transaction->updates[i]->msg); free(transaction->updates[i]); } @@ -1245,6 +1247,9 @@ struct ref_update *ref_transaction_add_update( oidcpy(&update->new_oid, new_oid); if (flags & REF_HAVE_OLD) oidcpy(&update->old_oid, old_oid); + if (flags & REF_UPDATE_SYMREF) + update->symref_target = xstrdup(symref); + update->msg = normalize_reflog_message(msg); return update; } @@ -2337,6 +2342,10 @@ static int run_transaction_hook(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + // Reference transaction does not support symbolic updates. + if (update->flags & REF_UPDATE_SYMREF) + continue; + strbuf_reset(&buf); strbuf_addf(&buf, "%s %s %s\n", oid_to_hex(&update->old_oid), diff --git a/refs/files-backend.c b/refs/files-backend.c index 4dbe73c106d..c2d9e4409e1 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -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, NULL); + update->msg, update->symref_target); /* * 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, NULL); + update->msg, update->symref_target); new_update->parent_update = update; @@ -2397,6 +2397,7 @@ static int split_symref_update(struct ref_update *update, */ update->flags |= REF_LOG_ONLY | REF_NO_DEREF; update->flags &= ~REF_HAVE_OLD; + update->flags &= ~REF_UPDATE_SYMREF; /* * Add the referent. This insertion is O(N) in the transaction @@ -2567,6 +2568,9 @@ static int lock_ref_for_update(struct files_ref_store *refs, } } + if (update->flags & REF_UPDATE_SYMREF) + ret = create_symref_lock(refs, lock, update->refname, update->symref_target); + if ((update->flags & REF_HAVE_NEW) && !(update->flags & REF_DELETING) && !(update->flags & REF_LOG_ONLY)) { @@ -2597,6 +2601,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, update->flags |= REF_NEEDS_COMMIT; } } + if (!(update->flags & REF_NEEDS_COMMIT)) { /* * We didn't call write_ref_to_lockfile(), so @@ -2609,6 +2614,9 @@ static int lock_ref_for_update(struct files_ref_store *refs, ret = TRANSACTION_GENERIC_ERROR; goto out; } + + if (update->flags & REF_UPDATE_SYMREF) + update->flags |= REF_NEEDS_COMMIT; } out: diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 6ebc3ef9453..5366a178c5a 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -868,6 +868,39 @@ test_expect_success 'stdin delete symref works flag --no-deref' ' test_cmp expect actual ' +test_expect_success 'stdin update-symref works' ' + git update-ref TESTSYMREFONE $a && + git symbolic-ref TESTSYMREFTWO TESTSYMREFONE && + cat >stdin <<-EOF && + update-symref TESTSYMREFTWO $b + EOF + git update-ref --stdin expect && + git symbolic-ref --no-recurse TESTSYMREFTWO >actual && + test_cmp expect actual && + git rev-parse TESTSYMREFONE >actual && + git rev-parse $b >expect && + test_cmp expect actual && + git symbolic-ref TESTSYMREFONE >actual && + echo $b >expect && + test_cmp expect actual +' + +test_expect_success 'stdin update-symref works flag --no-deref' ' + git update-ref TESTREFONE $a && + git symbolic-ref TESTSYMREFTWO TESTSYMREFONE && + cat >stdin <<-EOF && + update-symref TESTSYMREFTWO $b + EOF + git update-ref --no-deref --stdin expect && + git rev-parse TESTREFONE >actual && + test_cmp expect actual && + echo $b >expect && + git symbolic-ref --no-recurse TESTSYMREFTWO >actual && + test_cmp expect actual +' + test_expect_success 'stdin delete ref works with right old value' ' echo "delete $b $m~1" >stdin && git update-ref --stdin stdin <<-EOF && + start + update-symref TESTSYMREFONE $b + prepare + commit + EOF + git update-ref --no-deref --stdin expect && + git symbolic-ref TESTSYMREFONE >actual && + test_cmp expect actual +' + +test_expect_success 'transaction can abort symref update' ' + git symbolic-ref TESTSYMREFONE $a && + cat >stdin <<-EOF && + start + update-symref TESTSYMREFONE $b + prepare + abort + EOF + git update-ref --no-deref --stdin expect && + git symbolic-ref TESTSYMREFONE >actual && + test_cmp expect actual +' + test_done -- GitLab