mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-26 22:10:52 +00:00
git: start tracking HEAD of underlying Git repo
This patch adds a place for tracking the current `HEAD` commit in the underlying Git repo. It updates `git::import_refs()` to record it. We don't use it anywhere yet. This is part of #44.
This commit is contained in:
parent
d06c74f5b8
commit
8a2f630ac0
7 changed files with 88 additions and 1 deletions
|
@ -64,6 +64,7 @@ message View {
|
|||
repeated Tag tags = 6;
|
||||
// Only a subset of the refs. For example, does not include refs/notes/.
|
||||
repeated GitRef git_refs = 3;
|
||||
bytes git_head = 7;
|
||||
}
|
||||
|
||||
message Operation {
|
||||
|
|
|
@ -62,7 +62,6 @@ pub fn import_refs(
|
|||
|| git_ref.name().is_none()
|
||||
{
|
||||
// Skip other refs (such as notes) and symbolic refs, as well as non-utf8 refs.
|
||||
// TODO: Is it useful to import HEAD (especially if it's detached)?
|
||||
continue;
|
||||
}
|
||||
let full_name = git_ref.name().unwrap().to_string();
|
||||
|
@ -114,6 +113,19 @@ pub fn import_refs(
|
|||
}
|
||||
}
|
||||
}
|
||||
// TODO: Should this be a separate function? We may not always want to import
|
||||
// the Git HEAD (and add it to our set of heads).
|
||||
if let Ok(head_git_commit) = git_repo
|
||||
.head()
|
||||
.and_then(|head_ref| head_ref.peel_to_commit())
|
||||
{
|
||||
let head_commit_id = CommitId::from_bytes(head_git_commit.id().as_bytes());
|
||||
let head_commit = store.get_commit(&head_commit_id).unwrap();
|
||||
mut_repo.add_head(&head_commit);
|
||||
mut_repo.set_git_head(head_commit_id);
|
||||
} else {
|
||||
mut_repo.clear_git_head();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,10 @@ pub struct View {
|
|||
pub branches: BTreeMap<String, BranchTarget>,
|
||||
pub tags: BTreeMap<String, RefTarget>,
|
||||
pub git_refs: BTreeMap<String, RefTarget>,
|
||||
/// The commit the Git HEAD points to.
|
||||
// TODO: Support multiple Git worktrees?
|
||||
// TODO: Do we want to store the current branch name too?
|
||||
pub git_head: Option<CommitId>,
|
||||
// The commit that *should be* checked out in the (default) working copy. Note that the
|
||||
// working copy (.jj/working_copy/) has the source of truth about which commit *is* checked out
|
||||
// (to be precise: the commit to which we most recently completed a checkout to).
|
||||
|
@ -157,6 +161,7 @@ impl View {
|
|||
branches: BTreeMap::new(),
|
||||
tags: BTreeMap::new(),
|
||||
git_refs: BTreeMap::new(),
|
||||
git_head: None,
|
||||
checkout,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -664,6 +664,14 @@ impl MutableRepo {
|
|||
self.view.remove_git_ref(name);
|
||||
}
|
||||
|
||||
pub fn set_git_head(&mut self, head_id: CommitId) {
|
||||
self.view.set_git_head(head_id);
|
||||
}
|
||||
|
||||
pub fn clear_git_head(&mut self) {
|
||||
self.view.clear_git_head();
|
||||
}
|
||||
|
||||
pub fn set_view(&mut self, data: op_store::View) {
|
||||
self.view.set_view(data);
|
||||
self.enforce_view_invariants();
|
||||
|
|
|
@ -237,6 +237,10 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
|||
proto.git_refs.push(git_ref_proto);
|
||||
}
|
||||
|
||||
if let Some(git_head) = &view.git_head {
|
||||
proto.set_git_head(git_head.to_bytes());
|
||||
}
|
||||
|
||||
proto
|
||||
}
|
||||
|
||||
|
@ -295,6 +299,10 @@ fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
|
|||
}
|
||||
}
|
||||
|
||||
if !proto.git_head.is_empty() {
|
||||
view.git_head = Some(CommitId::new(proto.git_head.clone()));
|
||||
}
|
||||
|
||||
view
|
||||
}
|
||||
|
||||
|
@ -387,6 +395,7 @@ mod tests {
|
|||
"refs/heads/main".to_string() => git_refs_main_target,
|
||||
"refs/heads/feature".to_string() => git_refs_feature_target
|
||||
},
|
||||
git_head: Some(CommitId::from_hex("fff111")),
|
||||
checkout: checkout_id,
|
||||
};
|
||||
let view_id = store.write_view(&view).unwrap();
|
||||
|
|
|
@ -71,6 +71,10 @@ impl View {
|
|||
&self.data.git_refs
|
||||
}
|
||||
|
||||
pub fn git_head(&self) -> Option<CommitId> {
|
||||
self.data.git_head.clone()
|
||||
}
|
||||
|
||||
pub fn set_checkout(&mut self, id: CommitId) {
|
||||
self.data.checkout = id;
|
||||
}
|
||||
|
@ -211,6 +215,14 @@ impl View {
|
|||
self.data.git_refs.remove(name);
|
||||
}
|
||||
|
||||
pub fn set_git_head(&mut self, head_id: CommitId) {
|
||||
self.data.git_head = Some(head_id);
|
||||
}
|
||||
|
||||
pub fn clear_git_head(&mut self) {
|
||||
self.data.git_head = None;
|
||||
}
|
||||
|
||||
pub fn set_view(&mut self, data: op_store::View) {
|
||||
self.data = data;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ fn test_import_refs() {
|
|||
empty_git_commit(&git_repo, "refs/notes/x", &[&commit2]);
|
||||
empty_git_commit(&git_repo, "refs/remotes/origin/HEAD", &[&commit2]);
|
||||
|
||||
git_repo.set_head("refs/heads/main").unwrap();
|
||||
|
||||
let git_repo = repo.store().git_repo().unwrap();
|
||||
let mut tx = repo.start_transaction("test");
|
||||
jujutsu_lib::git::import_refs(tx.mut_repo(), &git_repo).unwrap();
|
||||
|
@ -136,6 +138,7 @@ fn test_import_refs() {
|
|||
view.git_refs().get("refs/tags/v1.0"),
|
||||
Some(RefTarget::Normal(commit_id(&commit5))).as_ref()
|
||||
);
|
||||
assert_eq!(view.git_head(), Some(commit_id(&commit2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -260,6 +263,43 @@ fn test_import_refs_empty_git_repo() {
|
|||
assert_eq!(repo.view().branches().len(), 0);
|
||||
assert_eq!(repo.view().tags().len(), 0);
|
||||
assert_eq!(repo.view().git_refs().len(), 0);
|
||||
assert_eq!(repo.view().git_head(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_refs_detached_head() {
|
||||
let settings = testutils::user_settings();
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let git_repo_dir = temp_dir.path().join("source");
|
||||
let jj_repo_dir = temp_dir.path().join("jj");
|
||||
|
||||
let git_repo = git2::Repository::init_bare(&git_repo_dir).unwrap();
|
||||
let commit1 = empty_git_commit(&git_repo, "refs/heads/main", &[]);
|
||||
// Delete the reference. Check that the detached HEAD commit still gets added to
|
||||
// the set of heads
|
||||
git_repo
|
||||
.find_reference("refs/heads/main")
|
||||
.unwrap()
|
||||
.delete()
|
||||
.unwrap();
|
||||
git_repo.set_head_detached(commit1.id()).unwrap();
|
||||
|
||||
std::fs::create_dir(&jj_repo_dir).unwrap();
|
||||
let repo = ReadonlyRepo::init_external_git(&settings, jj_repo_dir, git_repo_dir);
|
||||
let mut tx = repo.start_transaction("test");
|
||||
jujutsu_lib::git::import_refs(tx.mut_repo(), &git_repo).unwrap();
|
||||
let repo = tx.commit();
|
||||
|
||||
let expected_heads = hashset! {
|
||||
repo.view().checkout().clone(),
|
||||
commit_id(&commit1),
|
||||
};
|
||||
assert_eq!(*repo.view().heads(), expected_heads);
|
||||
assert_eq!(repo.view().git_refs().len(), 0);
|
||||
assert_eq!(
|
||||
repo.view().git_head(),
|
||||
Some(CommitId::from_bytes(commit1.id().as_bytes()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue