forked from mirrors/jj
git_backend: ensure no-gc refs are created for all imported head commits
This also means that we can implement GC without taking care of extra metadata. I haven't tried, but it wouldn't be easy to keep Git refs and extra table in sync.
This commit is contained in:
parent
2e1aa6c49c
commit
96ee9bdb9f
1 changed files with 36 additions and 17 deletions
|
@ -349,29 +349,28 @@ impl GitBackend {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create no-gc ref even if known to the extras table. Concurrent GC
|
||||||
|
// process might have deleted the no-gc ref.
|
||||||
|
let locked_repo = self.lock_git_repo();
|
||||||
|
for &id in &head_ids {
|
||||||
|
prevent_gc(&locked_repo, id)?;
|
||||||
|
}
|
||||||
|
|
||||||
// These commits are imported from Git. Make our change ids persist (otherwise
|
// These commits are imported from Git. Make our change ids persist (otherwise
|
||||||
// future write_commit() could reassign new change id.)
|
// future write_commit() could reassign new change id.)
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
heads_count = head_ids.len(),
|
heads_count = head_ids.len(),
|
||||||
"import extra metadata entries"
|
"import extra metadata entries"
|
||||||
);
|
);
|
||||||
let locked_repo = self.lock_git_repo();
|
|
||||||
let (table, table_lock) = self.read_extra_metadata_table_locked()?;
|
let (table, table_lock) = self.read_extra_metadata_table_locked()?;
|
||||||
let mut mut_table = table.start_mutation();
|
let mut mut_table = table.start_mutation();
|
||||||
let missing_head_ids = head_ids
|
|
||||||
.into_iter()
|
|
||||||
.filter(|&id| mut_table.get_value(id.as_bytes()).is_none())
|
|
||||||
.collect_vec();
|
|
||||||
import_extra_metadata_entries_from_heads(
|
import_extra_metadata_entries_from_heads(
|
||||||
&locked_repo,
|
&locked_repo,
|
||||||
&mut mut_table,
|
&mut mut_table,
|
||||||
&table_lock,
|
&table_lock,
|
||||||
&missing_head_ids,
|
&head_ids,
|
||||||
self.imported_commit_uses_tree_conflict_format,
|
self.imported_commit_uses_tree_conflict_format,
|
||||||
)?;
|
)?;
|
||||||
for &id in &missing_head_ids {
|
|
||||||
prevent_gc(&locked_repo, id)?;
|
|
||||||
}
|
|
||||||
self.save_extra_metadata_table(mut_table, &table_lock)
|
self.save_extra_metadata_table(mut_table, &table_lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,10 +659,14 @@ fn import_extra_metadata_entries_from_heads(
|
||||||
git_repo: &gix::Repository,
|
git_repo: &gix::Repository,
|
||||||
mut_table: &mut MutableTable,
|
mut_table: &mut MutableTable,
|
||||||
_table_lock: &FileLock,
|
_table_lock: &FileLock,
|
||||||
missing_head_ids: &[&CommitId],
|
head_ids: &[&CommitId],
|
||||||
uses_tree_conflict_format: bool,
|
uses_tree_conflict_format: bool,
|
||||||
) -> BackendResult<()> {
|
) -> BackendResult<()> {
|
||||||
let mut work_ids = missing_head_ids.iter().map(|&id| id.clone()).collect_vec();
|
let mut work_ids = head_ids
|
||||||
|
.iter()
|
||||||
|
.filter(|&id| mut_table.get_value(id.as_bytes()).is_none())
|
||||||
|
.map(|&id| id.clone())
|
||||||
|
.collect_vec();
|
||||||
while let Some(id) = work_ids.pop() {
|
while let Some(id) = work_ids.pop() {
|
||||||
let git_object = git_repo
|
let git_object = git_repo
|
||||||
.find_object(validate_git_object_id(&id)?)
|
.find_object(validate_git_object_id(&id)?)
|
||||||
|
@ -1658,6 +1661,7 @@ mod tests {
|
||||||
let settings = user_settings();
|
let settings = user_settings();
|
||||||
let temp_dir = testutils::new_temp_dir();
|
let temp_dir = testutils::new_temp_dir();
|
||||||
let backend = GitBackend::init_internal(&settings, temp_dir.path()).unwrap();
|
let backend = GitBackend::init_internal(&settings, temp_dir.path()).unwrap();
|
||||||
|
let git_repo = backend.open_git_repo().unwrap();
|
||||||
let signature = Signature {
|
let signature = Signature {
|
||||||
name: "Someone".to_string(),
|
name: "Someone".to_string(),
|
||||||
email: "someone@example.com".to_string(),
|
email: "someone@example.com".to_string(),
|
||||||
|
@ -1677,14 +1681,29 @@ mod tests {
|
||||||
secure_sig: None,
|
secure_sig: None,
|
||||||
};
|
};
|
||||||
let commit_id = backend.write_commit(commit, None).unwrap().0;
|
let commit_id = backend.write_commit(commit, None).unwrap().0;
|
||||||
let git_refs = backend
|
let git_refs: Vec<_> = git_repo
|
||||||
.open_git_repo()
|
|
||||||
.unwrap()
|
|
||||||
.references_glob("refs/jj/keep/*")
|
.references_glob("refs/jj/keep/*")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|git_ref| git_ref.unwrap().target().unwrap())
|
.try_collect()
|
||||||
.collect_vec();
|
.unwrap();
|
||||||
assert_eq!(git_refs, vec![git_id(&commit_id)]);
|
assert!(git_refs
|
||||||
|
.iter()
|
||||||
|
.any(|git_ref| git_ref.target().unwrap() == git_id(&commit_id)));
|
||||||
|
|
||||||
|
// Concurrently-running GC deletes the ref, leaving the extra metadata.
|
||||||
|
for mut git_ref in git_refs {
|
||||||
|
git_ref.delete().unwrap();
|
||||||
|
}
|
||||||
|
// Re-imported commit should have new ref.
|
||||||
|
backend.import_head_commits([&commit_id]).unwrap();
|
||||||
|
let git_refs: Vec<_> = git_repo
|
||||||
|
.references_glob("refs/jj/keep/*")
|
||||||
|
.unwrap()
|
||||||
|
.try_collect()
|
||||||
|
.unwrap();
|
||||||
|
assert!(git_refs
|
||||||
|
.iter()
|
||||||
|
.any(|git_ref| git_ref.target().unwrap() == git_id(&commit_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue