Unified · Split
| @@ -230,7 +230,7 @@ pub fn into( · 229 unmodified lines | ||
| 230 | 230 | Ok(gix_discover::repository::Path::from_dot_git_dir( |
| 231 | 231 | dot_git, |
| 232 | 232 | if bare { |
| 233 | − gix_discover::repository::Kind::Bare | |
| 233 | + gix_discover::repository::Kind::PossiblyBare | |
| 234 | 234 | } else { |
| 235 | 235 | gix_discover::repository::Kind::WorkTree { linked_git_dir: None } |
| 236 | 236 | }, |
| @@ -240,10 +240,20 @@ impl ThreadSafeRepository { · 239 unmodified lines | ||
| 240 | 240 | let wt_path = wt |
| 241 | 241 | .interpolate(interpolate_context(git_install_dir.as_deref(), home.as_deref())) |
| 242 | 242 | .map_err(config::Error::PathInterpolation)?; |
| 243 | − worktree_dir = { | |
| 244 | − gix_path::normalize(git_dir.join(wt_path).into(), current_dir) | |
| 245 | − .and_then(|wt| wt.as_ref().is_dir().then(|| wt.into_owned())) | |
| 243 | + worktree_dir = gix_path::normalize(git_dir.join(wt_path).into(), current_dir).map(Cow::into_owned); | |
| 244 | + #[allow(unused_variables)] | |
| 245 | + if let Some(worktree_path) = worktree_dir.as_deref().filter(|wtd| !wtd.is_dir()) { | |
| 246 | + gix_trace::warn!("The configured worktree path '{}' is not a directory or doesn't exist - `core.worktree` may be misleading", worktree_path.display()); | |
| 246 | 247 | } |
| 248 | + } else if !config.lenient_config | |
| 249 | + && config | |
| 250 | + .resolved | |
| 251 | + .boolean_filter("core", None, Core::WORKTREE.name, &mut filter_config_section) | |
| 252 | + .is_some() | |
| 253 | + { | |
| 254 | + return Err(Error::from(config::Error::ConfigTypedString( | |
| 255 | + config::key::GenericErrorWithValue::from(&Core::WORKTREE), | |
| 256 | + ))); | |
| 247 | 257 | } |
| 248 | 258 | } |
| 249 | 259 | |
| @@ -13,7 +13,7 @@ impl From<gix_discover::repository::Kind> for Kind { · 12 unmodified lines | ||
| 13 | 13 | gix_discover::repository::Kind::Submodule { .. } | gix_discover::repository::Kind::SubmoduleGitDir => { |
| 14 | 14 | Kind::WorkTree { is_linked: false } |
| 15 | 15 | } |
| 16 | − gix_discover::repository::Kind::Bare => Kind::Bare, | |
| 16 | + gix_discover::repository::Kind::PossiblyBare => Kind::Bare, | |
| 17 | 17 | gix_discover::repository::Kind::WorkTreeGitDir { .. } => Kind::WorkTree { is_linked: true }, |
| 18 | 18 | gix_discover::repository::Kind::WorkTree { linked_git_dir } => Kind::WorkTree { |
| 19 | 19 | is_linked: linked_git_dir.is_some(), |
| @@ -17,18 +17,42 @@ mkdir repo · 16 unmodified lines | ||
| 17 | 17 | git commit -q -am c2 |
| 18 | 18 | ) |
| 19 | 19 | |
| 20 | −if [ "$bare" == "bare" ]; then | |
| 20 | +(if [ "$bare" == "bare" ]; then | |
| 21 | 21 | git clone --bare --shared repo repo.git |
| 22 | 22 | cd repo.git |
| 23 | 23 | else |
| 24 | 24 | cd repo |
| 25 | 25 | fi |
| 26 | 26 | |
| 27 | −git worktree add ../wt-a | |
| 28 | −git worktree add ../prev/wt-a HEAD~1 | |
| 29 | −git worktree add ../wt-b HEAD~1 | |
| 30 | −git worktree add ../wt-a/nested-wt-b HEAD~1 | |
| 31 | −git worktree add --lock ../wt-c-locked | |
| 32 | −git worktree add ../wt-deleted && rm -Rf ../wt-deleted | |
| 27 | + git worktree add ../wt-a | |
| 28 | + git worktree add ../prev/wt-a HEAD~1 | |
| 29 | + git worktree add ../wt-b HEAD~1 | |
| 30 | + git worktree add ../wt-a/nested-wt-b HEAD~1 | |
| 31 | + git worktree add --lock ../wt-c-locked | |
| 32 | + git worktree add ../wt-deleted && rm -Rf ../wt-deleted | |
| 33 | 33 | |
| 34 | −git worktree list --porcelain > ../worktree-list.baseline | |
| 34 | + git worktree list --porcelain > ../worktree-list.baseline | |
| 35 | +) | |
| 36 | + | |
| 37 | + | |
| 38 | +git --git-dir=repo-with-worktree-in-config-unborn-no-worktreedir --work-tree=does-not-exist-yet init | |
| 39 | +worktree=repo-with-worktree-in-config-unborn-worktree | |
| 40 | +git --git-dir=repo-with-worktree-in-config-unborn --work-tree=$worktree init && mkdir $worktree | |
| 41 | + | |
| 42 | +repo=repo-with-worktree-in-config-unborn-empty-worktreedir | |
| 43 | +git --git-dir=$repo --work-tree="." init | |
| 44 | +git -C $repo config core.worktree '' | |
| 45 | + | |
| 46 | +repo=repo-with-worktree-in-config-unborn-worktreedir-missing-value | |
| 47 | +git --git-dir=$repo init | |
| 48 | +touch $repo/index | |
| 49 | +git -C $repo config core.bare false | |
| 50 | +echo " worktree" >> $repo/config | |
| 51 | + | |
| 52 | +worktree=repo-with-worktree-in-config-worktree | |
| 53 | +git --git-dir=repo-with-worktree-in-config --work-tree=$worktree init | |
| 54 | +mkdir $worktree && touch $worktree/file | |
| 55 | +(cd repo-with-worktree-in-config | |
| 56 | + git add file | |
| 57 | + git commit -m "make sure na index exists" | |
| 58 | +) | |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | use crate::util::named_subrepo_opts; |
| 2 | +use std::error::Error; | |
| 2 | 3 | |
| 3 | 4 | #[test] |
| 4 | 5 | fn bare_repo_with_index() -> crate::Result { |
| @@ -26,6 +27,59 @@ fn none_bare_repo_without_index() -> crate::Result { · 21 unmodified lines | ||
| 26 | 27 | Ok(()) |
| 27 | 28 | } |
| 28 | 29 | |
| 30 | +#[test] | |
| 31 | +fn non_bare_split_worktree() -> crate::Result { | |
| 32 | + for (name, worktree_exists) in [ | |
| 33 | + ("repo-with-worktree-in-config-unborn-no-worktreedir", false), | |
| 34 | + ("repo-with-worktree-in-config-unborn", true), | |
| 35 | + ("repo-with-worktree-in-config", true), | |
| 36 | + ] { | |
| 37 | + let repo = named_subrepo_opts("make_worktree_repo.sh", name, gix::open::Options::isolated())?; | |
| 38 | + assert!(repo.git_dir().is_dir()); | |
| 39 | + assert!( | |
| 40 | + !repo.is_bare(), | |
| 41 | + "worktree is actually configured, and it's non-bare by configuration" | |
| 42 | + ); | |
| 43 | + assert_eq!( | |
| 44 | + repo.work_dir().expect("worktree is configured").is_dir(), | |
| 45 | + worktree_exists | |
| 46 | + ); | |
| 47 | + } | |
| 48 | + Ok(()) | |
| 49 | +} | |
| 50 | + | |
| 51 | +#[test] | |
| 52 | +fn non_bare_split_worktree_invalid_worktree_path_boolean() -> crate::Result { | |
| 53 | + let err = named_subrepo_opts( | |
| 54 | + "make_worktree_repo.sh", | |
| 55 | + "repo-with-worktree-in-config-unborn-worktreedir-missing-value", | |
| 56 | + gix::open::Options::isolated().strict_config(true), | |
| 57 | + ) | |
| 58 | + .unwrap_err(); | |
| 59 | + assert_eq!( | |
| 60 | + err.source().expect("present").to_string(), | |
| 61 | + "The key \"core.worktree\" (possibly from GIT_WORK_TREE) was invalid", | |
| 62 | + "in strict mode, we fail just like git does" | |
| 63 | + ); | |
| 64 | + Ok(()) | |
| 65 | +} | |
| 66 | + | |
| 67 | +#[test] | |
| 68 | +fn non_bare_split_worktree_invalid_worktree_path_empty() -> crate::Result { | |
| 69 | + // "repo-with-worktree-in-config-unborn-worktreedir-missing-value", | |
| 70 | + let err = named_subrepo_opts( | |
| 71 | + "make_worktree_repo.sh", | |
| 72 | + "repo-with-worktree-in-config-unborn-empty-worktreedir", | |
| 73 | + gix::open::Options::isolated(), | |
| 74 | + ) | |
| 75 | + .unwrap_err(); | |
| 76 | + assert!( | |
| 77 | + matches!(err, gix::open::Error::Config(gix::config::Error::PathInterpolation(_))), | |
| 78 | + "DEVIATION: could not read path at core.worktree as empty is always invalid, git tries to use an empty path, even though it's better to reject it" | |
| 79 | + ); | |
| 80 | + Ok(()) | |
| 81 | +} | |
| 82 | + | |
| 29 | 83 | mod missing_config_file { |
| 30 | 84 | |
| 31 | 85 | use crate::util::named_subrepo_opts; |
| @@ -91,10 +91,10 @@ mod with_core_worktree_config { · 90 unmodified lines | ||
| 91 | 91 | "git can't chdir into missing worktrees, has no error handling there" |
| 92 | 92 | ); |
| 93 | 93 | |
| 94 | − assert_eq!( | |
| 95 | − repo.work_dir(), | |
| 96 | − repo.git_dir().parent(), | |
| 97 | − "we just ignore missing configured worktree dirs and fall back to the default one" | |
| 94 | + assert!( | |
| 95 | + !repo.work_dir().expect("configured").exists(), | |
| 96 | + "non-existing or invalid worktrees (this one is a file) are taken verbatim and \ | |
| 97 | + may lead to errors later - just like in `git` and we explicitly do not try to be smart about it" | |
| 98 | 98 | ) |
| 99 | 99 | } |
| 100 | 100 | |
| @@ -103,11 +103,11 @@ mod with_core_worktree_config { · 2 unmodified lines | ||
| 103 | 103 | let repo = repo("relative-worktree-file"); |
| 104 | 104 | assert_eq!(count_deleted(repo.git_dir()), 0, "git can't chdir into a file"); |
| 105 | 105 | |
| 106 | − assert_eq!( | |
| 107 | − repo.work_dir(), | |
| 108 | − repo.git_dir().parent(), | |
| 109 | − "we just ignore missing configured worktree dirs and fall back to the default one" | |
| 110 | − ) | |
| 106 | + assert!( | |
| 107 | + repo.work_dir().expect("configured").is_file(), | |
| 108 | + "non-existing or invalid worktrees (this one is a file) are taken verbatim and \ | |
| 109 | + may lead to errors later - just like in `git` and we explicitly do not try to be smart about it" | |
| 110 | + ); | |
| 111 | 111 | } |
| 112 | 112 | |
| 113 | 113 | #[test] |
| @@ -1,6 +1,10 @@ | ||
| 1 | 1 | pub fn repo(name: &str) -> crate::Result<gix::Repository> { |
| 2 | 2 | use crate::util::named_subrepo_opts; |
| 3 | − named_subrepo_opts("make_submodules.sh", name, gix::open::Options::isolated()) | |
| 3 | + Ok(named_subrepo_opts( | |
| 4 | + "make_submodules.sh", | |
| 5 | + name, | |
| 6 | + gix::open::Options::isolated(), | |
| 7 | + )?) | |
| 4 | 8 | } |
| 5 | 9 | |
| 6 | 10 | mod open { |
| @@ -34,8 +34,12 @@ pub fn named_repo(name: &str) -> Result<Repository> { · 33 unmodified lines | ||
| 34 | 34 | Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?.to_thread_local()) |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | −pub fn named_subrepo_opts(fixture: &str, name: &str, opts: open::Options) -> Result<Repository> { | |
| 38 | − let repo_path = gix_testtools::scripted_fixture_read_only(fixture)?.join(name); | |
| 37 | +pub fn named_subrepo_opts( | |
| 38 | + fixture: &str, | |
| 39 | + name: &str, | |
| 40 | + opts: open::Options, | |
| 41 | +) -> std::result::Result<Repository, gix::open::Error> { | |
| 42 | + let repo_path = gix_testtools::scripted_fixture_read_only(fixture).unwrap().join(name); | |
| 39 | 43 | Ok(ThreadSafeRepository::open_opts(repo_path, opts)?.to_thread_local()) |
| 40 | 44 | } |
| 41 | 45 | |