History

[git-protocol] move everything into `blocking_io` for later translation…

…into async-io, as usual.

Sebastian Thiel committed 5 years ago

Commit fa033741 parent 0d39b5d12 files · +295 −254Browse files

Showing the changes to git-protocol/src/fetch/refs.rs (1 of 12 files). Show all

Unified · Split

git-protocol/src/fetch/refs.rs+0−271
@@ -1,271 +0,0 @@
1use bstr::{BString, ByteSlice};
2use quick_error::quick_error;
3use std::io;
4
5quick_error! {
6 #[derive(Debug)]
7 pub enum Error {
8 Io(err: io::Error) {
9 display("An IO error occurred while reading refs from the server")
10 from()
11 source(err)
12 }
13 Id(err: git_hash::decode::Error) {
14 display("Failed to hex-decode object hash")
15 from()
16 source(err)
17 }
18 MalformedSymref(symref: BString) {
19 display("'{}' could not be parsed. A symref is expected to look like <NAME>:<target>.", symref)
20 }
21 MalformedV1RefLine(line: String) {
22 display("'{}' could not be parsed. A V1 ref line should be '<hex-hash> <path>'.", line)
23 }
24 MalformedV2RefLine(line: String) {
25 display("'{}' could not be parsed. A V2 ref line should be '<hex-hash> <path>[ (peeled|symref-target):<value>'.", line)
26 }
27 UnkownAttribute(attribute: String, line: String) {
28 display("The ref attribute '{}' is unknown. Found in line '{}'", attribute, line)
29 }
30 InvariantViolation(message: &'static str) {
31 display("{}", message)
32 }
33 }
34}
35
36/// A git reference, commonly referred to as 'ref', as returned by a git server before sending a pack.
37#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
38#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
39pub enum Ref {
40 /// A ref pointing to a `tag` object, which in turns points to an `object`, usually a commit
41 Peeled {
42 /// The path at which the ref is located, like `/refs/heads/main`.
43 path: BString,
44 /// The hash of the tag the ref points to.
45 tag: git_hash::ObjectId,
46 /// The hash of the object the `tag` points to.
47 object: git_hash::ObjectId,
48 },
49 /// A ref pointing to a commit object
50 Direct {
51 /// The path at which the ref is located, like `/refs/heads/main`.
52 path: BString,
53 /// The hash of the object the ref points to.
54 object: git_hash::ObjectId,
55 },
56 /// A symbolic ref pointing to `target` ref, which in turn points to an `object`
57 Symbolic {
58 /// The path at which the symbolic ref is located, like `/refs/heads/main`.
59 path: BString,
60 /// The path of the ref the symbolic ref points to.
61 target: BString,
62 /// The hash of the object the `target` ref points to.
63 object: git_hash::ObjectId,
64 },
65}
66
67impl Ref {
68 /// Provide shared fields referring to the ref itself, namely `(path, object id)`.
69 /// In case of peeled refs, the tag object itself is returned as it is what the path refers to.
70 pub fn unpack(&self) -> (&BString, &git_hash::ObjectId) {
71 match self {
72 Ref::Direct { path, object, .. }
73 | Ref::Peeled { path, tag: object, .. } // the tag acts as reference
74 | Ref::Symbolic { path, object, .. } => (path, object),
75 }
76 }
77}
78
79impl From<InternalRef> for Ref {
80 fn from(v: InternalRef) -> Self {
81 match v {
82 InternalRef::Symbolic { path, target, object } => Ref::Symbolic { path, target, object },
83 InternalRef::Peeled { path, tag, object } => Ref::Peeled { path, tag, object },
84 InternalRef::Direct { path, object } => Ref::Direct { path, object },
85 InternalRef::SymbolicForLookup { .. } => {
86 unreachable!("this case should have been removed during processing")
87 }
88 }
89 }
90}
91
92#[cfg_attr(test, derive(PartialEq, Eq, Debug, Clone))]
93pub(crate) enum InternalRef {
94 /// A ref pointing to a `tag` object, which in turns points to an `object`, usually a commit
95 Peeled {
96 path: BString,
97 tag: git_hash::ObjectId,
98 object: git_hash::ObjectId,
99 },
100 /// A ref pointing to a commit object
101 Direct { path: BString, object: git_hash::ObjectId },
102 /// A symbolic ref pointing to `target` ref, which in turn points to an `object`
103 Symbolic {
104 path: BString,
105 target: BString,
106 object: git_hash::ObjectId,
107 },
108 /// extracted from V1 capabilities, which contain some important symbolic refs along with their targets
109 /// These don't contain the Id
110 SymbolicForLookup { path: BString, target: BString },
111}
112
113impl InternalRef {
114 fn unpack_direct(self) -> Option<(BString, git_hash::ObjectId)> {
115 match self {
116 InternalRef::Direct { path, object } => Some((path, object)),
117 _ => None,
118 }
119 }
120 fn lookup_symbol_has_path(&self, predicate_path: &str) -> bool {
121 matches!(self, InternalRef::SymbolicForLookup { path, .. } if path == predicate_path)
122 }
123}
124
125pub(crate) fn from_capabilities<'a>(
126 out_refs: &mut Vec<InternalRef>,
127 capabilities: impl Iterator<Item = git_transport::client::capabilities::Capability<'a>>,
128) -> Result<(), Error> {
129 let symref_values = capabilities.filter_map(|c| {
130 if c.name() == b"symref".as_bstr() {
131 c.value().map(ToOwned::to_owned)
132 } else {
133 None
134 }
135 });
136 for symref in symref_values {
137 let (left, right) = symref.split_at(
138 symref
139 .find_byte(b':')
140 .ok_or_else(|| Error::MalformedSymref(symref.to_owned()))?,
141 );
142 if left.is_empty() || right.is_empty() {
143 return Err(Error::MalformedSymref(symref.to_owned()));
144 }
145 out_refs.push(InternalRef::SymbolicForLookup {
146 path: left.into(),
147 target: right[1..].into(),
148 })
149 }
150 Ok(())
151}
152
153pub(crate) fn from_v2_refs(out_refs: &mut Vec<Ref>, in_refs: &mut dyn io::BufRead) -> Result<(), Error> {
154 let mut line = String::new();
155 loop {
156 line.clear();
157 let bytes_read = in_refs.read_line(&mut line)?;
158 if bytes_read == 0 {
159 break;
160 }
161 let trimmed = line.trim_end();
162 let mut tokens = trimmed.splitn(3, ' ');
163 match (tokens.next(), tokens.next()) {
164 (Some(hex_hash), Some(path)) => {
165 let id = git_hash::ObjectId::from_hex(hex_hash.as_bytes())?;
166 if path.is_empty() {
167 return Err(Error::MalformedV2RefLine(trimmed.to_owned()));
168 }
169 out_refs.push(if let Some(attribute) = tokens.next() {
170 let mut tokens = attribute.splitn(2, ':');
171 match (tokens.next(), tokens.next()) {
172 (Some(attribute), Some(value)) => {
173 if value.is_empty() {
174 return Err(Error::MalformedV2RefLine(trimmed.to_owned()));
175 }
176 match attribute {
177 "peeled" => Ref::Peeled {
178 path: path.into(),
179 object: git_hash::ObjectId::from_hex(value.as_bytes())?,
180 tag: id,
181 },
182 "symref-target" => Ref::Symbolic {
183 path: path.into(),
184 object: id,
185 target: value.into(),
186 },
187 _ => return Err(Error::UnkownAttribute(attribute.to_owned(), trimmed.to_owned())),
188 }
189 }
190 _ => return Err(Error::MalformedV2RefLine(trimmed.to_owned())),
191 }
192 } else {
193 Ref::Direct {
194 object: id,
195 path: path.into(),
196 }
197 });
198 }
199 _ => return Err(Error::MalformedV2RefLine(trimmed.to_owned())),
200 }
201 }
202 Ok(())
203}
204
205pub(crate) fn from_v1_refs_received_as_part_of_handshake(
206 out_refs: &mut Vec<InternalRef>,
207 in_refs: &mut dyn io::BufRead,
208) -> Result<(), Error> {
209 let number_of_possible_symbolic_refs_for_lookup = out_refs.len();
210 let mut line = String::new();
211 loop {
212 line.clear();
213 let bytes_read = in_refs.read_line(&mut line)?;
214 if bytes_read == 0 {
215 break;
216 }
217 let trimmed = line.trim_end();
218 let (hex_hash, path) = trimmed.split_at(
219 trimmed
220 .find(' ')
221 .ok_or_else(|| Error::MalformedV1RefLine(trimmed.to_owned()))?,
222 );
223 let path = &path[1..];
224 if path.is_empty() {
225 return Err(Error::MalformedV1RefLine(trimmed.to_owned()));
226 }
227 match path.strip_suffix("^{}") {
228 Some(stripped) => {
229 let (previous_path, tag) =
230 out_refs
231 .pop()
232 .and_then(InternalRef::unpack_direct)
233 .ok_or(Error::InvariantViolation(
234 "Expecting peeled refs to be preceded by direct refs",
235 ))?;
236 if previous_path != stripped {
237 return Err(Error::InvariantViolation(
238 "Expecting peeled refs to have the same base path as the previous, unpeeled one",
239 ));
240 }
241 out_refs.push(InternalRef::Peeled {
242 path: previous_path,
243 tag,
244 object: git_hash::ObjectId::from_hex(hex_hash.as_bytes())?,
245 });
246 }
247 None => {
248 let object = git_hash::ObjectId::from_hex(hex_hash.as_bytes())?;
249 match out_refs
250 .iter()
251 .take(number_of_possible_symbolic_refs_for_lookup)
252 .position(|r| r.lookup_symbol_has_path(path))
253 {
254 Some(position) => match out_refs.swap_remove(position) {
255 InternalRef::SymbolicForLookup { path: _, target } => out_refs.push(InternalRef::Symbolic {
256 path: path.into(),
257 object,
258 target,
259 }),
260 _ => unreachable!("Bug in lookup_symbol_has_path - must return lookup symbols"),
261 },
262 None => out_refs.push(InternalRef::Direct {
263 object,
264 path: path.into(),
265 }),
266 };
267 }
268 }
269 }
270 Ok(())
271}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t