@@ -748,25 +748,332 @@ static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
748
748
return 0 ;
749
749
}
750
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
+
751
980
static int packed_transaction_prepare (struct ref_store * ref_store ,
752
981
struct ref_transaction * transaction ,
753
982
struct strbuf * err )
754
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 ;
756
1037
}
757
1038
758
1039
static int packed_transaction_abort (struct ref_store * ref_store ,
759
1040
struct ref_transaction * transaction ,
760
1041
struct strbuf * err )
761
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 ;
763
1050
}
764
1051
765
1052
static int packed_transaction_finish (struct ref_store * ref_store ,
766
1053
struct ref_transaction * transaction ,
767
1054
struct strbuf * err )
768
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 ;
770
1077
}
771
1078
772
1079
static int packed_initial_transaction_commit (struct ref_store * ref_store ,
0 commit comments