@@ -5,21 +5,6 @@ use git_repository as git;
55
66use crate :: OutputFormat ;
77
8- mod info {
9- use std:: path:: PathBuf ;
10-
11- use git_repository:: odb:: store;
12-
13- #[ cfg_attr( feature = "serde1" , derive( serde:: Serialize ) ) ]
14- pub struct Statistics {
15- pub path : PathBuf ,
16- pub object_hash : String ,
17- pub use_multi_pack_index : bool ,
18- pub structure : Vec < store:: structure:: Record > ,
19- pub metrics : store:: Metrics ,
20- }
21- }
22-
238#[ cfg_attr( not( feature = "serde1" ) , allow( unused_variables) ) ]
249pub fn info (
2510 repo : git:: Repository ,
@@ -31,8 +16,17 @@ pub fn info(
3116 writeln ! ( err, "Only JSON is implemented - using that instead" ) ?;
3217 }
3318
19+ #[ cfg_attr( feature = "serde1" , derive( serde:: Serialize ) ) ]
20+ pub struct Statistics {
21+ pub path : std:: path:: PathBuf ,
22+ pub object_hash : String ,
23+ pub use_multi_pack_index : bool ,
24+ pub structure : Vec < git:: odb:: store:: structure:: Record > ,
25+ pub metrics : git:: odb:: store:: Metrics ,
26+ }
27+
3428 let store = repo. objects . store_ref ( ) ;
35- let stats = info :: Statistics {
29+ let stats = Statistics {
3630 path : store. path ( ) . into ( ) ,
3731 object_hash : store. object_hash ( ) . to_string ( ) ,
3832 use_multi_pack_index : store. use_multi_pack_index ( ) ,
@@ -48,6 +42,169 @@ pub fn info(
4842 Ok ( ( ) )
4943}
5044
45+ pub mod statistics {
46+ use crate :: OutputFormat ;
47+
48+ pub const PROGRESS_RANGE : std:: ops:: RangeInclusive < u8 > = 0 ..=3 ;
49+
50+ #[ derive( Debug , Copy , Clone ) ]
51+ pub struct Options {
52+ pub format : OutputFormat ,
53+ pub thread_limit : Option < usize > ,
54+ }
55+ }
56+
57+ #[ cfg_attr( not( feature = "serde1" ) , allow( unused_variables) ) ]
58+ pub fn statistics (
59+ repo : git:: Repository ,
60+ mut progress : impl git:: Progress ,
61+ out : impl io:: Write ,
62+ mut err : impl io:: Write ,
63+ statistics:: Options { format, thread_limit } : statistics:: Options ,
64+ ) -> anyhow:: Result < ( ) > {
65+ use bytesize:: ByteSize ;
66+ use git:: odb:: find;
67+ use git:: odb:: HeaderExt ;
68+
69+ if format == OutputFormat :: Human {
70+ writeln ! ( err, "Only JSON is implemented - using that instead" ) ?;
71+ }
72+
73+ progress. init ( None , git:: progress:: count ( "objects" ) ) ;
74+ progress. set_name ( "counting" ) ;
75+ let counter = progress. counter ( ) ;
76+ let start = std:: time:: Instant :: now ( ) ;
77+
78+ #[ cfg_attr( feature = "serde1" , derive( serde:: Serialize ) ) ]
79+ #[ derive( Default ) ]
80+ struct Statistics {
81+ total_objects : usize ,
82+ loose_objects : usize ,
83+ packed_objects : usize ,
84+ packed_delta_objects : usize ,
85+ total_delta_chain_length : u64 ,
86+ trees : usize ,
87+ trees_size : ByteSize ,
88+ tags : usize ,
89+ tags_size : ByteSize ,
90+ commits : usize ,
91+ commits_size : ByteSize ,
92+ blobs : usize ,
93+ blobs_size : ByteSize ,
94+ }
95+
96+ impl Statistics {
97+ fn count ( & mut self , kind : git:: object:: Kind , size : u64 ) {
98+ use git:: object:: Kind :: * ;
99+ match kind {
100+ Commit => {
101+ self . commits += 1 ;
102+ self . commits_size += size;
103+ }
104+ Tree => {
105+ self . trees += 1 ;
106+ self . trees_size += size;
107+ }
108+ Tag => {
109+ self . tags += 1 ;
110+ self . tags_size += size;
111+ }
112+ Blob => {
113+ self . blobs += 1 ;
114+ self . blobs_size += size;
115+ }
116+ }
117+ }
118+ fn consume ( & mut self , item : git:: odb:: find:: Header ) {
119+ match item {
120+ find:: Header :: Loose { size, kind } => {
121+ self . loose_objects += 1 ;
122+ self . count ( kind, size)
123+ }
124+ find:: Header :: Packed ( packed) => {
125+ self . packed_objects += 1 ;
126+ self . packed_delta_objects += usize:: from ( packed. num_deltas > 0 ) ;
127+ self . total_delta_chain_length += packed. num_deltas as u64 ;
128+ self . count ( packed. kind , packed. object_size ) ;
129+ }
130+ }
131+ }
132+ }
133+
134+ #[ derive( Default ) ]
135+ struct Reduce {
136+ stats : Statistics ,
137+ }
138+
139+ impl git:: parallel:: Reduce for Reduce {
140+ type Input = Result < Vec < git:: odb:: find:: Header > , anyhow:: Error > ;
141+ type FeedProduce = ( ) ;
142+ type Output = Statistics ;
143+ type Error = anyhow:: Error ;
144+
145+ fn feed ( & mut self , items : Self :: Input ) -> Result < Self :: FeedProduce , Self :: Error > {
146+ for item in items? {
147+ self . stats . consume ( item) ;
148+ }
149+ Ok ( ( ) )
150+ }
151+
152+ fn finalize ( mut self ) -> Result < Self :: Output , Self :: Error > {
153+ self . stats . total_objects = self . stats . loose_objects + self . stats . packed_objects ;
154+ Ok ( self . stats )
155+ }
156+ }
157+
158+ let cancelled = || anyhow:: anyhow!( "Cancelled by user" ) ;
159+ let object_ids = repo. objects . store_ref ( ) . iter ( ) ?. filter_map ( Result :: ok) ;
160+ let chunk_size = 1_000 ;
161+ let stats = if git:: parallel:: num_threads ( thread_limit) > 1 {
162+ git:: parallel:: in_parallel (
163+ git:: interrupt:: Iter :: new (
164+ git:: features:: iter:: Chunks {
165+ inner : object_ids,
166+ size : chunk_size,
167+ } ,
168+ cancelled,
169+ ) ,
170+ thread_limit,
171+ move |_| ( repo. objects . clone ( ) . into_inner ( ) , counter. clone ( ) ) ,
172+ |ids, ( handle, counter) | {
173+ let ids = ids?;
174+ if let Some ( counter) = counter {
175+ counter. fetch_add ( ids. len ( ) , std:: sync:: atomic:: Ordering :: SeqCst ) ;
176+ }
177+ let out = ids
178+ . into_iter ( )
179+ . map ( |id| handle. header ( id) )
180+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
181+ Ok ( out)
182+ } ,
183+ Reduce :: default ( ) ,
184+ ) ?
185+ } else {
186+ let mut stats = Statistics :: default ( ) ;
187+
188+ for ( count, id) in object_ids. enumerate ( ) {
189+ if count % chunk_size == 0 && git:: interrupt:: is_triggered ( ) {
190+ return Err ( cancelled ( ) ) ;
191+ }
192+ stats. consume ( repo. objects . header ( id) ?) ;
193+ progress. inc ( ) ;
194+ }
195+ stats
196+ } ;
197+
198+ progress. show_throughput ( start) ;
199+
200+ #[ cfg( feature = "serde1" ) ]
201+ {
202+ serde_json:: to_writer_pretty ( out, & stats) ?;
203+ }
204+
205+ Ok ( ( ) )
206+ }
207+
51208pub fn entries ( repo : git:: Repository , format : OutputFormat , mut out : impl io:: Write ) -> anyhow:: Result < ( ) > {
52209 if format != OutputFormat :: Human {
53210 bail ! ( "Only human output format is supported at the moment" ) ;
0 commit comments