diff --git a/gix/src/filter.rs b/gix/src/filter.rs index 5e5e238f5f0..73917ac93a9 100644 --- a/gix/src/filter.rs +++ b/gix/src/filter.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; pub use gix_filter as plumbing; use gix_object::Find; +use crate::prelude::ObjectIdExt; use crate::{ bstr::BStr, config::{ @@ -207,6 +208,9 @@ impl Pipeline<'_> { } /// Add the worktree file at `rela_path` to the object database and return its `(id, entry, symlink_metadata)` for use in a tree or in the index, for instance. + /// If `rela_path` is a directory *and* is a repository with its `HEAD` pointing to a commit, it will also be provided with the appropriate kind. + /// Note that this can easily lead to embedded repositories as no submodule-crosscheck is performed. Otherwise, unreadable repositories or directories + /// are ignored with `None` as return value. /// /// `index` is used in particularly rare cases where the CRLF filter in auto-mode tries to determine whether to apply itself, /// and it should match the state used when [instantiating this instance](Self::new()). @@ -257,6 +261,14 @@ impl Pipeline<'_> { gix_object::tree::EntryKind::Blob }; (id, kind) + } else if md.is_dir() { + let Some(submodule_repo) = crate::open_opts(&path, repo.open_options().clone()).ok() else { + return Ok(None); + }; + let Some(id) = submodule_repo.head_id().ok() else { + return Ok(None); + }; + (id.detach().attach(repo), gix_object::tree::EntryKind::Commit) } else { // This is probably a type-change to something we can't track. return Ok(None); diff --git a/gix/tests/fixtures/repo_with_untracked_files.sh b/gix/tests/fixtures/repo_with_untracked_files.sh index e3bca58c94b..4479053657f 100755 --- a/gix/tests/fixtures/repo_with_untracked_files.sh +++ b/gix/tests/fixtures/repo_with_untracked_files.sh @@ -1,9 +1,19 @@ #!/usr/bin/env bash set -eu -o pipefail +git init embedded-repository +(cd embedded-repository + echo content >file && git add file && git commit -m "init" +) + git init -q echo content >file ln -s file link echo binary >exe && chmod +x exe mkfifo fifo + +git submodule add ./embedded-repository submodule + +mkdir empty-dir +git init uninitialized-embedded-repository \ No newline at end of file diff --git a/gix/tests/gix/repository/filter.rs b/gix/tests/gix/repository/filter.rs index eef0ec8e42b..ab7ec75198a 100644 --- a/gix/tests/gix/repository/filter.rs +++ b/gix/tests/gix/repository/filter.rs @@ -35,11 +35,32 @@ fn pipeline_in_repo_without_special_options() -> crate::Result { #[cfg(unix)] fn pipeline_worktree_file_to_object() -> crate::Result { let repo = named_repo("repo_with_untracked_files.sh")?; + let work_dir = repo.work_dir().expect("non-bare"); let (mut pipe, index) = repo.filter_pipeline(None)?; fn take_two(t: Option<(A, B, C)>) -> Option<(A, B)> { t.map(|t| (t.0, t.1)) } + let submodule_id = hex_to_id("a047f8183ba2bb7eb00ef89e60050c5fde740483"); + assert_eq!( + take_two(pipe.worktree_file_to_object("embedded-repository".into(), &index)?), + Some((submodule_id, gix::object::tree::EntryKind::Commit)) + ); + assert_eq!( + take_two(pipe.worktree_file_to_object("submodule".into(), &index)?), + Some((submodule_id, gix::object::tree::EntryKind::Commit)) + ); + assert_eq!( + take_two(pipe.worktree_file_to_object("uninitialized-embedded-repository".into(), &index)?), + None, + "repositories that don't have HEAD pointing to an ID yet are ignored" + ); + assert!(work_dir.join("empty-dir").is_dir()); + assert_eq!( + take_two(pipe.worktree_file_to_object("empty-dir".into(), &index)?), + None, + "directories that aren't even repos are also ignored" + ); assert_eq!( take_two(pipe.worktree_file_to_object("file".into(), &index)?), Some(( @@ -66,10 +87,7 @@ fn pipeline_worktree_file_to_object() -> crate::Result { None, "Missing files are specifically typed and no error" ); - assert!( - repo.work_dir().expect("non-bare").join("fifo").exists(), - "there is a fifo" - ); + assert!(work_dir.join("fifo").exists(), "there is a fifo"); assert_eq!( take_two(pipe.worktree_file_to_object("fifo".into(), &index)?), None,