diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 1b367497ffa..0a26a3184e9 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -272,7 +272,13 @@ impl ThreadSafeRepository { section .path .as_deref() - .and_then(|p| gix_path::normalize(p.into(), current_dir)) + .and_then(|p| { + if p.exists() { + gix_path::realpath_opts(p, current_dir, gix_path::realpath::MAX_SYMLINKS).ok() + } else { + gix_path::normalize(p.into(), current_dir).map(Cow::into_owned) + } + }) .is_some_and(|config_path| config_path.starts_with(git_dir)) } let worktree_path = config @@ -301,7 +307,23 @@ 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) + let is_relative = wt_path.is_relative(); + worktree_dir = if wt_path.exists() { + gix_path::realpath(&wt_path).ok() + } else { + Some(wt_path.into_owned()) + }; + // restore the relative path if possible after resolving the absolute path + if 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" +)