-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
567 additions
and
505 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Result; | ||
use log::error; | ||
|
||
use crate::state::{Repository, State}; | ||
|
||
pub fn add(state: &mut State, repos: Vec<PathBuf>) -> Result<()> { | ||
// Just print the known repositories, if no arguments have been supplied. | ||
if repos.is_empty() { | ||
println!("Watched repositories:"); | ||
for repo in state.repositories.iter() { | ||
println!(" - {:?}", repo.path); | ||
} | ||
return Ok(()); | ||
} | ||
|
||
for path in repos { | ||
// Check if the directory to add actually exists | ||
if !path.exists() || !path.is_dir() { | ||
error!("Cannot find repository at {:?}", path); | ||
} | ||
|
||
// Store the absolute path. | ||
let real_path = std::fs::canonicalize(&path)?; | ||
if !state.has_repo_at_path(&real_path) { | ||
println!("Added repository: {:?}", &real_path); | ||
state.repositories.push(Repository::new(real_path)); | ||
} | ||
} | ||
state.save() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
use std::env::vars; | ||
use std::time::Instant; | ||
use std::{collections::HashMap, time::Duration}; | ||
|
||
use anyhow::Result; | ||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | ||
use log::debug; | ||
use rayon::iter::{IntoParallelIterator, ParallelIterator}; | ||
|
||
use crate::display::multi_progress_bar; | ||
use crate::git::{check_local_changes, check_unpushed_commits, get_stashed_entries}; | ||
use crate::repository_info::RepositoryState; | ||
use crate::{display::print_status, repository_info::RepositoryInfo, state::State}; | ||
|
||
pub fn check( | ||
state: &mut State, | ||
show_all: bool, | ||
parallel: bool, | ||
threads: Option<usize>, | ||
) -> Result<()> { | ||
let repo_infos = state.repo_infos_by_wall_time(); | ||
|
||
// Save all environment variables for later injection into git | ||
let mut envs = HashMap::new(); | ||
for (key, value) in vars() { | ||
envs.insert(key, value); | ||
} | ||
|
||
let (multi_progress, main_bar) = multi_progress_bar(repo_infos.len() as u64)?; | ||
|
||
let repo_infos = if parallel { | ||
// Set the amount of threads, if specified. | ||
if let Some(threads) = threads { | ||
rayon::ThreadPoolBuilder::new() | ||
.num_threads(threads) | ||
.build_global() | ||
.unwrap(); | ||
} | ||
|
||
let results: Result<Vec<RepositoryInfo>> = repo_infos | ||
.into_par_iter() | ||
.map(|mut repo_info| { | ||
// Handle the repository and track execution time. | ||
let start = Instant::now(); | ||
repo_info = match check_repo(&multi_progress, repo_info, &envs) { | ||
Ok(repo_info) => repo_info, | ||
Err(err) => { | ||
// Make sure the bar gets incremented even if we get an error. | ||
main_bar.inc(1); | ||
return Err(err); | ||
} | ||
}; | ||
repo_info.check_time = Some(start.elapsed().as_millis() as usize); | ||
|
||
main_bar.inc(1); | ||
Ok(repo_info) | ||
}) | ||
.collect(); | ||
|
||
main_bar.finish_with_message("All done: "); | ||
|
||
results? | ||
} else { | ||
let mut results = Vec::new(); | ||
for repo_info in repo_infos.into_iter() { | ||
// Handle the repository and track execution time. | ||
let start = Instant::now(); | ||
let mut repo_info = check_repo(&multi_progress, repo_info, &envs)?; | ||
repo_info.check_time = Some(start.elapsed().as_millis() as usize); | ||
|
||
debug!("Check took {}ms", start.elapsed().as_millis()); | ||
results.push(repo_info); | ||
} | ||
|
||
results | ||
}; | ||
|
||
// Finish and clean up the progress bar | ||
main_bar.finish(); | ||
let _ = multi_progress.clear(); | ||
|
||
state.update_check_times(&repo_infos)?; | ||
|
||
print_status(repo_infos, show_all)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// This is a simple wrapper around the actual repo check function | ||
/// for easier progress bar handling. | ||
pub fn check_repo( | ||
multi_progress: &MultiProgress, | ||
repo_info: RepositoryInfo, | ||
envs: &HashMap<String, String>, | ||
) -> Result<RepositoryInfo> { | ||
let mut bar = ProgressBar::new(3); | ||
let spinner_style = | ||
ProgressStyle::with_template("{duration} {spinner} {prefix:.bold.white.dim} - {wide_msg}") | ||
.unwrap() | ||
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "); | ||
bar.set_style(spinner_style); | ||
|
||
// Add the bar to the end of the multi_bar. | ||
bar = multi_progress.add(bar); | ||
|
||
// Enable a steady tick after adding it to the bar, to ensure correct position rendering. | ||
bar.enable_steady_tick(Duration::from_millis(125)); | ||
|
||
// Run the actual repo handling logic. | ||
let result = check_repo_inner(&bar, repo_info, envs); | ||
|
||
// Clean up this repo's progress bar. | ||
bar.disable_steady_tick(); | ||
bar.finish(); | ||
multi_progress.remove(&bar); | ||
|
||
result | ||
} | ||
|
||
pub fn check_repo_inner( | ||
bar: &ProgressBar, | ||
mut repo_info: RepositoryInfo, | ||
envs: &HashMap<String, String>, | ||
) -> Result<RepositoryInfo> { | ||
let name = repo_info.name.clone(); | ||
|
||
// Default to a `Ok` repo state. | ||
// If anything is not ok, it'll be set in the respective function. | ||
repo_info.state = RepositoryState::Ok; | ||
|
||
bar.set_prefix(format!("[1/3] - {name}")); | ||
bar.set_message(format!("{name}: Checking stash")); | ||
get_stashed_entries(&mut repo_info, envs)?; | ||
|
||
bar.set_prefix(format!("[2/3] - {name}")); | ||
bar.set_message(format!("{name}: Check for local changes")); | ||
check_local_changes(&mut repo_info, envs)?; | ||
|
||
bar.set_prefix(format!("[3/3] - {name}")); | ||
bar.set_message(format!("{name}: Check for unpushed commits")); | ||
check_unpushed_commits(&mut repo_info, envs)?; | ||
|
||
Ok(repo_info) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Result; | ||
use log::error; | ||
|
||
use super::unwatch; | ||
use crate::state::{discover, State}; | ||
|
||
/// Explicitly ignore a given directory. | ||
/// No repositories will be updated or discovered, even if it's inside a watched directory. | ||
pub fn ignore(state: &mut State, directories: &[PathBuf]) -> Result<()> { | ||
// First, unwatch in case any of them have been actively watched. | ||
// This also explicitly removes any repositories that were tracked. | ||
unwatch(state, directories)?; | ||
|
||
for path in directories.iter() { | ||
// Check if the directory to add actually exists | ||
if !path.exists() || !path.is_dir() { | ||
error!("Cannot find directory at {:?}", path); | ||
continue; | ||
} | ||
|
||
// Get the absolute path | ||
let real_path = std::fs::canonicalize(path)?; | ||
|
||
if state.ignored.contains(&real_path) { | ||
error!("The folder is already ignored: {:?}", &real_path); | ||
continue; | ||
} | ||
|
||
// Scan the watched path for repositories, so we can forget about them | ||
let mut repos = Vec::new(); | ||
discover(&state.ignored, &real_path, 0, &mut repos); | ||
|
||
for repo_to_remove in repos { | ||
println!("Forgetting about repository: {:?}", repo_to_remove.path); | ||
state | ||
.repositories | ||
.retain(|repo| repo.path != repo_to_remove.path); | ||
} | ||
|
||
println!("Ignoring directory: {:?}", real_path); | ||
state.ignored.push(real_path.to_owned()) | ||
} | ||
|
||
state.save() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
mod add; | ||
mod check; | ||
mod ignore; | ||
mod remove; | ||
mod unwatch; | ||
mod update; | ||
mod watch; | ||
|
||
pub use add::*; | ||
pub use check::*; | ||
pub use ignore::*; | ||
pub use remove::*; | ||
pub use unwatch::*; | ||
pub use update::*; | ||
pub use watch::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Result; | ||
use log::error; | ||
|
||
use crate::state::State; | ||
|
||
pub fn remove(state: &mut State, repos: Vec<PathBuf>) -> Result<()> { | ||
for path in repos { | ||
// Check if the directory to add actually exists | ||
if !path.exists() || !path.is_dir() { | ||
error!("Cannot find repository at {:?}", path); | ||
continue; | ||
} | ||
|
||
// Store the absolute path. | ||
let real_path = std::fs::canonicalize(&path)?; | ||
if !state.has_repo_at_path(&real_path) { | ||
error!( | ||
"The repository at {:?} hasn't been added to geil yet.", | ||
path | ||
); | ||
} else { | ||
println!("Forgetting about repository: {:?}", &real_path); | ||
state.repositories.retain(|repo| repo.path != real_path); | ||
} | ||
} | ||
state.save() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Result; | ||
use log::error; | ||
|
||
use crate::state::{discover, State}; | ||
|
||
pub fn unwatch(state: &mut State, directories: &[PathBuf]) -> Result<()> { | ||
for path in directories { | ||
// Check if the directory to add actually exists | ||
if !path.exists() || !path.is_dir() { | ||
error!("Cannot find directory at {:?}", path); | ||
continue; | ||
} | ||
|
||
// Get the absolute path | ||
let real_path = std::fs::canonicalize(path)?; | ||
if !state.watched.contains(&real_path) { | ||
error!("The folder hasn't been watched: {:?}", &real_path); | ||
} else { | ||
println!("Unwatching path : {:?}", &real_path); | ||
state.watched.retain(|path| path != &real_path); | ||
|
||
// Scan the watched path for repositories, so we can forget about them | ||
let mut repos = Vec::new(); | ||
discover(&state.ignored, &real_path, 0, &mut repos); | ||
|
||
for repo_to_remove in repos { | ||
println!("Forgetting about repository: {:?}", repo_to_remove.path); | ||
state | ||
.repositories | ||
.retain(|repo| repo.path != repo_to_remove.path); | ||
} | ||
} | ||
} | ||
|
||
state.save() | ||
} |
Oops, something went wrong.