@@ -748,25 +748,332 @@ static int packed_init_db(struct ref_store *ref_store, struct strbuf *err)
748748 return 0 ;
749749}
750750
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+
751980static int packed_transaction_prepare (struct ref_store * ref_store ,
752981 struct ref_transaction * transaction ,
753982 struct strbuf * err )
754983{
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 ;
7561037}
7571038
7581039static int packed_transaction_abort (struct ref_store * ref_store ,
7591040 struct ref_transaction * transaction ,
7601041 struct strbuf * err )
7611042{
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 ;
7631050}
7641051
7651052static int packed_transaction_finish (struct ref_store * ref_store ,
7661053 struct ref_transaction * transaction ,
7671054 struct strbuf * err )
7681055{
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 ;
7701077}
7711078
7721079static int packed_initial_transaction_commit (struct ref_store * ref_store ,
0 commit comments