Skip to main content

BlobContent

Struct BlobContent 

Source
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

Source

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?
examples/content_diff.rs (lines 51-57)
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}

Trait Implementations§

Source§

impl Debug for BlobContent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more