Skip to main content

push_local/
push_local.rs

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