pub mod refs { use crate::{OutputFormat, Protocol}; use git_features::progress::Progress; use git_protocol::{ fetch::{Action, Ref}, git_transport, }; pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=2; use git_protocol::fetch::{Arguments, Response}; use std::io; #[derive(Default)] struct LsRemotes { refs: Vec<Ref>, } impl git_protocol::fetch::Delegate for LsRemotes { fn prepare_fetch( &mut self, _version: git_transport::Protocol, _server: &git_transport::client::Capabilities, _features: &mut Vec<(&str, Option<&str>)>, refs: &[Ref], ) -> Action { self.refs = refs.into(); Action::Close } fn negotiate( &mut self, _refs: &[Ref], _arguments: &mut Arguments, _previous_result: Option<&Response>, ) -> Action { unreachable!("not to be called due to Action::Close in `prepare_fetch`") } fn receive_pack( &mut self, _input: impl io::BufRead, _progress: impl Progress, _refs: &[Ref], _previous: &Response, ) -> io::Result<()> { unreachable!("not called for ls-refs") } } pub struct Context<W: io::Write> { pub thread_limit: Option<usize>, pub format: OutputFormat, pub out: W, } pub fn list( protocol: Option<Protocol>, url: &str, progress: impl Progress, ctx: Context<impl io::Write>, ) -> anyhow::Result<()> { let transport = git_transport::client::connect(url.as_bytes(), protocol.unwrap_or_default().into())?; let mut delegate = LsRemotes::default(); git_protocol::fetch(transport, &mut delegate, git_protocol::credentials::helper, progress)?; match ctx.format { OutputFormat::Human => drop(print(ctx.out, &delegate.refs)), #[cfg(feature = "serde1")] OutputFormat::Json => serde_json::to_writer_pretty( ctx.out, &delegate.refs.into_iter().map(JsonRef::from).collect::<Vec<_>>(), )?, }; Ok(()) } #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub enum JsonRef { Peeled { path: String, tag: String, object: String, }, Direct { path: String, object: String, }, Symbolic { path: String, target: String, object: String, }, } impl From<Ref> for JsonRef { fn from(value: Ref) -> Self { match value { Ref::Direct { path, object } => JsonRef::Direct { path: path.to_string(), object: object.to_string(), }, Ref::Symbolic { path, target, object } => JsonRef::Symbolic { path: path.to_string(), target: target.to_string(), object: object.to_string(), }, Ref::Peeled { path, tag, object } => JsonRef::Peeled { path: path.to_string(), tag: tag.to_string(), object: object.to_string(), }, } } } pub(crate) fn print(mut out: impl io::Write, refs: &[Ref]) -> io::Result<()> { for r in refs { match r { Ref::Direct { path, object } => writeln!(&mut out, "{} {}", object.to_sha1_hex_string(), path), Ref::Peeled { path, object, tag } => { writeln!(&mut out, "{} {} tag:{}", object.to_sha1_hex_string(), path, tag) } Ref::Symbolic { path, target, object } => writeln!( &mut out, "{} {} symref-target:{}", object.to_sha1_hex_string(), path, target ), }?; } Ok(()) }}
pub mod refs {
use crate::{OutputFormat, Protocol};
use git_features::progress::Progress;
use git_protocol::{
fetch::{Action, Ref},
git_transport,
};
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=2;
use git_protocol::fetch::{Arguments, Response};
use std::io;
#[derive(Default)]
struct LsRemotes {
refs: Vec<Ref>,
}
impl git_protocol::fetch::Delegate for LsRemotes {
fn prepare_fetch(
&mut self,
_version: git_transport::Protocol,
_server: &git_transport::client::Capabilities,
_features: &mut Vec<(&str, Option<&str>)>,
refs: &[Ref],
) -> Action {
self.refs = refs.into();
Action::Close
}
fn negotiate(
&mut self,
_refs: &[Ref],
_arguments: &mut Arguments,
_previous_result: Option<&Response>,
) -> Action {
unreachable!("not to be called due to Action::Close in `prepare_fetch`")
}
fn receive_pack(
&mut self,
_input: impl io::BufRead,
_progress: impl Progress,
_refs: &[Ref],
_previous: &Response,
) -> io::Result<()> {
unreachable!("not called for ls-refs")
}
}
pub struct Context<W: io::Write> {
pub thread_limit: Option<usize>,
pub format: OutputFormat,
pub out: W,
}
pub fn list(
protocol: Option<Protocol>,
url: &str,
progress: impl Progress,
ctx: Context<impl io::Write>,
) -> anyhow::Result<()> {
let transport = git_transport::client::connect(url.as_bytes(), protocol.unwrap_or_default().into())?;
let mut delegate = LsRemotes::default();
git_protocol::fetch(transport, &mut delegate, git_protocol::credentials::helper, progress)?;
match ctx.format {
OutputFormat::Human => drop(print(ctx.out, &delegate.refs)),
#[cfg(feature = "serde1")]
OutputFormat::Json => serde_json::to_writer_pretty(
ctx.out,
&delegate.refs.into_iter().map(JsonRef::from).collect::<Vec<_>>(),
)?,
};
Ok(())
}
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub enum JsonRef {
Peeled {
path: String,
tag: String,
object: String,
},
Direct {
path: String,
object: String,
},
Symbolic {
path: String,
target: String,
object: String,
},
}
impl From<Ref> for JsonRef {
fn from(value: Ref) -> Self {
match value {
Ref::Direct { path, object } => JsonRef::Direct {
path: path.to_string(),
object: object.to_string(),
},
Ref::Symbolic { path, target, object } => JsonRef::Symbolic {
path: path.to_string(),
target: target.to_string(),
object: object.to_string(),
},
Ref::Peeled { path, tag, object } => JsonRef::Peeled {
path: path.to_string(),
tag: tag.to_string(),
object: object.to_string(),
},
}
}
}
pub(crate) fn print(mut out: impl io::Write, refs: &[Ref]) -> io::Result<()> {
for r in refs {
match r {
Ref::Direct { path, object } => writeln!(&mut out, "{} {}", object.to_sha1_hex_string(), path),
Ref::Peeled { path, object, tag } => {
writeln!(&mut out, "{} {} tag:{}", object.to_sha1_hex_string(), path, tag)
}
Ref::Symbolic { path, target, object } => writeln!(
&mut out,
"{} {} symref-target:{}",
object.to_sha1_hex_string(),
path,
target
),
}?;
}
Ok(())
}
}