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
5 pub bare: bool,5 pub bare: bool,
6 pub handshake_info: bool,6 pub handshake_info: bool,
7 pub no_tags: bool,7 pub no_tags: bool,
8 pub shallow: gix::remote::fetch::Shallow,
8}9}
9 10
10pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;11pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;
@@ -30,6 +31,7 @@ pub(crate) mod function { · 19 unmodified lines
30 handshake_info,31 handshake_info,
31 bare,32 bare,
32 no_tags,33 no_tags,
34 shallow,
33 }: Options,35 }: Options,
34 ) -> anyhow::Result<()>36 ) -> anyhow::Result<()>
35 where37 where
@@ -66,8 +68,9 @@ pub(crate) mod function { · 30 unmodified lines
66 if no_tags {68 if no_tags {
67 prepare = prepare.configure_remote(|r| Ok(r.with_fetch_tags(gix::remote::fetch::Tags::None)));69 prepare = prepare.configure_remote(|r| Ok(r.with_fetch_tags(gix::remote::fetch::Tags::None)));
68 }70 }
69 let (mut checkout, fetch_outcome) =71 let (mut checkout, fetch_outcome) = prepare
70 prepare.fetch_then_checkout(&mut progress, &gix::interrupt::IS_INTERRUPTED)?;72 .with_shallow(shallow)
73 .fetch_then_checkout(&mut progress, &gix::interrupt::IS_INTERRUPTED)?;
71 74
72 let (repo, outcome) = if bare {75 let (repo, outcome) = if bare {
73 (checkout.persist(), None)76 (checkout.persist(), None)
gitoxide-core/src/repository/fetch.rs+3−0
@@ -8,6 +8,7 @@ pub struct Options { · 7 unmodified lines
8 pub remote: Option<String>,8 pub remote: Option<String>,
9 /// If non-empty, override all ref-specs otherwise configured in the remote9 /// If non-empty, override all ref-specs otherwise configured in the remote
10 pub ref_specs: Vec<BString>,10 pub ref_specs: Vec<BString>,
11 pub shallow: gix::remote::fetch::Shallow,
11 pub handshake_info: bool,12 pub handshake_info: bool,
12}13}
13 14
@@ -30,6 +31,7 @@ pub(crate) mod function { · 16 unmodified lines
30 dry_run,31 dry_run,
31 remote,32 remote,
32 handshake_info,33 handshake_info,
34 shallow,
33 ref_specs,35 ref_specs,
34 }: Options,36 }: Options,
35 ) -> anyhow::Result<()>37 ) -> anyhow::Result<()>
@@ -50,6 +52,7 @@ pub(crate) mod function { · 14 unmodified lines
50 .connect(gix::remote::Direction::Fetch, progress)?52 .connect(gix::remote::Direction::Fetch, progress)?
51 .prepare_fetch(Default::default())?53 .prepare_fetch(Default::default())?
52 .with_dry_run(dry_run)54 .with_dry_run(dry_run)
55 .with_shallow(shallow)
53 .receive(&gix::interrupt::IS_INTERRUPTED)?;56 .receive(&gix::interrupt::IS_INTERRUPTED)?;
54 57
55 if handshake_info {58 if handshake_info {
src/plumbing/main.rs+4−0
@@ -130,6 +130,7 @@ pub fn main() -> Result<()> { · 129 unmodified lines
130 bare,130 bare,
131 no_tags,131 no_tags,
132 remote,132 remote,
133 shallow,
133 directory,134 directory,
134 }) => {135 }) => {
135 let opts = core::repository::clone::Options {136 let opts = core::repository::clone::Options {
@@ -137,6 +138,7 @@ pub fn main() -> Result<()> { · 1 unmodified line
137 bare,138 bare,
138 handshake_info,139 handshake_info,
139 no_tags,140 no_tags,
141 shallow: shallow.into(),
140 };142 };
141 prepare_and_run(143 prepare_and_run(
142 "clone",144 "clone",
@@ -152,6 +154,7 @@ pub fn main() -> Result<()> { · 9 unmodified lines
152 dry_run,154 dry_run,
153 handshake_info,155 handshake_info,
154 remote,156 remote,
157 shallow,
155 ref_spec,158 ref_spec,
156 }) => {159 }) => {
157 let opts = core::repository::fetch::Options {160 let opts = core::repository::fetch::Options {
@@ -159,6 +162,7 @@ pub fn main() -> Result<()> { · 1 unmodified line
159 dry_run,162 dry_run,
160 remote,163 remote,
161 handshake_info,164 handshake_info,
165 shallow: shallow.into(),
162 ref_specs: ref_spec,166 ref_specs: ref_spec,
163 };167 };
164 prepare_and_run(168 prepare_and_run(
src/plumbing/options/mod.rs+87−0
@@ -135,6 +135,9 @@ pub mod config { · 134 unmodified lines
135 135
136#[cfg(feature = "gitoxide-core-blocking-client")]136#[cfg(feature = "gitoxide-core-blocking-client")]
137pub mod fetch {137pub mod fetch {
138 use gix::remote::fetch::Shallow;
139 use std::num::NonZeroU32;
140
138 #[derive(Debug, clap::Parser)]141 #[derive(Debug, clap::Parser)]
139 pub struct Platform {142 pub struct Platform {
140 /// Don't change the local repository, but otherwise try to be as accurate as possible.143 /// 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
145 #[clap(long, short = 'H')]148 #[clap(long, short = 'H')]
146 pub handshake_info: bool,149 pub handshake_info: bool,
147 150
151 #[clap(flatten)]
152 pub shallow: ShallowOptions,
153
148 /// The name of the remote to connect to, or the url of the remote to connect to directly.154 /// The name of the remote to connect to, or the url of the remote to connect to directly.
149 ///155 ///
150 /// If unset, the current branch will determine the remote.156 /// If unset, the current branch will determine the remote.
@@ -155,10 +161,56 @@ pub mod fetch { · 4 unmodified lines
155 #[clap(value_parser = crate::shared::AsBString)]161 #[clap(value_parser = crate::shared::AsBString)]
156 pub ref_spec: Vec<gix::bstr::BString>,162 pub ref_spec: Vec<gix::bstr::BString>,
157 }163 }
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 }
158}208}
159 209
160#[cfg(feature = "gitoxide-core-blocking-client")]210#[cfg(feature = "gitoxide-core-blocking-client")]
161pub mod clone {211pub mod clone {
212 use gix::remote::fetch::Shallow;
213 use std::num::NonZeroU32;
162 use std::{ffi::OsString, path::PathBuf};214 use std::{ffi::OsString, path::PathBuf};
163 215
164 #[derive(Debug, clap::Parser)]216 #[derive(Debug, clap::Parser)]
@@ -175,12 +227,47 @@ pub mod clone { · 10 unmodified lines
175 #[clap(long)]227 #[clap(long)]
176 pub no_tags: bool,228 pub no_tags: bool,
177 229
230 #[clap(flatten)]
231 pub shallow: ShallowOptions,
232
178 /// The url of the remote to connect to, like `https://github.com/byron/gitoxide`.233 /// The url of the remote to connect to, like `https://github.com/byron/gitoxide`.
179 pub remote: OsString,234 pub remote: OsString,
180 235
181 /// The directory to initialize with the new repository and to which all data should be written.236 /// The directory to initialize with the new repository and to which all data should be written.
182 pub directory: Option<PathBuf>,237 pub directory: Option<PathBuf>,
183 }238 }
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 }
184}271}
185 272
186#[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]273#[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
214 },214 },
215 Record {215 Record {
216 config: "clone.rejectShallow",216 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",
217 usage: NotPlanned {223 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.",
219 },225 },
220 },226 },
221 Record {227 Record {
src/shared.rs+28−2
@@ -292,7 +292,7 @@ mod clap { · 291 unmodified lines
292 }292 }
293 }293 }
294 294
295 use clap::builder::{OsStringValueParser, TypedValueParser};295 use clap::builder::{OsStringValueParser, StringValueParser, TypedValueParser};
296 296
297 #[derive(Clone)]297 #[derive(Clone)]
298 pub struct AsPathSpec;298 pub struct AsPathSpec;
@@ -306,5 +306,31 @@ mod clap { · 7 unmodified lines
306 .parse_ref(cmd, arg, value)306 .parse_ref(cmd, arg, value)
307 }307 }
308 }308 }
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 }
309}335}
310pub use self::clap::{AsBString, AsHashKind, AsOutputFormat, AsPathSpec};336pub 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