2021-01-24 20:19:22 +00:00
|
|
|
|
use once_cell::sync::OnceCell;
|
2021-03-23 16:45:27 +00:00
|
|
|
|
use regex::Regex;
|
2019-05-14 04:43:11 +00:00
|
|
|
|
|
2019-10-26 06:20:20 +00:00
|
|
|
|
use super::{Context, Module, RootModuleConfig};
|
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
use crate::configs::git_status::GitStatusConfig;
|
|
|
|
|
use crate::formatter::StringFormatter;
|
|
|
|
|
use crate::segment::Segment;
|
2021-08-23 16:49:30 +00:00
|
|
|
|
use std::ffi::OsStr;
|
2021-01-24 20:19:22 +00:00
|
|
|
|
use std::sync::Arc;
|
2020-07-07 22:45:32 +00:00
|
|
|
|
|
|
|
|
|
const ALL_STATUS_FORMAT: &str = "$conflicted$stashed$deleted$renamed$modified$staged$untracked";
|
2019-05-14 04:43:11 +00:00
|
|
|
|
|
2019-07-19 20:18:52 +00:00
|
|
|
|
/// Creates a module with the Git branch in the current directory
|
2019-05-14 04:43:11 +00:00
|
|
|
|
///
|
|
|
|
|
/// Will display the branch name if the current directory is a git repo
|
|
|
|
|
/// By default, the following symbols will be used to represent the repo's status:
|
|
|
|
|
/// - `=` – This branch has merge conflicts
|
|
|
|
|
/// - `⇡` – This branch is ahead of the branch being tracked
|
2019-07-19 20:18:52 +00:00
|
|
|
|
/// - `⇣` – This branch is behind of the branch being tracked
|
2019-05-14 04:43:11 +00:00
|
|
|
|
/// - `⇕` – This branch has diverged from the branch being tracked
|
2021-08-07 17:22:00 +00:00
|
|
|
|
/// - `` – This branch is up-to-date with the branch being tracked
|
2019-05-14 04:43:11 +00:00
|
|
|
|
/// - `?` — There are untracked files in the working directory
|
2019-07-19 20:18:52 +00:00
|
|
|
|
/// - `$` — A stash exists for the local repository
|
2019-05-14 04:43:11 +00:00
|
|
|
|
/// - `!` — There are file modifications in the working directory
|
|
|
|
|
/// - `+` — A new file has been added to the staging area
|
|
|
|
|
/// - `»` — A renamed file has been added to the staging area
|
|
|
|
|
/// - `✘` — A file's deletion has been added to the staging area
|
2019-07-02 20:12:53 +00:00
|
|
|
|
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
2019-09-09 23:14:38 +00:00
|
|
|
|
let mut module = context.new_module("git_status");
|
2019-10-26 06:20:20 +00:00
|
|
|
|
let config: GitStatusConfig = GitStatusConfig::try_load(module.config);
|
2019-09-05 04:09:51 +00:00
|
|
|
|
|
2021-12-30 21:12:53 +00:00
|
|
|
|
let info = Arc::new(GitStatusInfo::load(context, config.clone()));
|
|
|
|
|
|
2021-08-14 13:19:43 +00:00
|
|
|
|
//Return None if not in git repository
|
|
|
|
|
context.get_repo().ok()?;
|
2022-03-07 03:18:23 +00:00
|
|
|
|
if let Some(git_status) = git_status_wsl(context, &config) {
|
|
|
|
|
if git_status.is_empty() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
module.set_segments(Segment::from_text(None, git_status));
|
|
|
|
|
return Some(module);
|
|
|
|
|
}
|
2021-08-14 13:19:43 +00:00
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
|
|
|
|
formatter
|
|
|
|
|
.map_meta(|variable, _| match variable {
|
|
|
|
|
"all_status" => Some(ALL_STATUS_FORMAT),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.map_style(|variable: &str| match variable {
|
|
|
|
|
"style" => Some(Ok(config.style)),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.map_variables_to_segments(|variable: &str| {
|
|
|
|
|
let info = Arc::clone(&info);
|
|
|
|
|
let segments = match variable {
|
|
|
|
|
"stashed" => info.get_stashed().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.stashed, "git_status.stashed", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
"ahead_behind" => info.get_ahead_behind().and_then(|(ahead, behind)| {
|
2021-08-07 17:22:00 +00:00
|
|
|
|
let (ahead, behind) = (ahead?, behind?);
|
2020-07-07 22:45:32 +00:00
|
|
|
|
if ahead > 0 && behind > 0 {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_text(
|
|
|
|
|
config.diverged,
|
|
|
|
|
"git_status.diverged",
|
|
|
|
|
context,
|
|
|
|
|
|variable| match variable {
|
2020-07-07 22:45:32 +00:00
|
|
|
|
"ahead_count" => Some(ahead.to_string()),
|
|
|
|
|
"behind_count" => Some(behind.to_string()),
|
|
|
|
|
_ => None,
|
2021-11-01 21:18:45 +00:00
|
|
|
|
},
|
|
|
|
|
)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
} else if ahead > 0 && behind == 0 {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.ahead, "git_status.ahead", context, ahead)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
} else if behind > 0 && ahead == 0 {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.behind, "git_status.behind", context, behind)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
} else {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_symbol(config.up_to_date, "git_status.up_to_date", context)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
"conflicted" => info.get_conflicted().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.conflicted, "git_status.conflicted", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
"deleted" => info.get_deleted().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.deleted, "git_status.deleted", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
"renamed" => info.get_renamed().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.renamed, "git_status.renamed", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
"modified" => info.get_modified().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.modified, "git_status.modified", context, count)
|
|
|
|
|
}),
|
|
|
|
|
"staged" => info.get_staged().and_then(|count| {
|
|
|
|
|
format_count(config.staged, "git_status.staged", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
"untracked" => info.get_untracked().and_then(|count| {
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_count(config.untracked, "git_status.untracked", context, count)
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}),
|
|
|
|
|
_ => None,
|
|
|
|
|
};
|
|
|
|
|
segments.map(Ok)
|
|
|
|
|
})
|
2021-11-01 21:18:45 +00:00
|
|
|
|
.parse(None, Some(context))
|
2020-07-07 22:45:32 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
module.set_segments(match parsed {
|
|
|
|
|
Ok(segments) => {
|
|
|
|
|
if segments.is_empty() {
|
|
|
|
|
return None;
|
|
|
|
|
} else {
|
|
|
|
|
segments
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(error) => {
|
|
|
|
|
log::warn!("Error in module `git_status`:\n{}", error);
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Some(module)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct GitStatusInfo<'a> {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
context: &'a Context<'a>,
|
2021-12-30 21:12:53 +00:00
|
|
|
|
config: GitStatusConfig<'a>,
|
2021-01-24 20:19:22 +00:00
|
|
|
|
repo_status: OnceCell<Option<RepoStatus>>,
|
|
|
|
|
stashed_count: OnceCell<Option<usize>>,
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> GitStatusInfo<'a> {
|
2021-12-30 21:12:53 +00:00
|
|
|
|
pub fn load(context: &'a Context, config: GitStatusConfig<'a>) -> Self {
|
2020-07-07 22:45:32 +00:00
|
|
|
|
Self {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
context,
|
2021-12-30 21:12:53 +00:00
|
|
|
|
config,
|
2021-01-24 20:19:22 +00:00
|
|
|
|
repo_status: OnceCell::new(),
|
|
|
|
|
stashed_count: OnceCell::new(),
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-07 17:22:00 +00:00
|
|
|
|
pub fn get_ahead_behind(&self) -> Option<(Option<usize>, Option<usize>)> {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
self.get_repo_status().map(|data| (data.ahead, data.behind))
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-24 20:19:22 +00:00
|
|
|
|
pub fn get_repo_status(&self) -> &Option<RepoStatus> {
|
2021-07-05 21:19:09 +00:00
|
|
|
|
self.repo_status
|
2021-12-30 21:12:53 +00:00
|
|
|
|
.get_or_init(|| match get_repo_status(self.context, &self.config) {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
Some(repo_status) => Some(repo_status),
|
|
|
|
|
None => {
|
|
|
|
|
log::debug!("get_repo_status: git status execution failed");
|
2020-07-07 22:45:32 +00:00
|
|
|
|
None
|
|
|
|
|
}
|
2021-07-05 21:19:09 +00:00
|
|
|
|
})
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
2019-09-05 04:09:51 +00:00
|
|
|
|
|
2021-01-24 20:19:22 +00:00
|
|
|
|
pub fn get_stashed(&self) -> &Option<usize> {
|
2021-07-05 21:19:09 +00:00
|
|
|
|
self.stashed_count
|
|
|
|
|
.get_or_init(|| match get_stashed_count(self.context) {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
Some(stashed_count) => Some(stashed_count),
|
|
|
|
|
None => {
|
|
|
|
|
log::debug!("get_stashed_count: git stash execution failed");
|
2020-07-07 22:45:32 +00:00
|
|
|
|
None
|
|
|
|
|
}
|
2021-07-05 21:19:09 +00:00
|
|
|
|
})
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
pub fn get_conflicted(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.conflicted)
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
pub fn get_deleted(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.deleted)
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
pub fn get_renamed(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.renamed)
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
2019-09-09 23:14:38 +00:00
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
pub fn get_modified(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.modified)
|
|
|
|
|
}
|
2019-05-14 04:43:11 +00:00
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
pub fn get_staged(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.staged)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_untracked(&self) -> Option<usize> {
|
|
|
|
|
self.get_repo_status().map(|data| data.untracked)
|
2019-10-26 06:20:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets the number of files in various git states (staged, modified, deleted, etc...)
|
2021-12-30 21:12:53 +00:00
|
|
|
|
fn get_repo_status(context: &Context, config: &GitStatusConfig) -> Option<RepoStatus> {
|
2020-10-14 16:12:41 +00:00
|
|
|
|
log::debug!("New repo status created");
|
2019-08-28 03:11:42 +00:00
|
|
|
|
|
2020-07-07 22:45:32 +00:00
|
|
|
|
let mut repo_status = RepoStatus::default();
|
2021-12-30 21:12:53 +00:00
|
|
|
|
let mut args = vec![
|
|
|
|
|
OsStr::new("-C"),
|
|
|
|
|
context.current_dir.as_os_str(),
|
|
|
|
|
OsStr::new("--no-optional-locks"),
|
|
|
|
|
OsStr::new("status"),
|
|
|
|
|
OsStr::new("--porcelain=2"),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// for performance reasons, only pass flags if necessary...
|
|
|
|
|
let has_ahead_behind = !config.ahead.is_empty() || !config.behind.is_empty();
|
|
|
|
|
let has_up_to_date_diverged = !config.up_to_date.is_empty() || !config.diverged.is_empty();
|
|
|
|
|
if has_ahead_behind || has_up_to_date_diverged {
|
|
|
|
|
args.push(OsStr::new("--branch"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ... and add flags that omit information the user doesn't want
|
|
|
|
|
let has_untracked = !config.untracked.is_empty();
|
|
|
|
|
if !has_untracked {
|
|
|
|
|
args.push(OsStr::new("--untracked-files=no"));
|
|
|
|
|
}
|
|
|
|
|
if config.ignore_submodules {
|
|
|
|
|
args.push(OsStr::new("--ignore-submodules=dirty"));
|
|
|
|
|
} else if !has_untracked {
|
|
|
|
|
args.push(OsStr::new("--ignore-submodules=untracked"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let status_output = context.exec_cmd("git", &args)?;
|
2021-03-23 16:45:27 +00:00
|
|
|
|
let statuses = status_output.stdout.lines();
|
|
|
|
|
|
|
|
|
|
statuses.for_each(|status| {
|
|
|
|
|
if status.starts_with("# branch.ab ") {
|
|
|
|
|
repo_status.set_ahead_behind(status);
|
|
|
|
|
} else if !status.starts_with('#') {
|
|
|
|
|
repo_status.add(status);
|
|
|
|
|
}
|
|
|
|
|
});
|
2020-07-07 22:45:32 +00:00
|
|
|
|
|
2021-03-23 16:45:27 +00:00
|
|
|
|
Some(repo_status)
|
2019-12-29 03:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 21:19:09 +00:00
|
|
|
|
fn get_stashed_count(context: &Context) -> Option<usize> {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
let stash_output = context.exec_cmd(
|
|
|
|
|
"git",
|
|
|
|
|
&[
|
2021-08-23 16:49:30 +00:00
|
|
|
|
OsStr::new("-C"),
|
|
|
|
|
context.current_dir.as_os_str(),
|
|
|
|
|
OsStr::new("--no-optional-locks"),
|
|
|
|
|
OsStr::new("stash"),
|
|
|
|
|
OsStr::new("list"),
|
2021-03-23 16:45:27 +00:00
|
|
|
|
],
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
Some(stash_output.stdout.trim().lines().count())
|
2019-05-14 04:43:11 +00:00
|
|
|
|
}
|
2019-10-26 06:20:20 +00:00
|
|
|
|
|
|
|
|
|
#[derive(Default, Debug, Copy, Clone)]
|
|
|
|
|
struct RepoStatus {
|
2021-08-07 17:22:00 +00:00
|
|
|
|
ahead: Option<usize>,
|
|
|
|
|
behind: Option<usize>,
|
2019-10-26 06:20:20 +00:00
|
|
|
|
conflicted: usize,
|
|
|
|
|
deleted: usize,
|
|
|
|
|
renamed: usize,
|
|
|
|
|
modified: usize,
|
|
|
|
|
staged: usize,
|
|
|
|
|
untracked: usize,
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RepoStatus {
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn is_deleted(short_status: &str) -> bool {
|
2021-03-23 16:45:27 +00:00
|
|
|
|
// is_wt_deleted || is_index_deleted
|
2021-08-18 17:55:40 +00:00
|
|
|
|
short_status.contains('D')
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn is_modified(short_status: &str) -> bool {
|
|
|
|
|
// is_wt_modified || is_wt_added
|
|
|
|
|
short_status.ends_with('M') || short_status.ends_with('A')
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn is_staged(short_status: &str) -> bool {
|
|
|
|
|
// is_index_modified || is_index_added
|
|
|
|
|
short_status.starts_with('M') || short_status.starts_with('A')
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn parse_normal_status(&mut self, short_status: &str) {
|
|
|
|
|
if Self::is_deleted(short_status) {
|
|
|
|
|
self.deleted += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if Self::is_modified(short_status) {
|
|
|
|
|
self.modified += 1;
|
|
|
|
|
}
|
2020-07-07 22:45:32 +00:00
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
if Self::is_staged(short_status) {
|
|
|
|
|
self.staged += 1;
|
|
|
|
|
}
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-23 16:45:27 +00:00
|
|
|
|
fn add(&mut self, s: &str) {
|
2021-08-18 17:55:40 +00:00
|
|
|
|
match s.chars().next() {
|
|
|
|
|
Some('1') => self.parse_normal_status(&s[2..4]),
|
|
|
|
|
Some('2') => {
|
|
|
|
|
self.renamed += 1;
|
|
|
|
|
self.parse_normal_status(&s[2..4])
|
|
|
|
|
}
|
|
|
|
|
Some('u') => self.conflicted += 1,
|
|
|
|
|
Some('?') => self.untracked += 1,
|
|
|
|
|
Some('!') => (),
|
|
|
|
|
Some(_) => log::error!("Unknown line type in git status output"),
|
|
|
|
|
None => log::error!("Missing line type in git status output"),
|
|
|
|
|
}
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
2021-03-23 16:45:27 +00:00
|
|
|
|
|
|
|
|
|
fn set_ahead_behind(&mut self, s: &str) {
|
|
|
|
|
let re = Regex::new(r"branch\.ab \+([0-9]+) \-([0-9]+)").unwrap();
|
|
|
|
|
|
|
|
|
|
if let Some(caps) = re.captures(s) {
|
2021-08-07 17:22:00 +00:00
|
|
|
|
self.ahead = caps.get(1).unwrap().as_str().parse::<usize>().ok();
|
|
|
|
|
self.behind = caps.get(2).unwrap().as_str().parse::<usize>().ok();
|
2021-03-23 16:45:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-07 22:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 21:18:45 +00:00
|
|
|
|
fn format_text<F>(
|
|
|
|
|
format_str: &str,
|
|
|
|
|
config_path: &str,
|
|
|
|
|
context: &Context,
|
|
|
|
|
mapper: F,
|
|
|
|
|
) -> Option<Vec<Segment>>
|
2020-07-07 22:45:32 +00:00
|
|
|
|
where
|
|
|
|
|
F: Fn(&str) -> Option<String> + Send + Sync,
|
|
|
|
|
{
|
|
|
|
|
if let Ok(formatter) = StringFormatter::new(format_str) {
|
|
|
|
|
formatter
|
|
|
|
|
.map(|variable| mapper(variable).map(Ok))
|
2021-11-01 21:18:45 +00:00
|
|
|
|
.parse(None, Some(context))
|
2020-07-07 22:45:32 +00:00
|
|
|
|
.ok()
|
|
|
|
|
} else {
|
2020-09-28 20:38:50 +00:00
|
|
|
|
log::warn!("Error parsing format string `{}`", &config_path);
|
2020-07-07 22:45:32 +00:00
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 21:18:45 +00:00
|
|
|
|
fn format_count(
|
|
|
|
|
format_str: &str,
|
|
|
|
|
config_path: &str,
|
|
|
|
|
context: &Context,
|
|
|
|
|
count: usize,
|
|
|
|
|
) -> Option<Vec<Segment>> {
|
2020-07-07 22:45:32 +00:00
|
|
|
|
if count == 0 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 21:18:45 +00:00
|
|
|
|
format_text(
|
|
|
|
|
format_str,
|
|
|
|
|
config_path,
|
|
|
|
|
context,
|
|
|
|
|
|variable| match variable {
|
|
|
|
|
"count" => Some(count.to_string()),
|
|
|
|
|
_ => None,
|
|
|
|
|
},
|
|
|
|
|
)
|
2019-10-26 06:20:20 +00:00
|
|
|
|
}
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-11-01 21:18:45 +00:00
|
|
|
|
fn format_symbol(format_str: &str, config_path: &str, context: &Context) -> Option<Vec<Segment>> {
|
|
|
|
|
format_text(format_str, config_path, context, |_variable| None)
|
2021-08-07 17:22:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 03:18:23 +00:00
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
fn git_status_wsl(context: &Context, conf: &GitStatusConfig) -> Option<String> {
|
|
|
|
|
use crate::utils::create_command;
|
|
|
|
|
use nix::sys::utsname::uname;
|
|
|
|
|
use std::env;
|
|
|
|
|
use std::io::ErrorKind;
|
|
|
|
|
|
|
|
|
|
let starship_exe = conf.windows_starship?;
|
|
|
|
|
|
|
|
|
|
// Ensure this is WSL
|
|
|
|
|
// This is lowercase in WSL1 and uppercase in WSL2, just skip the first letter
|
|
|
|
|
if !uname().release().contains("icrosoft") {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log::trace!("Using WSL mode");
|
|
|
|
|
|
|
|
|
|
// Get Windows path
|
|
|
|
|
let winpath = match create_command("wslpath")
|
|
|
|
|
.map(|mut c| {
|
|
|
|
|
c.arg("-w").arg(&context.current_dir);
|
|
|
|
|
c
|
|
|
|
|
})
|
|
|
|
|
.and_then(|mut c| c.output())
|
|
|
|
|
{
|
|
|
|
|
Ok(r) => r,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
// Not found might means this might not be WSL after all
|
|
|
|
|
let level = if e.kind() == ErrorKind::NotFound {
|
|
|
|
|
log::Level::Debug
|
|
|
|
|
} else {
|
|
|
|
|
log::Level::Error
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
log::log!(level, "Failed to get Windows path:\n{:?}", e);
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let winpath = match std::str::from_utf8(&winpath.stdout) {
|
|
|
|
|
Ok(r) => r.trim_end(),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
log::error!("Failed to parse Windows path:\n{:?}", e);
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
log::trace!("Windows path: {}", winpath);
|
|
|
|
|
|
|
|
|
|
// In Windows or Linux dir?
|
|
|
|
|
if winpath.starts_with(r"\\wsl") {
|
|
|
|
|
log::trace!("Not a Windows path");
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get foreign starship to use WSL config
|
|
|
|
|
// https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
|
|
|
|
|
let wslenv = env::var("WSLENV")
|
|
|
|
|
.map(|e| e + ":STARSHIP_CONFIG/wp")
|
|
|
|
|
.unwrap_or_else(|_| "STARSHIP_CONFIG/wp".to_string());
|
|
|
|
|
|
|
|
|
|
let out = match create_command(starship_exe)
|
|
|
|
|
.map(|mut c| {
|
|
|
|
|
c.env(
|
|
|
|
|
"STARSHIP_CONFIG",
|
|
|
|
|
crate::config::get_config_path().unwrap_or_else(|| "/dev/null".to_string()),
|
|
|
|
|
)
|
|
|
|
|
.env("WSLENV", wslenv)
|
|
|
|
|
.args(&["module", "git_status", "--path", winpath]);
|
|
|
|
|
c
|
|
|
|
|
})
|
|
|
|
|
.and_then(|mut c| c.output())
|
|
|
|
|
{
|
|
|
|
|
Ok(r) => r,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
log::error!("Failed to run Git Status module on Windows:\n{}", e);
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match String::from_utf8(out.stdout) {
|
|
|
|
|
Ok(r) => Some(r),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
log::error!(
|
|
|
|
|
"Failed to parse Windows Git Status module status output:\n{}",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
|
|
|
fn git_status_wsl(_context: &Context, _conf: &GitStatusConfig) -> Option<String> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use ansi_term::{ANSIStrings, Color};
|
2021-08-23 16:49:30 +00:00
|
|
|
|
use std::ffi::OsStr;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
use std::fs::{self, File};
|
2021-08-18 17:55:40 +00:00
|
|
|
|
use std::io::{self, prelude::*};
|
2020-08-07 19:13:12 +00:00
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
|
2021-07-16 19:20:59 +00:00
|
|
|
|
use crate::utils::create_command;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
/// Right after the calls to git the filesystem state may not have finished
|
|
|
|
|
/// updating yet causing some of the tests to fail. These barriers are placed
|
|
|
|
|
/// after each call to git.
|
|
|
|
|
/// This barrier is windows-specific though other operating systems may need it
|
|
|
|
|
/// in the future.
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
|
fn barrier() {}
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn barrier() {
|
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 20:08:17 +00:00
|
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2020-08-07 19:13:12 +00:00
|
|
|
|
fn format_output(symbols: &str) -> Option<String> {
|
|
|
|
|
Some(format!(
|
|
|
|
|
"{} ",
|
|
|
|
|
Color::Red.bold().paint(format!("[{}]", symbols))
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn show_nothing_on_empty_dir() -> io::Result<()> {
|
|
|
|
|
let repo_dir = tempfile::tempdir()?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = None;
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_behind() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
behind(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇣");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_behind_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
behind(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
behind = "⇣$count"
|
|
|
|
|
})
|
|
|
|
|
.path(repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇣1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_ahead() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
File::create(repo_dir.path().join("readme.md"))?.sync_all()?;
|
2021-07-29 18:27:46 +00:00
|
|
|
|
ahead(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇡");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_ahead_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
File::create(repo_dir.path().join("readme.md"))?.sync_all()?;
|
2021-07-29 18:27:46 +00:00
|
|
|
|
ahead(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
ahead="⇡$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇡1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_diverged() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
diverge(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇕");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_diverged_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
diverge(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
diverged=r"⇕⇡$ahead_count⇣$behind_count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("⇕⇡1⇣1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-07 17:22:00 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_up_to_date_with_upstream() -> io::Result<()> {
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
up_to_date="✓"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("✓");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_conflicted() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_conflict(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("=");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_conflicted_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_conflict(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
conflicted = "=$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("=1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_untracked_file() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_untracked(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("?");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_untracked_file_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_untracked(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
untracked = "?$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("?1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn doesnt_show_untracked_file_if_disabled() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_untracked(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["config", "status.showUntrackedFiles", "no"])
|
|
|
|
|
.current_dir(repo_dir.path())
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = None;
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_stashed() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_stash(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["reset", "--hard", "HEAD"])
|
|
|
|
|
.current_dir(repo_dir.path())
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("$");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_stashed_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_stash(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["reset", "--hard", "HEAD"])
|
|
|
|
|
.current_dir(repo_dir.path())
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
stashed = r"\$$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("$1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_modified() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_modified(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("!");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_modified_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_modified(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
modified = "!$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("!1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 23:31:18 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_added() -> io::Result<()> {
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_added(repo_dir.path())?;
|
2021-05-07 23:31:18 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("!");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_staged_file() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_staged(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("+");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_staged_file_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_staged(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
staged = "+[$count](green)"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = Some(format!(
|
|
|
|
|
"{} ",
|
|
|
|
|
ANSIStrings(&[
|
|
|
|
|
Color::Red.bold().paint("[+"),
|
|
|
|
|
Color::Green.paint("1"),
|
|
|
|
|
Color::Red.bold().paint("]"),
|
|
|
|
|
])
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_staged_and_modified_file() -> io::Result<()> {
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
|
|
|
|
create_staged_and_modified(repo_dir.path())?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("!+");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_renamed_file() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_renamed(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("»");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_renamed_file_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_renamed(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
renamed = "»$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("»1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_renamed_and_modified_file() -> io::Result<()> {
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
|
|
|
|
create_renamed_and_modified(repo_dir.path())?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("»!");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn shows_deleted_file() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_deleted(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("✘");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn shows_deleted_file_with_count() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
2021-07-29 18:27:46 +00:00
|
|
|
|
create_deleted(repo_dir.path())?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
deleted = "✘$count"
|
|
|
|
|
})
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("✘1");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn doesnt_show_ignored_file() -> io::Result<()> {
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
|
|
|
|
create_staged_and_ignored(repo_dir.path())?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("+");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 21:19:09 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn worktree_in_different_dir() -> io::Result<()> {
|
|
|
|
|
let worktree_dir = tempfile::tempdir()?;
|
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2021-07-05 21:19:09 +00:00
|
|
|
|
.args(&[
|
2021-08-23 16:49:30 +00:00
|
|
|
|
OsStr::new("config"),
|
|
|
|
|
OsStr::new("core.worktree"),
|
|
|
|
|
worktree_dir.path().as_os_str(),
|
2021-07-05 21:19:09 +00:00
|
|
|
|
])
|
|
|
|
|
.current_dir(repo_dir.path())
|
|
|
|
|
.output()?;
|
|
|
|
|
|
|
|
|
|
File::create(worktree_dir.path().join("test_file"))?.sync_all()?;
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("✘?");
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
worktree_dir.close()?;
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
// Whenever a file is manually renamed, git itself ('git status') does not treat such file as renamed,
|
|
|
|
|
// but as untracked instead. The following test checks if manually deleted and manually renamed
|
|
|
|
|
// files are tracked by git_status module in the same way 'git status' does.
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore]
|
|
|
|
|
fn ignore_manually_renamed() -> io::Result<()> {
|
2021-03-25 20:03:19 +00:00
|
|
|
|
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
2020-08-07 19:13:12 +00:00
|
|
|
|
File::create(repo_dir.path().join("a"))?.sync_all()?;
|
|
|
|
|
File::create(repo_dir.path().join("b"))?.sync_all()?;
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["add", "--all"])
|
|
|
|
|
.current_dir(&repo_dir.path())
|
|
|
|
|
.output()?;
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-10-03 10:22:19 +00:00
|
|
|
|
.args(&["commit", "-m", "add new files", "--no-gpg-sign"])
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.current_dir(&repo_dir.path())
|
|
|
|
|
.output()?;
|
|
|
|
|
|
|
|
|
|
fs::remove_file(repo_dir.path().join("a"))?;
|
|
|
|
|
fs::rename(repo_dir.path().join("b"), repo_dir.path().join("c"))?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let actual = ModuleRenderer::new("git_status")
|
|
|
|
|
.path(&repo_dir.path())
|
|
|
|
|
.config(toml::toml! {
|
|
|
|
|
[git_status]
|
|
|
|
|
ahead = "A"
|
|
|
|
|
deleted = "D"
|
|
|
|
|
untracked = "U"
|
|
|
|
|
renamed = "R"
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let expected = format_output("DUA");
|
|
|
|
|
|
|
|
|
|
assert_eq!(actual, expected);
|
|
|
|
|
|
|
|
|
|
repo_dir.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn ahead(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("readme.md"))?.sync_all()?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-10-03 10:22:19 +00:00
|
|
|
|
.args(&["commit", "-am", "Update readme", "--no-gpg-sign"])
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.current_dir(&repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn behind(repo_dir: &Path) -> io::Result<()> {
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["reset", "--hard", "HEAD^"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn diverge(repo_dir: &Path) -> io::Result<()> {
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["reset", "--hard", "HEAD^"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
fs::write(repo_dir.join("Cargo.toml"), " ")?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-10-03 10:22:19 +00:00
|
|
|
|
.args(&["commit", "-am", "Update readme", "--no-gpg-sign"])
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_conflict(repo_dir: &Path) -> io::Result<()> {
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["reset", "--hard", "HEAD^"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
fs::write(repo_dir.join("readme.md"), "# goodbye")?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["add", "."])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-10-03 10:22:19 +00:00
|
|
|
|
.args(&["commit", "-m", "Change readme", "--no-gpg-sign"])
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["pull", "--rebase"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_stash(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("readme.md"))?.sync_all()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["stash", "--all"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_untracked(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("license"))?.sync_all()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 23:31:18 +00:00
|
|
|
|
fn create_added(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("license"))?.sync_all()?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2021-05-07 23:31:18 +00:00
|
|
|
|
.args(&["add", "-A", "-N"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
fn create_modified(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("readme.md"))?.sync_all()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_staged(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
File::create(repo_dir.join("license"))?.sync_all()?;
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["add", "."])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn create_staged_and_modified(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
let mut file = File::create(repo_dir.join("readme.md"))?;
|
|
|
|
|
file.sync_all()?;
|
|
|
|
|
|
|
|
|
|
create_command("git")?
|
|
|
|
|
.args(&["add", "."])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
writeln!(&mut file, "modified")?;
|
|
|
|
|
file.sync_all()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
fn create_renamed(repo_dir: &Path) -> io::Result<()> {
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["mv", "readme.md", "readme.md.bak"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
2021-07-16 19:20:59 +00:00
|
|
|
|
create_command("git")?
|
2020-08-07 19:13:12 +00:00
|
|
|
|
.args(&["add", "-A"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 17:55:40 +00:00
|
|
|
|
fn create_renamed_and_modified(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
create_command("git")?
|
|
|
|
|
.args(&["mv", "readme.md", "readme.md.bak"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
create_command("git")?
|
|
|
|
|
.args(&["add", "-A"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let mut file = File::create(repo_dir.join("readme.md.bak"))?;
|
|
|
|
|
writeln!(&mut file, "modified")?;
|
|
|
|
|
file.sync_all()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 19:13:12 +00:00
|
|
|
|
fn create_deleted(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
fs::remove_file(repo_dir.join("readme.md"))?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2021-08-18 17:55:40 +00:00
|
|
|
|
|
|
|
|
|
fn create_staged_and_ignored(repo_dir: &Path) -> io::Result<()> {
|
|
|
|
|
let mut file = File::create(repo_dir.join(".gitignore"))?;
|
|
|
|
|
writeln!(&mut file, "ignored.txt")?;
|
|
|
|
|
file.sync_all()?;
|
|
|
|
|
|
|
|
|
|
create_command("git")?
|
|
|
|
|
.args(&["add", ".gitignore"])
|
|
|
|
|
.current_dir(repo_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
let mut file = File::create(repo_dir.join("ignored.txt"))?;
|
|
|
|
|
writeln!(&mut file, "modified")?;
|
|
|
|
|
file.sync_all()?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2020-08-07 19:13:12 +00:00
|
|
|
|
}
|