packed_ref_store: implement reference transactions

Implement the methods needed to support reference transactions for the packed-refs backend. The new methods are not yet used. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Sep 8, 2017 at 15:51 UTC 2775d8724d73ba2705af72db075b0cdfba3c94f1
2 files changed +319 -3
refs/packed-backend.c
+310 -3
@@ -748,25 +748,332 @@ static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
748 return 0;
749 }
750
751 +/*
752 + * Write the packed-refs from the cache to the packed-refs tempfile,
753 + * incorporating any changes from `updates`. `updates` must be a
754 + * sorted string list whose keys are the refnames and whose util
755 + * values are `struct ref_update *`. On error, rollback the tempfile,
756 + * write an error message to `err`, and return a nonzero value.
757 + *
758 + * The packfile must be locked before calling this function and will
759 + * remain locked when it is done.
760 + */
761 +static int write_with_updates(struct packed_ref_store *refs,
762 + struct string_list *updates,
763 + struct strbuf *err)
764 +{
765 + struct ref_iterator *iter = NULL;
766 + size_t i;
767 + int ok;
768 + FILE *out;
769 + struct strbuf sb = STRBUF_INIT;
770 + char *packed_refs_path;
771 +
772 + if (!is_lock_file_locked(&refs->lock))
773 + die("BUG: write_with_updates() called while unlocked");
774 +
775 + /*
776 + * If packed-refs is a symlink, we want to overwrite the
777 + * symlinked-to file, not the symlink itself. Also, put the
778 + * staging file next to it:
779 + */
780 + packed_refs_path = get_locked_file_path(&refs->lock);
781 + strbuf_addf(&sb, "%s.new", packed_refs_path);
782 + free(packed_refs_path);
783 + if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
784 + strbuf_addf(err, "unable to create file %s: %s",
785 + sb.buf, strerror(errno));
786 + strbuf_release(&sb);
787 + return -1;
788 + }
789 + strbuf_release(&sb);
790 +
791 + out = fdopen_tempfile(&refs->tempfile, "w");
792 + if (!out) {
793 + strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
794 + strerror(errno));
795 + goto error;
796 + }
797 +
798 + if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0)
799 + goto write_error;
800 +
801 + /*
802 + * We iterate in parallel through the current list of refs and
803 + * the list of updates, processing an entry from at least one
804 + * of the lists each time through the loop. When the current
805 + * list of refs is exhausted, set iter to NULL. When the list
806 + * of updates is exhausted, leave i set to updates->nr.
807 + */
808 + iter = packed_ref_iterator_begin(&refs->base, "",
809 + DO_FOR_EACH_INCLUDE_BROKEN);
810 + if ((ok = ref_iterator_advance(iter)) != ITER_OK)
811 + iter = NULL;
812 +
813 + i = 0;
814 +
815 + while (iter || i < updates->nr) {
816 + struct ref_update *update = NULL;
817 + int cmp;
818 +
819 + if (i >= updates->nr) {
820 + cmp = -1;
821 + } else {
822 + update = updates->items[i].util;
823 +
824 + if (!iter)
825 + cmp = +1;
826 + else
827 + cmp = strcmp(iter->refname, update->refname);
828 + }
829 +
830 + if (!cmp) {
831 + /*
832 + * There is both an old value and an update
833 + * for this reference. Check the old value if
834 + * necessary:
835 + */
836 + if ((update->flags & REF_HAVE_OLD)) {
837 + if (is_null_oid(&update->old_oid)) {
838 + strbuf_addf(err, "cannot update ref '%s': "
839 + "reference already exists",
840 + update->refname);
841 + goto error;
842 + } else if (oidcmp(&update->old_oid, iter->oid)) {
843 + strbuf_addf(err, "cannot update ref '%s': "
844 + "is at %s but expected %s",
845 + update->refname,
846 + oid_to_hex(iter->oid),
847 + oid_to_hex(&update->old_oid));
848 + goto error;
849 + }
850 + }
851 +
852 + /* Now figure out what to use for the new value: */
853 + if ((update->flags & REF_HAVE_NEW)) {
854 + /*
855 + * The update takes precedence. Skip
856 + * the iterator over the unneeded
857 + * value.
858 + */
859 + if ((ok = ref_iterator_advance(iter)) != ITER_OK)
860 + iter = NULL;
861 + cmp = +1;
862 + } else {
863 + /*
864 + * The update doesn't actually want to
865 + * change anything. We're done with it.
866 + */
867 + i++;
868 + cmp = -1;
869 + }
870 + } else if (cmp > 0) {
871 + /*
872 + * There is no old value but there is an
873 + * update for this reference. Make sure that
874 + * the update didn't expect an existing value:
875 + */
876 + if ((update->flags & REF_HAVE_OLD) &&
877 + !is_null_oid(&update->old_oid)) {
878 + strbuf_addf(err, "cannot update ref '%s': "
879 + "reference is missing but expected %s",
880 + update->refname,
881 + oid_to_hex(&update->old_oid));
882 + goto error;
883 + }
884 + }
885 +
886 + if (cmp < 0) {
887 + /* Pass the old reference through. */
888 +
889 + struct object_id peeled;
890 + int peel_error = ref_iterator_peel(iter, &peeled);
891 +
892 + if (write_packed_entry(out, iter->refname,
893 + iter->oid->hash,
894 + peel_error ? NULL : peeled.hash))
895 + goto write_error;
896 +
897 + if ((ok = ref_iterator_advance(iter)) != ITER_OK)
898 + iter = NULL;
899 + } else if (is_null_oid(&update->new_oid)) {
900 + /*
901 + * The update wants to delete the reference,
902 + * and the reference either didn't exist or we
903 + * have already skipped it. So we're done with
904 + * the update (and don't have to write
905 + * anything).
906 + */
907 + i++;
908 + } else {
909 + struct object_id peeled;
910 + int peel_error = peel_object(update->new_oid.hash,
911 + peeled.hash);
912 +
913 + if (write_packed_entry(out, update->refname,
914 + update->new_oid.hash,
915 + peel_error ? NULL : peeled.hash))
916 + goto write_error;
917 +
918 + i++;
919 + }
920 + }
921 +
922 + if (ok != ITER_DONE) {
923 + strbuf_addf(err, "unable to write packed-refs file: "
924 + "error iterating over old contents");
925 + goto error;
926 + }
927 +
928 + if (close_tempfile(&refs->tempfile)) {
929 + strbuf_addf(err, "error closing file %s: %s",
930 + get_tempfile_path(&refs->tempfile),
931 + strerror(errno));
932 + strbuf_release(&sb);
933 + return -1;
934 + }
935 +
936 + return 0;
937 +
938 +write_error:
939 + strbuf_addf(err, "error writing to %s: %s",
940 + get_tempfile_path(&refs->tempfile), strerror(errno));
941 +
942 +error:
943 + if (iter)
944 + ref_iterator_abort(iter);
945 +
946 + delete_tempfile(&refs->tempfile);
947 + return -1;
948 +}
949 +
950 +struct packed_transaction_backend_data {
951 + /* True iff the transaction owns the packed-refs lock. */
952 + int own_lock;
953 +
954 + struct string_list updates;
955 +};
956 +
957 +static void packed_transaction_cleanup(struct packed_ref_store *refs,
958 + struct ref_transaction *transaction)
959 +{
960 + struct packed_transaction_backend_data *data = transaction->backend_data;
961 +
962 + if (data) {
963 + string_list_clear(&data->updates, 0);
964 +
965 + if (is_tempfile_active(&refs->tempfile))
966 + delete_tempfile(&refs->tempfile);
967 +
968 + if (data->own_lock && is_lock_file_locked(&refs->lock)) {
969 + packed_refs_unlock(&refs->base);
970 + data->own_lock = 0;
971 + }
972 +
973 + free(data);
974 + transaction->backend_data = NULL;
975 + }
976 +
977 + transaction->state = REF_TRANSACTION_CLOSED;
978 +}
979 +
980 static int packed_transaction_prepare(struct ref_store *ref_store,
981 struct ref_transaction *transaction,
982 struct strbuf *err)
983 {
755 - die("BUG: not implemented yet");
984 + struct packed_ref_store *refs = packed_downcast(
985 + ref_store,
986 + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
987 + "ref_transaction_prepare");
988 + struct packed_transaction_backend_data *data;
989 + size_t i;
990 + int ret = TRANSACTION_GENERIC_ERROR;
991 +
992 + /*
993 + * Note that we *don't* skip transactions with zero updates,
994 + * because such a transaction might be executed for the side
995 + * effect of ensuring that all of the references are peeled.
996 + * If the caller wants to optimize away empty transactions, it
997 + * should do so itself.
998 + */
999 +
1000 + data = xcalloc(1, sizeof(*data));
1001 + string_list_init(&data->updates, 0);
1002 +
1003 + transaction->backend_data = data;
1004 +
1005 + /*
1006 + * Stick the updates in a string list by refname so that we
1007 + * can sort them:
1008 + */
1009 + for (i = 0; i < transaction->nr; i++) {
1010 + struct ref_update *update = transaction->updates[i];
1011 + struct string_list_item *item =
1012 + string_list_append(&data->updates, update->refname);
1013 +
1014 + /* Store a pointer to update in item->util: */
1015 + item->util = update;
1016 + }
1017 + string_list_sort(&data->updates);
1018 +
1019 + if (ref_update_reject_duplicates(&data->updates, err))
1020 + goto failure;
1021 +
1022 + if (!is_lock_file_locked(&refs->lock)) {
1023 + if (packed_refs_lock(ref_store, 0, err))
1024 + goto failure;
1025 + data->own_lock = 1;
1026 + }
1027 +
1028 + if (write_with_updates(refs, &data->updates, err))
1029 + goto failure;
1030 +
1031 + transaction->state = REF_TRANSACTION_PREPARED;
1032 + return 0;
1033 +
1034 +failure:
1035 + packed_transaction_cleanup(refs, transaction);
1036 + return ret;
1037 }
1038
1039 static int packed_transaction_abort(struct ref_store *ref_store,
1040 struct ref_transaction *transaction,
1041 struct strbuf *err)
1042 {
762 - die("BUG: not implemented yet");
1043 + struct packed_ref_store *refs = packed_downcast(
1044 + ref_store,
1045 + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
1046 + "ref_transaction_abort");
1047 +
1048 + packed_transaction_cleanup(refs, transaction);
1049 + return 0;
1050 }
1051
1052 static int packed_transaction_finish(struct ref_store *ref_store,
1053 struct ref_transaction *transaction,
1054 struct strbuf *err)
1055 {
769 - die("BUG: not implemented yet");
1056 + struct packed_ref_store *refs = packed_downcast(
1057 + ref_store,
1058 + REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
1059 + "ref_transaction_finish");
1060 + int ret = TRANSACTION_GENERIC_ERROR;
1061 + char *packed_refs_path;
1062 +
1063 + packed_refs_path = get_locked_file_path(&refs->lock);
1064 + if (rename_tempfile(&refs->tempfile, packed_refs_path)) {
1065 + strbuf_addf(err, "error replacing %s: %s",
1066 + refs->path, strerror(errno));
1067 + goto cleanup;
1068 + }
1069 +
1070 + clear_packed_ref_cache(refs);
1071 + ret = 0;
1072 +
1073 +cleanup:
1074 + free(packed_refs_path);
1075 + packed_transaction_cleanup(refs, transaction);
1076 + return ret;
1077 }
1078
1079 static int packed_initial_transaction_commit(struct ref_store *ref_store,
refs/packed-backend.h
+9
@@ -1,6 +1,15 @@
1 #ifndef REFS_PACKED_BACKEND_H
2 #define REFS_PACKED_BACKEND_H
3
4 +/*
5 + * Support for storing references in a `packed-refs` file.
6 + *
7 + * Note that this backend doesn't check for D/F conflicts, because it
8 + * doesn't care about them. But usually it should be wrapped in a
9 + * `files_ref_store` that prevents D/F conflicts from being created,
10 + * even among packed refs.
11 + */
12 +
13 struct ref_store *packed_ref_store_create(const char *path,
14 unsigned int store_flags);
15