Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions gitoxide-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ default = []
## Discover all git repositories within a directory. Particularly useful with [skim](https://github.com/lotabout/skim).
organize = ["dep:gix-url", "dep:jwalk"]
## Derive the amount of time invested into a git repository akin to [git-hours](https://github.com/kimmobrunfeldt/git-hours).
estimate-hours = ["dep:itertools", "dep:fs-err", "dep:crossbeam-channel", "dep:smallvec"]
estimate-hours = ["dep:fs-err", "dep:crossbeam-channel", "dep:smallvec"]
## Gather information about repositories and store it in a database for easy querying.
query = ["dep:rusqlite"]
## Run algorithms on a corpus of repositories and store their results for later comparison and intelligence gathering.
Expand All @@ -29,7 +29,7 @@ corpus = [ "dep:rusqlite", "dep:sysinfo", "organize", "dep:crossbeam-channel", "
archive = ["dep:gix-archive-for-configuration-only", "gix/worktree-archive"]

## The ability to clean a repository, similar to `git clean`.
clean = [ "gix/dirwalk" ]
clean = ["gix/dirwalk"]

#! ### Mutually Exclusive Networking
#! If both are set, _blocking-client_ will take precedence, allowing `--all-features` to be used.
Expand Down Expand Up @@ -72,7 +72,6 @@ gix-url = { version = "^0.27.0", path = "../gix-url", optional = true }
jwalk = { version = "0.8.0", optional = true }

# for 'hours'
itertools = { version = "0.12.0", optional = true }
fs-err = { version = "2.6.0", optional = true }
crossbeam-channel = { version = "0.5.6", optional = true }
smallvec = { version = "1.10.0", optional = true }
Expand Down
23 changes: 15 additions & 8 deletions gitoxide-core/src/hours/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::{
};

use gix::bstr::BStr;
use itertools::Itertools;

use crate::hours::{
util::{add_lines, remove_lines},
Expand All @@ -25,17 +24,25 @@ pub fn estimate_hours(
const MAX_COMMIT_DIFFERENCE_IN_MINUTES: f32 = 2.0 * MINUTES_PER_HOUR;
const FIRST_COMMIT_ADDITION_IN_MINUTES: f32 = 2.0 * MINUTES_PER_HOUR;

let hours_for_commits = commits.iter().map(|t| &t.1).rev().tuple_windows().fold(
0_f32,
|hours, (cur, next): (&gix::actor::SignatureRef<'_>, &gix::actor::SignatureRef<'_>)| {
let hours_for_commits = {
let mut hours = 0.0;

let mut commits = commits.iter().map(|t| &t.1).rev();
let mut cur = commits.next().expect("not a single commit found");

for next in commits {
let change_in_minutes = (next.time.seconds.saturating_sub(cur.time.seconds)) as f32 / MINUTES_PER_HOUR;
if change_in_minutes < MAX_COMMIT_DIFFERENCE_IN_MINUTES {
hours + change_in_minutes / MINUTES_PER_HOUR
hours += change_in_minutes / MINUTES_PER_HOUR
} else {
hours + (FIRST_COMMIT_ADDITION_IN_MINUTES / MINUTES_PER_HOUR)
hours += FIRST_COMMIT_ADDITION_IN_MINUTES / MINUTES_PER_HOUR
}
},
);

cur = next;
}

hours
};

let author = &commits[0].1;
let (files, lines) = (!stats.is_empty())
Expand Down
21 changes: 17 additions & 4 deletions gitoxide-core/src/hours/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::sync::atomic::{AtomicUsize, Ordering};

use gix::bstr::{BStr, ByteSlice};
use itertools::Itertools;

use crate::hours::core::HOURS_PER_WORKDAY;

Expand Down Expand Up @@ -53,9 +52,23 @@ impl WorkByPerson {
) -> std::io::Result<()> {
writeln!(
out,
"{} <{}>",
self.name.iter().join(", "),
self.email.iter().join(", ")
"{names} <{mails}>",
names = self
.name
.iter()
// BStr does not impl slice::Join
.map(|s| s.to_string())
.collect::<Vec<_>>()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could write an iterator for BStr to avoid this allocation (and the .to_string())?

Given that gix uses BStr a lot, I think it might worth the trouble.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not yet sure what the precise missing bound was as to why BStr does not impl Join. Given that the results are probably short and are directly written to the user it seemed fine to convert them to Strings but that might be wrong (?).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not yet sure what the precise missing bound was as to why BStr does not impl Join.

std::slice::join only accept types that impl Borrow<str>, BStr doesn't implement it.

Given that the results are probably short and are directly written to the user it seemed fine to convert them to Strings but that might be wrong (?).

True, though I still think achieving zero-allocation is better.

It probably won't take that much effect given that BStr implements std::fmt::Display, so basically you just need to re-implement join from itertool.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I inlined the join impl which was fairly compact.

.as_slice()
.join(", "),
mails = self
.email
.iter()
// BStr does not impl slice::Join
.map(|s| s.to_string())
.collect::<Vec<_>>()
.as_slice()
.join(", ")
)?;
writeln!(out, "{} commits found", self.num_commits)?;
writeln!(
Expand Down