|
1 |
| -use crate::onefetch::{error::*, utils::bytes_to_human_readable}; |
2 |
| -use chrono; |
3 |
| -use chrono::TimeZone; |
4 |
| -use chrono_humanize; |
5 |
| -use git2::{Commit, Repository, Time}; |
6 |
| -use std::process::Command; |
7 |
| -use std::str; |
8 |
| - |
9 |
| -pub fn get_git_history2(repo: &Repository, no_merges: bool) -> Result<Vec<Commit>> { |
10 |
| - let mut revwalk = repo.revwalk()?; |
11 |
| - revwalk.push_head()?; |
12 |
| - revwalk.set_sorting(git2::Sort::TIME)?; |
13 |
| - let commits: Vec<Commit> = revwalk |
14 |
| - .filter_map(|r| match r { |
15 |
| - Err(_) => return None, |
16 |
| - Ok(r) => { |
17 |
| - let commit = repo.find_commit(r).expect("Could not find commit"); |
18 |
| - if no_merges { |
19 |
| - let parents = commit.parents().len(); |
20 |
| - if parents > 1 { |
21 |
| - return None; |
22 |
| - } |
23 |
| - } |
24 |
| - Some(commit) |
25 |
| - } |
26 |
| - }) |
27 |
| - .collect(); |
| 1 | +use crate::onefetch::{error::*, utils}; |
| 2 | +use git2::{Commit, Repository}; |
28 | 3 |
|
29 |
| - Ok(commits) |
| 4 | +pub struct GitClient<'a> { |
| 5 | + repo: &'a Repository, |
| 6 | + commit_history: Vec<Commit<'a>>, |
30 | 7 | }
|
31 | 8 |
|
32 |
| -pub fn get_git_history(dir: &str, no_merges: bool) -> Result<Vec<String>> { |
33 |
| - let mut args = vec!["-C", dir, "log"]; |
34 |
| - if no_merges { |
35 |
| - args.push("--no-merges"); |
| 9 | +impl<'a> GitClient<'a> { |
| 10 | + pub fn new(repo: &Repository, no_merges: bool) -> Result<GitClient> { |
| 11 | + let commit_history = GitClient::get_git_history(repo, no_merges)?; |
| 12 | + Ok(GitClient { repo: repo, commit_history: commit_history }) |
36 | 13 | }
|
37 | 14 |
|
38 |
| - args.push("--pretty=%cr\t%ae\t%an"); |
| 15 | + pub fn get_creation_date(&self) -> Result<String> { |
| 16 | + let first_commit = self.commit_history.last(); |
39 | 17 |
|
40 |
| - let output = Command::new("git").args(args).output()?; |
41 |
| - |
42 |
| - let output = String::from_utf8_lossy(&output.stdout); |
43 |
| - Ok(output.lines().map(|x| x.to_string()).collect::<Vec<_>>()) |
44 |
| -} |
| 18 | + let output = match first_commit { |
| 19 | + Some(commit) => { |
| 20 | + let time = commit.time(); |
| 21 | + utils::git_time_to_human_time(&time) |
| 22 | + } |
| 23 | + None => "".into(), |
| 24 | + }; |
45 | 25 |
|
46 |
| -pub fn get_authors2(git_history: &Vec<Commit>, n: usize) -> Vec<(String, usize, usize)> { |
47 |
| - let mut authors = std::collections::HashMap::new(); |
48 |
| - let mut author_name_by_email = std::collections::HashMap::new(); |
49 |
| - let mut total_commits = 0; |
50 |
| - for commit in git_history { |
51 |
| - let author = commit.author(); |
52 |
| - let author_name = author.name().unwrap().to_string(); |
53 |
| - let author_email = author.email().unwrap(); |
54 |
| - |
55 |
| - let commit_count = authors.entry(author_email.to_string()).or_insert(0); |
56 |
| - author_name_by_email.entry(author_email.to_string()).or_insert(author_name); |
57 |
| - *commit_count += 1; |
58 |
| - total_commits += 1; |
| 26 | + Ok(output) |
59 | 27 | }
|
60 | 28 |
|
61 |
| - let mut authors: Vec<(String, usize)> = authors.into_iter().collect(); |
62 |
| - authors.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); |
| 29 | + pub fn get_number_of_commits(&self) -> String { |
| 30 | + let number_of_commits = self.commit_history.len(); |
| 31 | + number_of_commits.to_string() |
| 32 | + } |
63 | 33 |
|
64 |
| - authors.truncate(n); |
| 34 | + pub fn get_authors(&self, n: usize) -> Vec<(String, usize, usize)> { |
| 35 | + let mut authors = std::collections::HashMap::new(); |
| 36 | + let mut author_name_by_email = std::collections::HashMap::new(); |
| 37 | + let mut total_commits = 0; |
| 38 | + for commit in &self.commit_history { |
| 39 | + let author = commit.author(); |
| 40 | + let author_name = author.name().unwrap().to_string(); |
| 41 | + let author_email = author.email().unwrap(); |
| 42 | + |
| 43 | + let commit_count = authors.entry(author_email.to_string()).or_insert(0); |
| 44 | + author_name_by_email.entry(author_email.to_string()).or_insert(author_name); |
| 45 | + *commit_count += 1; |
| 46 | + total_commits += 1; |
| 47 | + } |
65 | 48 |
|
66 |
| - let authors: Vec<(String, usize, usize)> = authors |
67 |
| - .into_iter() |
68 |
| - .map(|(author, count)| { |
69 |
| - ( |
70 |
| - author_name_by_email.get(&author).unwrap().trim_matches('\'').to_string(), |
71 |
| - count, |
72 |
| - count * 100 / total_commits, |
73 |
| - ) |
74 |
| - }) |
75 |
| - .collect(); |
| 49 | + let mut authors: Vec<(String, usize)> = authors.into_iter().collect(); |
| 50 | + authors.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); |
76 | 51 |
|
77 |
| - authors |
78 |
| -} |
| 52 | + authors.truncate(n); |
79 | 53 |
|
80 |
| -pub fn get_date_of_last_commit2(git_history: &Vec<Commit>) -> Result<String> { |
81 |
| - let last_commit = git_history.first(); |
| 54 | + let authors: Vec<(String, usize, usize)> = authors |
| 55 | + .into_iter() |
| 56 | + .map(|(author, count)| { |
| 57 | + ( |
| 58 | + author_name_by_email.get(&author).unwrap().trim_matches('\'').to_string(), |
| 59 | + count, |
| 60 | + count * 100 / total_commits, |
| 61 | + ) |
| 62 | + }) |
| 63 | + .collect(); |
82 | 64 |
|
83 |
| - let output = match last_commit { |
84 |
| - Some(commit) => git_time_to_human_time(&commit.time()), |
85 |
| - None => "".into(), |
86 |
| - }; |
87 |
| - Ok(output) |
88 |
| -} |
| 65 | + authors |
| 66 | + } |
89 | 67 |
|
90 |
| -fn git_time_to_human_time(time: &Time) -> String { |
91 |
| - let (offset, _) = match time.offset_minutes() { |
92 |
| - n if n < 0 => (-n, '-'), |
93 |
| - n => (n, '+'), |
94 |
| - }; |
| 68 | + pub fn get_date_of_last_commit(&self) -> String { |
| 69 | + let last_commit = self.commit_history.first(); |
95 | 70 |
|
96 |
| - let offset = chrono::FixedOffset::west(offset); |
97 |
| - let dt_with_tz = offset.timestamp(time.seconds(), 0); |
98 |
| - let ht = chrono_humanize::HumanTime::from(dt_with_tz); |
99 |
| - format!("{}", ht.to_text_en(chrono_humanize::Accuracy::Rough, chrono_humanize::Tense::Past)) |
100 |
| -} |
| 71 | + let output = match last_commit { |
| 72 | + Some(commit) => utils::git_time_to_human_time(&commit.time()), |
| 73 | + None => "".into(), |
| 74 | + }; |
| 75 | + output |
| 76 | + } |
101 | 77 |
|
102 |
| -pub fn get_creation_date2(git_history: &Vec<Commit>) -> Result<String> { |
103 |
| - let first_commit = git_history.last(); |
| 78 | + // This collects the repo size excluding .git |
| 79 | + pub fn get_repo_size(&self) -> (String, u64) { |
| 80 | + let (repo_size, file_count) = match self.repo.index() { |
| 81 | + Ok(index) => { |
| 82 | + let mut repo_size: u128 = 0; |
| 83 | + let mut file_count: u64 = 0; |
| 84 | + for index_entry in index.iter() { |
| 85 | + file_count += 1; |
| 86 | + repo_size += index_entry.file_size as u128; |
| 87 | + } |
| 88 | + (repo_size, file_count) |
| 89 | + } |
| 90 | + Err(_) => (0, 0), |
| 91 | + }; |
104 | 92 |
|
105 |
| - let output = match first_commit { |
106 |
| - Some(commit) => { |
107 |
| - let time = commit.time(); |
108 |
| - git_time_to_human_time(&time) |
109 |
| - } |
110 |
| - None => "".into(), |
111 |
| - }; |
| 93 | + (utils::bytes_to_human_readable(repo_size), file_count) |
| 94 | + } |
112 | 95 |
|
113 |
| - Ok(output) |
114 |
| -} |
| 96 | + fn get_git_history(repo: &'a Repository, no_merges: bool) -> Result<Vec<Commit<'a>>> { |
| 97 | + let mut revwalk = repo.revwalk()?; |
| 98 | + revwalk.push_head()?; |
| 99 | + revwalk.set_sorting(git2::Sort::TIME)?; |
| 100 | + let commits: Vec<Commit<'a>> = revwalk |
| 101 | + .filter_map(|r| match r { |
| 102 | + Err(_) => return None, |
| 103 | + Ok(r) => { |
| 104 | + let commit = repo.find_commit(r).expect("Could not find commit"); |
| 105 | + if no_merges { |
| 106 | + let parents = commit.parents().len(); |
| 107 | + if parents > 1 { |
| 108 | + return None; |
| 109 | + } |
| 110 | + } |
| 111 | + Some(commit.to_owned()) |
| 112 | + } |
| 113 | + }) |
| 114 | + .collect(); |
115 | 115 |
|
116 |
| -pub fn get_number_of_commits2(git_history: &Vec<Commit>) -> String { |
117 |
| - let number_of_commits = git_history.len(); |
118 |
| - number_of_commits.to_string() |
| 116 | + Ok(commits) |
| 117 | + } |
119 | 118 | }
|
120 | 119 |
|
121 |
| -pub fn get_packed_size(repo_size: String, files_count: Option<u64>) -> Result<String> { |
| 120 | +// Should be moved to fmt::Display of Info |
| 121 | +pub fn get_packed_size(repo_size: String, files_count: u64) -> Result<String> { |
122 | 122 | match files_count {
|
123 |
| - Some(files_count) => { |
124 |
| - let res = format!("{} ({} files)", repo_size, files_count.to_string()); |
125 |
| - Ok(res) |
126 |
| - } |
127 |
| - None => { |
| 123 | + 0 => { |
128 | 124 | let res = repo_size;
|
129 | 125 | Ok(res.into())
|
130 | 126 | }
|
131 |
| - } |
132 |
| -} |
133 |
| - |
134 |
| -// This collects the repo size excluding .git |
135 |
| -pub fn get_repo_size2(repo: &Repository) -> String { |
136 |
| - let repo_size = match repo.index() { |
137 |
| - Ok(index) => { |
138 |
| - let mut repo_size: u128 = 0; |
139 |
| - for index_entry in index.iter() { |
140 |
| - println!("{}", str::from_utf8(&index_entry.path).unwrap()); |
141 |
| - repo_size += index_entry.file_size as u128; |
142 |
| - } |
143 |
| - repo_size |
144 |
| - } |
145 |
| - Err(_) => 0, |
146 |
| - }; |
147 |
| - |
148 |
| - bytes_to_human_readable(repo_size) |
149 |
| -} |
150 |
| - |
151 |
| -pub fn get_repo_size(dir: &str) -> String { |
152 |
| - let output = Command::new("git") |
153 |
| - .arg("-C") |
154 |
| - .arg(dir) |
155 |
| - .arg("count-objects") |
156 |
| - .arg("-vH") |
157 |
| - .output() |
158 |
| - .expect("Failed to execute git."); |
159 |
| - |
160 |
| - let output = String::from_utf8_lossy(&output.stdout); |
161 |
| - let lines = output.to_string(); |
162 |
| - let size_line = lines.split('\n').find(|line| line.starts_with("size-pack:")); |
163 |
| - |
164 |
| - let repo_size = match size_line { |
165 |
| - None => String::new(), |
166 |
| - Some(size_str) => String::from(&(size_str[11..])), |
167 |
| - }; |
168 |
| - repo_size |
169 |
| -} |
170 |
| - |
171 |
| -pub fn get_files_count(dir: &str) -> Option<u64> { |
172 |
| - let output = Command::new("git") |
173 |
| - .arg("-C") |
174 |
| - .arg(dir) |
175 |
| - .arg("ls-files") |
176 |
| - .output() |
177 |
| - .expect("Failed to execute git."); |
178 |
| - // To check if command executed successfully or not |
179 |
| - let error = &output.stderr; |
180 |
| - |
181 |
| - if error.is_empty() { |
182 |
| - let output = String::from_utf8_lossy(&output.stdout); |
183 |
| - |
184 |
| - let lines = output.to_string(); |
185 |
| - let files_list = lines.split('\n'); |
186 |
| - let mut files_count: u64 = 0; |
187 |
| - for _file in files_list { |
188 |
| - files_count += 1; |
| 127 | + _ => { |
| 128 | + let res = format!("{} ({} files)", repo_size, files_count.to_string()); |
| 129 | + Ok(res) |
189 | 130 | }
|
190 |
| - files_count -= 1; // As splitting giving one line extra(blank). |
191 |
| - Some(files_count) |
192 |
| - } else { |
193 |
| - None |
194 | 131 | }
|
195 | 132 | }
|
0 commit comments