Skip to content

Commit a4ac9cf

Browse files
committed
change: represent GIT_(COMMITTER|AUTHOR)_(NAME|EMAIL|DATE) with git configuration.
That way it becomes more obvious where values are coming from.
1 parent 49f39d6 commit a4ac9cf

File tree

8 files changed

+233
-73
lines changed

8 files changed

+233
-73
lines changed

git-repository/src/config/cache/access.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl Cache {
6060

6161
pub(crate) fn personas(&self) -> &identity::Personas {
6262
self.personas
63-
.get_or_init(|| identity::Personas::from_config_and_env(&self.resolved, self.git_prefix))
63+
.get_or_init(|| identity::Personas::from_config_and_env(&self.resolved))
6464
}
6565

6666
pub(crate) fn url_rewrite(&self) -> &remote::url::Rewrite {

git-repository/src/config/cache/init.rs

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ impl Cache {
3131
xdg_config_home: xdg_config_home_env,
3232
ssh_prefix: _,
3333
http_transport,
34+
identity,
3435
}: repository::permissions::Environment,
3536
repository::permissions::Config {
3637
git_binary: use_installation,
@@ -132,7 +133,7 @@ impl Cache {
132133
source: git_config::Source::Api,
133134
})?;
134135
}
135-
apply_environment_overrides(&mut globals, *git_prefix, http_transport)?;
136+
apply_environment_overrides(&mut globals, *git_prefix, http_transport, identity)?;
136137
globals
137138
};
138139

@@ -163,7 +164,6 @@ impl Cache {
163164
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
164165
url_scheme: Default::default(),
165166
diff_algorithm: Default::default(),
166-
git_prefix,
167167
})
168168
}
169169

@@ -243,6 +243,7 @@ fn apply_environment_overrides(
243243
config: &mut File<'static>,
244244
git_prefix: Permission,
245245
http_transport: Permission,
246+
identity: Permission,
246247
) -> Result<(), Error> {
247248
fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
248249
perm.check_opt(var)
@@ -298,13 +299,106 @@ fn apply_environment_overrides(
298299
}
299300
}
300301

302+
{
303+
let mut section = env_override
304+
.new_section("gitoxide", Some(Cow::Borrowed("committer".into())))
305+
.expect("statically known valid section name");
306+
307+
for (var, key) in [
308+
("GIT_COMMITTER_NAME", "nameFallback"),
309+
("GIT_COMMITTER_EMAIL", "emailFallback"),
310+
] {
311+
if let Some(value) = var_as_bstring(var, git_prefix) {
312+
section.push_with_comment(
313+
key.try_into().expect("statically known to be valid"),
314+
Some(value.as_ref()),
315+
format!("from {var}").as_str(),
316+
);
317+
}
318+
}
319+
320+
if section.num_values() == 0 {
321+
let id = section.id();
322+
env_override.remove_section_by_id(id);
323+
}
324+
}
325+
326+
{
327+
let mut section = env_override
328+
.new_section("gitoxide", Some(Cow::Borrowed("author".into())))
329+
.expect("statically known valid section name");
330+
331+
for (var, key) in [
332+
("GIT_AUTHOR_NAME", "nameFallback"),
333+
("GIT_AUTHOR_EMAIL", "emailFallback"),
334+
] {
335+
if let Some(value) = var_as_bstring(var, git_prefix) {
336+
section.push_with_comment(
337+
key.try_into().expect("statically known to be valid"),
338+
Some(value.as_ref()),
339+
format!("from {var}").as_str(),
340+
);
341+
}
342+
}
343+
344+
if section.num_values() == 0 {
345+
let id = section.id();
346+
env_override.remove_section_by_id(id);
347+
}
348+
}
349+
350+
{
351+
let mut section = env_override
352+
.new_section("gitoxide", Some(Cow::Borrowed("commit".into())))
353+
.expect("statically known valid section name");
354+
355+
for (var, key) in [
356+
("GIT_COMMITTER_DATE", "committerDate"),
357+
("GIT_AUTHOR_DATE", "authorDate"),
358+
] {
359+
if let Some(value) = var_as_bstring(var, git_prefix) {
360+
section.push_with_comment(
361+
key.try_into().expect("statically known to be valid"),
362+
Some(value.as_ref()),
363+
format!("from {var}").as_str(),
364+
);
365+
}
366+
}
367+
368+
if section.num_values() == 0 {
369+
let id = section.id();
370+
env_override.remove_section_by_id(id);
371+
}
372+
}
373+
301374
{
302375
let mut section = env_override
303376
.new_section("gitoxide", Some(Cow::Borrowed("allow".into())))
304377
.expect("statically known valid section name");
305378

306379
for (var, key) in [("GIT_PROTOCOL_FROM_USER", "protocolFromUser")] {
307-
if let Some(value) = var_as_bstring(var, http_transport) {
380+
if let Some(value) = var_as_bstring(var, git_prefix) {
381+
section.push_with_comment(
382+
key.try_into().expect("statically known to be valid"),
383+
Some(value.as_ref()),
384+
format!("from {var}").as_str(),
385+
);
386+
}
387+
}
388+
389+
if section.num_values() == 0 {
390+
let id = section.id();
391+
env_override.remove_section_by_id(id);
392+
}
393+
}
394+
395+
{
396+
let mut section = env_override
397+
.new_section("gitoxide", Some(Cow::Borrowed("user".into())))
398+
.expect("statically known valid section name");
399+
400+
for (var, key) in [("EMAIL", "emailFallback")] {
401+
if let Some(value) = var_as_bstring(var, identity) {
308402
section.push_with_comment(
309403
key.try_into().expect("statically known to be valid"),
310404
Some(value.as_ref()),
@@ -328,7 +422,7 @@ fn apply_environment_overrides(
328422
("GIT_NO_REPLACE_OBJECTS", "noReplace"),
329423
("GIT_REPLACE_REF_BASE", "replaceRefBase"),
330424
] {
331-
if let Some(value) = var_as_bstring(var, http_transport) {
425+
if let Some(value) = var_as_bstring(var, git_prefix) {
332426
section.push_with_comment(
333427
key.try_into().expect("statically known to be valid"),
334428
Some(value.as_ref()),

git-repository/src/config/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,5 @@ pub(crate) struct Cache {
208208
xdg_config_home_env: git_sec::Permission,
209209
/// Define how we can use values obtained with `xdg_config(…)`. and its `HOME` variable.
210210
home_env: git_sec::Permission,
211-
/// How to use git-prefixed environment variables
212-
git_prefix: git_sec::Permission,
213211
// TODO: make core.precomposeUnicode available as well.
214212
}

git-repository/src/repository/identity.rs

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{borrow::Cow, time::SystemTime};
1+
use std::time::SystemTime;
22

3-
use crate::bstr::BString;
3+
use crate::bstr::{BString, ByteSlice};
44

55
/// Identity handling.
66
impl crate::Repository {
@@ -105,43 +105,44 @@ pub(crate) struct Personas {
105105
}
106106

107107
impl Personas {
108-
pub fn from_config_and_env(config: &git_config::File<'_>, git_env: git_sec::Permission) -> Self {
109-
fn env_var(name: &str) -> Option<BString> {
110-
std::env::var_os(name).map(|value| git_path::into_bstr(Cow::Owned(value.into())).into_owned())
111-
}
112-
fn entity_in_section(name: &str, config: &git_config::File<'_>) -> (Option<BString>, Option<BString>) {
113-
config
108+
pub fn from_config_and_env(config: &git_config::File<'_>) -> Self {
109+
fn entity_in_section(
110+
name: &str,
111+
config: &git_config::File<'_>,
112+
fallback: bool,
113+
) -> (Option<BString>, Option<BString>) {
114+
let fallback = fallback
115+
.then(|| config.section("gitoxide", Some(name.into())).ok())
116+
.flatten();
117+
let (name, email) = config
114118
.section(name, None)
115-
.map(|section| {
116-
(
117-
section.value("name").map(|v| v.into_owned()),
118-
section.value("email").map(|v| v.into_owned()),
119-
)
120-
})
121-
.unwrap_or_default()
119+
.map(|section| (section.value("name"), section.value("email")))
120+
.unwrap_or_default();
121+
(
122+
name.or_else(|| fallback.as_ref().and_then(|s| s.value("nameFallback")))
123+
.map(|v| v.into_owned()),
124+
email
125+
.or_else(|| fallback.as_ref().and_then(|s| s.value("emailFallback")))
126+
.map(|v| v.into_owned()),
127+
)
122128
}
129+
let now = SystemTime::now();
130+
let parse_date = |key: &str| -> Option<git_date::Time> {
131+
config.string_by_key(key).and_then(|date| {
132+
date.to_str()
133+
.ok()
134+
.and_then(|date| git_date::parse(date, Some(now)).ok())
135+
})
136+
};
123137

124-
let (mut committer_name, mut committer_email) = entity_in_section("committer", config);
125-
let mut committer_date = None;
126-
let (mut author_name, mut author_email) = entity_in_section("author", config);
127-
let mut author_date = None;
128-
let (user_name, mut user_email) = entity_in_section("user", config);
129-
130-
if git_env.eq(&git_sec::Permission::Allow) {
131-
committer_name = committer_name.or_else(|| env_var("GIT_COMMITTER_NAME"));
132-
committer_email = committer_email.or_else(|| env_var("GIT_COMMITTER_EMAIL"));
133-
committer_date = std::env::var("GIT_COMMITTER_DATE")
134-
.ok()
135-
.and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
138+
let (committer_name, committer_email) = entity_in_section("committer", config, true);
139+
let (author_name, author_email) = entity_in_section("author", config, true);
140+
let (user_name, mut user_email) = entity_in_section("user", config, false);
136141

137-
author_name = author_name.or_else(|| env_var("GIT_AUTHOR_NAME"));
138-
author_email = author_email.or_else(|| env_var("GIT_AUTHOR_EMAIL"));
139-
author_date = std::env::var("GIT_AUTHOR_DATE")
140-
.ok()
141-
.and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
142+
let committer_date = parse_date("gitoxide.commit.committerDate");
143+
let author_date = parse_date("gitoxide.commit.authorDate");
142144

143-
user_email = user_email.or_else(|| env_var("EMAIL")); // NOTE: we don't have permission for this specific one…
144-
}
145+
user_email = user_email.or_else(|| config.string_by_key("gitoxide.user.email").map(|v| v.into_owned()));
145146
Personas {
146147
user: Entity {
147148
name: user_name,

git-repository/src/repository/permissions.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,14 @@ pub struct Environment {
7474
pub ssh_prefix: git_sec::Permission,
7575
/// Control if environment variables to configure the HTTP transport, like `http_proxy` may be used.
7676
///
77-
/// Note that those http-transport related environment variables prefixed with `GIT_` are falling under the
77+
/// Note that http-transport related environment variables prefixed with `GIT_` are falling under the
7878
/// `git_prefix` permission, like `GIT_HTTP_USER_AGENT`.
7979
pub http_transport: git_sec::Permission,
80+
/// Control if the `EMAIL` environment variables may be read.
81+
///
82+
/// Note that identity related environment variables prefixed with `GIT_` are falling under the
83+
/// `git_prefix` permission, like `GIT_AUTHOR_NAME`.
84+
pub identity: git_sec::Permission,
8085
}
8186

8287
impl Environment {
@@ -88,6 +93,7 @@ impl Environment {
8893
git_prefix: git_sec::Permission::Allow,
8994
ssh_prefix: git_sec::Permission::Allow,
9095
http_transport: git_sec::Permission::Allow,
96+
identity: git_sec::Permission::Allow,
9197
}
9298
}
9399
}
@@ -133,6 +139,7 @@ impl Permissions {
133139
ssh_prefix: deny,
134140
git_prefix: deny,
135141
http_transport: deny,
142+
identity: deny,
136143
}
137144
},
138145
}

git-repository/tests/repository/config/identity.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ fn author_and_committer_and_fallback() {
2121
.set("GIT_AUTHOR_NAME", "author")
2222
.set("GIT_AUTHOR_EMAIL", "author@email")
2323
.set("GIT_AUTHOR_DATE", "1979-02-26 18:30:00")
24+
.set("GIT_COMMITTER_NAME", "commiter-overrider-unused")
25+
.set("GIT_COMMITTER_EMAIL", "committer-override-unused@email")
26+
.set("GIT_COMMITTER_DATE", "1980-02-26 18:30:00")
27+
.set("EMAIL", "general@email-unused")
2428
.set("GIT_CONFIG_COUNT", "1")
2529
.set("GIT_CONFIG_KEY_0", "include.path")
2630
.set("GIT_CONFIG_VALUE_0", work_dir.join("c.config").display().to_string());

git-repository/tests/repository/open.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ mod with_overrides {
5757
#[test]
5858
#[serial]
5959
fn order_from_api_and_cli_and_environment() -> crate::Result {
60+
let default_date = "1979-02-26 18:30:00";
6061
let _env = Env::new()
6162
.set("GIT_HTTP_USER_AGENT", "agent-from-env")
6263
.set("GIT_HTTP_LOW_SPEED_LIMIT", "1")
@@ -72,7 +73,14 @@ mod with_overrides {
7273
.set("NO_PROXY", "no-proxy")
7374
.set("GIT_PROTOCOL_FROM_USER", "file-allowed")
7475
.set("GIT_REPLACE_REF_BASE", "refs/replace-mine")
75-
.set("GIT_NO_REPLACE_OBJECTS", "no-replace");
76+
.set("GIT_NO_REPLACE_OBJECTS", "no-replace")
77+
.set("GIT_COMMITTER_NAME", "committer name")
78+
.set("GIT_COMMITTER_EMAIL", "committer email")
79+
.set("GIT_COMMITTER_DATE", default_date)
80+
.set("GIT_AUTHOR_NAME", "author name")
81+
.set("GIT_AUTHOR_EMAIL", "author email")
82+
.set("GIT_AUTHOR_DATE", default_date)
83+
.set("EMAIL", "user email");
7684
let mut opts = git::open::Options::isolated()
7785
.config_overrides([
7886
"http.userAgent=agent-from-api",
@@ -86,6 +94,7 @@ mod with_overrides {
8694
]);
8795
opts.permissions.env.git_prefix = Permission::Allow;
8896
opts.permissions.env.http_transport = Permission::Allow;
97+
opts.permissions.env.identity = Permission::Allow;
8998
let repo = named_subrepo_opts("make_config_repos.sh", "http-config", opts)?;
9099
let config = repo.config_snapshot();
91100
assert_eq!(
@@ -154,32 +163,30 @@ mod with_overrides {
154163
cow_bstr(if cfg!(windows) { "no-proxy" } else { "no-proxy-lower" })
155164
]
156165
);
157-
assert_eq!(
158-
config
159-
.strings_by_key("gitoxide.http.verbose")
160-
.expect("at least one value"),
161-
[cow_bstr("true")]
162-
);
163-
assert_eq!(
164-
config
165-
.strings_by_key("gitoxide.allow.protocolFromUser")
166-
.expect("at least one value"),
167-
[cow_bstr("file-allowed")]
168-
);
169-
assert_eq!(
170-
config
171-
.string_by_key("gitoxide.objects.noReplace")
172-
.expect("at least one value")
173-
.as_ref(),
174-
"no-replace"
175-
);
176-
assert_eq!(
177-
config
178-
.string_by_key("gitoxide.objects.replaceRefBase")
179-
.expect("at least one value")
180-
.as_ref(),
181-
"refs/replace-mine"
182-
);
166+
for (key, expected) in [
167+
("gitoxide.http.verbose", "true"),
168+
("gitoxide.allow.protocolFromUser", "file-allowed"),
169+
("gitoxide.objects.noReplace", "no-replace"),
170+
("gitoxide.objects.replaceRefBase", "refs/replace-mine"),
171+
("gitoxide.committer.nameFallback", "committer name"),
172+
("gitoxide.committer.emailFallback", "committer email"),
173+
("gitoxide.author.nameFallback", "author name"),
174+
("gitoxide.author.emailFallback", "author email"),
175+
("gitoxide.commit.authorDate", default_date),
176+
("gitoxide.commit.committerDate", default_date),
177+
("gitoxide.user.emailFallback", "user email"),
178+
] {
179+
assert_eq!(
180+
config
181+
.string_by_key(key)
182+
.unwrap_or_else(|| panic!("no value for {key}"))
183+
.as_ref(),
184+
expected,
185+
"{} == {}",
186+
key,
187+
expected
188+
);
189+
}
183190
Ok(())
184191
}
185192

0 commit comments

Comments
 (0)