From df7a9261e146939846ce49c553172e23fbce471e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 20 Jan 2025 15:44:20 +0100 Subject: [PATCH 1/3] add `imara-diff::UnifiedDiffBuilder` as basis. It should be the basis for providing a more general way to obtain such diffs. --- gix-diff/src/blob/mod.rs | 4 + gix-diff/src/blob/unified_diff.rs | 174 +++++++++++++++++++++++ gix-diff/tests/diff/blob/mod.rs | 1 + gix-diff/tests/diff/blob/unified_diff.rs | 0 4 files changed, 179 insertions(+) create mode 100644 gix-diff/src/blob/unified_diff.rs create mode 100644 gix-diff/tests/diff/blob/unified_diff.rs diff --git a/gix-diff/src/blob/mod.rs b/gix-diff/src/blob/mod.rs index 3b84022bdea..c6a0f3148d6 100644 --- a/gix-diff/src/blob/mod.rs +++ b/gix-diff/src/blob/mod.rs @@ -11,6 +11,10 @@ pub mod pipeline; /// pub mod platform; +/// +pub mod unified_diff; +pub use unified_diff::_impl::UnifiedDiff; + /// Information about the diff performed to detect similarity. #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] pub struct DiffLineStats { diff --git a/gix-diff/src/blob/unified_diff.rs b/gix-diff/src/blob/unified_diff.rs new file mode 100644 index 00000000000..78d1f3aae79 --- /dev/null +++ b/gix-diff/src/blob/unified_diff.rs @@ -0,0 +1,174 @@ +//! Originally based on https://github.com/pascalkuthe/imara-diff/pull/14. +//! + +/// Defines the size of the context printed before and after each change. +/// +/// Similar to the `-U` option in git diff or gnu-diff. If the context overlaps +/// with previous or next change, the context gets reduced accordingly. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] +pub struct ContextSize { + /// Defines the size of the context printed before and after each change. + symmetrical: u32, +} + +impl Default for ContextSize { + fn default() -> Self { + ContextSize::symmetrical(3) + } +} + +/// Instantiation +impl ContextSize { + /// Create a symmetrical context with `n` lines before and after a changed hunk. + pub fn symmetrical(n: u32) -> Self { + ContextSize { symmetrical: n } + } +} + +pub(super) mod _impl { + use imara_diff::{intern, Sink}; + use std::fmt::{Display, Write}; + use std::hash::Hash; + use std::ops::Range; + + use super::ContextSize; + use intern::{InternedInput, Interner, Token}; + + /// A [`Sink`] that creates a textual diff + /// in the format typically output by git or gnu-diff if the `-u` option is used + pub struct UnifiedDiff<'a, W, T> + where + W: Write, + T: Hash + Eq + Display, + { + before: &'a [Token], + after: &'a [Token], + interner: &'a Interner, + + pos: u32, + before_hunk_start: u32, + after_hunk_start: u32, + before_hunk_len: u32, + after_hunk_len: u32, + /// Symmetrical context before and after the changed hunk. + ctx_size: u32, + + buffer: String, + dst: W, + } + + impl<'a, T> UnifiedDiff<'a, String, T> + where + T: Hash + Eq + Display, + { + /// Create a new `UnifiedDiffBuilder` for the given `input`, + /// displaying `context_size` lines of context around each change, + /// that will return a [`String`]. + pub fn new(input: &'a InternedInput, context_size: ContextSize) -> Self { + Self { + before_hunk_start: 0, + after_hunk_start: 0, + before_hunk_len: 0, + after_hunk_len: 0, + buffer: String::with_capacity(8), + dst: String::new(), + interner: &input.interner, + before: &input.before, + after: &input.after, + pos: 0, + ctx_size: context_size.symmetrical, + } + } + } + + impl<'a, W, T> UnifiedDiff<'a, W, T> + where + W: Write, + T: Hash + Eq + Display, + { + /// Create a new `UnifiedDiffBuilder` for the given `input`, + /// displaying `context_size` lines of context around each change, + /// that will writes it output to the provided implementation of [`Write`]. + pub fn with_writer(input: &'a InternedInput, writer: W, context_size: Option) -> Self { + Self { + before_hunk_start: 0, + after_hunk_start: 0, + before_hunk_len: 0, + after_hunk_len: 0, + buffer: String::with_capacity(8), + dst: writer, + interner: &input.interner, + before: &input.before, + after: &input.after, + pos: 0, + ctx_size: context_size.unwrap_or(3), + } + } + + fn print_tokens(&mut self, tokens: &[Token], prefix: char) { + for &token in tokens { + writeln!(&mut self.buffer, "{prefix}{}", self.interner[token]).unwrap(); + } + } + + fn flush(&mut self) { + if self.before_hunk_len == 0 && self.after_hunk_len == 0 { + return; + } + + let end = (self.pos + self.ctx_size).min(self.before.len() as u32); + self.update_pos(end, end); + + writeln!( + &mut self.dst, + "@@ -{},{} +{},{} @@", + self.before_hunk_start + 1, + self.before_hunk_len, + self.after_hunk_start + 1, + self.after_hunk_len, + ) + .unwrap(); + write!(&mut self.dst, "{}", &self.buffer).unwrap(); + self.buffer.clear(); + self.before_hunk_len = 0; + self.after_hunk_len = 0 + } + + fn update_pos(&mut self, print_to: u32, move_to: u32) { + self.print_tokens(&self.before[self.pos as usize..print_to as usize], ' '); + let len = print_to - self.pos; + self.pos = move_to; + self.before_hunk_len += len; + self.after_hunk_len += len; + } + } + + impl Sink for UnifiedDiff<'_, W, T> + where + W: Write, + T: Hash + Eq + Display, + { + type Out = W; + + fn process_change(&mut self, before: Range, after: Range) { + if ((self.pos == 0) && (before.start - self.pos > self.ctx_size)) + || (before.start - self.pos > 2 * self.ctx_size) + { + self.flush(); + self.pos = before.start - self.ctx_size; + self.before_hunk_start = self.pos; + self.after_hunk_start = after.start - self.ctx_size; + } + self.update_pos(before.start, before.end); + self.before_hunk_len += before.end - before.start; + self.after_hunk_len += after.end - after.start; + self.print_tokens(&self.before[before.start as usize..before.end as usize], '-'); + self.print_tokens(&self.after[after.start as usize..after.end as usize], '+'); + } + + fn finish(mut self) -> Self::Out { + self.flush(); + self.dst + } + } +} diff --git a/gix-diff/tests/diff/blob/mod.rs b/gix-diff/tests/diff/blob/mod.rs index bb412d412cf..1959c4e6fdb 100644 --- a/gix-diff/tests/diff/blob/mod.rs +++ b/gix-diff/tests/diff/blob/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod pipeline; mod platform; +mod unified_diff; diff --git a/gix-diff/tests/diff/blob/unified_diff.rs b/gix-diff/tests/diff/blob/unified_diff.rs new file mode 100644 index 00000000000..e69de29bb2d From 1ccbeef894e92d770ac0207d92b8e0f6686c3360 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 20 Jan 2025 15:54:51 +0100 Subject: [PATCH 2/3] feat: add `blob::UnifiedDiff` as Sink to build unified diffs. --- gix-diff/src/blob/mod.rs | 1 - gix-diff/src/blob/unified_diff.rs | 203 ++++++++++++++++------- gix-diff/tests/diff/blob/unified_diff.rs | 178 ++++++++++++++++++++ 3 files changed, 320 insertions(+), 62 deletions(-) diff --git a/gix-diff/src/blob/mod.rs b/gix-diff/src/blob/mod.rs index c6a0f3148d6..a5ce1fa7b47 100644 --- a/gix-diff/src/blob/mod.rs +++ b/gix-diff/src/blob/mod.rs @@ -11,7 +11,6 @@ pub mod pipeline; /// pub mod platform; -/// pub mod unified_diff; pub use unified_diff::_impl::UnifiedDiff; diff --git a/gix-diff/src/blob/unified_diff.rs b/gix-diff/src/blob/unified_diff.rs index 78d1f3aae79..bea6f98f560 100644 --- a/gix-diff/src/blob/unified_diff.rs +++ b/gix-diff/src/blob/unified_diff.rs @@ -1,5 +1,6 @@ -//! Originally based on https://github.com/pascalkuthe/imara-diff/pull/14. +//! Facilities to produce the unified diff format. //! +//! Originally based on . /// Defines the size of the context printed before and after each change. /// @@ -25,21 +26,50 @@ impl ContextSize { } } +/// A utility trait for use in [`UnifiedDiff`](super::UnifiedDiff). +pub trait ConsumeHunk { + /// The item this instance produces after consuming all hunks. + type Out; + + /// Consume a single `hunk` in unified diff format, that would be prefixed with `header`. + /// Note that all newlines are added. + /// + /// Note that the [`UnifiedDiff`](super::UnifiedDiff) sink will wrap its output in an [`std::io::Result`]. + /// After this method returned its first error, it will not be called anymore. + /// + /// The following is hunk-related information and the same that is used in the `header`. + /// * `before_hunk_start` is the 1-based first line of this hunk in the old file. + /// * `before_hunk_len` the amount of lines of this hunk in the old file. + /// * `after_hunk_start` is the 1-based first line of this hunk in the new file. + /// * `after_hunk_len` the amount of lines of this hunk in the new file. + fn consume_hunk( + &mut self, + before_hunk_start: u32, + before_hunk_len: u32, + after_hunk_start: u32, + after_hunk_len: u32, + header: &str, + hunk: &[u8], + ) -> std::io::Result<()>; + /// Called after the last hunk is consumed to produce an output. + fn finish(self) -> Self::Out; +} + pub(super) mod _impl { + use super::{ConsumeHunk, ContextSize}; + use bstr::{ByteSlice, ByteVec}; use imara_diff::{intern, Sink}; - use std::fmt::{Display, Write}; + use intern::{InternedInput, Interner, Token}; use std::hash::Hash; + use std::io::ErrorKind; use std::ops::Range; - use super::ContextSize; - use intern::{InternedInput, Interner, Token}; - - /// A [`Sink`] that creates a textual diff - /// in the format typically output by git or gnu-diff if the `-u` option is used - pub struct UnifiedDiff<'a, W, T> + /// A [`Sink`] that creates a textual diff in the format typically output by git or `gnu-diff` if the `-u` option is used, + /// and passes it in full to a consumer. + pub struct UnifiedDiff<'a, T, D> where - W: Write, - T: Hash + Eq + Display, + T: Hash + Eq + AsRef<[u8]>, + D: ConsumeHunk, { before: &'a [Token], after: &'a [Token], @@ -53,85 +83,92 @@ pub(super) mod _impl { /// Symmetrical context before and after the changed hunk. ctx_size: u32, - buffer: String, - dst: W, + buffer: Vec, + header_buf: String, + delegate: D, + newline: &'a str, + + err: Option, } - impl<'a, T> UnifiedDiff<'a, String, T> + impl<'a, T, D> UnifiedDiff<'a, T, D> where - T: Hash + Eq + Display, + T: Hash + Eq + AsRef<[u8]>, + D: ConsumeHunk, { - /// Create a new `UnifiedDiffBuilder` for the given `input`, - /// displaying `context_size` lines of context around each change, - /// that will return a [`String`]. - pub fn new(input: &'a InternedInput, context_size: ContextSize) -> Self { + /// Create a new instance to create unified diff using the lines in `input`, + /// which also must be used when running the diff algorithm. + /// `context_size` is the amount of lines around each hunk which will be passed + ///to `consume_hunk`. + /// + /// `consume_hunk` is called for each hunk in unified-diff format, as created from each line separated by `newline_separator`, + pub fn new( + input: &'a InternedInput, + consume_hunk: D, + newline_separator: &'a str, + context_size: ContextSize, + ) -> Self { Self { before_hunk_start: 0, after_hunk_start: 0, before_hunk_len: 0, after_hunk_len: 0, - buffer: String::with_capacity(8), - dst: String::new(), + buffer: Vec::with_capacity(8), + header_buf: String::new(), + delegate: consume_hunk, interner: &input.interner, before: &input.before, after: &input.after, pos: 0, ctx_size: context_size.symmetrical, - } - } - } + newline: newline_separator, - impl<'a, W, T> UnifiedDiff<'a, W, T> - where - W: Write, - T: Hash + Eq + Display, - { - /// Create a new `UnifiedDiffBuilder` for the given `input`, - /// displaying `context_size` lines of context around each change, - /// that will writes it output to the provided implementation of [`Write`]. - pub fn with_writer(input: &'a InternedInput, writer: W, context_size: Option) -> Self { - Self { - before_hunk_start: 0, - after_hunk_start: 0, - before_hunk_len: 0, - after_hunk_len: 0, - buffer: String::with_capacity(8), - dst: writer, - interner: &input.interner, - before: &input.before, - after: &input.after, - pos: 0, - ctx_size: context_size.unwrap_or(3), + err: None, } } fn print_tokens(&mut self, tokens: &[Token], prefix: char) { for &token in tokens { - writeln!(&mut self.buffer, "{prefix}{}", self.interner[token]).unwrap(); + self.buffer.push_char(prefix); + self.buffer.push_str(&self.interner[token]); + self.buffer.push_str(self.newline.as_bytes()); } } - fn flush(&mut self) { + fn flush(&mut self) -> std::io::Result<()> { if self.before_hunk_len == 0 && self.after_hunk_len == 0 { - return; + return Ok(()); } let end = (self.pos + self.ctx_size).min(self.before.len() as u32); self.update_pos(end, end); - writeln!( - &mut self.dst, - "@@ -{},{} +{},{} @@", + self.header_buf.clear(); + + std::fmt::Write::write_fmt( + &mut self.header_buf, + format_args!( + "@@ -{},{} +{},{} @@{nl}", + self.before_hunk_start + 1, + self.before_hunk_len, + self.after_hunk_start + 1, + self.after_hunk_len, + nl = self.newline + ), + ) + .map_err(|err| std::io::Error::new(ErrorKind::Other, err))?; + self.delegate.consume_hunk( self.before_hunk_start + 1, self.before_hunk_len, self.after_hunk_start + 1, self.after_hunk_len, - ) - .unwrap(); - write!(&mut self.dst, "{}", &self.buffer).unwrap(); + &self.header_buf, + &self.buffer, + )?; self.buffer.clear(); self.before_hunk_len = 0; - self.after_hunk_len = 0 + self.after_hunk_len = 0; + Ok(()) } fn update_pos(&mut self, print_to: u32, move_to: u32) { @@ -143,18 +180,24 @@ pub(super) mod _impl { } } - impl Sink for UnifiedDiff<'_, W, T> + impl Sink for UnifiedDiff<'_, T, D> where - W: Write, - T: Hash + Eq + Display, + T: Hash + Eq + AsRef<[u8]>, + D: ConsumeHunk, { - type Out = W; + type Out = std::io::Result; fn process_change(&mut self, before: Range, after: Range) { + if self.err.is_some() { + return; + } if ((self.pos == 0) && (before.start - self.pos > self.ctx_size)) || (before.start - self.pos > 2 * self.ctx_size) { - self.flush(); + if let Err(err) = self.flush() { + self.err = Some(err); + return; + } self.pos = before.start - self.ctx_size; self.before_hunk_start = self.pos; self.after_hunk_start = after.start - self.ctx_size; @@ -167,8 +210,46 @@ pub(super) mod _impl { } fn finish(mut self) -> Self::Out { - self.flush(); - self.dst + if let Err(err) = self.flush() { + self.err = Some(err); + } + if let Some(err) = self.err { + return Err(err); + } + Ok(self.delegate.finish()) + } + } + + /// An implementation that fails if the input isn't UTF-8. + impl ConsumeHunk for String { + type Out = Self; + + fn consume_hunk(&mut self, _: u32, _: u32, _: u32, _: u32, header: &str, hunk: &[u8]) -> std::io::Result<()> { + self.push_str(header); + self.push_str( + hunk.to_str() + .map_err(|err| std::io::Error::new(ErrorKind::Other, err))?, + ); + Ok(()) + } + + fn finish(self) -> Self::Out { + self + } + } + + /// An implementation that writes hunks into a byte buffer. + impl ConsumeHunk for Vec { + type Out = Self; + + fn consume_hunk(&mut self, _: u32, _: u32, _: u32, _: u32, header: &str, hunk: &[u8]) -> std::io::Result<()> { + self.push_str(header); + self.push_str(hunk); + Ok(()) + } + + fn finish(self) -> Self::Out { + self } } } diff --git a/gix-diff/tests/diff/blob/unified_diff.rs b/gix-diff/tests/diff/blob/unified_diff.rs index e69de29bb2d..aa43f5cd2cc 100644 --- a/gix-diff/tests/diff/blob/unified_diff.rs +++ b/gix-diff/tests/diff/blob/unified_diff.rs @@ -0,0 +1,178 @@ +use gix_diff::blob::unified_diff::{ConsumeHunk, ContextSize}; +use gix_diff::blob::{Algorithm, UnifiedDiff}; + +#[test] +fn removed_modified_added() -> crate::Result { + let a = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"; + let b = "2\n3\n4\n5\nsix\n7\n8\n9\n10\neleven\ntwelve"; + + let interner = gix_diff::blob::intern::InternedInput::new(a, b); + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(3)), + )?; + + // merged by context. + insta::assert_snapshot!(actual, @r" + @@ -1,10 +1,11 @@ + -1 + 2 + 3 + 4 + 5 + -6 + +six + 7 + 8 + 9 + 10 + +eleven + +twelve + "); + + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(1)), + )?; + // Small context lines keeps hunks separate. + insta::assert_snapshot!(actual, @r" + @@ -1,2 +1,1 @@ + -1 + 2 + @@ -5,3 +4,3 @@ + 5 + -6 + +six + 7 + @@ -10,1 +9,3 @@ + 10 + +eleven + +twelve + "); + + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(0)), + )?; + // No context is also fine + insta::assert_snapshot!(actual, @r" + @@ -1,1 +1,0 @@ + -1 + @@ -6,1 +5,1 @@ + -6 + +six + @@ -11,0 +10,2 @@ + +eleven + +twelve + "); + + #[derive(Default)] + struct Recorder { + #[allow(clippy::type_complexity)] + hunks: Vec<((u32, u32), (u32, u32), String)>, + } + + impl ConsumeHunk for Recorder { + type Out = Vec<((u32, u32), (u32, u32), String)>; + + fn consume_hunk( + &mut self, + before_hunk_start: u32, + before_hunk_len: u32, + after_hunk_start: u32, + after_hunk_len: u32, + header: &str, + _hunk: &[u8], + ) -> std::io::Result<()> { + self.hunks.push(( + (before_hunk_start, before_hunk_len), + (after_hunk_start, after_hunk_len), + header.to_string(), + )); + Ok(()) + } + + fn finish(self) -> Self::Out { + self.hunks + } + } + + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, Recorder::default(), "\n", ContextSize::symmetrical(1)), + )?; + assert_eq!( + actual, + &[ + ((1, 2), (1, 1), "@@ -1,2 +1,1 @@\n".to_string()), + ((5, 3), (4, 3), "@@ -5,3 +4,3 @@\n".into()), + ((10, 1), (9, 3), "@@ -10,1 +9,3 @@\n".into()) + ] + ); + + Ok(()) +} + +#[test] +fn all_added_or_removed() -> crate::Result { + let content = "1\n2\n3\n4\n5"; + + let samples = [0, 1, 3, 100]; + for context_lines in samples { + let interner = gix_diff::blob::intern::InternedInput::new("", content); + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(context_lines)), + )?; + assert_eq!( + actual, + r#"@@ -1,0 +1,5 @@ ++1 ++2 ++3 ++4 ++5 +"#, + "context lines don't matter here" + ); + } + + for context_lines in samples { + let interner = gix_diff::blob::intern::InternedInput::new(content, ""); + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(context_lines)), + )?; + assert_eq!( + actual, + r"@@ -1,5 +1,0 @@ +-1 +-2 +-3 +-4 +-5 +", + "context lines don't matter here" + ); + } + Ok(()) +} + +#[test] +fn empty() -> crate::Result { + let interner = gix_diff::blob::intern::InternedInput::new(&b""[..], &b""[..]); + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new(&interner, String::new(), "\n", ContextSize::symmetrical(3)), + )?; + + insta::assert_snapshot!(actual, @r""); + Ok(()) +} From f3257f3c0fd1b91aecdb8f1d947e9648080d162f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 20 Jan 2025 11:52:00 +0100 Subject: [PATCH 3/3] feat: add `Repository::big_file_threshold()` to easily learn what Git considers a big file. --- gix/src/config/cache/access.rs | 1 - gix/src/repository/config/mod.rs | 6 ++++++ .../generated-archives/make_config_repos.tar | Bin 1112064 -> 1154560 bytes gix/tests/fixtures/make_config_repos.sh | 5 +++++ gix/tests/gix/repository/config/mod.rs | 16 ++++++++++++++-- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/gix/src/config/cache/access.rs b/gix/src/config/cache/access.rs index 4022b6430da..df2ca193c10 100644 --- a/gix/src/config/cache/access.rs +++ b/gix/src/config/cache/access.rs @@ -162,7 +162,6 @@ impl Cache { .copied() } - #[cfg(feature = "blob-diff")] pub(crate) fn big_file_threshold(&self) -> Result { Ok(self .resolved diff --git a/gix/src/repository/config/mod.rs b/gix/src/repository/config/mod.rs index dbad6965f8e..c447b1201d0 100644 --- a/gix/src/repository/config/mod.rs +++ b/gix/src/repository/config/mod.rs @@ -42,6 +42,12 @@ impl crate::Repository { &self.options } + /// Return the big-file threshold above which Git will not perform a diff anymore or try to delta-diff packs, + /// as configured by `core.bigFileThreshold`, or the default value. + pub fn big_file_threshold(&self) -> Result { + self.config.big_file_threshold() + } + /// Obtain options for use when connecting via `ssh`. #[cfg(feature = "blocking-network-client")] pub fn ssh_connect_options( diff --git a/gix/tests/fixtures/generated-archives/make_config_repos.tar b/gix/tests/fixtures/generated-archives/make_config_repos.tar index 426d7c022f5dd2b11babd6b73627a76509fe5a6c..3d98d4afabf54f9882f0943413a78a3b35c2cf20 100644 GIT binary patch delta 1382 zcmai!O=uHA7=|-DJDauLBu(%KJV@G8E|tyh%r1h8rW#QO55%6zAo&mICbyy-y{{12Fha-= z(aBY310yq3^OMlbAq(0elG_5@juIJ@5b|@^+-t5VB@ocDoBSfQE<=nAj3c_}*#pmI zUkikH1^gQkIlIUD$R7!06_vABqNZY7y4h831L7-za9qHz`Q&YDPq_|!DG*KxcvIpf z_k}&skp{pA1$DA1R& zu|s+!t79vx8$2m|VtN)^7U}QeiBty%_#MjgGjyqch0-U|*l3?Nrof#RYk`fHboZfT zSl;p}xCXRy<%QX5*(uVg!n`}0gJ*{fVji;%8ymKFJqPRH(Q!EH9;vAluCpZ)at3|{ zl1aSfJhY|%zfYZqI})RiQJGQ1sKRKJQI*ksj1Dn6%&5j_jM4ZyP5ey!H6Fh)Q?7cW Po9H8Gq*Ln`Mp}OWMJvyr delta 198 zcmZqJQq^jpEv!~J7!k_LjxmoV+%6_ z3nSgqyvz#yX?)xg+o!%@&Sspbz&`B}v*L6)1y1MbDO&6%+h@FDe#^*YWVXHi4YN5D zk||sWQ?^UKXMWBE)Z6xnx$P56+b7nxPi$?UfLxBYPn>O^xY|B(w|(Mi`^4M!iEr5_ Y{y+Q(y>config ) + +git init big-file-threshold +(cd big-file-threshold + git config core.bigFileThreshold 42 +) diff --git a/gix/tests/gix/repository/config/mod.rs b/gix/tests/gix/repository/config/mod.rs index 227a7ed308e..35cbb3d1476 100644 --- a/gix/tests/gix/repository/config/mod.rs +++ b/gix/tests/gix/repository/config/mod.rs @@ -2,6 +2,20 @@ mod config_snapshot; mod identity; mod remote; +#[test] +fn big_file_threshold() -> crate::Result { + let repo = repo("with-hasconfig"); + assert_eq!( + repo.big_file_threshold()?, + 512 * 1024 * 1024, + "Git really handles huge files, and this is the default" + ); + + let repo = crate::repository::config::repo("big-file-threshold"); + assert_eq!(repo.big_file_threshold()?, 42, "It picks up configured values as well"); + Ok(()) +} + #[cfg(feature = "blocking-network-client")] mod ssh_options { use std::ffi::OsStr; @@ -38,12 +52,10 @@ mod ssh_options { #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] mod transport_options; -#[cfg(feature = "blocking-network-client")] pub fn repo(name: &str) -> gix::Repository { repo_opts(name, |opts| opts.strict_config(true)) } -#[cfg(feature = "blocking-network-client")] pub fn repo_opts(name: &str, modify: impl FnOnce(gix::open::Options) -> gix::open::Options) -> gix::Repository { let dir = gix_testtools::scripted_fixture_read_only("make_config_repos.sh").unwrap(); gix::open_opts(dir.join(name), modify(gix::open::Options::isolated())).unwrap()