fetch_local/
fetch_local.rs1use 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 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 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}