Skip to content

Commit 28c4cae

Browse files
committed
feat: ein tool hours --stat to collect additional statistics per author. (#470)
Note that these are expensive and unconditionally use threads to speed up these computations.
1 parent 1027be9 commit 28c4cae

File tree

3 files changed

+73
-68
lines changed

3 files changed

+73
-68
lines changed

gitoxide-core/src/hours.rs

+68-68
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub struct Context<W> {
1818
pub ignore_bots: bool,
1919
/// Show personally identifiable information before the summary. Includes names and email addresses.
2020
pub show_pii: bool,
21+
/// Collect additional information like tree changes and changed lines.
22+
pub stats: bool,
2123
/// Omit unifying identities by name and email which can lead to the same author appear multiple times
2224
/// due to using different names or email addresses.
2325
pub omit_unify_identities: bool,
@@ -38,6 +40,7 @@ pub fn estimate<W, P>(
3840
Context {
3941
show_pii,
4042
ignore_bots,
43+
stats: _,
4144
omit_unify_identities,
4245
mut out,
4346
}: Context<W>,
@@ -52,80 +55,77 @@ where
5255

5356
let (all_commits, is_shallow) = {
5457
let mut progress = progress.add_child("Traverse commit graph");
55-
let string_heap = &mut string_heap;
56-
std::thread::scope(
57-
move |scope| -> anyhow::Result<(Vec<actor::SignatureRef<'static>>, bool)> {
58-
let start = Instant::now();
59-
progress.init(None, progress::count("commits"));
60-
let (tx, rx) = std::sync::mpsc::channel::<Vec<u8>>();
61-
let mailmap = repo.open_mailmap();
58+
std::thread::scope(|scope| -> anyhow::Result<(Vec<actor::SignatureRef<'static>>, bool)> {
59+
let start = Instant::now();
60+
progress.init(None, progress::count("commits"));
61+
let (tx, rx) = std::sync::mpsc::channel::<Vec<u8>>();
62+
let mailmap = repo.open_mailmap();
6263

63-
let handle = scope.spawn(move || -> anyhow::Result<Vec<actor::SignatureRef<'static>>> {
64-
let mut out = Vec::new();
65-
for commit_data in rx {
66-
if let Some(author) = objs::CommitRefIter::from_bytes(&commit_data)
67-
.author()
68-
.map(|author| mailmap.resolve_cow(author.trim()))
69-
.ok()
70-
{
71-
let mut string_ref = |s: &[u8]| -> &'static BStr {
72-
match string_heap.get(s) {
73-
Some(n) => n.as_bstr(),
74-
None => {
75-
let sv: Vec<u8> = s.to_owned().into();
76-
string_heap.insert(Box::leak(sv.into_boxed_slice()));
77-
(*string_heap.get(s).expect("present")).as_ref()
78-
}
64+
let handle = scope.spawn(move || -> anyhow::Result<Vec<actor::SignatureRef<'static>>> {
65+
let mut out = Vec::new();
66+
for commit_data in rx {
67+
if let Some(author) = objs::CommitRefIter::from_bytes(&commit_data)
68+
.author()
69+
.map(|author| mailmap.resolve_cow(author.trim()))
70+
.ok()
71+
{
72+
let mut string_ref = |s: &[u8]| -> &'static BStr {
73+
match string_heap.get(s) {
74+
Some(n) => n.as_bstr(),
75+
None => {
76+
let sv: Vec<u8> = s.to_owned().into();
77+
string_heap.insert(Box::leak(sv.into_boxed_slice()));
78+
(*string_heap.get(s).expect("present")).as_ref()
7979
}
80-
};
81-
let name = string_ref(author.name.as_ref());
82-
let email = string_ref(&author.email.as_ref());
80+
}
81+
};
82+
let name = string_ref(author.name.as_ref());
83+
let email = string_ref(&author.email.as_ref());
8384

84-
out.push(actor::SignatureRef {
85-
name,
86-
email,
87-
time: author.time,
88-
});
89-
}
85+
out.push(actor::SignatureRef {
86+
name,
87+
email,
88+
time: author.time,
89+
});
9090
}
91-
out.shrink_to_fit();
92-
out.sort_by(|a, b| {
93-
a.email.cmp(&b.email).then(
94-
a.time
95-
.seconds_since_unix_epoch
96-
.cmp(&b.time.seconds_since_unix_epoch)
97-
.reverse(),
98-
)
99-
});
100-
Ok(out)
91+
}
92+
out.shrink_to_fit();
93+
out.sort_by(|a, b| {
94+
a.email.cmp(&b.email).then(
95+
a.time
96+
.seconds_since_unix_epoch
97+
.cmp(&b.time.seconds_since_unix_epoch)
98+
.reverse(),
99+
)
101100
});
101+
Ok(out)
102+
});
102103

103-
let commit_iter = interrupt::Iter::new(
104-
commit_id.ancestors(|oid, buf| {
105-
progress.inc();
106-
repo.objects.find(oid, buf).map(|o| {
107-
tx.send(o.data.to_owned()).ok();
108-
objs::CommitRefIter::from_bytes(o.data)
109-
})
110-
}),
111-
|| anyhow!("Cancelled by user"),
112-
);
113-
let mut is_shallow = false;
114-
for c in commit_iter {
115-
match c? {
116-
Ok(c) => c,
117-
Err(git::traverse::commit::ancestors::Error::FindExisting { .. }) => {
118-
is_shallow = true;
119-
break;
120-
}
121-
Err(err) => return Err(err.into()),
122-
};
123-
}
124-
drop(tx);
125-
progress.show_throughput(start);
126-
Ok((handle.join().expect("no panic")?, is_shallow))
127-
},
128-
)?
104+
let commit_iter = interrupt::Iter::new(
105+
commit_id.ancestors(|oid, buf| {
106+
progress.inc();
107+
repo.objects.find(oid, buf).map(|o| {
108+
tx.send(o.data.to_owned()).ok();
109+
objs::CommitRefIter::from_bytes(o.data)
110+
})
111+
}),
112+
|| anyhow!("Cancelled by user"),
113+
);
114+
let mut is_shallow = false;
115+
for c in commit_iter {
116+
match c? {
117+
Ok(c) => c,
118+
Err(git::traverse::commit::ancestors::Error::FindExisting { .. }) => {
119+
is_shallow = true;
120+
break;
121+
}
122+
Err(err) => return Err(err.into()),
123+
};
124+
}
125+
drop(tx);
126+
progress.show_throughput(start);
127+
Ok((handle.join().expect("no panic")?, is_shallow))
128+
})?
129129
};
130130

131131
if all_commits.is_empty() {

src/porcelain/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub fn main() -> Result<()> {
4040
working_dir,
4141
rev_spec,
4242
no_bots,
43+
stats,
4344
show_pii,
4445
omit_unify_identities,
4546
}) => {
@@ -58,6 +59,7 @@ pub fn main() -> Result<()> {
5859
hours::Context {
5960
show_pii,
6061
ignore_bots: no_bots,
62+
stats,
6163
omit_unify_identities,
6264
out,
6365
},

src/porcelain/options.rs

+3
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ pub struct EstimateHours {
101101
/// Ignore github bots which match the `[bot]` search string.
102102
#[clap(short = 'b', long)]
103103
pub no_bots: bool,
104+
/// Collect additional information like tree changes and changed lines.
105+
#[clap(short = 's', long)]
106+
pub stats: bool,
104107
/// Show personally identifiable information before the summary. Includes names and email addresses.
105108
#[clap(short = 'p', long)]
106109
pub show_pii: bool,

0 commit comments

Comments
 (0)