Skip to content

Commit 30712dc

Browse files
committed
Merge branch 'main' into fix-odb-race
2 parents 53b5086 + b833a4d commit 30712dc

File tree

11 files changed

+374
-309
lines changed

11 files changed

+374
-309
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pretty-cli = [ "gitoxide-core/serde1", "prodash/progress-tree", "prodash/progres
3737

3838
## The `--verbose` flag will be powered by an interactive progress mechanism that doubles as log as well as interactive progress
3939
## that appears after a short duration.
40-
prodash-render-line-crossterm = ["prodash-render-line", "prodash/render-line-crossterm", "atty", "crosstermion"]
40+
prodash-render-line-crossterm = ["prodash-render-line", "prodash/render-line-crossterm", "prodash/signal-hook", "atty", "crosstermion"]
4141

4242
#! ### Convenience Features
4343
#! These combine common choices of the above features to represent typical builds

README.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,6 @@ is usable to some extend.
167167
* [x] Generate and verify large commit graphs
168168
* [ ] Generate huge pack from a lot of loose objects
169169

170-
### Cargo features
171-
172-
Many crates use feature flags to allow tuning the compiled result based on your needs. Have a [look at the guide][cargo-features] for more information.
173-
174-
[cargo-features]: https://github.com/Byron/gitoxide/blob/main/cargo-features.md#git-config
175-
176170
### Stability and MSRV
177171

178172
Our [stability guide] helps to judge how much churn can be expected when depending on crates in this workspace.

git-object/src/tree/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ impl EntryMode {
3939
*self != EntryMode::Tree
4040
}
4141

42+
/// Return true if the entry is any kind of blob.
43+
pub fn is_blob(&self) -> bool {
44+
matches!(self, EntryMode::Blob | EntryMode::BlobExecutable)
45+
}
46+
4247
/// Represent the mode as descriptive string.
4348
pub fn as_str(&self) -> &'static str {
4449
use EntryMode::*;

gitoxide-core/src/hours/core.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::hours::FileStats;
2+
use crate::hours::LineStats;
3+
use crate::hours::WorkByEmail;
4+
use crate::hours::WorkByPerson;
5+
use git::bstr::BStr;
6+
use git_repository as git;
7+
use itertools::Itertools;
8+
use std::collections::hash_map::Entry;
9+
use std::collections::HashMap;
10+
11+
const MINUTES_PER_HOUR: f32 = 60.0;
12+
pub const HOURS_PER_WORKDAY: f32 = 8.0;
13+
14+
pub fn estimate_hours(
15+
commits: &[(u32, git::actor::SignatureRef<'static>)],
16+
stats: &[(u32, FileStats, LineStats)],
17+
) -> WorkByEmail {
18+
assert!(!commits.is_empty());
19+
const MAX_COMMIT_DIFFERENCE_IN_MINUTES: f32 = 2.0 * MINUTES_PER_HOUR;
20+
const FIRST_COMMIT_ADDITION_IN_MINUTES: f32 = 2.0 * MINUTES_PER_HOUR;
21+
22+
let hours_for_commits = commits.iter().map(|t| &t.1).rev().tuple_windows().fold(
23+
0_f32,
24+
|hours, (cur, next): (&git::actor::SignatureRef<'_>, &git::actor::SignatureRef<'_>)| {
25+
let change_in_minutes = (next
26+
.time
27+
.seconds_since_unix_epoch
28+
.saturating_sub(cur.time.seconds_since_unix_epoch)) as f32
29+
/ MINUTES_PER_HOUR;
30+
if change_in_minutes < MAX_COMMIT_DIFFERENCE_IN_MINUTES {
31+
hours + change_in_minutes as f32 / MINUTES_PER_HOUR
32+
} else {
33+
hours + (FIRST_COMMIT_ADDITION_IN_MINUTES / MINUTES_PER_HOUR)
34+
}
35+
},
36+
);
37+
38+
let author = &commits[0].1;
39+
let (files, lines) = (!stats.is_empty())
40+
.then(|| {
41+
commits
42+
.iter()
43+
.map(|t| &t.0)
44+
.fold((FileStats::default(), LineStats::default()), |mut acc, id| match stats
45+
.binary_search_by(|t| t.0.cmp(id))
46+
{
47+
Ok(idx) => {
48+
let t = &stats[idx];
49+
acc.0.add(&t.1);
50+
acc.1.add(&t.2);
51+
acc
52+
}
53+
Err(_) => acc,
54+
})
55+
})
56+
.unwrap_or_default();
57+
WorkByEmail {
58+
name: author.name,
59+
email: author.email,
60+
hours: FIRST_COMMIT_ADDITION_IN_MINUTES / 60.0 + hours_for_commits,
61+
num_commits: commits.len() as u32,
62+
files,
63+
lines,
64+
}
65+
}
66+
67+
pub fn deduplicate_identities(persons: &[WorkByEmail]) -> Vec<WorkByPerson> {
68+
let mut email_to_index = HashMap::<&'static BStr, usize>::with_capacity(persons.len());
69+
let mut name_to_index = HashMap::<&'static BStr, usize>::with_capacity(persons.len());
70+
let mut out = Vec::<WorkByPerson>::with_capacity(persons.len());
71+
for person_by_email in persons {
72+
match email_to_index.entry(person_by_email.email) {
73+
Entry::Occupied(email_entry) => {
74+
out[*email_entry.get()].merge(person_by_email);
75+
name_to_index.insert(&person_by_email.name, *email_entry.get());
76+
}
77+
Entry::Vacant(email_entry) => match name_to_index.entry(&person_by_email.name) {
78+
Entry::Occupied(name_entry) => {
79+
out[*name_entry.get()].merge(person_by_email);
80+
email_entry.insert(*name_entry.get());
81+
}
82+
Entry::Vacant(name_entry) => {
83+
let idx = out.len();
84+
name_entry.insert(idx);
85+
email_entry.insert(idx);
86+
out.push(person_by_email.into());
87+
}
88+
},
89+
}
90+
}
91+
out
92+
}

0 commit comments

Comments
 (0)