| @@ -266,6 +266,8 @@ pub(super) mod inner { · 265 unmodified lines |
| 266 | | 266 | |
| 267 | /// | 267 | /// |
| 268 | pub mod builtin_merge { | 268 | pub mod builtin_merge { |
| | 269 | use crate::blob::platform::resource; |
| | 270 | use crate::blob::platform::resource::Data; |
| 269 | use crate::blob::{builtin_driver, BuiltinDriver, PlatformRef, Resolution}; | 271 | use crate::blob::{builtin_driver, BuiltinDriver, PlatformRef, Resolution}; |
| 270 | | 272 | |
| 271 | /// An identifier to tell us how a merge conflict was resolved by [builtin_merge](PlatformRef::builtin_merge). | 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 | input: &mut imara_diff::intern::InternedInput<&'parent [u8]>, | 309 | input: &mut imara_diff::intern::InternedInput<&'parent [u8]>, |
| 308 | labels: builtin_driver::text::Labels<'_>, | 310 | labels: builtin_driver::text::Labels<'_>, |
| 309 | ) -> (Pick, Resolution) { | 311 | ) -> (Pick, Resolution) { |
| 310 | let base = self.ancestor.data.as_slice(); | 312 | let base = self.ancestor.data.as_slice().unwrap_or_default(); |
| 311 | let ours = self.current.data.as_slice(); | 313 | let ours = self.current.data.as_slice().unwrap_or_default(); |
| 312 | let theirs = self.other.data.as_slice(); | 314 | let theirs = self.other.data.as_slice().unwrap_or_default(); |
| 313 | let driver = if driver != BuiltinDriver::Binary | 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 | BuiltinDriver::Binary | 320 | BuiltinDriver::Binary |
| 317 | } else { | 321 | } else { |
| @@ -319,7 +323,6 @@ pub(super) mod inner { · 1 unmodified line |
| 319 | }; | 323 | }; |
| 320 | match driver { | 324 | match driver { |
| 321 | BuiltinDriver::Text => { | 325 | BuiltinDriver::Text => { |
| 322 | let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing"); | | |
| 323 | let resolution = | 326 | let resolution = |
| 324 | builtin_driver::text(out, input, labels, ours, base, theirs, self.options.text); | 327 | builtin_driver::text(out, input, labels, ours, base, theirs, self.options.text); |
| 325 | (Pick::Buffer, resolution) | 328 | (Pick::Buffer, resolution) |
| @@ -334,7 +337,6 @@ pub(super) mod inner { · 8 unmodified lines |
| 334 | (pick, resolution) | 337 | (pick, resolution) |
| 335 | } | 338 | } |
| 336 | BuiltinDriver::Union => { | 339 | BuiltinDriver::Union => { |
| 337 | let ((base, ours), theirs) = base.zip(ours).zip(theirs).expect("would use binary if missing"); | | |
| 338 | let resolution = builtin_driver::text( | 340 | let resolution = builtin_driver::text( |
| 339 | out, | 341 | out, |
| 340 | input, | 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 { | 358 | fn is_binary_buf(data: resource::Data<'_>) -> bool { |
| 357 | buf.map_or(true, |buf| { | 359 | match data { |
| 358 | let buf = &buf[..buf.len().min(8000)]; | 360 | Data::Missing => false, |
| 359 | buf.contains(&0) | 361 | Data::Buffer(buf) => { |
| 360 | }) | 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 | /// Using a `pick` obtained from [`merge()`](Self::merge), obtain the respective buffer suitable for reading or copying. | 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). | 421 | /// Return `Ok(None)` 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]> { | 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 | match pick { | 425 | match pick { |
| 418 | inner::builtin_merge::Pick::Ancestor => self.ancestor.data.as_slice(), | 426 | inner::builtin_merge::Pick::Ancestor => self.ancestor.data.as_slice().map(Some).ok_or(()), |
| 419 | inner::builtin_merge::Pick::Ours => self.current.data.as_slice(), | 427 | inner::builtin_merge::Pick::Ours => self.current.data.as_slice().map(Some).ok_or(()), |
| 420 | inner::builtin_merge::Pick::Theirs => self.other.data.as_slice(), | 428 | inner::builtin_merge::Pick::Theirs => self.other.data.as_slice().map(Some).ok_or(()), |
| 421 | inner::builtin_merge::Pick::Buffer => None, | 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 | } |