@@ -16,6 +16,10 @@ pub mod integrity {
16
16
IndexIntegrity ( #[ from] crate :: index:: verify:: integrity:: Error ) ,
17
17
#[ error( transparent) ]
18
18
BundleInit ( #[ from] crate :: bundle:: init:: Error ) ,
19
+ #[ error( "Counted {actual} objects, but expected {expected} as per multi-index" ) ]
20
+ UnexpectedObjectCount { actual : usize , expected : usize } ,
21
+ #[ error( "The object at multi-index entry {index} didn't match the expected oid sort-order or pack-offset" ) ]
22
+ OutOfOrder { index : usize } ,
19
23
}
20
24
21
25
/// Returned by [`multi_index::File::verify_integrity()`][crate::multi_index::File::verify_integrity()].
@@ -89,7 +93,13 @@ impl File {
89
93
Some ( self . num_indices as usize ) ,
90
94
git_features:: progress:: count ( "indices" ) ,
91
95
) ;
92
- for index_file_name in & self . index_names {
96
+ let mut order_progress = progress. add_child ( "obtain oid and offset" ) ;
97
+ order_progress. init (
98
+ Some ( self . num_objects as usize ) ,
99
+ git_features:: progress:: count ( "objects" ) ,
100
+ ) ;
101
+ let mut oids_and_offsets = Vec :: with_capacity ( self . num_objects as usize ) ;
102
+ for ( pack_id, index_file_name) in self . index_names . iter ( ) . enumerate ( ) {
93
103
progress. inc ( ) ;
94
104
let bundle = crate :: Bundle :: at ( parent. join ( index_file_name) , self . object_hash )
95
105
. map_err ( integrity:: Error :: from)
@@ -144,8 +154,51 @@ impl File {
144
154
}
145
155
} ) ?;
146
156
pack_traverse_outcomes. push ( pack_traverse_outcome) ;
157
+
158
+ oids_and_offsets. extend (
159
+ bundle
160
+ . index
161
+ . iter ( )
162
+ . map ( |e| ( e. oid , e. pack_offset , pack_id as crate :: multi_index:: PackIndex ) ) ,
163
+ ) ;
164
+ order_progress. inc_by ( bundle. index . num_objects ( ) as usize ) ;
165
+ }
166
+
167
+ let order_start = std:: time:: Instant :: now ( ) ;
168
+ order_progress. set_name ( "ordering by oid and deduplicating" ) ;
169
+ order_progress. set ( 0 ) ;
170
+ oids_and_offsets. sort_by ( |l, r| l. 0 . cmp ( & r. 0 ) ) ;
171
+ oids_and_offsets. dedup_by ( |l, r| l. 0 . eq ( & r. 0 ) ) ;
172
+
173
+ if oids_and_offsets. len ( ) != self . num_objects as usize {
174
+ return Err ( crate :: index:: traverse:: Error :: Processor (
175
+ integrity:: Error :: UnexpectedObjectCount {
176
+ actual : oids_and_offsets. len ( ) ,
177
+ expected : self . num_objects as usize ,
178
+ } ,
179
+ ) ) ;
147
180
}
148
181
182
+ order_progress. set_name ( "comparing oid and pack offset" ) ;
183
+ for ( index, ( ( loid, lpack_offset, lpack_id) , ( roid, rpack_offset, rpack_id) ) ) in oids_and_offsets
184
+ . into_iter ( )
185
+ . zip ( self . iter ( ) . map ( |e| ( e. oid , e. pack_offset , e. pack_index ) ) )
186
+ . enumerate ( )
187
+ {
188
+ if loid != roid || lpack_offset != rpack_offset || lpack_id != rpack_id {
189
+ if loid == roid && lpack_id != rpack_id {
190
+ // Right now we can't properly determine which pack would be chosen if objects exists in multiple packs, hence
191
+ // our comparison might be off here.
192
+ // TODO: check how git does the comparison or get into multi-index writing to be more true to the source.
193
+ continue ;
194
+ }
195
+ return Err ( crate :: index:: traverse:: Error :: Processor ( integrity:: Error :: OutOfOrder {
196
+ index,
197
+ } ) ) ;
198
+ }
199
+ }
200
+ order_progress. inc_by ( self . num_objects as usize ) ;
201
+ order_progress. show_throughput ( order_start) ;
149
202
progress. show_throughput ( start) ;
150
203
151
204
Ok ( integrity:: Outcome {
0 commit comments