History

Merge branch 'fixes'

Sebastian Thiel committed 2 years ago

Commit b4dba1c2 parents db1b223 1e92d1e16 files · +444 −96Browse files

Unified · Split

16 files
  1. gix-attributes/src/lib.rs +2 −1
  2. gix-attributes/src/name.rs +1 −2
  3. gix-attributes/src/parse.rs +1 −2
  4. gix-attributes/src/search/mod.rs +1 −2
  5. gix-attributes/src/state.rs +9 −14
  6. gix-attributes/tests/parse/mod.rs +29 −0
  7. gix-attributes/tests/search/mod.rs +1 −1
  8. gix-dir/src/walk/classify.rs +2 −0
  9. gix-dir/tests/fixtures/many.sh +80 −0
  10. gix-dir/tests/walk/mod.rs +158 −0
  11. gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh +68 −2
  12. gix-ignore/tests/search/mod.rs +75 −62
  13. gix-transport/src/client/git/mod.rs +1 −1
  14. gix/src/repository/config/transport.rs +2 −5
  15. gix/src/repository/remote.rs +4 −4
  16. gix/tests/remote/mod.rs +10 −0
gix-attributes/src/lib.rs+2−1
@@ -6,7 +6,8 @@ · 5 unmodified lines
6 doc = ::document_features::document_features!()6 doc = ::document_features::document_features!()
7)]7)]
8#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]8#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
9#![deny(missing_docs, rust_2018_idioms, unsafe_code)]9#![deny(missing_docs, rust_2018_idioms)]
10#![forbid(unsafe_code)]
10 11
11pub use gix_glob as glob;12pub use gix_glob as glob;
12use kstring::{KString, KStringRef};13use kstring::{KString, KStringRef};
gix-attributes/src/name.rs+1−2
@@ -1,8 +1,7 @@
1use crate::{Name, NameRef};
1use bstr::{BStr, BString, ByteSlice};2use bstr::{BStr, BString, ByteSlice};
2use kstring::KStringRef;3use kstring::KStringRef;
3 4
4use crate::{Name, NameRef};
5
6impl<'a> NameRef<'a> {5impl<'a> NameRef<'a> {
7 /// Turn this ref into its owned counterpart.6 /// Turn this ref into its owned counterpart.
8 pub fn to_owned(self) -> Name {7 pub fn to_owned(self) -> Name {
gix-attributes/src/parse.rs+1−2
@@ -1,10 +1,9 @@
1use std::borrow::Cow;1use std::borrow::Cow;
2 2
3use crate::{name, AssignmentRef, Name, NameRef, StateRef};
3use bstr::{BStr, ByteSlice};4use bstr::{BStr, ByteSlice};
4use kstring::KStringRef;5use kstring::KStringRef;
5 6
6use crate::{name, AssignmentRef, Name, NameRef, StateRef};
7
8/// The kind of attribute that was parsed.7/// The kind of attribute that was parsed.
9#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]8#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
gix-attributes/src/search/mod.rs+1−2
@@ -1,7 +1,6 @@
1use std::collections::HashMap;
2
3use kstring::KString;1use kstring::KString;
4use smallvec::SmallVec;2use smallvec::SmallVec;
3use std::collections::HashMap;
5 4
6use crate::{Assignment, AssignmentRef};5use crate::{Assignment, AssignmentRef};
7 6
gix-attributes/src/state.rs+9−14
@@ -1,29 +1,24 @@
1use bstr::{BStr, ByteSlice};
2use kstring::{KString, KStringRef};
3
4use crate::{State, StateRef};1use crate::{State, StateRef};
2use bstr::{BStr, BString, ByteSlice};
5 3
6/// A container to encapsulate a tightly packed and typically unallocated byte value that isn't necessarily UTF8 encoded.4/// A container to encapsulate a tightly packed and typically unallocated byte value that isn't necessarily UTF8 encoded.
7#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]5#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Value(KString);7// TODO: This should be some sort of 'smallbstring' - but can't use `kstring` here due to UTF8 requirement. 5% performance boost possible.
8// What's really needed here is a representation that displays as string when serialized which helps with JSON.
9// Maybe `smallvec` with display and serialization wrapper would do the trick?
10pub struct Value(BString);
10 11
11/// A reference container to encapsulate a tightly packed and typically unallocated byte value that isn't necessarily UTF8 encoded.12/// A reference container to encapsulate a tightly packed and typically unallocated byte value that isn't necessarily UTF8 encoded.
12#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]13#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct ValueRef<'a>(#[cfg_attr(feature = "serde", serde(borrow))] KStringRef<'a>);15pub struct ValueRef<'a>(#[cfg_attr(feature = "serde", serde(borrow))] &'a [u8]);
15 16
16/// Lifecycle17/// Lifecycle
17impl<'a> ValueRef<'a> {18impl<'a> ValueRef<'a> {
18 /// Keep `input` as our value.19 /// Keep `input` as our value.
19 pub fn from_bytes(input: &'a [u8]) -> Self {20 pub fn from_bytes(input: &'a [u8]) -> Self {
20 Self(KStringRef::from_ref(21 Self(input)
21 // SAFETY: our API makes accessing that value as `str` impossible, so illformed UTF8 is never exposed as such.
22 #[allow(unsafe_code)]
23 unsafe {
24 std::str::from_utf8_unchecked(input)
25 },
26 ))
27 }22 }
28}23}
29 24
@@ -42,7 +37,7 @@ impl ValueRef<'_> { · 12 unmodified lines
42 37
43impl<'a> From<&'a str> for ValueRef<'a> {38impl<'a> From<&'a str> for ValueRef<'a> {
44 fn from(v: &'a str) -> Self {39 fn from(v: &'a str) -> Self {
45 ValueRef(v.into())40 ValueRef(v.as_bytes())
46 }41 }
47}42}
48 43
@@ -54,7 +49,7 @@ impl<'a> From<ValueRef<'a>> for Value { · 5 unmodified lines
54 49
55impl From<&str> for Value {50impl From<&str> for Value {
56 fn from(v: &str) -> Self {51 fn from(v: &str) -> Self {
57 Value(KString::from_ref(v))52 Value(v.as_bytes().into())
58 }53 }
59}54}
60 55
gix-attributes/tests/parse/mod.rs+29−0
@@ -1,4 +1,5 @@
1use bstr::BString;1use bstr::BString;
2use gix_attributes::state::ValueRef;
2use gix_attributes::{parse, StateRef};3use gix_attributes::{parse, StateRef};
3use gix_glob::pattern::Mode;4use gix_glob::pattern::Mode;
4use gix_testtools::fixture_bytes;5use gix_testtools::fixture_bytes;
@@ -275,6 +276,19 @@ fn attributes_can_have_values() { · 270 unmodified lines
275 );276 );
276}277}
277 278
279#[test]
280fn attributes_can_have_illformed_utf8() {
281 assert_eq!(
282 byte_line(b"p a=one b=\xC3\x28\x41 c=d "),
283 (
284 pattern("p", Mode::NO_SUB_DIR, None),
285 vec![value("a", "one"), byte_value("b", b"\xC3\x28\x41"), value("c", "d")],
286 1
287 ),
288 "illformed UTF8 is fully supported"
289 );
290}
291
278#[test]292#[test]
279fn attributes_see_state_adjustments_over_value_assignments() {293fn attributes_see_state_adjustments_over_value_assignments() {
280 assert_eq!(294 assert_eq!(
@@ -325,6 +339,10 @@ fn value<'b>(attr: &str, value: &'b str) -> (BString, StateRef<'b>) { · 44 unmodified lines
325 (attr.into(), StateRef::Value(value.into()))339 (attr.into(), StateRef::Value(value.into()))
326}340}
327 341
342fn byte_value<'b>(attr: &str, value: &'b [u8]) -> (BString, StateRef<'b>) {
343 (attr.into(), StateRef::Value(ValueRef::from_bytes(value)))
344}
345
328fn pattern(name: &str, flags: gix_glob::pattern::Mode, first_wildcard_pos: Option<usize>) -> parse::Kind {346fn pattern(name: &str, flags: gix_glob::pattern::Mode, first_wildcard_pos: Option<usize>) -> parse::Kind {
329 parse::Kind::Pattern(gix_glob::Pattern {347 parse::Kind::Pattern(gix_glob::Pattern {
330 text: name.into(),348 text: name.into(),
@@ -344,6 +362,17 @@ fn line(input: &str) -> ExpandedAttribute { · 13 unmodified lines
344 try_line(input).unwrap()362 try_line(input).unwrap()
345}363}
346 364
365fn byte_line(input: &[u8]) -> ExpandedAttribute {
366 try_byte_line(input).unwrap()
367}
368
369fn try_byte_line(input: &[u8]) -> Result<ExpandedAttribute, parse::Error> {
370 let mut lines = gix_attributes::parse(input);
371 let res = expand(lines.next().unwrap())?;
372 assert!(lines.next().is_none(), "expected only one line");
373 Ok(res)
374}
375
347fn lenient_lines(input: &str) -> Vec<ExpandedAttribute> {376fn lenient_lines(input: &str) -> Vec<ExpandedAttribute> {
348 gix_attributes::parse(input.as_bytes())377 gix_attributes::parse(input.as_bytes())
349 .map(expand)378 .map(expand)
gix-attributes/tests/search/mod.rs+1−1
@@ -270,7 +270,7 @@ fn given_attributes_are_made_available_in_given_order() -> crate::Result { · 269 unmodified lines
270fn size_of_outcome() {270fn size_of_outcome() {
271 assert_eq!(271 assert_eq!(
272 std::mem::size_of::<Outcome>(),272 std::mem::size_of::<Outcome>(),
273 904,273 840,
274 "it's quite big, shouldn't change without us noticing"274 "it's quite big, shouldn't change without us noticing"
275 )275 )
276}276}
gix-dir/src/walk/classify.rs+2−0
@@ -169,6 +169,7 @@ pub fn path( · 168 unmodified lines
169 .map(|platform| platform.excluded_kind())169 .map(|platform| platform.excluded_kind())
170 })170 })
171 .map_err(Error::ExcludesAccess)?171 .map_err(Error::ExcludesAccess)?
172 .filter(|_| filename_start_idx > 0)
172 {173 {
173 out.status = entry::Status::Ignored(excluded);174 out.status = entry::Status::Ignored(excluded);
174 }175 }
@@ -256,6 +257,7 @@ pub fn path( · 81 unmodified lines
256 if let Some(excluded) = ctx257 if let Some(excluded) = ctx
257 .excludes258 .excludes
258 .as_mut()259 .as_mut()
260 .filter(|_| !rela_path.is_empty())
259 .map_or(Ok(None), |stack| {261 .map_or(Ok(None), |stack| {
260 stack262 stack
261 .at_entry(rela_path.as_bstr(), is_dir, ctx.objects)263 .at_entry(rela_path.as_bstr(), is_dir, ctx.objects)
gix-dir/tests/fixtures/many.sh+80−0
@@ -351,3 +351,83 @@ git clone submodule multiple-submodules · 350 unmodified lines
351 git submodule add ../submodule a/b351 git submodule add ../submodule a/b
352 git commit -m "add modules"352 git commit -m "add modules"
353)353)
354
355git clone submodule one-ignored-submodule
356(cd one-ignored-submodule
357 git submodule add ../submodule submodule
358 echo '/submodule/' > .gitignore
359 echo '*' > submodule/.gitignore
360 git commit -m "add seemingly ignored submodule"
361)
362
363git init slash-in-root-and-negated
364(cd slash-in-root-and-negated
365 cat <<'EOF' >.gitignore
366/
367!file
368!*.md
369!.github
370!.github/**
371EOF
372 touch file readme.md
373 mkdir .github
374 touch .github/workflow.yml
375 git add .github readme.md .gitignore
376 git commit -m "init"
377)
378
379git init star-in-root-and-negated
380(cd star-in-root-and-negated
381 cat <<'EOF' >.gitignore
382*
383!file
384!.gitignore
385!*.md
386!.github
387!.github/**
388EOF
389 touch file readme.md
390 mkdir .github
391 touch .github/workflow.yml
392 git add .github readme.md .gitignore
393 git commit -m "init"
394)
395
396git init slash-in-subdir-and-negated
397(cd slash-in-subdir-and-negated
398 mkdir sub
399 (cd sub
400 cat <<'EOF' >.gitignore
401/
402!file
403!*.md
404!.github
405!.github/**
406EOF
407 touch file readme.md
408 mkdir .github
409 touch .github/workflow.yml
410 git add .github readme.md .gitignore
411 git commit -m "init"
412 )
413)
414
415git init star-in-subdir-and-negated
416(cd star-in-subdir-and-negated
417 mkdir sub
418 (cd sub
419 cat <<'EOF' >.gitignore
420*
421!file
422!.gitignore
423!*.md
424!.github
425!.github/**
426EOF
427 touch file readme.md
428 mkdir .github
429 touch .github/workflow.yml
430 git add .github readme.md .gitignore
431 git commit -m "init"
432 )
433)
gix-dir/tests/walk/mod.rs+158−0
@@ -4276,3 +4276,161 @@ fn type_mismatch_ignore_case_clash_file_is_dir() { · 4275 unmodified lines
4276 If there was no special handling for this, it would have found the file (`d` in the index, icase), which would have been wrong."4276 If there was no special handling for this, it would have found the file (`d` in the index, icase), which would have been wrong."
4277 );4277 );
4278}4278}
4279
4280#[test]
4281fn top_level_slash_with_negations() -> crate::Result {
4282 for repo_name in ["slash-in-root-and-negated", "star-in-root-and-negated"] {
4283 let root = fixture(repo_name);
4284 let ((out, _root), entries) = collect(&root, None, |keep, ctx| walk(&root, ctx, options_emit_all(), keep));
4285 assert_eq!(
4286 out,
4287 walk::Outcome {
4288 read_dir_calls: 2,
4289 returned_entries: entries.len(),
4290 seen_entries: 5,
4291 }
4292 );
4293 assert_eq!(
4294 entries,
4295 &[
4296 entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
4297 entry(".github/workflow.yml", Tracked, File),
4298 entry(".gitignore", Tracked, File),
4299 entry("file", Untracked, File),
4300 entry("readme.md", Tracked, File),
4301 ],
4302 "the top-level is never considered ignored"
4303 );
4304
4305 let ((out, _root), entries) = collect(&root, None, |keep, ctx| {
4306 walk(
4307 &root,
4308 ctx,
4309 walk::Options {
4310 for_deletion: Some(ForDeletionMode::FindRepositoriesInIgnoredDirectories),
4311 emit_tracked: false,
4312 ..options_emit_all()
4313 },
4314 keep,
4315 )
4316 });
4317 assert_eq!(
4318 out,
4319 walk::Outcome {
4320 read_dir_calls: 2,
4321 returned_entries: entries.len(),
4322 seen_entries: 5,
4323 }
4324 );
4325 assert_eq!(
4326 entries,
4327 &[
4328 entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
4329 entry("file", Untracked, File)
4330 ],
4331 "And the negated file is correctly detected as untracked"
4332 );
4333 }
4334 Ok(())
4335}
4336
4337#[test]
4338fn subdir_slash_with_negations() -> crate::Result {
4339 for repo_name in ["slash-in-subdir-and-negated", "star-in-subdir-and-negated"] {
4340 let root = fixture(repo_name);
4341 let ((out, _root), entries) = collect(&root, None, |keep, ctx| walk(&root, ctx, options_emit_all(), keep));
4342 assert_eq!(
4343 out,
4344 walk::Outcome {
4345 read_dir_calls: 3,
4346 returned_entries: entries.len(),
4347 seen_entries: 5,
4348 }
4349 );
4350 assert_eq!(
4351 entries,
4352 &[
4353 entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
4354 entry("sub/.github/workflow.yml", Tracked, File),
4355 entry("sub/.gitignore", Tracked, File),
4356 entry("sub/file", Untracked, File),
4357 entry("sub/readme.md", Tracked, File),
4358 ],
4359 "subdirectory matches work as expected, also with a `/` which has no bearing."
4360 );
4361
4362 let ((out, _root), entries) = collect(&root, None, |keep, ctx| {
4363 walk(
4364 &root,
4365 ctx,
4366 walk::Options {
4367 for_deletion: Some(ForDeletionMode::FindRepositoriesInIgnoredDirectories),
4368 emit_tracked: false,
4369 ..options_emit_all()
4370 },
4371 keep,
4372 )
4373 });
4374 assert_eq!(
4375 out,
4376 walk::Outcome {
4377 read_dir_calls: 3,
4378 returned_entries: entries.len(),
4379 seen_entries: 5,
4380 }
4381 );
4382 assert_eq!(
4383 entries,
4384 &[
4385 entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
4386 entry("sub/file", Untracked, File)
4387 ],
4388 "This is expected, and the `.git` top-level is pruned."
4389 );
4390 }
4391 Ok(())
4392}
4393
4394#[test]
4395fn one_ignored_submodule() -> crate::Result {
4396 let root = fixture("one-ignored-submodule");
4397 let ((out, _root), entries) = collect(&root, None, |keep, ctx| walk(&root, ctx, options_emit_all(), keep));
4398 assert_eq!(
4399 out,
4400 walk::Outcome {
4401 read_dir_calls: 1,
4402 returned_entries: entries.len(),
4403 seen_entries: 5,
4404 }
4405 );
4406 assert_eq!(
4407 entries,
4408 &[
4409 entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
4410 entry(".gitignore", Untracked, File),
4411 entry(".gitmodules", Tracked, File),
4412 entry("empty", Tracked, File),
4413 entry("submodule", Tracked, Repository),
4414 ],
4415 "when traversing the worktree root, this is correct, the submodule doesn't count as ignored"
4416 );
4417
4418 let troot = root.join("submodule");
4419 let ((out, _root), entries) = collect(&root, Some(&troot), |keep, ctx| {
4420 walk(&root, ctx, options_emit_all(), keep)
4421 });
4422 assert_eq!(
4423 out,
4424 walk::Outcome {
4425 read_dir_calls: 0,
4426 returned_entries: entries.len(),
4427 seen_entries: 1
4428 }
4429 );
4430 assert_eq!(
4431 entries,
4432 &[entryps("submodule", Tracked, Repository, Verbatim)],
4433 "The submodule is simply tracked, it doesn't count as ignored"
4434 );
4435 Ok(())
4436}
gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh+68−2
@@ -15,9 +15,8 @@ a/b/* · 14 unmodified lines
15z/x15z/x
16EOF16EOF
17 17
18mkdir repo;18git init -q repo;
19(cd repo19(cd repo
20 git init -q
21 git config core.excludesFile ../user.exclude20 git config core.excludesFile ../user.exclude
22 21
23 cat <<EOF >.git/info/exclude22 cat <<EOF >.git/info/exclude
@@ -93,3 +92,70 @@ E/f · 69 unmodified lines
93E/F92E/F
94EOF93EOF
95)94)
95
96git init slash-and-excludes
97(cd slash-and-excludes
98 cat <<EOF >.gitignore
99# a lone slash does nothing
100/
101# a file that was never ignored to begin
102!file
103EOF
104
105 git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
106file
107a-file-not-mentioned-in-gitignore
108EOF
109)
110
111git init slash-and-excludes-in-subdir
112(cd slash-and-excludes-in-subdir
113 mkdir sub
114 (cd sub
115 cat <<EOF >.gitignore
116# a lone slash does nothing
117/
118# a file that was never ignored to begin
119!file
120EOF
121 )
122 git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
123sub/file
124sub/a-file-not-mentioned-in-gitignore
125a-file-not-mentioned-in-gitignore
126EOF
127)
128
129git init star-and-excludes
130(cd star-and-excludes
131 cat <<EOF >.gitignore
132# everything is excluded by default
133*
134# And negations are used as an allow-list
135!file
136EOF
137
138 git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
139file
140a-file-not-mentioned-in-gitignore
141EOF
142)
143
144git init star-and-excludes-in-subdir
145(cd star-and-excludes-in-subdir
146 mkdir sub
147 (cd sub
148 cat <<EOF >.gitignore
149# everything is excluded by default
150*
151# And negations are used as an allow-list
152!file
153EOF
154 )
155
156 git check-ignore -vn --stdin 2>&1 <<EOF >git-check-ignore.baseline || :
157sub/file
158sub/a-file-not-mentioned-in-gitignore
159a-file-not-mentioned-in-gitignore
160EOF
161)
gix-ignore/tests/search/mod.rs+75−62
@@ -1,5 +1,3 @@
1use std::io::Read;
2
3use bstr::{BStr, ByteSlice};1use bstr::{BStr, ByteSlice};
4use gix_glob::pattern::Case;2use gix_glob::pattern::Case;
5use gix_ignore::search::Match;3use gix_ignore::search::Match;
@@ -31,69 +29,84 @@ impl<'a> Iterator for Expectations<'a> { · 25 unmodified lines
31 29
32#[test]30#[test]
33fn baseline_from_git_dir() -> crate::Result {31fn baseline_from_git_dir() -> crate::Result {
34 let case = if gix_fs::Capabilities::probe("../.git".as_ref()).ignore_case {32 for repo_name in [
35 Case::Fold33 "repo",
36 } else {34 "slash-and-excludes",
37 Case::Sensitive35 "star-and-excludes-in-subdir",
38 };36 "slash-and-excludes-in-subdir",
39 let dir = gix_testtools::scripted_fixture_read_only("make_global_and_external_and_dir_ignores.sh")?;37 ] {
40 let repo_dir = dir.join("repo");38 let case = if gix_fs::Capabilities::probe("../.git".as_ref()).ignore_case {
41 let git_dir = repo_dir.join(".git");39 Case::Fold
42 let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline"))?;40 } else {
43 let mut buf = Vec::new();41 Case::Sensitive
44 let mut group = gix_ignore::Search::from_git_dir(&git_dir, Some(dir.join("user.exclude")), &mut buf)?;42 };
43 let dir = gix_testtools::scripted_fixture_read_only("make_global_and_external_and_dir_ignores.sh")?;
44 let repo_dir = dir.join(repo_name);
45 let git_dir = repo_dir.join(".git");
46 let baseline = std::fs::read(git_dir.parent().unwrap().join("git-check-ignore.baseline"))?;
47 let mut buf = Vec::new();
48 let user_exclude = dir.join("user.exclude");
49 let mut group =
50 gix_ignore::Search::from_git_dir(&git_dir, user_exclude.is_file().then_some(user_exclude), &mut buf)?;
45 51
46 assert!(52 assert!(
47 !gix_glob::search::add_patterns_file(&mut group.patterns, "not-a-file".into(), false, None, &mut buf)?,53 !gix_glob::search::add_patterns_file(&mut group.patterns, "not-a-file".into(), false, None, &mut buf)?,
48 "missing files are no problem and cause a negative response"54 "missing files are no problem and cause a negative response"
49 );55 );
50 assert!(56 let mut ignore_file = repo_dir.join(".gitignore");
51 gix_glob::search::add_patterns_file(57 if !ignore_file.is_file() {
52 &mut group.patterns,58 ignore_file.pop();
53 repo_dir.join(".gitignore"),59 ignore_file.push("sub/.gitignore");
54 true,60 }
55 repo_dir.as_path().into(),61 assert!(
56 &mut buf62 gix_glob::search::add_patterns_file(
57 )?,63 &mut group.patterns,
58 "existing files return true"64 ignore_file,
59 );65 true,
66 repo_dir.as_path().into(),
67 &mut buf
68 )?,
69 "existing files return true"
70 );
60 71
61 buf.clear();72 let ignore_file = repo_dir.join("dir-with-ignore").join(".gitignore");
62 let ignore_file = repo_dir.join("dir-with-ignore").join(".gitignore");73 if ignore_file.is_file() {
63 std::fs::File::open(&ignore_file)?.read_to_end(&mut buf)?;74 let buf = std::fs::read(&ignore_file)?;
64 group.add_patterns_buffer(&buf, ignore_file, repo_dir.as_path().into());75 group.add_patterns_buffer(&buf, ignore_file, repo_dir.as_path().into());
76 }
65 77
66 for (path, source_and_line) in (Expectations {78 for (path, source_and_line) in (Expectations {
67 lines: baseline.lines(),79 lines: baseline.lines(),
68 }) {80 }) {
69 let actual = group.pattern_matching_relative_path(81 let actual = group.pattern_matching_relative_path(
70 path,82 path,
71 repo_dir83 repo_dir
72 .join(path.to_str_lossy().as_ref())84 .join(path.to_str_lossy().as_ref())
73 .metadata()85 .metadata()
74 .ok()86 .ok()
75 .map(|m| m.is_dir()),87 .map(|m| m.is_dir()),
76 case,88 case,
77 );89 );
78 match (actual, source_and_line) {90 match (actual, source_and_line) {
79 (91 (
80 Some(Match {92 Some(Match {
81 sequence_number,93 sequence_number,
82 pattern: _,94 pattern: _,
83 source,95 source,
84 kind: gix_ignore::Kind::Expendable,96 kind: gix_ignore::Kind::Expendable,
85 }),97 }),
86 Some((expected_source, line, _expected_pattern)),98 Some((expected_source, line, _expected_pattern)),
87 ) => {99 ) => {
88 assert_eq!(sequence_number, line, "our counting should match the one used in git");100 assert_eq!(sequence_number, line, "our counting should match the one used in git");
89 assert_eq!(101 assert_eq!(
90 source.map(|p| p.canonicalize().unwrap()),102 source.map(|p| p.canonicalize().unwrap()),
91 Some(repo_dir.join(expected_source.to_str_lossy().as_ref()).canonicalize()?)103 Some(repo_dir.join(expected_source.to_str_lossy().as_ref()).canonicalize()?)
92 );104 );
93 }105 }
94 (None, None) => {}106 (None, None) => {}
95 (actual, expected) => {107 (actual, expected) => {
96 panic!("{case:?}: actual {actual:?} should match {expected:?} with path '{path}'")108 panic!("{repo_name}: {case:?}: actual {actual:?} should match {expected:?} with path '{path}'")
109 }
97 }110 }
98 }111 }
99 }112 }
gix-transport/src/client/git/mod.rs+1−1
@@ -64,7 +64,7 @@ mod message { · 63 unmodified lines
64 out.extend_from_slice(host.as_bytes());64 out.extend_from_slice(host.as_bytes());
65 if let Some(port) = port {65 if let Some(port) = port {
66 out.push_byte(b':');66 out.push_byte(b':');
67 out.push_str(&format!("{port}"));67 out.push_str(format!("{port}"));
68 }68 }
69 out.push(0);69 out.push(0);
70 }70 }
gix/src/repository/config/transport.rs+2−5
@@ -193,10 +193,7 @@ impl crate::Repository { · 192 unmodified lines
193 remote_name193 remote_name
194 .and_then(|name| {194 .and_then(|name| {
195 config195 config
196 .string_filter(196 .string_filter(format!("remote.{}.{}", name, Remote::PROXY.name), &mut trusted_only)
197 &format!("remote.{}.{}", name, Remote::PROXY.name),
198 &mut trusted_only,
199 )
200 .map(|v| (v, Cow::Owned(format!("remote.{name}.proxy").into()), &Remote::PROXY))197 .map(|v| (v, Cow::Owned(format!("remote.{name}.proxy").into()), &Remote::PROXY))
201 })198 })
202 .or_else(|| {199 .or_else(|| {
@@ -254,7 +251,7 @@ impl crate::Repository { · 51 unmodified lines
254 remote_name251 remote_name
255 .and_then(|name| {252 .and_then(|name| {
256 config253 config
257 .string_filter(&format!("remote.{name}.proxyAuthMethod"), &mut trusted_only)254 .string_filter(format!("remote.{name}.proxyAuthMethod"), &mut trusted_only)
258 .map(|v| {255 .map(|v| {
259 (256 (
260 v,257 v,
gix/src/repository/remote.rs+4−4
@@ -137,7 +137,7 @@ impl crate::Repository { · 136 unmodified lines
137 let mut config_url = |key: &'static config::tree::keys::Url, kind: &'static str| {137 let mut config_url = |key: &'static config::tree::keys::Url, kind: &'static str| {
138 self.config138 self.config
139 .resolved139 .resolved
140 .string_filter(&format!("remote.{}.{}", name_or_url, key.name), &mut filter)140 .string_filter(format!("remote.{}.{}", name_or_url, key.name), &mut filter)
141 .map(|url| {141 .map(|url| {
142 key.try_into_url(url).map_err(|err| find::Error::Url {142 key.try_into_url(url).map_err(|err| find::Error::Url {
143 kind,143 kind,
@@ -151,7 +151,7 @@ impl crate::Repository { · 7 unmodified lines
151 let config = &self.config.resolved;151 let config = &self.config.resolved;
152 152
153 let fetch_specs = config153 let fetch_specs = config
154 .strings_filter(&format!("remote.{}.{}", name_or_url, "fetch"), &mut filter)154 .strings_filter(format!("remote.{}.{}", name_or_url, "fetch"), &mut filter)
155 .map(|specs| {155 .map(|specs| {
156 config_spec(156 config_spec(
157 specs,157 specs,
@@ -161,7 +161,7 @@ impl crate::Repository { · 3 unmodified lines
161 )161 )
162 });162 });
163 let push_specs = config163 let push_specs = config
164 .strings_filter(&format!("remote.{}.{}", name_or_url, "push"), &mut filter)164 .strings_filter(format!("remote.{}.{}", name_or_url, "push"), &mut filter)
165 .map(|specs| {165 .map(|specs| {
166 config_spec(166 config_spec(
167 specs,167 specs,
@@ -171,7 +171,7 @@ impl crate::Repository { · 3 unmodified lines
171 )171 )
172 });172 });
173 let fetch_tags = config173 let fetch_tags = config
174 .string_filter(&format!("remote.{}.{}", name_or_url, "tagOpt"), &mut filter)174 .string_filter(format!("remote.{}.{}", name_or_url, "tagOpt"), &mut filter)
175 .map(|value| {175 .map(|value| {
176 config::tree::Remote::TAG_OPT176 config::tree::Remote::TAG_OPT
177 .try_into_tag_opt(value)177 .try_into_tag_opt(value)
gix/tests/remote/mod.rs+10−0
@@ -70,6 +70,16 @@ mod ref_map; · 69 unmodified lines
70mod save;70mod save;
71mod name {71mod name {
72 72
73 #[test]
74 fn origin_is_valid() {
75 assert!(gix::remote::name::validated("origin").is_ok());
76 }
77
78 #[test]
79 fn multiple_slashes_are_valid() {
80 assert!(gix::remote::name::validated("origin/another").is_ok());
81 }
82
73 #[test]83 #[test]
74 fn empty_is_invalid() {84 fn empty_is_invalid() {
75 assert!(gix::remote::name::validated("").is_err());85 assert!(gix::remote::name::validated("").is_err());