clone_repository/
clone_repository.rs1use std::ops::ControlFlow;
3use std::sync::atomic::AtomicBool;
4use std::time::{Duration, Instant};
5
6use girt::clone::{BranchSelection, CloneRequest};
7use girt::fetch::{FetchLimits, FetchUpdateLimits};
8use girt::refs::{Expected, RefName, Reflog, Target};
9use girt::remote::Remote;
10use girt::transport::TransportControl;
11use girt::{Commit, CommitFields, EntryMode, InitKind, Repository, Signature, Tree, TreeEntry};
12
13fn main() -> Result<(), Box<dyn std::error::Error>> {
14 let root = tempfile::tempdir()?;
15 let source = Repository::init(
16 girt::ObjectFormat::Sha1,
17 root.path().join("source"),
18 InitKind::Bare,
19 )?;
20 let destination_path = root.path().join("destination");
21
22 let objects = source.loose_objects();
23 let blob = objects.write_blob(b"hello from the clone\n")?;
24 let tree = objects.write_tree(&Tree::new(
25 girt::ObjectFormat::Sha1,
26 vec![TreeEntry {
27 mode: EntryMode::Blob,
28 name: b"hello.txt".to_vec(),
29 id: blob,
30 }],
31 )?)?;
32
33 let identity = Signature {
34 name: b"Fetch Example".to_vec(),
35 email: b"fetch@example.com".to_vec(),
36 seconds: 1700000000,
37 offset_minutes: 0,
38 };
39 let commit = objects.write_commit(&Commit::new(CommitFields {
40 tree,
41 parents: Vec::new(),
42 author: identity.clone(),
43 committer: identity.clone(),
44 extra_headers: Vec::new(),
45 message: b"Original fetch example\n".to_vec(),
46 })?)?;
47
48 source.references()?.update_without_reflog(
49 &RefName::branch("main")?,
50 Target::Direct(commit),
51 Expected::Absent,
52 )?;
53
54 let source_path = std::fs::canonicalize(source.git_dir())?;
56 let url = source_path
57 .to_str()
58 .ok_or("example needs a UTF-8 temporary path")?;
59 let request = CloneRequest::prepare_tracking(
60 &destination_path,
61 InitKind::Worktree,
62 url.as_bytes(),
63 BranchSelection::Default,
64 Reflog::Preserve,
65 )?;
66
67 let cancel = AtomicBool::new(false);
68 let ready = request.receive_local(
69 &source_path,
70 FetchLimits::default(),
71 TransportControl::new(&cancel)
72 .with_deadline(Some(Instant::now() + Duration::from_secs(30))),
73 |_| ControlFlow::Continue(()),
74 )?;
75
76 assert!(!destination_path.exists());
77
78 let report = match ready.finish(FetchUpdateLimits::default(), &cancel) {
81 Ok(report) => report,
82 Err(error) => {
83 eprintln!(
86 "Clone effects: {:?}; failure: {}",
87 error.report, error.source
88 );
89 return Err(error.into());
90 }
91 };
92
93 let repository = report
94 .repository
95 .as_ref()
96 .ok_or("missing completed repository")?;
97 assert_eq!(
98 repository
99 .references()?
100 .read(&RefName::new("refs/heads/main")?)?,
101 Some(Target::Direct(commit))
102 );
103 assert!(!repository.git_dir().join("index").exists());
104 assert_eq!(std::fs::read_dir(&destination_path)?.count(), 1); let remote = Remote::find(repository.config(), b"origin")?.ok_or("missing origin")?;
107 assert_eq!(remote.fetch_url(), Some(url.as_bytes()));
108
109 println!(
111 "Created no-checkout repository at {} with {:?}",
112 destination_path.display(),
113 report.head
114 );
115 drop(report);
116
117 let checked_out = root.path().join("checked-out");
120 let outcome = girt::workflow::clone_with(
121 url,
122 &checked_out,
123 &girt::workflow::CloneOptions::default()
124 .with_checkout(cfg!(any(target_os = "linux", target_os = "macos"))),
125 &girt::config::ConfigInputs::default(),
126 &girt::workflow::Environment::default(),
127 &mut girt::workflow::NoCallbacks,
128 )?;
129
130 if cfg!(any(target_os = "linux", target_os = "macos")) {
131 assert!(
132 outcome
133 .checkout
134 .as_ref()
135 .is_some_and(|report| report.index_published)
136 );
137 }
138
139 if cfg!(any(target_os = "linux", target_os = "macos")) {
140 assert_eq!(
141 std::fs::read(checked_out.join("hello.txt"))?,
142 b"hello from the clone\n"
143 );
144 }
145
146 drop(outcome);
147 root.close()?; Ok(())
149}