diff --git a/config.example.toml b/config.example.toml index 3809d13e..425c515f 100644 --- a/config.example.toml +++ b/config.example.toml @@ -153,34 +153,18 @@ [git] -# max_concurrency = 5 - -# Git repositories that you want to pull and push -# repos = [ -# "~/src/*/", -# "~/.config/something" -# ] - -# Repositories that you only want to pull -# pull_only_repos = [ -# "~/.config/something_else" -# ] - -# Repositories that you only want to push -# push_only_repos = [ -# "~/src/*/", -# "~/.config/something_third" -# ] +#max_concurrency = 5 +# Additional git repositories to pull +#repos = [ +# "~/src/*/", +# "~/.config/something" +#] # Don't pull the predefined git repos # pull_predefined = false -# Arguments to pass Git when pulling repositories -# pull_arguments = "--rebase --autostash" - -# Arguments to pass Git when pushing repositories -# push_arguments = "--all" - +# Arguments to pass Git when pulling Repositories +#arguments = "--rebase --autostash" [windows] # Manually select Windows updates diff --git a/src/config.rs b/src/config.rs index d9a0f08e..1441bf97 100644 --- a/src/config.rs +++ b/src/config.rs @@ -165,17 +165,10 @@ pub struct Git { max_concurrency: Option, #[merge(strategy = crate::utils::merge_strategies::string_append_opt)] - pull_arguments: Option, - - #[merge(strategy = crate::utils::merge_strategies::string_append_opt)] - push_arguments: Option, + arguments: Option, #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] repos: Option>, - #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] - pull_only_repos: Option>, - #[merge(strategy = crate::utils::merge_strategies::vec_prepend_opt)] - push_only_repos: Option>, pull_predefined: Option, } @@ -617,22 +610,7 @@ impl ConfigFile { } } - if let Some(paths) = result.git.as_mut().and_then(|git| git.pull_only_repos.as_mut()) { - for path in paths.iter_mut() { - let expanded = shellexpand::tilde::<&str>(&path.as_ref()).into_owned(); - debug!("Path {} expanded to {}", path, expanded); - *path = expanded; - } - } - - if let Some(paths) = result.git.as_mut().and_then(|git| git.push_only_repos.as_mut()) { - for path in paths.iter_mut() { - let expanded = shellexpand::tilde::<&str>(&path.as_ref()).into_owned(); - debug!("Path {} expanded to {}", path, expanded); - *path = expanded; - } - } - + debug!("Loaded configuration: {:?}", result); Ok(result) } @@ -860,23 +838,9 @@ impl Config { &self.config_file.commands } - /// The list of git repositories to push and pull. - pub fn git_repos(&self) -> Option<&Vec> { - self.config_file.git.as_ref().and_then(|git| git.repos.as_ref()) - } /// The list of additional git repositories to pull. - pub fn git_pull_only_repos(&self) -> Option<&Vec> { - self.config_file - .git - .as_ref() - .and_then(|git| git.pull_only_repos.as_ref()) - } - /// The list of git repositories to push. - pub fn git_push_only_repos(&self) -> Option<&Vec> { - self.config_file - .git - .as_ref() - .and_then(|git| git.push_only_repos.as_ref()) + pub fn git_repos(&self) -> &Option> { + get_deprecated_moved_opt!(&self.config_file.misc, git_repos, &self.config_file.git, repos) } /// Tell whether the specified step should run. @@ -987,19 +951,9 @@ impl Config { .and_then(|misc| misc.ssh_arguments.as_ref()) } - /// Extra Git arguments for when pushing - pub fn push_git_arguments(&self) -> Option<&String> { - self.config_file - .git - .as_ref() - .and_then(|git| git.push_arguments.as_ref()) - } - /// Extra Git arguments for when pulling - pub fn pull_git_arguments(&self) -> Option<&String> { - self.config_file - .git - .as_ref() - .and_then(|git| git.pull_arguments.as_ref()) + /// Extra Git arguments + pub fn git_arguments(&self) -> &Option { + get_deprecated_moved_opt!(&self.config_file.misc, git_arguments, &self.config_file.git, arguments) } /// Extra Tmux arguments diff --git a/src/main.rs b/src/main.rs index c684fa64..d1e9c521 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,6 @@ use etcetera::base_strategy::{BaseStrategy, Xdg}; use once_cell::sync::Lazy; use tracing::debug; -use crate::steps::git::GitAction; - use self::config::{CommandLineArgs, Config, Step}; use self::error::StepFailed; #[cfg(all(windows, feature = "self-update"))] @@ -385,35 +383,35 @@ fn run() -> Result<()> { if config.should_run(Step::Emacs) { if !emacs.is_doom() { if let Some(directory) = emacs.directory() { - git_repos.insert_if_repo(directory, GitAction::Pull); + git_repos.insert_if_repo(directory); } } - git_repos.insert_if_repo(HOME_DIR.join(".doom.d"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".doom.d")); } if config.should_run(Step::Vim) { - git_repos.insert_if_repo(HOME_DIR.join(".vim"), GitAction::Pull); - git_repos.insert_if_repo(HOME_DIR.join(".config/nvim"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".vim")); + git_repos.insert_if_repo(HOME_DIR.join(".config/nvim")); } - git_repos.insert_if_repo(HOME_DIR.join(".ideavimrc"), GitAction::Pull); - git_repos.insert_if_repo(HOME_DIR.join(".intellimacs"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".ideavimrc")); + git_repos.insert_if_repo(HOME_DIR.join(".intellimacs")); if config.should_run(Step::Rcm) { - git_repos.insert_if_repo(HOME_DIR.join(".dotfiles"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".dotfiles")); } #[cfg(unix)] { - git_repos.insert_if_repo(zsh::zshrc(), GitAction::Pull); + git_repos.insert_if_repo(zsh::zshrc()); if config.should_run(Step::Tmux) { - git_repos.insert_if_repo(HOME_DIR.join(".tmux"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".tmux")); } - git_repos.insert_if_repo(HOME_DIR.join(".config/fish"), GitAction::Pull); - git_repos.insert_if_repo(XDG_DIRS.config_dir().join("openbox"), GitAction::Pull); - git_repos.insert_if_repo(XDG_DIRS.config_dir().join("bspwm"), GitAction::Pull); - git_repos.insert_if_repo(XDG_DIRS.config_dir().join("i3"), GitAction::Pull); - git_repos.insert_if_repo(XDG_DIRS.config_dir().join("sway"), GitAction::Pull); + git_repos.insert_if_repo(HOME_DIR.join(".config/fish")); + git_repos.insert_if_repo(XDG_DIRS.config_dir().join("openbox")); + git_repos.insert_if_repo(XDG_DIRS.config_dir().join("bspwm")); + git_repos.insert_if_repo(XDG_DIRS.config_dir().join("i3")); + git_repos.insert_if_repo(XDG_DIRS.config_dir().join("sway")); } #[cfg(windows)] @@ -421,39 +419,24 @@ fn run() -> Result<()> { WINDOWS_DIRS .cache_dir() .join("Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState"), - GitAction::Pull, ); #[cfg(windows)] windows::insert_startup_scripts(&mut git_repos).ok(); if let Some(profile) = powershell.profile() { - git_repos.insert_if_repo(profile, GitAction::Pull); + git_repos.insert_if_repo(profile); } } if config.should_run(Step::GitRepos) { if let Some(custom_git_repos) = config.git_repos() { for git_repo in custom_git_repos { - git_repos.glob_insert(git_repo, GitAction::Pull); - git_repos.glob_insert(git_repo, GitAction::Push); + git_repos.glob_insert(git_repo); } } - - if let Some(git_pull_only_repos) = config.git_pull_only_repos() { - for git_repo in git_pull_only_repos { - git_repos.glob_insert(git_repo, GitAction::Pull); - } - } - - if let Some(git_push_only_repos) = config.git_push_only_repos() { - for git_repo in git_push_only_repos { - git_repos.glob_insert(git_repo, GitAction::Push); - } - } - runner.execute(Step::GitRepos, "Git repositories", || { - git.multi_repo_step(&git_repos, &ctx) + git.multi_pull_step(&git_repos, &ctx) })?; } diff --git a/src/steps/git.rs b/src/steps/git.rs index d80a1c53..2a27e38a 100644 --- a/src/steps/git.rs +++ b/src/steps/git.rs @@ -27,17 +27,9 @@ pub struct Git { git: Option, } -#[derive(Clone, Copy)] -pub enum GitAction { - Push, - Pull, -} - -#[derive(Debug)] pub struct Repositories<'a> { git: &'a Git, - pull_repositories: HashSet, - push_repositories: HashSet, + repositories: HashSet, glob_match_options: MatchOptions, bad_patterns: Vec, } @@ -52,36 +44,6 @@ fn output_checked_utf8(output: Output) -> Result<()> { Ok(()) } } -async fn push_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) -> Result<()> { - let path = repo.to_string(); - - println!("{} {}", style("Pushing").cyan().bold(), path); - - let mut command = AsyncCommand::new(git); - - command - .stdin(Stdio::null()) - .current_dir(&repo) - .args(["push", "--porcelain"]); - if let Some(extra_arguments) = ctx.config().push_git_arguments() { - command.args(extra_arguments.split_whitespace()); - } - - let output = command.output().await?; - let result = match output.status.success() { - true => Ok(()), - false => Err(format!("Failed to push {repo}")), - }; - - if result.is_err() { - println!("{} pushing {}", style("Failed").red().bold(), &repo); - }; - - match result { - Ok(_) => Ok(()), - Err(e) => Err(eyre!(e)), - } -} async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) -> Result<()> { let path = repo.to_string(); @@ -96,7 +58,7 @@ async fn pull_repository(repo: String, git: &Path, ctx: &ExecutionContext<'_>) - .current_dir(&repo) .args(["pull", "--ff-only"]); - if let Some(extra_arguments) = ctx.config().pull_git_arguments() { + if let Some(extra_arguments) = ctx.config().git_arguments() { command.args(extra_arguments.split_whitespace()); } @@ -219,7 +181,7 @@ impl Git { None } - pub fn multi_repo_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { + pub fn multi_pull_step(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { // Warn the user about the bad patterns. // // NOTE: this should be executed **before** skipping the Git step or the @@ -230,15 +192,12 @@ impl Git { .iter() .for_each(|pattern| print_warning(format!("Path {pattern} did not contain any git repositories"))); - if repositories.is_empty() { - return Err(SkipStep(String::from("No repositories to pull or push")).into()); + if repositories.repositories.is_empty() { + return Err(SkipStep(String::from("No repositories to pull")).into()); } print_separator("Git repositories"); - self.multi_pull(repositories, ctx)?; - self.multi_push(repositories, ctx)?; - - Ok(()) + self.multi_pull(repositories, ctx) } pub fn multi_pull(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { @@ -246,7 +205,7 @@ impl Git { if ctx.run_type().dry() { repositories - .pull_repositories + .repositories .iter() .for_each(|repo| println!("Would pull {}", &repo)); @@ -254,7 +213,7 @@ impl Git { } let futures_iterator = repositories - .pull_repositories + .repositories .iter() .filter(|repo| match has_remotes(git, repo) { Some(false) => { @@ -281,47 +240,6 @@ impl Git { let error = results.into_iter().find(|r| r.is_err()); error.unwrap_or(Ok(())) } - - pub fn multi_push(&self, repositories: &Repositories, ctx: &ExecutionContext) -> Result<()> { - let git = self.git.as_ref().unwrap(); - - if ctx.run_type().dry() { - repositories - .push_repositories - .iter() - .for_each(|repo| println!("Would push {}", &repo)); - - return Ok(()); - } - - let futures_iterator = repositories - .push_repositories - .iter() - .filter(|repo| match has_remotes(git, repo) { - Some(false) => { - println!( - "{} {} because it has no remotes", - style("Skipping").yellow().bold(), - repo - ); - false - } - _ => true, // repo has remotes or command to check for remotes has failed. proceed to pull anyway. - }) - .map(|repo| push_repository(repo.clone(), git, ctx)); - - let stream_of_futures = if let Some(limit) = ctx.config().git_concurrency_limit() { - iter(futures_iterator).buffer_unordered(limit).boxed() - } else { - futures_iterator.collect::>().boxed() - }; - - let basic_rt = runtime::Runtime::new()?; - let results = basic_rt.block_on(async { stream_of_futures.collect::>>().await }); - - let error = results.into_iter().find(|r| r.is_err()); - error.unwrap_or(Ok(())) - } } impl<'a> Repositories<'a> { @@ -334,27 +252,22 @@ impl<'a> Repositories<'a> { Self { git, + repositories: HashSet::new(), bad_patterns: Vec::new(), glob_match_options, - pull_repositories: HashSet::new(), - push_repositories: HashSet::new(), } } - pub fn insert_if_repo>(&mut self, path: P, action: GitAction) -> bool { + pub fn insert_if_repo>(&mut self, path: P) -> bool { if let Some(repo) = self.git.get_repo_root(path) { - match action { - GitAction::Push => self.push_repositories.insert(repo), - GitAction::Pull => self.pull_repositories.insert(repo), - }; - + self.repositories.insert(repo); true } else { false } } - pub fn glob_insert(&mut self, pattern: &str, action: GitAction) { + pub fn glob_insert(&mut self, pattern: &str) { if let Ok(glob) = glob_with(pattern, self.glob_match_options) { let mut last_git_repo: Option = None; for entry in glob { @@ -370,7 +283,7 @@ impl<'a> Repositories<'a> { continue; } } - if self.insert_if_repo(&path, action) { + if self.insert_if_repo(&path) { last_git_repo = Some(path); } } @@ -388,27 +301,16 @@ impl<'a> Repositories<'a> { } } - /// Return true if `pull_repos` and `push_repos` are both empty. + #[cfg(unix)] pub fn is_empty(&self) -> bool { - self.pull_repositories.is_empty() && self.push_repositories.is_empty() + self.repositories.is_empty() } // The following 2 functions are `#[cfg(unix)]` because they are only used in // the `oh-my-zsh` step, which is UNIX-only. - #[cfg(unix)] - /// Return true if `pull_repos` is empty. - pub fn pull_is_empty(&self) -> bool { - self.pull_repositories.is_empty() - } - - #[cfg(unix)] - /// Remove `path` from `pull_repos` - /// - /// # Panic - /// Will panic if `path` is not in the `pull_repos` under a debug build. - pub fn remove_from_pull(&mut self, path: &str) { - let _removed = self.pull_repositories.remove(path); + pub fn remove(&mut self, path: &str) { + let _removed = self.repositories.remove(path); debug_assert!(_removed); } } diff --git a/src/steps/os/windows.rs b/src/steps/os/windows.rs index 1567876c..a459584f 100644 --- a/src/steps/os/windows.rs +++ b/src/steps/os/windows.rs @@ -8,7 +8,6 @@ use tracing::debug; use crate::command::CommandExt; use crate::execution_context::ExecutionContext; -use crate::steps::git::GitAction; use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, which}; use crate::{error::SkipStep, steps::git::Repositories}; @@ -240,7 +239,7 @@ pub fn insert_startup_scripts(git_repos: &mut Repositories) -> Result<()> { if let Ok(lnk) = parselnk::Lnk::try_from(Path::new(&path)) { debug!("Startup link: {:?}", lnk); if let Some(path) = lnk.relative_path() { - git_repos.insert_if_repo(&startup_dir.join(path), GitAction::Pull); + git_repos.insert_if_repo(&startup_dir.join(path)); } } } diff --git a/src/steps/zsh.rs b/src/steps/zsh.rs index 3660b0ea..dc246ab1 100644 --- a/src/steps/zsh.rs +++ b/src/steps/zsh.rs @@ -231,7 +231,7 @@ pub fn run_oh_my_zsh(ctx: &ExecutionContext) -> Result<()> { for entry in WalkDir::new(custom_dir).max_depth(2) { let entry = entry?; - custom_repos.insert_if_repo(entry.path(), crate::steps::git::GitAction::Pull); + custom_repos.insert_if_repo(entry.path()); } custom_repos.remove_from_pull(&oh_my_zsh.to_string_lossy());