Skip to content

Commit

Permalink
feat: Add --reference/-r flag to gixp pack-receive (#200)
Browse files Browse the repository at this point in the history
Right now it only works with 'ref-in-want', but in future it could
actually act as a filter.
  • Loading branch information
Byron committed Sep 19, 2021
1 parent 454c840 commit 637d12c
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 20 deletions.
49 changes: 39 additions & 10 deletions gitoxide-core/src/pack/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,17 @@ struct CloneDelegate<W> {
directory: Option<PathBuf>,
refs_directory: Option<PathBuf>,
ref_filter: Option<&'static [&'static str]>,
wanted_refs: Vec<BString>,
}
static FILTER: &[&str] = &["HEAD", "refs/tags", "refs/heads"];

fn remote_supports_ref_in_want(server: &Capabilities) -> bool {
server
.capability("fetch")
.and_then(|cap| cap.supports("ref-in-want"))
.unwrap_or(false)
}

impl<W> protocol::fetch::DelegateBlocking for CloneDelegate<W> {
fn prepare_ls_refs(
&mut self,
Expand All @@ -45,16 +53,26 @@ impl<W> protocol::fetch::DelegateBlocking for CloneDelegate<W> {
if server.contains("ls-refs") {
arguments.extend(FILTER.iter().map(|r| format!("ref-prefix {}", r).into()));
}
Ok(LsRefsAction::Continue)
Ok(if self.wanted_refs.is_empty() {
LsRefsAction::Continue
} else {
LsRefsAction::Skip
})
}

fn prepare_fetch(
&mut self,
version: transport::Protocol,
_server: &Capabilities,
server: &Capabilities,
_features: &mut Vec<(&str, Option<&str>)>,
_refs: &[Ref],
) -> io::Result<Action> {
if !self.wanted_refs.is_empty() && !remote_supports_ref_in_want(server) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Want to get specific refs, but remote doesn't support this capability",
));
}
if version == transport::Protocol::V1 {
self.ref_filter = Some(FILTER);
}
Expand All @@ -67,15 +85,21 @@ impl<W> protocol::fetch::DelegateBlocking for CloneDelegate<W> {
arguments: &mut Arguments,
_previous_response: Option<&Response>,
) -> io::Result<Action> {
for r in refs {
let (path, id) = r.unpack();
match self.ref_filter {
Some(ref_prefixes) => {
if ref_prefixes.iter().any(|prefix| path.starts_with_str(prefix)) {
arguments.want(id);
if self.wanted_refs.is_empty() {
for r in refs {
let (path, id) = r.unpack();
match self.ref_filter {
Some(ref_prefixes) => {
if ref_prefixes.iter().any(|prefix| path.starts_with_str(prefix)) {
arguments.want(id);
}
}
None => arguments.want(id),
}
None => arguments.want(id),
}
} else {
for r in &self.wanted_refs {
arguments.want_ref(r.as_ref())
}
}
Ok(Action::Cancel)
Expand All @@ -87,6 +111,7 @@ mod blocking_io {
use std::{io, io::BufRead, path::PathBuf};

use git_repository::{
bstr::BString,
protocol,
protocol::fetch::{Ref, Response},
Progress,
Expand Down Expand Up @@ -119,6 +144,7 @@ mod blocking_io {
url: &str,
directory: Option<PathBuf>,
refs_directory: Option<PathBuf>,
wanted_refs: Vec<BString>,
progress: P,
ctx: Context<W>,
) -> anyhow::Result<()> {
Expand All @@ -128,6 +154,7 @@ mod blocking_io {
directory,
refs_directory,
ref_filter: None,
wanted_refs,
};
protocol::fetch(
transport,
Expand All @@ -150,7 +177,7 @@ mod async_io {
use async_trait::async_trait;
use futures_io::AsyncBufRead;
use git_repository::{
objs::bstr::{BString, ByteSlice},
bstr::{BString, ByteSlice},
odb::pack,
protocol,
protocol::fetch::{Ref, Response},
Expand Down Expand Up @@ -185,6 +212,7 @@ mod async_io {
url: &str,
directory: Option<PathBuf>,
refs_directory: Option<PathBuf>,
wanted_refs: Vec<BString>,
progress: P,
ctx: Context<W>,
) -> anyhow::Result<()> {
Expand All @@ -194,6 +222,7 @@ mod async_io {
directory,
refs_directory,
ref_filter: None,
wanted_refs,
};
blocking::unblock(move || {
futures_lite::future::block_on(protocol::fetch(
Expand Down
2 changes: 2 additions & 0 deletions src/plumbing/lean/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub fn main() -> Result<()> {
protocol,
url,
directory,
refs,
refs_directory,
}) => {
let (_handle, progress) = prepare(verbose, "pack-receive", core::pack::receive::PROGRESS_RANGE);
Expand All @@ -100,6 +101,7 @@ pub fn main() -> Result<()> {
&url,
directory,
refs_directory,
refs.into_iter().map(|s| s.into()).collect(),
DoOrDiscard::from(progress),
core::pack::receive::Context {
thread_limit,
Expand Down
8 changes: 7 additions & 1 deletion src/plumbing/lean/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub struct PackReceive {
/// the directory into which to write references. Existing files will be overwritten.
///
/// Note that the directory will be created if needed.
#[argh(option, short = 'r')]
#[argh(option, short = 'd')]
pub refs_directory: Option<PathBuf>,

/// the URLs or path from which to receive the pack.
Expand All @@ -111,6 +111,12 @@ pub struct PackReceive {
#[argh(positional)]
pub url: String,

/// if set once or more times, these references will be fetched instead of all advertised ones.
///
/// Note that this requires a reasonably modern git server.
#[argh(option, long = "reference", short = 'r')]
pub refs: Vec<String>,

/// the directory into which to write the received pack and index.
///
/// If unset, they will be discarded.
Expand Down
2 changes: 2 additions & 0 deletions src/plumbing/pretty/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn main() -> Result<()> {
protocol,
url,
directory,
refs,
refs_directory,
} => prepare_and_run(
"pack-receive",
Expand All @@ -96,6 +97,7 @@ pub fn main() -> Result<()> {
&url,
directory,
refs_directory,
refs.into_iter().map(|r| r.into()).collect(),
git_features::progress::DoOrDiscard::from(progress),
core::pack::receive::Context {
thread_limit,
Expand Down
8 changes: 7 additions & 1 deletion src/plumbing/pretty/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,20 @@ pub enum Subcommands {
/// the directory into which to write references. Existing files will be overwritten.
///
/// Note that the directory will be created if needed.
#[clap(long, short = 'r')]
#[clap(long, short = 'd')]
refs_directory: Option<PathBuf>,

/// The URLs or path from which to receive the pack.
///
/// See here for a list of supported URLs: <https://www.git-scm.com/docs/git-clone#_git_urls>
url: String,

/// If set once or more times, these references will be fetched instead of all advertised ones.
///
/// Note that this requires a reasonably modern git server.
#[clap(long = "reference", short = 'r')]
refs: Vec<String>,

/// The directory into which to write the received pack and index.
///
/// If unset, they will be discarded.
Expand Down
32 changes: 24 additions & 8 deletions tests/journey/gixp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,18 @@ title "gixp pack-receive"
launch-git-daemon
(with "version 1"
(with "NO output directory"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output" \
expect_run $SUCCESSFULLY "$exe_plumbing" pack-receive -p 1 git://localhost/
}
(with "no wanted refs"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output" \
expect_run $SUCCESSFULLY "$exe_plumbing" pack-receive -p 1 git://localhost/
}
)
(with "wanted refs"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output-wanted-ref-p1" \
expect_run $WITH_FAILURE "$exe_plumbing" pack-receive -p 1 git://localhost/ -r =refs/heads/main
}
)
)
(with "output directory"
mkdir out
Expand All @@ -133,10 +141,18 @@ title "gixp pack-receive"
)
(with "version 2"
(with "NO output directory"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output" \
expect_run $SUCCESSFULLY "$exe_plumbing" pack-receive -p 2 git://localhost/
}
(with "NO wanted refs"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output" \
expect_run $SUCCESSFULLY "$exe_plumbing" pack-receive -p 2 git://localhost/
}
)
(with "wanted refs"
it "generates the correct output" && {
WITH_SNAPSHOT="$snapshot/file-v-any-no-output-single-ref" \
expect_run $WITH_FAILURE "$exe_plumbing" pack-receive -p 2 git://localhost/ -r refs/heads/main
}
)
)
(with "output directory"
it "generates the correct output" && {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Error: Could not access repository or failed to read streaming pack file

Caused by:
Want to get specific refs, but remote doesn't support this capability
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Error: Could not access repository or failed to read streaming pack file

Caused by:
Want to get specific refs, but remote doesn't support this capability

0 comments on commit 637d12c

Please sign in to comment.