Skip to content

Commit

Permalink
Support unborn remotes and pick up their default branch name. (#450)
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Nov 2, 2022
1 parent 64db0b2 commit 619fd61
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 28 deletions.
5 changes: 5 additions & 0 deletions git-repository/src/clone/fetch/mod.rs
Expand Up @@ -41,6 +41,11 @@ impl PrepareFetch {
/// it was newly initialized.
///
/// Note that all data we created will be removed once this instance drops if the operation wasn't successful.
///
/// # Deviation
///
/// When the remote side is freshly initialized without commits, we pick up their reference name _and_ create a reflog entry like
/// before, with old and new hash being the `null-hex-sha`. That way the branch still remembers where it was created from.
#[cfg(feature = "blocking-network-client")]
pub fn fetch_only<P>(
&mut self,
Expand Down
64 changes: 40 additions & 24 deletions git-repository/src/clone/fetch/util.rs
Expand Up @@ -46,14 +46,19 @@ pub fn update_head(
use git_ref::transaction::{PreviousValue, RefEdit};
use git_ref::Target;
use std::convert::TryInto;
let (head_peeled_id, head_ref) = match remote_refs.iter().find_map(|r| match r {
git_protocol::fetch::Ref::Symbolic {
full_ref_name,
target,
object,
} if full_ref_name == "HEAD" => Some((object, Some(target))),
git_protocol::fetch::Ref::Direct { full_ref_name, object } if full_ref_name == "HEAD" => Some((object, None)),
_ => None,
let (head_peeled_id, head_ref) = match remote_refs.iter().find_map(|r| {
Some(match r {
git_protocol::fetch::Ref::Symbolic {
full_ref_name,
target,
object,
} if full_ref_name == "HEAD" => (Some(object), Some(target)),
git_protocol::fetch::Ref::Direct { full_ref_name, object } if full_ref_name == "HEAD" => {
(Some(object), None)
}
git_protocol::fetch::Ref::Unborn { target } => (None, Some(target)),
_ => return None,
})
}) {
Some(t) => t,
None => return Ok(()),
Expand Down Expand Up @@ -82,39 +87,46 @@ pub fn update_head(
}),
))
.prepare(
[
RefEdit {
change: git_ref::transaction::Change::Update {
log: reflog_message(),
expected: PreviousValue::Any,
new: Target::Peeled(head_peeled_id.to_owned()),
},
name: referent.clone(),
deref: false,
},
RefEdit {
{
let mut edits = vec![RefEdit {
change: git_ref::transaction::Change::Update {
log: reflog_message(),
expected: PreviousValue::Any,
new: Target::Symbolic(referent),
new: Target::Symbolic(referent.clone()),
},
name: head.clone(),
deref: false,
},
],
}];
if let Some(head_peeled_id) = head_peeled_id {
edits.push(RefEdit {
change: git_ref::transaction::Change::Update {
log: reflog_message(),
expected: PreviousValue::Any,
new: Target::Peeled(head_peeled_id.to_owned()),
},
name: referent,
deref: false,
});
};
edits
},
git_lock::acquire::Fail::Immediately,
git_lock::acquire::Fail::Immediately,
)
.map_err(crate::reference::edit::Error::from)?
.commit(repo.committer_or_default())
.map_err(crate::reference::edit::Error::from)?;

let mut log = reflog_message();
log.mode = RefLog::Only;
repo.edit_reference(RefEdit {
change: git_ref::transaction::Change::Update {
log,
expected: PreviousValue::Any,
new: Target::Peeled(head_peeled_id.to_owned()),
new: Target::Peeled(match head_peeled_id {
Some(id) => id.to_owned(),
None => git_hash::ObjectId::null(repo.object_hash()),
}),
},
name: head,
deref: false,
Expand All @@ -125,7 +137,11 @@ pub fn update_head(
change: git_ref::transaction::Change::Update {
log: reflog_message(),
expected: PreviousValue::Any,
new: Target::Peeled(head_peeled_id.to_owned()),
new: Target::Peeled(
head_peeled_id
.expect("detached heads always point to something")
.to_owned(),
),
},
name: head,
deref: false,
Expand Down
12 changes: 8 additions & 4 deletions git-repository/tests/clone/mod.rs
Expand Up @@ -193,7 +193,6 @@ mod blocking_io {
}

#[test]
#[ignore]
fn fetch_and_checkout_empty_remote_repo() -> crate::Result {
let tmp = git_testtools::tempfile::TempDir::new()?;
let mut prepare = git::prepare_clone(
Expand All @@ -206,7 +205,12 @@ mod blocking_io {
let (repo, _) = checkout.main_worktree(git::progress::Discard, &std::sync::atomic::AtomicBool::default())?;

assert!(!repo.index_path().is_file(), "newly initialized repos have no index");
assert!(repo.head()?.is_unborn());
let head = repo.head()?;
assert!(head.is_unborn());

let mut logs = head.log_iter();
assert_reflog(logs.all());

if out
.ref_map
.handshake
Expand All @@ -217,13 +221,13 @@ mod blocking_io {
== Some(true)
{
assert_eq!(
repo.head()?.referent_name().expect("present").as_bstr(),
head.referent_name().expect("present").as_bstr(),
"refs/heads/special",
"we pick up the name as present on the server, not the one we default to"
);
} else {
assert_eq!(
repo.head()?.referent_name().expect("present").as_bstr(),
head.referent_name().expect("present").as_bstr(),
"refs/heads/main",
"we simply keep our own post-init HEAD which defaults to the branch name we configured locally"
);
Expand Down

0 comments on commit 619fd61

Please sign in to comment.