| @@ -266,6 +266,8 @@ pub(super) mod inner { · 265 unmodified lines |
| 266 | 266 | |
| 267 | 267 | /// |
| 268 | 268 | pub mod builtin_merge { |
| 269 | + use crate::blob::platform::resource; |
| 270 | + use crate::blob::platform::resource::Data; |
| 269 | 271 | use crate::blob::{builtin_driver, BuiltinDriver, PlatformRef, Resolution}; |
| 270 | 272 | |
| 271 | 273 | /// An identifier to tell us how a merge conflict was resolved by [builtin_merge](PlatformRef::builtin_merge). |
| @@ -307,11 +309,13 @@ pub(super) mod inner { · 35 unmodified lines |
| 307 | 309 | input: &mut imara_diff::intern::InternedInput<&'parent [u8]>, |
| 308 | 310 | labels: builtin_driver::text::Labels<'_>, |
| 309 | 311 | ) -> (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(); |
| 312 | + let base = self.ancestor.data.as_slice().unwrap_or_default(); |
| 313 | + let ours = self.current.data.as_slice().unwrap_or_default(); |
| 314 | + let theirs = self.other.data.as_slice().unwrap_or_default(); |
| 313 | 315 | let driver = if driver != BuiltinDriver::Binary |
| 314 | | − && (is_binary_buf(ours) || is_binary_buf(theirs) || is_binary_buf(base)) |
| 316 | + && (is_binary_buf(self.ancestor.data) |
| 317 | + || is_binary_buf(self.other.data) |
| 318 | + || is_binary_buf(self.current.data)) |
| 315 | 319 | { |
| 316 | 320 | BuiltinDriver::Binary |
| 317 | 321 | } else { |
| @@ -319,7 +323,6 @@ pub(super) mod inner { · 1 unmodified line |
| 319 | 323 | }; |
| 320 | 324 | match driver { |
| 321 | 325 | BuiltinDriver::Text => { |
| 322 | | − let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing"); |
| 323 | 326 | let resolution = |
| 324 | 327 | builtin_driver::text(out, input, labels, ours, base, theirs, self.options.text); |
| 325 | 328 | (Pick::Buffer, resolution) |
| @@ -334,7 +337,6 @@ pub(super) mod inner { · 8 unmodified lines |
| 334 | 337 | (pick, resolution) |
| 335 | 338 | } |
| 336 | 339 | BuiltinDriver::Union => { |
| 337 | | − let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing"); |
| 338 | 340 | let resolution = builtin_driver::text( |
| 339 | 341 | out, |
| 340 | 342 | input, |
| @@ -353,11 +355,15 @@ pub(super) mod inner { · 12 unmodified lines |
| 353 | 355 | } |
| 354 | 356 | } |
| 355 | 357 | |
| 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 | | − }) |
| 358 | + fn is_binary_buf(data: resource::Data<'_>) -> bool { |
| 359 | + match data { |
| 360 | + Data::Missing => false, |
| 361 | + Data::Buffer(buf) => { |
| 362 | + let buf = &buf[..buf.len().min(8000)]; |
| 363 | + buf.contains(&0) |
| 364 | + } |
| 365 | + Data::TooLarge { .. } => true, |
| 366 | + } |
| 361 | 367 | } |
| 362 | 368 | } |
| 363 | 369 | } |
| @@ -412,13 +418,45 @@ impl<'parent> PlatformRef<'parent> { · 48 unmodified lines |
| 412 | 418 | } |
| 413 | 419 | |
| 414 | 420 | /// Using a `pick` obtained from [`merge()`](Self::merge), obtain the respective buffer suitable for reading or copying. |
| 415 | | − /// Return `None` if the buffer is too large, or if the `pick` corresponds to a buffer (that was written separately). |
| 416 | | − pub fn buffer_by_pick(&self, pick: inner::builtin_merge::Pick) -> Option<&'parent [u8]> { |
| 421 | + /// Return `Ok(None)` if the `pick` corresponds to a buffer (that was written separately). |
| 422 | + /// Return `Err(())` if the buffer is *too large*, so it was never read. |
| 423 | + #[allow(clippy::result_unit_err)] |
| 424 | + pub fn buffer_by_pick(&self, pick: inner::builtin_merge::Pick) -> Result<Option<&'parent [u8]>, ()> { |
| 417 | 425 | match pick { |
| 418 | | − inner::builtin_merge::Pick::Ancestor => self.ancestor.data.as_slice(), |
| 419 | | − inner::builtin_merge::Pick::Ours => self.current.data.as_slice(), |
| 420 | | − inner::builtin_merge::Pick::Theirs => self.other.data.as_slice(), |
| 421 | | − inner::builtin_merge::Pick::Buffer => None, |
| 426 | + inner::builtin_merge::Pick::Ancestor => self.ancestor.data.as_slice().map(Some).ok_or(()), |
| 427 | + inner::builtin_merge::Pick::Ours => self.current.data.as_slice().map(Some).ok_or(()), |
| 428 | + inner::builtin_merge::Pick::Theirs => self.other.data.as_slice().map(Some).ok_or(()), |
| 429 | + inner::builtin_merge::Pick::Buffer => Ok(None), |
| 430 | + } |
| 431 | + } |
| 432 | + |
| 433 | + /// Use `pick` to return the object id of the merged result, assuming that `buf` was passed as `out` to [merge()](Self::merge). |
| 434 | + /// In case of binary or large files, this will simply be the existing ID of the resource. |
| 435 | + /// In case of resources available in the object DB for binary merges, the object ID will be returned. |
| 436 | + /// If new content was produced due to a content merge, `buf` will be written out |
| 437 | + /// to the object database using `write_blob`. |
| 438 | + /// Beware that the returned ID could be `Ok(None)` if the underlying resource was loaded |
| 439 | + /// from the worktree *and* was too large so it was never loaded from disk. |
| 440 | + /// `Ok(None)` will also be returned if one of the resources was missing. |
| 441 | + /// `write_blob()` is used to turn buffers. |
| 442 | + pub fn id_by_pick<E>( |
| 443 | + &self, |
| 444 | + pick: inner::builtin_merge::Pick, |
| 445 | + buf: &[u8], |
| 446 | + mut write_blob: impl FnMut(&[u8]) -> Result<gix_hash::ObjectId, E>, |
| 447 | + ) -> Result<Option<gix_hash::ObjectId>, E> { |
| 448 | + let field = match pick { |
| 449 | + inner::builtin_merge::Pick::Ancestor => &self.ancestor, |
| 450 | + inner::builtin_merge::Pick::Ours => &self.current, |
| 451 | + inner::builtin_merge::Pick::Theirs => &self.other, |
| 452 | + inner::builtin_merge::Pick::Buffer => return write_blob(buf).map(Some), |
| 453 | + }; |
| 454 | + use crate::blob::platform::resource::Data; |
| 455 | + match field.data { |
| 456 | + Data::TooLarge { .. } | Data::Missing if !field.id.is_null() => Ok(Some(field.id.to_owned())), |
| 457 | + Data::TooLarge { .. } | Data::Missing => Ok(None), |
| 458 | + Data::Buffer(buf) if field.id.is_null() => write_blob(buf).map(Some), |
| 459 | + Data::Buffer(_) => Ok(Some(field.id.to_owned())), |
| 422 | 460 | } |
| 423 | 461 | } |
| 424 | 462 | } |