1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
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<P>( &mut self, _input: impl io::BufRead, _progress: P, _refs: &[Ref], _previous: &Response, ) -> io::Result<()> where P: Progress, <P as Progress>::SubProgress: Send + 'static, <<P as Progress>::SubProgress as Progress>::SubProgress: Send + 'static, { 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<P, W: io::Write>( protocol: Option<Protocol>, url: &str, progress: P, ctx: Context<W>, ) -> anyhow::Result<()> where P: Progress, <P as Progress>::SubProgress: Send + 'static, <<P as Progress>::SubProgress as Progress>::SubProgress: Send, { 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(()) } }