From d370a77b6d0c7f569a96b8bc2f3ab7e41f7f6a45 Mon Sep 17 00:00:00 2001 From: Anonymous Fantasy <44533763+Pistonight@users.noreply.github.com> Date: Sun, 26 Nov 2023 16:21:22 -0800 Subject: [PATCH] Fix windows path issue (#4) * fix path error on windows * update docs * bump version * update pipeline to run in other OS * run cargo fmt --- .github/workflows/rust.yml | 3 +- Cargo.lock | 2 +- Cargo.toml | 2 +- src/git.rs | 66 ++++++++++++++++++++++++++------------ src/status.rs | 28 +++++++--------- src/submodule.rs | 6 ++-- 6 files changed, 64 insertions(+), 43 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 97ad7c4..25c25c9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,7 +10,8 @@ on: jobs: build: name: Check, Build, Test - runs-on: ubuntu-latest + strategy: { matrix: { os: [ ubuntu-latest, macos-latest, windows-latest ] } } + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: arduino/setup-task@v1 diff --git a/Cargo.lock b/Cargo.lock index 94ae4d4..bd2dc32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,7 +157,7 @@ checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "magoo" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clap", "fs4", diff --git a/Cargo.toml b/Cargo.toml index 350faf5..814b474 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "magoo" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "A wrapper for git submodule that simplifies the workflows" repository = "https://github.com/Pistonite/magoo" diff --git a/src/git.rs b/src/git.rs index a4a2b6a..583cc54 100644 --- a/src/git.rs +++ b/src/git.rs @@ -121,12 +121,12 @@ impl GitContext { } else { let path = pathdiff::diff_paths(top_level_dir, &cwd) .unwrap_or(top_level_dir.to_path_buf()); - let diff = path.display().to_string(); + let diff = path.to_cmd_arg(); Some(quote_arg(&diff).to_string()) } } Err(_) => { - let top_level = top_level_dir.display().to_string(); + let top_level = top_level_dir.to_cmd_arg(); Some(quote_arg(&top_level).to_string()) } }; @@ -212,7 +212,7 @@ impl GitContext { /// Run `git -C top_level ls-files ...` pub fn ls_files(&self, extra_args: &[&str]) -> Result, GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "ls-files"]; args.extend_from_slice(extra_args); self.run_git_command(&args, false) @@ -238,7 +238,7 @@ impl GitContext { where S: AsRef, { - let config_path = config_path.as_ref().display().to_string(); + let config_path = config_path.to_cmd_arg(); let value = self .run_git_command(&["config", "-f", &config_path, "--get", key], false)? .into_iter() @@ -257,7 +257,7 @@ impl GitContext { where S: AsRef, { - let config_path = config_path.as_ref().display().to_string(); + let config_path = config_path.to_cmd_arg(); let name_and_values = self.run_git_command( &["config", "-f", &config_path, "--get-regexp", regexp], false, @@ -303,7 +303,7 @@ impl GitContext { where S: AsRef, { - let config_path = config_path.as_ref().display().to_string(); + let config_path = config_path.to_cmd_arg(); let mut args = vec!["config", "-f", &config_path]; match value { Some(v) => { @@ -326,7 +326,7 @@ impl GitContext { where S: AsRef, { - let config_path = config_path.as_ref().display().to_string(); + let config_path = config_path.to_cmd_arg(); self.run_git_command( &["config", "-f", &config_path, "--remove-section", section], false, @@ -336,7 +336,7 @@ impl GitContext { /// Remove an object from the index and stage the change. The path should be relative from repo top level pub fn remove_from_index(&self, path: &str) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); // ignore the error because the file might not be in the index let _ = self.run_git_command(&["-C", &top_level_dir, "rm", path], false); @@ -347,7 +347,7 @@ impl GitContext { /// Run `git add` pub fn add(&self, path: &str) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); self.run_git_command(&["-C", &top_level_dir, "add", path], false)?; Ok(()) @@ -355,7 +355,7 @@ impl GitContext { /// Runs `git submodule deinit [-- ]`. Path should be from top level pub fn submodule_deinit(&self, path: Option<&str>, force: bool) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "deinit"]; if force { @@ -375,7 +375,7 @@ impl GitContext { /// Runs `git submodule init [-- ]`. Path should be from top level pub fn submodule_init(&self, path: Option<&str>) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "init"]; if let Some(path) = path { @@ -389,7 +389,7 @@ impl GitContext { /// Runs `git submodule sync [-- ]`. Path should be from top level pub fn submodule_sync(&self, path: Option<&str>) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "sync"]; if let Some(path) = path { @@ -403,7 +403,7 @@ impl GitContext { /// Runs `git submodule set-branch`. Path should be from top level pub fn submodule_set_branch(&self, path: &str, branch: Option<&str>) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "set-branch"]; match branch { Some(branch) => { @@ -422,7 +422,7 @@ impl GitContext { /// Runs `git submodule set-url`. Path should be from top level pub fn submodule_set_url(&self, path: &str, url: &str) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); self.run_git_command( &[ "-C", @@ -445,7 +445,7 @@ impl GitContext { force: bool, remote: bool, ) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "update"]; if force { @@ -475,7 +475,7 @@ impl GitContext { depth: Option, force: bool, ) -> Result<(), GitError> { - let top_level_dir = self.top_level_dir()?.display().to_string(); + let top_level_dir = self.top_level_dir()?.to_cmd_arg(); let mut args = vec!["-C", &top_level_dir, "submodule", "add"]; if force { args.push("--force"); @@ -517,7 +517,7 @@ impl Guard { { let path = path.as_ref(); if path.exists() { - println_warn!("Waiting on file lock. If you are sure no other magoo processes are running, you can remove the lock file `{}`", path.display()); + println_warn!("Waiting on file lock. If you are sure no other magoo processes are running, you can remove the lock file `{}`", path.to_cmd_arg()); } while path.exists() { println_verbose!("Waiting for lock file..."); @@ -528,17 +528,17 @@ impl Guard { .write(true) .create(true) .open(path) - .map_err(|e| GitError::LockFailed(path.display().to_string(), e))?; + .map_err(|e| GitError::LockFailed(path.to_cmd_arg(), e))?; file.lock_exclusive() - .map_err(|e| GitError::LockFailed(path.display().to_string(), e))?; - println_verbose!("Acquired lock file `{}`", path.display()); + .map_err(|e| GitError::LockFailed(path.to_cmd_arg(), e))?; + println_verbose!("Acquired lock file `{}`", path.to_cmd_arg()); Ok(Self(file, path.to_path_buf())) } } impl Drop for Guard { fn drop(&mut self) { - let path = &self.1.display(); + let path = &self.1.to_cmd_arg(); println_verbose!("Releasing lock file `{path}`"); if self.0.unlock().is_err() { println_verbose!("Failed to unlock file `{path}`"); @@ -602,6 +602,30 @@ where } } +/// Helper trait to clean a path to be used as command line argument +pub trait GitCmdPath { + fn to_cmd_arg(&self) -> String; +} + +impl GitCmdPath for S +where + S: AsRef, +{ + #[cfg(not(windows))] + fn to_cmd_arg(&self) -> String { + self.as_ref().display().to_string() + } + + #[cfg(windows)] + fn to_cmd_arg(&self) -> String { + let s = self.as_ref().display().to_string(); + match s.strip_prefix(r"\\?\") { + Some(x) => x.to_string(), + None => s, + } + } +} + /// Quote the argument for shell. pub fn quote_arg(s: &str) -> Cow<'_, str> { // note that this implementation doesn't work in a few edge cases diff --git a/src/status.rs b/src/status.rs index 5f5b798..82e60e0 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,11 +1,13 @@ +//! Logic for getting the status of submodules + use std::collections::BTreeMap; use std::path::Path; -use crate::git::{GitContext, GitError}; +use crate::git::{GitCmdPath, GitContext, GitError}; use crate::print::println_verbose; use crate::submodule::*; -/// Data returned from [`GitContext::submodule_status`] +/// Status of all submodules in a repository #[derive(Debug, Clone, PartialEq, Default)] pub struct Status { /// The submodule status map from name to [`Submodule`] @@ -37,9 +39,6 @@ macro_rules! insert_with_name { impl Status { /// Return a flattened view of all the submodules - /// - /// If the status was created with the `--all` flag, it will also include the nameless - /// submodules pub fn flattened(&self) -> Vec<&Submodule> { let mut modules = self.modules.values().collect::>(); for index_obj in &self.nameless { @@ -49,9 +48,6 @@ impl Status { } /// Return a flattened view of all the submodules - /// - /// If the status was created with the `--all` flag, it will also include the nameless - /// submodules pub fn flattened_mut(&mut self) -> Vec<&mut Submodule> { let mut modules = self.modules.values_mut().collect::>(); for index_obj in self.nameless.iter_mut() { @@ -61,9 +57,6 @@ impl Status { } /// Flattens the submodules into a vector of [`Submodule`] - /// - /// If the status was created with the `--all` flag, it will also include the nameless - /// submodules pub fn into_flattened(self) -> Vec { let mut modules = self.modules.into_values().collect::>(); for index_obj in self.nameless { @@ -80,6 +73,7 @@ impl Status { .collect() } + /// Check if all submodules are healthy pub fn is_healthy(&self, context: &GitContext) -> Result { for submodule in self.flattened() { if !submodule.is_healthy(context)? { @@ -89,7 +83,7 @@ impl Status { Ok(true) } - /// Get the submodule status in the repository. + /// Factory function. Get the submodule status in the repository. pub fn read_from(context: &GitContext) -> Result { let mut status = Self::default(); status.read_dot_gitmodules(context)?; @@ -107,7 +101,7 @@ impl Status { let dot_gitmodules_path = top_level_dir.join(".gitmodules"); let config_entries = - Self::read_submodule_from_config(context, &dot_gitmodules_path.display().to_string())?; + Self::read_submodule_from_config(context, &dot_gitmodules_path.to_cmd_arg())?; for (key, value) in config_entries { let name = if let Some(name) = key.strip_suffix(".path") { @@ -135,7 +129,7 @@ impl Status { let config_entries = match Self::read_submodule_from_config( context, - &dot_git_config_path.display().to_string(), + &dot_git_config_path.to_cmd_arg(), ) { Ok(entries) => entries, Err(e) => { @@ -208,7 +202,7 @@ impl Status { name: Option<&str>, dir_path: &Path, ) { - println_verbose!("Scanning for git modules in `{}`", dir_path.display()); + println_verbose!("Scanning for git modules in `{}`", dir_path.to_cmd_arg()); let config_path = dir_path.join("config"); if config_path.is_file() { if let Some(name) = name { @@ -239,7 +233,7 @@ impl Status { // dir_path is not a module, recurse let dir = match dir_path.read_dir() { Err(e) => { - println_verbose!("Failed to read directory `{}`: {e}", dir_path.display()); + println_verbose!("Failed to read directory `{}`: {e}", dir_path.to_cmd_arg()); return; } Ok(dir) => dir, @@ -249,7 +243,7 @@ impl Status { Err(e) => { println_verbose!( "Failed to read directory entry in `{}`: {e}", - dir_path.display() + dir_path.to_cmd_arg() ); continue; } diff --git a/src/submodule.rs b/src/submodule.rs index fbea1ad..4f177bd 100644 --- a/src/submodule.rs +++ b/src/submodule.rs @@ -1,6 +1,8 @@ +//! Submodule data and operations + use std::path::{Path, PathBuf}; -use crate::git::{quote_arg, GitCanonicalize, GitContext, GitError}; +use crate::git::{quote_arg, GitCanonicalize, GitCmdPath, GitContext, GitError}; use crate::print::{ print_info, print_warn, println_error, println_hint, println_info, println_verbose, println_warn, @@ -488,7 +490,7 @@ impl Submodule { if worktree_path.exists() { println_info!( "Deleting the worktree of submodule `{name}` at `{}`", - worktree_path.display().to_string() + worktree_path.to_cmd_arg() ); let _ = std::fs::remove_dir_all(worktree_path); }