forked from mirrors/jj
op_store: add support for tracking multiple workspaces (#13)
This patch teaches the `View` object to keep track of the checkout in each workspace. It serializes that information into the `OpStore`. For compatibility with existing repos, the existing field for a single workspace's checkout is interpreted as being for the workspace called "default". This is just an early step towards support for multiple workspaces. Remaining things to do: * Record the workspace ID somewhere in `.jj/` (maybe in `.jj/working_copy/`) * Update existing code to use the workspace ID instead of assuming it's always "default" as we do after this patch * Add a way of indicating in `.jj/` that the repo lives elsewhere and make it possible to load a repo from such workspaces * Add a command for creating additional workspaces * Show each workspace's checkout in log output
This commit is contained in:
parent
ca055d91d9
commit
0098edd3d2
5 changed files with 72 additions and 31 deletions
|
@ -59,7 +59,8 @@ message Tag {
|
|||
message View {
|
||||
repeated bytes head_ids = 1;
|
||||
repeated bytes public_head_ids = 4;
|
||||
bytes checkout = 2;
|
||||
bytes checkout = 2 [deprecated = true];
|
||||
map<string, bytes> checkouts = 8;
|
||||
repeated Branch branches = 5;
|
||||
repeated Tag tags = 6;
|
||||
// Only a subset of the refs. For example, does not include refs/notes/.
|
||||
|
|
|
@ -17,6 +17,29 @@ use std::fmt::{Debug, Error, Formatter};
|
|||
|
||||
use crate::backend::{CommitId, Timestamp};
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
pub struct WorkspaceId(String);
|
||||
|
||||
impl Debug for WorkspaceId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
f.debug_tuple("WorkspaceId").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkspaceId {
|
||||
pub fn new(value: String) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self("default".to_string())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
pub struct ViewId(Vec<u8>);
|
||||
|
||||
|
@ -133,7 +156,7 @@ pub struct BranchTarget {
|
|||
|
||||
/// Represents the way the repo looks at a given time, just like how a Tree
|
||||
/// object represents how the file system looks at a given time.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Default)]
|
||||
pub struct View {
|
||||
/// All head commits
|
||||
pub head_ids: HashSet<CommitId>,
|
||||
|
@ -146,25 +169,10 @@ pub struct View {
|
|||
// TODO: Support multiple Git worktrees?
|
||||
// TODO: Do we want to store the current branch name too?
|
||||
pub git_head: Option<CommitId>,
|
||||
// The commit that *should be* checked out in the (default) working copy. Note that the
|
||||
// working copy (.jj/working_copy/) has the source of truth about which commit *is* checked out
|
||||
// (to be precise: the commit to which we most recently completed a checkout to).
|
||||
// TODO: Allow multiple working copies
|
||||
pub checkout: CommitId,
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(checkout: CommitId) -> Self {
|
||||
Self {
|
||||
head_ids: HashSet::new(),
|
||||
public_head_ids: HashSet::new(),
|
||||
branches: BTreeMap::new(),
|
||||
tags: BTreeMap::new(),
|
||||
git_refs: BTreeMap::new(),
|
||||
git_head: None,
|
||||
checkout,
|
||||
}
|
||||
}
|
||||
// The commit that *should be* checked out in the workspace. Note that the working copy
|
||||
// (.jj/working_copy/) has the source of truth about which commit *is* checked out (to be
|
||||
// precise: the commit to which we most recently completed a checkout to).
|
||||
pub checkouts: HashMap<WorkspaceId, CommitId>,
|
||||
}
|
||||
|
||||
/// Represents an operation (transaction) on the repo view, just like how a
|
||||
|
|
|
@ -31,7 +31,7 @@ use crate::dag_walk::topo_order_reverse;
|
|||
use crate::index::{IndexRef, MutableIndex, ReadonlyIndex};
|
||||
use crate::index_store::IndexStore;
|
||||
use crate::op_heads_store::OpHeadsStore;
|
||||
use crate::op_store::{BranchTarget, OpStore, OperationId, RefTarget};
|
||||
use crate::op_store::{BranchTarget, OpStore, OperationId, RefTarget, WorkspaceId};
|
||||
use crate::operation::Operation;
|
||||
use crate::rewrite::DescendantRebaser;
|
||||
use crate::settings::{RepoSettings, UserSettings};
|
||||
|
@ -177,10 +177,14 @@ impl ReadonlyRepo {
|
|||
is_open: true,
|
||||
};
|
||||
let checkout_commit = store.write_commit(checkout_commit);
|
||||
let workspace_id = WorkspaceId::default();
|
||||
|
||||
let op_store: Arc<dyn OpStore> = Arc::new(SimpleOpStore::init(repo_path.join("op_store")));
|
||||
|
||||
let mut root_view = op_store::View::new(checkout_commit.id().clone());
|
||||
let mut root_view = op_store::View::default();
|
||||
root_view
|
||||
.checkouts
|
||||
.insert(workspace_id, checkout_commit.id().clone());
|
||||
root_view.head_ids.insert(checkout_commit.id().clone());
|
||||
root_view
|
||||
.public_head_ids
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::backend::{CommitId, MillisSinceEpoch, Timestamp};
|
|||
use crate::file_util::persist_content_addressed_temp_file;
|
||||
use crate::op_store::{
|
||||
BranchTarget, OpStore, OpStoreError, OpStoreResult, Operation, OperationId, OperationMetadata,
|
||||
RefTarget, View, ViewId,
|
||||
RefTarget, View, ViewId, WorkspaceId,
|
||||
};
|
||||
|
||||
impl From<std::io::Error> for OpStoreError {
|
||||
|
@ -200,7 +200,11 @@ fn operation_from_proto(proto: &crate::protos::op_store::Operation) -> Operation
|
|||
|
||||
fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
||||
let mut proto = crate::protos::op_store::View::new();
|
||||
proto.checkout = view.checkout.to_bytes();
|
||||
for (workspace_id, commit_id) in &view.checkouts {
|
||||
proto
|
||||
.checkouts
|
||||
.insert(workspace_id.as_str().to_string(), commit_id.to_bytes());
|
||||
}
|
||||
for head_id in &view.head_ids {
|
||||
proto.head_ids.push(head_id.to_bytes());
|
||||
}
|
||||
|
@ -245,7 +249,21 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
|||
}
|
||||
|
||||
fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
|
||||
let mut view = View::new(CommitId::new(proto.checkout.clone()));
|
||||
let mut view = View::default();
|
||||
// For compatibility with old repos before we had support for multiple working
|
||||
// copies
|
||||
if !proto.checkout.is_empty() {
|
||||
view.checkouts.insert(
|
||||
WorkspaceId::default(),
|
||||
CommitId::new(proto.checkout.clone()),
|
||||
);
|
||||
}
|
||||
for (workspace_id, commit_id) in &proto.checkouts {
|
||||
view.checkouts.insert(
|
||||
WorkspaceId::new(workspace_id.clone()),
|
||||
CommitId::new(commit_id.clone()),
|
||||
);
|
||||
}
|
||||
for head_id_bytes in proto.head_ids.iter() {
|
||||
view.head_ids.insert(CommitId::from_bytes(head_id_bytes));
|
||||
}
|
||||
|
@ -370,7 +388,8 @@ mod tests {
|
|||
removes: vec![CommitId::from_hex("fff111")],
|
||||
adds: vec![CommitId::from_hex("fff222"), CommitId::from_hex("fff333")],
|
||||
};
|
||||
let checkout_id = CommitId::from_hex("abc111");
|
||||
let default_checkout_id = CommitId::from_hex("abc111");
|
||||
let test_checkout_id = CommitId::from_hex("abc222");
|
||||
let view = View {
|
||||
head_ids: hashset! {head_id1, head_id2},
|
||||
public_head_ids: hashset! {public_head_id1, public_head_id2},
|
||||
|
@ -396,7 +415,10 @@ mod tests {
|
|||
"refs/heads/feature".to_string() => git_refs_feature_target
|
||||
},
|
||||
git_head: Some(CommitId::from_hex("fff111")),
|
||||
checkout: checkout_id,
|
||||
checkouts: hashmap! {
|
||||
WorkspaceId::default() => default_checkout_id,
|
||||
WorkspaceId::new("test".to_string()) => test_checkout_id,
|
||||
},
|
||||
};
|
||||
let view_id = store.write_view(&view).unwrap();
|
||||
let read_view = store.read_view(&view_id).unwrap();
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::collections::{BTreeMap, HashSet};
|
|||
use crate::backend::CommitId;
|
||||
use crate::index::IndexRef;
|
||||
use crate::op_store;
|
||||
use crate::op_store::{BranchTarget, RefTarget};
|
||||
use crate::op_store::{BranchTarget, RefTarget, WorkspaceId};
|
||||
use crate::refs::merge_ref_targets;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
|
@ -40,8 +40,13 @@ impl View {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Delete this function
|
||||
pub fn checkout(&self) -> &CommitId {
|
||||
&self.data.checkout
|
||||
self.get_checkout(&WorkspaceId::default()).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_checkout(&self, workspace_id: &WorkspaceId) -> Option<&CommitId> {
|
||||
self.data.checkouts.get(workspace_id)
|
||||
}
|
||||
|
||||
pub fn heads(&self) -> &HashSet<CommitId> {
|
||||
|
@ -68,8 +73,9 @@ impl View {
|
|||
self.data.git_head.clone()
|
||||
}
|
||||
|
||||
// TODO: Pass in workspace id here
|
||||
pub fn set_checkout(&mut self, id: CommitId) {
|
||||
self.data.checkout = id;
|
||||
self.data.checkouts.insert(WorkspaceId::default(), id);
|
||||
}
|
||||
|
||||
pub fn add_head(&mut self, head_id: &CommitId) {
|
||||
|
|
Loading…
Reference in a new issue