forked from mirrors/jj
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.
This commit is contained in:
parent
df53871daf
commit
0a4ef1030f
4 changed files with 88 additions and 12 deletions
|
@ -233,7 +233,14 @@ impl ReadonlyRepo {
|
|||
user_settings: &UserSettings,
|
||||
wc_path: PathBuf,
|
||||
) -> Result<Arc<ReadonlyRepo>, 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, RepoLoadError> {
|
||||
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<Arc<ReadonlyRepo>, RepoLoadError> {
|
||||
pub fn load_at_head(self) -> Result<Arc<ReadonlyRepo>, 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<Arc<ReadonlyRepo>, 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<Arc<ReadonlyRepo>, RepoLoadError> {
|
||||
let working_copy = WorkingCopy::load(
|
||||
self.store.clone(),
|
||||
self.wc_path.clone(),
|
||||
|
|
|
@ -403,6 +403,21 @@ impl ReadonlyView {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_at(
|
||||
store: Arc<StoreWrapper>,
|
||||
op_store: Arc<dyn OpStore>,
|
||||
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) =
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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<RepoLoadError> for CommandError {
|
|||
}
|
||||
|
||||
fn get_repo(ui: &Ui, matches: &ArgMatches) -> Result<Arc<ReadonlyRepo>, 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<Operation, Com
|
|||
let view = repo.view();
|
||||
if op_str == "@" {
|
||||
Ok(view.as_view_ref().base_op_head())
|
||||
} else if let Ok(binary_op_id) = hex::decode(op_str) {
|
||||
} else {
|
||||
resolve_single_op_from_store(&repo.view().op_store(), op_str)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_single_op_from_store(
|
||||
op_store: &Arc<dyn OpStore>,
|
||||
op_str: &str,
|
||||
) -> Result<Operation, CommandError> {
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue