From be383cebc7bb797e950d93a3c030b6623121d4e5 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sat, 3 Dec 2022 09:49:09 -0800 Subject: [PATCH] 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. --- CHANGELOG.md | 4 ++++ lib/src/git.rs | 14 ++++++++++++++ lib/src/git_backend.rs | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e48265e64..b0f65b6d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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! diff --git a/lib/src/git.rs b/lib/src/git.rs index 92304b9cb..0e0e7ee2f 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -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 { } } +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); diff --git a/lib/src/git_backend.rs b/lib/src/git_backend.rs index 0ab9549c1..084bdadec 100644 --- a/lib/src/git_backend.rs +++ b/lib/src/git_backend.rs @@ -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 for BackendError {