Skip to content

Commit c06d819

Browse files
committed
[clone] This actually works: first MVP of retrieving packs via clone
1 parent 264ec82 commit c06d819

File tree

11 files changed

+154
-36
lines changed

11 files changed

+154
-36
lines changed

Diff for: git-features/src/interrupt.rs

+13
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ where
5252
}
5353
}
5454

55+
impl<R> io::BufRead for Read<R>
56+
where
57+
R: io::BufRead,
58+
{
59+
fn fill_buf(&mut self) -> io::Result<&[u8]> {
60+
self.inner.fill_buf()
61+
}
62+
63+
fn consume(&mut self, amt: usize) {
64+
self.inner.consume(amt)
65+
}
66+
}
67+
5568
#[cfg(not(feature = "disable-interrupts"))]
5669
static IS_INTERRUPTED: AtomicBool = AtomicBool::new(false);
5770

Diff for: git-features/src/progress/mod.rs

+14
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ where
4545
Ok(bytes_read)
4646
}
4747
}
48+
49+
impl<R, P> io::BufRead for Read<R, P>
50+
where
51+
R: io::BufRead,
52+
P: Progress,
53+
{
54+
fn fill_buf(&mut self) -> io::Result<&[u8]> {
55+
self.reader.fill_buf()
56+
}
57+
58+
fn consume(&mut self, amt: usize) {
59+
self.reader.consume(amt)
60+
}
61+
}

Diff for: git-odb/src/pack/bundle/write/mod.rs

+80-16
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,59 @@ pub struct Options {
2525
}
2626

2727
impl pack::Bundle {
28+
pub fn write_stream_to_directory<P>(
29+
pack: impl io::BufRead,
30+
pack_size: Option<u64>,
31+
directory: Option<impl AsRef<Path>>,
32+
mut progress: P,
33+
options: Options,
34+
) -> Result<Outcome, Error>
35+
where
36+
P: Progress,
37+
<P as Progress>::SubProgress: Send + 'static,
38+
<<P as Progress>::SubProgress as Progress>::SubProgress: Send,
39+
{
40+
let mut read_progress = progress.add_child("read pack");
41+
read_progress.init(pack_size.map(|s| s as usize), progress::bytes());
42+
let pack = progress::Read {
43+
reader: pack,
44+
progress: progress::ThroughputOnDrop::new(read_progress),
45+
};
46+
47+
let data_file = Arc::new(parking_lot::Mutex::new(match directory.as_ref() {
48+
Some(directory) => NamedTempFile::new_in(directory.as_ref())?,
49+
None => NamedTempFile::new()?,
50+
}));
51+
let data_path: PathBuf = data_file.lock().path().into();
52+
let pack = PassThrough {
53+
reader: interrupt::Read { inner: pack },
54+
writer: Some(data_file.clone()),
55+
};
56+
let pack_entries_iter = pack::data::Iter::new_from_header(
57+
pack,
58+
options.iteration_mode,
59+
pack::data::iter::CompressedBytesMode::CRC32,
60+
)?;
61+
let pack_kind = pack_entries_iter.kind();
62+
let (outcome, data_path, index_path) =
63+
pack::Bundle::inner_write(directory, progress, options, data_file, data_path, pack_entries_iter)?;
64+
65+
Ok(Outcome {
66+
index: outcome,
67+
pack_kind,
68+
data_path,
69+
index_path,
70+
})
71+
}
2872
/// If `directory` is `None`, the output will be written to a sink
29-
pub fn write_to_directory<P>(
73+
/// In this case, `pack` will be read in its own thread to offset these costs.
74+
/// If that's not possible, use `write_stream_to_directory` instead.
75+
pub fn write_to_directory_eagerly<P>(
3076
pack: impl io::Read + Send + 'static,
3177
pack_size: Option<u64>,
3278
directory: Option<impl AsRef<Path>>,
3379
mut progress: P,
34-
Options {
35-
thread_limit,
36-
iteration_mode,
37-
index_kind,
38-
}: Options,
80+
options: Options,
3981
) -> Result<Outcome, Error>
4082
where
4183
P: Progress,
@@ -48,7 +90,6 @@ impl pack::Bundle {
4890
reader: pack,
4991
progress: progress::ThroughputOnDrop::new(read_progress),
5092
};
51-
let indexing_progress = progress.add_child("create index file");
5293

5394
let data_file = Arc::new(parking_lot::Mutex::new(match directory.as_ref() {
5495
Some(directory) => NamedTempFile::new_in(directory.as_ref())?,
@@ -63,15 +104,45 @@ impl pack::Bundle {
63104
let buffered_pack = io::BufReader::with_capacity(eight_pages, pack);
64105
let pack_entries_iter = pack::data::Iter::new_from_header(
65106
buffered_pack,
66-
iteration_mode,
107+
options.iteration_mode,
67108
pack::data::iter::CompressedBytesMode::CRC32,
68109
)?;
69110
let pack_kind = pack_entries_iter.kind();
70111
let num_objects = pack_entries_iter.size_hint().0;
71112
let pack_entries_iter =
72113
git_features::parallel::EagerIterIf::new(|| num_objects > 25_000, pack_entries_iter, 5_000, 5);
73114

74-
let (outcome, data_path, index_path) = match directory {
115+
let (outcome, data_path, index_path) =
116+
pack::Bundle::inner_write(directory, progress, options, data_file, data_path, pack_entries_iter)?;
117+
118+
Ok(Outcome {
119+
index: outcome,
120+
pack_kind,
121+
data_path,
122+
index_path,
123+
})
124+
}
125+
126+
fn inner_write<P, I>(
127+
directory: Option<impl AsRef<Path>>,
128+
mut progress: P,
129+
Options {
130+
thread_limit,
131+
iteration_mode: _,
132+
index_kind,
133+
}: Options,
134+
data_file: Arc<parking_lot::Mutex<NamedTempFile>>,
135+
data_path: PathBuf,
136+
pack_entries_iter: I,
137+
) -> Result<(pack::index::write::Outcome, Option<PathBuf>, Option<PathBuf>), Error>
138+
where
139+
I: Iterator<Item = Result<pack::data::iter::Entry, pack::data::iter::Error>>,
140+
P: Progress,
141+
<P as Progress>::SubProgress: Send + 'static,
142+
<<P as Progress>::SubProgress as Progress>::SubProgress: Send,
143+
{
144+
let indexing_progress = progress.add_child("create index file");
145+
Ok(match directory {
75146
Some(directory) => {
76147
let directory = directory.as_ref();
77148
let mut index_file = NamedTempFile::new_in(directory)?;
@@ -115,13 +186,6 @@ impl pack::Bundle {
115186
None,
116187
None,
117188
),
118-
};
119-
120-
Ok(Outcome {
121-
index: outcome,
122-
pack_kind,
123-
data_path,
124-
index_path,
125189
})
126190
}
127191
}

Diff for: git-odb/src/pack/bundle/write/types.rs

+12
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,15 @@ where
3636
Ok(bytes_read)
3737
}
3838
}
39+
impl<R> io::BufRead for PassThrough<R>
40+
where
41+
R: io::BufRead,
42+
{
43+
fn fill_buf(&mut self) -> io::Result<&[u8]> {
44+
self.reader.fill_buf()
45+
}
46+
47+
fn consume(&mut self, amt: usize) {
48+
self.reader.consume(amt)
49+
}
50+
}

Diff for: git-odb/tests/pack/bundle.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ mod write_to_directory {
139139
pack_file: &str,
140140
) -> Result<bundle::write::Outcome, Box<dyn std::error::Error>> {
141141
let pack_file = fs::File::open(fixture_path(pack_file))?;
142-
pack::Bundle::write_to_directory(
142+
pack::Bundle::write_to_directory_eagerly(
143143
pack_file,
144144
None,
145145
directory,

Diff for: git-protocol/src/fetch/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ where
180180
return Ok(());
181181
}
182182

183-
Response::check_required_features(&fetch_features)?;
183+
Response::check_required_features(protocol_version, &fetch_features)?;
184184
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
185185
let mut arguments = Arguments::new(protocol_version, fetch_features)?;
186186
let mut previous_response = None::<Response>;

Diff for: git-protocol/src/fetch/response.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,20 @@ impl Response {
8282
pub fn has_pack(&self) -> bool {
8383
self.has_pack
8484
}
85-
pub fn check_required_features(features: &[Feature]) -> Result<(), Error> {
86-
let has = |name: &str| features.iter().any(|f| f.0 == name);
87-
// Let's focus on V2 standards, and simply not support old servers to keep our code simpler
88-
if !has("multi_ack_detailed") {
89-
return Err(Error::MissingServerCapability("multi_ack_detailed"));
90-
}
91-
// It's easy to NOT do sideband for us, but then again, everyone supports it.
92-
if !has("side-band") && !has("side-band-64k") {
93-
return Err(Error::MissingServerCapability("side-band OR side-band-64k"));
85+
pub fn check_required_features(version: Protocol, features: &[Feature]) -> Result<(), Error> {
86+
match version {
87+
Protocol::V1 => {
88+
let has = |name: &str| features.iter().any(|f| f.0 == name);
89+
// Let's focus on V2 standards, and simply not support old servers to keep our code simpler
90+
if !has("multi_ack_detailed") {
91+
return Err(Error::MissingServerCapability("multi_ack_detailed"));
92+
}
93+
// It's easy to NOT do sideband for us, but then again, everyone supports it.
94+
if !has("side-band") && !has("side-band-64k") {
95+
return Err(Error::MissingServerCapability("side-band OR side-band-64k"));
96+
}
97+
}
98+
Protocol::V2 => {}
9499
}
95100
Ok(())
96101
}

Diff for: gitoxide-core/src/pack/index.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ where
9090
Some(pack) => {
9191
let pack_len = pack.metadata()?.len();
9292
let pack_file = fs::File::open(pack)?;
93-
pack::Bundle::write_to_directory(pack_file, Some(pack_len), directory, progress, options)
93+
pack::Bundle::write_to_directory_eagerly(pack_file, Some(pack_len), directory, progress, options)
9494
}
9595
None => {
9696
let stdin = io::stdin();
97-
pack::Bundle::write_to_directory(stdin, None, directory, progress, options)
97+
pack::Bundle::write_to_directory_eagerly(stdin, None, directory, progress, options)
9898
}
9999
}
100100
.with_context(|| "Failed to write pack and index")?;

Diff for: gitoxide-core/src/pack/receive.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use crate::{OutputFormat, Protocol};
22
use git_features::progress::Progress;
3+
use git_odb::pack;
34
use git_protocol::fetch::{Action, Arguments, Ref, Response};
45
use std::{io, io::BufRead, path::PathBuf};
56

7+
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=2;
8+
69
pub struct Context<W: io::Write> {
710
pub thread_limit: Option<usize>,
811
pub format: OutputFormat,
@@ -19,22 +22,28 @@ impl<W: io::Write> git_protocol::fetch::Delegate for CloneDelegate<W> {
1922
for r in refs {
2023
arguments.want(r.unpack_common().1.to_borrowed());
2124
}
22-
Action::Continue
25+
Action::Close
2326
}
2427

25-
fn receive_pack<P>(&mut self, input: impl BufRead, progress: P, refs: &[Ref], previous: &Response) -> io::Result<()>
28+
fn receive_pack<P>(
29+
&mut self,
30+
input: impl BufRead,
31+
progress: P,
32+
_refs: &[Ref],
33+
_previous: &Response,
34+
) -> io::Result<()>
2635
where
2736
P: Progress,
2837
<P as Progress>::SubProgress: Send + 'static,
2938
<<P as Progress>::SubProgress as Progress>::SubProgress: Send + 'static,
3039
{
31-
let options = git_odb::pack::bundle::write::Options {
40+
let options = pack::bundle::write::Options {
3241
thread_limit: self.ctx.thread_limit,
33-
index_kind: git_odb::pack::index::Kind::V2,
34-
iteration_mode: git_odb::pack::data::iter::Mode::Verify,
42+
index_kind: pack::index::Kind::V2,
43+
iteration_mode: pack::data::iter::Mode::Verify,
3544
};
3645
let outcome =
37-
git_odb::pack::bundle::Bundle::write_to_directory(input, None, self.directory.take(), progress, options)
46+
pack::bundle::Bundle::write_stream_to_directory(input, None, self.directory.take(), progress, options)
3847
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
3948
writeln!(self.ctx.out, "{:?}", outcome)?;
4049
Ok(())

Diff for: src/plumbing/lean/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn main() -> Result<()> {
5454
url,
5555
directory,
5656
}) => {
57-
let (_handle, progress) = prepare(verbose, "pack-receive", None);
57+
let (_handle, progress) = prepare(verbose, "pack-receive", core::pack::receive::PROGRESS_RANGE);
5858
core::pack::receive(
5959
protocol,
6060
&url,

Diff for: tasks.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* **gixp-pack-receive**
1111
* [ ] hookup `git-protocol` with delegate to allow for receiving full packs
1212
* [ ] **gixp-pack-receive** may optionally write received refs to the specified directory
13+
* [ ] json support
1314
* [ ] journey tests for each connection method
1415
* [ ] file
1516
* [ ] git

0 commit comments

Comments
 (0)