Skip to main content

fetch_local/
fetch_local.rs

1//! Run `cargo run --example fetch_local`; all repositories are disposable and local.
2use std::sync::atomic::AtomicBool;
3use std::time::{Duration, Instant};
4
5use girt::fetch::{FetchLimits, KnownHistory, receive_local};
6use girt::refs::{Expected, RefName, Target};
7use girt::transport::TransportControl;
8use girt::{Commit, CommitFields, EntryMode, Repository, Signature, Tree, TreeEntry};
9
10fn repository() -> Result<(tempfile::TempDir, Repository), Box<dyn std::error::Error>> {
11    let root = tempfile::tempdir()?;
12    std::fs::create_dir(root.path().join("objects"))?;
13    std::fs::create_dir(root.path().join("refs"))?;
14    std::fs::write(root.path().join("HEAD"), b"ref: refs/heads/main\n")?;
15    std::fs::write(root.path().join("config"), b"[core]\nbare=true\n")?;
16    let repo = Repository::open(root.path())?;
17    Ok((root, repo))
18}
19
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}