mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-27 14:57:14 +00:00
git: add a ref to each commit we create
I just learned that attaching a git note is not enough to keep a commit from being GC'd. I had read `git help gc` before but it was quite misleading (I just sent a patch to clarify it). Since the git note is not enough, we need to create some other reference. This patch makes it so we write refs in `refs/jj/keep/` for every commit we create. We will probably want to remove unnecessary refs (ancestors of commits pointed to by other refs) once we have a `jj gc` command.
This commit is contained in:
parent
dd98f0564e
commit
a1983ebe96
1 changed files with 58 additions and 2 deletions
|
@ -29,7 +29,11 @@ use crate::store::{
|
|||
};
|
||||
use backoff::{ExponentialBackoff, Operation};
|
||||
use std::ops::Deref;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Ref namespace used only for preventing GC.
|
||||
const NO_GC_REF_NAMESPACE: &str = "refs/jj/keep/";
|
||||
/// Notes ref for commit metadata
|
||||
const COMMITS_NOTES_REF: &str = "refs/notes/jj/commits";
|
||||
const CONFLICT_SUFFIX: &str = ".jjconflict";
|
||||
|
||||
|
@ -108,6 +112,18 @@ fn deserialize_note(commit: &mut Commit, note: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a random ref in refs/jj/. Used for preventing GC of commits we
|
||||
/// create.
|
||||
fn create_no_gc_ref() -> String {
|
||||
let mut no_gc_ref = NO_GC_REF_NAMESPACE.to_owned();
|
||||
let mut uuid_buffer = Uuid::encode_buffer();
|
||||
let uuid_str = Uuid::new_v4()
|
||||
.to_hyphenated()
|
||||
.encode_lower(&mut uuid_buffer);
|
||||
no_gc_ref.push_str(uuid_str);
|
||||
no_gc_ref
|
||||
}
|
||||
|
||||
fn write_note(
|
||||
git_repo: &git2::Repository,
|
||||
committer: &git2::Signature,
|
||||
|
@ -349,8 +365,14 @@ impl Store for GitStore {
|
|||
parents.push(parent_git_commit);
|
||||
}
|
||||
let parent_refs: Vec<_> = parents.iter().collect();
|
||||
let git_id =
|
||||
locked_repo.commit(None, &author, &committer, &message, &git_tree, &parent_refs)?;
|
||||
let git_id = locked_repo.commit(
|
||||
Some(&create_no_gc_ref()),
|
||||
&author,
|
||||
&committer,
|
||||
&message,
|
||||
&git_tree,
|
||||
&parent_refs,
|
||||
)?;
|
||||
let id = CommitId(git_id.as_bytes().to_vec());
|
||||
let note = serialize_note(contents);
|
||||
|
||||
|
@ -579,6 +601,40 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_has_ref() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let git_repo_path = temp_dir.path();
|
||||
let git_repo = git2::Repository::init(git_repo_path.clone()).unwrap();
|
||||
let store = GitStore::load(git_repo_path.to_owned());
|
||||
let signature = Signature {
|
||||
name: "Someone".to_string(),
|
||||
email: "someone@example.com".to_string(),
|
||||
timestamp: Timestamp {
|
||||
timestamp: MillisSinceEpoch(0),
|
||||
tz_offset: 0,
|
||||
},
|
||||
};
|
||||
let commit = Commit {
|
||||
parents: vec![],
|
||||
predecessors: vec![],
|
||||
root_tree: store.empty_tree_id().clone(),
|
||||
change_id: ChangeId(vec![]),
|
||||
description: "initial".to_string(),
|
||||
author: signature.clone(),
|
||||
committer: signature,
|
||||
is_open: false,
|
||||
is_pruned: false,
|
||||
};
|
||||
let commit_id = store.write_commit(&commit).unwrap();
|
||||
let git_refs: Vec<_> = git_repo
|
||||
.references_glob("refs/jj/keep/*")
|
||||
.unwrap()
|
||||
.map(|git_ref| git_ref.unwrap().target().unwrap())
|
||||
.collect();
|
||||
assert_eq!(git_refs, vec![Oid::from_bytes(&commit_id.0).unwrap()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping_git_commit_id() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
|
Loading…
Reference in a new issue