Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix windows path issue #4

Merged
merged 5 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
66 changes: 45 additions & 21 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
};
Expand Down Expand Up @@ -212,7 +212,7 @@ impl GitContext {

/// Run `git -C top_level ls-files ...`
pub fn ls_files(&self, extra_args: &[&str]) -> Result<Vec<String>, 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)
Expand All @@ -238,7 +238,7 @@ impl GitContext {
where
S: AsRef<Path>,
{
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()
Expand All @@ -257,7 +257,7 @@ impl GitContext {
where
S: AsRef<Path>,
{
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,
Expand Down Expand Up @@ -303,7 +303,7 @@ impl GitContext {
where
S: AsRef<Path>,
{
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) => {
Expand All @@ -326,7 +326,7 @@ impl GitContext {
where
S: AsRef<Path>,
{
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,
Expand All @@ -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);
Expand All @@ -347,15 +347,15 @@ 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(())
}

/// Runs `git submodule deinit [-- <path>]`. 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 {
Expand All @@ -375,7 +375,7 @@ impl GitContext {

/// Runs `git submodule init [-- <path>]`. 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 {
Expand All @@ -389,7 +389,7 @@ impl GitContext {

/// Runs `git submodule sync [-- <path>]`. 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 {
Expand All @@ -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) => {
Expand All @@ -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",
Expand All @@ -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 {
Expand Down Expand Up @@ -475,7 +475,7 @@ impl GitContext {
depth: Option<usize>,
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");
Expand Down Expand Up @@ -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...");
Expand All @@ -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}`");
Expand Down Expand Up @@ -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<S> GitCmdPath for S
where
S: AsRef<Path>,
{
#[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
Expand Down
28 changes: 11 additions & 17 deletions src/status.rs
Original file line number Diff line number Diff line change
@@ -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`]
Expand Down Expand Up @@ -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::<Vec<_>>();
for index_obj in &self.nameless {
Expand All @@ -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::<Vec<_>>();
for index_obj in self.nameless.iter_mut() {
Expand All @@ -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<Submodule> {
let mut modules = self.modules.into_values().collect::<Vec<_>>();
for index_obj in self.nameless {
Expand All @@ -80,6 +73,7 @@ impl Status {
.collect()
}

/// Check if all submodules are healthy
pub fn is_healthy(&self, context: &GitContext) -> Result<bool, GitError> {
for submodule in self.flattened() {
if !submodule.is_healthy(context)? {
Expand All @@ -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<Self, GitError> {
let mut status = Self::default();
status.read_dot_gitmodules(context)?;
Expand All @@ -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") {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
6 changes: 4 additions & 2 deletions src/submodule.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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);
}
Expand Down