@@ -20,8 +20,10 @@ pub struct Context<W> {
2020 pub ignore_bots : bool ,
2121 /// Show personally identifiable information before the summary. Includes names and email addresses.
2222 pub show_pii : bool ,
23- /// Collect additional information like tree changes and changed lines.
24- pub stats : bool ,
23+ /// Collect how many files have been added, removed and modified (without rename tracking).
24+ pub file_stats : bool ,
25+ /// Collect how many lines in files have been added, removed and modified (without rename tracking).
26+ pub line_stats : bool ,
2527 /// Omit unifying identities by name and email which can lead to the same author appear multiple times
2628 /// due to using different names or email addresses.
2729 pub omit_unify_identities : bool ,
@@ -42,7 +44,8 @@ pub fn estimate<W, P>(
4244 Context {
4345 show_pii,
4446 ignore_bots,
45- stats,
47+ file_stats,
48+ line_stats,
4649 omit_unify_identities,
4750 mut out,
4851 } : Context < W > ,
5659 let mut string_heap = BTreeSet :: < & ' static [ u8 ] > :: new ( ) ;
5760
5861 let ( commit_authors, stats, is_shallow) = {
59- let stat_progress = stats . then ( || progress. add_child ( "extract stats" ) ) . map ( |mut p| {
62+ let stat_progress = file_stats . then ( || progress. add_child ( "extract stats" ) ) . map ( |mut p| {
6063 p. init ( None , progress:: count ( "commits" ) ) ;
6164 p
6265 } ) ;
@@ -113,7 +116,7 @@ where
113116 Ok ( out)
114117 } ) ;
115118
116- let ( tx_tree_id, stat_threads) = stats
119+ let ( tx_tree_id, stat_threads) = ( file_stats || line_stats )
117120 . then ( || {
118121 let num_threads = num_cpus:: get ( ) . saturating_sub ( 1 /*main thread*/ ) . max ( 1 ) ;
119122 let ( tx, rx) = flume:: unbounded :: < ( u32 , Option < git:: hash:: ObjectId > , git:: hash:: ObjectId ) > ( ) ;
@@ -130,7 +133,7 @@ where
130133 if let Some ( c) = counter. as_ref ( ) {
131134 c. fetch_add ( 1 , Ordering :: SeqCst ) ;
132135 }
133- let mut stat = Stats :: default ( ) ;
136+ let mut stat = FileStats :: default ( ) ;
134137 let from = match parent_commit {
135138 Some ( id) => {
136139 match repo. find_object ( id) . ok ( ) . and_then ( |c| c. peel_to_tree ( ) . ok ( ) ) {
@@ -283,16 +286,15 @@ where
283286 ) ) ;
284287
285288 let num_unique_authors = results_by_hours. len ( ) ;
286- let ( total_hours, total_commits, total_stats ) = results_by_hours
289+ let ( total_hours, total_commits, total_files , total_lines ) = results_by_hours
287290 . iter ( )
288- . map ( |e| ( e. hours , e. num_commits , e. stats ) )
289- . reduce ( |a, b| ( a. 0 + b. 0 , a. 1 + b. 1 , a. 2 . clone ( ) . added ( & b. 2 ) ) )
291+ . map ( |e| ( e. hours , e. num_commits , e. files , e . lines ) )
292+ . reduce ( |a, b| ( a. 0 + b. 0 , a. 1 + b. 1 , a. 2 . clone ( ) . added ( & b. 2 ) , a . 3 . clone ( ) . added ( & b . 3 ) ) )
290293 . expect ( "at least one commit at this point" ) ;
291294 if show_pii {
292295 results_by_hours. sort_by ( |a, b| a. hours . partial_cmp ( & b. hours ) . unwrap_or ( std:: cmp:: Ordering :: Equal ) ) ;
293- let show_stats = !stats. is_empty ( ) ;
294296 for entry in results_by_hours. iter ( ) {
295- entry. write_to ( total_hours, show_stats , & mut out) ?;
297+ entry. write_to ( total_hours, file_stats , line_stats , & mut out) ?;
296298 writeln ! ( out) ?;
297299 }
298300 }
@@ -305,11 +307,18 @@ where
305307 is_shallow. then( || " (shallow)" ) . unwrap_or_default( ) ,
306308 num_authors
307309 ) ?;
308- if !stats . is_empty ( ) {
310+ if file_stats {
309311 writeln ! (
310312 out,
311313 "total files added/removed/modified: {}/{}/{}" ,
312- total_stats. added, total_stats. removed, total_stats. modified
314+ total_files. added, total_files. removed, total_files. modified
315+ ) ?;
316+ }
317+ if line_stats {
318+ writeln ! (
319+ out,
320+ "total lines added/removed: {}/{}" ,
321+ total_lines. added, total_lines. removed
313322 ) ?;
314323 }
315324 if !omit_unify_identities {
@@ -334,7 +343,7 @@ where
334343const MINUTES_PER_HOUR : f32 = 60.0 ;
335344const HOURS_PER_WORKDAY : f32 = 8.0 ;
336345
337- fn estimate_hours ( commits : & [ ( u32 , actor:: SignatureRef < ' static > ) ] , stats : & [ ( u32 , Stats ) ] ) -> WorkByEmail {
346+ fn estimate_hours ( commits : & [ ( u32 , actor:: SignatureRef < ' static > ) ] , stats : & [ ( u32 , FileStats ) ] ) -> WorkByEmail {
338347 assert ! ( !commits. is_empty( ) ) ;
339348 const MAX_COMMIT_DIFFERENCE_IN_MINUTES : f32 = 2.0 * MINUTES_PER_HOUR ;
340349 const FIRST_COMMIT_ADDITION_IN_MINUTES : f32 = 2.0 * MINUTES_PER_HOUR ;
@@ -361,9 +370,9 @@ fn estimate_hours(commits: &[(u32, actor::SignatureRef<'static>)], stats: &[(u32
361370 email : author. email ,
362371 hours : FIRST_COMMIT_ADDITION_IN_MINUTES / 60.0 + hours_for_commits,
363372 num_commits : commits. len ( ) as u32 ,
364- stats : ( !stats. is_empty ( ) )
373+ files : ( !stats. is_empty ( ) )
365374 . then ( || {
366- commits. iter ( ) . map ( |t| & t. 0 ) . fold ( Stats :: default ( ) , |mut acc, id| {
375+ commits. iter ( ) . map ( |t| & t. 0 ) . fold ( FileStats :: default ( ) , |mut acc, id| {
367376 match stats. binary_search_by ( |t| t. 0 . cmp ( id) ) {
368377 Ok ( idx) => {
369378 acc. add ( & stats[ idx] . 1 ) ;
@@ -374,6 +383,7 @@ fn estimate_hours(commits: &[(u32, actor::SignatureRef<'static>)], stats: &[(u32
374383 } )
375384 } )
376385 . unwrap_or_default ( ) ,
386+ lines : Default :: default ( ) ,
377387 }
378388}
379389
@@ -410,7 +420,8 @@ struct WorkByPerson {
410420 email : Vec < & ' static BStr > ,
411421 hours : f32 ,
412422 num_commits : u32 ,
413- stats : Stats ,
423+ files : FileStats ,
424+ lines : LineStats ,
414425}
415426
416427impl < ' a > WorkByPerson {
@@ -423,7 +434,7 @@ impl<'a> WorkByPerson {
423434 }
424435 self . num_commits += other. num_commits ;
425436 self . hours += other. hours ;
426- self . stats . add ( & other. stats ) ;
437+ self . files . add ( & other. files ) ;
427438 }
428439}
429440
@@ -434,13 +445,20 @@ impl<'a> From<&'a WorkByEmail> for WorkByPerson {
434445 email : vec ! [ w. email] ,
435446 hours : w. hours ,
436447 num_commits : w. num_commits ,
437- stats : w. stats ,
448+ files : w. files ,
449+ lines : w. lines ,
438450 }
439451 }
440452}
441453
442454impl WorkByPerson {
443- fn write_to ( & self , total_hours : f32 , show_stats : bool , mut out : impl std:: io:: Write ) -> std:: io:: Result < ( ) > {
455+ fn write_to (
456+ & self ,
457+ total_hours : f32 ,
458+ show_files : bool ,
459+ show_lines : bool ,
460+ mut out : impl std:: io:: Write ,
461+ ) -> std:: io:: Result < ( ) > {
444462 writeln ! (
445463 out,
446464 "{} <{}>" ,
@@ -455,11 +473,18 @@ impl WorkByPerson {
455473 self . hours / HOURS_PER_WORKDAY ,
456474 ( self . hours / total_hours) * 100.0
457475 ) ?;
458- if show_stats {
476+ if show_files {
459477 writeln ! (
460478 out,
461479 "total files added/removed/modified: {}/{}/{}" ,
462- self . stats. added, self . stats. removed, self . stats. modified
480+ self . files. added, self . files. removed, self . files. modified
481+ ) ?;
482+ }
483+ if show_lines {
484+ writeln ! (
485+ out,
486+ "total lines added/removed: {}/{}" ,
487+ self . lines. added, self . lines. removed
463488 ) ?;
464489 }
465490 Ok ( ( ) )
@@ -472,12 +497,13 @@ struct WorkByEmail {
472497 email : & ' static BStr ,
473498 hours : f32 ,
474499 num_commits : u32 ,
475- stats : Stats ,
500+ files : FileStats ,
501+ lines : LineStats ,
476502}
477503
478- /// Statistics for a particular commit.
504+ /// File statistics for a particular commit.
479505#[ derive( Debug , Default , Copy , Clone ) ]
480- struct Stats {
506+ struct FileStats {
481507 /// amount of added files
482508 added : usize ,
483509 /// amount of removed files
@@ -486,15 +512,38 @@ struct Stats {
486512 modified : usize ,
487513}
488514
489- impl Stats {
490- fn add ( & mut self , other : & Stats ) -> & mut Self {
515+ /// Line statistics for a particular commit.
516+ #[ derive( Debug , Default , Copy , Clone ) ]
517+ struct LineStats {
518+ /// amount of added lines
519+ added : usize ,
520+ /// amount of removed lines
521+ removed : usize ,
522+ }
523+
524+ impl FileStats {
525+ fn add ( & mut self , other : & FileStats ) -> & mut Self {
491526 self . added += other. added ;
492527 self . removed += other. removed ;
493528 self . modified += other. modified ;
494529 self
495530 }
496531
497- fn added ( & self , other : & Stats ) -> Self {
532+ fn added ( & self , other : & FileStats ) -> Self {
533+ let mut a = * self ;
534+ a. add ( other) ;
535+ a
536+ }
537+ }
538+
539+ impl LineStats {
540+ fn add ( & mut self , other : & LineStats ) -> & mut Self {
541+ self . added += other. added ;
542+ self . removed += other. removed ;
543+ self
544+ }
545+
546+ fn added ( & self , other : & LineStats ) -> Self {
498547 let mut a = * self ;
499548 a. add ( other) ;
500549 a
0 commit comments