History

Merge pull request #1752 from GitoxideLabs/git-shell

git login shell

Sebastian Thiel committed 1 year ago

Commit 1ca480a2 parents 8d84818 3aff1e530 files · +141 −114Browse files

Unified · Split

30 files
  1. .github/workflows/ci.yml +1 −1
  2. Cargo.toml +1 −0
  3. gitoxide-core/src/repository/attributes/query.rs +1 −1
  4. gitoxide-core/src/repository/clean.rs +2 −2
  5. gitoxide-core/src/repository/config.rs +5 −4
  6. gitoxide-core/src/repository/exclude.rs +1 −1
  7. gitoxide-core/src/repository/fetch.rs +1 −1
  8. gitoxide-core/src/repository/index/entries.rs +1 −1
  9. gitoxide-core/src/repository/remote.rs +1 −1
  10. gitoxide-core/src/repository/revision/list.rs +1 −1
  11. gix-dir/src/walk/classify.rs +1 −1
  12. gix-filter/examples/arrow.rs +1 −1
  13. gix-filter/src/driver/process/mod.rs +1 −1
  14. gix-pack/src/multi_index/chunk.rs +2 −2
  15. gix-packetline-blocking/src/line/async_io.rs +4 −4
  16. gix-packetline/src/line/async_io.rs +4 −4
  17. gix-path/src/env/mod.rs +17 −0
  18. gix-path/tests/path.rs +0 −80
  19. gix-path/tests/path/convert/mod.rs +0 −0
  20. gix-path/tests/path/convert/normalize.rs +0 −0
  21. gix-path/tests/path/env.rs +71 −0
  22. gix-path/tests/path/main.rs +18 −0
  23. gix-path/tests/path/realpath.rs +0 −0
  24. gix-path/tests/path/util.rs +0 −0
  25. gix-ref/src/store/file/transaction/commit.rs +1 −1
  26. gix-submodule/src/lib.rs +1 −3
  27. gix-validate/src/path.rs +1 −1
  28. gix/src/config/tree/sections/mod.rs +1 −0
  29. gix/src/remote/url/scheme_permission.rs +1 −1
  30. tests/tools/src/lib.rs +2 −2
.github/workflows/ci.yml+1−1
@@ -318,7 +318,7 @@ jobs: · 317 unmodified lines
318318
319319 strategy:
320320 matrix:
321− target: [ wasm32-unknown-unknown, wasm32-wasi ]
321+ target: [ wasm32-unknown-unknown, wasm32-wasip1 ]
322322
323323 env:
324324 TARGET: ${{ matrix.target }}
Cargo.toml+1−0
@@ -394,3 +394,4 @@ stable_sort_primitive = "allow" # x1 · 393 unmodified lines
394394 no_effect_underscore_binding = "allow" # x1
395395 empty_docs = "allow"
396396 too_long_first_doc_paragraph = "allow"
397+large_stack_arrays = "allow"
gitoxide-core/src/repository/attributes/query.rs+1−1
@@ -89,7 +89,7 @@ pub(crate) mod function { · 88 unmodified lines
8989 let entry = cache.at_entry(
9090 path,
9191 Some(is_dir_to_mode(
92− workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
92+ workdir.is_some_and(|wd| wd.join(gix::path::from_bstr(path)).is_dir())
9393 || pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
9494 )),
9595 )?;
gitoxide-core/src/repository/clean.rs+2−2
@@ -128,10 +128,10 @@ pub(crate) mod function { · 127 unmodified lines
128128 let pathspec_includes_entry = match pathspec.as_mut() {
129129 None => entry
130130 .pathspec_match
131− .map_or(false, |m| m != gix::dir::entry::PathspecMatch::Excluded),
131+ .is_some_and(|m| m != gix::dir::entry::PathspecMatch::Excluded),
132132 Some(pathspec) => pathspec
133133 .pattern_matching_relative_path(entry.rela_path.as_bstr(), entry.disk_kind.map(|k| k.is_dir()))
134− .map_or(false, |m| !m.is_excluded()),
134+ .is_some_and(|m| !m.is_excluded()),
135135 };
136136 pruned_entries += usize::from(!pathspec_includes_entry);
137137 if !pathspec_includes_entry && debug {
gitoxide-core/src/repository/config.rs+5−4
@@ -32,7 +32,7 @@ pub fn list( · 31 unmodified lines
3232 }
3333
3434 let meta = section.meta();
35− if last_meta.map_or(true, |last| last != meta) {
35+ if last_meta != Some(meta) {
3636 write_meta(meta, &mut out)?;
3737 }
3838 last_meta = Some(meta);
@@ -41,9 +41,10 @@ pub fn list( · 2 unmodified lines
4141 for event in matter {
4242 event.write_to(&mut out)?;
4343 }
44− if it.peek().map_or(false, |(next_section, _)| {
45− next_section.header().name() != section.header().name()
46− }) {
44+ if it
45+ .peek()
46+ .is_some_and(|(next_section, _)| next_section.header().name() != section.header().name())
47+ {
4748 writeln!(&mut out)?;
4849 }
4950 }
gitoxide-core/src/repository/exclude.rs+1−1
@@ -93,7 +93,7 @@ pub fn query( · 92 unmodified lines
9393 let entry = cache.at_entry(
9494 path,
9595 Some(is_dir_to_mode(
96− workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
96+ workdir.is_some_and(|wd| wd.join(gix::path::from_bstr(path)).is_dir())
9797 || pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
9898 )),
9999 )?;
gitoxide-core/src/repository/fetch.rs+1−1
@@ -305,7 +305,7 @@ pub(crate) mod function { · 304 unmodified lines
305305 for fix in &map.fixes {
306306 match fix {
307307 Fix::MappingWithPartialDestinationRemoved { name, spec } => {
308− if prev_spec.map_or(true, |prev_spec| prev_spec != spec) {
308+ if prev_spec.is_some_and(|prev_spec| prev_spec != spec) {
309309 prev_spec = spec.into();
310310 spec.to_ref().write_to(&mut err)?;
311311 writeln!(err)?;
gitoxide-core/src/repository/index/entries.rs+1−1
@@ -198,7 +198,7 @@ pub(crate) mod function { · 197 unmodified lines
198198 sms_by_path
199199 .iter()
200200 .find_map(|(path, sm)| (path == entry_path).then_some(sm))
201− .filter(|sm| sm.git_dir_try_old_form().map_or(false, |dot_git| dot_git.exists()))
201+ .filter(|sm| sm.git_dir_try_old_form().is_ok_and(|dot_git| dot_git.exists()))
202202 })
203203 {
204204 let sm_path = gix::path::to_unix_separators_on_windows(sm.path()?);
gitoxide-core/src/repository/remote.rs+1−1
@@ -190,7 +190,7 @@ mod refs_impl { · 189 unmodified lines
190190 for fix in &map.fixes {
191191 match fix {
192192 Fix::MappingWithPartialDestinationRemoved { name, spec } => {
193− if prev_spec.map_or(true, |prev_spec| prev_spec != spec) {
193+ if prev_spec.is_some_and(|prev_spec| prev_spec != spec) {
194194 prev_spec = spec.into();
195195 spec.to_ref().write_to(&mut err)?;
196196 writeln!(err)?;
gitoxide-core/src/repository/revision/list.rs+1−1
@@ -108,7 +108,7 @@ pub(crate) mod function { · 107 unmodified lines
108108 }
109109 }
110110 progress.inc();
111− if limit.map_or(false, |limit| limit == progress.step()) {
111+ if limit.is_some_and(|limit| limit == progress.step()) {
112112 break;
113113 }
114114 }
gix-dir/src/walk/classify.rs+1−1
@@ -215,7 +215,7 @@ pub fn path( · 214 unmodified lines
215215
216216 let is_dir = if symlinks_to_directories_are_ignored_like_directories
217217 && ctx.excludes.is_some()
218− && kind.map_or(false, |ft| ft == entry::Kind::Symlink)
218+ && kind == Some(entry::Kind::Symlink)
219219 {
220220 path.metadata().ok().map(|md| is_dir_to_mode(md.is_dir()))
221221 } else {
gix-filter/examples/arrow.rs+1−1
@@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { · 18 unmodified lines
1919
2020 match sub_command.as_str() {
2121 "process" => {
22− let disallow_delay = next_arg.as_deref().map_or(false, |arg| arg == "disallow-delay");
22+ let disallow_delay = next_arg.as_deref() == Some("disallow-delay");
2323 let mut srv = gix_filter::driver::process::Server::handshake(
2424 stdin(),
2525 stdout(),
gix-filter/src/driver/process/mod.rs+1−1
@@ -83,7 +83,7 @@ impl Status { · 82 unmodified lines
8383
8484 /// Returns true if this is an `abort` status.
8585 pub fn is_abort(&self) -> bool {
86− self.message().map_or(false, |m| m == "abort")
86+ self.message() == Some("abort")
8787 }
8888
8989 /// Return true if the status is explicitly set to indicated delayed output processing
gix-pack/src/multi_index/chunk.rs+2−2
@@ -69,7 +69,7 @@ pub mod index_names { · 68 unmodified lines
6969 ascii_path.is_ascii(),
7070 "must use ascii bytes for correct size computation"
7171 );
72− count += (ascii_path.as_bytes().len() + 1/* null byte */) as u64;
72+ count += (ascii_path.len() + 1/* null byte */) as u64;
7373 }
7474
7575 let needed_alignment = CHUNK_ALIGNMENT - (count % CHUNK_ALIGNMENT);
@@ -89,7 +89,7 @@ pub mod index_names { · 13 unmodified lines
8989 let path = path.as_ref().to_str().expect("UTF-8 path");
9090 out.write_all(path.as_bytes())?;
9191 out.write_all(&[0])?;
92− written_bytes += path.as_bytes().len() as u64 + 1;
92+ written_bytes += path.len() as u64 + 1;
9393 }
9494
9595 let needed_alignment = CHUNK_ALIGNMENT - (written_bytes % CHUNK_ALIGNMENT);
gix-packetline-blocking/src/line/async_io.rs+4−4
@@ -6,7 +6,7 @@ use futures_io::AsyncWrite; · 5 unmodified lines
66
77 use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef};
88
9−impl<'a> BandRef<'a> {
9+impl BandRef<'_> {
1010 /// Serialize this instance to `out`, returning the amount of bytes written.
1111 ///
1212 /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`.
@@ -20,14 +20,14 @@ impl<'a> BandRef<'a> { · 7 unmodified lines
2020 }
2121 }
2222
23−impl<'a> TextRef<'a> {
23+impl TextRef<'_> {
2424 /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written.
2525 pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result<usize> {
2626 encode::text_to_write(self.0, out).await
2727 }
2828 }
2929
30−impl<'a> ErrorRef<'a> {
30+impl ErrorRef<'_> {
3131 /// Serialize this line as error to `out`.
3232 ///
3333 /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written.
@@ -36,7 +36,7 @@ impl<'a> ErrorRef<'a> { · 2 unmodified lines
3636 }
3737 }
3838
39−impl<'a> PacketLineRef<'a> {
39+impl PacketLineRef<'_> {
4040 /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`.
4141 pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result<usize> {
4242 match self {
gix-packetline/src/line/async_io.rs+4−4
@@ -4,7 +4,7 @@ use futures_io::AsyncWrite; · 3 unmodified lines
44
55 use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef};
66
7−impl<'a> BandRef<'a> {
7+impl BandRef<'_> {
88 /// Serialize this instance to `out`, returning the amount of bytes written.
99 ///
1010 /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`.
@@ -18,14 +18,14 @@ impl<'a> BandRef<'a> { · 7 unmodified lines
1818 }
1919 }
2020
21−impl<'a> TextRef<'a> {
21+impl TextRef<'_> {
2222 /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written.
2323 pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result<usize> {
2424 encode::text_to_write(self.0, out).await
2525 }
2626 }
2727
28−impl<'a> ErrorRef<'a> {
28+impl ErrorRef<'_> {
2929 /// Serialize this line as error to `out`.
3030 ///
3131 /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written.
@@ -34,7 +34,7 @@ impl<'a> ErrorRef<'a> { · 2 unmodified lines
3434 }
3535 }
3636
37−impl<'a> PacketLineRef<'a> {
37+impl PacketLineRef<'_> {
3838 /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`.
3939 pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result<usize> {
4040 match self {
gix-path/src/env/mod.rs+17−0
@@ -28,6 +28,23 @@ pub fn installation_config_prefix() -> Option<&'static Path> { · 27 unmodified lines
2828 installation_config().map(git::config_to_base_path)
2929 }
3030
31+/// Return the shell that Git would prefer as login shell, the shell to execute Git commands from.
32+///
33+/// On Windows, this is the `bash.exe` bundled with it, and on Unix it's the shell specified by `SHELL`,
34+/// or `None` if it is truly unspecified.
35+pub fn login_shell() -> Option<&'static Path> {
36+ static PATH: Lazy<Option<PathBuf>> = Lazy::new(|| {
37+ if cfg!(windows) {
38+ installation_config_prefix()
39+ .and_then(|p| p.parent())
40+ .map(|p| p.join("usr").join("bin").join("bash.exe"))
41+ } else {
42+ std::env::var_os("SHELL").map(PathBuf::from)
43+ }
44+ });
45+ PATH.as_deref()
46+}
47+
3148 /// Return the name of the Git executable to invoke it.
3249 /// If it's in the `PATH`, it will always be a short name.
3350 ///
gix-path/tests/path.rs+0−80
@@ -1,80 +0,0 @@
1−pub type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>;
2−
3−mod convert;
4−mod realpath;
5−mod home_dir {
6− #[test]
7− fn returns_existing_directory() {
8− if let Some(home) = gix_path::env::home_dir() {
9− assert!(
10− home.is_dir(),
11− "the home directory would typically exist, even though on unix we don't test for that."
12− );
13− }
14− }
15−}
16−
17−mod env {
18− #[test]
19− fn exe_invocation() {
20− let actual = gix_path::env::exe_invocation();
21− assert!(
22− !actual.as_os_str().is_empty(),
23− "it finds something as long as git is installed somewhere on the system (or a default location)"
24− );
25− }
26−
27− #[test]
28− fn installation_config() {
29− assert_ne!(
30− gix_path::env::installation_config().map(|p| p.components().count()),
31− gix_path::env::installation_config_prefix().map(|p| p.components().count()),
32− "the prefix is a bit shorter than the installation config path itself"
33− );
34− }
35−
36− #[test]
37− fn system_prefix() {
38− assert_ne!(
39− gix_path::env::system_prefix(),
40− None,
41− "git should be present when running tests"
42− );
43− }
44−
45− #[test]
46− fn home_dir() {
47− assert_ne!(
48− gix_path::env::home_dir(),
49− None,
50− "we find a home on every system these tests execute"
51− );
52− }
53−
54− mod xdg_config {
55− use std::ffi::OsStr;
56−
57− #[test]
58− fn prefers_xdg_config_bases() {
59− let actual = gix_path::env::xdg_config("test", &mut |n| {
60− (n == OsStr::new("XDG_CONFIG_HOME")).then(|| "marker".into())
61− })
62− .expect("set");
63− #[cfg(unix)]
64− assert_eq!(actual.to_str(), Some("marker/git/test"));
65− #[cfg(windows)]
66− assert_eq!(actual.to_str(), Some("marker\\git\\test"));
67− }
68−
69− #[test]
70− fn falls_back_to_home() {
71− let actual = gix_path::env::xdg_config("test", &mut |n| (n == OsStr::new("HOME")).then(|| "marker".into()))
72− .expect("set");
73− #[cfg(unix)]
74− assert_eq!(actual.to_str(), Some("marker/.config/git/test"));
75− #[cfg(windows)]
76− assert_eq!(actual.to_str(), Some("marker\\.config\\git\\test"));
77− }
78− }
79−}
80−mod util;
gix-path/tests/path/convert/mod.rsrenamed from gix-path/tests/convert/mod.rs+0−0

File renamed without changes.

gix-path/tests/path/convert/normalize.rsrenamed from gix-path/tests/convert/normalize.rs+0−0

File renamed without changes.

gix-path/tests/path/env.rs+71−0
@@ -0,0 +1,71 @@
1+#[test]
2+fn exe_invocation() {
3+ let actual = gix_path::env::exe_invocation();
4+ assert!(
5+ !actual.as_os_str().is_empty(),
6+ "it finds something as long as git is installed somewhere on the system (or a default location)"
7+ );
8+}
9+
10+#[test]
11+fn login_shell() {
12+ // On CI, the $SHELL variable isn't necessarily set. Maybe other ways to get the login shell should be used then.
13+ if !gix_testtools::is_ci::cached() {
14+ assert!(gix_path::env::login_shell()
15+ .expect("There should always be the notion of a shell used by git")
16+ .exists());
17+ }
18+}
19+
20+#[test]
21+fn installation_config() {
22+ assert_ne!(
23+ gix_path::env::installation_config().map(|p| p.components().count()),
24+ gix_path::env::installation_config_prefix().map(|p| p.components().count()),
25+ "the prefix is a bit shorter than the installation config path itself"
26+ );
27+}
28+
29+#[test]
30+fn system_prefix() {
31+ assert_ne!(
32+ gix_path::env::system_prefix(),
33+ None,
34+ "git should be present when running tests"
35+ );
36+}
37+
38+#[test]
39+fn home_dir() {
40+ assert_ne!(
41+ gix_path::env::home_dir(),
42+ None,
43+ "we find a home on every system these tests execute"
44+ );
45+}
46+
47+mod xdg_config {
48+ use std::ffi::OsStr;
49+
50+ #[test]
51+ fn prefers_xdg_config_bases() {
52+ let actual = gix_path::env::xdg_config("test", &mut |n| {
53+ (n == OsStr::new("XDG_CONFIG_HOME")).then(|| "marker".into())
54+ })
55+ .expect("set");
56+ #[cfg(unix)]
57+ assert_eq!(actual.to_str(), Some("marker/git/test"));
58+ #[cfg(windows)]
59+ assert_eq!(actual.to_str(), Some("marker\\git\\test"));
60+ }
61+
62+ #[test]
63+ fn falls_back_to_home() {
64+ let actual = gix_path::env::xdg_config("test", &mut |n| (n == OsStr::new("HOME")).then(|| "marker".into()))
65+ .expect("set");
66+ #[cfg(unix)]
67+ assert_eq!(actual.to_str(), Some("marker/.config/git/test"));
68+ #[cfg(windows)]
69+ assert_eq!(actual.to_str(), Some("marker\\.config\\git\\test"));
70+ }
71+}
gix-path/tests/path/main.rs+18−0
@@ -0,0 +1,18 @@
1+pub type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>;
2+
3+mod convert;
4+mod realpath;
5+mod home_dir {
6+ #[test]
7+ fn returns_existing_directory() {
8+ if let Some(home) = gix_path::env::home_dir() {
9+ assert!(
10+ home.is_dir(),
11+ "the home directory would typically exist, even though on unix we don't test for that."
12+ );
13+ }
14+ }
15+}
16+
17+mod env;
18+mod util;
gix-path/tests/path/realpath.rsrenamed from gix-path/tests/realpath/mod.rs+0−0

File renamed without changes.

gix-path/tests/path/util.rsrenamed from gix-path/tests/util/mod.rs+0−0

File renamed without changes.

gix-ref/src/store/file/transaction/commit.rs+1−1
@@ -72,7 +72,7 @@ impl Transaction<'_, '_> { · 71 unmodified lines
7272 }
7373 };
7474 if let Some((previous, new_oid)) = log_update {
75− let do_update = previous.as_ref().map_or(true, |previous| previous != new_oid);
75+ let do_update = previous.as_ref() != Some(new_oid);
7676 if do_update {
7777 self.store.reflog_create_or_append(
7878 change.update.name.as_ref(),
gix-submodule/src/lib.rs+1−3
@@ -4,8 +4,6 @@ · 3 unmodified lines
44
55 use std::{borrow::Cow, collections::BTreeMap};
66
7−use bstr::BStr;
8−
97 /// All relevant information about a git module, typically from `.gitmodules` files.
108 ///
119 /// Note that overrides from other configuration might be relevant, which is why this type
@@ -64,7 +62,7 @@ impl File { · 52 unmodified lines
6462 let mut config_to_append = gix_config::File::new(config.meta_owned());
6563 let mut prev_name = None;
6664 for ((module_name, field), values) in values {
67− if prev_name.map_or(true, |pn: &BStr| pn != module_name) {
65+ if prev_name != Some(module_name) {
6866 config_to_append
6967 .new_section("submodule", Some(Cow::Owned(module_name.to_owned())))
7068 .expect("all names come from valid configuration, so remain valid");
gix-validate/src/path.rs+1−1
@@ -185,7 +185,7 @@ fn check_win_devices_and_illegal_characters(input: &BStr) -> Option<component::E · 184 unmodified lines
185185 }
186186
187187 fn is_symlink(mode: Option<component::Mode>) -> bool {
188− mode.map_or(false, |m| m == component::Mode::Symlink)
188+ mode == Some(component::Mode::Symlink)
189189 }
190190
191191 fn is_dot_hfs(input: &BStr, search_case_insensitive: &str) -> bool {
gix/src/config/tree/sections/mod.rs+1−0
@@ -1,3 +1,4 @@
1+#![allow(clippy::unnecessary_literal_bound)]
12 #![allow(missing_docs)]
23
34 /// The `author` top-level section.
gix/src/remote/url/scheme_permission.rs+1−1
@@ -63,7 +63,7 @@ impl SchemePermission { · 62 unmodified lines
6363 .map(|value| Protocol::ALLOW.try_into_allow(value, None))
6464 .transpose()?;
6565
66− let mut saw_user = allow.map_or(false, |allow| allow == Allow::User);
66+ let mut saw_user = allow == Some(Allow::User);
6767 let allow_per_scheme = match config.sections_by_name_and_filter("protocol", &mut filter) {
6868 Some(it) => {
6969 let mut map = BTreeMap::default();
tests/tools/src/lib.rs+2−2
@@ -652,6 +652,7 @@ fn configure_command<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>( · 651 unmodified lines
652652 }
653653
654654 fn bash_program() -> &'static Path {
655+ // TODO: use `gix_path::env::login_shell()` when available.
655656 if cfg!(windows) {
656657 static GIT_BASH: Lazy<Option<PathBuf>> = Lazy::new(|| {
657658 GIT_CORE_DIR
@@ -685,8 +686,7 @@ fn is_lfs_pointer_file(path: &Path) -> bool { · 27 unmodified lines
685686 std::fs::OpenOptions::new()
686687 .read(true)
687688 .open(path)
688− .and_then(|mut f| f.read_exact(&mut buf))
689− .map_or(false, |_| buf.starts_with(PREFIX))
689+ .is_ok_and(|mut f| f.read_exact(&mut buf).is_ok_and(|_| buf.starts_with(PREFIX)))
690690 }
691691
692692 /// The `script_identity` will be baked into the soon to be created `archive` as it identifies the script