git_store: prevent conflict data from being GC'd

Before this commit, running Git's GC in a Git repo backing a Jujube
repo would risk deleting the conflict data we store as blobs in the
Git repo. This commit fixes that by adding a Git note pointing to the
conflict blob.

I wasn't able to add a test case for this because libgit2 doesn't
support gc [1]. Just testing that the ref is there doesn't seem very
useful.

 [1] https://github.com/libgit2/libgit2/issues/3247
This commit is contained in:
Martin von Zweigbergk 2020-12-25 00:20:56 -08:00
parent 3613fe3f59
commit 9138de6ff2

View file

@ -30,7 +30,8 @@ use crate::store::{
use backoff::{ExponentialBackoff, Operation};
use std::ops::Deref;
const NOTES_REF: &str = "refs/notes/jj/commits";
const COMMITS_NOTES_REF: &str = "refs/notes/jj/commits";
const CONFLICTS_NOTES_REF: &str = "refs/notes/jj/conflicts";
const CONFLICT_SUFFIX: &str = ".jjconflict";
impl From<git2::Error> for StoreError {
@ -318,7 +319,9 @@ impl Store for GitStore {
is_pruned: false,
};
let maybe_note = locked_repo.find_note(Some(NOTES_REF), git_commit_id).ok();
let maybe_note = locked_repo
.find_note(Some(COMMITS_NOTES_REF), git_commit_id)
.ok();
if let Some(note) = maybe_note {
deserialize_note(&mut commit, note.message().unwrap());
}
@ -350,7 +353,13 @@ impl Store for GitStore {
// TODO: Include the extra commit data in commit headers instead of a ref.
// Unfortunately, it doesn't seem like libgit2-rs supports that. Perhaps
// we'll have to serialize/deserialize the commit data ourselves.
write_note(locked_repo.deref(), &committer, NOTES_REF, git_id, &note)?;
write_note(
locked_repo.deref(),
&committer,
COMMITS_NOTES_REF,
git_id,
&note,
)?;
Ok(id)
}
@ -373,8 +382,23 @@ impl Store for GitStore {
let json_string = json.to_string();
let bytes = json_string.as_bytes();
let locked_repo = self.repo.lock().unwrap();
// TODO: add a ref pointing to it so it won't get GC'd
let oid = locked_repo.blob(bytes).unwrap();
let signature = git2::Signature::now("Jujube", "jj@example.com").unwrap();
let note_result = write_note(
locked_repo.deref(),
&signature,
CONFLICTS_NOTES_REF,
oid,
"Conflict object used by Jujube",
);
match note_result {
// It's fine if the conflict already existed (no need to update the note), but
// any other error is unexpected.
Err(err) if err.code() != git2::ErrorCode::Exists => {
return Err(StoreError::from(err));
}
_ => {}
}
Ok(ConflictId(oid.as_bytes().to_vec()))
}
}