From 4177d72c95bd94cf6a49e917dc21918044e8250b Mon Sep 17 00:00:00 2001 From: Aratrik Pal Date: Sun, 23 Jan 2022 13:06:31 +0530 Subject: [PATCH 1/5] Implemented git-worktree --- Cargo.lock | 10 ++ git-index/src/lib.rs | 4 + git-worktree/Cargo.toml | 10 ++ git-worktree/src/lib.rs | 125 +++++++++++++++++++++++ git-worktree/tests/copy_index/mod.rs | 83 +++++++++++++++ git-worktree/tests/fixtures/make_repo.sh | 19 ++++ git-worktree/tests/mod.rs | 25 +++++ 7 files changed, 276 insertions(+) create mode 100644 git-worktree/tests/copy_index/mod.rs create mode 100755 git-worktree/tests/fixtures/make_repo.sh create mode 100644 git-worktree/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 91e6f2d369a..3ad39c16013 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1697,6 +1697,16 @@ dependencies = [ [[package]] name = "git-worktree" version = "0.0.0" +dependencies = [ + "git-hash 0.9.0", + "git-index", + "git-object 0.17.0", + "git-odb 0.26.0", + "git-testtools", + "quick-error", + "tempfile", + "walkdir", +] [[package]] name = "git2" diff --git a/git-index/src/lib.rs b/git-index/src/lib.rs index f1beb940933..095c0ea54b0 100644 --- a/git-index/src/lib.rs +++ b/git-index/src/lib.rs @@ -22,6 +22,10 @@ mod access { pub fn entries(&self) -> &[Entry] { &self.entries } + + pub fn entries_mut(&mut self) -> &mut [Entry] { + &mut self.entries + } } } diff --git a/git-worktree/Cargo.toml b/git-worktree/Cargo.toml index 6aa73e98b5e..abc5ce18c1b 100644 --- a/git-worktree/Cargo.toml +++ b/git-worktree/Cargo.toml @@ -13,3 +13,13 @@ doctest = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +git-index = { version = "^0.1.0", path = "../git-index" } +quick-error = "2.0.1" +git-hash = { version = "^0.9.0", path = "../git-hash" } +git-object = { version = "^0.17.0", path = "../git-object" } + +[dev-dependencies] +git-odb = { path = "../git-odb" } +walkdir = "2.3.2" +git-testtools = { path = "../tests/tools" } +tempfile = "3.2.0" diff --git a/git-worktree/src/lib.rs b/git-worktree/src/lib.rs index d7a83e4f525..de5e0851d9a 100644 --- a/git-worktree/src/lib.rs +++ b/git-worktree/src/lib.rs @@ -1 +1,126 @@ #![forbid(unsafe_code, rust_2018_idioms)] +//! Git Worktree + +use git_hash::oid; +use git_object::bstr::ByteSlice; +use quick_error::quick_error; +use std::convert::TryFrom; +use std::fs; +use std::fs::create_dir_all; +use std::path::Path; +use std::time::Duration; + +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + +quick_error! { + #[derive(Debug)] + pub enum Error { + Utf8Error(err: git_object::bstr::Utf8Error) { + from() + display("Could not convert path to UTF8: {}", err) + } + TimeError(err: std::time::SystemTimeError) { + from() + display("Could not read file time in proper format: {}", err) + } + ToU32Error(err: std::num::TryFromIntError) { + from() + display("Could not convert seconds to u32: {}", err) + } + IoError(err: std::io::Error) { + from() + display("IO error while writing blob or reading file metadata or changing filetype: {}", err) + } + FindOidError(oid: git_hash::ObjectId, path: std::path::PathBuf) { + display("unable to read sha1 file of {} ({})", path.display(), oid.to_hex()) + } + } +} + +/// Copy index to `path` +pub fn copy_index(state: &mut git_index::State, path: P, mut find: Find, opts: Options) -> Result<(), Error> +where + P: AsRef, + Find: for<'a> FnMut(&oid, &'a mut Vec) -> Option>, +{ + let path = path.as_ref(); + let mut buf = Vec::new(); + let mut entry_time = Vec::new(); // Entries whose timestamps have to be updated + for (i, entry) in state.entries().iter().enumerate() { + if entry.flags.contains(git_index::entry::Flags::SKIP_WORKTREE) { + continue; + } + let entry_path = entry.path(state).to_path()?; + let dest = path.join(entry_path); + create_dir_all(dest.parent().expect("entry paths are never empty"))?; + match entry.mode { + git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => { + let obj = find(&entry.id, &mut buf).ok_or_else(|| Error::FindOidError(entry.id, path.to_path_buf()))?; + std::fs::write(&dest, obj.data)?; + if entry.mode == git_index::entry::Mode::FILE_EXECUTABLE { + #[cfg(unix)] + fs::set_permissions(&dest, fs::Permissions::from_mode(0o777))?; + } + let met = std::fs::symlink_metadata(&dest)?; + let ctime = met + .created() + .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + let mtime = met + .modified() + .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + entry_time.push((ctime?, mtime?, i)); + } + git_index::entry::Mode::SYMLINK => { + let obj = find(&entry.id, &mut buf).unwrap(); + let linked_to = obj.data.to_path()?; + if opts.symlinks { + #[cfg(unix)] + std::os::unix::fs::symlink(linked_to, &dest)?; + #[cfg(windows)] + if dest.exists() { + if dest.is_file() { + std::os::windows::fs::symlink_file(linked_to, &dest)?; + } else { + std::os::windows::fs::symlink_dir(linked_to, &dest)?; + } + } + } else { + std::fs::write(&dest, obj.data)?; + } + let met = std::fs::symlink_metadata(&dest)?; + let ctime = met + .created() + .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + let mtime = met + .modified() + .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + entry_time.push((ctime?, mtime?, i)); + } + git_index::entry::Mode::DIR => todo!(), + git_index::entry::Mode::COMMIT => todo!(), + _ => unreachable!(), + } + } + let entries = state.entries_mut(); + for (ctime, mtime, i) in entry_time { + let stat = &mut entries[i].stat; + stat.mtime.secs = u32::try_from(mtime.as_secs())?; + stat.mtime.nsecs = mtime.subsec_nanos(); + stat.ctime.secs = u32::try_from(ctime.as_secs())?; + stat.ctime.nsecs = ctime.subsec_nanos(); + } + Ok(()) +} + +/// Options for [copy_index](crate::copy_index) +pub struct Options { + /// Enable/disable symlinks + pub symlinks: bool, +} + +impl Default for Options { + fn default() -> Self { + Options { symlinks: true } + } +} diff --git a/git-worktree/tests/copy_index/mod.rs b/git-worktree/tests/copy_index/mod.rs new file mode 100644 index 00000000000..827385b035e --- /dev/null +++ b/git-worktree/tests/copy_index/mod.rs @@ -0,0 +1,83 @@ +use crate::{dir_structure, fixture_path, Result}; +use git_object::bstr::ByteSlice; +use git_odb::FindExt; +use git_worktree::{copy_index, Options}; +use std::fs; + +#[cfg(unix)] +use std::os::unix::prelude::MetadataExt; + +#[test] +fn test_copy_index() -> Result<()> { + let path = fixture_path("make_repo"); + let path_git = path.join(".git"); + let mut file = git_index::File::at(path_git.join("index"), git_index::decode::Options::default())?; + let output_dir = tempfile::tempdir()?; + let output = output_dir.path(); + let odb_handle = git_odb::at(path_git.join("objects"))?; + + copy_index( + &mut file, + &output, + move |oid, buf| odb_handle.find_blob(oid, buf).ok(), + Options::default(), + )?; + + let repo_files = dir_structure(&path); + let copy_files = dir_structure(output); + + let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); + let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); + assert_eq!(srepo_files, scopy_files); + + for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { + assert_eq!(fs::read(file1)?, fs::read(file2)?); + #[cfg(unix)] + assert_eq!( + fs::symlink_metadata(file1)?.mode() & 0b111 << 6, + fs::symlink_metadata(file2)?.mode() & 0b111 << 6 + ); + } + + Ok(()) +} + +#[test] +fn test_copy_index_without_symlinks() -> Result<()> { + let path = fixture_path("make_repo"); + let path_git = path.join(".git"); + let mut file = git_index::File::at(path_git.join("index"), git_index::decode::Options::default())?; + let output_dir = tempfile::tempdir()?; + let output = output_dir.path(); + let odb_handle = git_odb::at(path_git.join("objects"))?; + + copy_index( + &mut file, + &output, + move |oid, buf| odb_handle.find_blob(oid, buf).ok(), + Options { symlinks: false }, + )?; + + let repo_files = dir_structure(&path); + let copy_files = dir_structure(output); + + let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); + let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); + assert_eq!(srepo_files, scopy_files); + + for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { + if file1.is_symlink() { + assert!(!file2.is_symlink()); + assert_eq!(fs::read(file2)?.to_path()?, fs::read_link(file1)?); + } else { + assert_eq!(fs::read(file1)?, fs::read(file2)?); + #[cfg(unix)] + assert_eq!( + fs::symlink_metadata(file1)?.mode() & 0b111 << 6, + fs::symlink_metadata(file2)?.mode() & 0b111 << 6 + ); + } + } + + Ok(()) +} diff --git a/git-worktree/tests/fixtures/make_repo.sh b/git-worktree/tests/fixtures/make_repo.sh new file mode 100755 index 00000000000..e573f721f14 --- /dev/null +++ b/git-worktree/tests/fixtures/make_repo.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -eu -o pipefail + +git init -q + +touch a +echo "Test Vals" > a +touch b +touch c +touch executable.sh +chmod +x executable.sh + +mkdir d +touch d/a +echo "Subdir" > d/a +ln -sf d/a sa + +git add -A +git commit -m "Commit" diff --git a/git-worktree/tests/mod.rs b/git-worktree/tests/mod.rs new file mode 100644 index 00000000000..236c1fbebd0 --- /dev/null +++ b/git-worktree/tests/mod.rs @@ -0,0 +1,25 @@ +use std::path::{Path, PathBuf}; +use walkdir::WalkDir; + +mod copy_index; + +type Result = std::result::Result>; + +pub fn dir_structure>(path: P) -> Vec { + let path = path.as_ref(); + let mut ps: Vec<_> = WalkDir::new(path) + .into_iter() + .filter_entry(|e| e.path() == path || !e.file_name().to_str().map(|s| s.starts_with('.')).unwrap_or(false)) + .flatten() + .filter(|e| e.path().is_file()) + .map(|p| p.path().to_path_buf()) + .collect(); + ps.sort(); + ps +} + +pub fn fixture_path(name: &str) -> PathBuf { + let dir = + git_testtools::scripted_fixture_repo_read_only(Path::new(name).with_extension("sh")).expect("script works"); + dir +} From eaee85595dc658549e62e3292b025ec016e70abd Mon Sep 17 00:00:00 2001 From: Aratrik Pal Date: Thu, 3 Feb 2022 16:55:40 +0530 Subject: [PATCH 2/5] Refactor errors and remove unwraps --- git-worktree/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/git-worktree/src/lib.rs b/git-worktree/src/lib.rs index de5e0851d9a..a563e998c02 100644 --- a/git-worktree/src/lib.rs +++ b/git-worktree/src/lib.rs @@ -7,7 +7,7 @@ use quick_error::quick_error; use std::convert::TryFrom; use std::fs; use std::fs::create_dir_all; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::time::Duration; #[cfg(unix)] @@ -16,23 +16,23 @@ use std::os::unix::fs::PermissionsExt; quick_error! { #[derive(Debug)] pub enum Error { - Utf8Error(err: git_object::bstr::Utf8Error) { + Utf8(err: git_object::bstr::Utf8Error) { from() display("Could not convert path to UTF8: {}", err) } - TimeError(err: std::time::SystemTimeError) { + Time(err: std::time::SystemTimeError) { from() display("Could not read file time in proper format: {}", err) } - ToU32Error(err: std::num::TryFromIntError) { + U32Conversion(err: std::num::TryFromIntError) { from() display("Could not convert seconds to u32: {}", err) } - IoError(err: std::io::Error) { + Io(err: std::io::Error) { from() display("IO error while writing blob or reading file metadata or changing filetype: {}", err) } - FindOidError(oid: git_hash::ObjectId, path: std::path::PathBuf) { + NotFound(oid: git_hash::ObjectId, path: PathBuf) { display("unable to read sha1 file of {} ({})", path.display(), oid.to_hex()) } } @@ -56,7 +56,7 @@ where create_dir_all(dest.parent().expect("entry paths are never empty"))?; match entry.mode { git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => { - let obj = find(&entry.id, &mut buf).ok_or_else(|| Error::FindOidError(entry.id, path.to_path_buf()))?; + let obj = find(&entry.id, &mut buf).ok_or_else(|| Error::NotFound(entry.id, path.to_path_buf()))?; std::fs::write(&dest, obj.data)?; if entry.mode == git_index::entry::Mode::FILE_EXECUTABLE { #[cfg(unix)] @@ -72,7 +72,7 @@ where entry_time.push((ctime?, mtime?, i)); } git_index::entry::Mode::SYMLINK => { - let obj = find(&entry.id, &mut buf).unwrap(); + let obj = find(&entry.id, &mut buf).ok_or_else(|| Error::NotFound(entry.id, path.to_path_buf()))?; let linked_to = obj.data.to_path()?; if opts.symlinks { #[cfg(unix)] From e838eaa5721d8b1b13155aa81234c9c44d9b15fe Mon Sep 17 00:00:00 2001 From: Aratrik Pal Date: Thu, 3 Feb 2022 17:25:44 +0530 Subject: [PATCH 3/5] Reduce io calls --- git-worktree/src/lib.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/git-worktree/src/lib.rs b/git-worktree/src/lib.rs index a563e998c02..9e43c773850 100644 --- a/git-worktree/src/lib.rs +++ b/git-worktree/src/lib.rs @@ -5,13 +5,13 @@ use git_hash::oid; use git_object::bstr::ByteSlice; use quick_error::quick_error; use std::convert::TryFrom; -use std::fs; -use std::fs::create_dir_all; +use std::fs::{create_dir_all, OpenOptions}; +use std::io::Write; use std::path::{Path, PathBuf}; use std::time::Duration; #[cfg(unix)] -use std::os::unix::fs::PermissionsExt; +use std::os::unix::fs::OpenOptionsExt; quick_error! { #[derive(Debug)] @@ -57,12 +57,15 @@ where match entry.mode { git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => { let obj = find(&entry.id, &mut buf).ok_or_else(|| Error::NotFound(entry.id, path.to_path_buf()))?; - std::fs::write(&dest, obj.data)?; + let mut options = OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] if entry.mode == git_index::entry::Mode::FILE_EXECUTABLE { - #[cfg(unix)] - fs::set_permissions(&dest, fs::Permissions::from_mode(0o777))?; + options.mode(0o777); } - let met = std::fs::symlink_metadata(&dest)?; + let mut file = options.open(&dest)?; + file.write_all(obj.data)?; + let met = file.metadata()?; let ctime = met .created() .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); From 25a9dc16dbb26e9aa0f3379b2af53cc0baa96663 Mon Sep 17 00:00:00 2001 From: Aratrik Pal Date: Thu, 3 Feb 2022 17:39:39 +0530 Subject: [PATCH 4/5] Refactored tests --- git-worktree/tests/copy_index/mod.rs | 58 ++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/git-worktree/tests/copy_index/mod.rs b/git-worktree/tests/copy_index/mod.rs index 827385b035e..46f95906b8e 100644 --- a/git-worktree/tests/copy_index/mod.rs +++ b/git-worktree/tests/copy_index/mod.rs @@ -1,4 +1,4 @@ -use crate::{dir_structure, fixture_path, Result}; +use crate::{dir_structure, fixture_path}; use git_object::bstr::ByteSlice; use git_odb::FindExt; use git_worktree::{copy_index, Options}; @@ -8,7 +8,7 @@ use std::fs; use std::os::unix::prelude::MetadataExt; #[test] -fn test_copy_index() -> Result<()> { +fn test_copy_index() -> crate::Result<()> { let path = fixture_path("make_repo"); let path_git = path.join(".git"); let mut file = git_index::File::at(path_git.join("index"), git_index::decode::Options::default())?; @@ -28,14 +28,29 @@ fn test_copy_index() -> Result<()> { let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); - assert_eq!(srepo_files, scopy_files); + assert_eq!( + srepo_files, + scopy_files, + "Testing if {} and {} have the same structure", + path.display(), + output.display() + ); for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { - assert_eq!(fs::read(file1)?, fs::read(file2)?); + assert_eq!( + fs::read(file1)?, + fs::read(file2)?, + "Testing if the contents of {} and {} are the same", + file1.display(), + file2.display(), + ); #[cfg(unix)] assert_eq!( fs::symlink_metadata(file1)?.mode() & 0b111 << 6, - fs::symlink_metadata(file2)?.mode() & 0b111 << 6 + fs::symlink_metadata(file2)?.mode() & 0b111 << 6, + "Testing if the permissions of {} and {} are the same", + file1.display(), + file2.display(), ); } @@ -43,7 +58,7 @@ fn test_copy_index() -> Result<()> { } #[test] -fn test_copy_index_without_symlinks() -> Result<()> { +fn test_copy_index_without_symlinks() -> crate::Result<()> { let path = fixture_path("make_repo"); let path_git = path.join(".git"); let mut file = git_index::File::at(path_git.join("index"), git_index::decode::Options::default())?; @@ -63,18 +78,39 @@ fn test_copy_index_without_symlinks() -> Result<()> { let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); - assert_eq!(srepo_files, scopy_files); + assert_eq!( + srepo_files, + scopy_files, + "Testing if {} and {} have the same structure", + path.display(), + output.display() + ); for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { if file1.is_symlink() { - assert!(!file2.is_symlink()); - assert_eq!(fs::read(file2)?.to_path()?, fs::read_link(file1)?); + assert!(!file2.is_symlink(), "Testing if the new file is not a symlink"); + assert_eq!( + fs::read(file2)?.to_path()?, + fs::read_link(file1)?, + "Testing if the {} contains the path the symlink {} is pointing to", + file2.display(), + file1.display(), + ); } else { - assert_eq!(fs::read(file1)?, fs::read(file2)?); + assert_eq!( + fs::read(file1)?, + fs::read(file2)?, + "Testing if the contents of {} and {} are the same", + file1.display(), + file2.display(), + ); #[cfg(unix)] assert_eq!( fs::symlink_metadata(file1)?.mode() & 0b111 << 6, - fs::symlink_metadata(file2)?.mode() & 0b111 << 6 + fs::symlink_metadata(file2)?.mode() & 0b111 << 6, + "Testing if the permissions of {} and {} are the same", + file1.display(), + file2.display(), ); } } From a4b880cf17665b61e3f7f193de57704b1db5318f Mon Sep 17 00:00:00 2001 From: Aratrik Pal Date: Mon, 7 Feb 2022 15:06:07 +0530 Subject: [PATCH 5/5] Refactored code and tests --- git-worktree/src/lib.rs | 10 ++-- git-worktree/tests/copy_index/mod.rs | 68 +++++++++--------------- git-worktree/tests/fixtures/make_repo.sh | 1 + 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/git-worktree/src/lib.rs b/git-worktree/src/lib.rs index 9e43c773850..b68f45a3d80 100644 --- a/git-worktree/src/lib.rs +++ b/git-worktree/src/lib.rs @@ -33,7 +33,7 @@ quick_error! { display("IO error while writing blob or reading file metadata or changing filetype: {}", err) } NotFound(oid: git_hash::ObjectId, path: PathBuf) { - display("unable to read sha1 file of {} ({})", path.display(), oid.to_hex()) + display("unable find object of {} ({})", path.display(), oid.to_hex()) } } } @@ -68,10 +68,10 @@ where let met = file.metadata()?; let ctime = met .created() - .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + .map_or(Ok(Duration::default()), |x| x.duration_since(std::time::UNIX_EPOCH)); let mtime = met .modified() - .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + .map_or(Ok(Duration::default()), |x| x.duration_since(std::time::UNIX_EPOCH)); entry_time.push((ctime?, mtime?, i)); } git_index::entry::Mode::SYMLINK => { @@ -94,10 +94,10 @@ where let met = std::fs::symlink_metadata(&dest)?; let ctime = met .created() - .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + .map_or(Ok(Duration::default()), |x| x.duration_since(std::time::UNIX_EPOCH)); let mtime = met .modified() - .map_or(Ok(Duration::from_secs(0)), |x| x.duration_since(std::time::UNIX_EPOCH)); + .map_or(Ok(Duration::default()), |x| x.duration_since(std::time::UNIX_EPOCH)); entry_time.push((ctime?, mtime?, i)); } git_index::entry::Mode::DIR => todo!(), diff --git a/git-worktree/tests/copy_index/mod.rs b/git-worktree/tests/copy_index/mod.rs index 46f95906b8e..ce8a4efd236 100644 --- a/git-worktree/tests/copy_index/mod.rs +++ b/git-worktree/tests/copy_index/mod.rs @@ -26,29 +26,24 @@ fn test_copy_index() -> crate::Result<()> { let repo_files = dir_structure(&path); let copy_files = dir_structure(output); - let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); - let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); assert_eq!( - srepo_files, - scopy_files, - "Testing if {} and {} have the same structure", - path.display(), - output.display() + repo_files + .iter() + .flat_map(|p| p.strip_prefix(&path)) + .collect::>(), + copy_files + .iter() + .flat_map(|p| p.strip_prefix(output)) + .collect::>() ); for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { - assert_eq!( - fs::read(file1)?, - fs::read(file2)?, - "Testing if the contents of {} and {} are the same", - file1.display(), - file2.display(), - ); + assert_eq!(fs::read(file1)?, fs::read(file2)?); #[cfg(unix)] assert_eq!( - fs::symlink_metadata(file1)?.mode() & 0b111 << 6, - fs::symlink_metadata(file2)?.mode() & 0b111 << 6, - "Testing if the permissions of {} and {} are the same", + fs::symlink_metadata(file1)?.mode() & 0o700, + fs::symlink_metadata(file2)?.mode() & 0o700, + "Testing if the permissions (normal/executable) of {} and {} are the same", file1.display(), file2.display(), ); @@ -76,39 +71,28 @@ fn test_copy_index_without_symlinks() -> crate::Result<()> { let repo_files = dir_structure(&path); let copy_files = dir_structure(output); - let srepo_files: Vec<_> = repo_files.iter().flat_map(|p| p.strip_prefix(&path)).collect(); - let scopy_files: Vec<_> = copy_files.iter().flat_map(|p| p.strip_prefix(output)).collect(); assert_eq!( - srepo_files, - scopy_files, - "Testing if {} and {} have the same structure", - path.display(), - output.display() + repo_files + .iter() + .flat_map(|p| p.strip_prefix(&path)) + .collect::>(), + copy_files + .iter() + .flat_map(|p| p.strip_prefix(output)) + .collect::>() ); for (file1, file2) in repo_files.iter().zip(copy_files.iter()) { if file1.is_symlink() { - assert!(!file2.is_symlink(), "Testing if the new file is not a symlink"); - assert_eq!( - fs::read(file2)?.to_path()?, - fs::read_link(file1)?, - "Testing if the {} contains the path the symlink {} is pointing to", - file2.display(), - file1.display(), - ); + assert!(!file2.is_symlink()); + assert_eq!(fs::read(file2)?.to_path()?, fs::read_link(file1)?); } else { - assert_eq!( - fs::read(file1)?, - fs::read(file2)?, - "Testing if the contents of {} and {} are the same", - file1.display(), - file2.display(), - ); + assert_eq!(fs::read(file1)?, fs::read(file2)?); #[cfg(unix)] assert_eq!( - fs::symlink_metadata(file1)?.mode() & 0b111 << 6, - fs::symlink_metadata(file2)?.mode() & 0b111 << 6, - "Testing if the permissions of {} and {} are the same", + fs::symlink_metadata(file1)?.mode() & 0o700, + fs::symlink_metadata(file2)?.mode() & 0o700, + "Testing if the permissions (normal/executable) of {} and {} are the same", file1.display(), file2.display(), ); diff --git a/git-worktree/tests/fixtures/make_repo.sh b/git-worktree/tests/fixtures/make_repo.sh index e573f721f14..cc2c6ccf42d 100755 --- a/git-worktree/tests/fixtures/make_repo.sh +++ b/git-worktree/tests/fixtures/make_repo.sh @@ -2,6 +2,7 @@ set -eu -o pipefail git init -q +git config commit.gpgsign false touch a echo "Test Vals" > a