History

feat: `gix clone` and `gix fetch` with controls for shallow repositories.

Sebastian Thiel committed 3 years ago

Commit 9723e1a1 parent 93d412c6 files · +134 −5Browse files

Unified · Split

6 files
  1. gitoxide-core/src/repository/clone.rs +5 −2
  2. gitoxide-core/src/repository/fetch.rs +3 −0
  3. src/plumbing/main.rs +4 −0
  4. src/plumbing/options/mod.rs +87 −0
  5. src/plumbing/progress.rs +7 −1
  6. src/shared.rs +28 −2
gitoxide-core/src/repository/clone.rs+5−2
@@ -5,6 +5,7 @@ pub struct Options { · 4 unmodified lines
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 { · 19 unmodified lines
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 { · 30 unmodified lines
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+3−0
@@ -8,6 +8,7 @@ pub struct Options { · 7 unmodified lines
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 { · 16 unmodified lines
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 { · 14 unmodified lines
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 {
src/plumbing/main.rs+4−0
@@ -130,6 +130,7 @@ pub fn main() -> Result<()> { · 129 unmodified lines
130130 bare,
131131 no_tags,
132132 remote,
133+ shallow,
133134 directory,
134135 }) => {
135136 let opts = core::repository::clone::Options {
@@ -137,6 +138,7 @@ pub fn main() -> Result<()> { · 1 unmodified line
137138 bare,
138139 handshake_info,
139140 no_tags,
141+ shallow: shallow.into(),
140142 };
141143 prepare_and_run(
142144 "clone",
@@ -152,6 +154,7 @@ pub fn main() -> Result<()> { · 9 unmodified lines
152154 dry_run,
153155 handshake_info,
154156 remote,
157+ shallow,
155158 ref_spec,
156159 }) => {
157160 let opts = core::repository::fetch::Options {
@@ -159,6 +162,7 @@ pub fn main() -> Result<()> { · 1 unmodified line
159162 dry_run,
160163 remote,
161164 handshake_info,
165+ shallow: shallow.into(),
162166 ref_specs: ref_spec,
163167 };
164168 prepare_and_run(
src/plumbing/options/mod.rs+87−0
@@ -135,6 +135,9 @@ pub mod config { · 134 unmodified lines
135135
136136 #[cfg(feature = "gitoxide-core-blocking-client")]
137137 pub mod fetch {
138+ use gix::remote::fetch::Shallow;
139+ use std::num::NonZeroU32;
140+
138141 #[derive(Debug, clap::Parser)]
139142 pub struct Platform {
140143 /// Don't change the local repository, but otherwise try to be as accurate as possible.
@@ -145,6 +148,9 @@ pub mod fetch { · 4 unmodified lines
145148 #[clap(long, short = 'H')]
146149 pub handshake_info: bool,
147150
151+ #[clap(flatten)]
152+ pub shallow: ShallowOptions,
153+
148154 /// The name of the remote to connect to, or the url of the remote to connect to directly.
149155 ///
150156 /// If unset, the current branch will determine the remote.
@@ -155,10 +161,56 @@ pub mod fetch { · 4 unmodified lines
155161 #[clap(value_parser = crate::shared::AsBString)]
156162 pub ref_spec: Vec<gix::bstr::BString>,
157163 }
164+
165+ #[derive(Debug, clap::Parser)]
166+ pub struct ShallowOptions {
167+ /// Fetch with the history truncated to the given number of commits as seen from the remote.
168+ #[clap(long, conflicts_with_all = ["shallow_since", "shallow_exclude", "deepen", "unshallow"])]
169+ pub depth: Option<NonZeroU32>,
170+
171+ /// Extend the current shallow boundary by the given amount of commits, with 0 meaning no change.
172+ #[clap(long, value_name = "DEPTH", conflicts_with_all = ["depth", "shallow_since", "shallow_exclude", "unshallow"])]
173+ pub deepen: Option<u32>,
174+
175+ /// Cutoff all history past the given date. Can be combined with shallow-exclude.
176+ #[clap(long, value_parser = crate::shared::AsTime, value_name = "DATE", conflicts_with_all = ["depth", "deepen", "unshallow"])]
177+ pub shallow_since: Option<gix::date::Time>,
178+
179+ /// Cutoff all history past the tag-name or ref-name. Can be combined with shallow-since.
180+ #[clap(long, value_parser = crate::shared::AsPartialRefName, value_name = "REF_NAME", conflicts_with_all = ["depth", "deepen", "unshallow"])]
181+ pub shallow_exclude: Vec<gix::refs::PartialName>,
182+
183+ /// Remove the shallow boundary and fetch the entire history available on the remote.
184+ #[clap(long, conflicts_with_all = ["shallow_since", "shallow_exclude", "depth", "deepen", "unshallow"])]
185+ pub unshallow: bool,
186+ }
187+
188+ impl From<ShallowOptions> for Shallow {
189+ fn from(opts: ShallowOptions) -> Self {
190+ if let Some(depth) = opts.depth {
191+ Shallow::DepthAtRemote(depth)
192+ } else if !opts.shallow_exclude.is_empty() {
193+ Shallow::Exclude {
194+ remote_refs: opts.shallow_exclude,
195+ since_cutoff: opts.shallow_since,
196+ }
197+ } else if let Some(cutoff) = opts.shallow_since {
198+ Shallow::Since { cutoff }
199+ } else if let Some(depth) = opts.deepen {
200+ Shallow::Deepen(depth)
201+ } else if opts.unshallow {
202+ Shallow::undo()
203+ } else {
204+ Shallow::default()
205+ }
206+ }
207+ }
158208 }
159209
160210 #[cfg(feature = "gitoxide-core-blocking-client")]
161211 pub mod clone {
212+ use gix::remote::fetch::Shallow;
213+ use std::num::NonZeroU32;
162214 use std::{ffi::OsString, path::PathBuf};
163215
164216 #[derive(Debug, clap::Parser)]
@@ -175,12 +227,47 @@ pub mod clone { · 10 unmodified lines
175227 #[clap(long)]
176228 pub no_tags: bool,
177229
230+ #[clap(flatten)]
231+ pub shallow: ShallowOptions,
232+
178233 /// The url of the remote to connect to, like `https://github.com/byron/gitoxide`.
179234 pub remote: OsString,
180235
181236 /// The directory to initialize with the new repository and to which all data should be written.
182237 pub directory: Option<PathBuf>,
183238 }
239+
240+ #[derive(Debug, clap::Parser)]
241+ pub struct ShallowOptions {
242+ /// Create a shallow clone with the history truncated to the given number of commits.
243+ #[clap(long, conflicts_with_all = ["shallow_since", "shallow_exclude"])]
244+ pub depth: Option<NonZeroU32>,
245+
246+ /// Cutoff all history past the given date. Can be combined with shallow-exclude.
247+ #[clap(long, value_parser = crate::shared::AsTime, value_name = "DATE")]
248+ pub shallow_since: Option<gix::date::Time>,
249+
250+ /// Cutoff all history past the tag-name or ref-name. Can be combined with shallow-since.
251+ #[clap(long, value_parser = crate::shared::AsPartialRefName, value_name = "REF_NAME")]
252+ pub shallow_exclude: Vec<gix::refs::PartialName>,
253+ }
254+
255+ impl From<ShallowOptions> for Shallow {
256+ fn from(opts: ShallowOptions) -> Self {
257+ if let Some(depth) = opts.depth {
258+ Shallow::DepthAtRemote(depth)
259+ } else if !opts.shallow_exclude.is_empty() {
260+ Shallow::Exclude {
261+ remote_refs: opts.shallow_exclude,
262+ since_cutoff: opts.shallow_since,
263+ }
264+ } else if let Some(cutoff) = opts.shallow_since {
265+ Shallow::Since { cutoff }
266+ } else {
267+ Shallow::default()
268+ }
269+ }
270+ }
184271 }
185272
186273 #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]
src/plumbing/progress.rs+7−1
@@ -214,8 +214,14 @@ static GIT_CONFIG: &[Record] = &[ · 213 unmodified lines
214214 },
215215 Record {
216216 config: "clone.rejectShallow",
217+ usage: Planned {
218+ note: Some("probably trivial to implement once there is protocol support for shallow clones"),
219+ },
220+ },
221+ Record {
222+ config: "receive.shallowUpdate",
217223 usage: NotPlanned {
218− reason: "it's not a use-case we consider important now, but once that changes it can be implemented",
224+ reason: "it looks like a server-only setting that allows boundaries to change if refs are pushed that are outside of the boundary.",
219225 },
220226 },
221227 Record {
src/shared.rs+28−2
@@ -292,7 +292,7 @@ mod clap { · 291 unmodified lines
292292 }
293293 }
294294
295− use clap::builder::{OsStringValueParser, TypedValueParser};
295+ use clap::builder::{OsStringValueParser, StringValueParser, TypedValueParser};
296296
297297 #[derive(Clone)]
298298 pub struct AsPathSpec;
@@ -306,5 +306,31 @@ mod clap { · 7 unmodified lines
306306 .parse_ref(cmd, arg, value)
307307 }
308308 }
309+
310+ #[derive(Clone)]
311+ pub struct AsTime;
312+
313+ impl TypedValueParser for AsTime {
314+ type Value = gix::date::Time;
315+
316+ fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> {
317+ StringValueParser::new()
318+ .try_map(|arg| gix::date::parse(&arg, Some(std::time::SystemTime::now())))
319+ .parse_ref(cmd, arg, value)
320+ }
321+ }
322+
323+ #[derive(Clone)]
324+ pub struct AsPartialRefName;
325+
326+ impl TypedValueParser for AsPartialRefName {
327+ type Value = gix::refs::PartialName;
328+
329+ fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> {
330+ AsBString
331+ .try_map(gix::refs::PartialName::try_from)
332+ .parse_ref(cmd, arg, value)
333+ }
334+ }
309335 }
310−pub use self::clap::{AsBString, AsHashKind, AsOutputFormat, AsPathSpec};
336+pub use self::clap::{AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t