Skip to content

Commit c1cf08c

Browse files
committed
feat!: Don't fail on big files during blob-merge, but turn them into binary merges.
Binary merges are mere choices of which side to pick, which works well for big files as well. Git doesn't define this well during its own merges, so there is some room here.
1 parent 78a5355 commit c1cf08c

File tree

2 files changed

+30
-28
lines changed

2 files changed

+30
-28
lines changed

gix-merge/src/blob/platform/merge.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ pub struct Options {
1818
#[derive(Debug, thiserror::Error)]
1919
#[allow(missing_docs)]
2020
pub enum Error {
21-
#[error("At least one resource was too large to be processed")]
22-
ResourceTooLarge,
2321
#[error(transparent)]
2422
PrepareExternalDriver(#[from] inner::prepare_external_driver::Error),
2523
#[error("Failed to launch external merge driver: {cmd}")]
@@ -299,25 +297,29 @@ pub(super) mod inner {
299297
/// Returns `None` if one of the buffers is too large, making a merge impossible.
300298
/// Note that if the *pick* wasn't [`Pick::Buffer`], then `out` will not have been cleared,
301299
/// and one has to take the data from the respective resource.
300+
///
301+
/// If there is no buffer loaded as the resource is too big, we will automatically perform a binary merge
302+
/// which effectively chooses our side by default.
302303
pub fn builtin_merge(
303304
&self,
304305
driver: BuiltinDriver,
305306
out: &mut Vec<u8>,
306307
input: &mut imara_diff::intern::InternedInput<&'parent [u8]>,
307308
labels: builtin_driver::text::Labels<'_>,
308-
) -> Option<(Pick, Resolution)> {
309-
let base = self.ancestor.data.as_slice()?;
310-
let ours = self.current.data.as_slice()?;
311-
let theirs = self.other.data.as_slice()?;
309+
) -> (Pick, Resolution) {
310+
let base = self.ancestor.data.as_slice();
311+
let ours = self.current.data.as_slice();
312+
let theirs = self.other.data.as_slice();
312313
let driver = if driver != BuiltinDriver::Binary
313314
&& (is_binary_buf(ours) || is_binary_buf(theirs) || is_binary_buf(base))
314315
{
315316
BuiltinDriver::Binary
316317
} else {
317318
driver
318319
};
319-
Some(match driver {
320+
match driver {
320321
BuiltinDriver::Text => {
322+
let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing");
321323
let resolution =
322324
builtin_driver::text(out, input, labels, ours, base, theirs, self.options.text);
323325
(Pick::Buffer, resolution)
@@ -332,6 +334,7 @@ pub(super) mod inner {
332334
(pick, resolution)
333335
}
334336
BuiltinDriver::Union => {
337+
let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing");
335338
let resolution = builtin_driver::text(
336339
out,
337340
input,
@@ -346,13 +349,15 @@ pub(super) mod inner {
346349
);
347350
(Pick::Buffer, resolution)
348351
}
349-
})
352+
}
350353
}
351354
}
352355

353-
fn is_binary_buf(buf: &[u8]) -> bool {
354-
let buf = &buf[..buf.len().min(8000)];
355-
buf.contains(&0)
356+
fn is_binary_buf(buf: Option<&[u8]>) -> bool {
357+
buf.map_or(true, |buf| {
358+
let buf = &buf[..buf.len().min(8000)];
359+
buf.contains(&0)
360+
})
356361
}
357362
}
358363
}
@@ -400,9 +405,7 @@ impl<'parent> PlatformRef<'parent> {
400405
Err(builtin) => {
401406
let mut input = imara_diff::intern::InternedInput::new(&[][..], &[]);
402407
out.clear();
403-
let (pick, resolution) = self
404-
.builtin_merge(builtin, out, &mut input, labels)
405-
.ok_or(Error::ResourceTooLarge)?;
408+
let (pick, resolution) = self.builtin_merge(builtin, out, &mut input, labels);
406409
Ok((pick, resolution))
407410
}
408411
}

gix-merge/tests/merge/blob/platform.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,7 @@ theirs
295295

296296
let mut input = imara_diff::intern::InternedInput::new(&[][..], &[]);
297297
let res = platform_ref.builtin_merge(BuiltinDriver::Text, &mut buf, &mut input, Default::default());
298-
assert_eq!(
299-
res,
300-
Some((Pick::Buffer, Resolution::Complete)),
301-
"both versions are deleted"
302-
);
298+
assert_eq!(res, (Pick::Buffer, Resolution::Complete), "both versions are deleted");
303299
assert!(buf.is_empty(), "the result is the same on direct invocation");
304300

305301
let print_all = "for arg in $@ %O %A %B %L %P %S %X %Y %F; do echo $arg; done";
@@ -358,24 +354,27 @@ theirs
358354
assert_eq!(platform_ref.other.data, platform::resource::Data::TooLarge { size: 12 });
359355

360356
let mut out = Vec::new();
361-
let err = platform_ref
362-
.merge(&mut out, Default::default(), &Default::default())
363-
.unwrap_err();
364-
assert!(matches!(err, platform::merge::Error::ResourceTooLarge));
357+
let res = platform_ref.merge(&mut out, Default::default(), &Default::default())?;
358+
assert_eq!(
359+
res,
360+
(Pick::Ours, Resolution::Conflict),
361+
"this is the default for binary merges, which are used in this case"
362+
);
365363

366364
let mut input = imara_diff::intern::InternedInput::new(&[][..], &[]);
367365
assert_eq!(
368366
platform_ref.builtin_merge(BuiltinDriver::Text, &mut out, &mut input, Default::default(),),
369-
None
367+
res,
368+
"we can't enforce it, it will just default to using binary"
370369
);
371370

372371
let err = platform_ref
373372
.prepare_external_driver("bogus".into(), Default::default(), Default::default())
374373
.unwrap_err();
375-
assert!(matches!(
376-
err,
377-
platform::prepare_external_driver::Error::ResourceTooLarge { .. }
378-
));
374+
assert!(
375+
matches!(err, platform::prepare_external_driver::Error::ResourceTooLarge { .. }),
376+
"however, for external drivers, resources can still be too much to handle, until we learn how to stream them"
377+
);
379378
Ok(())
380379
}
381380

0 commit comments

Comments
 (0)