blob: 3c482b154ee864a4040e4681762b6b26d238c57e [file] [log] [blame]
Charisee635618d2023-06-01 20:46:00 +00001#![allow(clippy::result_large_err)]
Chris Wailescd1aefd2023-07-13 13:36:21 -07002use std::{borrow::Cow, ffi::OsString};
Charisee635618d2023-06-01 20:46:00 +00003
4use gix_sec::Permission;
5
6use super::{interpolate_context, util, Error, StageOne};
7use crate::{
8 bstr::BString,
9 config,
10 config::{
11 cache::util::ApplyLeniency,
12 tree::{gitoxide, Core, Http},
13 Cache,
14 },
Chris Wailescd1aefd2023-07-13 13:36:21 -070015 open,
Chris Wailes951ae7a2023-12-07 10:11:13 -080016 repository::init::setup_objects,
Charisee635618d2023-06-01 20:46:00 +000017};
18
19/// Initialization
20impl Cache {
21 #[allow(clippy::too_many_arguments)]
22 pub fn from_stage_one(
23 StageOne {
24 git_dir_config,
25 mut buf,
26 lossy,
27 is_bare,
28 object_hash,
29 reflog: _,
30 }: StageOne,
31 git_dir: &std::path::Path,
32 branch_name: Option<&gix_ref::FullNameRef>,
33 filter_config_section: fn(&gix_config::file::Metadata) -> bool,
34 git_install_dir: Option<&std::path::Path>,
35 home: Option<&std::path::Path>,
Chris Wailescd1aefd2023-07-13 13:36:21 -070036 environment @ open::permissions::Environment {
Charisee635618d2023-06-01 20:46:00 +000037 git_prefix,
Charisee635618d2023-06-01 20:46:00 +000038 ssh_prefix: _,
Chris Wailescd1aefd2023-07-13 13:36:21 -070039 xdg_config_home: _,
40 home: _,
Charisee635618d2023-06-01 20:46:00 +000041 http_transport,
42 identity,
43 objects,
Chris Wailescd1aefd2023-07-13 13:36:21 -070044 }: open::permissions::Environment,
45 attributes: open::permissions::Attributes,
46 open::permissions::Config {
Charisee635618d2023-06-01 20:46:00 +000047 git_binary: use_installation,
48 system: use_system,
49 git: use_git,
50 user: use_user,
51 env: use_env,
52 includes: use_includes,
Chris Wailescd1aefd2023-07-13 13:36:21 -070053 }: open::permissions::Config,
Charisee635618d2023-06-01 20:46:00 +000054 lenient_config: bool,
55 api_config_overrides: &[BString],
56 cli_config_overrides: &[BString],
57 ) -> Result<Self, Error> {
58 let options = gix_config::file::init::Options {
59 includes: if use_includes {
60 gix_config::file::includes::Options::follow(
61 interpolate_context(git_install_dir, home),
62 gix_config::file::includes::conditional::Context {
63 git_dir: git_dir.into(),
64 branch_name,
65 },
66 )
67 } else {
68 gix_config::file::includes::Options::no_follow()
69 },
Chris Wailescd1aefd2023-07-13 13:36:21 -070070 ..util::base_options(lossy, lenient_config)
Charisee635618d2023-06-01 20:46:00 +000071 };
72
73 let config = {
Charisee635618d2023-06-01 20:46:00 +000074 let git_prefix = &git_prefix;
Chris Wailes951ae7a2023-12-07 10:11:13 -080075 let mut metas = [
Charisee635618d2023-06-01 20:46:00 +000076 gix_config::source::Kind::GitInstallation,
77 gix_config::source::Kind::System,
78 gix_config::source::Kind::Global,
79 ]
80 .iter()
81 .flat_map(|kind| kind.sources())
82 .filter_map(|source| {
83 match source {
84 gix_config::Source::GitInstallation if !use_installation => return None,
85 gix_config::Source::System if !use_system => return None,
86 gix_config::Source::Git if !use_git => return None,
87 gix_config::Source::User if !use_user => return None,
88 _ => {}
89 }
90 source
Chris Wailescd1aefd2023-07-13 13:36:21 -070091 .storage_location(&mut Self::make_source_env(environment))
Charisee635618d2023-06-01 20:46:00 +000092 .map(|p| (source, p.into_owned()))
93 })
94 .map(|(source, path)| gix_config::file::Metadata {
95 path: Some(path),
96 source: *source,
97 level: 0,
98 trust: gix_sec::Trust::Full,
99 });
100
101 let err_on_nonexisting_paths = false;
102 let mut globals = gix_config::File::from_paths_metadata_buf(
Chris Wailes951ae7a2023-12-07 10:11:13 -0800103 &mut metas,
Charisee635618d2023-06-01 20:46:00 +0000104 &mut buf,
105 err_on_nonexisting_paths,
106 gix_config::file::init::Options {
107 includes: gix_config::file::includes::Options::no_follow(),
108 ..options
109 },
110 )
111 .map_err(|err| match err {
112 gix_config::file::init::from_paths::Error::Init(err) => Error::from(err),
Chris Wailescd1aefd2023-07-13 13:36:21 -0700113 gix_config::file::init::from_paths::Error::Io { source, path } => Error::Io { source, path },
Charisee635618d2023-06-01 20:46:00 +0000114 })?
115 .unwrap_or_default();
116
117 let local_meta = git_dir_config.meta_owned();
118 globals.append(git_dir_config);
119 globals.resolve_includes(options)?;
120 if use_env {
121 globals.append(gix_config::File::from_env(options)?.unwrap_or_default());
122 }
123 if !cli_config_overrides.is_empty() {
124 config::overrides::append(&mut globals, cli_config_overrides, gix_config::Source::Cli, |_| None)
125 .map_err(|err| Error::ConfigOverrides {
126 err,
127 source: gix_config::Source::Cli,
128 })?;
129 }
130 if !api_config_overrides.is_empty() {
131 config::overrides::append(&mut globals, api_config_overrides, gix_config::Source::Api, |_| None)
132 .map_err(|err| Error::ConfigOverrides {
133 err,
134 source: gix_config::Source::Api,
135 })?;
136 }
137 apply_environment_overrides(&mut globals, *git_prefix, http_transport, identity, objects)?;
138 globals.set_meta(local_meta);
139 globals
140 };
141
142 let hex_len = util::parse_core_abbrev(&config, object_hash).with_leniency(lenient_config)?;
143
144 use util::config_bool;
145 let reflog = util::query_refupdates(&config, lenient_config)?;
146 let ignore_case = config_bool(&config, &Core::IGNORE_CASE, "core.ignoreCase", false, lenient_config)?;
147 let use_multi_pack_index = config_bool(
148 &config,
149 &Core::MULTIPACK_INDEX,
150 "core.multiPackIndex",
151 true,
152 lenient_config,
153 )?;
Chris Wailes951ae7a2023-12-07 10:11:13 -0800154 #[cfg(feature = "revision")]
Charisee635618d2023-06-01 20:46:00 +0000155 let object_kind_hint = util::disambiguate_hint(&config, lenient_config)?;
James Farrell91b821d2023-08-24 14:18:40 +0000156 let (static_pack_cache_limit_bytes, pack_cache_bytes, object_cache_bytes) =
Charisee635618d2023-06-01 20:46:00 +0000157 util::parse_object_caches(&config, lenient_config, filter_config_section)?;
158 // NOTE: When adding a new initial cache, consider adjusting `reread_values_and_clear_caches()` as well.
159 Ok(Cache {
160 resolved: config.into(),
161 use_multi_pack_index,
162 object_hash,
Chris Wailes951ae7a2023-12-07 10:11:13 -0800163 #[cfg(feature = "revision")]
Charisee635618d2023-06-01 20:46:00 +0000164 object_kind_hint,
James Farrell91b821d2023-08-24 14:18:40 +0000165 static_pack_cache_limit_bytes,
Charisee635618d2023-06-01 20:46:00 +0000166 pack_cache_bytes,
167 object_cache_bytes,
168 reflog,
169 is_bare,
170 ignore_case,
171 hex_len,
172 filter_config_section,
Chris Wailescd1aefd2023-07-13 13:36:21 -0700173 environment,
Charisee635618d2023-06-01 20:46:00 +0000174 lenient_config,
Chris Wailescd1aefd2023-07-13 13:36:21 -0700175 attributes,
Charisee635618d2023-06-01 20:46:00 +0000176 user_agent: Default::default(),
177 personas: Default::default(),
178 url_rewrite: Default::default(),
Chris Wailes951ae7a2023-12-07 10:11:13 -0800179 #[cfg(feature = "blob-diff")]
Charisee635618d2023-06-01 20:46:00 +0000180 diff_renames: Default::default(),
181 #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
182 url_scheme: Default::default(),
Chris Wailes951ae7a2023-12-07 10:11:13 -0800183 #[cfg(feature = "blob-diff")]
Charisee635618d2023-06-01 20:46:00 +0000184 diff_algorithm: Default::default(),
185 })
186 }
187
188 /// Call this with new `config` to update values and clear caches. Note that none of the values will be applied if a single
189 /// one is invalid.
190 /// However, those that are lazily read won't be re-evaluated right away and might thus pass now but fail later.
191 ///
192 /// Note that we unconditionally re-read all values.
193 pub fn reread_values_and_clear_caches_replacing_config(&mut self, config: crate::Config) -> Result<(), Error> {
194 let prev = std::mem::replace(&mut self.resolved, config);
195 match self.reread_values_and_clear_caches() {
196 Err(err) => {
197 drop(std::mem::replace(&mut self.resolved, prev));
198 Err(err)
199 }
200 Ok(()) => Ok(()),
201 }
202 }
203
204 /// Similar to `reread_values_and_clear_caches_replacing_config()`, but works on the existing configuration instead of a passed
205 /// in one that it them makes the default.
206 pub fn reread_values_and_clear_caches(&mut self) -> Result<(), Error> {
207 let config = &self.resolved;
208 let hex_len = util::parse_core_abbrev(config, self.object_hash).with_leniency(self.lenient_config)?;
209
210 use util::config_bool;
211 let ignore_case = config_bool(
212 config,
213 &Core::IGNORE_CASE,
214 "core.ignoreCase",
215 false,
216 self.lenient_config,
217 )?;
Chris Wailes951ae7a2023-12-07 10:11:13 -0800218
219 #[cfg(feature = "revision")]
220 {
221 let object_kind_hint = util::disambiguate_hint(config, self.lenient_config)?;
222 self.object_kind_hint = object_kind_hint;
223 }
Charisee635618d2023-06-01 20:46:00 +0000224 let reflog = util::query_refupdates(config, self.lenient_config)?;
225
226 self.hex_len = hex_len;
227 self.ignore_case = ignore_case;
Charisee635618d2023-06-01 20:46:00 +0000228 self.reflog = reflog;
229
230 self.user_agent = Default::default();
231 self.personas = Default::default();
232 self.url_rewrite = Default::default();
Chris Wailes951ae7a2023-12-07 10:11:13 -0800233 #[cfg(feature = "blob-diff")]
234 {
235 self.diff_renames = Default::default();
236 self.diff_algorithm = Default::default();
237 }
James Farrell91b821d2023-08-24 14:18:40 +0000238 (
239 self.static_pack_cache_limit_bytes,
240 self.pack_cache_bytes,
241 self.object_cache_bytes,
242 ) = util::parse_object_caches(config, self.lenient_config, self.filter_config_section)?;
Charisee635618d2023-06-01 20:46:00 +0000243 #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
244 {
245 self.url_scheme = Default::default();
246 }
247
248 Ok(())
249 }
Chris Wailescd1aefd2023-07-13 13:36:21 -0700250
251 pub(crate) fn make_source_env(
252 crate::open::permissions::Environment {
253 xdg_config_home,
254 git_prefix,
255 home,
256 ..
257 }: open::permissions::Environment,
258 ) -> impl FnMut(&str) -> Option<OsString> {
259 move |name| {
260 match name {
261 git_ if git_.starts_with("GIT_") => Some(git_prefix),
262 "XDG_CONFIG_HOME" => Some(xdg_config_home),
263 "HOME" => {
264 return if home.is_allowed() {
265 gix_path::env::home_dir().map(Into::into)
266 } else {
267 None
268 }
269 }
270 _ => None,
271 }
272 .and_then(|perm| perm.check_opt(name).and_then(gix_path::env::var))
273 }
274 }
Charisee635618d2023-06-01 20:46:00 +0000275}
276
277impl crate::Repository {
278 /// Replace our own configuration with `config` and re-read all cached values, and apply them to select in-memory instances.
279 pub(crate) fn reread_values_and_clear_caches_replacing_config(
280 &mut self,
281 config: crate::Config,
282 ) -> Result<(), Error> {
Chris Wailes951ae7a2023-12-07 10:11:13 -0800283 let (a, b, c) = (
284 self.config.static_pack_cache_limit_bytes,
285 self.config.pack_cache_bytes,
286 self.config.object_cache_bytes,
287 );
Charisee635618d2023-06-01 20:46:00 +0000288 self.config.reread_values_and_clear_caches_replacing_config(config)?;
289 self.apply_changed_values();
Chris Wailes951ae7a2023-12-07 10:11:13 -0800290 if a != self.config.static_pack_cache_limit_bytes
291 || b != self.config.pack_cache_bytes
292 || c != self.config.object_cache_bytes
293 {
294 setup_objects(&mut self.objects, &self.config);
295 }
Charisee635618d2023-06-01 20:46:00 +0000296 Ok(())
297 }
298
299 fn apply_changed_values(&mut self) {
300 self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some());
301 }
302}
303
304fn apply_environment_overrides(
305 config: &mut gix_config::File<'static>,
306 git_prefix: Permission,
307 http_transport: Permission,
308 identity: Permission,
309 objects: Permission,
310) -> Result<(), Error> {
311 fn env(key: &'static dyn config::tree::Key) -> &'static str {
312 key.the_environment_override()
313 }
314 fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
315 perm.check_opt(var)
316 .and_then(std::env::var_os)
317 .and_then(|val| gix_path::os_string_into_bstring(val).ok())
318 }
319
320 let mut env_override = gix_config::File::new(gix_config::file::Metadata::from(gix_config::Source::EnvOverride));
321 for (section_name, subsection_name, permission, data) in [
322 (
323 "http",
324 None,
325 http_transport,
326 &[
327 ("GIT_HTTP_LOW_SPEED_LIMIT", "lowSpeedLimit"),
328 ("GIT_HTTP_LOW_SPEED_TIME", "lowSpeedTime"),
329 ("GIT_HTTP_USER_AGENT", "userAgent"),
330 {
331 let key = &Http::SSL_CA_INFO;
332 (env(key), key.name)
333 },
334 {
335 let key = &Http::SSL_VERSION;
336 (env(key), key.name)
337 },
338 ][..],
339 ),
340 (
341 "gitoxide",
342 Some(Cow::Borrowed("https".into())),
343 http_transport,
344 &[
345 ("HTTPS_PROXY", gitoxide::Https::PROXY.name),
346 ("https_proxy", gitoxide::Https::PROXY.name),
347 ],
348 ),
349 (
350 "gitoxide",
351 Some(Cow::Borrowed("http".into())),
352 http_transport,
353 &[
354 ("ALL_PROXY", "allProxy"),
355 {
356 let key = &gitoxide::Http::ALL_PROXY;
357 (env(key), key.name)
358 },
359 ("NO_PROXY", "noProxy"),
360 {
361 let key = &gitoxide::Http::NO_PROXY;
362 (env(key), key.name)
363 },
364 {
365 let key = &gitoxide::Http::PROXY;
366 (env(key), key.name)
367 },
368 {
369 let key = &gitoxide::Http::VERBOSE;
370 (env(key), key.name)
371 },
372 {
373 let key = &gitoxide::Http::PROXY_AUTH_METHOD;
374 (env(key), key.name)
375 },
376 ],
377 ),
378 (
379 "gitoxide",
380 Some(Cow::Borrowed("committer".into())),
381 identity,
382 &[
383 {
384 let key = &gitoxide::Committer::NAME_FALLBACK;
385 (env(key), key.name)
386 },
387 {
388 let key = &gitoxide::Committer::EMAIL_FALLBACK;
389 (env(key), key.name)
390 },
391 ],
392 ),
393 (
394 "gitoxide",
Chris Wailescd1aefd2023-07-13 13:36:21 -0700395 Some(Cow::Borrowed("core".into())),
396 git_prefix,
397 &[{
398 let key = &gitoxide::Core::SHALLOW_FILE;
399 (env(key), key.name)
400 }],
401 ),
402 (
403 "gitoxide",
Charisee635618d2023-06-01 20:46:00 +0000404 Some(Cow::Borrowed("author".into())),
405 identity,
406 &[
407 {
408 let key = &gitoxide::Author::NAME_FALLBACK;
409 (env(key), key.name)
410 },
411 {
412 let key = &gitoxide::Author::EMAIL_FALLBACK;
413 (env(key), key.name)
414 },
415 ],
416 ),
417 (
418 "gitoxide",
419 Some(Cow::Borrowed("commit".into())),
420 git_prefix,
421 &[
422 {
423 let key = &gitoxide::Commit::COMMITTER_DATE;
424 (env(key), key.name)
425 },
426 {
427 let key = &gitoxide::Commit::AUTHOR_DATE;
428 (env(key), key.name)
429 },
430 ],
431 ),
432 (
433 "gitoxide",
434 Some(Cow::Borrowed("allow".into())),
435 http_transport,
436 &[("GIT_PROTOCOL_FROM_USER", "protocolFromUser")],
437 ),
438 (
439 "gitoxide",
440 Some(Cow::Borrowed("user".into())),
441 identity,
442 &[{
443 let key = &gitoxide::User::EMAIL_FALLBACK;
444 (env(key), key.name)
445 }],
446 ),
447 (
448 "gitoxide",
449 Some(Cow::Borrowed("objects".into())),
450 objects,
451 &[
452 {
Charisee635618d2023-06-01 20:46:00 +0000453 let key = &gitoxide::Objects::REPLACE_REF_BASE;
454 (env(key), key.name)
455 },
456 {
457 let key = &gitoxide::Objects::CACHE_LIMIT;
458 (env(key), key.name)
459 },
460 ],
461 ),
462 (
463 "gitoxide",
464 Some(Cow::Borrowed("ssh".into())),
465 git_prefix,
466 &[{
467 let key = &gitoxide::Ssh::COMMAND_WITHOUT_SHELL_FALLBACK;
468 (env(key), key.name)
469 }],
470 ),
471 (
Chris Wailes951ae7a2023-12-07 10:11:13 -0800472 "gitoxide",
473 Some(Cow::Borrowed("pathspec".into())),
474 git_prefix,
475 &[
476 {
477 let key = &gitoxide::Pathspec::LITERAL;
478 (env(key), key.name)
479 },
480 {
481 let key = &gitoxide::Pathspec::GLOB;
482 (env(key), key.name)
483 },
484 {
485 let key = &gitoxide::Pathspec::NOGLOB;
486 (env(key), key.name)
487 },
488 {
489 let key = &gitoxide::Pathspec::ICASE;
490 (env(key), key.name)
491 },
492 ],
493 ),
494 (
Charisee635618d2023-06-01 20:46:00 +0000495 "ssh",
496 None,
497 git_prefix,
498 &[{
499 let key = &config::tree::Ssh::VARIANT;
500 (env(key), key.name)
501 }],
502 ),
503 ] {
504 let mut section = env_override
505 .new_section(section_name, subsection_name)
506 .expect("statically known valid section name");
507 for (var, key) in data {
508 if let Some(value) = var_as_bstring(var, permission) {
509 section.push_with_comment(
510 (*key).try_into().expect("statically known to be valid"),
511 Some(value.as_ref()),
512 format!("from {var}").as_str(),
513 );
514 }
515 }
516 if section.num_values() == 0 {
517 let id = section.id();
518 env_override.remove_section_by_id(id);
519 }
520 }
521
522 {
523 let mut section = env_override
524 .new_section("core", None)
525 .expect("statically known valid section name");
526
527 for (var, key, permission) in [
528 {
529 let key = &Core::DELTA_BASE_CACHE_LIMIT;
530 (env(key), key.name, objects)
531 },
532 {
533 let key = &Core::SSH_COMMAND;
534 (env(key), key.name, git_prefix)
535 },
James Farrell91b821d2023-08-24 14:18:40 +0000536 {
537 let key = &Core::USE_REPLACE_REFS;
538 (env(key), key.name, objects)
539 },
Charisee635618d2023-06-01 20:46:00 +0000540 ] {
541 if let Some(value) = var_as_bstring(var, permission) {
542 section.push_with_comment(
543 key.try_into().expect("statically known to be valid"),
544 Some(value.as_ref()),
545 format!("from {var}").as_str(),
546 );
547 }
548 }
549
550 if section.num_values() == 0 {
551 let id = section.id();
552 env_override.remove_section_by_id(id);
553 }
554 }
555
556 if !env_override.is_void() {
557 config.append(env_override);
558 }
559 Ok(())
560}