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 backoff::{ExponentialBackoff, Operation};
|
||||||
use std::ops::Deref;
|
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 COMMITS_NOTES_REF: &str = "refs/notes/jj/commits";
|
||||||
const CONFLICT_SUFFIX: &str = ".jjconflict";
|
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(
|
fn write_note(
|
||||||
git_repo: &git2::Repository,
|
git_repo: &git2::Repository,
|
||||||
committer: &git2::Signature,
|
committer: &git2::Signature,
|
||||||
|
@ -349,8 +365,14 @@ impl Store for GitStore {
|
||||||
parents.push(parent_git_commit);
|
parents.push(parent_git_commit);
|
||||||
}
|
}
|
||||||
let parent_refs: Vec<_> = parents.iter().collect();
|
let parent_refs: Vec<_> = parents.iter().collect();
|
||||||
let git_id =
|
let git_id = locked_repo.commit(
|
||||||
locked_repo.commit(None, &author, &committer, &message, &git_tree, &parent_refs)?;
|
Some(&create_no_gc_ref()),
|
||||||
|
&author,
|
||||||
|
&committer,
|
||||||
|
&message,
|
||||||
|
&git_tree,
|
||||||
|
&parent_refs,
|
||||||
|
)?;
|
||||||
let id = CommitId(git_id.as_bytes().to_vec());
|
let id = CommitId(git_id.as_bytes().to_vec());
|
||||||
let note = serialize_note(contents);
|
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]
|
#[test]
|
||||||
fn overlapping_git_commit_id() {
|
fn overlapping_git_commit_id() {
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue