pub struct BlobContent {
pub old: Vec<u8>,
pub new: Vec<u8>,
}Expand description
Owned blob sides loaded from a tree change, ready to borrow in super::diff.
Missing sides become empty buffers. Existence and modes remain on the original TreeChange:
an absent side and a present empty blob compare as unchanged content. Symlinks are compared as
their stored target bytes, without dereferencing a filesystem path.
Fields§
§old: Vec<u8>Original payload, or empty bytes for an addition.
new: Vec<u8>Replacement payload, or empty bytes for a deletion.
Implementations§
Source§impl BlobContent
impl BlobContent
Sourcepub fn read(
objects: &Objects,
change: &TreeChange,
read: ReadLimits,
max_bytes: usize,
cancel: &AtomicBool,
) -> Result<Self, ContentReadError>
pub fn read( objects: &Objects, change: &TreeChange, read: ReadLimits, max_bytes: usize, cancel: &AtomicBool, ) -> Result<Self, ContentReadError>
Loads and verifies both blob sides without changing storage.
Accepts regular, executable and symlink modes. Tree/gitlink modes are rejected before any
read. Both sides are read even for equal IDs, so dangling or wrong-kind objects cannot be
hidden by an identity skip. read bounds each individual read; max_bytes additionally
bounds the combined retained payload bytes, constraining decoding before each read.
Memory also includes Objects’ pack snapshot and per-read reconstruction workspace.
Storage is synchronous and subject to Objects::read’s trust/pruning assumptions.
Cancellation is checked before mode validation, between reads and after the final read;
an individual storage read cannot be interrupted. A read error takes precedence over a
cancellation observed after it. Callers own scheduling and may borrow the returned buffers
for repeated pure comparisons under different binary policies.
§Errors
Returns ContentReadError for unsupported modes, missing/wrong-kind objects, storage or
decoding failures, or cancellation. Errors retain the offending side’s ID or mode; the
caller retains path and side context in change. No partial pair or writes are produced.
use std::sync::atomic::AtomicBool;
use girt::{EntryMode, InitKind, ObjectFormat, Repository, Tree, TreeEntry};
use girt::content_diff::BlobContent;
let directory = tempfile::tempdir()?;
let repo = Repository::init(ObjectFormat::Sha1, directory.path().join("r"), InitKind::Bare)?;
let loose = repo.loose_objects();
let id = loose.write_blob(b"added\n")?;
let tree = loose.write_tree(&Tree::new(ObjectFormat::Sha1,
vec![TreeEntry { name: b"file".to_vec(), mode: EntryMode::Blob, id }])?)?;
let objects = repo.objects_with(Default::default())?;
let cancel = AtomicBool::new(false);
let changes = objects.compare_trees_with(None, Some(tree), Default::default(), &cancel)?;
let content = BlobContent::read(&objects, &changes[0], Default::default(), 1024, &cancel)?;
assert!(content.old.is_empty());
assert_eq!(content.new, b"added\n");
assert!(changes[0].old.is_none()); // Keep existence independently from the empty content buffer.Examples found in repository?
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let root = tempfile::tempdir()?;
10 let repo = Repository::init(
11 girt::ObjectFormat::Sha1,
12 root.path().join("repo"),
13 InitKind::Bare,
14 )?;
15
16 let store = repo.loose_objects();
17 let old_blob = store.write_blob(b"heading\r\nold\n")?;
18 let new_blob = store.write_blob(b"heading\r\nnew\xff")?;
19
20 let old = store.write_tree(&Tree::new(
21 girt::ObjectFormat::Sha1,
22 vec![TreeEntry {
23 name: b"example.txt".to_vec(),
24 mode: EntryMode::Blob,
25 id: old_blob,
26 }],
27 )?)?;
28 let new = store.write_tree(&Tree::new(
29 girt::ObjectFormat::Sha1,
30 vec![TreeEntry {
31 name: b"example.txt".to_vec(),
32 mode: EntryMode::Executable,
33 id: new_blob,
34 }],
35 )?)?;
36
37 let objects = repo.objects()?;
38 let cancel = AtomicBool::new(false);
39 let limits = DiffLimits::default();
40
41 let changes = objects.compare_trees(Some(old), Some(new))?;
42 for change in changes {
43 println!(
44 "{}: {:?} -> {:?}",
45 escaped(&change.path),
46 change.old.map(|v| v.mode),
47 change.new.map(|v| v.mode)
48 );
49
50 // Gitlinks require a submodule policy; the blob adapter deliberately rejects them.
51 let content = BlobContent::read(
52 &objects,
53 &change,
54 ReadLimits::default(),
55 limits.max_input_bytes,
56 &cancel,
57 )?;
58
59 match diff(
60 &content.old,
61 &content.new,
62 BinaryMode::Auto,
63 limits,
64 &cancel,
65 )? {
66 ContentDiff::Unchanged => println!(" unchanged content (check modes/existence above)"),
67 ContentDiff::Binary { old, new } => {
68 println!(" binary: {} -> {} bytes", old.len(), new.len())
69 }
70 ContentDiff::Text(text) => {
71 for edit in text.edits() {
72 println!(
73 " old lines {:?} -> new lines {:?} (zero-based)",
74 edit.old_lines, edit.new_lines
75 );
76 println!(" - {}", escaped(&text.old_bytes()[edit.old_bytes.clone()]));
77 println!(" + {}", escaped(&text.new_bytes()[edit.new_bytes.clone()]));
78 }
79 }
80 _ => return Err("unsupported content diff kind".into()),
81 }
82 }
83
84 Ok(())
85}