1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Conditional reference publication over receive-pack protocol v0.
//!
//! # Push lifecycle
//!
//! Build [`PushCommand`] values with exact expected old targets, then use [`PreparedPush::new`]
//! or [`PreparedPush::new_local`] to select objects and spool the pack. Choose [`send_local`]
//! for a native local destination or [`send`] for caller-owned protocol streams. A successful
//! exchange can contain rejected refs: inspect [`PushReport`] per command. [`PushError`] retains
//! known acknowledgements when the final outcome is uncertain; read destination refs before
//! retrying.
//!
//! Preparation validates structured objects, locates blobs and spools a pack (possibly thin against
//! advertised history) before any commands can reach a server. Reused compressed entries are
//! CRC-checked; the receiver derives their identities and checks connectivity before publishing
//! refs. With receiver roots, it reads only the history they do not cover plus a boundary; see
//! [`PreparedPush::new_excluding`]. Preparation leaves local refs and reflogs untouched. These
//! functions take an already prepared push, so its roots come from the caller's knowledge.
//! [`crate::Repository::push`] instead prepares after reading the advertisement and excludes
//! [`PushAdvertisement::receiver_roots`], as `git send-pack` does.
//!
//! Wire and native local push support SHA-1 and SHA-256. `report-status` is required by wire push;
//! deletion requires the advertised `delete-refs` capability, and supplied push options require
//! `push-options`. Report-status-v2 is preferred when advertised; optional sideband progress is
//! retained as bounded, untrusted bytes. Atomic and signed pushes are unsupported. Multiple
//! commands can partially succeed. The `http` feature adds async smart-HTTP sending with
//! explicit caller-supplied authorization headers; `ssh` adds system OpenSSH on macOS/Linux.
//! Both require a caller-owned Tokio runtime. [`crate::remote`] maps configured refspecs
//! separately; callers must authorize force and supply exact expected values. Pruning and
//! automatic force are deferred. [`crate::remote::CredentialSession`] is a separate
//! application-approved helper lifecycle; callers attach resulting credentials to their chosen
//! transport and keep authentication approval or rejection under application control.
//! `send_http_checked` lets an HTTP caller inspect validated receive-pack tips and decline a
//! whole batch before POST. A decision does not reserve those tips; the receiver still checks each
//! command's expected old value. `send_ssh_checked_with_progress` provides the same inspection
//! boundary in one SSH session, closing a declined session with only a protocol flush.
//!
//! # Example
//!
//! Push a commit (setup hidden) to create `main` in an empty local destination. `expected: None`
//! requires the destination reference to be absent:
//!
//! ```
//! use std::sync::atomic::AtomicBool;
//!
//! use girt::refs::Target;
//! use girt::{InitKind, ObjectFormat, PackLimits, Repository};
//! use girt::push::{ForcePolicy, PreparedPush, PushCommand, PushLimits, send_local};
//! # use girt::refs::{Expected, RefName};
//! # use girt::{Commit, CommitFields, Signature, Tree};
//! # let directory = tempfile::tempdir()?;
//! # let source_path = directory.path().join("source.git");
//! # let source = Repository::init(ObjectFormat::Sha1, source_path, InitKind::Bare)?;
//! # let person = Signature {
//! # name: b"A. Writer".to_vec(),
//! # email: b"writer@example.com".to_vec(),
//! # seconds: 1_700_000_000,
//! # offset_minutes: 0,
//! # };
//! # let loose = source.loose_objects();
//! # let commit = loose.write_commit(&Commit::new(CommitFields {
//! # tree: loose.write_tree(&Tree::new(ObjectFormat::Sha1, vec![])?)?,
//! # parents: vec![],
//! # author: person.clone(),
//! # committer: person.clone(),
//! # extra_headers: vec![],
//! # message: b"Initial commit\n".to_vec(),
//! # })?)?;
//! # let main = RefName::new("refs/heads/main")?;
//! # source.references()?.update_without_reflog(&main, Target::Direct(commit), Expected::Absent)?;
//!
//! let destination =
//! Repository::init(ObjectFormat::Sha1, directory.path().join("remote.git"), InitKind::Bare)?;
//! let cancel = AtomicBool::new(false);
//! let objects = source.objects(PackLimits::default())?;
//! let command = PushCommand::new(main.clone(), None, commit).with_force(ForcePolicy::FastForwardOnly);
//! let prepared = PreparedPush::new(&objects, vec![command], PushLimits::default(), &cancel)?;
//! assert_eq!(prepared.object_count(), 2); // The commit and its empty tree.
//!
//! let report = send_local(destination.git_dir(), &prepared, &cancel)?;
//! assert!(report.all_succeeded(), "individual references can be rejected: {report:?}");
//! assert_eq!(destination.references()?.read(&main)?, Some(Target::Direct(commit)));
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
pub use send_ssh_preparing;
pub use ;
pub use send_http_preparing;
pub use ;
pub use send_local_preparing_observed;
pub use ;
pub use PreparedPush;
pub use PreparationProgress;
pub use fuzz_exchange;
pub use ;
pub use ;