From c85b92d991a91bb1044fe5f784524f568e72c45b Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 May 2025 11:02:36 +0200 Subject: [PATCH 1/5] fix: Prefer the actual state over following `core.symlinks` in `entry::Mode` --- gix-index/src/entry/mode.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/gix-index/src/entry/mode.rs b/gix-index/src/entry/mode.rs index 817d7a06e0e..3b29548485f 100644 --- a/gix-index/src/entry/mode.rs +++ b/gix-index/src/entry/mode.rs @@ -49,6 +49,7 @@ impl Mode { ) -> Option { match self { Mode::FILE if !stat.is_file() => (), + Mode::SYMLINK if stat.is_symlink() => return None, Mode::SYMLINK if has_symlinks && !stat.is_symlink() => (), Mode::SYMLINK if !has_symlinks && !stat.is_file() => (), Mode::COMMIT | Mode::DIR if !stat.is_dir() => (), From 376ed0cb602e4df457b0b6c87fe16af027bdff48 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 May 2025 10:24:02 +0200 Subject: [PATCH 2/5] fix: if `core.symlinks=false`, don't misclassify actual symlinks as files. Thus, prefer the actual observation over the stored and maybe incorrect filesystem settings. This avoids false-positives when checking for changes. --- gix-status/src/index_as_worktree/function.rs | 23 +++++++++++----- gix-status/tests/status/index_as_worktree.rs | 29 +++++++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/gix-status/src/index_as_worktree/function.rs b/gix-status/src/index_as_worktree/function.rs index 72e58e8457a..474c9c6c1b0 100644 --- a/gix-status/src/index_as_worktree/function.rs +++ b/gix-status/src/index_as_worktree/function.rs @@ -408,12 +408,12 @@ impl<'index> State<'_, 'index> { None => false, }; - // Here we implement racy-git. See racy-git.txt in the git documentation for a detailed documentation. + // We implement racy-git. See racy-git.txt in the git documentation for detailed documentation. // // A file is racy if: - // 1. its `mtime` is at or after the last index timestamp and its entry stat information - // matches the on-disk file but the file contents are actually modified - // 2. it's size is 0 (set after detecting a file was racy previously) + // 1. Its `mtime` is at or after the last index timestamp and its entry stat information + // matches the on-disk file, but the file contents are actually modified + // 2. Its size is 0 (set after detecting a file was racy previously) // // The first case is detected below by checking the timestamp if the file is marked unmodified. // The second case is usually detected either because the on-disk file is not empty, hence @@ -449,7 +449,16 @@ impl<'index> State<'_, 'index> { file_len: file_size_bytes, filter: &mut self.filter, attr_stack: &mut self.attr_stack, - options: self.options, + core_symlinks: + // If this is legitimately a symlink, then pretend symlinks are enabled as the option seems stale. + // Otherwise, respect the option. + if metadata.is_symlink() + && entry.mode.to_tree_entry_mode().map(|m| m.kind()) == Some(gix_object::tree::EntryKind::Link) + { + true + } else { + self.options.fs.symlink + }, id: &entry.id, objects, worktree_reads: self.worktree_reads, @@ -517,7 +526,7 @@ where entry: &'a gix_index::Entry, filter: &'a mut gix_filter::Pipeline, attr_stack: &'a mut gix_worktree::Stack, - options: &'a Options, + core_symlinks: bool, id: &'a gix_hash::oid, objects: Find, worktree_bytes: &'a AtomicU64, @@ -545,7 +554,7 @@ where // let is_symlink = self.entry.mode == gix_index::entry::Mode::SYMLINK; // TODO: what to do about precompose unicode and ignore_case for symlinks - let out = if is_symlink && self.options.fs.symlink { + let out = if is_symlink && self.core_symlinks { // conversion to bstr can never fail because symlinks are only used // on unix (by git) so no reason to use the try version here let symlink_path = diff --git a/gix-status/tests/status/index_as_worktree.rs b/gix-status/tests/status/index_as_worktree.rs index a5f512f2b5b..d0f7a4a57f0 100644 --- a/gix-status/tests/status/index_as_worktree.rs +++ b/gix-status/tests/status/index_as_worktree.rs @@ -48,6 +48,7 @@ fn nonfile_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome { false, Default::default(), false, + None, ) } @@ -65,6 +66,7 @@ fn fixture_with_index( false, Default::default(), false, + None, ) } @@ -78,6 +80,7 @@ fn submodule_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome false, Default::default(), false, + None, ) } @@ -91,6 +94,7 @@ fn conflict_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome false, Default::default(), false, + None, ) } @@ -104,6 +108,7 @@ fn submodule_fixture_status(name: &str, expected_status: &[Expectation<'_>], sub submodule_dirty, Default::default(), false, + None, ) } @@ -117,6 +122,7 @@ fn fixture_filtered(name: &str, pathspecs: &[&str], expected_status: &[Expectati false, Default::default(), false, + None, ) } @@ -130,6 +136,7 @@ fn fixture_filtered_detailed( submodule_dirty: bool, auto_crlf: gix_filter::eol::AutoCrlf, use_odb: bool, + fs_capabilities: Option<&dyn Fn(&std::path::Path) -> gix_fs::Capabilities>, ) -> Outcome { // This can easily happen in some fixtures, which can cause flakiness. It's time-dependent after all. fn ignore_racyclean(mut out: Outcome) -> Outcome { @@ -179,7 +186,7 @@ fn fixture_filtered_detailed( should_interrupt: &AtomicBool::default(), }; let options = Options { - fs: gix_fs::Capabilities::probe(&git_dir), + fs: fs_capabilities.map_or_else(|| gix_fs::Capabilities::probe(&git_dir), |new| new(&git_dir)), stat: TEST_OPTIONS, ..Options::default() }; @@ -353,6 +360,7 @@ fn replace_dir_with_file() { false, Default::default(), false, + None, ); assert_eq!( out, @@ -560,6 +568,24 @@ fn unchanged() { fixture("status_unchanged", &[]); } +#[test] +fn unchanged_symlinks_present_but_deactivated() { + fixture_filtered_detailed( + "status_unchanged", + "", + &[], + &[], + |_| {}, + false, + Default::default(), + false, + Some(&|dir| gix_fs::Capabilities { + symlink: false, + ..gix_fs::Capabilities::probe(dir) + }), + ); +} + #[test] fn unchanged_despite_filter() { let actual_outcome = fixture_filtered_detailed( @@ -571,6 +597,7 @@ fn unchanged_despite_filter() { false, AutoCrlf::Enabled, true, /* make ODB available */ + None, ); let expected_outcome = Outcome { From 26ae766b182218151ae4c3f30306b6d41bab358a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 May 2025 11:29:22 +0200 Subject: [PATCH 3/5] fix!: allow querying `Repository::submodules()` in an unborn repository. It's a breaking change merely because the error type changed. --- gix/src/repository/submodule.rs | 20 ++++++++++++------ gix/src/status/index_worktree.rs | 7 ------ gix/src/submodule/errors.rs | 8 +++++-- .../generated-archives/make_submodules.tar | Bin 2120192 -> 2162688 bytes gix/tests/fixtures/make_submodules.sh | 2 ++ gix/tests/gix/submodule.rs | 11 ++++++++++ 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/gix/src/repository/submodule.rs b/gix/src/repository/submodule.rs index a605bfbd322..bcd54bc382b 100644 --- a/gix/src/repository/submodule.rs +++ b/gix/src/repository/submodule.rs @@ -26,7 +26,7 @@ impl Repository { )?)) } - /// Return a shared [`.gitmodules` file](crate::submodule::File) which is updated automatically if the in-memory snapshot + /// Return a shared [`.gitmodules` file](submodule::File) which is updated automatically if the in-memory snapshot /// has become stale as the underlying file on disk has changed. The snapshot based on the file on disk is shared across all /// clones of this repository. /// @@ -54,12 +54,20 @@ impl Repository { }) { Some(id) => id, None => match self - .head_commit()? - .tree()? - .find_entry(submodule::MODULES_FILE) - .map(|entry| entry.inner.oid) + .head()? + .try_peel_to_id_in_place()? + .map(|id| -> Result, submodule::modules::Error> { + Ok(id + .object()? + .peel_to_commit()? + .tree()? + .find_entry(submodule::MODULES_FILE) + .map(|entry| entry.inner.oid.to_owned())) + }) + .transpose()? + .flatten() { - Some(id) => id.to_owned(), + Some(id) => id, None => return Ok(None), }, }; diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs index 86a7dccc877..1911650a1c2 100644 --- a/gix/src/status/index_worktree.rs +++ b/gix/src/status/index_worktree.rs @@ -226,13 +226,6 @@ mod submodule_status { v } Ok(None) => Vec::new(), - Err(crate::submodule::modules::Error::FindHeadCommit( - crate::reference::head_commit::Error::PeelToCommit( - crate::head::peel::to_commit::Error::PeelToObject( - crate::head::peel::to_object::Error::Unborn { .. }, - ), - ), - )) => Vec::new(), Err(err) => return Err(err), }; Ok(Self { diff --git a/gix/src/submodule/errors.rs b/gix/src/submodule/errors.rs index 4e41337de45..81c76b8fe8a 100644 --- a/gix/src/submodule/errors.rs +++ b/gix/src/submodule/errors.rs @@ -23,8 +23,12 @@ pub mod modules { OpenIndex(#[from] crate::worktree::open_index::Error), #[error("Could not find the .gitmodules file by id in the object database")] FindExistingBlob(#[from] crate::object::find::existing::Error), - #[error("Did not find commit in current HEAD to access its tree")] - FindHeadCommit(#[from] crate::reference::head_commit::Error), + #[error(transparent)] + FindHeadRef(#[from] crate::reference::find::existing::Error), + #[error(transparent)] + PeelHeadRef(#[from] crate::head::peel::Error), + #[error(transparent)] + PeelObjectToCommit(#[from] crate::object::peel::to_kind::Error), #[error(transparent)] TreeFromCommit(#[from] crate::object::commit::Error), } diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar b/gix/tests/fixtures/generated-archives/make_submodules.tar index 4a3566aabdfa490b91c64243ca1539001d4c4ba6..220c4e9a56f672ef4bc0171fe5fc93b30a5fc5b0 100644 GIT binary patch delta 7235 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 delta 6122 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 X^YDT>?*N&K^%?~qT+iC{$RPL^+DI?3 diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index 906170711ed..524faf6282a 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -144,3 +144,5 @@ git clone with-submodules not-a-submodule mv modules.bak .gitmodules git add m1 && git commit -m "no submodule in index and commit, but in configuration" ) + +git init unborn \ No newline at end of file diff --git a/gix/tests/gix/submodule.rs b/gix/tests/gix/submodule.rs index 597c97948ce..ab29c22c018 100644 --- a/gix/tests/gix/submodule.rs +++ b/gix/tests/gix/submodule.rs @@ -340,6 +340,17 @@ mod open { Ok(()) } + #[test] + fn in_unborn() -> crate::Result { + let repo = repo("unborn")?; + assert_eq!( + repo.submodules()?.into_iter().flatten().count(), + 0, + "there is nothing, and that is fine" + ); + Ok(()) + } + #[test] #[cfg(feature = "revision")] fn submodule_worktrees() -> crate::Result { From c8a63ab4618d3a71176703a04ab69da02d850267 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 May 2025 14:08:04 +0200 Subject: [PATCH 4/5] feat: add `file::Store::is_empty()` --- gix-ref/src/store/file/mod.rs | 31 +++++++++++++++++- .../generated-archives/make_pristine.tar | Bin 0 -> 174592 bytes gix-ref/tests/fixtures/make_pristine.sh | 19 +++++++++++ gix-ref/tests/refs/file/mod.rs | 8 +++-- gix-ref/tests/refs/file/store/access.rs | 21 +++++++++++- gix-ref/tests/refs/file/store/find.rs | 1 + gix-ref/tests/refs/file/store/iter.rs | 1 + gix-ref/tests/refs/file/worktree.rs | 2 ++ 8 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 gix-ref/tests/fixtures/generated-archives/make_pristine.tar create mode 100755 gix-ref/tests/fixtures/make_pristine.sh diff --git a/gix-ref/src/store/file/mod.rs b/gix-ref/src/store/file/mod.rs index 3c7d20e8c94..982273d4a67 100644 --- a/gix-ref/src/store/file/mod.rs +++ b/gix-ref/src/store/file/mod.rs @@ -53,7 +53,7 @@ mod access { } } - use crate::file; + use crate::{file, Target}; /// Access impl file::Store { @@ -78,6 +78,35 @@ mod access { pub fn common_dir_resolved(&self) -> &Path { self.common_dir.as_deref().unwrap_or(&self.git_dir) } + + /// Return `Some(true)` if this is a freshly initialized ref store without any observable changes. + /// Return `None` if `HEAD` couldn't be read. + /// + /// This is the case if: + /// + /// * the ref-store is valid + /// * `HEAD` exists + /// * `HEAD` still points to `default_ref` + /// * there are no packed refs + /// * There are no observable references in `refs/` + pub fn is_pristine(&self, default_ref: &crate::FullNameRef) -> Option { + let head = self.find_loose("HEAD").ok()?; + match head.target { + Target::Object(_) => return Some(false), + Target::Symbolic(name) => { + if name.as_ref() != default_ref { + return Some(false); + } + } + } + if self.loose_iter().ok()?.filter_map(Result::ok).next().is_some() { + return Some(false); + } + if self.packed_refs_path().is_file() { + return Some(false); + } + Some(true) + } } } diff --git a/gix-ref/tests/fixtures/generated-archives/make_pristine.tar b/gix-ref/tests/fixtures/generated-archives/make_pristine.tar new file mode 100644 index 0000000000000000000000000000000000000000..40e705b753df6acecdf04268ea6e35f6b03f0148 GIT binary patch literal 174592 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 Lz>ETaO$z)!+U(l3 literal 0 HcmV?d00001 diff --git a/gix-ref/tests/fixtures/make_pristine.sh b/gix-ref/tests/fixtures/make_pristine.sh new file mode 100755 index 00000000000..ed42133f519 --- /dev/null +++ b/gix-ref/tests/fixtures/make_pristine.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init untouched + +git init changed-headref +(cd changed-headref + echo "ref: refs/heads/other" >.git/HEAD +) + +git init detached +(cd detached + echo "abcdefabcdefabcdefabcdefabcdefabcdefabcd" >.git/HEAD +) + +git init invalid-loose-ref +(cd invalid-loose-ref + touch .git/refs/heads/empty +) diff --git a/gix-ref/tests/refs/file/mod.rs b/gix-ref/tests/refs/file/mod.rs index 0c71dba67b5..c2e7b201b09 100644 --- a/gix-ref/tests/refs/file/mod.rs +++ b/gix-ref/tests/refs/file/mod.rs @@ -14,8 +14,12 @@ pub fn store_with_packed_refs() -> crate::Result { } pub fn store_at(name: &str) -> crate::Result { - let path = gix_testtools::scripted_fixture_read_only_standalone(name)?; - Ok(Store::at(path.join(".git"), Default::default())) + named_store_at(name, "") +} + +pub fn named_store_at(script_name: &str, name: &str) -> crate::Result { + let path = gix_testtools::scripted_fixture_read_only_standalone(script_name)?; + Ok(Store::at(path.join(name).join(".git"), Default::default())) } pub fn store_at_with_args(name: &str, args: impl IntoIterator>) -> crate::Result { diff --git a/gix-ref/tests/refs/file/store/access.rs b/gix-ref/tests/refs/file/store/access.rs index df1e7d6d0ec..b7d4097d797 100644 --- a/gix-ref/tests/refs/file/store/access.rs +++ b/gix-ref/tests/refs/file/store/access.rs @@ -1,4 +1,4 @@ -use crate::file::store; +use crate::file::{named_store_at, store}; #[test] fn set_packed_buffer_mmap_threshold() -> crate::Result { @@ -20,3 +20,22 @@ fn set_packed_buffer_mmap_threshold() -> crate::Result { ); Ok(()) } + +#[test] +fn is_pristine() -> crate::Result { + let store = named_store_at("make_pristine.sh", "untouched")?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(true)); + assert_eq!(store.is_pristine("refs/heads/other".try_into()?), Some(false)); + + let store = named_store_at("make_pristine.sh", "changed-headref")?; + assert_eq!(store.is_pristine("refs/heads/other".try_into()?), Some(true)); + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); + + let store = named_store_at("make_pristine.sh", "detached")?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); + + let store = named_store_at("make_pristine.sh", "invalid-loose-ref")?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(true)); + + Ok(()) +} diff --git a/gix-ref/tests/refs/file/store/find.rs b/gix-ref/tests/refs/file/store/find.rs index f9cb0a26489..2ecd52cddd9 100644 --- a/gix-ref/tests/refs/file/store/find.rs +++ b/gix-ref/tests/refs/file/store/find.rs @@ -9,6 +9,7 @@ mod existing { "make_packed_ref_repository_for_overlay.sh", ] { let store = store_at(fixture)?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); let r = store.find("main")?; assert_eq!(r.target.into_id(), c1); diff --git a/gix-ref/tests/refs/file/store/iter.rs b/gix-ref/tests/refs/file/store/iter.rs index 527fd03c1cb..68259c0b1b9 100644 --- a/gix-ref/tests/refs/file/store/iter.rs +++ b/gix-ref/tests/refs/file/store/iter.rs @@ -576,6 +576,7 @@ fn overlay_partial_prefix_iter_when_prefix_is_dir() -> crate::Result { use gix_ref::Target::*; let store = store_at("make_packed_ref_repository_for_overlay.sh")?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); let ref_names = store diff --git a/gix-ref/tests/refs/file/worktree.rs b/gix-ref/tests/refs/file/worktree.rs index d7b61ccf992..5d23c779f8f 100644 --- a/gix-ref/tests/refs/file/worktree.rs +++ b/gix-ref/tests/refs/file/worktree.rs @@ -84,6 +84,7 @@ mod read_only { fn linked() -> crate::Result { for packed in [false, true] { let (store, odb, _tmp) = worktree_store(packed, "w1", Mode::Read)?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); let peel = into_peel(&store, odb); let w1_head_id = peel(store.find("HEAD").unwrap()); @@ -132,6 +133,7 @@ mod read_only { fn main() -> crate::Result { for packed in [false, true] { let (store, odb, _tmp) = main_store(packed, Mode::Read)?; + assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false)); let peel = into_peel(&store, odb); let head_id = peel(store.find("HEAD").unwrap()); From b985766c9c9c5eb09ea4c4b17be9e380bfdad9b4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 18 May 2025 14:37:11 +0200 Subject: [PATCH 5/5] feat: add `Repository::is_empty()` to emulate the similar `git2` API --- gix/src/repository/location.rs | 17 +++++++++++++++++ gix/tests/gix/repository/mod.rs | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/gix/src/repository/location.rs b/gix/src/repository/location.rs index e7a18138df3..6f38b3c958d 100644 --- a/gix/src/repository/location.rs +++ b/gix/src/repository/location.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::path::{Path, PathBuf}; use gix_path::realpath::MAX_SYMLINKS; @@ -108,4 +109,20 @@ impl crate::Repository { None => crate::repository::Kind::Bare, } } + + /// Returns `Some(true)` if the reference database [is untouched](gix_ref::file::Store::is_pristine()). + /// This typically indicates that the repository is new and empty. + /// Return `None` if a defect in the database makes the answer uncertain. + #[doc(alias = "is_empty", alias = "git2")] + pub fn is_pristine(&self) -> Option { + let name = self + .config + .resolved + .string(crate::config::tree::Init::DEFAULT_BRANCH) + .unwrap_or(Cow::Borrowed("master".into())); + let default_branch_ref_name: gix_ref::FullName = format!("refs/heads/{name}") + .try_into() + .unwrap_or_else(|_| gix_ref::FullName::try_from("refs/heads/master").expect("known to be valid")); + self.refs.is_pristine(default_branch_ref_name.as_ref()) + } } diff --git a/gix/tests/gix/repository/mod.rs b/gix/tests/gix/repository/mod.rs index cf29cfc1170..4253ff4b121 100644 --- a/gix/tests/gix/repository/mod.rs +++ b/gix/tests/gix/repository/mod.rs @@ -56,6 +56,16 @@ mod index { repo.index_or_load_from_head_or_empty()?.entries().is_empty(), "an empty index is created on the fly" ); + assert_eq!( + repo.is_pristine(), + Some(false), + "not pristine as it things the initial ref was changed to 'main'" + ); + assert_eq!( + repo.refs.is_pristine("refs/heads/main".try_into()?), + Some(true), + "This is a quirk of default values in gix and the way we override the initial branch for test fixtures" + ); Ok(()) } }