Unified · Split
| @@ -105,9 +105,9 @@ Please see _'Development Status'_ for a listing of all crates and their capabili · 104 unmodified lines | |||
| 105 | * [ ] via pure Rust implementation if no git is installed | 105 | * [ ] via pure Rust implementation if no git is installed |
| 106 | * [ ] fetch & clone | 106 | * [ ] fetch & clone |
| 107 | * [x] control credentials provider to fill, approve and reject | 107 | * [x] control credentials provider to fill, approve and reject |
| 108 | * [ ] ls-ref | 108 | * [x] ls-ref |
| 109 | * [x] parse V1 refs as provided during handshake | 109 | * [x] parse V1 refs as provided during handshake |
| 110 | * [ ] parse V2 refs | 110 | * [x] parse V2 refs |
| 111 | * [ ] push | 111 | * [ ] push |
| 112 | * [ ] shallow clones | 112 | * [ ] shallow clones |
| 113 | * [ ] API documentation with examples | 113 | * [ ] API documentation with examples |
| @@ -146,7 +146,7 @@ pub fn fetch<F: FnMut(credentials::Action) -> credentials::Result>( · 145 unmodified lines | |||
| 146 | mut delegate: impl Delegate, | 146 | mut delegate: impl Delegate, |
| 147 | mut authenticate: F, | 147 | mut authenticate: F, |
| 148 | ) -> Result<(), Error> { | 148 | ) -> Result<(), Error> { |
| 149 | let (actual_protocol, _refs, capabilities, call_ls_refs) = { | 149 | let (actual_protocol, mut parsed_refs, capabilities, call_ls_refs) = { |
| 150 | let result = transport.handshake(Service::UploadPack); | 150 | let result = transport.handshake(Service::UploadPack); |
| 151 | let SetServiceResponse { | 151 | let SetServiceResponse { |
| 152 | actual_protocol, | 152 | actual_protocol, |
| @@ -216,11 +216,12 @@ pub fn fetch<F: FnMut(credentials::Action) -> credentials::Result>( · 63 unmodified lines | |||
| 216 | let _next = delegate.prepare_command(ls_refs, &capabilities, &mut ls_args, &mut ls_features); | 216 | let _next = delegate.prepare_command(ls_refs, &capabilities, &mut ls_args, &mut ls_features); |
| 217 | ls_refs.validate_prefixes_or_panic(&capabilities, &ls_args, &ls_features); | 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 | ls_refs.as_str(), | 220 | ls_refs.as_str(), |
| 221 | ls_features.iter().map(|f| (*f, None)).chain(Some(agent)), | 221 | ls_features.iter().map(|f| (*f, None)).chain(Some(agent)), |
| 222 | if ls_args.is_empty() { None } else { Some(ls_args) }, | 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 | transport.close()?; | 227 | transport.close()?; |
| @@ -22,6 +22,12 @@ quick_error! { · 21 unmodified lines | |||
| 22 | MalformedV1RefLine(line: String) { | 22 | MalformedV1RefLine(line: String) { |
| 23 | display("'{}' could not be parsed. A V1 ref line should be '<hex-hash> <path>'.", line) | 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 | InvariantViolation(message: &'static str) { | 31 | InvariantViolation(message: &'static str) { |
| 26 | display("{}", message) | 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 | Ok(()) | 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 | pub(crate) fn from_v1_refs_received_as_part_of_handshake( | 144 | pub(crate) fn from_v1_refs_received_as_part_of_handshake( |
| 87 | out_refs: &mut Vec<Ref>, | 145 | out_refs: &mut Vec<Ref>, |
| 88 | in_refs: &mut dyn io::BufRead, | 146 | in_refs: &mut dyn io::BufRead, |
| @@ -8,6 +8,43 @@ mod refs { · 7 unmodified lines | |||
| 8 | owned::Id::from_40_bytes_in_hex(hex_sha.as_bytes()).expect("valid input") | 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 | #[test] | 48 | #[test] |
| 12 | fn extract_references_from_v1_refs() { | 49 | fn extract_references_from_v1_refs() { |
| 13 | let input: &mut dyn io::BufRead = &mut "73a6868963993a3328e7d8fe94e5a6ac5078a944 HEAD | 50 | let input: &mut dyn io::BufRead = &mut "73a6868963993a3328e7d8fe94e5a6ac5078a944 HEAD |
| @@ -40,7 +40,7 @@ · 39 unmodified lines | |||
| 40 | * [x] implement authentication provider using git-helpers | 40 | * [x] implement authentication provider using git-helpers |
| 41 | * [x] git-url round tripping | 41 | * [x] git-url round tripping |
| 42 | * [x] v1 ref parsing | 42 | * [x] v1 ref parsing |
| 43 | * [ ] v2 ref parsing | 43 | * [x] v2 ref parsing |
| 44 | * [ ] delegate to support clone | 44 | * [ ] delegate to support clone |
| 45 | * [ ] assure there is a way to do fetches with have/want negotiation | 45 | * [ ] assure there is a way to do fetches with have/want negotiation |
| 46 | * [ ] progress | 46 | * [ ] progress |