Skip to main content

Object

Struct Object 

Source
pub struct Object { /* private fields */ }
Expand description

An owned, identity-verified object kind and exact uncompressed payload.

Storage framing is validated; tree, commit, and tag payload syntax is not. Pass Self::data to crate::Tree::parse, crate::Commit::parse, or crate::Tag::parse as appropriate.

A packed payload may be shared with the reader’s delta base cache, and clones share it too, so borrowing it through Self::data costs no copy. Self::into_data copies it only while another Object or the cache shares it.

use girt::{InitKind, ObjectFormat, PackLimits, ReadLimits, Repository};

let directory = tempfile::tempdir()?;
let repo = Repository::init(
    ObjectFormat::Sha1,
    directory.path().join("r.git"),
    InitKind::Bare,
)?;

let id = repo.loose_objects().write_blob(b"payload\n")?;
let objects = repo.objects_with(PackLimits::default())?;
let object = objects
    .read_with(id, ReadLimits::default())?
    .ok_or("missing blob")?;

assert_eq!(object.data(), b"payload\n");
assert_eq!(object.id(), id);

Implementations§

Source§

impl Object

Source

pub fn object_format(&self) -> ObjectFormat

The storage format in which the identity was verified.

Source

pub fn kind(&self) -> ObjectKind

The verified Git object type.

Examples found in repository?
examples/packed_repository.rs (line 29)
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22    let args = Args::parse();
23    let repository = Repository::open(args.path)?;
24    let objects = repository.objects()?;
25    let object = objects.read(args.id)?.ok_or("object absent")?;
26
27    eprintln!(
28        "{} {} ({} bytes)",
29        object.kind().as_str(),
30        object.id(),
31        object.data().len()
32    );
33    io::stdout().write_all(object.data())?;
34    Ok(())
35}
More examples
Hide additional examples
examples/write_pack.rs (line 50)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    let root = tempfile::tempdir()?;
9    let format = girt::ObjectFormat::Sha256;
10    let payload = b"explicit export\0\xff";
11    let id = ObjectId::for_blob(format, payload);
12
13    let inputs = [PackObject {
14        id,
15        kind: ObjectKind::Blob,
16        data: payload,
17    }];
18
19    let mut pack = BufWriter::new(File::create(root.path().join("export.pack"))?);
20    let mut index = BufWriter::new(File::create(root.path().join("export.idx"))?);
21    let written = write_pack(
22        format,
23        &inputs,
24        &mut pack,
25        &mut index,
26        PackWriteLimits::default(),
27    )?;
28
29    pack.flush()?;
30    index.flush()?;
31    drop((pack, index));
32
33    // Only this example owns the new repository. Prepare both files before opening any readers.
34    // A live repository needs a separate safe publication protocol, not this two-rename sequence.
35    let repo_path = root.path().join("private.git");
36    let repo = Repository::init(format, &repo_path, girt::InitKind::Bare)?;
37
38    let basename = format!("objects/pack/pack-{}", written.checksum);
39    fs::rename(
40        root.path().join("export.pack"),
41        repo_path.join(format!("{basename}.pack")),
42    )?;
43    fs::rename(
44        root.path().join("export.idx"),
45        repo_path.join(format!("{basename}.idx")),
46    )?;
47
48    let objects = repo.objects()?;
49    let restored = objects.read(id)?.ok_or("exported object missing")?;
50    assert_eq!(restored.kind(), ObjectKind::Blob);
51    assert_eq!(restored.data(), payload);
52
53    println!(
54        "Verified {} explicit object in {} pack bytes and {} index bytes",
55        written.objects, written.pack_bytes, written.index_bytes
56    );
57    Ok(())
58}
Source

pub fn data(&self) -> &[u8] ⓘ

Exact uncompressed payload, without the Git object header.

Examples found in repository?
examples/packed_repository.rs (line 31)
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22    let args = Args::parse();
23    let repository = Repository::open(args.path)?;
24    let objects = repository.objects()?;
25    let object = objects.read(args.id)?.ok_or("object absent")?;
26
27    eprintln!(
28        "{} {} ({} bytes)",
29        object.kind().as_str(),
30        object.id(),
31        object.data().len()
32    );
33    io::stdout().write_all(object.data())?;
34    Ok(())
35}
More examples
Hide additional examples
examples/scheduling/fixture.rs (line 93)
83    pub fn operation(&self, gate: Option<Gate>) {
84        let repo = Repository::open(self.root.path()).unwrap();
85        let objects = repo.objects().unwrap();
86
87        if let Some(gate) = gate {
88            gate.started.send(()).unwrap();
89            gate.release.recv().unwrap();
90        }
91
92        let blob = objects.read(self.blob).unwrap().unwrap();
93        assert_eq!(blob.data().len(), 2 * 1024 * 1024);
94        assert_eq!(objects.walk(&[self.tip]).unwrap().len(), 512);
95    }
examples/write_pack.rs (line 51)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    let root = tempfile::tempdir()?;
9    let format = girt::ObjectFormat::Sha256;
10    let payload = b"explicit export\0\xff";
11    let id = ObjectId::for_blob(format, payload);
12
13    let inputs = [PackObject {
14        id,
15        kind: ObjectKind::Blob,
16        data: payload,
17    }];
18
19    let mut pack = BufWriter::new(File::create(root.path().join("export.pack"))?);
20    let mut index = BufWriter::new(File::create(root.path().join("export.idx"))?);
21    let written = write_pack(
22        format,
23        &inputs,
24        &mut pack,
25        &mut index,
26        PackWriteLimits::default(),
27    )?;
28
29    pack.flush()?;
30    index.flush()?;
31    drop((pack, index));
32
33    // Only this example owns the new repository. Prepare both files before opening any readers.
34    // A live repository needs a separate safe publication protocol, not this two-rename sequence.
35    let repo_path = root.path().join("private.git");
36    let repo = Repository::init(format, &repo_path, girt::InitKind::Bare)?;
37
38    let basename = format!("objects/pack/pack-{}", written.checksum);
39    fs::rename(
40        root.path().join("export.pack"),
41        repo_path.join(format!("{basename}.pack")),
42    )?;
43    fs::rename(
44        root.path().join("export.idx"),
45        repo_path.join(format!("{basename}.idx")),
46    )?;
47
48    let objects = repo.objects()?;
49    let restored = objects.read(id)?.ok_or("exported object missing")?;
50    assert_eq!(restored.kind(), ObjectKind::Blob);
51    assert_eq!(restored.data(), payload);
52
53    println!(
54        "Verified {} explicit object in {} pack bytes and {} index bytes",
55        written.objects, written.pack_bytes, written.index_bytes
56    );
57    Ok(())
58}
examples/fetch_local.rs (line 83)
20fn main() -> Result<(), Box<dyn std::error::Error>> {
21    let (_source_root, source) = repository()?;
22    let (_destination_root, destination) = repository()?;
23
24    let objects = source.loose_objects();
25    let blob = objects.write_blob(b"Fetched through native local storage\n")?;
26    let tree = objects.write_tree(&Tree::new(
27        girt::ObjectFormat::Sha1,
28        vec![TreeEntry {
29            mode: EntryMode::Blob,
30            name: b"hello.txt".to_vec(),
31            id: blob,
32        }],
33    )?)?;
34
35    let author = Signature::now(b"Fetch Example", b"fetch@example.com")?;
36    let commit = objects.write_commit(&Commit::new(CommitFields {
37        tree,
38        parents: vec![],
39        author: author.clone(),
40        committer: author,
41        extra_headers: vec![],
42        message: b"Local fetch fixture\n".to_vec(),
43    })?)?;
44
45    let branch = RefName::branch("main")?;
46    source.references()?.update_without_reflog(
47        &branch,
48        Target::Direct(commit),
49        Expected::Absent,
50    )?;
51
52    let cancel = AtomicBool::new(false);
53    let received = receive_local(
54        source.git_dir(),
55        |advertisement| {
56            advertisement
57                .refs
58                .iter()
59                .filter(|reference| {
60                    reference.name == branch
61                        && reference.kind == girt::fetch::AdvertisementKind::Tip
62                })
63                .map(|reference| reference.id)
64                .collect()
65        },
66        girt::fetch::LocalFetchRequest::new(FetchLimits::default()),
67        TransportControl::new(&cancel)
68            .with_deadline(Some(Instant::now() + Duration::from_secs(30))),
69        girt::fetch::LocalFetchObserver::default(),
70    )?;
71
72    let result = received.install(&destination, girt::PackLimits::default(), &cancel)?;
73
74    // Updating a ref is a separate, conditional operation, explicitly without a reflog.
75    destination.references()?.update_without_reflog(
76        &branch,
77        Target::Direct(commit),
78        Expected::Absent,
79    )?;
80
81    let fetched = destination.objects()?;
82    let restored = fetched.read(blob)?.ok_or("missing fetched blob")?;
83    assert_eq!(restored.data(), b"Fetched through native local storage\n");
84
85    println!(
86        "Fetched {} objects ({} pack bytes); published {commit} without a reflog",
87        result.objects,
88        received.pack_bytes()
89    );
90
91    let known = KnownHistory::new(&fetched, &[commit], FetchLimits::default(), &cancel)?;
92    let repeated = receive_local(
93        source.git_dir(),
94        |_| vec![commit],
95        girt::fetch::LocalFetchRequest::new(FetchLimits::default()).with_known(&known),
96        TransportControl::new(&cancel)
97            .with_deadline(Some(Instant::now() + Duration::from_secs(30))),
98        girt::fetch::LocalFetchObserver::default(),
99    )?;
100
101    // Recheck known dependencies in the destination even when no pack was needed.
102    repeated.install(&destination, girt::PackLimits::default(), &cancel)?;
103    assert_eq!(repeated.pack_bytes(), 0);
104    println!(
105        "Repeated fetch: {} objects, {} pack bytes",
106        repeated.object_count(),
107        repeated.pack_bytes()
108    );
109
110    Ok(())
111}
examples/push_local.rs (line 85)
23fn main() -> Result<(), Box<dyn std::error::Error>> {
24    let (_source_root, source) = repository()?;
25    let (_destination_root, destination) = repository()?;
26
27    let loose = source.loose_objects();
28    let blob = loose.write_blob(b"Published by girt through native local storage\n")?;
29    let tree = loose.write_tree(&Tree::new(
30        girt::ObjectFormat::Sha1,
31        vec![TreeEntry {
32            mode: EntryMode::Blob,
33            name: b"hello.txt".to_vec(),
34            id: blob,
35        }],
36    )?)?;
37
38    let author = Signature::now(b"Push Example", b"push@example.com")?;
39    let commit = loose.write_commit(&Commit::new(CommitFields {
40        tree,
41        parents: vec![],
42        author: author.clone(),
43        committer: author.clone(),
44        extra_headers: vec![],
45        message: b"Local push fixture\n".to_vec(),
46    })?)?;
47
48    let tag = loose.write_tag(&Tag::new(TagFields {
49        target: commit,
50        target_kind: ObjectKind::Commit,
51        name: b"v1".to_vec(),
52        tagger: Some(author),
53        extra_headers: vec![],
54        message: b"First publication\n".to_vec(),
55    })?)?;
56
57    let cancel = AtomicBool::new(false);
58    let objects = source.objects()?;
59    let commands = vec![
60        PushCommand::new(RefName::new("refs/heads/main")?, None, commit)
61            .with_force(ForcePolicy::FastForwardOnly),
62        PushCommand::new(RefName::new("refs/tags/v1")?, None, tag)
63            .with_force(ForcePolicy::FastForwardOnly),
64    ];
65    let prepared = PreparedPush::new(&objects, commands, &[], PushLimits::default(), &cancel)?;
66
67    let report = send_local(
68        destination.git_dir(),
69        girt::push::LocalPushRequest::new(&prepared),
70        TransportControl::new(&cancel)
71            .with_deadline(Some(Instant::now() + Duration::from_secs(30))),
72        |_| {},
73    )?;
74
75    // A complete protocol exchange can still contain rejection or partial success.
76    if !report.all_succeeded() {
77        return Err(format!("native local push rejected updates: {report:?}").into());
78    }
79
80    let restored = destination
81        .objects()?
82        .read(blob)?
83        .ok_or("missing pushed blob")?;
84    assert_eq!(
85        restored.data(),
86        b"Published by girt through native local storage\n"
87    );
88
89    println!(
90        "Published branch and annotated tag: {} objects, {} pack bytes",
91        prepared.object_count(),
92        prepared.pack_bytes()
93    );
94
95    let repeated = PreparedPush::new(
96        &objects,
97        vec![
98            PushCommand::new(RefName::new("refs/heads/main")?, Some(commit), commit)
99                .with_force(ForcePolicy::FastForwardOnly),
100        ],
101        &[commit],
102        PushLimits::default(),
103        &cancel,
104    )?;
105
106    let report = send_local(
107        destination.git_dir(),
108        girt::push::LocalPushRequest::new(&repeated),
109        TransportControl::new(&cancel)
110            .with_deadline(Some(Instant::now() + Duration::from_secs(30))),
111        |_| {},
112    )?;
113
114    if !report.all_succeeded() {
115        return Err(format!("repeat rejected: {report:?}").into());
116    }
117    assert_eq!(repeated.object_count(), 0);
118    println!(
119        "Repeated push: {} objects, {} pack bytes",
120        repeated.object_count(),
121        repeated.pack_bytes()
122    );
123
124    Ok(())
125}
examples/storage_resources.rs (line 146)
47fn main() -> Result<(), Box<dyn std::error::Error>> {
48    let args = Args::parse();
49    let repository = Repository::open(args.repository)?;
50    let id = args.id;
51
52    let baseline = handles();
53    let failed = repository.objects_with(PackLimits::default().with_max_index_bytes(0));
54    assert!(matches!(
55        failed,
56        Err(girt::ObjectReadError::Limit("pack index bytes"))
57    ));
58    assert_eq!(handles(), baseline);
59
60    if args.mode == Some(Mode::Cancel) {
61        let cancelled = AtomicBool::new(false);
62        let start = Instant::now();
63
64        let result = std::thread::scope(|scope| {
65            scope.spawn(|| {
66                std::thread::sleep(Duration::from_millis(10));
67                cancelled.store(true, std::sync::atomic::Ordering::Relaxed);
68            });
69            repository.objects_controlled(
70                PackLimits::default(),
71                AlternateLimits::default(),
72                &cancelled,
73            )
74        });
75
76        assert!(matches!(result, Err(girt::ObjectReadError::Cancelled)));
77        assert_eq!(handles(), baseline);
78
79        println!(
80            "cancel_us={} handles_before={baseline:?} handles_after={:?}",
81            start.elapsed().as_micros(),
82            handles()
83        );
84        return Ok(());
85    }
86
87    let cancelled = AtomicBool::new(false);
88    let start = Instant::now();
89    let mut objects = repository.objects_controlled(
90        PackLimits::default(),
91        AlternateLimits::default(),
92        &cancelled,
93    )?;
94
95    let opened = start.elapsed();
96    let retained = handles();
97    let clone = objects.clone();
98    let cloned = handles();
99
100    let failed = objects.refresh(
101        PackLimits::default().with_max_index_bytes(0),
102        AlternateLimits::default(),
103    );
104    assert!(matches!(
105        failed,
106        Err(girt::ObjectReadError::Limit("pack index bytes"))
107    ));
108    assert_eq!(handles(), cloned);
109
110    let start = Instant::now();
111    objects.refresh(PackLimits::default(), AlternateLimits::default())?;
112    let refreshed = start.elapsed();
113
114    let generations = handles();
115    assert_eq!(
116        generations,
117        baseline
118            .zip(retained)
119            .map(|(base, open)| base + 2 * (open - base))
120    );
121
122    println!(
123        "refresh_us={} handles_two_generations={generations:?}",
124        refreshed.as_micros()
125    );
126
127    let start = Instant::now();
128    let object = clone
129        .read_controlled(id, ReadLimits::default(), &cancelled)?
130        .ok_or("object missing")?;
131    let read = start.elapsed();
132
133    let start = Instant::now();
134    let second = clone
135        .read_with(id, ReadLimits::default())?
136        .ok_or("object missing")?;
137    let reread = start.elapsed();
138
139    assert_eq!(object, second);
140    println!(
141        "pid={} open_us={} first_read_us={} warm_read_us={} payload_bytes={} handles_before={baseline:?} handles_open={retained:?} handles_clone={cloned:?}",
142        std::process::id(),
143        opened.as_micros(),
144        read.as_micros(),
145        reread.as_micros(),
146        object.data().len()
147    );
148
149    drop(second);
150    drop(object);
151    drop(objects);
152    drop(clone);
153
154    println!("handles_after_drop={:?}", handles());
155    assert_eq!(handles(), baseline);
156
157    // Optional sampling interval is outside measured operations and retains no reader.
158    if args.mode == Some(Mode::Hold) {
159        std::thread::sleep(Duration::from_secs(1));
160    }
161    Ok(())
162}
Source

pub fn into_data(self) -> Vec<u8> ⓘ

Takes ownership of the payload, copying it if another Object or the cache shares it.

use girt::{InitKind, ObjectFormat, PackLimits, ReadLimits, Repository};

let directory = tempfile::tempdir()?;
let repo = Repository::init(
    ObjectFormat::Sha1,
    directory.path().join("r.git"),
    InitKind::Bare,
)?;

let id = repo.loose_objects().write_blob(b"payload")?;
let objects = repo.objects_with(PackLimits::default())?;
let object = objects
    .read_with(id, ReadLimits::default())?
    .ok_or("blob absent")?;

let retained = object.clone();
let owned = object.into_data(); // A shared payload may be copied; the other owner stays usable.
assert_eq!(owned, retained.data());
Source

pub fn id(&self) -> ObjectId

Hashes the kind, canonical header, and payload.

Examples found in repository?
examples/packed_repository.rs (line 30)
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22    let args = Args::parse();
23    let repository = Repository::open(args.path)?;
24    let objects = repository.objects()?;
25    let object = objects.read(args.id)?.ok_or("object absent")?;
26
27    eprintln!(
28        "{} {} ({} bytes)",
29        object.kind().as_str(),
30        object.id(),
31        object.data().len()
32    );
33    io::stdout().write_all(object.data())?;
34    Ok(())
35}

Trait Implementations§

Source§

impl Clone for Object

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Object

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Eq for Object

Source§

impl PartialEq for Object

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Object

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. 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