-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathstatus.rs
135 lines (119 loc) · 4.43 KB
/
status.rs
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use clap::{App, Arg, SubCommand};
use colored::*;
use git2::{Error as GitError, Repository, StatusOptions};
use regex::Regex;
use std::path::PathBuf;
use crate::dir::DirectoryTreeOptions;
use crate::git::GitAction;
use crate::input_args::InputArgs;
use crate::progress::{ProgressReporter, ProgressTracker};
pub fn sub_command<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("status")
.arg(
Arg::with_name("PATH")
.short("f")
.takes_value(true)
.help("path at which to create the local repo"),
)
.arg(
Arg::with_name("traverse-hidden")
.short("i")
.help("traverse through hidden directories also"),
)
}
pub fn status(args: InputArgs, filter_list: Vec<Regex>) {
let matches = args.get_matches();
let filter_hidden = matches.is_present("traverse-hidden");
let dir_tree_with_options = DirectoryTreeOptions {
filter_list,
filter_hidden,
};
let root_path = args.get_root_path("PATH");
let root = root_path
.to_str()
.expect(format!("{}", "Error in converting directory to string".red()).as_str());
let multi_bars = ProgressTracker::new(matches.value_of("jobs").and_then(|e| e.parse().ok()));
dir_tree_with_options
.process_directories(root)
.flat_map(|dir| {
dir.ok().and_then(|d| {
if d.file_name().eq(".git") {
d.path().parent().map(|e| e.to_path_buf())
} else {
None
}
})
})
.map(|dir| GitStatus {
dir,
root: root.to_string()
})
.for_each(|status| multi_bars.start_task(status));
multi_bars.join().unwrap();
}
pub struct GitStatus {
dir: PathBuf,
root: String
}
impl<'a> GitAction for GitStatus {
fn get_name(&self) -> String {
Self::relative_path(&self.dir, &self.root)
}
fn git_action(&mut self, _progress: &ProgressReporter) -> Result<String, GitError> {
let mut opts = StatusOptions::new();
opts.include_ignored(true)
.include_untracked(true)
.recurse_untracked_dirs(false)
.exclude_submodules(false);
let repo = Repository::open(self.dir.clone())?;
let git_statuses = repo.statuses(Some(&mut opts))?;
let mut statuses_in_dir = vec![];
for entry in git_statuses
.iter()
.filter(|e| e.status() != git2::Status::CURRENT)
{
let status = &entry.status();
if git2::Status::is_wt_new(status) {
statuses_in_dir.push("new files".to_string());
};
if git2::Status::is_wt_deleted(status) {
statuses_in_dir.push("deletions".to_string());
};
if git2::Status::is_wt_renamed(status) {
statuses_in_dir.push("renames".to_string());
};
if git2::Status::is_wt_typechange(status) {
statuses_in_dir.push("typechanges".to_string());
};
if git2::Status::is_wt_modified(status) {
statuses_in_dir.push("modifications".to_string());
};
}
// Adapted from @Kurt-Bonatz in https://github.com/rust-lang/git2-rs/issues/332#issuecomment-408453956
if repo.revparse_single("HEAD").is_ok() {
let head_ref = repo.revparse_single("HEAD").expect("HEAD not found").id();
let (is_ahead, is_behind) = repo
.revparse_ext("@{u}")
.ok()
.and_then(|(upstream, _)| repo.graph_ahead_behind(head_ref, upstream.id()).ok())
.unwrap_or((0, 0));
if is_ahead > 0 {
let push_string = format!("{} ahead", is_ahead);
let push_string_colored = format!("{}", push_string.blue());
statuses_in_dir.push(push_string_colored);
}
if is_behind > 0 {
let pull_string = format!("{} behind", is_behind);
let pull_string_colored = format!("{}", pull_string.yellow());
statuses_in_dir.push(pull_string_colored);
}
}
Ok(if statuses_in_dir.is_empty() {
"no changes".green().to_string()
} else {
statuses_in_dir.sort();
statuses_in_dir.dedup();
statuses_in_dir.join(", ").red().to_string()
})
}
}