From 0a4ef1030f937a596daf395117bb853f033c0c8f Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Mon, 4 Jan 2021 09:40:46 -0800 Subject: [PATCH] repo: add support for loading at given operation without loading head op first The only way to load the repo at a current operation (as with `--at-op`) is currently to first load it at the head operation and then call `reload()` on the repo. This patch makes it so we can load the repo directly at the requested operation. --- lib/src/repo.rs | 25 +++++++++++++++++++++++-- lib/src/view.rs | 15 +++++++++++++++ lib/tests/test_load_repo.rs | 30 ++++++++++++++++++++++++++++++ src/commands.rs | 30 ++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/lib/src/repo.rs b/lib/src/repo.rs index b94e6476b..4a9b43acc 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -233,7 +233,14 @@ impl ReadonlyRepo { user_settings: &UserSettings, wc_path: PathBuf, ) -> Result, RepoLoadError> { - RepoLoader::init(user_settings, wc_path)?.load() + ReadonlyRepo::loader(user_settings, wc_path)?.load_at_head() + } + + pub fn loader( + user_settings: &UserSettings, + wc_path: PathBuf, + ) -> Result { + RepoLoader::init(user_settings, wc_path) } pub fn as_repo_ref(&self) -> RepoRef { @@ -381,12 +388,26 @@ impl RepoLoader { &self.op_store } - pub fn load(self) -> Result, RepoLoadError> { + pub fn load_at_head(self) -> Result, RepoLoadError> { let view = ReadonlyView::load( self.store.clone(), self.op_store.clone(), self.repo_path.join("view"), ); + self._finish_load(view) + } + + pub fn load_at(self, op: &Operation) -> Result, RepoLoadError> { + let view = ReadonlyView::load_at( + self.store.clone(), + self.op_store.clone(), + self.repo_path.join("view"), + op, + ); + self._finish_load(view) + } + + fn _finish_load(self, view: ReadonlyView) -> Result, RepoLoadError> { let working_copy = WorkingCopy::load( self.store.clone(), self.wc_path.clone(), diff --git a/lib/src/view.rs b/lib/src/view.rs index 430ac1fdd..5b8032dfd 100644 --- a/lib/src/view.rs +++ b/lib/src/view.rs @@ -403,6 +403,21 @@ impl ReadonlyView { } } + pub fn load_at( + store: Arc, + op_store: Arc, + path: PathBuf, + operation: &Operation, + ) -> Self { + ReadonlyView { + store, + path, + op_store, + op_id: operation.id().clone(), + data: operation.view().take_store_view(), + } + } + pub fn reload(&mut self) -> OperationId { let op_heads_dir = self.path.join("op_heads"); let (op_id, _operation, view) = diff --git a/lib/tests/test_load_repo.rs b/lib/tests/test_load_repo.rs index db5f189f9..52b2cac96 100644 --- a/lib/tests/test_load_repo.rs +++ b/lib/tests/test_load_repo.rs @@ -14,6 +14,8 @@ use jujube_lib::repo::{ReadonlyRepo, RepoLoadError}; use jujube_lib::testutils; +use std::sync::Arc; +use test_case::test_case; #[test] fn test_load_bad_path() { @@ -24,3 +26,31 @@ fn test_load_bad_path() { let result = ReadonlyRepo::load(&settings, wc_path.clone()); assert_eq!(result.err(), Some(RepoLoadError::NoRepoHere(wc_path))); } + +#[test_case(false ; "local store")] +#[test_case(true ; "git store")] +fn test_load_at_operation(use_git: bool) { + let settings = testutils::user_settings(); + let (_temp_dir, mut repo) = testutils::init_repo(&settings, use_git); + + let mut tx = repo.start_transaction("add commit"); + let commit = testutils::create_random_commit(&settings, &repo).write_to_transaction(&mut tx); + let op = tx.commit(); + Arc::get_mut(&mut repo).unwrap().reload(); + + let mut tx = repo.start_transaction("remove commit"); + tx.remove_head(&commit); + tx.commit(); + + // If we load the repo at head, we should not see the commit since it was + // removed + let loader = ReadonlyRepo::loader(&settings, repo.working_copy_path().clone()).unwrap(); + let head_repo = loader.load_at_head().unwrap(); + assert!(!head_repo.view().heads().contains(commit.id())); + + // If we load the repo at the previous operation, we should see the commit since + // it has not been removed yet + let loader = ReadonlyRepo::loader(&settings, repo.working_copy_path().clone()).unwrap(); + let old_repo = loader.load_at(&op).unwrap(); + assert!(old_repo.view().heads().contains(commit.id())); +} diff --git a/src/commands.rs b/src/commands.rs index 89d24ed48..220186e25 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -40,7 +40,7 @@ use jujube_lib::evolution::EvolveListener; use jujube_lib::files; use jujube_lib::files::DiffLine; use jujube_lib::git; -use jujube_lib::op_store::{OpStoreError, OperationId}; +use jujube_lib::op_store::{OpStore, OpStoreError, OperationId}; use jujube_lib::repo::{ReadonlyRepo, RepoLoadError}; use jujube_lib::repo_path::RepoPath; use jujube_lib::rewrite::{back_out_commit, merge_commit_trees, rebase_commit}; @@ -92,14 +92,15 @@ impl From for CommandError { } fn get_repo(ui: &Ui, matches: &ArgMatches) -> Result, CommandError> { - let repo_path_str = matches.value_of("repository").unwrap(); - let repo_path = ui.cwd().join(repo_path_str); - let mut repo = ReadonlyRepo::load(ui.settings(), repo_path)?; + let wc_path_str = matches.value_of("repository").unwrap(); + let wc_path = ui.cwd().join(wc_path_str); + let loader = ReadonlyRepo::loader(ui.settings(), wc_path)?; if let Some(op_str) = matches.value_of("at_op") { - let op = resolve_single_op(&repo, op_str)?; - Arc::get_mut(&mut repo).unwrap().reload_at(&op); + let op = resolve_single_op_from_store(loader.op_store(), op_str)?; + Ok(loader.load_at(&op)?) + } else { + Ok(loader.load_at_head()?) } - Ok(repo) } fn resolve_commit_id_prefix( @@ -205,10 +206,19 @@ fn resolve_single_op(repo: &ReadonlyRepo, op_str: &str) -> Result, + op_str: &str, +) -> Result { + if let Ok(binary_op_id) = hex::decode(op_str) { let op_id = OperationId(binary_op_id); - match view.as_view_ref().get_operation(&op_id) { - Ok(operation) => Ok(operation), + match op_store.read_operation(&op_id) { + Ok(operation) => Ok(Operation::new(op_store.clone(), op_id, operation)), Err(OpStoreError::NotFound) => Err(CommandError::UserError(format!( "Operation id not found: {}", op_str