repo: replace Repo trait by enum with readonly and mutable variants

I want to keep the index updated within the transaction. I tried doing
that by adding a `trait Index`, implemented by `ReadonlyIndex` and
`MutableIndex`. However, `ReadonlyRepo::index` is of type
`Mutex<Option<Arc<IndexFile>>>` (because it is lazily initialized),
and we cannot get a `&dyn Index` that lives long enough to be returned
from a `Repo::index()` from that. It seems the best solution is to
instead create an `Index` enum (instead of a trait), with one readonly
and one mutable variant. This commit starts the migration to that
design by replacing the `Repo` trait by an enum. I never intended for
there there to be more implementations of `Repo` than `ReadonlyRepo`
and `MutableRepo` anyway.
This commit is contained in:
Martin von Zweigbergk 2021-01-30 23:44:31 -08:00
parent a1983ebe96
commit f1666375bd
17 changed files with 238 additions and 164 deletions

View file

@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex};
use crate::commit::Commit;
use crate::commit_builder::CommitBuilder;
use crate::dag_walk::{bfs, closest_common_node, leaves, walk_ancestors};
use crate::repo::{MutableRepo, ReadonlyRepo, Repo};
use crate::repo::{MutableRepo, ReadonlyRepo};
use crate::repo_path::DirRepoPath;
use crate::rewrite::{merge_commit_trees, rebase_commit};
use crate::settings::UserSettings;

View file

@ -13,7 +13,6 @@
// limitations under the License.
use crate::commit::Commit;
use crate::repo::Repo;
use crate::store::CommitId;
use crate::transaction::Transaction;
use thiserror::Error;
@ -31,7 +30,7 @@ pub fn import_refs(
) -> Result<(), GitImportError> {
let store = tx.store().clone();
let git_refs = git_repo.references()?;
let existing_git_refs: Vec<_> = tx.as_repo().view().git_refs().keys().cloned().collect();
let existing_git_refs: Vec<_> = tx.as_repo_ref().view().git_refs().keys().cloned().collect();
// TODO: Store the id of the previous import and read it back here, so we can
// merge the views instead of overwriting.
for existing_git_ref in existing_git_refs {

View file

@ -30,9 +30,10 @@ use crate::commit::Commit;
use crate::dag_walk;
use crate::op_store::OperationId;
use crate::operation::Operation;
use crate::repo::{ReadonlyRepo, Repo};
use crate::repo::ReadonlyRepo;
use crate::store::CommitId;
use crate::store_wrapper::StoreWrapper;
use crate::view::View;
use std::fmt::{Debug, Formatter};
use std::ops::Bound;

View file

@ -54,10 +54,35 @@ impl From<StoreError> for RepoError {
pub type RepoResult<T> = Result<T, RepoError>;
pub trait Repo: Sync {
fn store(&self) -> &Arc<StoreWrapper>;
fn view(&self) -> &dyn View;
fn evolution(&self) -> &dyn Evolution;
// TODO: Should we implement From<&ReadonlyRepo> and From<&MutableRepo> for
// RepoRef?
#[derive(Clone, Copy)]
pub enum RepoRef<'a, 'r: 'a> {
Readonly(&'a ReadonlyRepo),
Mutable(&'a MutableRepo<'r>),
}
impl<'a, 'r> RepoRef<'a, 'r> {
pub fn store(&self) -> &'a Arc<StoreWrapper> {
match self {
RepoRef::Readonly(repo) => repo.store(),
RepoRef::Mutable(repo) => repo.store(),
}
}
pub fn view(&self) -> &'a dyn View {
match self {
RepoRef::Readonly(repo) => repo.view(),
RepoRef::Mutable(repo) => repo.view(),
}
}
pub fn evolution(&self) -> &'a dyn Evolution {
match self {
RepoRef::Readonly(repo) => repo.evolution(),
RepoRef::Mutable(repo) => repo.evolution(),
}
}
}
pub struct ReadonlyRepo {
@ -241,6 +266,10 @@ impl ReadonlyRepo {
Ok(repo)
}
pub fn as_repo_ref(&self) -> RepoRef {
RepoRef::Readonly(&self)
}
pub fn repo_path(&self) -> &PathBuf {
&self.repo_path
}
@ -249,6 +278,16 @@ impl ReadonlyRepo {
&self.wc_path
}
pub fn view(&self) -> &ReadonlyView {
&self.view
}
pub fn evolution<'a>(&'a self) -> &ReadonlyEvolution<'a> {
let evolution: &ReadonlyEvolution<'static> = self.evolution.as_ref().unwrap();
let evolution: &ReadonlyEvolution<'a> = unsafe { std::mem::transmute(evolution) };
evolution
}
pub fn index(&self) -> Arc<ReadonlyIndex> {
let mut locked_index = self.index.lock().unwrap();
if locked_index.is_none() {
@ -315,40 +354,12 @@ impl ReadonlyRepo {
}
}
impl Repo for ReadonlyRepo {
fn store(&self) -> &Arc<StoreWrapper> {
&self.store
}
fn view(&self) -> &dyn View {
&self.view
}
fn evolution(&self) -> &dyn Evolution {
self.evolution.as_ref().unwrap()
}
}
pub struct MutableRepo<'r> {
repo: &'r ReadonlyRepo,
view: Option<MutableView>,
evolution: Option<MutableEvolution<'static, 'static>>,
}
impl<'r> Repo for MutableRepo<'r> {
fn store(&self) -> &Arc<StoreWrapper> {
self.repo.store()
}
fn view(&self) -> &dyn View {
self.view.as_ref().unwrap()
}
fn evolution(&self) -> &dyn Evolution {
self.evolution.as_ref().unwrap()
}
}
impl<'r> MutableRepo<'r> {
pub fn new(
repo: &'r ReadonlyRepo,
@ -371,10 +382,22 @@ impl<'r> MutableRepo<'r> {
mut_repo
}
pub fn as_repo_ref(&self) -> RepoRef {
RepoRef::Mutable(&self)
}
pub fn store(&self) -> &Arc<StoreWrapper> {
self.repo.store()
}
pub fn base_repo(&self) -> &'r ReadonlyRepo {
self.repo
}
pub fn view(&self) -> &dyn View {
self.view.as_ref().unwrap()
}
pub fn view_mut(&mut self) -> &mut MutableView {
self.view.as_mut().unwrap()
}
@ -383,6 +406,10 @@ impl<'r> MutableRepo<'r> {
self.view.take().unwrap()
}
pub fn evolution(&self) -> &dyn Evolution {
self.evolution.as_ref().unwrap()
}
pub fn evolution_mut<'m>(&'m mut self) -> &'m mut MutableEvolution<'r, 'm> {
let evolution: &mut MutableEvolution<'static, 'static> = self.evolution.as_mut().unwrap();
let evolution: &mut MutableEvolution<'r, 'm> = unsafe { std::mem::transmute(evolution) };

View file

@ -17,12 +17,11 @@ use crate::commit_builder::CommitBuilder;
use crate::conflicts;
use crate::op_store;
use crate::operation::Operation;
use crate::repo::{MutableRepo, ReadonlyRepo, Repo};
use crate::repo::{MutableRepo, ReadonlyRepo, RepoRef};
use crate::settings::UserSettings;
use crate::store;
use crate::store::{CommitId, Timestamp};
use crate::store_wrapper::StoreWrapper;
use std::ops::Deref;
use std::sync::Arc;
pub struct Transaction<'r> {
@ -50,8 +49,8 @@ impl<'r> Transaction<'r> {
self.repo.as_ref().unwrap().store()
}
pub fn as_repo<'a: 'r>(&'a self) -> &(impl Repo + 'a) {
self.repo.as_ref().unwrap().deref()
pub fn as_repo_ref(&self) -> RepoRef {
self.repo.as_ref().unwrap().as_repo_ref()
}
pub fn as_repo_mut(&mut self) -> &mut MutableRepo<'r> {
@ -65,12 +64,15 @@ impl<'r> Transaction<'r> {
}
pub fn check_out(&mut self, settings: &UserSettings, commit: &Commit) -> Commit {
let current_checkout_id = self.as_repo().view().checkout().clone();
let current_checkout_id = self.as_repo_ref().view().checkout().clone();
let current_checkout = self.store().get_commit(&current_checkout_id).unwrap();
assert!(current_checkout.is_open(), "current checkout is closed");
if current_checkout.is_empty()
&& !(current_checkout.is_pruned()
|| self.as_repo().evolution().is_obsolete(&current_checkout_id))
|| self
.as_repo_ref()
.evolution()
.is_obsolete(&current_checkout_id))
{
// Prune the checkout we're leaving if it's empty.
// TODO: Also prune it if the only changes are conflicts that got materialized.

View file

@ -16,8 +16,9 @@ use std::path::PathBuf;
use tempfile::TempDir;
use jujube_lib::repo::{ReadonlyRepo, Repo};
use jujube_lib::repo::ReadonlyRepo;
use jujube_lib::testutils;
use jujube_lib::view::View;
use std::sync::Arc;
use test_case::test_case;

View file

@ -15,8 +15,9 @@
use std::thread;
use jujube_lib::dag_walk;
use jujube_lib::repo::{ReadonlyRepo, Repo};
use jujube_lib::repo::ReadonlyRepo;
use jujube_lib::testutils;
use jujube_lib::view::View;
use std::sync::Arc;
use test_case::test_case;

View file

@ -16,7 +16,7 @@ use jujube_lib::commit::Commit;
use jujube_lib::commit_builder::CommitBuilder;
use jujube_lib::evolution::evolve;
use jujube_lib::evolution::EvolveListener;
use jujube_lib::repo::{ReadonlyRepo, Repo};
use jujube_lib::repo::ReadonlyRepo;
use jujube_lib::repo_path::FileRepoPath;
use jujube_lib::settings::UserSettings;
use jujube_lib::testutils;
@ -37,8 +37,8 @@ fn test_obsolete_and_orphan(use_git: bool) {
// A commit without successors should not be obsolete and not an orphan.
let original = child_commit(&settings, &repo, &root_commit).write_to_transaction(&mut tx);
assert!(!tx.as_repo().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo().evolution().is_orphan(original.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(original.id()));
// A commit with a successor with a different change_id should not be obsolete.
let child = child_commit(&settings, &repo, &original).write_to_transaction(&mut tx);
@ -46,30 +46,30 @@ fn test_obsolete_and_orphan(use_git: bool) {
let cherry_picked = child_commit(&settings, &repo, &root_commit)
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert!(!tx.as_repo().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo().evolution().is_orphan(original.id()));
assert!(!tx.as_repo().evolution().is_obsolete(child.id()));
assert!(!tx.as_repo().evolution().is_orphan(child.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(original.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(child.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(child.id()));
// A commit with a successor with the same change_id should be obsolete.
let rewritten = child_commit(&settings, &repo, &root_commit)
.set_predecessors(vec![original.id().clone()])
.set_change_id(original.change_id().clone())
.write_to_transaction(&mut tx);
assert!(tx.as_repo().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo().evolution().is_obsolete(child.id()));
assert!(tx.as_repo().evolution().is_orphan(child.id()));
assert!(tx.as_repo().evolution().is_orphan(grandchild.id()));
assert!(!tx.as_repo().evolution().is_obsolete(cherry_picked.id()));
assert!(!tx.as_repo().evolution().is_orphan(cherry_picked.id()));
assert!(!tx.as_repo().evolution().is_obsolete(rewritten.id()));
assert!(!tx.as_repo().evolution().is_orphan(rewritten.id()));
assert!(tx.as_repo_ref().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(child.id()));
assert!(tx.as_repo_ref().evolution().is_orphan(child.id()));
assert!(tx.as_repo_ref().evolution().is_orphan(grandchild.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(cherry_picked.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(cherry_picked.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(rewritten.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(rewritten.id()));
// It should no longer be obsolete if we remove the successor.
tx.remove_head(&rewritten);
assert!(!tx.as_repo().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo().evolution().is_orphan(child.id()));
assert!(!tx.as_repo().evolution().is_orphan(grandchild.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(original.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(child.id()));
assert!(!tx.as_repo_ref().evolution().is_orphan(grandchild.id()));
tx.discard();
}
@ -83,7 +83,10 @@ fn test_divergent(use_git: bool) {
// A single commit should not be divergent
let original = child_commit(&settings, &repo, &root_commit).write_to_transaction(&mut tx);
assert!(!tx.as_repo().evolution().is_divergent(original.change_id()));
assert!(!tx
.as_repo_ref()
.evolution()
.is_divergent(original.change_id()));
// Commits with the same change id are divergent, including the original commit
// (it's the change that's divergent)
@ -95,7 +98,10 @@ fn test_divergent(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.set_change_id(original.change_id().clone())
.write_to_transaction(&mut tx);
assert!(tx.as_repo().evolution().is_divergent(original.change_id()));
assert!(tx
.as_repo_ref()
.evolution()
.is_divergent(original.change_id()));
tx.discard();
}
@ -121,7 +127,10 @@ fn test_divergent_pruned(use_git: bool) {
.set_change_id(original.change_id().clone())
.set_pruned(true)
.write_to_transaction(&mut tx);
assert!(tx.as_repo().evolution().is_divergent(original.change_id()));
assert!(tx
.as_repo_ref()
.evolution()
.is_divergent(original.change_id()));
tx.discard();
}
@ -141,13 +150,16 @@ fn test_divergent_duplicate(use_git: bool) {
let cherry_picked2 = child_commit(&settings, &repo, &root_commit)
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert!(!tx.as_repo().evolution().is_divergent(original.change_id()));
assert!(!tx
.as_repo()
.as_repo_ref()
.evolution()
.is_divergent(original.change_id()));
assert!(!tx
.as_repo_ref()
.evolution()
.is_divergent(cherry_picked1.change_id()));
assert!(!tx
.as_repo()
.as_repo_ref()
.evolution()
.is_divergent(cherry_picked2.change_id()));
tx.discard();
@ -170,7 +182,7 @@ fn test_new_parent_rewritten(use_git: bool) {
.set_change_id(original.change_id().clone())
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![rewritten.id().clone()].into_iter().collect()
);
tx.discard();
@ -190,7 +202,7 @@ fn test_new_parent_cherry_picked(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![original.id().clone()].into_iter().collect()
);
tx.discard();
@ -214,7 +226,7 @@ fn test_new_parent_is_pruned(use_git: bool) {
.set_change_id(original.change_id().clone())
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![new_parent.id().clone()].into_iter().collect()
);
tx.discard();
@ -243,7 +255,7 @@ fn test_new_parent_divergent(use_git: bool) {
.set_change_id(original.change_id().clone())
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![
rewritten1.id().clone(),
rewritten2.id().clone(),
@ -284,7 +296,7 @@ fn test_new_parent_divergent_one_not_pruned(use_git: bool) {
.set_pruned(true)
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![
rewritten1.id().clone(),
parent2.id().clone(),
@ -327,7 +339,7 @@ fn test_new_parent_divergent_all_pruned(use_git: bool) {
.set_pruned(true)
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![
parent1.id().clone(),
parent2.id().clone(),
@ -363,7 +375,7 @@ fn test_new_parent_split(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![rewritten3.id().clone()].into_iter().collect()
);
tx.discard();
@ -397,7 +409,7 @@ fn test_new_parent_split_pruned_descendant(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![rewritten2.id().clone()].into_iter().collect()
);
tx.discard();
@ -431,7 +443,7 @@ fn test_new_parent_split_forked(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![rewritten2.id().clone(), rewritten3.id().clone()]
.into_iter()
.collect()
@ -467,7 +479,7 @@ fn test_new_parent_split_forked_pruned(use_git: bool) {
.set_predecessors(vec![original.id().clone()])
.write_to_transaction(&mut tx);
assert_eq!(
tx.as_repo().evolution().new_parent(original.id()),
tx.as_repo_ref().evolution().new_parent(original.id()),
vec![rewritten3.id().clone()].into_iter().collect()
);
tx.discard();

View file

@ -16,10 +16,11 @@ use git2::Oid;
use jujube_lib::commit::Commit;
use jujube_lib::git;
use jujube_lib::git::{GitFetchError, GitPushError};
use jujube_lib::repo::{ReadonlyRepo, Repo};
use jujube_lib::repo::ReadonlyRepo;
use jujube_lib::settings::UserSettings;
use jujube_lib::store::CommitId;
use jujube_lib::testutils;
use jujube_lib::view::View;
use maplit::hashset;
use std::collections::HashSet;
use std::path::PathBuf;
@ -69,7 +70,7 @@ fn test_import_refs() {
let mut tx = repo.start_transaction("test");
let heads_before: HashSet<_> = repo.view().heads().clone();
jujube_lib::git::import_refs(&mut tx, &git_repo).unwrap_or_default();
let view = tx.as_repo().view();
let view = tx.as_repo_ref().view();
let expected_heads: HashSet<_> = heads_before
.union(&hashset!(
commit_id(&commit3),
@ -125,7 +126,7 @@ fn test_import_refs_reimport() {
let mut tx = repo.start_transaction("test");
jujube_lib::git::import_refs(&mut tx, &git_repo).unwrap_or_default();
let view = tx.as_repo().view();
let view = tx.as_repo_ref().view();
// TODO: commit3 and commit4 should probably be removed
let expected_heads: HashSet<_> = heads_before
.union(&hashset!(
@ -277,7 +278,7 @@ fn test_import_refs_empty_git_repo() {
let heads_before = repo.view().heads().clone();
let mut tx = repo.start_transaction("test");
jujube_lib::git::import_refs(&mut tx, &git_repo).unwrap_or_default();
let view = tx.as_repo().view();
let view = tx.as_repo_ref().view();
assert_eq!(*view.heads(), heads_before);
assert_eq!(view.git_refs().len(), 0);
tx.discard();
@ -324,7 +325,7 @@ fn test_fetch_success() {
let mut tx = jj_repo.start_transaction("test");
git::fetch(&mut tx, &clone_git_repo, "origin").unwrap();
assert!(tx
.as_repo()
.as_repo_ref()
.view()
.heads()
.contains(&commit_id(&new_git_commit)));

View file

@ -13,9 +13,11 @@
// limitations under the License.
use jujube_lib::commit_builder::CommitBuilder;
use jujube_lib::repo::Repo;
use jujube_lib::evolution::Evolution;
use jujube_lib::repo::RepoRef;
use jujube_lib::store::CommitId;
use jujube_lib::testutils;
use jujube_lib::view::View;
use std::path::Path;
use std::sync::Arc;
use test_case::test_case;
@ -105,7 +107,7 @@ fn test_concurrent_operations(use_git: bool) {
assert_eq!(list_dir(&op_heads_dir), vec![merged_op_head_id.hex()]);
}
fn assert_heads(repo: &impl Repo, expected: Vec<&CommitId>) {
fn assert_heads(repo: RepoRef, expected: Vec<&CommitId>) {
let expected = expected.iter().cloned().cloned().collect();
assert_eq!(*repo.view().heads(), expected);
}
@ -126,12 +128,12 @@ fn test_isolation(use_git: bool) {
let mut tx1 = repo.start_transaction("transaction 1");
let mut tx2 = repo.start_transaction("transaction 2");
assert_heads(repo.as_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx1.as_repo(), vec![&wc_id, initial.id()]);
assert_heads(tx2.as_repo(), vec![&wc_id, initial.id()]);
assert_heads(repo.as_repo_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx1.as_repo_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx2.as_repo_ref(), vec![&wc_id, initial.id()]);
assert!(!repo.evolution().is_obsolete(initial.id()));
assert!(!tx1.as_repo().evolution().is_obsolete(initial.id()));
assert!(!tx2.as_repo().evolution().is_obsolete(initial.id()));
assert!(!tx1.as_repo_ref().evolution().is_obsolete(initial.id()));
assert!(!tx2.as_repo_ref().evolution().is_obsolete(initial.id()));
let rewrite1 = CommitBuilder::for_rewrite_from(&settings, repo.store(), &initial)
.set_description("rewrite1".to_string())
@ -142,25 +144,25 @@ fn test_isolation(use_git: bool) {
// Neither transaction has committed yet, so each transaction sees its own
// commit.
assert_heads(repo.as_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx1.as_repo(), vec![&wc_id, initial.id(), rewrite1.id()]);
assert_heads(tx2.as_repo(), vec![&wc_id, initial.id(), rewrite2.id()]);
assert_heads(repo.as_repo_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx1.as_repo_ref(), vec![&wc_id, initial.id(), rewrite1.id()]);
assert_heads(tx2.as_repo_ref(), vec![&wc_id, initial.id(), rewrite2.id()]);
assert!(!repo.evolution().is_obsolete(initial.id()));
assert!(tx1.as_repo().evolution().is_obsolete(initial.id()));
assert!(tx2.as_repo().evolution().is_obsolete(initial.id()));
assert!(tx1.as_repo_ref().evolution().is_obsolete(initial.id()));
assert!(tx2.as_repo_ref().evolution().is_obsolete(initial.id()));
// The base repo and tx2 don't see the commits from tx1.
tx1.commit();
assert_heads(repo.as_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx2.as_repo(), vec![&wc_id, initial.id(), rewrite2.id()]);
assert_heads(repo.as_repo_ref(), vec![&wc_id, initial.id()]);
assert_heads(tx2.as_repo_ref(), vec![&wc_id, initial.id(), rewrite2.id()]);
// The base repo still doesn't see the commits after both transactions commit.
tx2.commit();
assert_heads(repo.as_ref(), vec![&wc_id, initial.id()]);
assert_heads(repo.as_repo_ref(), vec![&wc_id, initial.id()]);
// After reload, the base repo sees both rewrites.
Arc::get_mut(&mut repo).unwrap().reload();
assert_heads(
repo.as_ref(),
repo.as_repo_ref(),
vec![&wc_id, initial.id(), rewrite1.id(), rewrite2.id()],
);
}

View file

@ -13,11 +13,11 @@
// limitations under the License.
use jujube_lib::commit_builder::CommitBuilder;
use jujube_lib::repo::Repo;
use jujube_lib::repo_path::FileRepoPath;
use jujube_lib::store::{Conflict, ConflictId, ConflictPart, TreeValue};
use jujube_lib::store_wrapper::StoreWrapper;
use jujube_lib::testutils;
use jujube_lib::view::View;
use std::sync::Arc;
use test_case::test_case;
@ -203,7 +203,7 @@ fn test_checkout_previous_not_empty(use_git: bool) {
.set_open(true)
.write_to_transaction(&mut tx);
tx.check_out(&settings, &new_checkout);
assert!(!tx.as_repo().evolution().is_obsolete(old_checkout.id()));
assert!(!tx.as_repo_ref().evolution().is_obsolete(old_checkout.id()));
tx.discard();
}
@ -232,7 +232,7 @@ fn test_checkout_previous_empty(use_git: bool) {
.set_open(true)
.write_to_transaction(&mut tx);
tx.check_out(&settings, &new_checkout);
assert!(tx.as_repo().evolution().is_obsolete(old_checkout.id()));
assert!(tx.as_repo_ref().evolution().is_obsolete(old_checkout.id()));
tx.discard();
}
@ -263,7 +263,7 @@ fn test_checkout_previous_empty_and_obsolete(use_git: bool) {
.set_open(true)
.write_to_transaction(&mut tx);
tx.check_out(&settings, &new_checkout);
let successors = tx.as_repo().evolution().successors(old_checkout.id());
let successors = tx.as_repo_ref().evolution().successors(old_checkout.id());
assert_eq!(successors.len(), 1);
assert_eq!(successors.iter().next().unwrap(), successor.id());
tx.discard();
@ -292,7 +292,7 @@ fn test_checkout_previous_empty_and_pruned(use_git: bool) {
.write_to_transaction(&mut tx);
tx.check_out(&settings, &new_checkout);
assert!(tx
.as_repo()
.as_repo_ref()
.evolution()
.successors(old_checkout.id())
.is_empty());
@ -315,9 +315,9 @@ fn test_add_head_success(use_git: bool) {
tx.discard();
let mut tx = repo.start_transaction("test");
assert!(!tx.as_repo().view().heads().contains(new_commit.id()));
assert!(!tx.as_repo_ref().view().heads().contains(new_commit.id()));
tx.add_head(&new_commit);
assert!(tx.as_repo().view().heads().contains(new_commit.id()));
assert!(tx.as_repo_ref().view().heads().contains(new_commit.id()));
tx.commit();
Arc::get_mut(&mut repo).unwrap().reload();
assert!(repo.view().heads().contains(new_commit.id()));
@ -344,7 +344,7 @@ fn test_add_head_ancestor(use_git: bool) {
let mut tx = repo.start_transaction("test");
tx.add_head(&commit1);
assert!(!tx.as_repo().view().heads().contains(commit1.id()));
assert!(!tx.as_repo_ref().view().heads().contains(commit1.id()));
tx.discard();
}
@ -368,9 +368,9 @@ fn test_remove_head(use_git: bool) {
Arc::get_mut(&mut repo).unwrap().reload();
let mut tx = repo.start_transaction("test");
assert!(tx.as_repo().view().heads().contains(commit3.id()));
assert!(tx.as_repo_ref().view().heads().contains(commit3.id()));
tx.remove_head(&commit3);
let heads = tx.as_repo().view().heads().clone();
let heads = tx.as_repo_ref().view().heads().clone();
assert!(!heads.contains(commit3.id()));
assert!(!heads.contains(commit2.id()));
assert!(!heads.contains(commit1.id()));
@ -404,10 +404,10 @@ fn test_remove_head_ancestor_git_ref(use_git: bool) {
Arc::get_mut(&mut repo).unwrap().reload();
let mut tx = repo.start_transaction("test");
let heads = tx.as_repo().view().heads().clone();
let heads = tx.as_repo_ref().view().heads().clone();
assert!(heads.contains(commit3.id()));
tx.remove_head(&commit3);
let heads = tx.as_repo().view().heads().clone();
let heads = tx.as_repo_ref().view().heads().clone();
assert!(!heads.contains(commit3.id()));
assert!(!heads.contains(commit2.id()));
assert!(heads.contains(commit1.id()));
@ -434,9 +434,17 @@ fn test_add_public_head(use_git: bool) {
Arc::get_mut(&mut repo).unwrap().reload();
let mut tx = repo.start_transaction("test");
assert!(!tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(!tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.add_public_head(&commit1);
assert!(tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.commit();
Arc::get_mut(&mut repo).unwrap().reload();
assert!(repo.view().public_heads().contains(commit1.id()));
@ -460,9 +468,17 @@ fn test_add_public_head_ancestor(use_git: bool) {
Arc::get_mut(&mut repo).unwrap().reload();
let mut tx = repo.start_transaction("test");
assert!(!tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(!tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.add_public_head(&commit1);
assert!(!tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(!tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.commit();
Arc::get_mut(&mut repo).unwrap().reload();
assert!(!repo.view().public_heads().contains(commit1.id()));
@ -483,9 +499,17 @@ fn test_remove_public_head(use_git: bool) {
Arc::get_mut(&mut repo).unwrap().reload();
let mut tx = repo.start_transaction("test");
assert!(tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.remove_public_head(&commit1);
assert!(!tx.as_repo().view().public_heads().contains(commit1.id()));
assert!(!tx
.as_repo_ref()
.view()
.public_heads()
.contains(commit1.id()));
tx.commit();
Arc::get_mut(&mut repo).unwrap().reload();
assert!(!repo.view().public_heads().contains(commit1.id()));

View file

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use jujube_lib::repo::Repo;
use jujube_lib::testutils;
use jujube_lib::view::View;
use maplit::hashset;
use test_case::test_case;
@ -46,7 +46,7 @@ fn test_heads_fork(use_git: bool) {
let wc = repo.working_copy_locked();
assert_eq!(
*tx.as_repo().view().heads(),
*tx.as_repo_ref().view().heads(),
hashset! {
wc.current_commit_id(),
child1.id().clone(),
@ -78,7 +78,7 @@ fn test_heads_merge(use_git: bool) {
let wc = repo.working_copy_locked();
assert_eq!(
*tx.as_repo().view().heads(),
*tx.as_repo_ref().view().heads(),
hashset! {wc.current_commit_id(), merge.id().clone()}
);
tx.discard();

View file

@ -16,12 +16,13 @@
use std::os::unix::fs::PermissionsExt;
use jujube_lib::commit_builder::CommitBuilder;
use jujube_lib::repo::{ReadonlyRepo, Repo};
use jujube_lib::repo::ReadonlyRepo;
use jujube_lib::repo_path::{FileRepoPath, RepoPath};
use jujube_lib::settings::UserSettings;
use jujube_lib::store::TreeValue;
use jujube_lib::testutils;
use jujube_lib::tree_builder::TreeBuilder;
use jujube_lib::view::View;
use std::fs::OpenOptions;
use std::io::Write;
use std::sync::Arc;

View file

@ -36,12 +36,12 @@ use jujube_lib::commit_builder::CommitBuilder;
use jujube_lib::conflicts;
use jujube_lib::dag_walk::{common_ancestor, topo_order_reverse, walk_ancestors};
use jujube_lib::evolution::evolve;
use jujube_lib::evolution::EvolveListener;
use jujube_lib::evolution::{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::repo::{ReadonlyRepo, Repo, RepoLoadError};
use jujube_lib::repo::{ReadonlyRepo, RepoLoadError, RepoRef};
use jujube_lib::repo_path::RepoPath;
use jujube_lib::rewrite::{back_out_commit, merge_commit_trees, rebase_commit};
use jujube_lib::settings::UserSettings;
@ -49,6 +49,7 @@ use jujube_lib::store::{CommitId, Timestamp};
use jujube_lib::store::{StoreError, TreeValue};
use jujube_lib::tree::Tree;
use jujube_lib::trees::TreeValueDiff;
use jujube_lib::view::View;
use jujube_lib::working_copy::{CheckoutStats, WorkingCopy};
use self::chrono::{FixedOffset, TimeZone, Utc};
@ -237,7 +238,7 @@ fn update_working_copy(
return Ok(None);
}
ui.write("leaving: ");
ui.write_commit_summary(repo, &old_commit);
ui.write_commit_summary(repo.as_repo_ref(), &old_commit);
ui.write("\n");
// TODO: CheckoutError::ConcurrentCheckout should probably just result in a
// warning for most commands (but be an error for the checkout command)
@ -249,14 +250,14 @@ fn update_working_copy(
))
})?;
ui.write("now at: ");
ui.write_commit_summary(repo, &new_commit);
ui.write_commit_summary(repo.as_repo_ref(), &new_commit);
ui.write("\n");
Ok(Some(stats))
}
fn update_checkout_after_rewrite(ui: &mut Ui, tx: &mut Transaction) {
// TODO: Perhaps this method should be in Transaction.
let repo = tx.as_repo();
let repo = tx.as_repo_ref();
let new_checkout_candidates = repo.evolution().new_parent(repo.view().checkout());
if new_checkout_candidates.is_empty() {
return;
@ -929,10 +930,10 @@ fn cmd_status(
let wc = owned_wc.lock().unwrap();
let commit = wc.commit(ui.settings(), mut_repo);
ui.write("Working copy : ");
ui.write_commit_summary(repo.as_ref(), &commit);
ui.write_commit_summary(repo.as_repo_ref(), &commit);
ui.write("\n");
ui.write("Parent commit: ");
ui.write_commit_summary(repo.as_ref(), &commit.parents()[0]);
ui.write_commit_summary(repo.as_repo_ref(), &commit.parents()[0]);
ui.write("\n");
ui.write("Diff summary:\n");
show_diff_summary(ui, &commit.parents()[0].tree(), &commit.tree());
@ -1035,7 +1036,8 @@ fn cmd_log(
}
}
};
let template = crate::template_parser::parse_commit_template(repo.as_ref(), &template_string);
let template =
crate::template_parser::parse_commit_template(repo.as_repo_ref(), &template_string);
let mut styler = ui.styler();
let mut styler = styler.as_mut();
@ -1108,7 +1110,8 @@ fn cmd_obslog(
}
}
};
let template = crate::template_parser::parse_commit_template(repo.as_ref(), &template_string);
let template =
crate::template_parser::parse_commit_template(repo.as_repo_ref(), &template_string);
let mut styler = ui.styler();
let mut styler = styler.as_mut();
@ -1284,7 +1287,7 @@ fn cmd_duplicate(
.generate_new_change_id()
.write_to_transaction(&mut tx);
ui.write("created: ");
ui.write_commit_summary(tx.as_repo(), &new_commit);
ui.write_commit_summary(tx.as_repo_ref(), &new_commit);
ui.write("\n");
tx.commit();
Ok(())
@ -1335,7 +1338,7 @@ fn cmd_new(
);
let mut tx = repo.start_transaction("new empty commit");
let new_commit = commit_builder.write_to_transaction(&mut tx);
if tx.as_repo().view().checkout() == parent.id() {
if tx.as_repo_ref().view().checkout() == parent.id() {
tx.check_out(ui.settings(), &new_commit);
}
tx.commit();
@ -1460,7 +1463,7 @@ fn cmd_restore(
.set_tree(tree_id)
.write_to_transaction(&mut tx);
ui.write("Created ");
ui.write_commit_summary(tx.as_repo(), &new_commit);
ui.write_commit_summary(tx.as_repo_ref(), &new_commit);
ui.write("\n");
update_checkout_after_rewrite(ui, &mut tx);
tx.commit();
@ -1492,7 +1495,7 @@ fn cmd_edit(
.set_tree(tree_id)
.write_to_transaction(&mut tx);
ui.write("Created ");
ui.write_commit_summary(tx.as_repo(), &new_commit);
ui.write_commit_summary(tx.as_repo_ref(), &new_commit);
ui.write("\n");
update_checkout_after_rewrite(ui, &mut tx);
tx.commit();
@ -1535,9 +1538,9 @@ fn cmd_split(
.set_description(second_description)
.write_to_transaction(&mut tx);
ui.write("First part: ");
ui.write_commit_summary(tx.as_repo(), &first_commit);
ui.write_commit_summary(tx.as_repo_ref(), &first_commit);
ui.write("Second part: ");
ui.write_commit_summary(tx.as_repo(), &second_commit);
ui.write_commit_summary(tx.as_repo_ref(), &second_commit);
ui.write("\n");
update_checkout_after_rewrite(ui, &mut tx);
tx.commit();
@ -1660,7 +1663,7 @@ fn cmd_evolve<'s>(
struct Listener<'a, 's, 'r> {
ui: &'a mut Ui<'s>,
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
};
impl<'a, 's, 'r> EvolveListener for Listener<'a, 's, 'r> {
@ -1710,9 +1713,9 @@ fn cmd_evolve<'s>(
let user_settings = ui.settings().clone();
let mut listener = Listener {
ui,
// TODO: This should be using tx.as_repo() so the templater sees the updated state, but
// TODO: This should be using tx.as_repo_ref() so the templater sees the updated state, but
// we can't do that because we let evolution::evolve() mutably borrow the Transaction.
repo: repo.as_ref(),
repo: repo.as_repo_ref(),
};
let mut tx = repo.start_transaction("evolve");
evolve(&user_settings, &mut tx, &mut listener);

View file

@ -29,7 +29,7 @@ use crate::templater::{
LiteralTemplate, ObsoleteProperty, OpenProperty, OrphanProperty, PrunedProperty,
StringPropertyTemplate, Template, TemplateFunction, TemplateProperty,
};
use jujube_lib::repo::Repo;
use jujube_lib::repo::RepoRef;
#[derive(Parser)]
#[grammar = "template.pest"]
@ -216,7 +216,7 @@ impl<'a, I: 'a> Property<'a, I> {
}
fn parse_commit_keyword<'a, 'r: 'a>(
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
pair: Pair<Rule>,
) -> (Property<'a, Commit>, String) {
assert_eq!(pair.as_rule(), Rule::identifier);
@ -260,7 +260,7 @@ fn coerce_to_string<'a, I: 'a>(
}
fn parse_boolean_commit_property<'a, 'r: 'a>(
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
pair: Pair<Rule>,
) -> Box<dyn TemplateProperty<Commit, bool> + 'a> {
let mut inner = pair.into_inner();
@ -277,7 +277,7 @@ fn parse_boolean_commit_property<'a, 'r: 'a>(
}
fn parse_commit_term<'a, 'r: 'a>(
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
pair: Pair<Rule>,
) -> Box<dyn Template<Commit> + 'a> {
assert_eq!(pair.as_rule(), Rule::term);
@ -375,7 +375,7 @@ fn parse_commit_term<'a, 'r: 'a>(
}
fn parse_commit_template_rule<'a, 'r: 'a>(
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
pair: Pair<Rule>,
) -> Box<dyn Template<Commit> + 'a> {
match pair.as_rule() {
@ -398,7 +398,7 @@ fn parse_commit_template_rule<'a, 'r: 'a>(
}
pub fn parse_commit_template<'a, 'r: 'a>(
repo: &'r dyn Repo,
repo: RepoRef<'a, 'r>,
template_text: &str,
) -> Box<dyn Template<Commit> + 'a> {
let mut pairs: Pairs<Rule> = TemplateParser::parse(Rule::template, template_text).unwrap();

View file

@ -16,7 +16,7 @@ use std::borrow::BorrowMut;
use std::ops::Add;
use jujube_lib::commit::Commit;
use jujube_lib::repo::Repo;
use jujube_lib::repo::RepoRef;
use jujube_lib::store::{CommitId, Signature};
use crate::styler::Styler;
@ -202,21 +202,21 @@ impl TemplateProperty<Commit, bool> for PrunedProperty {
}
}
pub struct CurrentCheckoutProperty<'r> {
pub repo: &'r dyn Repo,
pub struct CurrentCheckoutProperty<'a, 'r> {
pub repo: RepoRef<'a, 'r>,
}
impl<'r> TemplateProperty<Commit, bool> for CurrentCheckoutProperty<'r> {
impl TemplateProperty<Commit, bool> for CurrentCheckoutProperty<'_, '_> {
fn extract(&self, context: &Commit) -> bool {
context.id() == self.repo.view().checkout()
}
}
pub struct GitRefsProperty<'r> {
pub repo: &'r dyn Repo,
pub struct GitRefsProperty<'a, 'r> {
pub repo: RepoRef<'a, 'r>,
}
impl<'r> TemplateProperty<Commit, String> for GitRefsProperty<'r> {
impl TemplateProperty<Commit, String> for GitRefsProperty<'_, '_> {
fn extract(&self, context: &Commit) -> String {
let refs: Vec<_> = self
.repo
@ -230,31 +230,31 @@ impl<'r> TemplateProperty<Commit, String> for GitRefsProperty<'r> {
}
}
pub struct ObsoleteProperty<'r> {
pub repo: &'r dyn Repo,
pub struct ObsoleteProperty<'a, 'r> {
pub repo: RepoRef<'a, 'r>,
}
impl<'r> TemplateProperty<Commit, bool> for ObsoleteProperty<'r> {
impl TemplateProperty<Commit, bool> for ObsoleteProperty<'_, '_> {
fn extract(&self, context: &Commit) -> bool {
self.repo.evolution().is_obsolete(context.id())
}
}
pub struct OrphanProperty<'r> {
pub repo: &'r dyn Repo,
pub struct OrphanProperty<'a, 'r> {
pub repo: RepoRef<'a, 'r>,
}
impl<'r> TemplateProperty<Commit, bool> for OrphanProperty<'r> {
impl TemplateProperty<Commit, bool> for OrphanProperty<'_, '_> {
fn extract(&self, context: &Commit) -> bool {
self.repo.evolution().is_orphan(context.id())
}
}
pub struct DivergentProperty<'r> {
pub repo: &'r dyn Repo,
pub struct DivergentProperty<'a, 'r> {
pub repo: RepoRef<'a, 'r>,
}
impl<'r> TemplateProperty<Commit, bool> for DivergentProperty<'r> {
impl TemplateProperty<Commit, bool> for DivergentProperty<'_, '_> {
fn extract(&self, context: &Commit) -> bool {
self.repo.evolution().is_divergent(context.change_id())
}
@ -262,7 +262,7 @@ impl<'r> TemplateProperty<Commit, bool> for DivergentProperty<'r> {
pub struct ConflictProperty;
impl<'r> TemplateProperty<Commit, bool> for ConflictProperty {
impl TemplateProperty<Commit, bool> for ConflictProperty {
fn extract(&self, context: &Commit) -> bool {
context.tree().has_conflict()
}

View file

@ -23,7 +23,7 @@ use jujube_lib::settings::UserSettings;
use crate::styler::{ColorStyler, PlainTextStyler, Styler};
use crate::templater::TemplateFormatter;
use jujube_lib::repo::Repo;
use jujube_lib::repo::RepoRef;
pub struct Ui<'a> {
cwd: PathBuf,
@ -84,7 +84,7 @@ impl<'a> Ui<'a> {
styler.remove_label();
}
pub fn write_commit_summary<'r>(&mut self, repo: &'r dyn Repo, commit: &Commit) {
pub fn write_commit_summary(&mut self, repo: RepoRef, commit: &Commit) {
let template_string = self
.settings
.config()