Skip to content

Commit b7573d9

Browse files
codexByron
authored andcommitted
Address review feedback about repository formats
Codex review noted that default initialization in SHA256-only builds wrote a v0 repository without extensions.objectFormat, causing gix::init() to fail when reopening the repository. Default creation options now select Sha256 when SHA1 support is unavailable, so the written repository can be opened by the same build. Codex review also noted that repository format versions greater than 1 were treated as supporting extensions.objectFormat. Opening now rejects unsupported repository format versions before interpreting extensions, matching Git's fail-closed behavior. Validated with: - cargo test -p gix --lib default_object_hash_matches_available_hash_support - cargo test -p gix --no-default-features --features sha256 --lib default_object_hash_matches_available_hash_support - cargo test -p gix --test gix rejects_future_repository_format_versions - cargo test -p gix --test gix rejects_object_format_on_v0_repo - cargo check -p gix --no-default-features --features sha256
1 parent c391a12 commit b7573d9

6 files changed

Lines changed: 83 additions & 6 deletions

File tree

gix/src/config/cache/incubate.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ impl StageOne {
4545
.map(|version| Core::REPOSITORY_FORMAT_VERSION.try_into_usize(version))
4646
.transpose()?
4747
.unwrap_or_default();
48-
let object_hash = match config.string(Extensions::OBJECT_FORMAT) {
49-
// objectFormat is honored from repository format version 1 onwards.
50-
Some(format) if repo_format_version >= 1 => Extensions::OBJECT_FORMAT.try_into_object_format(format)?,
51-
Some(_) => return Err(Error::ObjectFormatRequiresV1),
52-
None => legacy_object_hash()?,
48+
let object_hash = match (repo_format_version, config.string(Extensions::OBJECT_FORMAT)) {
49+
// objectFormat is a repository format version 1 extension.
50+
(1, Some(format)) => Extensions::OBJECT_FORMAT.try_into_object_format(format)?,
51+
(0, Some(_)) => return Err(Error::ObjectFormatRequiresV1),
52+
(0 | 1, None) => legacy_object_hash()?,
53+
(version, _) => return Err(Error::UnsupportedRepositoryFormatVersion { version }),
5354
};
5455

5556
let extension_worktree = util::config_bool(

gix/src/config/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ pub enum Error {
9191
set core.repositoryFormatVersion=1 to use it, or remove extensions.objectFormat to fall back to the default Sha1 format (if supported by this build)"
9292
)]
9393
ObjectFormatRequiresV1,
94+
#[error("Unsupported repository format version {version}; only versions 0 and 1 are supported")]
95+
UnsupportedRepositoryFormatVersion { version: usize },
9496
#[error(transparent)]
9597
CoreAbbrev(#[from] abbrev::Error),
9698
#[error("Could not read configuration file at \"{}\"", path.display())]

gix/src/create.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ fn create_dir(p: &Path) -> Result<(), Error> {
108108
}
109109

110110
/// Options for use in [`into()`];
111-
#[derive(Copy, Default, Clone)]
111+
#[derive(Copy, Clone)]
112112
pub struct Options {
113113
/// Control whether the destination directory must be empty when creating a repository with a worktree.
114114
///
@@ -134,6 +134,31 @@ pub struct Options {
134134
pub object_hash: Option<gix_hash::Kind>,
135135
}
136136

137+
impl Default for Options {
138+
fn default() -> Self {
139+
Options {
140+
destination_must_be_empty: None,
141+
fs_capabilities: None,
142+
object_hash: default_object_hash(),
143+
}
144+
}
145+
}
146+
147+
fn default_object_hash() -> Option<gix_hash::Kind> {
148+
#[cfg(feature = "sha1")]
149+
{
150+
None
151+
}
152+
#[cfg(all(not(feature = "sha1"), feature = "sha256"))]
153+
{
154+
Some(gix_hash::Kind::Sha256)
155+
}
156+
#[cfg(all(not(feature = "sha1"), not(feature = "sha256")))]
157+
{
158+
unreachable!("hash support features are validated by gix-hash")
159+
}
160+
}
161+
137162
/// Create a new `.git` repository of `kind` within the possibly non-existing `directory`
138163
/// and return its path.
139164
/// Note that this is a simple template-based initialization routine which should be accompanied with additional corrections
@@ -286,3 +311,22 @@ fn bool(v: bool) -> &'static str {
286311
false => "false",
287312
}
288313
}
314+
315+
#[cfg(test)]
316+
mod tests {
317+
#[test]
318+
fn default_object_hash_matches_available_hash_support() {
319+
let object_hash = super::Options::default().object_hash;
320+
#[cfg(feature = "sha1")]
321+
assert_eq!(
322+
object_hash, None,
323+
"SHA1-capable builds keep Git's implicit legacy object format"
324+
);
325+
#[cfg(all(not(feature = "sha1"), feature = "sha256"))]
326+
assert_eq!(
327+
object_hash,
328+
Some(gix_hash::Kind::Sha256),
329+
"SHA256-only builds must initialize repositories that can be reopened"
330+
);
331+
}
332+
}
44.5 KB
Binary file not shown.

gix/tests/fixtures/make_config_repos.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,18 @@ EOF
178178
)
179179
done
180180

181+
git init repository-format-v2-with-objectformat-sha1
182+
(cd repository-format-v2-with-objectformat-sha1
183+
# Future repository formats may change invariants beyond the object hash. Git
184+
# refuses such repositories before interpreting extensions; gix should too.
185+
cat <<EOF >>.git/config
186+
[core]
187+
repositoryFormatVersion = 2
188+
[extensions]
189+
objectFormat = sha1
190+
EOF
191+
)
192+
181193
git init ssl-verify-disabled
182194
(cd ssl-verify-disabled
183195
git config http.sslVerify false

gix/tests/gix/repository/open.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,24 @@ mod object_format_extension {
378378
}
379379
Ok(())
380380
}
381+
382+
#[test]
383+
fn rejects_future_repository_format_versions() -> crate::Result {
384+
let err = named_subrepo_opts(
385+
"make_config_repos.sh",
386+
"repository-format-v2-with-objectformat-sha1",
387+
gix::open::Options::isolated(),
388+
)
389+
.expect_err("future repository format versions must be rejected");
390+
assert!(
391+
matches!(
392+
err,
393+
gix::open::Error::Config(gix::config::Error::UnsupportedRepositoryFormatVersion { version: 2 })
394+
),
395+
"future repository format versions must be rejected before interpreting extensions, got {err:?}"
396+
);
397+
Ok(())
398+
}
381399
}
382400

383401
mod open_path_as_is {

0 commit comments

Comments
 (0)