Skip to content

Commit 531dd19

Browse files
committed
Merge branch 'shallow-protocol'
2 parents 01277a6 + 9723e1a commit 531dd19

File tree

36 files changed

+959
-67
lines changed

36 files changed

+959
-67
lines changed

crate-status.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,10 +637,14 @@ See its [README.md](https://github.com/Byron/gitoxide/blob/main/gix-lock/README.
637637
* [x] find remote itself
638638
- [ ] respect `branch.<name>.merge` in the returned remote.
639639
* **remotes**
640-
* [ ] clone
640+
* [x] clone
641641
* [ ] shallow
642+
* [ ] include-tags when shallow is used (needs separate fetch)
643+
* [ ] prune non-existing shallow commits
642644
* [ ] [bundles](https://git-scm.com/docs/git-bundle)
643645
* [x] fetch
646+
* [ ] 'ref-in-want'
647+
* [ ] standard negotiation algorithms (right now we only have a 'naive' one)
644648
* [ ] push
645649
* [x] ls-refs
646650
* [x] ls-refs with ref-spec filter

gitoxide-core/src/repository/clone.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub struct Options {
55
pub bare: bool,
66
pub handshake_info: bool,
77
pub no_tags: bool,
8+
pub shallow: gix::remote::fetch::Shallow,
89
}
910

1011
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;
@@ -30,6 +31,7 @@ pub(crate) mod function {
3031
handshake_info,
3132
bare,
3233
no_tags,
34+
shallow,
3335
}: Options,
3436
) -> anyhow::Result<()>
3537
where
@@ -66,8 +68,9 @@ pub(crate) mod function {
6668
if no_tags {
6769
prepare = prepare.configure_remote(|r| Ok(r.with_fetch_tags(gix::remote::fetch::Tags::None)));
6870
}
69-
let (mut checkout, fetch_outcome) =
70-
prepare.fetch_then_checkout(&mut progress, &gix::interrupt::IS_INTERRUPTED)?;
71+
let (mut checkout, fetch_outcome) = prepare
72+
.with_shallow(shallow)
73+
.fetch_then_checkout(&mut progress, &gix::interrupt::IS_INTERRUPTED)?;
7174

7275
let (repo, outcome) = if bare {
7376
(checkout.persist(), None)

gitoxide-core/src/repository/fetch.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct Options {
88
pub remote: Option<String>,
99
/// If non-empty, override all ref-specs otherwise configured in the remote
1010
pub ref_specs: Vec<BString>,
11+
pub shallow: gix::remote::fetch::Shallow,
1112
pub handshake_info: bool,
1213
}
1314

@@ -30,6 +31,7 @@ pub(crate) mod function {
3031
dry_run,
3132
remote,
3233
handshake_info,
34+
shallow,
3335
ref_specs,
3436
}: Options,
3537
) -> anyhow::Result<()>
@@ -50,6 +52,7 @@ pub(crate) mod function {
5052
.connect(gix::remote::Direction::Fetch, progress)?
5153
.prepare_fetch(Default::default())?
5254
.with_dry_run(dry_run)
55+
.with_shallow(shallow)
5356
.receive(&gix::interrupt::IS_INTERRUPTED)?;
5457

5558
if handshake_info {

gix-packetline/src/read/sidebands/async_io.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ where
170170
}
171171

172172
/// Read a packet line as string line.
173-
pub fn read_line<'b>(&'b mut self, buf: &'b mut String) -> ReadLineFuture<'a, 'b, T, F> {
173+
pub fn read_line_to_string<'b>(&'b mut self, buf: &'b mut String) -> ReadLineFuture<'a, 'b, T, F> {
174174
ReadLineFuture { parent: self, buf }
175175
}
176176

gix-packetline/src/read/sidebands/blocking_io.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ where
109109
);
110110
self.parent.read_line()
111111
}
112+
113+
/// Like `BufRead::read_line()`, but will only read one packetline at a time.
114+
///
115+
/// It will also be easier to call as sometimes it's unclear which implementation we get on a type like this with
116+
/// plenty of generic parameters.
117+
pub fn read_line_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
118+
assert_eq!(
119+
self.cap, 0,
120+
"we don't support partial buffers right now - read-line must be used consistently"
121+
);
122+
let line = std::str::from_utf8(self.fill_buf()?).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
123+
buf.push_str(line);
124+
let bytes = line.len();
125+
self.cap = 0;
126+
Ok(bytes)
127+
}
112128
}
113129

114130
impl<'a, T, F> BufRead for WithSidebands<'a, T, F>
@@ -168,18 +184,6 @@ where
168184
fn consume(&mut self, amt: usize) {
169185
self.pos = std::cmp::min(self.pos + amt, self.cap);
170186
}
171-
172-
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
173-
assert_eq!(
174-
self.cap, 0,
175-
"we don't support partial buffers right now - read-line must be used consistently"
176-
);
177-
let line = std::str::from_utf8(self.fill_buf()?).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
178-
buf.push_str(line);
179-
let bytes = line.len();
180-
self.cap = 0;
181-
Ok(bytes)
182-
}
183187
}
184188

185189
impl<'a, T, F> io::Read for WithSidebands<'a, T, F>

gix-packetline/tests/read/sideband.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#[cfg(feature = "blocking-io")]
2-
use std::io::{BufRead, Read};
2+
use std::io::Read;
33

44
use bstr::{BString, ByteSlice};
55
#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))]
@@ -106,16 +106,16 @@ async fn read_line_trait_method_reads_one_packet_line_at_a_time() -> crate::Resu
106106

107107
let mut out = String::new();
108108
let mut r = rd.as_read();
109-
r.read_line(&mut out).await?;
109+
r.read_line_to_string(&mut out).await?;
110110
assert_eq!(out, "808e50d724f604f69ab93c6da2919c014667bedb HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/master object-format=sha1 agent=git/2.28.0\n");
111111
out.clear();
112-
r.read_line(&mut out).await?;
112+
r.read_line_to_string(&mut out).await?;
113113
assert_eq!(out, "808e50d724f604f69ab93c6da2919c014667bedb refs/heads/master\n");
114114
out.clear();
115-
r.read_line(&mut out).await?;
115+
r.read_line_to_string(&mut out).await?;
116116
assert_eq!(out, "", "flush means empty lines…");
117117
out.clear();
118-
r.read_line(&mut out).await?;
118+
r.read_line_to_string(&mut out).await?;
119119
assert_eq!(out, "", "…which can't be overcome unless the reader is reset");
120120
assert_eq!(
121121
r.stopped_at(),
@@ -127,18 +127,18 @@ async fn read_line_trait_method_reads_one_packet_line_at_a_time() -> crate::Resu
127127
rd.reset();
128128

129129
let mut r = rd.as_read();
130-
r.read_line(&mut out).await?;
130+
r.read_line_to_string(&mut out).await?;
131131
assert_eq!(out, "NAK\n");
132132

133133
drop(r);
134134

135135
let mut r = rd.as_read_with_sidebands(|_, _| ());
136136
out.clear();
137-
r.read_line(&mut out).await?;
137+
r.read_line_to_string(&mut out).await?;
138138
assert_eq!(out, "&");
139139

140140
out.clear();
141-
r.read_line(&mut out).await?;
141+
r.read_line_to_string(&mut out).await?;
142142
assert_eq!(out, "");
143143

144144
Ok(())
@@ -199,7 +199,7 @@ async fn peek_past_an_actual_eof_is_an_error() -> crate::Result {
199199
assert_eq!(res.expect("one line")??, b"ERR e");
200200

201201
let mut buf = String::new();
202-
reader.read_line(&mut buf).await?;
202+
reader.read_line_to_string(&mut buf).await?;
203203
assert_eq!(
204204
buf, "ERR e",
205205
"by default ERR lines won't propagate as failure but are merely text"
@@ -223,7 +223,7 @@ async fn peek_past_a_delimiter_is_no_error() -> crate::Result {
223223
assert_eq!(res.expect("one line")??, b"hello");
224224

225225
let mut buf = String::new();
226-
reader.read_line(&mut buf).await?;
226+
reader.read_line_to_string(&mut buf).await?;
227227
assert_eq!(buf, "hello");
228228

229229
let res = reader.peek_data_line().await;

gix-protocol/src/fetch/response/async_io.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::io;
22

3-
use futures_lite::AsyncBufReadExt;
43
use gix_transport::{client, Protocol};
54

65
use crate::fetch::{
@@ -16,7 +15,7 @@ async fn parse_v2_section<T>(
1615
parse: impl Fn(&str) -> Result<T, response::Error>,
1716
) -> Result<bool, response::Error> {
1817
line.clear();
19-
while reader.read_line(line).await? != 0 {
18+
while reader.readline_str(line).await? != 0 {
2019
res.push(parse(line)?);
2120
line.clear();
2221
}
@@ -62,7 +61,7 @@ impl Response {
6261
Some(client::MessageKind::Flush),
6362
"If this isn't a flush packet, we don't know what's going on"
6463
);
65-
reader.read_line(&mut line).await?;
64+
reader.readline_str(&mut line).await?;
6665
reader.reset(Protocol::V1);
6766
match reader.peek_data_line().await {
6867
Some(Ok(Ok(line))) => String::from_utf8_lossy(line),
@@ -76,7 +75,11 @@ impl Response {
7675
if Response::parse_v1_ack_or_shallow_or_assume_pack(&mut acks, &mut shallows, &peeked_line) {
7776
break 'lines true;
7877
}
79-
assert_ne!(reader.read_line(&mut line).await?, 0, "consuming a peeked line works");
78+
assert_ne!(
79+
reader.readline_str(&mut line).await?,
80+
0,
81+
"consuming a peeked line works"
82+
);
8083
};
8184
Ok(Response {
8285
acks,
@@ -94,7 +97,7 @@ impl Response {
9497
let mut wanted_refs = Vec::<WantedRef>::new();
9598
let has_pack = 'section: loop {
9699
line.clear();
97-
if reader.read_line(&mut line).await? == 0 {
100+
if reader.readline_str(&mut line).await? == 0 {
98101
return Err(response::Error::Io(io::Error::new(
99102
io::ErrorKind::UnexpectedEof,
100103
"Could not read message headline",

gix-protocol/src/fetch/response/blocking_io.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn parse_v2_section<T>(
1515
parse: impl Fn(&str) -> Result<T, response::Error>,
1616
) -> Result<bool, response::Error> {
1717
line.clear();
18-
while reader.read_line(line)? != 0 {
18+
while reader.readline_str(line)? != 0 {
1919
res.push(parse(line)?);
2020
line.clear();
2121
}
@@ -61,7 +61,7 @@ impl Response {
6161
Some(client::MessageKind::Flush),
6262
"If this isn't a flush packet, we don't know what's going on"
6363
);
64-
reader.read_line(&mut line)?;
64+
reader.readline_str(&mut line)?;
6565
reader.reset(Protocol::V1);
6666
match reader.peek_data_line() {
6767
Some(Ok(Ok(line))) => String::from_utf8_lossy(line),
@@ -75,7 +75,7 @@ impl Response {
7575
if Response::parse_v1_ack_or_shallow_or_assume_pack(&mut acks, &mut shallows, &peeked_line) {
7676
break 'lines true;
7777
}
78-
assert_ne!(reader.read_line(&mut line)?, 0, "consuming a peeked line works");
78+
assert_ne!(reader.readline_str(&mut line)?, 0, "consuming a peeked line works");
7979
};
8080
Ok(Response {
8181
acks,
@@ -93,7 +93,7 @@ impl Response {
9393
let mut wanted_refs = Vec::<WantedRef>::new();
9494
let has_pack = 'section: loop {
9595
line.clear();
96-
if reader.read_line(&mut line)? == 0 {
96+
if reader.readline_str(&mut line)? == 0 {
9797
return Err(response::Error::Io(io::Error::new(
9898
io::ErrorKind::UnexpectedEof,
9999
"Could not read message headline",

gix-protocol/src/handshake/refs/tests.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,20 @@ impl<'a> gix_transport::client::ReadlineBufRead for Fixture<'a> {
170170
self.0 = lines.as_bytes();
171171
Some(Ok(Ok(gix_packetline::PacketLineRef::Data(res))))
172172
}
173+
174+
fn readline_str(&mut self, line: &mut String) -> std::io::Result<usize> {
175+
use bstr::{BStr, ByteSlice};
176+
let bytes: &BStr = self.0.into();
177+
let mut lines = bytes.lines();
178+
let res = match lines.next() {
179+
None => return Ok(0),
180+
Some(line) => line,
181+
};
182+
self.0 = lines.as_bytes();
183+
let len = res.len();
184+
line.push_str(res.to_str().expect("valid UTF8 in fixture"));
185+
Ok(len)
186+
}
173187
}
174188

175189
#[cfg(feature = "async-client")]
@@ -220,4 +234,17 @@ impl<'a> gix_transport::client::ReadlineBufRead for Fixture<'a> {
220234
self.0 = lines.as_bytes();
221235
Some(Ok(Ok(gix_packetline::PacketLineRef::Data(res))))
222236
}
237+
async fn readline_str(&mut self, line: &mut String) -> std::io::Result<usize> {
238+
use bstr::{BStr, ByteSlice};
239+
let bytes: &BStr = self.0.into();
240+
let mut lines = bytes.lines();
241+
let res = match lines.next() {
242+
None => return Ok(0),
243+
Some(line) => line,
244+
};
245+
self.0 = lines.as_bytes();
246+
let len = res.len();
247+
line.push_str(res.to_str().expect("valid UTF8 in fixture"));
248+
Ok(len)
249+
}
223250
}

0 commit comments

Comments
 (0)