Skip to content

Commit

Permalink
feat!: Support for unborn ls-refs capability if server supports it. (
Browse files Browse the repository at this point in the history
…#450)

We can also parse it, adding yet another variant to `fetch::Refs`.
  • Loading branch information
Byron committed Nov 1, 2022
1 parent b219033 commit 02e37f0
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 9 deletions.
2 changes: 1 addition & 1 deletion git-protocol/src/fetch/command.rs
Expand Up @@ -31,7 +31,7 @@ mod with_io {
/// Only V2
fn all_argument_prefixes(&self) -> &'static [&'static str] {
match self {
Command::LsRefs => &["symrefs", "peel", "ref-prefix "],
Command::LsRefs => &["symrefs", "peel", "ref-prefix ", "unborn"],
Command::Fetch => &[
"want ", // hex oid
"have ", // hex oid
Expand Down
7 changes: 7 additions & 0 deletions git-protocol/src/fetch/refs/function.rs
Expand Up @@ -32,6 +32,13 @@ pub async fn refs(
let ls_refs = Command::LsRefs;
let mut ls_features = ls_refs.default_features(protocol_version, capabilities);
let mut ls_args = ls_refs.initial_arguments(&ls_features);
if capabilities
.capability("ls-refs")
.and_then(|cap| cap.supports("unborn"))
.unwrap_or_default()
{
ls_args.push("unborn".into());
}
let refs = match prepare_ls_refs(capabilities, &mut ls_args, &mut ls_features) {
Ok(LsRefsAction::Skip) => Vec::new(),
Ok(LsRefsAction::Continue) => {
Expand Down
10 changes: 10 additions & 0 deletions git-protocol/src/fetch/refs/mod.rs
Expand Up @@ -77,12 +77,18 @@ pub enum Ref {
/// The hash of the object the `target` ref points to.
object: git_hash::ObjectId,
},
/// `HEAD` is unborn on the remote and just points to the initial, unborn branch.
Unborn {
/// The path of the ref the symbolic ref points to, like `refs/heads/main`.
target: BString,
},
}

impl Ref {
/// Provide shared fields referring to the ref itself, namely `(name, target, [peeled])`.
/// In case of peeled refs, the tag object itself is returned as it is what the ref directly refers to, and target of the tag is returned
/// as `peeled`.
/// If `unborn`, the first object id will be the null oid.
pub fn unpack(&self) -> (&BStr, &git_hash::oid, Option<&git_hash::oid>) {
match self {
Ref::Direct { full_ref_name, object }
Expand All @@ -94,6 +100,10 @@ impl Ref {
tag: object,
object: peeled,
} => (full_ref_name.as_ref(), object, Some(peeled)),
Ref::Unborn { target: _ } => {
static NULL: git_hash::ObjectId = git_hash::ObjectId::null(git_hash::Kind::Sha1);
("HEAD".into(), &NULL, None)
}
}
}
}
Expand Down
29 changes: 21 additions & 8 deletions git-protocol/src/fetch/refs/shared.rs
Expand Up @@ -171,7 +171,11 @@ pub(in crate::fetch::refs) fn parse_v2(line: &str) -> Result<Ref, Error> {
let mut tokens = trimmed.splitn(3, ' ');
match (tokens.next(), tokens.next()) {
(Some(hex_hash), Some(path)) => {
let id = git_hash::ObjectId::from_hex(hex_hash.as_bytes())?;
let id = if hex_hash == "unborn" {
None
} else {
Some(git_hash::ObjectId::from_hex(hex_hash.as_bytes())?)
};
if path.is_empty() {
return Err(Error::MalformedV2RefLine(trimmed.to_owned()));
}
Expand All @@ -186,17 +190,24 @@ pub(in crate::fetch::refs) fn parse_v2(line: &str) -> Result<Ref, Error> {
"peeled" => Ref::Peeled {
full_ref_name: path.into(),
object: git_hash::ObjectId::from_hex(value.as_bytes())?,
tag: id,
tag: id.ok_or_else(|| Error::InvariantViolation {
message: "got 'unborn' as tag target",
})?,
},
"symref-target" => match value {
"(null)" => Ref::Direct {
full_ref_name: path.into(),
object: id,
object: id.ok_or_else(|| Error::InvariantViolation {
message: "got 'unborn' while (null) was a symref target",
})?,
},
name => Ref::Symbolic {
full_ref_name: path.into(),
object: id,
target: name.into(),
name => match id {
Some(id) => Ref::Symbolic {
full_ref_name: path.into(),
object: id,
target: name.into(),
},
None => Ref::Unborn { target: name.into() },
},
},
_ => {
Expand All @@ -211,7 +222,9 @@ pub(in crate::fetch::refs) fn parse_v2(line: &str) -> Result<Ref, Error> {
}
} else {
Ref::Direct {
object: id,
object: id.ok_or_else(|| Error::InvariantViolation {
message: "got 'unborn' as object name of direct reference",
})?,
full_ref_name: path.into(),
}
})
Expand Down
4 changes: 4 additions & 0 deletions git-protocol/src/fetch/tests/refs.rs
Expand Up @@ -7,6 +7,7 @@ use crate::fetch::{refs, refs::shared::InternalRef, Ref};
async fn extract_references_from_v2_refs() {
let input = &mut "808e50d724f604f69ab93c6da2919c014667bedb HEAD symref-target:refs/heads/main
808e50d724f604f69ab93c6da2919c014667bedb MISSING_NAMESPACE_TARGET symref-target:(null)
unborn HEAD symref-target:refs/heads/main
808e50d724f604f69ab93c6da2919c014667bedb refs/heads/main
7fe1b98b39423b71e14217aa299a03b7c937d656 refs/tags/foo peeled:808e50d724f604f69ab93c6da2919c014667bedb
7fe1b98b39423b71e14217aa299a03b7c937d6ff refs/tags/blaz
Expand All @@ -27,6 +28,9 @@ async fn extract_references_from_v2_refs() {
full_ref_name: "MISSING_NAMESPACE_TARGET".into(),
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
},
Ref::Unborn {
target: "refs/heads/main".into(),
},
Ref::Direct {
full_ref_name: "refs/heads/main".into(),
object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
Expand Down

0 comments on commit 02e37f0

Please sign in to comment.