From 85e18d21534d31791df0f43335c36deeb16996c4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Oct 2024 07:57:47 +0200 Subject: [PATCH 1/4] new layout for integration tests --- gix-discover/tests/{ => discover}/is_git/mod.rs | 0 gix-discover/tests/{discover.rs => discover/main.rs} | 0 gix-discover/tests/{ => discover}/parse/mod.rs | 0 gix-discover/tests/{ => discover}/path/mod.rs | 0 gix-discover/tests/{ => discover}/upwards/ceiling_dirs.rs | 0 gix-discover/tests/{ => discover}/upwards/mod.rs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename gix-discover/tests/{ => discover}/is_git/mod.rs (100%) rename gix-discover/tests/{discover.rs => discover/main.rs} (100%) rename gix-discover/tests/{ => discover}/parse/mod.rs (100%) rename gix-discover/tests/{ => discover}/path/mod.rs (100%) rename gix-discover/tests/{ => discover}/upwards/ceiling_dirs.rs (100%) rename gix-discover/tests/{ => discover}/upwards/mod.rs (100%) diff --git a/gix-discover/tests/is_git/mod.rs b/gix-discover/tests/discover/is_git/mod.rs similarity index 100% rename from gix-discover/tests/is_git/mod.rs rename to gix-discover/tests/discover/is_git/mod.rs diff --git a/gix-discover/tests/discover.rs b/gix-discover/tests/discover/main.rs similarity index 100% rename from gix-discover/tests/discover.rs rename to gix-discover/tests/discover/main.rs diff --git a/gix-discover/tests/parse/mod.rs b/gix-discover/tests/discover/parse/mod.rs similarity index 100% rename from gix-discover/tests/parse/mod.rs rename to gix-discover/tests/discover/parse/mod.rs diff --git a/gix-discover/tests/path/mod.rs b/gix-discover/tests/discover/path/mod.rs similarity index 100% rename from gix-discover/tests/path/mod.rs rename to gix-discover/tests/discover/path/mod.rs diff --git a/gix-discover/tests/upwards/ceiling_dirs.rs b/gix-discover/tests/discover/upwards/ceiling_dirs.rs similarity index 100% rename from gix-discover/tests/upwards/ceiling_dirs.rs rename to gix-discover/tests/discover/upwards/ceiling_dirs.rs diff --git a/gix-discover/tests/upwards/mod.rs b/gix-discover/tests/discover/upwards/mod.rs similarity index 100% rename from gix-discover/tests/upwards/mod.rs rename to gix-discover/tests/discover/upwards/mod.rs From 208cf5a663da9728da98ff6be30534049ae38568 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Oct 2024 07:55:04 +0200 Subject: [PATCH 2/4] fix: properly classify non-bare repositories without worktree. Such repositories claim they are not bare, but also don't have a workdir with a .git directory inside of it (which is always needed unless certain variables are set). Now they are classified as repository that needs more detailed checking later, while indicating that it 'usually' would be having a worktree that is configured via git configuration. --- gix-discover/src/is.rs | 129 +++--------------- gix-discover/src/lib.rs | 2 + gix-discover/src/repository.rs | 11 +- gix-discover/src/upwards/mod.rs | 6 +- gix-discover/tests/discover/is_git/mod.rs | 24 ++++ .../tests/fixtures/make_basic_repo.sh | 11 ++ 6 files changed, 65 insertions(+), 118 deletions(-) diff --git a/gix-discover/src/is.rs b/gix-discover/src/is.rs index c0172ef9a06..6817a5fe8af 100644 --- a/gix-discover/src/is.rs +++ b/gix-discover/src/is.rs @@ -9,107 +9,6 @@ pub fn bare(git_dir_candidate: &Path) -> bool { !(git_dir_candidate.join("index").exists() || (git_dir_candidate.file_name() == Some(OsStr::new(DOT_GIT_DIR)))) } -/// Parse `/config` quickly to evaluate the value of the `bare` line, or return `true` if the file doesn't exist -/// similar to what`guess_repository_type` seems to be doing. -/// Return `None` if the `bare` line can't be found or the value of `bare` can't be determined. -fn bare_by_config(git_dir_candidate: &Path) -> std::io::Result> { - match std::fs::read(git_dir_candidate.join("config")) { - Ok(buf) => Ok(config::parse_bare(&buf)), - Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(Some(true)), - Err(err) => Err(err), - } -} - -// Copied and adapted from `gix-config-value::boolean`. -mod config { - use bstr::{BStr, ByteSlice}; - - /// Note that we intentionally turn repositories that have a worktree configuration into bare repos, - /// as we don't actually parse the worktree from the config file and expect the caller to do the right - /// think when seemingly seeing bare repository. - /// The reason we do this is to not incorrectly pretend this is a worktree. - pub(crate) fn parse_bare(buf: &[u8]) -> Option { - let mut is_bare = None; - let mut has_worktree_configuration = false; - for line in buf.lines() { - if is_bare.is_none() { - if let Some(line) = line.trim().strip_prefix(b"bare") { - is_bare = match line.first() { - None => Some(true), - Some(c) if *c == b'=' => parse_bool(line.get(1..)?.trim_start().as_bstr()), - Some(c) if c.is_ascii_whitespace() => match line.split_once_str(b"=") { - Some((_left, right)) => parse_bool(right.trim_start().as_bstr()), - None => Some(true), - }, - Some(_other_char_) => None, - }; - continue; - } - } - if line.trim().strip_prefix(b"worktree").is_some() { - has_worktree_configuration = true; - break; - } - } - is_bare.map(|bare| bare || has_worktree_configuration) - } - - fn parse_bool(value: &BStr) -> Option { - Some(if parse_true(value) { - true - } else if parse_false(value) { - false - } else { - use std::str::FromStr; - if let Some(integer) = value.to_str().ok().and_then(|s| i64::from_str(s).ok()) { - integer != 0 - } else { - return None; - } - }) - } - - fn parse_true(value: &BStr) -> bool { - value.eq_ignore_ascii_case(b"yes") || value.eq_ignore_ascii_case(b"on") || value.eq_ignore_ascii_case(b"true") - } - - fn parse_false(value: &BStr) -> bool { - value.eq_ignore_ascii_case(b"no") - || value.eq_ignore_ascii_case(b"off") - || value.eq_ignore_ascii_case(b"false") - || value.is_empty() - } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn various() { - for (input, expected) in [ - ("bare=true", Some(true)), - ("bare=1", Some(true)), - ("bare =1", Some(true)), - ("bare= yes", Some(true)), - ("bare=false", Some(false)), - ("bare=0", Some(false)), - ("bare=blah", None), - ("bare=", Some(false)), - ("bare= \n", Some(false)), - ("bare = true \n", Some(true)), - ("\t bare = false \n", Some(false)), - ("\n\tbare=true", Some(true)), - ("\n\tbare=true\n\tfoo", Some(true)), - ("\n\tbare ", Some(true)), - ("\n\tbare", Some(true)), - ("not found\nreally", None), - ] { - assert_eq!(parse_bare(input.as_bytes()), expected, "{input:?}"); - } - } - } -} - /// Returns true if `git_dir` is located within a `.git/modules` directory, indicating it's a submodule clone. pub fn submodule_git_dir(git_dir: &Path) -> bool { let mut last_comp = None; @@ -141,12 +40,15 @@ pub fn git(git_dir: &Path) -> Result Result { #[derive(Eq, PartialEq)] enum Kind { @@ -166,6 +68,8 @@ pub(crate) fn git_with_metadata( { // Fast-path: avoid doing the complete search if HEAD is already not there. // TODO(reftable): use a ref-store to lookup HEAD if ref-tables should be supported, or detect ref-tables beforehand. + // Actually ref-tables still keep a specially marked `HEAD` around, so nothing might be needed here + // Even though our head-check later would fail without supporting it. if !dot_git.join("HEAD").exists() { return Err(crate::is_git::Error::MissingHead); } @@ -236,25 +140,26 @@ pub(crate) fn git_with_metadata( }, Kind::MaybeRepo => { let conformed_git_dir = if git_dir == Path::new(".") { - gix_path::realpath(git_dir) + gix_path::realpath_opts(git_dir, cwd, gix_path::realpath::MAX_SYMLINKS) .map(Cow::Owned) .unwrap_or(Cow::Borrowed(git_dir)) } else { - Cow::Borrowed(git_dir) + gix_path::normalize(git_dir.into(), cwd).unwrap_or(Cow::Borrowed(git_dir)) }; if bare(conformed_git_dir.as_ref()) || conformed_git_dir.extension() == Some(OsStr::new("git")) { crate::repository::Kind::PossiblyBare } else if submodule_git_dir(conformed_git_dir.as_ref()) { crate::repository::Kind::SubmoduleGitDir - } else if conformed_git_dir.file_name() == Some(OsStr::new(DOT_GIT_DIR)) - || !bare_by_config(conformed_git_dir.as_ref()) - .map_err(|err| crate::is_git::Error::Metadata { - source: err, - path: conformed_git_dir.join("config"), - })? - .ok_or(crate::is_git::Error::Inconclusive)? - { + } else if conformed_git_dir.file_name() == Some(OsStr::new(DOT_GIT_DIR)) { crate::repository::Kind::WorkTree { linked_git_dir: None } + // } else if !bare_by_config(conformed_git_dir.as_ref()) + // .map_err(|err| crate::is_git::Error::Metadata { + // source: err, + // path: conformed_git_dir.join("config"), + // })? + // .ok_or(crate::is_git::Error::Inconclusive)? + // { + // crate::repository::Kind::WorktreePossiblyInConfiguration } else { crate::repository::Kind::PossiblyBare } diff --git a/gix-discover/src/lib.rs b/gix-discover/src/lib.rs index a035c5cf6bb..df0ca849489 100644 --- a/gix-discover/src/lib.rs +++ b/gix-discover/src/lib.rs @@ -39,6 +39,8 @@ pub mod is_git { Metadata { source: std::io::Error, path: PathBuf }, #[error("The repository's config file doesn't exist or didn't have a 'bare' configuration or contained core.worktree without value")] Inconclusive, + #[error("Could not obtain current directory when conforming repository path")] + CurrentDir(#[from] std::io::Error), } } diff --git a/gix-discover/src/repository.rs b/gix-discover/src/repository.rs index 7d0e20f1889..92fc633f354 100644 --- a/gix-discover/src/repository.rs +++ b/gix-discover/src/repository.rs @@ -14,8 +14,10 @@ pub enum Path { /// The currently checked out or nascent work tree of a git repository WorkTree(PathBuf), /// The git repository itself, typically bare and without known worktree. + /// It could also be non-bare with a worktree configured using git configuration, or no worktree at all despite + /// not being bare (due to mis-configuration for example). /// - /// Note that it might still have linked work-trees which can be accessed later, weather bare or not, or it might be a + /// Note that it might still have linked work-trees which can be accessed later, bare or not, or it might be a /// submodule git directory in the `.git/modules/**/` directory of the parent repository. Repository(PathBuf), } @@ -112,8 +114,11 @@ pub enum Kind { /// Note that this is merely a guess at this point as we didn't read the configuration yet. /// /// Also note that due to optimizing for performance and *just* making an educated *guess in some situations*, - /// we may consider a non-bare repository bare if it it doesn't have an index yet due to be freshly initialized. - /// The caller is has to handle this, typically by reading the configuration. + /// we may consider a non-bare repository bare if it doesn't have an index yet due to be freshly initialized. + /// The caller has to handle this, typically by reading the configuration. + /// + /// It could also be a directory which is non-bare by configuration, but is *not* named `.git`. + /// Unusual, but it's possible that a worktree is configured via `core.worktree`. PossiblyBare, /// A `git` repository along with checked out files in a work tree. WorkTree { diff --git a/gix-discover/src/upwards/mod.rs b/gix-discover/src/upwards/mod.rs index b39d216c659..7419e0c7600 100644 --- a/gix-discover/src/upwards/mod.rs +++ b/gix-discover/src/upwards/mod.rs @@ -38,11 +38,11 @@ pub(crate) mod function { // Normalize the path so that `Path::parent()` _actually_ gives // us the parent directory. (`Path::parent` just strips off the last // path component, which means it will not do what you expect when - // working with paths paths that contain '..'.) + // working with paths that contain '..'.) let cwd = current_dir.map_or_else( || { // The paths we return are relevant to the repository, but at this time it's impossible to know - // what `core.precomposeUnicode` is going to be. Hence the one using these paths will have to + // what `core.precomposeUnicode` is going to be. Hence, the one using these paths will have to // transform the paths as needed, because we can't. `false` means to leave the obtained path as is. gix_fs::current_dir(false).map(Cow::Owned) }, @@ -130,7 +130,7 @@ pub(crate) mod function { cursor_metadata_backup = cursor_metadata.take(); } if let Ok(kind) = match cursor_metadata.take() { - Some(metadata) => is_git_with_metadata(&cursor, metadata), + Some(metadata) => is_git_with_metadata(&cursor, metadata, &cwd), None => is_git(&cursor), } { match filter_by_trust(&cursor)? { diff --git a/gix-discover/tests/discover/is_git/mod.rs b/gix-discover/tests/discover/is_git/mod.rs index 060720d46f1..09f8576a409 100644 --- a/gix-discover/tests/discover/is_git/mod.rs +++ b/gix-discover/tests/discover/is_git/mod.rs @@ -58,6 +58,30 @@ fn bare_repo_with_index_file_looks_still_looks_like_bare() -> crate::Result { Ok(()) } +#[test] +fn non_bare_repo_without_workdir() -> crate::Result { + let repo = repo_path()?.join("non-bare-without-worktree"); + let kind = gix_discover::is_git(&repo)?; + assert_eq!( + kind, + gix_discover::repository::Kind::PossiblyBare, + "typically due to misconfiguration, but worktrees could also be configured in Git configuration" + ); + Ok(()) +} + +#[test] +fn non_bare_repo_without_workdir_with_index() -> crate::Result { + let repo = repo_path()?.join("non-bare-without-worktree-with-index"); + let kind = gix_discover::is_git(&repo)?; + assert_eq!( + kind, + gix_discover::repository::Kind::PossiblyBare, + "this means it has to be validated later" + ); + Ok(()) +} + #[test] fn bare_repo_with_index_file_looks_still_looks_like_bare_if_it_was_renamed() -> crate::Result { for repo_name in ["bare-with-index-bare", "bare-with-index-no-config-bare"] { diff --git a/gix-discover/tests/fixtures/make_basic_repo.sh b/gix-discover/tests/fixtures/make_basic_repo.sh index b0c79baf404..4895aab8295 100755 --- a/gix-discover/tests/fixtures/make_basic_repo.sh +++ b/gix-discover/tests/fixtures/make_basic_repo.sh @@ -15,6 +15,17 @@ mkdir -p some/very/deeply/nested/subdir git clone --bare --shared . bare.git +git clone --bare --shared . non-bare-without-worktree +(cd non-bare-without-worktree + git config core.bare false +) + +git clone --bare --shared . non-bare-without-worktree-with-index +(cd non-bare-without-worktree + git config core.bare false + cp ../.git/index . +) + git worktree add worktrees/a git worktree add worktrees/b-private-dir-deleted rm -R .git/worktrees/b-private-dir-deleted From f8952e4cbfaf9ab7ddc12a028a1cdb821ac9a3b1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Oct 2024 17:30:39 +0200 Subject: [PATCH 3/4] fix: don't be too generous when extrapolating worktree directories. Previously it was possible that a non-bare repository that didn't have worktree directory incorrectly claimed it had one. --- gix/src/open/repository.rs | 8 +++--- .../generated-archives/make_basic_repo.tar | Bin 311296 -> 342016 bytes gix/tests/fixtures/make_basic_repo.sh | 5 ++++ gix/tests/gix/repository/mod.rs | 1 + gix/tests/gix/repository/open.rs | 24 ++++++++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 679abd4a7dc..7e369ffa6b9 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -1,7 +1,7 @@ #![allow(clippy::result_large_err)] -use std::{borrow::Cow, path::PathBuf}; - use gix_features::threading::OwnShared; +use std::ffi::OsStr; +use std::{borrow::Cow, path::PathBuf}; use super::{Error, Options}; use crate::{ @@ -86,7 +86,7 @@ impl ThreadSafeRepository { } }; - // The be altered later based on `core.precomposeUnicode`. + // To be altered later based on `core.precomposeUnicode`. let cwd = gix_fs::current_dir(false)?; let (git_dir, worktree_dir) = gix_discover::repository::Path::from_dot_git_dir(path, kind, &cwd) .expect("we have sanitized path with is_git()") @@ -284,7 +284,7 @@ impl ThreadSafeRepository { } match worktree_dir { - None if !config.is_bare => { + None if !config.is_bare && refs.git_dir().extension() == Some(OsStr::new(gix_discover::DOT_GIT_DIR)) => { worktree_dir = Some(git_dir.parent().expect("parent is always available").to_owned()); } Some(_) => { diff --git a/gix/tests/fixtures/generated-archives/make_basic_repo.tar b/gix/tests/fixtures/generated-archives/make_basic_repo.tar index 07621a9467567cf8f68ca914cf60a2a6801d8cad..f65e4ce784510b8f8618bfba98d7434ee0142174 100644 GIT binary patch delta 4662 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 TSDz;Dk)m|wobwBEvv>Rt!KM|B diff --git a/gix/tests/fixtures/make_basic_repo.sh b/gix/tests/fixtures/make_basic_repo.sh index 45defa67648..e3cd8024ec7 100755 --- a/gix/tests/fixtures/make_basic_repo.sh +++ b/gix/tests/fixtures/make_basic_repo.sh @@ -36,4 +36,9 @@ git init all-untracked git init empty-core-excludes (cd empty-core-excludes echo $'[core]\n\texcludesFile = ' >> .git/config +) + +git clone --bare . non-bare-without-worktree +(cd non-bare-without-worktree + git config core.bare false ) \ No newline at end of file diff --git a/gix/tests/gix/repository/mod.rs b/gix/tests/gix/repository/mod.rs index 750987bc590..8acf0308529 100644 --- a/gix/tests/gix/repository/mod.rs +++ b/gix/tests/gix/repository/mod.rs @@ -42,6 +42,7 @@ mod dirwalk { ("bare.git".into(), Directory), ("empty-core-excludes".into(), Repository), ("non-bare-repo-without-index".into(), Repository), + ("non-bare-without-worktree".into(), Directory), ("some".into(), Directory), ]; assert_eq!( diff --git a/gix/tests/gix/repository/open.rs b/gix/tests/gix/repository/open.rs index 17a658dbf28..cea3bf8ceca 100644 --- a/gix/tests/gix/repository/open.rs +++ b/gix/tests/gix/repository/open.rs @@ -62,6 +62,30 @@ fn bare_repo_with_index() -> crate::Result { repo.is_bare(), "it's properly classified as it reads the configuration (and has no worktree)" ); + assert_eq!(repo.work_dir(), None); + Ok(()) +} + +#[test] +fn non_bare_non_git_repo_without_worktree() -> crate::Result { + let repo = named_subrepo_opts( + "make_basic_repo.sh", + "non-bare-without-worktree", + gix::open::Options::isolated(), + )?; + assert!(!repo.is_bare()); + assert_eq!(repo.work_dir(), None, "it doesn't assume that workdir exists"); + + let repo = gix::open_opts( + repo.git_dir().join("objects").join(".."), + gix::open::Options::isolated(), + )?; + assert!(!repo.is_bare()); + assert_eq!( + repo.work_dir(), + None, + "it figures this out even if a non-normalized gitdir is used" + ); Ok(()) } From c18ebbeabb3e4bd775cf59bd90e6672749ce9549 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 11 Oct 2024 17:57:00 +0200 Subject: [PATCH 4/4] silently re-add `diff::tree::ChangeDetached` It's nice to have a symmetric API and it fits into `gix`. --- gix/src/object/tree/diff/change.rs | 2 +- gix/src/object/tree/diff/mod.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gix/src/object/tree/diff/change.rs b/gix/src/object/tree/diff/change.rs index a40c7100a91..0651d8dbd11 100644 --- a/gix/src/object/tree/diff/change.rs +++ b/gix/src/object/tree/diff/change.rs @@ -1,8 +1,8 @@ +use super::ChangeDetached; use crate::bstr::{BStr, ByteSlice}; use crate::ext::ObjectIdExt; use crate::object::tree::diff::Change; use crate::Repository; -use gix_diff::tree_with_rewrites::Change as ChangeDetached; impl Change<'_, '_, '_> { /// Produce a platform for performing a line-diff no matter whether the underlying [Change] is an addition, modification, diff --git a/gix/src/object/tree/diff/mod.rs b/gix/src/object/tree/diff/mod.rs index 592600055f6..adab1c34504 100644 --- a/gix/src/object/tree/diff/mod.rs +++ b/gix/src/object/tree/diff/mod.rs @@ -12,6 +12,8 @@ pub enum Action { Cancel, } +pub use gix_diff::tree_with_rewrites::Change as ChangeDetached; + /// Represents any possible change in order to turn one tree into another. #[derive(Debug, Clone, Copy)] pub enum Change<'a, 'old, 'new> {