git: on import, add GC-preventing refs to all seen refs

To prevent git's GC from breaking a repo, we already add a git ref to
commits we create in the git backend. However, we don't add refs to
commits we import from git. This fixes that.

Closes #815.
This commit is contained in:
Martin von Zweigbergk 2022-12-03 09:49:09 -08:00 committed by Martin von Zweigbergk
parent 9df247f87c
commit be383cebc7
3 changed files with 19 additions and 1 deletions

View file

@ -105,6 +105,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
later removed if you checked out another commit. You can now use `git` to
populate the submodule directory and `jj` will leave it alone.
* Git's GC could remove commits that were referenced from jj in some cases. We
are now better at adding Git refs to prevent that.
[#815](https://github.com/martinvonz/jj/issues/815)
### Contributors
Thanks to the people who made this release happen!

View file

@ -22,6 +22,7 @@ use thiserror::Error;
use crate::backend::CommitId;
use crate::commit::Commit;
use crate::git_backend::NO_GC_REF_NAMESPACE;
use crate::op_store;
use crate::op_store::RefTarget;
use crate::repo::MutableRepo;
@ -50,6 +51,17 @@ fn parse_git_ref(ref_name: &str) -> Option<RefName> {
}
}
fn prevent_gc(git_repo: &git2::Repository, id: &CommitId) {
git_repo
.reference(
&format!("{}{}", NO_GC_REF_NAMESPACE, id.hex()),
Oid::from_bytes(id.as_bytes()).unwrap(),
true,
"used by jj",
)
.unwrap();
}
/// Reflect changes made in the underlying Git repo in the Jujutsu repo.
pub fn import_refs(
mut_repo: &mut MutableRepo,
@ -75,6 +87,7 @@ pub fn import_refs(
let head_commit_id = CommitId::from_bytes(head_git_commit.id().as_bytes());
let head_commit = store.get_commit(&head_commit_id).unwrap();
new_git_heads.insert(head_commit_id.clone());
prevent_gc(git_repo, &head_commit_id);
mut_repo.add_head(&head_commit);
mut_repo.set_git_head(head_commit_id);
} else {
@ -112,6 +125,7 @@ pub fn import_refs(
let old_target = existing_git_refs.remove(&full_name);
let new_target = Some(RefTarget::Normal(id.clone()));
if new_target != old_target {
prevent_gc(git_repo, &id);
mut_repo.set_git_ref(full_name.clone(), RefTarget::Normal(id.clone()));
let commit = store.get_commit(&id).unwrap();
mut_repo.add_head(&commit);

View file

@ -33,7 +33,7 @@ use crate::stacked_table::{TableSegment, TableStore};
const HASH_LENGTH: usize = 20;
/// Ref namespace used only for preventing GC.
const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
pub const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
const CONFLICT_SUFFIX: &str = ".jjconflict";
impl From<git2::Error> for BackendError {