Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion gix/src/open/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using realpath should be conditional, based on if the path exists or not.
Because what's under test here really is that normalize can mess up paths that have symlinks in them, yielding an invalid path.

So I think there should even be a warning in gix_path::normalize() to tell people to check if the path is still valid.
In a follow-up, one should check all call-sites as they will have the same issue.

// 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());
}
}
Comment on lines +311 to +314
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have this, there must be a test for this. Maybe it's OK to leave it out for now, since this symlink handling is already quite a special case.


#[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());
Expand Down
3 changes: 2 additions & 1 deletion gix/tests/fixtures/generated-archives/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
/make_signatures_repo.tar
/make_diff_repos.tar
/make_submodule_with_worktree.tar
/repo_with_untracked_files.tar
/repo_with_untracked_files.tar
/make_submodule_with_symlinked_git_dir.tar
28 changes: 28 additions & 0 deletions gix/tests/fixtures/make_submodule_with_symlinked_git_dir.sh
Original file line number Diff line number Diff line change
@@ -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"
)
21 changes: 21 additions & 0 deletions gix/tests/gix/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,27 @@ mod index_worktree {
);
}

#[test]
fn submodule_in_symlinked_dir() -> crate::Result {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should be removed in favor of specific tests which make clear what's under test.
This test 'only' tests if the status succeeds, otherwise it fails with something like.

Error: IndexAsWorktreeWithRenames(TrackedFileModifications(SubmoduleStatus { rela_path: "m1", source: SubmoduleStatus(NextStatusItem(IndexWorktree(IndexAsWorktreeWithRenames(DirWalk(WorktreeRootIsFile { root: "tests/fixtures/generated-do-not-edit/make_submodule_with_symlinked_git_dir/3844911589-unix/m1" }))))) }))

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")?;
Expand Down
Loading