diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index a2668a2bd39..d71a21b9c86 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -382,6 +382,9 @@ pub mod status { return Ok(status); } + if !state.worktree_checkout { + return Ok(status); + } let statusses = adjust_options(sm_repo.status(gix_features::progress::Discard)?) .index_worktree_options_mut(|opts| { if ignore == config::Ignore::Untracked { @@ -408,7 +411,7 @@ pub mod status { /// Return `None` if the repository clone or the worktree are missing entirely, which would leave /// it to the caller to determine if that's considered dirty or not. pub fn is_dirty(&self) -> Option { - if !self.state.worktree_checkout && !self.state.repository_exists { + if !self.state.worktree_checkout || !self.state.repository_exists { return None; } let is_dirty = diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index e4c34d017c4..ac4c0109ac8 100644 Binary files a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz and b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz differ diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index 740cc278ee5..4533033e6fb 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -21,6 +21,15 @@ git init submodule-head-changed cd m1 && git checkout @~1 ) +git init submodule-head-changed-no-worktree +(cd submodule-head-changed-no-worktree + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 && git checkout @~1) + rm -Rf m1 && mkdir m1 +) + git init modified-and-untracked (cd modified-and-untracked git submodule add ../module1 m1 diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 5434ac2b152..0ff6480d88e 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -63,7 +63,7 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, - Some(true), + None, )], ), ] { @@ -241,6 +241,39 @@ mod open { Ok(()) } + #[test] + fn changed_head_empty_worktree() -> crate::Result { + let repo = repo("submodule-head-changed-no-worktree")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::None, false)?; + assert_eq!( + status.state, + gix::submodule::State { + repository_exists: true, + is_old_form: false, + worktree_checkout: false, + superproject_configuration: true, + } + ); + assert_eq!( + status.is_dirty(), + None, + "a missing worktree counts as no-dirty, even though the checked out HEAD changed. \ + Git does the same, even though as we express it as 'not determined'" + ); + assert_ne!( + status.index_id, status.checked_out_head_id, + "not considered dirty despite head mismatch" + ); + assert!( + status.changes.is_none(), + "Detailed changes are never done if there is no worktree" + ); + + Ok(()) + } + #[test] fn is_dirty_skips_expensive_checks() -> crate::Result { let repo = repo("submodule-head-changed-and-modified")?;