diff --git a/lib/src/git.rs b/lib/src/git.rs index 93ad6c6039..d6066f8648 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -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. diff --git a/lib/src/git_backend.rs b/lib/src/git_backend.rs index ed04364501..09a032de06 100644 --- a/lib/src/git_backend.rs +++ b/lib/src/git_backend.rs @@ -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 { 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);