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