Skip to content

Commit 0de7117

Browse files
committed
fix!: handshake::Ref::Symbolic now has a tag field.
This allows symbolic refs to be parsed which are in fact pointing to an annotated tag as in-between object.
1 parent d9e551b commit 0de7117

File tree

6 files changed

+97
-38
lines changed

6 files changed

+97
-38
lines changed

gix-protocol/src/handshake/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub enum Ref {
2121
/// The hash of the object the ref points to.
2222
object: gix_hash::ObjectId,
2323
},
24-
/// A symbolic ref pointing to `target` ref, which in turn points to an `object`
24+
/// A symbolic ref pointing to `target` ref, which in turn, ultimately after possibly following `tag`, points to an `object`
2525
Symbolic {
2626
/// The name at which the symbolic ref is located, like `HEAD`.
2727
full_ref_name: BString,
@@ -31,7 +31,11 @@ pub enum Ref {
3131
///
3232
/// [#205]: https://github.com/Byron/gitoxide/issues/205
3333
target: BString,
34-
/// The hash of the object the `target` ref points to.
34+
/// The hash of the annotated tag the ref points to, if present.
35+
///
36+
/// Note that this field is also `None` if `full_ref_name` is a lightweight tag.
37+
tag: Option<gix_hash::ObjectId>,
38+
/// The hash of the object the `target` ref ultimately points to.
3539
object: gix_hash::ObjectId,
3640
},
3741
/// A ref is unborn on the remote and just points to the initial, unborn branch, as is the case in a newly initialized repository

gix-protocol/src/handshake/refs/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,17 @@ impl Ref {
3838
/// If `unborn`, the first object id will be the null oid.
3939
pub fn unpack(&self) -> (&BStr, Option<&gix_hash::oid>, Option<&gix_hash::oid>) {
4040
match self {
41-
Ref::Direct { full_ref_name, object }
42-
| Ref::Symbolic {
43-
full_ref_name, object, ..
44-
} => (full_ref_name.as_ref(), Some(object), None),
41+
Ref::Direct { full_ref_name, object } => (full_ref_name.as_ref(), Some(object), None),
42+
Ref::Symbolic {
43+
full_ref_name,
44+
tag,
45+
object,
46+
..
47+
} => (
48+
full_ref_name.as_ref(),
49+
Some(tag.as_deref().unwrap_or(object)),
50+
tag.as_deref().map(|_| object.as_ref()),
51+
),
4552
Ref::Peeled {
4653
full_ref_name,
4754
tag: object,

gix-protocol/src/handshake/refs/shared.rs

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,33 @@ impl From<InternalRef> for Ref {
88
InternalRef::Symbolic {
99
path,
1010
target: Some(target),
11+
tag,
1112
object,
1213
} => Ref::Symbolic {
1314
full_ref_name: path,
1415
target,
16+
tag,
1517
object,
1618
},
1719
InternalRef::Symbolic {
1820
path,
1921
target: None,
22+
tag: None,
2023
object,
2124
} => Ref::Direct {
2225
full_ref_name: path,
2326
object,
2427
},
28+
InternalRef::Symbolic {
29+
path,
30+
target: None,
31+
tag: Some(tag),
32+
object,
33+
} => Ref::Peeled {
34+
full_ref_name: path,
35+
tag,
36+
object,
37+
},
2538
InternalRef::Peeled { path, tag, object } => Ref::Peeled {
2639
full_ref_name: path,
2740
tag,
@@ -56,6 +69,7 @@ pub(crate) enum InternalRef {
5669
///
5770
/// The latter is more of an edge case, please [this issue][#205] for details.
5871
target: Option<BString>,
72+
tag: Option<gix_hash::ObjectId>,
5973
object: gix_hash::ObjectId,
6074
},
6175
/// extracted from V1 capabilities, which contain some important symbolic refs along with their targets
@@ -155,6 +169,7 @@ pub(in crate::handshake::refs) fn parse_v1(
155169
Some(position) => match out_refs.swap_remove(position) {
156170
InternalRef::SymbolicForLookup { path: _, target } => out_refs.push(InternalRef::Symbolic {
157171
path: path.into(),
172+
tag: None, // TODO: figure out how annotated tags work here.
158173
object,
159174
target,
160175
}),
@@ -172,7 +187,7 @@ pub(in crate::handshake::refs) fn parse_v1(
172187

173188
pub(in crate::handshake::refs) fn parse_v2(line: &BStr) -> Result<Ref, Error> {
174189
let trimmed = line.trim_end();
175-
let mut tokens = trimmed.splitn(3, |b| *b == b' ');
190+
let mut tokens = trimmed.splitn(4, |b| *b == b' ');
176191
match (tokens.next(), tokens.next()) {
177192
(Some(hex_hash), Some(path)) => {
178193
let id = if hex_hash == b"unborn" {
@@ -183,40 +198,22 @@ pub(in crate::handshake::refs) fn parse_v2(line: &BStr) -> Result<Ref, Error> {
183198
if path.is_empty() {
184199
return Err(Error::MalformedV2RefLine(trimmed.to_owned().into()));
185200
}
186-
Ok(if let Some(attribute) = tokens.next() {
201+
let mut symref_target = None;
202+
let mut peeled = None;
203+
for attribute in tokens.by_ref().take(2) {
187204
let mut tokens = attribute.splitn(2, |b| *b == b':');
188205
match (tokens.next(), tokens.next()) {
189206
(Some(attribute), Some(value)) => {
190207
if value.is_empty() {
191208
return Err(Error::MalformedV2RefLine(trimmed.to_owned().into()));
192209
}
193210
match attribute {
194-
b"peeled" => Ref::Peeled {
195-
full_ref_name: path.into(),
196-
object: gix_hash::ObjectId::from_hex(value.as_bytes())?,
197-
tag: id.ok_or(Error::InvariantViolation {
198-
message: "got 'unborn' as tag target",
199-
})?,
200-
},
201-
b"symref-target" => match value {
202-
b"(null)" => Ref::Direct {
203-
full_ref_name: path.into(),
204-
object: id.ok_or(Error::InvariantViolation {
205-
message: "got 'unborn' while (null) was a symref target",
206-
})?,
207-
},
208-
name => match id {
209-
Some(id) => Ref::Symbolic {
210-
full_ref_name: path.into(),
211-
object: id,
212-
target: name.into(),
213-
},
214-
None => Ref::Unborn {
215-
full_ref_name: path.into(),
216-
target: name.into(),
217-
},
218-
},
219-
},
211+
b"peeled" => {
212+
peeled = Some(gix_hash::ObjectId::from_hex(value.as_bytes())?);
213+
}
214+
b"symref-target" => {
215+
symref_target = Some(value);
216+
}
220217
_ => {
221218
return Err(Error::UnknownAttribute {
222219
attribute: attribute.to_owned().into(),
@@ -227,13 +224,53 @@ pub(in crate::handshake::refs) fn parse_v2(line: &BStr) -> Result<Ref, Error> {
227224
}
228225
_ => return Err(Error::MalformedV2RefLine(trimmed.to_owned().into())),
229226
}
230-
} else {
231-
Ref::Direct {
227+
}
228+
if tokens.next().is_some() {
229+
return Err(Error::MalformedV2RefLine(trimmed.to_owned().into()));
230+
}
231+
Ok(match (symref_target, peeled) {
232+
(Some(target_name), peeled) => match target_name {
233+
b"(null)" => match peeled {
234+
None => Ref::Direct {
235+
full_ref_name: path.into(),
236+
object: id.ok_or(Error::InvariantViolation {
237+
message: "got 'unborn' while (null) was a symref target",
238+
})?,
239+
},
240+
Some(peeled) => Ref::Peeled {
241+
full_ref_name: path.into(),
242+
object: peeled,
243+
tag: id.ok_or(Error::InvariantViolation {
244+
message: "got 'unborn' while (null) was a symref target",
245+
})?,
246+
},
247+
},
248+
name => match id {
249+
Some(id) => Ref::Symbolic {
250+
full_ref_name: path.into(),
251+
tag: peeled.map(|_| id),
252+
object: peeled.unwrap_or(id),
253+
target: name.into(),
254+
},
255+
None => Ref::Unborn {
256+
full_ref_name: path.into(),
257+
target: name.into(),
258+
},
259+
},
260+
},
261+
(None, Some(peeled)) => Ref::Peeled {
262+
full_ref_name: path.into(),
263+
object: peeled,
264+
tag: id.ok_or(Error::InvariantViolation {
265+
message: "got 'unborn' as tag target",
266+
})?,
267+
},
268+
(None, None) => Ref::Direct {
232269
object: id.ok_or(Error::InvariantViolation {
233270
message: "got 'unborn' as object name of direct reference",
234271
})?,
235272
full_ref_name: path.into(),
236-
}
273+
},
237274
})
238275
}
239276
_ => Err(Error::MalformedV2RefLine(trimmed.to_owned().into())),

gix-protocol/src/handshake/refs/tests.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ unborn refs/heads/symbolic symref-target:refs/heads/target
1717
808e50d724f604f69ab93c6da2919c014667bedb refs/heads/main
1818
7fe1b98b39423b71e14217aa299a03b7c937d656 refs/tags/foo peeled:808e50d724f604f69ab93c6da2919c014667bedb
1919
7fe1b98b39423b71e14217aa299a03b7c937d6ff refs/tags/blaz
20+
978f927e6397113757dfec6332e7d9c7e356ac25 refs/heads/symbolic symref-target:refs/tags/v1.0 peeled:4d979abcde5cea47b079c38850828956c9382a56
2021
"
2122
.as_bytes(),
2223
);
@@ -29,6 +30,7 @@ unborn refs/heads/symbolic symref-target:refs/heads/target
2930
Ref::Symbolic {
3031
full_ref_name: "HEAD".into(),
3132
target: "refs/heads/main".into(),
33+
tag: None,
3234
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
3335
},
3436
Ref::Direct {
@@ -56,8 +58,14 @@ unborn refs/heads/symbolic symref-target:refs/heads/target
5658
full_ref_name: "refs/tags/blaz".into(),
5759
object: oid("7fe1b98b39423b71e14217aa299a03b7c937d6ff")
5860
},
61+
Ref::Symbolic {
62+
full_ref_name: "refs/heads/symbolic".into(),
63+
target: "refs/tags/v1.0".into(),
64+
tag: Some(oid("978f927e6397113757dfec6332e7d9c7e356ac25")),
65+
object: oid("4d979abcde5cea47b079c38850828956c9382a56")
66+
},
5967
]
60-
)
68+
);
6169
}
6270

6371
#[maybe_async::test(feature = "blocking-client", async(feature = "async-client", async_std::test))]
@@ -86,6 +94,7 @@ dce0ea858eef7ff61ad345cc5cdac62203fb3c10 refs/tags/gix-commitgraph-v0.0.0
8694
Ref::Symbolic {
8795
full_ref_name: "HEAD".into(),
8896
target: "refs/heads/main".into(),
97+
tag: None,
8998
object: oid("73a6868963993a3328e7d8fe94e5a6ac5078a944")
9099
},
91100
Ref::Direct {

gix-protocol/tests/fetch/v1.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ async fn ls_remote() -> crate::Result {
8181
handshake::Ref::Symbolic {
8282
full_ref_name: "HEAD".into(),
8383
object: oid("808e50d724f604f69ab93c6da2919c014667bedb"),
84+
tag: None,
8485
target: "refs/heads/master".into()
8586
},
8687
handshake::Ref::Direct {

gix-protocol/tests/fetch/v2.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ async fn ls_remote() -> crate::Result {
8181
handshake::Ref::Symbolic {
8282
full_ref_name: "HEAD".into(),
8383
object: oid("808e50d724f604f69ab93c6da2919c014667bedb"),
84+
tag: None,
8485
target: "refs/heads/master".into()
8586
},
8687
handshake::Ref::Direct {

0 commit comments

Comments
 (0)