1+ use std:: cell:: RefCell ;
12use std:: {
23 convert:: Infallible ,
34 sync:: atomic:: { AtomicUsize , Ordering } ,
45 time:: Instant ,
56} ;
67
78use anyhow:: { anyhow, bail} ;
9+ use gix:: objs:: find:: Error ;
810use gix:: {
911 bstr:: { BStr , BString , ByteSlice } ,
1012 features:: progress,
1113 object:: tree:: diff:: rewrites:: CopySource ,
12- odb:: FindExt ,
1314 parallel:: { InOrderIter , SequenceId } ,
1415 prelude:: ObjectIdExt ,
1516 Count , Progress ,
@@ -150,13 +151,18 @@ pub fn update(
150151 } ) ;
151152 r
152153 } ;
154+
155+ #[ derive( Clone ) ]
153156 struct Task {
154157 commit : gix:: hash:: ObjectId ,
155158 parent_commit : Option < gix:: hash:: ObjectId > ,
156159 compute_stats : bool ,
157160 }
161+
162+ type Packet = ( SequenceId , Vec < Task > ) ;
163+
158164 let ( tx_tree_ids, stat_threads) = {
159- let ( tx, rx) = crossbeam_channel:: unbounded :: < ( SequenceId , Vec < Task > ) > ( ) ;
165+ let ( tx, rx) = crossbeam_channel:: unbounded :: < Packet > ( ) ;
160166 let stat_workers = ( 0 ..threads)
161167 . map ( |_| {
162168 scope. spawn ( {
@@ -304,46 +310,94 @@ pub fn update(
304310 } ;
305311 drop ( tx_stats) ;
306312
307- const CHUNK_SIZE : usize = 50 ;
308- let mut chunk = Vec :: with_capacity ( CHUNK_SIZE ) ;
309- let mut chunk_id: SequenceId = 0 ;
310- let commit_iter = gix:: interrupt:: Iter :: new (
311- commit_id. ancestors ( |oid, buf| -> Result < _ , gix:: object:: find:: existing:: Error > {
312- let obj = repo. objects . find ( oid, buf) ?;
313- traverse_progress. inc ( ) ;
314- if known_commits. binary_search ( & oid. to_owned ( ) ) . is_err ( ) {
313+ #[ derive( Clone ) ]
314+ struct Db < ' a , Find : Clone > {
315+ inner : & ' a Find ,
316+ progress : & ' a dyn gix:: progress:: Count ,
317+ chunk : std:: cell:: RefCell < Vec < Task > > ,
318+ chunk_id : std:: cell:: RefCell < SequenceId > ,
319+ chunk_size : usize ,
320+ tx : crossbeam_channel:: Sender < Packet > ,
321+ known_commits : & ' a [ gix:: ObjectId ] ,
322+ }
323+
324+ impl < ' a , Find > Db < ' a , Find >
325+ where
326+ Find : gix:: prelude:: Find + Clone ,
327+ {
328+ fn new (
329+ inner : & ' a Find ,
330+ progress : & ' a dyn gix:: progress:: Count ,
331+ chunk_size : usize ,
332+ tx : crossbeam_channel:: Sender < Packet > ,
333+ known_commits : & ' a [ gix:: ObjectId ] ,
334+ ) -> Self {
335+ Self {
336+ inner,
337+ progress,
338+ known_commits,
339+ tx,
340+ chunk_size,
341+ chunk_id : 0 . into ( ) ,
342+ chunk : RefCell :: new ( Vec :: with_capacity ( chunk_size) ) ,
343+ }
344+ }
345+
346+ fn send_last_chunk ( self ) {
347+ self . tx . send ( ( self . chunk_id . into_inner ( ) , self . chunk . into_inner ( ) ) ) . ok ( ) ;
348+ }
349+ }
350+
351+ impl < ' a , Find > gix:: prelude:: Find for Db < ' a , Find >
352+ where
353+ Find : gix:: prelude:: Find + Clone ,
354+ {
355+ fn try_find < ' b > ( & self , id : & gix:: oid , buf : & ' b mut Vec < u8 > ) -> Result < Option < gix:: objs:: Data < ' b > > , Error > {
356+ let obj = self . inner . try_find ( id, buf) ?;
357+ let Some ( obj) = obj else { return Ok ( None ) } ;
358+ if !obj. kind . is_commit ( ) {
359+ return Ok ( None ) ;
360+ }
361+
362+ self . progress . inc ( ) ;
363+ if self . known_commits . binary_search ( & id. to_owned ( ) ) . is_err ( ) {
315364 let res = {
316365 let mut parents = gix:: objs:: CommitRefIter :: from_bytes ( obj. data ) . parent_ids ( ) ;
317- let res = parents. next ( ) . map ( |first_parent| ( Some ( first_parent) , oid . to_owned ( ) ) ) ;
366+ let res = parents. next ( ) . map ( |first_parent| ( Some ( first_parent) , id . to_owned ( ) ) ) ;
318367 match parents. next ( ) {
319368 Some ( _) => None ,
320369 None => res,
321370 }
322371 } ;
323372 if let Some ( ( first_parent, commit) ) = res {
324- chunk. push ( Task {
373+ self . chunk . borrow_mut ( ) . push ( Task {
325374 parent_commit : first_parent,
326375 commit,
327376 compute_stats : true ,
328377 } ) ;
329378 } else {
330- chunk. push ( Task {
379+ self . chunk . borrow_mut ( ) . push ( Task {
331380 parent_commit : None ,
332- commit : oid . to_owned ( ) ,
381+ commit : id . to_owned ( ) ,
333382 compute_stats : false ,
334383 } ) ;
335384 }
336- if chunk. len ( ) == CHUNK_SIZE {
337- tx_tree_ids
338- . send ( ( chunk_id, std:: mem:: replace ( & mut chunk, Vec :: with_capacity ( CHUNK_SIZE ) ) ) )
385+ if self . chunk . borrow ( ) . len ( ) == self . chunk_size {
386+ self . tx
387+ . send ( (
388+ * self . chunk_id . borrow ( ) ,
389+ std:: mem:: replace ( & mut self . chunk . borrow_mut ( ) , Vec :: with_capacity ( self . chunk_size ) ) ,
390+ ) )
339391 . ok ( ) ;
340- chunk_id += 1 ;
392+ * self . chunk_id . borrow_mut ( ) += 1 ;
341393 }
342394 }
343- Ok ( gix:: objs:: CommitRefIter :: from_bytes ( obj. data ) )
344- } ) ,
345- || anyhow ! ( "Cancelled by user" ) ,
346- ) ;
395+ Ok ( Some ( obj) )
396+ }
397+ }
398+
399+ let db = Db :: new ( & repo. objects , & traverse_progress, 50 , tx_tree_ids, & known_commits) ;
400+ let commit_iter = gix:: interrupt:: Iter :: new ( commit_id. ancestors ( & db) , || anyhow ! ( "Cancelled by user" ) ) ;
347401 let mut commits = Vec :: new ( ) ;
348402 for c in commit_iter {
349403 match c?. map ( |c| c. id ) {
@@ -354,15 +408,14 @@ pub fn update(
354408 break ;
355409 }
356410 }
357- Err ( gix:: traverse:: commit:: ancestors:: Error :: FindExisting { .. } ) => {
411+ Err ( gix:: traverse:: commit:: ancestors:: Error :: Find { .. } ) => {
358412 writeln ! ( err, "shallow repository - commit history is truncated" ) . ok ( ) ;
359413 break ;
360414 }
361415 Err ( err) => return Err ( err. into ( ) ) ,
362416 } ;
363417 }
364- tx_tree_ids. send ( ( chunk_id, chunk) ) . ok ( ) ;
365- drop ( tx_tree_ids) ;
418+ db. send_last_chunk ( ) ;
366419 let saw_new_commits = !commits. is_empty ( ) ;
367420 if saw_new_commits {
368421 traverse_progress. show_throughput ( start) ;
0 commit comments