Skip to content

Commit

Permalink
refactor configuration handling to allow pre-parsing of common values (
Browse files Browse the repository at this point in the history
…#298)

Similar to what git does during setup.
  • Loading branch information
Byron committed Apr 5, 2022
1 parent d71bd9d commit e3d280f
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 66 deletions.
70 changes: 70 additions & 0 deletions git-repository/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Could not open repository conifguration file")]
Open(#[from] git_config::parser::ParserOrIoError<'static>),
#[error("Cannot handle objects formatted as {:?}", .name)]
UnsupportedObjectFormat { name: crate::bstr::BString },
}

/// Utility type to keep pre-obtained configuration values.
#[derive(Debug, Clone)]
pub(crate) struct Cache {
pub resolved: crate::Config,
/// The hex-length to assume when shortening object ids. If `None`, it should be computed based on the approximate object count.
pub hex_len: Option<u8>,
/// true if the repository is designated as 'bare', without work tree
pub is_bare: bool,
/// The type of hash to use
pub object_hash: git_hash::Kind,
/// If true, multi-pack indices, whether present or not, may be used by the object database.
pub use_multi_pack_index: bool,
}

mod cache {
use super::{Cache, Error};
use git_config::file::GitConfig;
use git_config::values::{Boolean, Integer};
use std::borrow::Cow;

impl Cache {
pub fn new(git_dir: &std::path::Path) -> Result<Self, Error> {
let config = GitConfig::open(git_dir.join("config"))?;
let is_bare = config_bool(&config, "core.bare", false);
let use_multi_pack_index = config_bool(&config, "core.multiPackIndex", true);
let repo_format_version = config
.value::<Integer>("core", None, "repositoryFormatVersion")
.map_or(0, |v| v.value);
let object_hash = if repo_format_version == 1 {
if let Ok(format) = config.value::<Cow<'_, [u8]>>("extensions", None, "objectFormat") {
match format.as_ref() {
b"sha1" => git_hash::Kind::Sha1,
_ => {
return Err(Error::UnsupportedObjectFormat {
name: format.to_vec().into(),
})
}
}
} else {
git_hash::Kind::Sha1
}
} else {
git_hash::Kind::Sha1
};

Ok(Cache {
resolved: config.into(),
use_multi_pack_index,
object_hash,
is_bare,
hex_len: None,
})
}
}

fn config_bool(config: &GitConfig<'_>, key: &str, default: bool) -> bool {
let (section, key) = key.split_once('.').expect("valid section.key format");
config
.value::<Boolean<'_>>(section, None, key)
.map_or(default, |b| matches!(b, Boolean::True(_)))
}
}
7 changes: 1 addition & 6 deletions git-repository/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,7 @@ pub fn open(directory: impl Into<std::path::PathBuf>) -> Result<crate::Repositor
pub mod open;

///
mod config {
///
pub mod open {
pub type Error = git_config::parser::ParserOrIoError<'static>;
}
}
mod config;

///
pub mod mailmap {
Expand Down
57 changes: 13 additions & 44 deletions git-repository/src/open.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use std::{borrow::Cow, path::PathBuf};
use std::path::PathBuf;

use git_config::{
file::GitConfig,
values::{Boolean, Integer},
};
use git_features::threading::OwnShared;

/// A way to configure the usage of replacement objects, see `git replace`.
Expand Down Expand Up @@ -93,13 +89,11 @@ impl Options {
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Config(#[from] crate::config::open::Error),
Config(#[from] crate::config::Error),
#[error(transparent)]
NotARepository(#[from] crate::path::is::Error),
#[error(transparent)]
ObjectStoreInitialization(#[from] std::io::Error),
#[error("Cannot handle objects formatted as {:?}", .name)]
UnsupportedObjectFormat { name: crate::bstr::BString },
}

impl crate::ThreadSafeRepository {
Expand Down Expand Up @@ -131,33 +125,16 @@ impl crate::ThreadSafeRepository {
replacement_objects,
}: Options,
) -> Result<Self, Error> {
let config = git_config::file::GitConfig::open(git_dir.join("config"))?;
if worktree_dir.is_none() {
let is_bare = config_bool(&config, "core.bare", false);
if !is_bare {
let mut config = crate::config::Cache::new(&git_dir)?;
match worktree_dir {
None if !config.is_bare => {
worktree_dir = Some(git_dir.parent().expect("parent is always available").to_owned());
}
}
let use_multi_pack_index = config_bool(&config, "core.multiPackIndex", true);
let repo_format_version = config
.value::<Integer>("core", None, "repositoryFormatVersion")
.map_or(0, |v| v.value);
let object_hash = if repo_format_version == 1 {
if let Ok(format) = config.value::<Cow<'_, [u8]>>("extensions", None, "objectFormat") {
match format.as_ref() {
b"sha1" => git_hash::Kind::Sha1,
_ => {
return Err(Error::UnsupportedObjectFormat {
name: format.to_vec().into(),
})
}
}
} else {
git_hash::Kind::Sha1
Some(_) => {
config.is_bare = false;
}
} else {
git_hash::Kind::Sha1
};
None => {}
}

let refs = crate::RefStore::at(
&git_dir,
Expand All @@ -166,7 +143,7 @@ impl crate::ThreadSafeRepository {
} else {
git_ref::store::WriteReflog::Normal
},
object_hash,
config.object_hash,
);

let replacements = replacement_objects
Expand Down Expand Up @@ -194,21 +171,13 @@ impl crate::ThreadSafeRepository {
replacements,
git_odb::store::init::Options {
slots: object_store_slots,
object_hash,
use_multi_pack_index,
object_hash: config.object_hash,
use_multi_pack_index: config.use_multi_pack_index,
},
)?),
refs,
work_tree: worktree_dir,
object_hash,
config: config.into(),
config,
})
}
}

fn config_bool(config: &GitConfig<'_>, key: &str, default: bool) -> bool {
let (section, key) = key.split_once('.').expect("valid section.key format");
config
.value::<Boolean<'_>>(section, None, key)
.map_or(default, |b| matches!(b, Boolean::True(_)))
}
1 change: 1 addition & 0 deletions git-repository/src/repository/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ impl crate::Repository {
pub(crate) fn config_int(&self, key: &str, default: i64) -> i64 {
let (section, key) = key.split_once('.').expect("valid section.key format");
self.config
.resolved
.value::<git_config::values::Integer>(section, None, key)
.map_or(default, |v| v.value)
}
Expand Down
4 changes: 0 additions & 4 deletions git-repository/src/repository/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ impl Clone for crate::Repository {
crate::Repository::from_refs_and_objects(
self.refs.clone(),
self.objects.clone(),
self.object_hash,
self.work_tree.clone(),
self.config.clone(),
)
Expand Down Expand Up @@ -31,7 +30,6 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository {
crate::Repository::from_refs_and_objects(
repo.refs.clone(),
repo.objects.to_handle().into(),
repo.object_hash,
repo.work_tree.clone(),
repo.config.clone(),
)
Expand All @@ -43,7 +41,6 @@ impl From<crate::ThreadSafeRepository> for crate::Repository {
crate::Repository::from_refs_and_objects(
repo.refs,
repo.objects.to_handle().into(),
repo.object_hash,
repo.work_tree,
repo.config,
)
Expand All @@ -56,7 +53,6 @@ impl From<crate::Repository> for crate::ThreadSafeRepository {
refs: r.refs,
objects: r.objects.into_inner().store(),
work_tree: r.work_tree,
object_hash: r.object_hash,
config: r.config,
}
}
Expand Down
8 changes: 3 additions & 5 deletions git-repository/src/repository/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl crate::Repository {

/// The kind of object hash the repository is configured to use.
pub fn object_hash(&self) -> git_hash::Kind {
self.object_hash
self.config.object_hash
}
}

Expand All @@ -46,13 +46,11 @@ mod init {
pub(crate) fn from_refs_and_objects(
refs: crate::RefStore,
objects: crate::OdbHandle,
object_hash: git_hash::Kind,
work_tree: Option<std::path::PathBuf>,
config: crate::Config,
config: crate::config::Cache,
) -> Self {
crate::Repository {
bufs: RefCell::new(Vec::with_capacity(4)),
object_hash,
work_tree,
objects: {
#[cfg(feature = "max-performance")]
Expand All @@ -65,7 +63,7 @@ mod init {
}
},
refs,
config,
config: config,
}
}

Expand Down
4 changes: 3 additions & 1 deletion git-repository/src/repository/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ impl crate::Repository {
pub fn load_index(&self) -> Option<Result<git_index::File, git_index::file::init::Error>> {
// TODO: choose better/correct options
let opts = git_index::decode::Options {
object_hash: self.object_hash,
object_hash: self.config.object_hash,
thread_limit: None,
min_extension_block_in_bytes_for_threading: 1024 * 256,
};
Expand Down Expand Up @@ -47,6 +47,7 @@ impl crate::Repository {
let mut buf = Vec::new();
let mut blob_id = self
.config
.resolved
.get_raw_value("mailmap", None, "blob")
.ok()
.and_then(|spec| {
Expand Down Expand Up @@ -91,6 +92,7 @@ impl crate::Repository {

let configured_path = self
.config
.resolved
.value::<git_config::values::Path<'_>>("mailmap", None, "file")
.ok()
.and_then(|path| {
Expand Down
9 changes: 3 additions & 6 deletions git-repository/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,10 @@ pub struct Repository {
pub objects: crate::OdbHandle,

pub(crate) work_tree: Option<PathBuf>,
/// The kind of hash that is used or should be used for object ids
pub(crate) object_hash: git_hash::Kind,
/// Access to all repository configuration, must be hidden as there is a lot figure out.
pub(crate) config: crate::Config,
/// A free-list of re-usable object backing buffers
pub(crate) bufs: RefCell<Vec<Vec<u8>>>,
/// A pre-assembled selection of often-accessed configuration values for quick access.
pub(crate) config: crate::config::Cache,
}

/// An instance with access to everything a git repository entails, best imagined as container implementing `Sync + Send` for _most_
Expand All @@ -143,9 +141,8 @@ pub struct ThreadSafeRepository {
pub(crate) objects: git_features::threading::OwnShared<git_odb::Store>,
/// The path to the worktree at which to find checked out files
pub work_tree: Option<PathBuf>,
pub(crate) object_hash: git_hash::Kind,
// TODO: git-config should be here - it's read a lot but not written much in must applications, so shouldn't be in `State`.
// Probably it's best reload it on signal (in servers) or refresh it when it's known to have been changed similar to how
// packs are refreshed. This would be `git_config::fs::Config` when ready.
pub(crate) config: crate::Config,
pub(crate) config: crate::config::Cache,
}

0 comments on commit e3d280f

Please sign in to comment.