Showing the changes to git-protocol/src (3 of 5 files). Show all
Unified · Split
| @@ -146,7 +146,7 @@ pub fn fetch<F: FnMut(credentials::Action) -> credentials::Result>( · 145 unmodified lines | ||
| 146 | 146 | mut delegate: impl Delegate, |
| 147 | 147 | mut authenticate: F, |
| 148 | 148 | ) -> Result<(), Error> { |
| 149 | − let (actual_protocol, _refs, capabilities, call_ls_refs) = { | |
| 149 | + let (actual_protocol, mut parsed_refs, capabilities, call_ls_refs) = { | |
| 150 | 150 | let result = transport.handshake(Service::UploadPack); |
| 151 | 151 | let SetServiceResponse { |
| 152 | 152 | actual_protocol, |
| @@ -216,11 +216,12 @@ pub fn fetch<F: FnMut(credentials::Action) -> credentials::Result>( · 63 unmodified lines | ||
| 216 | 216 | let _next = delegate.prepare_command(ls_refs, &capabilities, &mut ls_args, &mut ls_features); |
| 217 | 217 | ls_refs.validate_prefixes_or_panic(&capabilities, &ls_args, &ls_features); |
| 218 | 218 | |
| 219 | − let _refs = transport.invoke( | |
| 219 | + let mut refs = transport.invoke( | |
| 220 | 220 | ls_refs.as_str(), |
| 221 | 221 | ls_features.iter().map(|f| (*f, None)).chain(Some(agent)), |
| 222 | 222 | if ls_args.is_empty() { None } else { Some(ls_args) }, |
| 223 | 223 | )?; |
| 224 | + refs::from_v2_refs(&mut parsed_refs, &mut refs)?; | |
| 224 | 225 | } |
| 225 | 226 | |
| 226 | 227 | transport.close()?; |
| @@ -22,6 +22,12 @@ quick_error! { · 21 unmodified lines | ||
| 22 | 22 | MalformedV1RefLine(line: String) { |
| 23 | 23 | display("'{}' could not be parsed. A V1 ref line should be '<hex-hash> <path>'.", line) |
| 24 | 24 | } |
| 25 | + MalformedV2RefLine(line: String) { | |
| 26 | + display("'{}' could not be parsed. A V2 ref line should be '<hex-hash> <path>[ (peeled|symref-target):<value>'.", line) | |
| 27 | + } | |
| 28 | + UnkownAttribute(attribute: String, line: String) { | |
| 29 | + display("The ref attribute '{}' is unknown. Found in line '{}'", attribute, line) | |
| 30 | + } | |
| 25 | 31 | InvariantViolation(message: &'static str) { |
| 26 | 32 | display("{}", message) |
| 27 | 33 | } |
| @@ -83,6 +89,58 @@ pub(crate) fn from_capabilities(out_refs: &mut Vec<Ref>, symrefs: Vec<BString>) · 55 unmodified lines | ||
| 83 | 89 | Ok(()) |
| 84 | 90 | } |
| 85 | 91 | |
| 92 | +pub(crate) fn from_v2_refs(out_refs: &mut Vec<Ref>, in_refs: &mut dyn io::BufRead) -> Result<(), Error> { | |
| 93 | + let mut line = String::new(); | |
| 94 | + loop { | |
| 95 | + line.clear(); | |
| 96 | + let bytes_read = in_refs.read_line(&mut line)?; | |
| 97 | + if bytes_read == 0 { | |
| 98 | + break; | |
| 99 | + } | |
| 100 | + let trimmed = line.trim_end(); | |
| 101 | + let mut tokens = trimmed.splitn(3, ' '); | |
| 102 | + match (tokens.next(), tokens.next()) { | |
| 103 | + (Some(hex_hash), Some(path)) => { | |
| 104 | + let id = owned::Id::from_40_bytes_in_hex(hex_hash.as_bytes())?; | |
| 105 | + if path.is_empty() { | |
| 106 | + return Err(Error::MalformedV2RefLine(trimmed.to_owned())); | |
| 107 | + } | |
| 108 | + out_refs.push(if let Some(attribute) = tokens.next() { | |
| 109 | + let mut tokens = attribute.splitn(2, ':'); | |
| 110 | + match (tokens.next(), tokens.next()) { | |
| 111 | + (Some(attribute), Some(value)) => { | |
| 112 | + if value.is_empty() { | |
| 113 | + return Err(Error::MalformedV2RefLine(trimmed.to_owned())); | |
| 114 | + } | |
| 115 | + match attribute { | |
| 116 | + "peeled" => Ref::Peeled { | |
| 117 | + path: path.into(), | |
| 118 | + object: owned::Id::from_40_bytes_in_hex(value.as_bytes())?, | |
| 119 | + tag: id, | |
| 120 | + }, | |
| 121 | + "symref-target" => Ref::Symbolic { | |
| 122 | + path: path.into(), | |
| 123 | + object: id, | |
| 124 | + target: value.into(), | |
| 125 | + }, | |
| 126 | + _ => return Err(Error::UnkownAttribute(attribute.to_owned(), trimmed.to_owned())), | |
| 127 | + } | |
| 128 | + } | |
| 129 | + _ => return Err(Error::MalformedV2RefLine(trimmed.to_owned())), | |
| 130 | + } | |
| 131 | + } else { | |
| 132 | + Ref::Direct { | |
| 133 | + object: id, | |
| 134 | + path: path.into(), | |
| 135 | + } | |
| 136 | + }); | |
| 137 | + } | |
| 138 | + _ => return Err(Error::MalformedV2RefLine(trimmed.to_owned())), | |
| 139 | + } | |
| 140 | + } | |
| 141 | + Ok(()) | |
| 142 | +} | |
| 143 | + | |
| 86 | 144 | pub(crate) fn from_v1_refs_received_as_part_of_handshake( |
| 87 | 145 | out_refs: &mut Vec<Ref>, |
| 88 | 146 | in_refs: &mut dyn io::BufRead, |
| @@ -8,6 +8,43 @@ mod refs { · 7 unmodified lines | ||
| 8 | 8 | owned::Id::from_40_bytes_in_hex(hex_sha.as_bytes()).expect("valid input") |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | + #[test] | |
| 12 | + fn extract_references_from_v2_refs() { | |
| 13 | + let input: &mut dyn io::BufRead = | |
| 14 | + &mut "808e50d724f604f69ab93c6da2919c014667bedb HEAD symref-target:refs/heads/main | |
| 15 | +808e50d724f604f69ab93c6da2919c014667bedb refs/heads/main | |
| 16 | +7fe1b98b39423b71e14217aa299a03b7c937d656 refs/tags/foo peeled:808e50d724f604f69ab93c6da2919c014667bedb | |
| 17 | +7fe1b98b39423b71e14217aa299a03b7c937d6ff refs/tags/blaz | |
| 18 | +" | |
| 19 | + .as_bytes(); | |
| 20 | + let mut out = Vec::new(); | |
| 21 | + refs::from_v2_refs(&mut out, input).expect("no failure on valid input"); | |
| 22 | + | |
| 23 | + assert_eq!( | |
| 24 | + out, | |
| 25 | + vec![ | |
| 26 | + Ref::Symbolic { | |
| 27 | + path: "HEAD".into(), | |
| 28 | + target: "refs/heads/main".into(), | |
| 29 | + object: oid("808e50d724f604f69ab93c6da2919c014667bedb") | |
| 30 | + }, | |
| 31 | + Ref::Direct { | |
| 32 | + path: "refs/heads/main".into(), | |
| 33 | + object: oid("808e50d724f604f69ab93c6da2919c014667bedb") | |
| 34 | + }, | |
| 35 | + Ref::Peeled { | |
| 36 | + path: "refs/tags/foo".into(), | |
| 37 | + tag: oid("7fe1b98b39423b71e14217aa299a03b7c937d656"), | |
| 38 | + object: oid("808e50d724f604f69ab93c6da2919c014667bedb") | |
| 39 | + }, | |
| 40 | + Ref::Direct { | |
| 41 | + path: "refs/tags/blaz".into(), | |
| 42 | + object: oid("7fe1b98b39423b71e14217aa299a03b7c937d6ff") | |
| 43 | + }, | |
| 44 | + ] | |
| 45 | + ) | |
| 46 | + } | |
| 47 | + | |
| 11 | 48 | #[test] |
| 12 | 49 | fn extract_references_from_v1_refs() { |
| 13 | 50 | let input: &mut dyn io::BufRead = &mut "73a6868963993a3328e7d8fe94e5a6ac5078a944 HEAD |