mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-18 02:04:19 +00:00
Transaction: allow writing a transaction to the OpStore without publishing it
It can be useful to write an operation to the `OpStore` without also making it visible when you load the repo. I had planned to add that functionality at least for hooks, so the hooks can be run commands with `jj --at-op=<operation>` and decide whether to publish the operation. However, the immediate goal is to let us rewrite `op_heads_store::merge_op_heads()` to use the usual `Transaction` API. That needs to be able to just write the operation without publishing it, since the publishing step takes a long, which `op_heads_store::merge_op_heads()` (its caller, actually) has already taken.
This commit is contained in:
parent
337b15c98d
commit
27293829d6
2 changed files with 76 additions and 4 deletions
|
@ -15,6 +15,7 @@
|
|||
use crate::commit::Commit;
|
||||
use crate::evolution::MutableEvolution;
|
||||
use crate::index::MutableIndex;
|
||||
use crate::op_heads_store::OpHeadsStore;
|
||||
use crate::op_store;
|
||||
use crate::operation::Operation;
|
||||
use crate::repo::{MutableRepo, ReadonlyRepo, RepoRef};
|
||||
|
@ -120,7 +121,15 @@ impl<'r> Transaction<'r> {
|
|||
mut_repo.set_view(data);
|
||||
}
|
||||
|
||||
pub fn commit(mut self) -> Operation {
|
||||
/// Writes the transaction to the operation store and publishes it.
|
||||
pub fn commit(self) -> Operation {
|
||||
self.write().publish()
|
||||
}
|
||||
|
||||
/// Writes the transaction to the operation store, but does not publish it.
|
||||
/// That means that a repo can be loaded at the operation, but the
|
||||
/// operation will not be seen when loading the repo at head.
|
||||
pub fn write(mut self) -> UnpublishedOperation {
|
||||
let mut_repo = Arc::try_unwrap(self.repo.take().unwrap()).ok().unwrap();
|
||||
let base_repo = mut_repo.base_repo();
|
||||
let index_store = base_repo.index_store();
|
||||
|
@ -130,9 +139,8 @@ impl<'r> Transaction<'r> {
|
|||
index_store
|
||||
.associate_file_with_operation(&index, operation.id())
|
||||
.unwrap();
|
||||
base_repo.op_heads_store().update_op_heads(&operation);
|
||||
self.closed = true;
|
||||
operation
|
||||
UnpublishedOperation::new(base_repo.op_heads_store().clone(), operation)
|
||||
}
|
||||
|
||||
pub fn discard(mut self) {
|
||||
|
@ -140,10 +148,53 @@ impl<'r> Transaction<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r> Drop for Transaction<'r> {
|
||||
impl Drop for Transaction<'_> {
|
||||
fn drop(&mut self) {
|
||||
if !std::thread::panicking() {
|
||||
debug_assert!(self.closed, "Transaction was dropped without being closed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnpublishedOperation {
|
||||
op_heads_store: Arc<OpHeadsStore>,
|
||||
operation: Option<Operation>,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
impl UnpublishedOperation {
|
||||
fn new(op_heads_store: Arc<OpHeadsStore>, operation: Operation) -> Self {
|
||||
UnpublishedOperation {
|
||||
op_heads_store,
|
||||
operation: Some(operation),
|
||||
closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> &Operation {
|
||||
self.operation.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn publish(mut self) -> Operation {
|
||||
let operation = self.operation.take().unwrap();
|
||||
self.op_heads_store.update_op_heads(&operation);
|
||||
self.closed = true;
|
||||
operation
|
||||
}
|
||||
|
||||
pub fn leave_unpublished(mut self) -> Operation {
|
||||
self.closed = true;
|
||||
self.operation.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UnpublishedOperation {
|
||||
fn drop(&mut self) {
|
||||
if !std::thread::panicking() {
|
||||
debug_assert!(
|
||||
self.closed,
|
||||
"UnpublishedOperation was dropped without being closed."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,27 @@ fn list_dir(dir: &Path) -> Vec<String> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_unpublished_operation(use_git: bool) {
|
||||
// Test that the operation doesn't get published until that's requested.
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
let op_heads_dir = repo.repo_path().join("op_heads");
|
||||
let op_id0 = repo.view().op_id().clone();
|
||||
assert_eq!(list_dir(&op_heads_dir), vec![repo.view().op_id().hex()]);
|
||||
|
||||
let mut tx1 = repo.start_transaction("transaction 1");
|
||||
testutils::create_random_commit(&settings, &repo).write_to_transaction(&mut tx1);
|
||||
let unpublished_op = tx1.write();
|
||||
let op_id1 = unpublished_op.operation().id().clone();
|
||||
assert_ne!(op_id1, op_id0);
|
||||
assert_eq!(list_dir(&op_heads_dir), vec![op_id0.hex()]);
|
||||
unpublished_op.publish();
|
||||
assert_eq!(list_dir(&op_heads_dir), vec![op_id1.hex()]);
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_consecutive_operations(use_git: bool) {
|
||||
|
|
Loading…
Reference in a new issue