diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 1b367497ffa..bff4ab93098 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -301,7 +301,18 @@ impl ThreadSafeRepository { | gix_config::Source::EnvOverride => wt_path, _ => git_dir.join(wt_path).into(), }; - worktree_dir = gix_path::normalize(wt_path, current_dir).map(Cow::into_owned); + + // the reason we use realpath instead of gix_path::normalize here is because there + // could be any intermediate symlinks (for example due to a symlinked .git + // directory) + worktree_dir = gix_path::realpath(&wt_path).ok(); + // restore the relative path if possible after resolving the absolute path + if wt_path.is_relative() { + if let Some(rel_path) = worktree_dir.as_deref().and_then(|p| p.strip_prefix(current_dir).ok()) { + worktree_dir = Some(rel_path.to_path_buf()); + } + } + #[allow(unused_variables)] if let Some(worktree_path) = worktree_dir.as_deref().filter(|wtd| !wtd.is_dir()) { gix_trace::warn!("The configured worktree path '{}' is not a directory or doesn't exist - `core.worktree` may be misleading", worktree_path.display()); diff --git a/gix/tests/fixtures/generated-archives/.gitignore b/gix/tests/fixtures/generated-archives/.gitignore index 9279e744abb..82408d215eb 100644 --- a/gix/tests/fixtures/generated-archives/.gitignore +++ b/gix/tests/fixtures/generated-archives/.gitignore @@ -8,4 +8,5 @@ /make_signatures_repo.tar /make_diff_repos.tar /make_submodule_with_worktree.tar -/repo_with_untracked_files.tar \ No newline at end of file +/repo_with_untracked_files.tar +/make_submodule_with_symlinked_git_dir.tar diff --git a/gix/tests/fixtures/make_submodule_with_symlinked_git_dir.sh b/gix/tests/fixtures/make_submodule_with_symlinked_git_dir.sh new file mode 100755 index 00000000000..1ea5a1071ac --- /dev/null +++ b/gix/tests/fixtures/make_submodule_with_symlinked_git_dir.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q module1 +(cd module1 + touch this + mkdir subdir + touch subdir/that + git add . + git commit -q -m c1 + echo hello >> this + git commit -q -am c2 + touch untracked +) + +mkdir symlinked-git-dir +(cd symlinked-git-dir + git init -q r1 + (cd r1 + git commit -q --allow-empty -m "init" + ) + + git config -f r1/.git/config core.worktree "$(pwd)" + ln -s r1/.git .git + + git -c protocol.file.allow=always submodule add ../module1 m1 + git commit -m "add module 1" +) diff --git a/gix/tests/gix/status.rs b/gix/tests/gix/status.rs index 8b832f5c2c8..cb68289de4c 100644 --- a/gix/tests/gix/status.rs +++ b/gix/tests/gix/status.rs @@ -310,6 +310,27 @@ mod index_worktree { ); } + #[test] + fn submodule_in_symlinked_dir() -> crate::Result { + use crate::util::named_subrepo_opts; + let repo = named_subrepo_opts( + "make_submodule_with_symlinked_git_dir.sh", + "symlinked-git-dir", + gix::open::Options::isolated(), + )?; + let status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + .into_index_worktree_iter(None)?; + for change in status { + change?; + } + Ok(()) + } + #[test] fn submodule_modification() -> crate::Result { let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?;