Skip to content

Commit

Permalink
change!: open::ReplacementObjects is removed in favor of two custom…
Browse files Browse the repository at this point in the history
… git-configuration flags.

Now it's possible to map the environment variables `GIT_REPLACE_REF_BASE` and `GIT_NO_REPLACE_OBJECTS`
to custom git configuration keys which can also be set, namely `gitoxide.odb.replaceObjectsRefBase`
and `gitoxide.odb.noReplaceObjects`.

Along with the possibility of disabling the usage of `GIT_` prefixed environment variables one
reaches the previous level of control without making object replacement a special case.
  • Loading branch information
Byron committed Nov 29, 2022
1 parent 9441c26 commit 49f39d6
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 73 deletions.
24 changes: 24 additions & 0 deletions git-repository/src/config/cache/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,30 @@ fn apply_environment_overrides(
}
}

{
let mut section = env_override
.new_section("gitoxide", Some(Cow::Borrowed("objects".into())))
.expect("statically known valid section name");

for (var, key) in [
("GIT_NO_REPLACE_OBJECTS", "noReplace"),
("GIT_REPLACE_REF_BASE", "replaceRefBase"),
] {
if let Some(value) = var_as_bstring(var, http_transport) {
section.push_with_comment(
key.try_into().expect("statically known to be valid"),
Some(value.as_ref()),
format!("from {var}").as_str(),
);
}
}

if section.num_values() == 0 {
let id = section.id();
env_override.remove_section_by_id(id);
}
}

{
let mut section = env_override
.new_section("gitoxide", Some(Cow::Borrowed("http".into())))
Expand Down
5 changes: 5 additions & 0 deletions git-repository/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pub(crate) mod section {
pub enum Error {
#[error("Could not read configuration file")]
Io(#[from] std::io::Error),
#[error("Could not decode configuration value at {key:?}")]
Value {
source: git_config::value::Error,
key: &'static str,
},
#[error(transparent)]
Init(#[from] git_config::file::init::Error),
#[error(transparent)]
Expand Down
65 changes: 8 additions & 57 deletions git-repository/src/open/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,17 @@ use std::path::PathBuf;

use crate::{bstr::BString, config, permission, Permissions};

/// A way to configure the usage of replacement objects, see `git replace`.
#[derive(Debug, Clone)]
pub enum ReplacementObjects {
/// Allow replacement objects and configure the ref prefix the standard environment variable `GIT_REPLACE_REF_BASE`,
/// or default to the standard `refs/replace/` prefix.
UseWithEnvironmentRefPrefixOrDefault {
/// If true, default true, a standard environment variable `GIT_NO_REPLACE_OBJECTS` to disable replacement objects entirely.
allow_disable_via_environment: bool,
},
/// Use replacement objects and configure the prefix yourself.
UseWithRefPrefix {
/// The ref prefix to use, like `refs/alternative/` - note the trailing slash.
prefix: PathBuf,
/// If true, default true, a standard environment variable `GIT_NO_REPLACE_OBJECTS`
allow_disable_via_environment: bool,
},
/// Do not use replacement objects at all.
Disable,
}

impl Default for ReplacementObjects {
fn default() -> Self {
ReplacementObjects::UseWithEnvironmentRefPrefixOrDefault {
allow_disable_via_environment: true,
}
}
}

impl ReplacementObjects {
fn refs_prefix(self) -> Option<PathBuf> {
use ReplacementObjects::*;
let is_disabled = |allow_env: bool| allow_env && std::env::var_os("GIT_NO_REPLACE_OBJECTS").is_some();
match self {
UseWithEnvironmentRefPrefixOrDefault {
allow_disable_via_environment,
} => {
if is_disabled(allow_disable_via_environment) {
return None;
};
PathBuf::from(std::env::var("GIT_REPLACE_REF_BASE").unwrap_or_else(|_| "refs/replace/".into())).into()
}
UseWithRefPrefix {
prefix,
allow_disable_via_environment,
} => {
if is_disabled(allow_disable_via_environment) {
return None;
};
prefix.into()
}
Disable => None,
}
}
}

/// The options used in [`ThreadSafeRepository::open_opts`]
/// The options used in [`ThreadSafeRepository::open_opts()`][crate::ThreadSafeRepository::open_opts()].
///
/// ### Replacement Objects for the object database
///
/// The environment variables `GIT_REPLACE_REF_BASE` and `GIT_NO_REPLACE_OBJECTS` are mapped to `gitoxide.objects.replaceRefBase`
/// and `gitoxide.objects.noReplace` respectively and then interpreted exactly as their environment variable counterparts.
///
/// Use [Permissions] to control which environment variables can be read, and config-overrides to control these values programmatically.
#[derive(Clone)]
pub struct Options {
pub(crate) object_store_slots: git_odb::store::init::Slots,
pub(crate) replacement_objects: ReplacementObjects,
/// Define what is allowed while opening a repository.
pub permissions: Permissions,
pub(crate) git_dir_trust: Option<git_sec::Trust>,
Expand Down
12 changes: 1 addition & 11 deletions git-repository/src/open/options.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Error, Options, ReplacementObjects};
use super::{Error, Options};
use crate::bstr::BString;
use crate::{config, Permissions, ThreadSafeRepository};
use std::path::PathBuf;
Expand All @@ -7,7 +7,6 @@ impl Default for Options {
fn default() -> Self {
Options {
object_store_slots: Default::default(),
replacement_objects: Default::default(),
permissions: Default::default(),
git_dir_trust: None,
filter_config_section: None,
Expand Down Expand Up @@ -63,13 +62,6 @@ impl Options {
self
}

// TODO: tests
/// Configure replacement objects, see the [`ReplacementObjects`] type for details.
pub fn replacement_objects(mut self, config: ReplacementObjects) -> Self {
self.replacement_objects = config;
self
}

// TODO: tests
/// Set the given permissions, which are typically derived by a `Trust` level.
pub fn permissions(mut self, permissions: Permissions) -> Self {
Expand Down Expand Up @@ -148,7 +140,6 @@ impl git_sec::trust::DefaultForLevel for Options {
match level {
git_sec::Trust::Full => Options {
object_store_slots: Default::default(),
replacement_objects: Default::default(),
permissions: Permissions::default_for_level(level),
git_dir_trust: git_sec::Trust::Full.into(),
filter_config_section: Some(config::section::is_trusted),
Expand All @@ -161,7 +152,6 @@ impl git_sec::trust::DefaultForLevel for Options {
},
git_sec::Trust::Reduced => Options {
object_store_slots: git_odb::store::init::Slots::Given(32), // limit resource usage
replacement_objects: ReplacementObjects::Disable, // don't be tricked into seeing manufactured objects
permissions: Permissions::default_for_level(level),
git_dir_trust: git_sec::Trust::Reduced.into(),
filter_config_section: Some(config::section::is_trusted),
Expand Down
34 changes: 30 additions & 4 deletions git-repository/src/open/repository.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::{Error, Options};
use crate::config::cache::interpolate_context;
use crate::config::cache::util::ApplyLeniency;
use crate::{config, permission, Permissions, ThreadSafeRepository};
use git_features::threading::OwnShared;
use std::borrow::Cow;
use std::path::PathBuf;

#[derive(Default, Clone)]
Expand Down Expand Up @@ -108,7 +110,6 @@ impl ThreadSafeRepository {
git_dir_trust,
object_store_slots,
filter_config_section,
ref replacement_objects,
lossy_config,
lenient_config,
bail_if_untrusted,
Expand Down Expand Up @@ -191,9 +192,7 @@ impl ThreadSafeRepository {
}

refs.write_reflog = config::cache::util::reflog_or_default(config.reflog, worktree_dir.is_some());
let replacements = replacement_objects
.clone()
.refs_prefix()
let replacements = replacement_objects_refs_prefix(&config.resolved, lenient_config, filter_config_section)?
.and_then(|prefix| {
let platform = refs.iter().ok()?;
let iter = platform.prefixed(&prefix).ok()?;
Expand Down Expand Up @@ -233,6 +232,33 @@ impl ThreadSafeRepository {
}
}

// TODO: tests
fn replacement_objects_refs_prefix(
config: &git_config::File<'static>,
lenient: bool,
mut filter_config_section: fn(&git_config::file::Metadata) -> bool,
) -> Result<Option<PathBuf>, Error> {
let key = "gitoxide.objects.noReplace";
let is_disabled = config
.boolean_filter_by_key(key, &mut filter_config_section)
.transpose()
.with_leniency(lenient)
.map_err(|err| config::Error::Value { source: err, key })?
.unwrap_or_default();

if is_disabled {
return Ok(None);
}

let ref_base = git_path::from_bstr(
config
.string_filter_by_key("gitoxide.objects.replaceRefBase", &mut filter_config_section)
.unwrap_or_else(|| Cow::Borrowed("refs/replace/".into())),
)
.into_owned();
Ok(ref_base.into())
}

fn check_safe_directories(
git_dir: &std::path::Path,
git_install_dir: Option<&std::path::Path>,
Expand Down
18 changes: 17 additions & 1 deletion git-repository/tests/repository/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ mod with_overrides {
.set("ALL_PROXY", "all-proxy")
.set("no_proxy", "no-proxy-lower")
.set("NO_PROXY", "no-proxy")
.set("GIT_PROTOCOL_FROM_USER", "file-allowed");
.set("GIT_PROTOCOL_FROM_USER", "file-allowed")
.set("GIT_REPLACE_REF_BASE", "refs/replace-mine")
.set("GIT_NO_REPLACE_OBJECTS", "no-replace");
let mut opts = git::open::Options::isolated()
.config_overrides([
"http.userAgent=agent-from-api",
Expand Down Expand Up @@ -164,6 +166,20 @@ mod with_overrides {
.expect("at least one value"),
[cow_bstr("file-allowed")]
);
assert_eq!(
config
.string_by_key("gitoxide.objects.noReplace")
.expect("at least one value")
.as_ref(),
"no-replace"
);
assert_eq!(
config
.string_by_key("gitoxide.objects.replaceRefBase")
.expect("at least one value")
.as_ref(),
"refs/replace-mine"
);
Ok(())
}

Expand Down
14 changes: 14 additions & 0 deletions src/plumbing/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,20 @@ static GIT_CONFIG: &[Record] = &[
deviation: Some("corresponds to GIT_PROTOCOL_FROM_USER environment variable")
}
},
Record {
config: "gitoxide.objects.replaceRefBase",
usage: InModule {
name: "open",
deviation: Some("corresponds to the GIT_REPLACE_REF_BASE environment variable")
}
},
Record {
config: "gitoxide.objects.noReplace",
usage: InModule {
name: "open",
deviation: Some("corresponds to the GIT_NO_REPLACE_OBJECTS environment variable")
}
},
];

/// A programmatic way to record and display progress.
Expand Down

0 comments on commit 49f39d6

Please sign in to comment.