Skip to content

Commit e9295dc

Browse files
committed
fix!: make repositories with worktree config work.
Also rename `repository::Kind::Bare` to `repository::Kind::PossiblyBare` to better indicate the caller has to scrutinize this value.
1 parent c5a7e66 commit e9295dc

File tree

7 files changed

+89
-27
lines changed

7 files changed

+89
-27
lines changed

gix-discover/src/is.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,34 @@ fn bare_by_config(git_dir_candidate: &Path) -> std::io::Result<Option<bool>> {
2424
mod config {
2525
use bstr::{BStr, ByteSlice};
2626

27+
/// Note that we intentionally turn repositories that have a worktree configuration into bare repos,
28+
/// as we don't actually parse the worktree from the config file and expect the caller to do the right
29+
/// think when seemingly seeing bare repository.
30+
/// The reason we do this is to not incorrectly pretend this is a worktree.
2731
pub(crate) fn parse_bare(buf: &[u8]) -> Option<bool> {
28-
buf.lines().find_map(|line| {
29-
let line = line.trim().strip_prefix(b"bare")?;
30-
match line.first() {
31-
None => Some(true),
32-
Some(c) if *c == b'=' => parse_bool(line.get(1..)?.trim_start().as_bstr()),
33-
Some(c) if c.is_ascii_whitespace() => match line.split_once_str(b"=") {
34-
Some((_left, right)) => parse_bool(right.trim_start().as_bstr()),
35-
None => Some(true),
36-
},
37-
Some(_other_char_) => None,
32+
let mut is_bare = None;
33+
let mut has_worktree_configuration = false;
34+
for line in buf.lines() {
35+
if is_bare.is_none() {
36+
if let Some(line) = line.trim().strip_prefix(b"bare") {
37+
is_bare = match line.first() {
38+
None => Some(true),
39+
Some(c) if *c == b'=' => parse_bool(line.get(1..)?.trim_start().as_bstr()),
40+
Some(c) if c.is_ascii_whitespace() => match line.split_once_str(b"=") {
41+
Some((_left, right)) => parse_bool(right.trim_start().as_bstr()),
42+
None => Some(true),
43+
},
44+
Some(_other_char_) => None,
45+
};
46+
continue;
47+
}
3848
}
39-
})
49+
if line.trim().strip_prefix(b"worktree").is_some() {
50+
has_worktree_configuration = true;
51+
break;
52+
}
53+
}
54+
is_bare.map(|bare| bare || has_worktree_configuration)
4055
}
4156

4257
fn parse_bool(value: &BStr) -> Option<bool> {
@@ -233,7 +248,7 @@ pub(crate) fn git_with_metadata(
233248
Cow::Borrowed(git_dir)
234249
};
235250
if bare(conformed_git_dir.as_ref()) || conformed_git_dir.extension() == Some(OsStr::new("git")) {
236-
crate::repository::Kind::Bare
251+
crate::repository::Kind::PossiblyBare
237252
} else if submodule_git_dir(conformed_git_dir.as_ref()) {
238253
crate::repository::Kind::SubmoduleGitDir
239254
} else if conformed_git_dir.file_name() == Some(OsStr::new(DOT_GIT_DIR))
@@ -246,7 +261,7 @@ pub(crate) fn git_with_metadata(
246261
{
247262
crate::repository::Kind::WorkTree { linked_git_dir: None }
248263
} else {
249-
crate::repository::Kind::Bare
264+
crate::repository::Kind::PossiblyBare
250265
}
251266
}
252267
})

gix-discover/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub mod is_git {
3737
GitFile(#[from] crate::path::from_gitdir_file::Error),
3838
#[error("Could not retrieve metadata of \"{path}\"")]
3939
Metadata { source: std::io::Error, path: PathBuf },
40-
#[error("The repository's config file doesn't exist or didn't have a 'bare' configuration")]
40+
#[error("The repository's config file doesn't exist or didn't have a 'bare' configuration or contained core.worktree without value")]
4141
Inconclusive,
4242
}
4343
}

gix-discover/src/repository.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ mod path {
7878
Path::WorkTree(work_dir)
7979
}
8080
},
81-
Kind::Bare => Path::Repository(dir),
81+
Kind::PossiblyBare => Path::Repository(dir),
8282
}
8383
.into()
8484
}
@@ -89,7 +89,7 @@ mod path {
8989
linked_git_dir: Some(git_dir.to_owned()),
9090
},
9191
Path::WorkTree(_) => Kind::WorkTree { linked_git_dir: None },
92-
Path::Repository(_) => Kind::Bare,
92+
Path::Repository(_) => Kind::PossiblyBare,
9393
}
9494
}
9595

@@ -110,7 +110,11 @@ pub enum Kind {
110110
/// A bare repository does not have a work tree, that is files on disk beyond the `git` repository itself.
111111
///
112112
/// Note that this is merely a guess at this point as we didn't read the configuration yet.
113-
Bare,
113+
///
114+
/// Also note that due to optimizing for performance and *just* making an educated *guess in some situations*,
115+
/// we may consider a non-bare repository bare if it it doesn't have an index yet due to be freshly initialized.
116+
/// The caller is has to handle this, typically by reading the configuration.
117+
PossiblyBare,
114118
/// A `git` repository along with checked out files in a work tree.
115119
WorkTree {
116120
/// If set, this is the git dir associated with this _linked_ worktree.
@@ -135,6 +139,6 @@ pub enum Kind {
135139
impl Kind {
136140
/// Returns true if this is a bare repository, one without a work tree.
137141
pub fn is_bare(&self) -> bool {
138-
matches!(self, Kind::Bare)
142+
matches!(self, Kind::PossiblyBare)
139143
}
140144
}

gix-discover/tests/fixtures/make_basic_repo.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,25 @@ git init non-bare-without-index
7070
git commit -m "init"
7171
rm .git/index
7272
)
73+
74+
git --git-dir=repo-with-worktree-in-config-unborn-no-worktreedir --work-tree=does-not-exist-yet init
75+
worktree=repo-with-worktree-in-config-unborn-worktree
76+
git --git-dir=repo-with-worktree-in-config-unborn --work-tree=$worktree init && mkdir $worktree
77+
78+
repo=repo-with-worktree-in-config-unborn-empty-worktreedir
79+
git --git-dir=$repo --work-tree="." init
80+
touch $repo/index
81+
git -C $repo config core.worktree ''
82+
83+
repo=repo-with-worktree-in-config-unborn-worktreedir-missing-value
84+
git --git-dir=$repo init
85+
touch $repo/index
86+
echo " worktree" >> $repo/config
87+
88+
worktree=repo-with-worktree-in-config-worktree
89+
git --git-dir=repo-with-worktree-in-config --work-tree=$worktree init
90+
mkdir $worktree && touch $worktree/file
91+
(cd repo-with-worktree-in-config
92+
git add file
93+
git commit -m "make sure na index exists"
94+
)

gix-discover/tests/is_git/mod.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fn missing_configuration_file_is_not_a_dealbreaker_in_bare_repo() -> crate::Resu
4545
for name in ["bare-no-config-after-init.git", "bare-no-config.git"] {
4646
let repo = repo_path()?.join(name);
4747
let kind = gix_discover::is_git(&repo)?;
48-
assert_eq!(kind, gix_discover::repository::Kind::Bare);
48+
assert_eq!(kind, gix_discover::repository::Kind::PossiblyBare);
4949
}
5050
Ok(())
5151
}
@@ -54,7 +54,7 @@ fn missing_configuration_file_is_not_a_dealbreaker_in_bare_repo() -> crate::Resu
5454
fn bare_repo_with_index_file_looks_still_looks_like_bare() -> crate::Result {
5555
let repo = repo_path()?.join("bare-with-index.git");
5656
let kind = gix_discover::is_git(&repo)?;
57-
assert_eq!(kind, gix_discover::repository::Kind::Bare);
57+
assert_eq!(kind, gix_discover::repository::Kind::PossiblyBare);
5858
Ok(())
5959
}
6060

@@ -63,7 +63,7 @@ fn bare_repo_with_index_file_looks_still_looks_like_bare_if_it_was_renamed() ->
6363
for repo_name in ["bare-with-index-bare", "bare-with-index-no-config-bare"] {
6464
let repo = repo_path()?.join(repo_name);
6565
let kind = gix_discover::is_git(&repo)?;
66-
assert_eq!(kind, gix_discover::repository::Kind::Bare);
66+
assert_eq!(kind, gix_discover::repository::Kind::PossiblyBare);
6767
}
6868
Ok(())
6969
}
@@ -85,3 +85,24 @@ fn missing_configuration_file_is_not_a_dealbreaker_in_nonbare_repo() -> crate::R
8585
}
8686
Ok(())
8787
}
88+
89+
#[test]
90+
fn split_worktree_using_configuration() -> crate::Result {
91+
for name in [
92+
"repo-with-worktree-in-config",
93+
"repo-with-worktree-in-config-unborn",
94+
"repo-with-worktree-in-config-unborn-no-worktreedir",
95+
"repo-with-worktree-in-config-unborn-empty-worktreedir",
96+
"repo-with-worktree-in-config-unborn-worktreedir-missing-value",
97+
] {
98+
let repo = repo_path()?.join(name);
99+
let kind = gix_discover::is_git(&repo)?;
100+
assert_eq!(
101+
kind,
102+
gix_discover::repository::Kind::PossiblyBare,
103+
"{name}: we think these are bare as we don't read the configuration in this case - \
104+
a shortcoming to favor performance which still comes out correct in `gix`"
105+
);
106+
}
107+
Ok(())
108+
}

gix-discover/tests/isolated.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn upwards_bare_repo_with_index() -> gix_testtools::Result {
1212
let (repo_path, _trust) = gix_discover::upwards(".".as_ref())?;
1313
assert_eq!(
1414
repo_path.kind(),
15-
gix_discover::repository::Kind::Bare,
15+
gix_discover::repository::Kind::PossiblyBare,
1616
"bare stays bare, even with index, as it resolves the path as needed in this special case"
1717
);
1818
Ok(())
@@ -25,7 +25,7 @@ fn in_cwd_upwards_bare_repo_without_index() -> gix_testtools::Result {
2525

2626
let _keep = gix_testtools::set_current_dir(repo.join("bare.git"))?;
2727
let (repo_path, _trust) = gix_discover::upwards(".".as_ref())?;
28-
assert_eq!(repo_path.kind(), gix_discover::repository::Kind::Bare);
28+
assert_eq!(repo_path.kind(), gix_discover::repository::Kind::PossiblyBare);
2929
Ok(())
3030
}
3131

gix-discover/tests/upwards/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fn from_bare_git_dir() -> crate::Result {
1717
let dir = repo_path()?.join("bare.git");
1818
let (path, trust) = gix_discover::upwards(&dir)?;
1919
assert_eq!(path.as_ref(), dir, "the bare .git dir is directly returned");
20-
assert_eq!(path.kind(), Kind::Bare);
20+
assert_eq!(path.kind(), Kind::PossiblyBare);
2121
assert_eq!(trust, expected_trust());
2222
Ok(())
2323
}
@@ -27,7 +27,7 @@ fn from_bare_with_index() -> crate::Result {
2727
let dir = repo_path()?.join("bare-with-index.git");
2828
let (path, trust) = gix_discover::upwards(&dir)?;
2929
assert_eq!(path.as_ref(), dir, "the bare .git dir is directly returned");
30-
assert_eq!(path.kind(), Kind::Bare);
30+
assert_eq!(path.kind(), Kind::PossiblyBare);
3131
assert_eq!(trust, expected_trust());
3232
Ok(())
3333
}
@@ -48,7 +48,7 @@ fn from_bare_git_dir_without_config_file() -> crate::Result {
4848
let dir = repo_path()?.join(name);
4949
let (path, trust) = gix_discover::upwards(&dir)?;
5050
assert_eq!(path.as_ref(), dir, "the bare .git dir is directly returned");
51-
assert_eq!(path.kind(), Kind::Bare);
51+
assert_eq!(path.kind(), Kind::PossiblyBare);
5252
assert_eq!(trust, expected_trust());
5353
}
5454
Ok(())
@@ -64,7 +64,7 @@ fn from_inside_bare_git_dir() -> crate::Result {
6464
git_dir,
6565
"the bare .git dir is found while traversing upwards"
6666
);
67-
assert_eq!(path.kind(), Kind::Bare);
67+
assert_eq!(path.kind(), Kind::PossiblyBare);
6868
assert_eq!(trust, expected_trust());
6969
Ok(())
7070
}

0 commit comments

Comments
 (0)