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
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(()) } }