1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use crate::advisory::Date;
use crate::error::Error;
use git2::Time;
use std::ops::Add;
use std::str::FromStr;
use std::{
cmp::{max, min},
collections::HashMap,
path::PathBuf,
};
use super::GitPath;
#[cfg_attr(docsrs, doc(cfg(feature = "osv-export")))]
pub struct GitModificationTimes {
mtimes: HashMap<PathBuf, Time>,
ctimes: HashMap<PathBuf, Time>,
}
impl GitModificationTimes {
pub fn new(repo: &super::Repository) -> Result<Self, Error> {
let mut mtimes: HashMap<PathBuf, Time> = HashMap::new();
let mut ctimes: HashMap<PathBuf, Time> = HashMap::new();
let repo = git2::Repository::open(repo.path())?;
let mut revwalk = repo.revwalk()?;
revwalk.set_sorting(git2::Sort::TIME)?;
revwalk.push_head()?;
for commit_id in revwalk {
let commit_id = commit_id?;
let commit = repo.find_commit(commit_id)?;
if commit.parent_count() <= 1 {
let tree = commit.tree()?;
let prev_tree = match commit.parent_count() {
1 => Some(commit.parent(0)?.tree()?), 0 => None, _ => unreachable!(), };
let diff = repo.diff_tree_to_tree(prev_tree.as_ref(), Some(&tree), None)?;
for delta in diff.deltas() {
let file_path = delta.new_file().path().unwrap();
let file_mod_time = commit.time();
mtimes
.entry(file_path.to_owned())
.and_modify(|t| *t = max(*t, file_mod_time))
.or_insert(file_mod_time);
ctimes
.entry(file_path.to_owned())
.and_modify(|t| *t = min(*t, file_mod_time))
.or_insert(file_mod_time);
}
}
}
Ok(GitModificationTimes { mtimes, ctimes })
}
pub fn for_path(&self, path: GitPath<'_>) -> &Time {
self.mtimes.get(path.path()).unwrap()
}
pub fn mdate_for_path(&self, path: GitPath<'_>) -> Date {
Date::from_str(&Self::git2_time_to_date(
self.mtimes.get(path.path()).unwrap(),
))
.unwrap()
}
pub fn cdate_for_path(&self, path: GitPath<'_>) -> Date {
Date::from_str(&Self::git2_time_to_date(
self.ctimes.get(path.path()).unwrap(),
))
.unwrap()
}
fn git2_time_to_date(git_timestamp: &Time) -> String {
let unix_timestamp: u64 = git_timestamp.seconds().try_into().unwrap();
let duration_from_epoch = std::time::Duration::from_secs(unix_timestamp);
let mut time =
humantime::format_rfc3339(std::time::UNIX_EPOCH.add(duration_from_epoch)).to_string();
time.truncate(10);
time
}
}