From 5ab2854df6ea9eddce18195ef3bda37da0e1eab7 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sun, 18 Sep 2022 17:33:39 -0700 Subject: [PATCH] backend: make backend aware of root commit I had made the backends unaware of the virtual root commit because they don't need to know about it, and we could avoid some duplicated code by putting that in `Store` instead. However, as we saw in b21a123bc894, the root commit being virtual has some user-visible effects (they can't create a merge with the root and some other commit). So I'm thinking that we may want to make the root commit an actual commit, depending on which backend is used. Specificially, when using the Git backend, we cannot record the root commit as an actual parent since Git would fail when trying to look it up. Backends that don't need compatibility can make the root commit an actual commit, however. This commit therefore makes the backends aware of the root commit. It makes it remain a virtual commit in the Git backend, and makes it an actual commit in the `LocalBackend`. This commit breaks any existing repos using the `LocalBackend`, but there shouldn't be any such repos other than for testing. --- lib/src/backend.rs | 25 +++++++++++++++++++++++++ lib/src/git_backend.rs | 17 ++++++++++++++--- lib/src/local_backend.rs | 17 ++++++++++++++--- lib/src/store.rs | 39 ++++----------------------------------- 4 files changed, 57 insertions(+), 41 deletions(-) diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 5a6c203ad..c267dc9a5 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -374,6 +374,29 @@ impl Tree { } } +pub fn make_root_commit(empty_tree_id: TreeId) -> Commit { + let timestamp = Timestamp { + timestamp: MillisSinceEpoch(0), + tz_offset: 0, + }; + let signature = Signature { + name: String::new(), + email: String::new(), + timestamp, + }; + let change_id = ChangeId::new(vec![0; 16]); + Commit { + parents: vec![], + predecessors: vec![], + root_tree: empty_tree_id, + change_id, + description: String::new(), + author: signature.clone(), + committer: signature, + is_open: false, + } +} + pub trait Backend: Send + Sync + Debug { fn hash_length(&self) -> usize; @@ -387,6 +410,8 @@ pub trait Backend: Send + Sync + Debug { fn write_symlink(&self, path: &RepoPath, target: &str) -> BackendResult; + fn root_commit_id(&self) -> &CommitId; + fn empty_tree_id(&self) -> &TreeId; fn read_tree(&self, path: &RepoPath, id: &TreeId) -> BackendResult; diff --git a/lib/src/git_backend.rs b/lib/src/git_backend.rs index bf9f52272..2a49edaef 100644 --- a/lib/src/git_backend.rs +++ b/lib/src/git_backend.rs @@ -24,9 +24,9 @@ use protobuf::Message; use uuid::Uuid; use crate::backend::{ - Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict, ConflictId, - ConflictPart, FileId, MillisSinceEpoch, Signature, SymlinkId, Timestamp, Tree, TreeId, - TreeValue, + make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict, + ConflictId, ConflictPart, FileId, MillisSinceEpoch, Signature, SymlinkId, Timestamp, Tree, + TreeId, TreeValue, }; use crate::repo_path::{RepoPath, RepoPathComponent}; use crate::stacked_table::{TableSegment, TableStore}; @@ -47,15 +47,18 @@ impl From for BackendError { pub struct GitBackend { repo: Mutex, + root_commit_id: CommitId, empty_tree_id: TreeId, extra_metadata_store: TableStore, } impl GitBackend { fn new(repo: git2::Repository, extra_metadata_store: TableStore) -> Self { + let root_commit_id = CommitId::from_bytes(&[0; HASH_LENGTH]); let empty_tree_id = TreeId::from_hex("4b825dc642cb6eb9a060e54bf8d69288fbee4904"); GitBackend { repo: Mutex::new(repo), + root_commit_id, empty_tree_id, extra_metadata_store, } @@ -208,6 +211,10 @@ impl Backend for GitBackend { Ok(SymlinkId::new(oid.as_bytes().to_vec())) } + fn root_commit_id(&self) -> &CommitId { + &self.root_commit_id + } + fn empty_tree_id(&self) -> &TreeId { &self.empty_tree_id } @@ -339,6 +346,10 @@ impl Backend for GitBackend { return Err(BackendError::NotFound); } + if *id == self.root_commit_id { + return Ok(make_root_commit(self.empty_tree_id.clone())); + } + let locked_repo = self.repo.lock().unwrap(); let git_commit_id = Oid::from_bytes(id.as_bytes())?; let commit = locked_repo.find_commit(git_commit_id)?; diff --git a/lib/src/local_backend.rs b/lib/src/local_backend.rs index ac3dec9f9..28aeb644d 100644 --- a/lib/src/local_backend.rs +++ b/lib/src/local_backend.rs @@ -23,9 +23,9 @@ use protobuf::{Message, MessageField}; use tempfile::{NamedTempFile, PersistError}; use crate::backend::{ - Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict, ConflictId, - ConflictPart, FileId, MillisSinceEpoch, Signature, SymlinkId, Timestamp, Tree, TreeId, - TreeValue, + make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict, + ConflictId, ConflictPart, FileId, MillisSinceEpoch, Signature, SymlinkId, Timestamp, Tree, + TreeId, TreeValue, }; use crate::file_util::persist_content_addressed_temp_file; use crate::repo_path::{RepoPath, RepoPathComponent}; @@ -51,6 +51,7 @@ impl From for BackendError { #[derive(Debug)] pub struct LocalBackend { path: PathBuf, + root_commit_id: CommitId, empty_tree_id: TreeId, } @@ -70,9 +71,11 @@ impl LocalBackend { } pub fn load(store_path: PathBuf) -> Self { + let root_commit_id = CommitId::from_bytes(&[0; 64]); let empty_tree_id = TreeId::from_hex("786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"); LocalBackend { path: store_path, + root_commit_id, empty_tree_id, } } @@ -165,6 +168,10 @@ impl Backend for LocalBackend { Ok(id) } + fn root_commit_id(&self) -> &CommitId { + &self.root_commit_id + } + fn empty_tree_id(&self) -> &TreeId { &self.empty_tree_id } @@ -216,6 +223,10 @@ impl Backend for LocalBackend { } fn read_commit(&self, id: &CommitId) -> BackendResult { + if *id == self.root_commit_id { + return Ok(make_root_commit(self.empty_tree_id.clone())); + } + let path = self.commit_path(id); let mut file = File::open(path).map_err(not_found_to_backend_error)?; diff --git a/lib/src/store.rs b/lib/src/store.rs index e4bd74d01..944bf1a0c 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -18,8 +18,7 @@ use std::sync::{Arc, RwLock}; use crate::backend; use crate::backend::{ - Backend, BackendResult, ChangeId, CommitId, Conflict, ConflictId, FileId, MillisSinceEpoch, - Signature, SymlinkId, Timestamp, TreeId, + Backend, BackendResult, CommitId, Conflict, ConflictId, FileId, SymlinkId, TreeId, }; use crate::commit::Commit; use crate::repo_path::RepoPath; @@ -31,17 +30,14 @@ use crate::tree_builder::TreeBuilder; #[derive(Debug)] pub struct Store { backend: Box, - root_commit_id: CommitId, commit_cache: RwLock>>, tree_cache: RwLock>>, } impl Store { pub fn new(backend: Box) -> Arc { - let root_commit_id = CommitId::new(vec![0; backend.hash_length()]); Arc::new(Store { backend, - root_commit_id, commit_cache: Default::default(), tree_cache: Default::default(), }) @@ -60,11 +56,11 @@ impl Store { } pub fn root_commit_id(&self) -> &CommitId { - &self.root_commit_id + self.backend.root_commit_id() } pub fn root_commit(self: &Arc) -> Commit { - self.get_commit(&self.root_commit_id).unwrap() + self.get_commit(self.backend.root_commit_id()).unwrap() } pub fn get_commit(self: &Arc, id: &CommitId) -> BackendResult { @@ -72,29 +68,6 @@ impl Store { Ok(Commit::new(self.clone(), id.clone(), data)) } - fn make_root_commit(&self) -> backend::Commit { - let timestamp = Timestamp { - timestamp: MillisSinceEpoch(0), - tz_offset: 0, - }; - let signature = Signature { - name: String::new(), - email: String::new(), - timestamp, - }; - let change_id = ChangeId::new(vec![0; 16]); - backend::Commit { - parents: vec![], - predecessors: vec![], - root_tree: self.backend.empty_tree_id().clone(), - change_id, - description: String::new(), - author: signature.clone(), - committer: signature, - is_open: false, - } - } - fn get_backend_commit(&self, id: &CommitId) -> BackendResult> { { let read_locked_cached = self.commit_cache.read().unwrap(); @@ -102,11 +75,7 @@ impl Store { return Ok(data); } } - let commit = if id == self.root_commit_id() { - self.make_root_commit() - } else { - self.backend.read_commit(id)? - }; + let commit = self.backend.read_commit(id)?; let data = Arc::new(commit); let mut write_locked_cache = self.commit_cache.write().unwrap(); write_locked_cache.insert(id.clone(), data.clone());