refs: introduce enum-based transaction error types

Replace preprocessor-defined transaction errors with a strongly-typed enum `ref_transaction_error`. This change: - Improves type safety and function signature clarity. - Makes error handling more explicit and discoverable. - Maintains existing error cases, while adding new error cases for common scenarios. This refactoring paves the way for more comprehensive error handling which we will utilize in the upcoming commits to add batch reference update support. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Apr 8, 2025 at 10:51 UTC 76e760b99923cb9afb52ef08607f736ff3eeaad7
7 files changed +207 -186
builtin/fetch.c
+1 -1
@@ -687,7 +687,7 @@ static int s_update_ref(const char *action,
687 switch (ref_transaction_commit(our_transaction, &err)) {
688 case 0:
689 break;
690 - case TRANSACTION_NAME_CONFLICT:
690 + case REF_TRANSACTION_ERROR_NAME_CONFLICT:
691 ret = STORE_REF_ERROR_DF_CONFLICT;
692 goto out;
693 default:
refs.c
+26 -23
@@ -2271,7 +2271,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
2271 REF_NO_DEREF, logmsg, &err))
2272 goto error_return;
2273 prepret = ref_transaction_prepare(transaction, &err);
2274 - if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
2274 + if (prepret && prepret != REF_TRANSACTION_ERROR_CREATE_EXISTS)
2275 goto error_return;
2276 } else {
2277 if (ref_transaction_update(transaction, ref, NULL, NULL,
@@ -2289,7 +2289,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
2289 }
2290 }
2291
2292 - if (prepret == TRANSACTION_CREATE_EXISTS)
2292 + if (prepret == REF_TRANSACTION_ERROR_CREATE_EXISTS)
2293 goto cleanup;
2294
2295 if (ref_transaction_commit(transaction, &err))
@@ -2425,7 +2425,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
2425
2426 string_list_sort(&transaction->refnames);
2427 if (ref_update_reject_duplicates(&transaction->refnames, err))
2428 - return TRANSACTION_GENERIC_ERROR;
2428 + return REF_TRANSACTION_ERROR_GENERIC;
2429
2430 ret = refs->be->transaction_prepare(refs, transaction, err);
2431 if (ret)
@@ -2497,19 +2497,19 @@ int ref_transaction_commit(struct ref_transaction *transaction,
2497 return ret;
2498 }
2499
2500 -int refs_verify_refnames_available(struct ref_store *refs,
2501 - const struct string_list *refnames,
2502 - const struct string_list *extras,
2503 - const struct string_list *skip,
2504 - unsigned int initial_transaction,
2505 - struct strbuf *err)
2500 +enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs,
2501 + const struct string_list *refnames,
2502 + const struct string_list *extras,
2503 + const struct string_list *skip,
2504 + unsigned int initial_transaction,
2505 + struct strbuf *err)
2506 {
2507 struct strbuf dirname = STRBUF_INIT;
2508 struct strbuf referent = STRBUF_INIT;
2509 struct string_list_item *item;
2510 struct ref_iterator *iter = NULL;
2511 struct strset dirnames;
2512 - int ret = -1;
2512 + int ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
2513
2514 /*
2515 * For the sake of comments in this function, suppose that
@@ -2625,12 +2625,13 @@ cleanup:
2625 return ret;
2626 }
2627
2628 -int refs_verify_refname_available(struct ref_store *refs,
2629 - const char *refname,
2630 - const struct string_list *extras,
2631 - const struct string_list *skip,
2632 - unsigned int initial_transaction,
2633 - struct strbuf *err)
2628 +enum ref_transaction_error refs_verify_refname_available(
2629 + struct ref_store *refs,
2630 + const char *refname,
2631 + const struct string_list *extras,
2632 + const struct string_list *skip,
2633 + unsigned int initial_transaction,
2634 + struct strbuf *err)
2635 {
2636 struct string_list_item item = { .string = (char *) refname };
2637 struct string_list refnames = {
@@ -2818,8 +2819,9 @@ int ref_update_has_null_new_value(struct ref_update *update)
2819 return !update->new_target && is_null_oid(&update->new_oid);
2820 }
2821
2821 -int ref_update_check_old_target(const char *referent, struct ref_update *update,
2822 - struct strbuf *err)
2822 +enum ref_transaction_error ref_update_check_old_target(const char *referent,
2823 + struct ref_update *update,
2824 + struct strbuf *err)
2825 {
2826 if (!update->old_target)
2827 BUG("called without old_target set");
@@ -2827,17 +2829,18 @@ int ref_update_check_old_target(const char *referent, struct ref_update *update,
2829 if (!strcmp(referent, update->old_target))
2830 return 0;
2831
2830 - if (!strcmp(referent, ""))
2832 + if (!strcmp(referent, "")) {
2833 strbuf_addf(err, "verifying symref target: '%s': "
2834 "reference is missing but expected %s",
2835 ref_update_original_update_refname(update),
2836 update->old_target);
2835 - else
2836 - strbuf_addf(err, "verifying symref target: '%s': "
2837 - "is at %s but expected %s",
2837 + return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
2838 + }
2839 +
2840 + strbuf_addf(err, "verifying symref target: '%s': is at %s but expected %s",
2841 ref_update_original_update_refname(update),
2842 referent, update->old_target);
2840 - return -1;
2843 + return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
2844 }
2845
2846 struct migration_data {
refs.h
+29 -19
@@ -16,6 +16,23 @@ struct worktree;
16 enum ref_storage_format ref_storage_format_by_name(const char *name);
17 const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
18
19 +enum ref_transaction_error {
20 + /* Default error code */
21 + REF_TRANSACTION_ERROR_GENERIC = -1,
22 + /* Ref name conflict like A vs A/B */
23 + REF_TRANSACTION_ERROR_NAME_CONFLICT = -2,
24 + /* Ref to be created already exists */
25 + REF_TRANSACTION_ERROR_CREATE_EXISTS = -3,
26 + /* ref expected but doesn't exist */
27 + REF_TRANSACTION_ERROR_NONEXISTENT_REF = -4,
28 + /* Provided old_oid or old_target of reference doesn't match actual */
29 + REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE = -5,
30 + /* Provided new_oid or new_target is invalid */
31 + REF_TRANSACTION_ERROR_INVALID_NEW_VALUE = -6,
32 + /* Expected ref to be symref, but is a regular ref */
33 + REF_TRANSACTION_ERROR_EXPECTED_SYMREF = -7,
34 +};
35 +
36 /*
37 * Resolve a reference, recursively following symbolic references.
38 *
@@ -117,24 +134,24 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
134 *
135 * extras and skip must be sorted.
136 */
120 -int refs_verify_refname_available(struct ref_store *refs,
121 - const char *refname,
122 - const struct string_list *extras,
123 - const struct string_list *skip,
124 - unsigned int initial_transaction,
125 - struct strbuf *err);
137 +enum ref_transaction_error refs_verify_refname_available(struct ref_store *refs,
138 + const char *refname,
139 + const struct string_list *extras,
140 + const struct string_list *skip,
141 + unsigned int initial_transaction,
142 + struct strbuf *err);
143
144 /*
145 * Same as `refs_verify_refname_available()`, but checking for a list of
146 * refnames instead of only a single item. This is more efficient in the case
147 * where one needs to check multiple refnames.
148 */
132 -int refs_verify_refnames_available(struct ref_store *refs,
133 - const struct string_list *refnames,
134 - const struct string_list *extras,
135 - const struct string_list *skip,
136 - unsigned int initial_transaction,
137 - struct strbuf *err);
149 +enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs,
150 + const struct string_list *refnames,
151 + const struct string_list *extras,
152 + const struct string_list *skip,
153 + unsigned int initial_transaction,
154 + struct strbuf *err);
155
156 int refs_ref_exists(struct ref_store *refs, const char *refname);
157
@@ -830,13 +847,6 @@ int ref_transaction_verify(struct ref_transaction *transaction,
847 unsigned int flags,
848 struct strbuf *err);
849
833 -/* Naming conflict (for example, the ref names A and A/B conflict). */
834 -#define TRANSACTION_NAME_CONFLICT -1
835 -/* When only creation was requested, but the ref already exists. */
836 -#define TRANSACTION_CREATE_EXISTS -2
837 -/* All other errors. */
838 -#define TRANSACTION_GENERIC_ERROR -3
839 -
850 /*
851 * Perform the preparatory stages of committing `transaction`. Acquire
852 * any needed locks, check preconditions, etc.; basically, do as much
refs/files-backend.c
+102 -100
@@ -663,7 +663,7 @@ static void unlock_ref(struct ref_lock *lock)
663 * broken, lock the reference anyway but clear old_oid.
664 *
665 * Return 0 on success. On failure, write an error message to err and
666 - * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR.
666 + * return REF_TRANSACTION_ERROR_NAME_CONFLICT or REF_TRANSACTION_ERROR_GENERIC.
667 *
668 * Implementation note: This function is basically
669 *
@@ -676,19 +676,20 @@ static void unlock_ref(struct ref_lock *lock)
676 * avoided, namely if we were successfully able to read the ref
677 * - Generate informative error messages in the case of failure
678 */
679 -static int lock_raw_ref(struct files_ref_store *refs,
680 - const char *refname, int mustexist,
681 - struct string_list *refnames_to_check,
682 - const struct string_list *extras,
683 - struct ref_lock **lock_p,
684 - struct strbuf *referent,
685 - unsigned int *type,
686 - struct strbuf *err)
687 -{
679 +static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
680 + const char *refname,
681 + int mustexist,
682 + struct string_list *refnames_to_check,
683 + const struct string_list *extras,
684 + struct ref_lock **lock_p,
685 + struct strbuf *referent,
686 + unsigned int *type,
687 + struct strbuf *err)
688 +{
689 + enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
690 struct ref_lock *lock;
691 struct strbuf ref_file = STRBUF_INIT;
692 int attempts_remaining = 3;
691 - int ret = TRANSACTION_GENERIC_ERROR;
693 int failure_errno;
694
695 assert(err);
@@ -728,13 +729,14 @@ retry:
729 strbuf_reset(err);
730 strbuf_addf(err, "unable to resolve reference '%s'",
731 refname);
732 + ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
733 } else {
734 /*
735 * The error message set by
736 * refs_verify_refname_available() is
737 * OK.
738 */
737 - ret = TRANSACTION_NAME_CONFLICT;
739 + ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
740 }
741 } else {
742 /*
@@ -788,6 +790,7 @@ retry:
790 /* Garden variety missing reference. */
791 strbuf_addf(err, "unable to resolve reference '%s'",
792 refname);
793 + ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
794 goto error_return;
795 } else {
796 /*
@@ -820,6 +823,7 @@ retry:
823 /* Garden variety missing reference. */
824 strbuf_addf(err, "unable to resolve reference '%s'",
825 refname);
826 + ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
827 goto error_return;
828 } else if (remove_dir_recursively(&ref_file,
829 REMOVE_DIR_EMPTY_ONLY)) {
@@ -830,7 +834,7 @@ retry:
834 * The error message set by
835 * verify_refname_available() is OK.
836 */
833 - ret = TRANSACTION_NAME_CONFLICT;
837 + ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
838 goto error_return;
839 } else {
840 /*
@@ -1517,10 +1521,11 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
1521 return ret;
1522 }
1523
1520 -static int write_ref_to_lockfile(struct files_ref_store *refs,
1521 - struct ref_lock *lock,
1522 - const struct object_id *oid,
1523 - int skip_oid_verification, struct strbuf *err);
1524 +static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
1525 + struct ref_lock *lock,
1526 + const struct object_id *oid,
1527 + int skip_oid_verification,
1528 + struct strbuf *err);
1529 static int commit_ref_update(struct files_ref_store *refs,
1530 struct ref_lock *lock,
1531 const struct object_id *oid, const char *logmsg,
@@ -1926,10 +1931,11 @@ static int files_log_ref_write(struct files_ref_store *refs,
1931 * Write oid into the open lockfile, then close the lockfile. On
1932 * errors, rollback the lockfile, fill in *err and return -1.
1933 */
1929 -static int write_ref_to_lockfile(struct files_ref_store *refs,
1930 - struct ref_lock *lock,
1931 - const struct object_id *oid,
1932 - int skip_oid_verification, struct strbuf *err)
1934 +static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
1935 + struct ref_lock *lock,
1936 + const struct object_id *oid,
1937 + int skip_oid_verification,
1938 + struct strbuf *err)
1939 {
1940 static char term = '\n';
1941 struct object *o;
@@ -1943,7 +1949,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
1949 "trying to write ref '%s' with nonexistent object %s",
1950 lock->ref_name, oid_to_hex(oid));
1951 unlock_ref(lock);
1946 - return -1;
1952 + return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
1953 }
1954 if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
1955 strbuf_addf(
@@ -1951,7 +1957,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
1957 "trying to write non-commit object %s to branch '%s'",
1958 oid_to_hex(oid), lock->ref_name);
1959 unlock_ref(lock);
1954 - return -1;
1960 + return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
1961 }
1962 }
1963 fd = get_lock_file_fd(&lock->lk);
@@ -1962,7 +1968,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
1968 strbuf_addf(err,
1969 "couldn't write '%s'", get_lock_file_path(&lock->lk));
1970 unlock_ref(lock);
1965 - return -1;
1971 + return REF_TRANSACTION_ERROR_GENERIC;
1972 }
1973 return 0;
1974 }
@@ -2376,9 +2382,10 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
2382 * If update is a direct update of head_ref (the reference pointed to
2383 * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
2384 */
2379 -static int split_head_update(struct ref_update *update,
2380 - struct ref_transaction *transaction,
2381 - const char *head_ref, struct strbuf *err)
2385 +static enum ref_transaction_error split_head_update(struct ref_update *update,
2386 + struct ref_transaction *transaction,
2387 + const char *head_ref,
2388 + struct strbuf *err)
2389 {
2390 struct ref_update *new_update;
2391
@@ -2402,7 +2409,7 @@ static int split_head_update(struct ref_update *update,
2409 "multiple updates for 'HEAD' (including one "
2410 "via its referent '%s') are not allowed",
2411 update->refname);
2405 - return TRANSACTION_NAME_CONFLICT;
2412 + return REF_TRANSACTION_ERROR_NAME_CONFLICT;
2413 }
2414
2415 new_update = ref_transaction_add_update(
@@ -2430,10 +2437,10 @@ static int split_head_update(struct ref_update *update,
2437 * Note that the new update will itself be subject to splitting when
2438 * the iteration gets to it.
2439 */
2433 -static int split_symref_update(struct ref_update *update,
2434 - const char *referent,
2435 - struct ref_transaction *transaction,
2436 - struct strbuf *err)
2440 +static enum ref_transaction_error split_symref_update(struct ref_update *update,
2441 + const char *referent,
2442 + struct ref_transaction *transaction,
2443 + struct strbuf *err)
2444 {
2445 struct ref_update *new_update;
2446 unsigned int new_flags;
@@ -2450,7 +2457,7 @@ static int split_symref_update(struct ref_update *update,
2457 "multiple updates for '%s' (including one "
2458 "via symref '%s') are not allowed",
2459 referent, update->refname);
2453 - return TRANSACTION_NAME_CONFLICT;
2460 + return REF_TRANSACTION_ERROR_NAME_CONFLICT;
2461 }
2462
2463 new_flags = update->flags;
@@ -2491,11 +2498,10 @@ static int split_symref_update(struct ref_update *update,
2498 * everything is OK, return 0; otherwise, write an error message to
2499 * err and return -1.
2500 */
2494 -static int check_old_oid(struct ref_update *update, struct object_id *oid,
2495 - struct strbuf *err)
2501 +static enum ref_transaction_error check_old_oid(struct ref_update *update,
2502 + struct object_id *oid,
2503 + struct strbuf *err)
2504 {
2497 - int ret = TRANSACTION_GENERIC_ERROR;
2498 -
2505 if (!(update->flags & REF_HAVE_OLD) ||
2506 oideq(oid, &update->old_oid))
2507 return 0;
@@ -2504,21 +2510,20 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
2510 strbuf_addf(err, "cannot lock ref '%s': "
2511 "reference already exists",
2512 ref_update_original_update_refname(update));
2507 - ret = TRANSACTION_CREATE_EXISTS;
2508 - }
2509 - else if (is_null_oid(oid))
2513 + return REF_TRANSACTION_ERROR_CREATE_EXISTS;
2514 + } else if (is_null_oid(oid)) {
2515 strbuf_addf(err, "cannot lock ref '%s': "
2516 "reference is missing but expected %s",
2517 ref_update_original_update_refname(update),
2518 oid_to_hex(&update->old_oid));
2514 - else
2515 - strbuf_addf(err, "cannot lock ref '%s': "
2516 - "is at %s but expected %s",
2517 - ref_update_original_update_refname(update),
2518 - oid_to_hex(oid),
2519 - oid_to_hex(&update->old_oid));
2519 + return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
2520 + }
2521
2521 - return ret;
2522 + strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s",
2523 + ref_update_original_update_refname(update), oid_to_hex(oid),
2524 + oid_to_hex(&update->old_oid));
2525 +
2526 + return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
2527 }
2528
2529 struct files_transaction_backend_data {
@@ -2540,17 +2545,17 @@ struct files_transaction_backend_data {
2545 * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
2546 * update of HEAD.
2547 */
2543 -static int lock_ref_for_update(struct files_ref_store *refs,
2544 - struct ref_update *update,
2545 - struct ref_transaction *transaction,
2546 - const char *head_ref,
2547 - struct string_list *refnames_to_check,
2548 - struct strbuf *err)
2548 +static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *refs,
2549 + struct ref_update *update,
2550 + struct ref_transaction *transaction,
2551 + const char *head_ref,
2552 + struct string_list *refnames_to_check,
2553 + struct strbuf *err)
2554 {
2555 struct strbuf referent = STRBUF_INIT;
2556 int mustexist = ref_update_expects_existing_old_ref(update);
2557 struct files_transaction_backend_data *backend_data;
2553 - int ret = 0;
2558 + enum ref_transaction_error ret = 0;
2559 struct ref_lock *lock;
2560
2561 files_assert_main_repository(refs, "lock_ref_for_update");
@@ -2602,22 +2607,17 @@ static int lock_ref_for_update(struct files_ref_store *refs,
2607 strbuf_addf(err, "cannot lock ref '%s': "
2608 "error reading reference",
2609 ref_update_original_update_refname(update));
2605 - ret = TRANSACTION_GENERIC_ERROR;
2610 + ret = REF_TRANSACTION_ERROR_GENERIC;
2611 goto out;
2612 }
2613 }
2614
2610 - if (update->old_target) {
2611 - if (ref_update_check_old_target(referent.buf, update, err)) {
2612 - ret = TRANSACTION_GENERIC_ERROR;
2613 - goto out;
2614 - }
2615 - } else {
2615 + if (update->old_target)
2616 + ret = ref_update_check_old_target(referent.buf, update, err);
2617 + else
2618 ret = check_old_oid(update, &lock->old_oid, err);
2617 - if (ret) {
2618 - goto out;
2619 - }
2620 - }
2619 + if (ret)
2620 + goto out;
2621 } else {
2622 /*
2623 * Create a new update for the reference this
@@ -2644,7 +2644,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
2644 "but is a regular ref"),
2645 ref_update_original_update_refname(update),
2646 update->old_target);
2647 - ret = TRANSACTION_GENERIC_ERROR;
2647 + ret = REF_TRANSACTION_ERROR_EXPECTED_SYMREF;
2648 goto out;
2649 } else {
2650 ret = check_old_oid(update, &lock->old_oid, err);
@@ -2668,14 +2668,14 @@ static int lock_ref_for_update(struct files_ref_store *refs,
2668
2669 if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
2670 if (create_symref_lock(lock, update->new_target, err)) {
2671 - ret = TRANSACTION_GENERIC_ERROR;
2671 + ret = REF_TRANSACTION_ERROR_GENERIC;
2672 goto out;
2673 }
2674
2675 if (close_ref_gently(lock)) {
2676 strbuf_addf(err, "couldn't close '%s.lock'",
2677 update->refname);
2678 - ret = TRANSACTION_GENERIC_ERROR;
2678 + ret = REF_TRANSACTION_ERROR_GENERIC;
2679 goto out;
2680 }
2681
@@ -2693,25 +2693,27 @@ static int lock_ref_for_update(struct files_ref_store *refs,
2693 * The reference already has the desired
2694 * value, so we don't need to write it.
2695 */
2696 - } else if (write_ref_to_lockfile(
2697 - refs, lock, &update->new_oid,
2698 - update->flags & REF_SKIP_OID_VERIFICATION,
2699 - err)) {
2700 - char *write_err = strbuf_detach(err, NULL);
2701 -
2702 - /*
2703 - * The lock was freed upon failure of
2704 - * write_ref_to_lockfile():
2705 - */
2706 - update->backend_data = NULL;
2707 - strbuf_addf(err,
2708 - "cannot update ref '%s': %s",
2709 - update->refname, write_err);
2710 - free(write_err);
2711 - ret = TRANSACTION_GENERIC_ERROR;
2712 - goto out;
2696 } else {
2714 - update->flags |= REF_NEEDS_COMMIT;
2697 + ret = write_ref_to_lockfile(
2698 + refs, lock, &update->new_oid,
2699 + update->flags & REF_SKIP_OID_VERIFICATION,
2700 + err);
2701 + if (ret) {
2702 + char *write_err = strbuf_detach(err, NULL);
2703 +
2704 + /*
2705 + * The lock was freed upon failure of
2706 + * write_ref_to_lockfile():
2707 + */
2708 + update->backend_data = NULL;
2709 + strbuf_addf(err,
2710 + "cannot update ref '%s': %s",
2711 + update->refname, write_err);
2712 + free(write_err);
2713 + goto out;
2714 + } else {
2715 + update->flags |= REF_NEEDS_COMMIT;
2716 + }
2717 }
2718 }
2719 if (!(update->flags & REF_NEEDS_COMMIT)) {
@@ -2723,7 +2725,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
2725 if (close_ref_gently(lock)) {
2726 strbuf_addf(err, "couldn't close '%s.lock'",
2727 update->refname);
2726 - ret = TRANSACTION_GENERIC_ERROR;
2728 + ret = REF_TRANSACTION_ERROR_GENERIC;
2729 goto out;
2730 }
2731 }
@@ -2865,7 +2867,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
2867 refs->packed_ref_store,
2868 transaction->flags, err);
2869 if (!packed_transaction) {
2868 - ret = TRANSACTION_GENERIC_ERROR;
2870 + ret = REF_TRANSACTION_ERROR_GENERIC;
2871 goto cleanup;
2872 }
2873
@@ -2897,13 +2899,13 @@ static int files_transaction_prepare(struct ref_store *ref_store,
2899 */
2900 if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
2901 &transaction->refnames, NULL, 0, err)) {
2900 - ret = TRANSACTION_NAME_CONFLICT;
2902 + ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
2903 goto cleanup;
2904 }
2905
2906 if (packed_transaction) {
2907 if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
2906 - ret = TRANSACTION_GENERIC_ERROR;
2908 + ret = REF_TRANSACTION_ERROR_GENERIC;
2909 goto cleanup;
2910 }
2911 backend_data->packed_refs_locked = 1;
@@ -2934,7 +2936,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
2936 */
2937 backend_data->packed_transaction = NULL;
2938 if (ref_transaction_abort(packed_transaction, err)) {
2937 - ret = TRANSACTION_GENERIC_ERROR;
2939 + ret = REF_TRANSACTION_ERROR_GENERIC;
2940 goto cleanup;
2941 }
2942 }
@@ -3035,7 +3037,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3037 packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,
3038 transaction->flags, err);
3039 if (!packed_transaction) {
3038 - ret = TRANSACTION_GENERIC_ERROR;
3040 + ret = REF_TRANSACTION_ERROR_GENERIC;
3041 goto cleanup;
3042 }
3043
@@ -3058,7 +3060,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3060 if (!loose_transaction) {
3061 loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
3062 if (!loose_transaction) {
3061 - ret = TRANSACTION_GENERIC_ERROR;
3063 + ret = REF_TRANSACTION_ERROR_GENERIC;
3064 goto cleanup;
3065 }
3066 }
@@ -3083,19 +3085,19 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3085 }
3086
3087 if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
3086 - ret = TRANSACTION_GENERIC_ERROR;
3088 + ret = REF_TRANSACTION_ERROR_GENERIC;
3089 goto cleanup;
3090 }
3091
3092 if (refs_verify_refnames_available(&refs->base, &refnames_to_check,
3093 &affected_refnames, NULL, 1, err)) {
3094 packed_refs_unlock(refs->packed_ref_store);
3093 - ret = TRANSACTION_NAME_CONFLICT;
3095 + ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
3096 goto cleanup;
3097 }
3098
3099 if (ref_transaction_commit(packed_transaction, err)) {
3098 - ret = TRANSACTION_GENERIC_ERROR;
3100 + ret = REF_TRANSACTION_ERROR_GENERIC;
3101 goto cleanup;
3102 }
3103 packed_refs_unlock(refs->packed_ref_store);
@@ -3103,7 +3105,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3105 if (loose_transaction) {
3106 if (ref_transaction_prepare(loose_transaction, err) ||
3107 ref_transaction_commit(loose_transaction, err)) {
3106 - ret = TRANSACTION_GENERIC_ERROR;
3108 + ret = REF_TRANSACTION_ERROR_GENERIC;
3109 goto cleanup;
3110 }
3111 }
@@ -3152,7 +3154,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
3154 if (update->flags & REF_NEEDS_COMMIT ||
3155 update->flags & REF_LOG_ONLY) {
3156 if (parse_and_write_reflog(refs, update, lock, err)) {
3155 - ret = TRANSACTION_GENERIC_ERROR;
3157 + ret = REF_TRANSACTION_ERROR_GENERIC;
3158 goto cleanup;
3159 }
3160 }
@@ -3171,7 +3173,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
3173 strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
3174 unlock_ref(lock);
3175 update->backend_data = NULL;
3174 - ret = TRANSACTION_GENERIC_ERROR;
3176 + ret = REF_TRANSACTION_ERROR_GENERIC;
3177 goto cleanup;
3178 }
3179 }
@@ -3227,7 +3229,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
3229 strbuf_reset(&sb);
3230 files_ref_path(refs, &sb, lock->ref_name);
3231 if (unlink_or_msg(sb.buf, err)) {
3230 - ret = TRANSACTION_GENERIC_ERROR;
3232 + ret = REF_TRANSACTION_ERROR_GENERIC;
3233 goto cleanup;
3234 }
3235 }
refs/packed-backend.c
+14 -9
@@ -1326,10 +1326,11 @@ static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
1326 * The packfile must be locked before calling this function and will
1327 * remain locked when it is done.
1328 */
1329 -static int write_with_updates(struct packed_ref_store *refs,
1330 - struct string_list *updates,
1331 - struct strbuf *err)
1329 +static enum ref_transaction_error write_with_updates(struct packed_ref_store *refs,
1330 + struct string_list *updates,
1331 + struct strbuf *err)
1332 {
1333 + enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
1334 struct ref_iterator *iter = NULL;
1335 size_t i;
1336 int ok;
@@ -1353,7 +1354,7 @@ static int write_with_updates(struct packed_ref_store *refs,
1354 strbuf_addf(err, "unable to create file %s: %s",
1355 sb.buf, strerror(errno));
1356 strbuf_release(&sb);
1356 - return -1;
1357 + return REF_TRANSACTION_ERROR_GENERIC;
1358 }
1359 strbuf_release(&sb);
1360
@@ -1409,6 +1410,7 @@ static int write_with_updates(struct packed_ref_store *refs,
1410 strbuf_addf(err, "cannot update ref '%s': "
1411 "reference already exists",
1412 update->refname);
1413 + ret = REF_TRANSACTION_ERROR_CREATE_EXISTS;
1414 goto error;
1415 } else if (!oideq(&update->old_oid, iter->oid)) {
1416 strbuf_addf(err, "cannot update ref '%s': "
@@ -1416,6 +1418,7 @@ static int write_with_updates(struct packed_ref_store *refs,
1418 update->refname,
1419 oid_to_hex(iter->oid),
1420 oid_to_hex(&update->old_oid));
1421 + ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
1422 goto error;
1423 }
1424 }
@@ -1452,6 +1455,7 @@ static int write_with_updates(struct packed_ref_store *refs,
1455 "reference is missing but expected %s",
1456 update->refname,
1457 oid_to_hex(&update->old_oid));
1458 + ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
1459 goto error;
1460 }
1461 }
@@ -1509,7 +1513,7 @@ static int write_with_updates(struct packed_ref_store *refs,
1513 strerror(errno));
1514 strbuf_release(&sb);
1515 delete_tempfile(&refs->tempfile);
1512 - return -1;
1516 + return REF_TRANSACTION_ERROR_GENERIC;
1517 }
1518
1519 return 0;
@@ -1521,7 +1525,7 @@ write_error:
1525 error:
1526 ref_iterator_free(iter);
1527 delete_tempfile(&refs->tempfile);
1524 - return -1;
1528 + return ret;
1529 }
1530
1531 int is_packed_transaction_needed(struct ref_store *ref_store,
@@ -1654,7 +1658,7 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
1658 REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
1659 "ref_transaction_prepare");
1660 struct packed_transaction_backend_data *data;
1657 - int ret = TRANSACTION_GENERIC_ERROR;
1661 + enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
1662
1663 /*
1664 * Note that we *don't* skip transactions with zero updates,
@@ -1675,7 +1679,8 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
1679 data->own_lock = 1;
1680 }
1681
1678 - if (write_with_updates(refs, &transaction->refnames, err))
1682 + ret = write_with_updates(refs, &transaction->refnames, err);
1683 + if (ret)
1684 goto failure;
1685
1686 transaction->state = REF_TRANSACTION_PREPARED;
@@ -1707,7 +1712,7 @@ static int packed_transaction_finish(struct ref_store *ref_store,
1712 ref_store,
1713 REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
1714 "ref_transaction_finish");
1710 - int ret = TRANSACTION_GENERIC_ERROR;
1715 + int ret = REF_TRANSACTION_ERROR_GENERIC;
1716 char *packed_refs_path;
1717
1718 clear_snapshot(refs);
refs/refs-internal.h
+3 -2
@@ -770,8 +770,9 @@ int ref_update_has_null_new_value(struct ref_update *update);
770 * If everything is OK, return 0; otherwise, write an error message to
771 * err and return -1.
772 */
773 -int ref_update_check_old_target(const char *referent, struct ref_update *update,
774 - struct strbuf *err);
773 +enum ref_transaction_error ref_update_check_old_target(const char *referent,
774 + struct ref_update *update,
775 + struct strbuf *err);
776
777 /*
778 * Check if the ref must exist, this means that the old_oid or
refs/reftable-backend.c
+32 -32
@@ -1069,20 +1069,20 @@ static int queue_transaction_update(struct reftable_ref_store *refs,
1069 return 0;
1070 }
1071
1072 -static int prepare_single_update(struct reftable_ref_store *refs,
1073 - struct reftable_transaction_data *tx_data,
1074 - struct ref_transaction *transaction,
1075 - struct reftable_backend *be,
1076 - struct ref_update *u,
1077 - struct string_list *refnames_to_check,
1078 - unsigned int head_type,
1079 - struct strbuf *head_referent,
1080 - struct strbuf *referent,
1081 - struct strbuf *err)
1072 +static enum ref_transaction_error prepare_single_update(struct reftable_ref_store *refs,
1073 + struct reftable_transaction_data *tx_data,
1074 + struct ref_transaction *transaction,
1075 + struct reftable_backend *be,
1076 + struct ref_update *u,
1077 + struct string_list *refnames_to_check,
1078 + unsigned int head_type,
1079 + struct strbuf *head_referent,
1080 + struct strbuf *referent,
1081 + struct strbuf *err)
1082 {
1083 + enum ref_transaction_error ret = 0;
1084 struct object_id current_oid = {0};
1085 const char *rewritten_ref;
1085 - int ret = 0;
1086
1087 /*
1088 * There is no need to reload the respective backends here as
@@ -1093,7 +1093,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1093 */
1094 ret = backend_for(&be, refs, u->refname, &rewritten_ref, 0);
1095 if (ret)
1096 - return ret;
1096 + return REF_TRANSACTION_ERROR_GENERIC;
1097
1098 /* Verify that the new object ID is valid. */
1099 if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
@@ -1104,13 +1104,13 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1104 strbuf_addf(err,
1105 _("trying to write ref '%s' with nonexistent object %s"),
1106 u->refname, oid_to_hex(&u->new_oid));
1107 - return -1;
1107 + return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
1108 }
1109
1110 if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
1111 strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
1112 oid_to_hex(&u->new_oid), u->refname);
1113 - return -1;
1113 + return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
1114 }
1115 }
1116
@@ -1134,7 +1134,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1134 _("multiple updates for 'HEAD' (including one "
1135 "via its referent '%s') are not allowed"),
1136 u->refname);
1137 - return TRANSACTION_NAME_CONFLICT;
1137 + return REF_TRANSACTION_ERROR_NAME_CONFLICT;
1138 }
1139
1140 ref_transaction_add_update(
@@ -1147,7 +1147,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1147 ret = reftable_backend_read_ref(be, rewritten_ref,
1148 &current_oid, referent, &u->type);
1149 if (ret < 0)
1150 - return ret;
1150 + return REF_TRANSACTION_ERROR_GENERIC;
1151 if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
1152 /*
1153 * The reference does not exist, and we either have no
@@ -1168,7 +1168,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1168 ret = queue_transaction_update(refs, tx_data, u,
1169 &current_oid, err);
1170 if (ret)
1171 - return ret;
1171 + return REF_TRANSACTION_ERROR_GENERIC;
1172 }
1173
1174 return 0;
@@ -1180,7 +1180,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1180
1181 "unable to resolve reference '%s'"),
1182 ref_update_original_update_refname(u), u->refname);
1183 - return -1;
1183 + return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
1184 }
1185
1186 if (u->type & REF_ISSYMREF) {
@@ -1196,7 +1196,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1196 if (u->flags & REF_HAVE_OLD && !resolved) {
1197 strbuf_addf(err, _("cannot lock ref '%s': "
1198 "error reading reference"), u->refname);
1199 - return -1;
1199 + return REF_TRANSACTION_ERROR_GENERIC;
1200 }
1201 } else {
1202 struct ref_update *new_update;
@@ -1211,7 +1211,7 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1211 _("multiple updates for '%s' (including one "
1212 "via symref '%s') are not allowed"),
1213 referent->buf, u->refname);
1214 - return TRANSACTION_NAME_CONFLICT;
1214 + return REF_TRANSACTION_ERROR_NAME_CONFLICT;
1215 }
1216
1217 /*
@@ -1255,31 +1255,32 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1255 "but is a regular ref"),
1256 ref_update_original_update_refname(u),
1257 u->old_target);
1258 - return -1;
1258 + return REF_TRANSACTION_ERROR_EXPECTED_SYMREF;
1259 }
1260
1261 - if (ref_update_check_old_target(referent->buf, u, err)) {
1262 - return -1;
1263 - }
1261 + ret = ref_update_check_old_target(referent->buf, u, err);
1262 + if (ret)
1263 + return ret;
1264 } else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
1265 if (is_null_oid(&u->old_oid)) {
1266 strbuf_addf(err, _("cannot lock ref '%s': "
1267 "reference already exists"),
1268 ref_update_original_update_refname(u));
1269 - return TRANSACTION_CREATE_EXISTS;
1270 - }
1271 - else if (is_null_oid(&current_oid))
1269 + return REF_TRANSACTION_ERROR_CREATE_EXISTS;
1270 + } else if (is_null_oid(&current_oid)) {
1271 strbuf_addf(err, _("cannot lock ref '%s': "
1272 "reference is missing but expected %s"),
1273 ref_update_original_update_refname(u),
1274 oid_to_hex(&u->old_oid));
1276 - else
1275 + return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
1276 + } else {
1277 strbuf_addf(err, _("cannot lock ref '%s': "
1278 "is at %s but expected %s"),
1279 ref_update_original_update_refname(u),
1280 oid_to_hex(&current_oid),
1281 oid_to_hex(&u->old_oid));
1282 - return TRANSACTION_NAME_CONFLICT;
1282 + return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
1283 + }
1284 }
1285
1286 /*
@@ -1296,8 +1297,8 @@ static int prepare_single_update(struct reftable_ref_store *refs,
1297 if ((u->type & REF_ISSYMREF) ||
1298 (u->flags & REF_LOG_ONLY) ||
1299 (u->flags & REF_HAVE_NEW && !oideq(&current_oid, &u->new_oid)))
1299 - return queue_transaction_update(refs, tx_data, u,
1300 - &current_oid, err);
1300 + if (queue_transaction_update(refs, tx_data, u, &current_oid, err))
1301 + return REF_TRANSACTION_ERROR_GENERIC;
1302
1303 return 0;
1304 }
@@ -1385,7 +1386,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
1386 transaction->state = REF_TRANSACTION_PREPARED;
1387
1388 done:
1388 - assert(ret != REFTABLE_API_ERROR);
1389 if (ret < 0) {
1390 free_transaction_data(tx_data);
1391 transaction->state = REF_TRANSACTION_CLOSED;