git: create no-gc ref by GitBackend

Since we now have an explicit method to import heads, it makes more sense to
manage no-gc refs by the backend.
This commit is contained in:
Yuya Nishihara 2023-09-08 16:29:09 +09:00
parent 17f502c83a
commit f744e5920b
2 changed files with 25 additions and 17 deletions

View file

@ -26,7 +26,7 @@ use tempfile::NamedTempFile;
use thiserror::Error;
use crate::backend::{BackendError, CommitId, ObjectId};
use crate::git_backend::{GitBackend, NO_GC_REF_NAMESPACE};
use crate::git_backend::GitBackend;
use crate::op_store::{BranchTarget, RefTarget, RefTargetOptionExt};
use crate::repo::{MutableRepo, Repo};
use crate::revset;
@ -172,18 +172,6 @@ pub fn get_local_git_tracking_branch<'a>(view: &'a View, branch: &str) -> &'a Re
view.get_git_ref(&format!("refs/heads/{branch}"))
}
fn prevent_gc(git_repo: &git2::Repository, id: &CommitId) -> Result<(), git2::Error> {
// If multiple processes do git::import_refs() in parallel, this can fail to
// acquire a lock file even with force=true.
git_repo.reference(
&format!("{}{}", NO_GC_REF_NAMESPACE, id.hex()),
Oid::from_bytes(id.as_bytes()).unwrap(),
true,
"used by jj",
)?;
Ok(())
}
/// Reflect changes made in the underlying Git repo in the Jujutsu repo.
///
/// This function detects conflicts (if both Git and JJ modified a branch) and
@ -255,9 +243,6 @@ pub fn import_some_refs(
head_commits.push(commit);
}
}
for commit in &head_commits {
prevent_gc(git_repo, commit.id())?;
}
mut_repo.add_heads(&head_commits);
// Apply the change that happened in git since last time we imported refs.

View file

@ -43,7 +43,7 @@ use crate::stacked_table::{
const HASH_LENGTH: usize = 20;
const CHANGE_ID_LENGTH: usize = 16;
/// Ref namespace used only for preventing GC.
pub const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
const CONFLICT_SUFFIX: &str = ".jjconflict";
#[derive(Debug, Error)]
@ -233,6 +233,9 @@ impl GitBackend {
&table_lock,
&missing_head_ids,
)?;
for &id in &missing_head_ids {
prevent_gc(&locked_repo, id)?;
}
self.save_extra_metadata_table(mut_table, &table_lock)
}
}
@ -371,6 +374,18 @@ fn create_no_gc_ref() -> String {
format!("{NO_GC_REF_NAMESPACE}{}", hex::encode(random_bytes))
}
fn prevent_gc(git_repo: &git2::Repository, id: &CommitId) -> Result<(), BackendError> {
git_repo
.reference(
&format!("{NO_GC_REF_NAMESPACE}{}", id.hex()),
Oid::from_bytes(id.as_bytes()).unwrap(),
true,
"used by jj",
)
.map_err(|err| BackendError::Other(Box::new(err)))?;
Ok(())
}
fn validate_git_object_id(id: &impl ObjectId) -> Result<git2::Oid, BackendError> {
if id.as_bytes().len() != HASH_LENGTH {
return Err(BackendError::InvalidHashLength {
@ -943,6 +958,14 @@ mod tests {
// Import the head commit and its ancestors
store.import_head_commits([&commit_id2]).unwrap();
// Ref should be created only for the head commit
let git_refs = store
.git_repo()
.references_glob("refs/jj/keep/*")
.unwrap()
.map(|git_ref| git_ref.unwrap().target().unwrap())
.collect_vec();
assert_eq!(git_refs, vec![git_commit_id2]);
let commit = store.read_commit(&commit_id).unwrap();
assert_eq!(&commit.change_id, &change_id);