1#![expect(missing_docs)]
16
17use std::collections::BTreeMap;
18use std::collections::HashMap;
19use std::collections::HashSet;
20use std::collections::hash_map::Entry;
21use std::fmt::Debug;
22use std::fmt::Formatter;
23use std::fs;
24use std::path::Path;
25use std::slice;
26use std::sync::Arc;
27
28use futures::StreamExt as _;
29use futures::TryStreamExt as _;
30use futures::future::try_join_all;
31use futures::stream;
32use itertools::Itertools as _;
33use once_cell::sync::OnceCell;
34use thiserror::Error;
35use tracing::instrument;
36
37use self::dirty_cell::DirtyCell;
38use crate::backend::Backend;
39use crate::backend::BackendError;
40use crate::backend::BackendInitError;
41use crate::backend::BackendLoadError;
42use crate::backend::BackendResult;
43use crate::backend::ChangeId;
44use crate::backend::CommitId;
45use crate::commit::Commit;
46use crate::commit::CommitByCommitterTimestamp;
47use crate::commit_builder::CommitBuilder;
48use crate::commit_builder::DetachedCommitBuilder;
49use crate::dag_walk;
50use crate::dag_walk_async;
51use crate::default_index::DefaultIndexStore;
52use crate::default_index::DefaultMutableIndex;
53use crate::default_submodule_store::DefaultSubmoduleStore;
54use crate::file_util::IoResultExt as _;
55use crate::file_util::PathError;
56use crate::index::ChangeIdIndex;
57use crate::index::Index;
58use crate::index::IndexError;
59use crate::index::IndexResult;
60use crate::index::IndexStore;
61use crate::index::IndexStoreError;
62use crate::index::MutableIndex;
63use crate::index::ReadonlyIndex;
64use crate::index::ResolvedChangeTargets;
65use crate::merge::MergeBuilder;
66use crate::merge::SameChange;
67use crate::merge::trivial_merge;
68use crate::merged_tree::MergedTree;
69use crate::object_id::HexPrefix;
70use crate::object_id::PrefixResolution;
71use crate::op_heads_store;
72use crate::op_heads_store::OpHeadsStore;
73use crate::op_heads_store::OpHeadsStoreError;
74use crate::op_store;
75use crate::op_store::OpStore;
76use crate::op_store::OpStoreError;
77use crate::op_store::OpStoreResult;
78use crate::op_store::OperationId;
79use crate::op_store::RefTarget;
80use crate::op_store::RemoteRef;
81use crate::op_store::RemoteRefState;
82use crate::op_store::RootOperationData;
83use crate::operation::Operation;
84use crate::ref_name::GitRefName;
85use crate::ref_name::RefName;
86use crate::ref_name::RemoteName;
87use crate::ref_name::RemoteRefSymbol;
88use crate::ref_name::WorkspaceName;
89use crate::ref_name::WorkspaceNameBuf;
90use crate::refs::diff_named_commit_ids;
91use crate::refs::diff_named_ref_targets;
92use crate::refs::diff_named_remote_refs;
93use crate::refs::merge_ref_targets;
94use crate::refs::merge_remote_refs;
95use crate::revset;
96use crate::revset::RevsetEvaluationError;
97use crate::revset::RevsetExpression;
98use crate::revset::RevsetStreamExt as _;
99use crate::rewrite::CommitRewriter;
100use crate::rewrite::RebaseOptions;
101use crate::rewrite::RebasedCommit;
102use crate::rewrite::RewriteRefsOptions;
103use crate::rewrite::merge_commit_trees;
104use crate::rewrite::rebase_commit_with_options;
105use crate::settings::UserSettings;
106use crate::signing::SignInitError;
107use crate::signing::Signer;
108use crate::simple_backend::SimpleBackend;
109use crate::simple_op_heads_store::SimpleOpHeadsStore;
110use crate::simple_op_store::SimpleOpStore;
111use crate::store::Store;
112use crate::submodule_store::SubmoduleStore;
113use crate::transaction::Transaction;
114use crate::transaction::TransactionCommitError;
115use crate::tree_merge::MergeOptions;
116use crate::view::RenameWorkspaceError;
117use crate::view::View;
118
119pub trait Repo {
120 fn base_repo(&self) -> &ReadonlyRepo;
123
124 fn store(&self) -> &Arc<Store>;
125
126 fn op_store(&self) -> &Arc<dyn OpStore>;
127
128 fn index(&self) -> &dyn Index;
129
130 fn view(&self) -> &View;
131
132 fn submodule_store(&self) -> &Arc<dyn SubmoduleStore>;
133
134 fn resolve_change_id(
135 &self,
136 change_id: &ChangeId,
137 ) -> IndexResult<Option<ResolvedChangeTargets>> {
138 let prefix = HexPrefix::from_id(change_id);
140 match self.resolve_change_id_prefix(&prefix)? {
141 PrefixResolution::NoMatch => Ok(None),
142 PrefixResolution::SingleMatch(entries) => Ok(Some(entries)),
143 PrefixResolution::AmbiguousMatch => panic!("complete change_id should be unambiguous"),
144 }
145 }
146
147 fn resolve_change_id_prefix(
148 &self,
149 prefix: &HexPrefix,
150 ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>>;
151
152 fn shortest_unique_change_id_prefix_len(
153 &self,
154 target_id_bytes: &ChangeId,
155 ) -> IndexResult<usize>;
156}
157
158pub struct ReadonlyRepo {
159 loader: RepoLoader,
160 operation: Operation,
161 index: Box<dyn ReadonlyIndex>,
162 change_id_index: OnceCell<Box<dyn ChangeIdIndex>>,
163 view: View,
165}
166
167impl Debug for ReadonlyRepo {
168 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
169 f.debug_struct("ReadonlyRepo")
170 .field("store", &self.loader.store)
171 .finish_non_exhaustive()
172 }
173}
174
175#[derive(Error, Debug)]
176pub enum RepoInitError {
177 #[error(transparent)]
178 Backend(#[from] BackendInitError),
179 #[error(transparent)]
180 OpHeadsStore(#[from] OpHeadsStoreError),
181 #[error(transparent)]
182 Path(#[from] PathError),
183}
184
185impl ReadonlyRepo {
186 pub fn default_op_store_initializer() -> &'static OpStoreInitializer<'static> {
187 &|_settings, store_path, root_data| {
188 Ok(Box::new(SimpleOpStore::init(store_path, root_data)?))
189 }
190 }
191
192 pub fn default_op_heads_store_initializer() -> &'static OpHeadsStoreInitializer<'static> {
193 &|_settings, store_path, root_op_id| {
194 Ok(Box::new(SimpleOpHeadsStore::init(store_path, root_op_id)?))
195 }
196 }
197
198 pub fn default_index_store_initializer() -> &'static IndexStoreInitializer<'static> {
199 &|_settings, store_path| Ok(Box::new(DefaultIndexStore::init(store_path)?))
200 }
201
202 pub fn default_submodule_store_initializer() -> &'static SubmoduleStoreInitializer<'static> {
203 &|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::init(store_path)))
204 }
205
206 #[expect(clippy::too_many_arguments)]
207 pub async fn init(
208 settings: &UserSettings,
209 repo_path: &Path,
210 backend_initializer: &BackendInitializer<'_>,
211 signer: Signer,
212 op_store_initializer: &OpStoreInitializer<'_>,
213 op_heads_store_initializer: &OpHeadsStoreInitializer<'_>,
214 index_store_initializer: &IndexStoreInitializer<'_>,
215 submodule_store_initializer: &SubmoduleStoreInitializer<'_>,
216 ) -> Result<Arc<Self>, RepoInitError> {
217 let repo_path = dunce::canonicalize(repo_path).context(repo_path)?;
218
219 let store_path = repo_path.join("store");
220 fs::create_dir(&store_path).context(&store_path)?;
221 let backend = backend_initializer(settings, &store_path)?;
222 let backend_path = store_path.join("type");
223 fs::write(&backend_path, backend.name()).context(&backend_path)?;
224 let merge_options =
225 MergeOptions::from_settings(settings).map_err(|err| BackendInitError(err.into()))?;
226 let store = Store::new(backend, signer, merge_options);
227
228 let op_store_path = repo_path.join("op_store");
229 fs::create_dir(&op_store_path).context(&op_store_path)?;
230 let root_op_data = RootOperationData {
231 root_commit_id: store.root_commit_id().clone(),
232 };
233 let op_store = op_store_initializer(settings, &op_store_path, root_op_data)?;
234 let op_store_type_path = op_store_path.join("type");
235 fs::write(&op_store_type_path, op_store.name()).context(&op_store_type_path)?;
236 let op_store: Arc<dyn OpStore> = Arc::from(op_store);
237
238 let op_heads_path = repo_path.join("op_heads");
239 fs::create_dir(&op_heads_path).context(&op_heads_path)?;
240 let op_heads_store =
241 op_heads_store_initializer(settings, &op_heads_path, op_store.root_operation_id())?;
242 let op_heads_type_path = op_heads_path.join("type");
243 fs::write(&op_heads_type_path, op_heads_store.name()).context(&op_heads_type_path)?;
244 let op_heads_store: Arc<dyn OpHeadsStore> = Arc::from(op_heads_store);
245
246 let index_path = repo_path.join("index");
247 fs::create_dir(&index_path).context(&index_path)?;
248 let index_store = index_store_initializer(settings, &index_path)?;
249 let index_type_path = index_path.join("type");
250 fs::write(&index_type_path, index_store.name()).context(&index_type_path)?;
251 let index_store: Arc<dyn IndexStore> = Arc::from(index_store);
252
253 let submodule_store_path = repo_path.join("submodule_store");
254 fs::create_dir(&submodule_store_path).context(&submodule_store_path)?;
255 let submodule_store = submodule_store_initializer(settings, &submodule_store_path)?;
256 let submodule_store_type_path = submodule_store_path.join("type");
257 fs::write(&submodule_store_type_path, submodule_store.name())
258 .context(&submodule_store_type_path)?;
259 let submodule_store = Arc::from(submodule_store);
260
261 let loader = RepoLoader {
262 settings: settings.clone(),
263 store,
264 op_store,
265 op_heads_store,
266 index_store,
267 submodule_store,
268 };
269
270 let root_operation = loader.root_operation().await;
271 let root_view = root_operation
272 .view()
273 .await
274 .expect("failed to read root view");
275 assert!(!root_view.heads().is_empty());
276 let index = loader
277 .index_store
278 .get_index_at_op(&root_operation, &loader.store)
279 .await
280 .map_err(|err| BackendInitError(err.into()))?;
283 Ok(Arc::new(Self {
284 loader,
285 operation: root_operation,
286 index,
287 change_id_index: OnceCell::new(),
288 view: root_view,
289 }))
290 }
291
292 pub fn loader(&self) -> &RepoLoader {
293 &self.loader
294 }
295
296 pub fn op_id(&self) -> &OperationId {
297 self.operation.id()
298 }
299
300 pub fn operation(&self) -> &Operation {
301 &self.operation
302 }
303
304 pub fn view(&self) -> &View {
305 &self.view
306 }
307
308 pub fn readonly_index(&self) -> &dyn ReadonlyIndex {
309 self.index.as_ref()
310 }
311
312 fn change_id_index(&self) -> &dyn ChangeIdIndex {
313 self.change_id_index
314 .get_or_init(|| {
315 self.readonly_index()
316 .change_id_index(&mut self.view().heads().iter())
317 })
318 .as_ref()
319 }
320
321 pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> {
322 self.loader.op_heads_store()
323 }
324
325 pub fn index_store(&self) -> &Arc<dyn IndexStore> {
326 self.loader.index_store()
327 }
328
329 pub fn settings(&self) -> &UserSettings {
330 self.loader.settings()
331 }
332
333 pub fn start_transaction(self: &Arc<Self>) -> Transaction {
334 let mut_repo = MutableRepo::new(self.clone(), self.readonly_index(), &self.view);
335 Transaction::new(mut_repo, self.settings())
336 }
337
338 pub async fn reload_at_head(&self) -> Result<Arc<Self>, RepoLoaderError> {
339 self.loader().load_at_head().await
340 }
341
342 #[instrument]
343 pub async fn reload_at(&self, operation: &Operation) -> Result<Arc<Self>, RepoLoaderError> {
344 self.loader().load_at(operation).await
345 }
346}
347
348impl Repo for ReadonlyRepo {
349 fn base_repo(&self) -> &ReadonlyRepo {
350 self
351 }
352
353 fn store(&self) -> &Arc<Store> {
354 self.loader.store()
355 }
356
357 fn op_store(&self) -> &Arc<dyn OpStore> {
358 self.loader.op_store()
359 }
360
361 fn index(&self) -> &dyn Index {
362 self.readonly_index().as_index()
363 }
364
365 fn view(&self) -> &View {
366 &self.view
367 }
368
369 fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
370 self.loader.submodule_store()
371 }
372
373 fn resolve_change_id_prefix(
374 &self,
375 prefix: &HexPrefix,
376 ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>> {
377 self.change_id_index().resolve_prefix(prefix)
378 }
379
380 fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> IndexResult<usize> {
381 self.change_id_index().shortest_unique_prefix_len(target_id)
382 }
383}
384
385pub type BackendInitializer<'a> =
386 dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendInitError> + 'a;
387#[rustfmt::skip] pub type OpStoreInitializer<'a> =
389 dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError>
390 + 'a;
391#[rustfmt::skip] pub type OpHeadsStoreInitializer<'a> =
393 dyn Fn(&UserSettings, &Path, &OperationId)
394 -> Result<Box<dyn OpHeadsStore>, BackendInitError>
395 + 'a;
396pub type IndexStoreInitializer<'a> =
397 dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendInitError> + 'a;
398pub type SubmoduleStoreInitializer<'a> =
399 dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendInitError> + 'a;
400
401type BackendFactory =
402 Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>;
403type OpStoreFactory = Box<
404 dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>,
405>;
406type OpHeadsStoreFactory =
407 Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>;
408type IndexStoreFactory =
409 Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>;
410type SubmoduleStoreFactory =
411 Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>;
412
413pub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) {
414 for (name, factory) in ext {
415 match base.entry(name) {
416 Entry::Vacant(v) => {
417 v.insert(factory);
418 }
419 Entry::Occupied(o) => {
420 panic!("Conflicting factory definitions for '{}' factory", o.key())
421 }
422 }
423 }
424}
425
426pub struct StoreFactories {
427 backend_factories: HashMap<String, BackendFactory>,
428 op_store_factories: HashMap<String, OpStoreFactory>,
429 op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>,
430 index_store_factories: HashMap<String, IndexStoreFactory>,
431 submodule_store_factories: HashMap<String, SubmoduleStoreFactory>,
432}
433
434impl Default for StoreFactories {
435 fn default() -> Self {
436 let mut factories = Self::empty();
437
438 factories.add_backend(
440 SimpleBackend::name(),
441 Box::new(|_settings, store_path| Ok(Box::new(SimpleBackend::load(store_path)))),
442 );
443 #[cfg(feature = "git")]
444 factories.add_backend(
445 crate::git_backend::GitBackend::name(),
446 Box::new(|settings, store_path| {
447 Ok(Box::new(crate::git_backend::GitBackend::load(
448 settings, store_path,
449 )?))
450 }),
451 );
452 #[cfg(feature = "testing")]
453 factories.add_backend(
454 crate::secret_backend::SecretBackend::name(),
455 Box::new(|settings, store_path| {
456 Ok(Box::new(crate::secret_backend::SecretBackend::load(
457 settings, store_path,
458 )?))
459 }),
460 );
461
462 factories.add_op_store(
464 SimpleOpStore::name(),
465 Box::new(|_settings, store_path, root_data| {
466 Ok(Box::new(SimpleOpStore::load(store_path, root_data)))
467 }),
468 );
469
470 factories.add_op_heads_store(
472 SimpleOpHeadsStore::name(),
473 Box::new(|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::load(store_path)))),
474 );
475
476 factories.add_index_store(
478 DefaultIndexStore::name(),
479 Box::new(|_settings, store_path| Ok(Box::new(DefaultIndexStore::load(store_path)))),
480 );
481
482 factories.add_submodule_store(
484 DefaultSubmoduleStore::name(),
485 Box::new(|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::load(store_path)))),
486 );
487
488 factories
489 }
490}
491
492#[derive(Debug, Error)]
493pub enum StoreLoadError {
494 #[error("Unsupported {store} backend type '{store_type}'")]
495 UnsupportedType {
496 store: &'static str,
497 store_type: String,
498 },
499 #[error("Failed to read {store} backend type")]
500 ReadError {
501 store: &'static str,
502 source: PathError,
503 },
504 #[error(transparent)]
505 Backend(#[from] BackendLoadError),
506 #[error(transparent)]
507 Signing(#[from] SignInitError),
508}
509
510impl StoreFactories {
511 pub fn empty() -> Self {
512 Self {
513 backend_factories: HashMap::new(),
514 op_store_factories: HashMap::new(),
515 op_heads_store_factories: HashMap::new(),
516 index_store_factories: HashMap::new(),
517 submodule_store_factories: HashMap::new(),
518 }
519 }
520
521 pub fn merge(&mut self, ext: Self) {
522 let Self {
523 backend_factories,
524 op_store_factories,
525 op_heads_store_factories,
526 index_store_factories,
527 submodule_store_factories,
528 } = ext;
529
530 merge_factories_map(&mut self.backend_factories, backend_factories);
531 merge_factories_map(&mut self.op_store_factories, op_store_factories);
532 merge_factories_map(&mut self.op_heads_store_factories, op_heads_store_factories);
533 merge_factories_map(&mut self.index_store_factories, index_store_factories);
534 merge_factories_map(
535 &mut self.submodule_store_factories,
536 submodule_store_factories,
537 );
538 }
539
540 pub fn add_backend(&mut self, name: &str, factory: BackendFactory) {
541 self.backend_factories.insert(name.to_string(), factory);
542 }
543
544 pub fn load_backend(
545 &self,
546 settings: &UserSettings,
547 store_path: &Path,
548 ) -> Result<Box<dyn Backend>, StoreLoadError> {
549 let backend_type = read_store_type("commit", store_path.join("type"))?;
550 let backend_factory = self.backend_factories.get(&backend_type).ok_or_else(|| {
551 StoreLoadError::UnsupportedType {
552 store: "commit",
553 store_type: backend_type.clone(),
554 }
555 })?;
556 Ok(backend_factory(settings, store_path)?)
557 }
558
559 pub fn add_op_store(&mut self, name: &str, factory: OpStoreFactory) {
560 self.op_store_factories.insert(name.to_string(), factory);
561 }
562
563 pub fn load_op_store(
564 &self,
565 settings: &UserSettings,
566 store_path: &Path,
567 root_data: RootOperationData,
568 ) -> Result<Box<dyn OpStore>, StoreLoadError> {
569 let op_store_type = read_store_type("operation", store_path.join("type"))?;
570 let op_store_factory = self.op_store_factories.get(&op_store_type).ok_or_else(|| {
571 StoreLoadError::UnsupportedType {
572 store: "operation",
573 store_type: op_store_type.clone(),
574 }
575 })?;
576 Ok(op_store_factory(settings, store_path, root_data)?)
577 }
578
579 pub fn add_op_heads_store(&mut self, name: &str, factory: OpHeadsStoreFactory) {
580 self.op_heads_store_factories
581 .insert(name.to_string(), factory);
582 }
583
584 pub fn load_op_heads_store(
585 &self,
586 settings: &UserSettings,
587 store_path: &Path,
588 ) -> Result<Box<dyn OpHeadsStore>, StoreLoadError> {
589 let op_heads_store_type = read_store_type("operation heads", store_path.join("type"))?;
590 let op_heads_store_factory = self
591 .op_heads_store_factories
592 .get(&op_heads_store_type)
593 .ok_or_else(|| StoreLoadError::UnsupportedType {
594 store: "operation heads",
595 store_type: op_heads_store_type.clone(),
596 })?;
597 Ok(op_heads_store_factory(settings, store_path)?)
598 }
599
600 pub fn add_index_store(&mut self, name: &str, factory: IndexStoreFactory) {
601 self.index_store_factories.insert(name.to_string(), factory);
602 }
603
604 pub fn load_index_store(
605 &self,
606 settings: &UserSettings,
607 store_path: &Path,
608 ) -> Result<Box<dyn IndexStore>, StoreLoadError> {
609 let index_store_type = read_store_type("index", store_path.join("type"))?;
610 let index_store_factory = self
611 .index_store_factories
612 .get(&index_store_type)
613 .ok_or_else(|| StoreLoadError::UnsupportedType {
614 store: "index",
615 store_type: index_store_type.clone(),
616 })?;
617 Ok(index_store_factory(settings, store_path)?)
618 }
619
620 pub fn add_submodule_store(&mut self, name: &str, factory: SubmoduleStoreFactory) {
621 self.submodule_store_factories
622 .insert(name.to_string(), factory);
623 }
624
625 pub fn load_submodule_store(
626 &self,
627 settings: &UserSettings,
628 store_path: &Path,
629 ) -> Result<Box<dyn SubmoduleStore>, StoreLoadError> {
630 let submodule_store_type = read_store_type("submodule_store", store_path.join("type"))?;
631 let submodule_store_factory = self
632 .submodule_store_factories
633 .get(&submodule_store_type)
634 .ok_or_else(|| StoreLoadError::UnsupportedType {
635 store: "submodule_store",
636 store_type: submodule_store_type.clone(),
637 })?;
638
639 Ok(submodule_store_factory(settings, store_path)?)
640 }
641}
642
643pub fn read_store_type(
644 store: &'static str,
645 path: impl AsRef<Path>,
646) -> Result<String, StoreLoadError> {
647 let path = path.as_ref();
648 fs::read_to_string(path)
649 .context(path)
650 .map_err(|source| StoreLoadError::ReadError { store, source })
651}
652
653#[derive(Debug, Error)]
654pub enum RepoLoaderError {
655 #[error(transparent)]
656 Backend(#[from] BackendError),
657 #[error(transparent)]
658 Index(#[from] IndexError),
659 #[error(transparent)]
660 IndexStore(#[from] IndexStoreError),
661 #[error(transparent)]
662 OpHeadsStoreError(#[from] OpHeadsStoreError),
663 #[error(transparent)]
664 OpStore(#[from] OpStoreError),
665 #[error(transparent)]
666 TransactionCommit(#[from] TransactionCommitError),
667}
668
669#[derive(Clone)]
672pub struct RepoLoader {
673 settings: UserSettings,
674 store: Arc<Store>,
675 op_store: Arc<dyn OpStore>,
676 op_heads_store: Arc<dyn OpHeadsStore>,
677 index_store: Arc<dyn IndexStore>,
678 submodule_store: Arc<dyn SubmoduleStore>,
679}
680
681impl RepoLoader {
682 pub fn new(
683 settings: UserSettings,
684 store: Arc<Store>,
685 op_store: Arc<dyn OpStore>,
686 op_heads_store: Arc<dyn OpHeadsStore>,
687 index_store: Arc<dyn IndexStore>,
688 submodule_store: Arc<dyn SubmoduleStore>,
689 ) -> Self {
690 Self {
691 settings,
692 store,
693 op_store,
694 op_heads_store,
695 index_store,
696 submodule_store,
697 }
698 }
699
700 pub fn init_from_file_system(
704 settings: &UserSettings,
705 repo_path: &Path,
706 store_factories: &StoreFactories,
707 ) -> Result<Self, StoreLoadError> {
708 let merge_options =
709 MergeOptions::from_settings(settings).map_err(|err| BackendLoadError(err.into()))?;
710 let store = Store::new(
711 store_factories.load_backend(settings, &repo_path.join("store"))?,
712 Signer::from_settings(settings)?,
713 merge_options,
714 );
715 let root_op_data = RootOperationData {
716 root_commit_id: store.root_commit_id().clone(),
717 };
718 let op_store = Arc::from(store_factories.load_op_store(
719 settings,
720 &repo_path.join("op_store"),
721 root_op_data,
722 )?);
723 let op_heads_store =
724 Arc::from(store_factories.load_op_heads_store(settings, &repo_path.join("op_heads"))?);
725 let index_store =
726 Arc::from(store_factories.load_index_store(settings, &repo_path.join("index"))?);
727 let submodule_store = Arc::from(
728 store_factories.load_submodule_store(settings, &repo_path.join("submodule_store"))?,
729 );
730 Ok(Self {
731 settings: settings.clone(),
732 store,
733 op_store,
734 op_heads_store,
735 index_store,
736 submodule_store,
737 })
738 }
739
740 pub fn settings(&self) -> &UserSettings {
741 &self.settings
742 }
743
744 pub fn store(&self) -> &Arc<Store> {
745 &self.store
746 }
747
748 pub fn index_store(&self) -> &Arc<dyn IndexStore> {
749 &self.index_store
750 }
751
752 pub fn op_store(&self) -> &Arc<dyn OpStore> {
753 &self.op_store
754 }
755
756 pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> {
757 &self.op_heads_store
758 }
759
760 pub fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
761 &self.submodule_store
762 }
763
764 pub async fn load_at_head(&self) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
765 let op = op_heads_store::resolve_op_heads(
766 self.op_heads_store.as_ref(),
767 &self.op_store,
768 async |op_heads| self.resolve_op_heads(op_heads).await,
769 )
770 .await?;
771 let view = op.view().await?;
772 self.finish_load(op, view).await
773 }
774
775 #[instrument(skip(self))]
776 pub async fn load_at(&self, op: &Operation) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
777 let view = op.view().await?;
778 self.finish_load(op.clone(), view).await
779 }
780
781 pub fn create_from(
782 &self,
783 operation: Operation,
784 view: View,
785 index: Box<dyn ReadonlyIndex>,
786 ) -> Arc<ReadonlyRepo> {
787 let repo = ReadonlyRepo {
788 loader: self.clone(),
789 operation,
790 index,
791 change_id_index: OnceCell::new(),
792 view,
793 };
794 Arc::new(repo)
795 }
796
797 pub async fn root_operation(&self) -> Operation {
802 self.load_operation(self.op_store.root_operation_id())
803 .await
804 .expect("failed to read root operation")
805 }
806
807 pub async fn load_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
809 let data = self.op_store.read_operation(id).await?;
810 Ok(Operation::new(self.op_store.clone(), id.clone(), data))
811 }
812
813 pub async fn merge_operations(
816 &self,
817 operations: Vec<Operation>,
818 tx_description: Option<&str>,
819 ) -> Result<Operation, RepoLoaderError> {
820 let num_operations = operations.len();
821 let mut operations = operations.into_iter();
822 let Some(base_op) = operations.next() else {
823 return Ok(self.root_operation().await);
824 };
825 let final_op = if num_operations > 1 {
826 let base_repo = self.load_at(&base_op).await?;
827 let mut tx = base_repo.start_transaction();
828 for other_op in operations {
829 tx.merge_operation(other_op).await?;
830 tx.repo_mut().rebase_descendants().await?;
831 }
832 let tx_description = tx_description.map_or_else(
833 || format!("merge {num_operations} operations"),
834 |tx_description| tx_description.to_string(),
835 );
836 let merged_repo = tx.write(tx_description).await?.leave_unpublished();
837 merged_repo.operation().clone()
838 } else {
839 base_op
840 };
841
842 Ok(final_op)
843 }
844
845 async fn resolve_op_heads(
846 &self,
847 op_heads: Vec<Operation>,
848 ) -> Result<Operation, RepoLoaderError> {
849 assert!(!op_heads.is_empty());
850 self.merge_operations(op_heads, Some("reconcile divergent operations"))
851 .await
852 }
853
854 async fn finish_load(
855 &self,
856 operation: Operation,
857 view: View,
858 ) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
859 let index = self
860 .index_store
861 .get_index_at_op(&operation, &self.store)
862 .await?;
863 let repo = ReadonlyRepo {
864 loader: self.clone(),
865 operation,
866 index,
867 change_id_index: OnceCell::new(),
868 view,
869 };
870 Ok(Arc::new(repo))
871 }
872}
873
874#[derive(Clone, Debug, PartialEq, Eq)]
875enum Rewrite {
876 Rewritten(CommitId),
879 Divergent(Vec<CommitId>),
882 Abandoned(Vec<CommitId>),
885}
886
887impl Rewrite {
888 fn new_parent_ids(&self) -> &[CommitId] {
889 match self {
890 Self::Rewritten(new_parent_id) => std::slice::from_ref(new_parent_id),
891 Self::Divergent(new_parent_ids) => new_parent_ids.as_slice(),
892 Self::Abandoned(new_parent_ids) => new_parent_ids.as_slice(),
893 }
894 }
895}
896
897pub struct MutableRepo {
898 base_repo: Arc<ReadonlyRepo>,
899 index: Box<dyn MutableIndex>,
900 view: DirtyCell<View>,
901 commit_predecessors: BTreeMap<CommitId, Vec<CommitId>>,
906 parent_mapping: HashMap<CommitId, Rewrite>,
915}
916
917impl MutableRepo {
918 pub fn new(base_repo: Arc<ReadonlyRepo>, index: &dyn ReadonlyIndex, view: &View) -> Self {
919 let mut_view = view.clone();
920 let mut_index = index.start_modification();
921 Self {
922 base_repo,
923 index: mut_index,
924 view: DirtyCell::with_clean(mut_view),
925 commit_predecessors: Default::default(),
926 parent_mapping: Default::default(),
927 }
928 }
929
930 pub fn base_repo(&self) -> &Arc<ReadonlyRepo> {
931 &self.base_repo
932 }
933
934 fn view_mut(&mut self) -> &mut View {
935 self.view.get_mut()
936 }
937
938 pub fn mutable_index(&self) -> &dyn MutableIndex {
939 self.index.as_ref()
940 }
941
942 pub(crate) fn is_backed_by_default_index(&self) -> bool {
943 self.index.downcast_ref::<DefaultMutableIndex>().is_some()
944 }
945
946 pub fn has_changes(&self) -> bool {
947 self.view.ensure_clean(|v| self.enforce_view_invariants(v));
948 !(self.commit_predecessors.is_empty()
949 && self.parent_mapping.is_empty()
950 && self.view() == &self.base_repo.view)
951 }
952
953 pub(crate) fn consume(
954 self,
955 ) -> (
956 Box<dyn MutableIndex>,
957 View,
958 BTreeMap<CommitId, Vec<CommitId>>,
959 ) {
960 self.view.ensure_clean(|v| self.enforce_view_invariants(v));
961 (self.index, self.view.into_inner(), self.commit_predecessors)
962 }
963
964 pub fn new_commit(&mut self, parents: Vec<CommitId>, tree: MergedTree) -> CommitBuilder<'_> {
966 let settings = self.base_repo.settings();
967 DetachedCommitBuilder::for_new_commit(self, settings, parents, tree).attach(self)
968 }
969
970 pub fn rewrite_commit(&mut self, predecessor: &Commit) -> CommitBuilder<'_> {
972 let settings = self.base_repo.settings();
973 DetachedCommitBuilder::for_rewrite_from(self, settings, predecessor).attach(self)
974 }
977
978 pub(crate) fn set_predecessors(&mut self, id: CommitId, predecessors: Vec<CommitId>) {
979 self.commit_predecessors.insert(id, predecessors);
980 }
981
982 pub fn set_rewritten_commit(&mut self, old_id: CommitId, new_id: CommitId) {
989 assert_ne!(old_id, *self.store().root_commit_id());
990 self.parent_mapping
991 .insert(old_id, Rewrite::Rewritten(new_id));
992 }
993
994 pub fn set_divergent_rewrite(
1002 &mut self,
1003 old_id: CommitId,
1004 new_ids: impl IntoIterator<Item = CommitId>,
1005 ) {
1006 assert_ne!(old_id, *self.store().root_commit_id());
1007 self.parent_mapping.insert(
1008 old_id.clone(),
1009 Rewrite::Divergent(new_ids.into_iter().collect()),
1010 );
1011 }
1012
1013 pub fn record_abandoned_commit(&mut self, old_commit: &Commit) {
1023 assert_ne!(old_commit.id(), self.store().root_commit_id());
1024 self.record_abandoned_commit_with_parents(
1026 old_commit.id().clone(),
1027 old_commit.parent_ids().iter().cloned(),
1028 );
1029 }
1030
1031 pub fn record_abandoned_commit_with_parents(
1037 &mut self,
1038 old_id: CommitId,
1039 new_parent_ids: impl IntoIterator<Item = CommitId>,
1040 ) {
1041 assert_ne!(old_id, *self.store().root_commit_id());
1042 self.parent_mapping.insert(
1043 old_id,
1044 Rewrite::Abandoned(new_parent_ids.into_iter().collect()),
1045 );
1046 }
1047
1048 pub fn has_rewrites(&self) -> bool {
1049 !self.parent_mapping.is_empty()
1050 }
1051
1052 pub fn new_parents(&self, old_ids: &[CommitId]) -> Vec<CommitId> {
1059 self.rewritten_ids_with(old_ids, |rewrite| !matches!(rewrite, Rewrite::Divergent(_)))
1060 }
1061
1062 fn rewritten_ids_with(
1063 &self,
1064 old_ids: &[CommitId],
1065 mut predicate: impl FnMut(&Rewrite) -> bool,
1066 ) -> Vec<CommitId> {
1067 assert!(!old_ids.is_empty());
1068 let mut new_ids = Vec::with_capacity(old_ids.len());
1069 let mut to_visit = old_ids.iter().rev().collect_vec();
1070 let mut visited = HashSet::new();
1071 while let Some(id) = to_visit.pop() {
1072 if !visited.insert(id) {
1073 continue;
1074 }
1075 match self.parent_mapping.get(id).filter(|&v| predicate(v)) {
1076 None => {
1077 new_ids.push(id.clone());
1078 }
1079 Some(rewrite) => {
1080 let replacements = rewrite.new_parent_ids();
1081 assert!(
1082 !replacements.is_empty(),
1087 "Found empty value for key {id:?} in the parent mapping",
1088 );
1089 to_visit.extend(replacements.iter().rev());
1090 }
1091 }
1092 }
1093 assert!(
1094 !new_ids.is_empty(),
1095 "new ids become empty because of cycle in the parent mapping"
1096 );
1097 debug_assert!(new_ids.iter().all_unique());
1098 new_ids
1099 }
1100
1101 fn resolve_rewrite_mapping_with(
1105 &self,
1106 mut predicate: impl FnMut(&Rewrite) -> bool,
1107 ) -> BackendResult<HashMap<CommitId, Vec<CommitId>>> {
1108 let sorted_ids = dag_walk::topo_order_forward(
1109 self.parent_mapping.keys(),
1110 |&id| id,
1111 |&id| match self.parent_mapping.get(id).filter(|&v| predicate(v)) {
1112 None => &[],
1113 Some(rewrite) => rewrite.new_parent_ids(),
1114 },
1115 |id| {
1116 BackendError::Other(
1117 format!("Cycle between rewritten commits involving commit {id}").into(),
1118 )
1119 },
1120 )?;
1121 let mut new_mapping: HashMap<CommitId, Vec<CommitId>> = HashMap::new();
1122 for old_id in sorted_ids {
1123 let Some(rewrite) = self.parent_mapping.get(old_id).filter(|&v| predicate(v)) else {
1124 continue;
1125 };
1126 let lookup = |id| new_mapping.get(id).map_or(slice::from_ref(id), |ids| ids);
1127 let new_ids = match rewrite.new_parent_ids() {
1128 [id] => lookup(id).to_vec(), ids => ids.iter().flat_map(lookup).unique().cloned().collect(),
1130 };
1131 debug_assert_eq!(
1132 new_ids,
1133 self.rewritten_ids_with(slice::from_ref(old_id), &mut predicate)
1134 );
1135 new_mapping.insert(old_id.clone(), new_ids);
1136 }
1137 Ok(new_mapping)
1138 }
1139
1140 pub async fn update_rewritten_references(
1143 &mut self,
1144 options: &RewriteRefsOptions,
1145 ) -> BackendResult<()> {
1146 self.update_all_references(options).await?;
1147 self.update_heads()
1148 .await
1149 .map_err(|err| err.into_backend_error())?;
1150 Ok(())
1151 }
1152
1153 async fn update_all_references(&mut self, options: &RewriteRefsOptions) -> BackendResult<()> {
1154 let rewrite_mapping = self.resolve_rewrite_mapping_with(|_| true)?;
1155 self.update_local_bookmarks(&rewrite_mapping, options)
1156 .map_err(|err| BackendError::Other(err.into()))?;
1158 self.update_wc_commits(&rewrite_mapping).await?;
1159 Ok(())
1160 }
1161
1162 fn update_local_bookmarks(
1163 &mut self,
1164 rewrite_mapping: &HashMap<CommitId, Vec<CommitId>>,
1165 options: &RewriteRefsOptions,
1166 ) -> IndexResult<()> {
1167 let changed_branches = self
1168 .view()
1169 .local_bookmarks()
1170 .flat_map(|(name, target)| {
1171 target.added_ids().filter_map(|id| {
1172 let change = rewrite_mapping.get_key_value(id)?;
1173 Some((name.to_owned(), change))
1174 })
1175 })
1176 .collect_vec();
1177 for (bookmark_name, (old_commit_id, new_commit_ids)) in changed_branches {
1178 let should_delete = options.delete_abandoned_bookmarks
1179 && matches!(
1180 self.parent_mapping.get(old_commit_id),
1181 Some(Rewrite::Abandoned(_))
1182 );
1183 let old_target = RefTarget::normal(old_commit_id.clone());
1184 let new_target = if should_delete {
1185 RefTarget::absent()
1186 } else {
1187 let ids = itertools::intersperse(new_commit_ids, old_commit_id)
1188 .map(|id| Some(id.clone()));
1189 RefTarget::from_merge(MergeBuilder::from_iter(ids).build())
1190 };
1191
1192 self.merge_local_bookmark(&bookmark_name, &old_target, &new_target)?;
1193 }
1194 Ok(())
1195 }
1196
1197 async fn update_wc_commits(
1198 &mut self,
1199 rewrite_mapping: &HashMap<CommitId, Vec<CommitId>>,
1200 ) -> BackendResult<()> {
1201 let changed_wc_commits = self
1202 .view()
1203 .wc_commit_ids()
1204 .iter()
1205 .filter_map(|(name, commit_id)| {
1206 let change = rewrite_mapping.get_key_value(commit_id)?;
1207 Some((name.to_owned(), change))
1208 })
1209 .collect_vec();
1210 let mut recreated_wc_commits: HashMap<&CommitId, Commit> = HashMap::new();
1211 for (name, (old_commit_id, new_commit_ids)) in changed_wc_commits {
1212 let abandoned_old_commit = matches!(
1213 self.parent_mapping.get(old_commit_id),
1214 Some(Rewrite::Abandoned(_))
1215 );
1216 let new_wc_commit = if !abandoned_old_commit {
1217 self.store().get_commit_async(&new_commit_ids[0]).await?
1219 } else if let Some(commit) = recreated_wc_commits.get(old_commit_id) {
1220 commit.clone()
1221 } else {
1222 let new_commit_futures = new_commit_ids
1223 .iter()
1224 .map(async |id| self.store().get_commit_async(id).await);
1225 let new_commits = try_join_all(new_commit_futures).await?;
1226 let merged_parents_tree = merge_commit_trees(self, &new_commits).await?;
1227 let commit = self
1228 .new_commit(new_commit_ids.clone(), merged_parents_tree)
1229 .write()
1230 .await?;
1231 recreated_wc_commits.insert(old_commit_id, commit.clone());
1232 commit
1233 };
1234 self.edit(name, &new_wc_commit)
1235 .await
1236 .map_err(|err| match err {
1237 EditCommitError::BackendError(backend_error) => backend_error,
1238 EditCommitError::WorkingCopyCommitNotFound(_)
1239 | EditCommitError::RewriteRootCommit(_) => panic!("unexpected error: {err:?}"),
1240 })?;
1241 }
1242 Ok(())
1243 }
1244
1245 async fn update_heads(&mut self) -> Result<(), RevsetEvaluationError> {
1246 let old_commits_expression =
1247 RevsetExpression::commits(self.parent_mapping.keys().cloned().collect())
1248 .intersection(&RevsetExpression::visible_heads().ancestors());
1249 let heads_to_add_expression = old_commits_expression
1250 .parents()
1251 .minus(&old_commits_expression);
1252 let heads_to_add: Vec<_> = heads_to_add_expression
1253 .evaluate(self)?
1254 .stream()
1255 .try_collect()
1256 .await?;
1257
1258 let mut view = self.view().store_view().clone();
1259 for commit_id in self.parent_mapping.keys() {
1260 view.head_ids.remove(commit_id);
1261 }
1262 view.head_ids.extend(heads_to_add);
1263 self.set_view(view);
1264 Ok(())
1265 }
1266
1267 pub async fn find_descendants_for_rebase(
1270 &self,
1271 roots: Vec<CommitId>,
1272 ) -> BackendResult<Vec<Commit>> {
1273 let to_visit_revset = RevsetExpression::commits(roots)
1274 .descendants()
1275 .minus(&RevsetExpression::commits(
1276 self.parent_mapping.keys().cloned().collect(),
1277 ))
1278 .evaluate(self)
1279 .map_err(|err| err.into_backend_error())?;
1280 let to_visit = to_visit_revset
1281 .stream()
1282 .commits(self.store())
1283 .try_collect()
1284 .await
1285 .map_err(|err| err.into_backend_error())?;
1286 Ok(to_visit)
1287 }
1288
1289 async fn order_commits_for_rebase(
1292 &self,
1293 to_visit: Vec<Commit>,
1294 new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1295 ) -> BackendResult<Vec<Commit>> {
1296 let to_visit_set: HashSet<CommitId> =
1297 to_visit.iter().map(|commit| commit.id().clone()).collect();
1298 let mut visited = HashSet::new();
1299 let store = self.store();
1302 dag_walk_async::topo_order_reverse(
1303 to_visit.into_iter().map(Ok),
1304 |commit| commit.id().clone(),
1305 async |commit| -> Vec<BackendResult<Commit>> {
1306 visited.insert(commit.id().clone());
1307 let mut dependents = vec![];
1308 let parent_ids = new_parents_map
1309 .get(commit.id())
1310 .map_or(commit.parent_ids(), |parent_ids| parent_ids);
1311 for parent_id in parent_ids {
1312 let parent = store.get_commit_async(parent_id).await;
1313 let Ok(parent) = parent else {
1314 dependents.push(parent);
1315 continue;
1316 };
1317 if let Some(rewrite) = self.parent_mapping.get(parent.id()) {
1318 for target in rewrite.new_parent_ids() {
1319 if to_visit_set.contains(target) && !visited.contains(target) {
1320 dependents.push(store.get_commit_async(target).await);
1321 }
1322 }
1323 }
1324 if to_visit_set.contains(parent.id()) {
1325 dependents.push(Ok(parent));
1326 }
1327 }
1328 dependents
1329 },
1330 |_| panic!("graph has cycle"),
1331 )
1332 .await
1333 }
1334
1335 pub async fn transform_descendants(
1347 &mut self,
1348 roots: Vec<CommitId>,
1349 callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1350 ) -> BackendResult<()> {
1351 let options = RewriteRefsOptions::default();
1352 self.transform_descendants_with_options(roots, &HashMap::new(), &options, callback)
1353 .await
1354 }
1355
1356 pub async fn transform_descendants_with_options(
1364 &mut self,
1365 roots: Vec<CommitId>,
1366 new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1367 options: &RewriteRefsOptions,
1368 callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1369 ) -> BackendResult<()> {
1370 let descendants = self.find_descendants_for_rebase(roots).await?;
1371 self.transform_commits(descendants, new_parents_map, options, callback)
1372 .await
1373 }
1374
1375 pub async fn transform_commits(
1383 &mut self,
1384 commits: Vec<Commit>,
1385 new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1386 options: &RewriteRefsOptions,
1387 mut callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1388 ) -> BackendResult<()> {
1389 let mut to_visit = self
1390 .order_commits_for_rebase(commits, new_parents_map)
1391 .await?;
1392 while let Some(old_commit) = to_visit.pop() {
1393 let parent_ids = new_parents_map
1394 .get(old_commit.id())
1395 .map_or(old_commit.parent_ids(), |parent_ids| parent_ids);
1396 let new_parent_ids = self.new_parents(parent_ids);
1397 let rewriter = CommitRewriter::new(self, old_commit, new_parent_ids);
1398 callback(rewriter).await?;
1399 }
1400 self.update_rewritten_references(options).await?;
1401 Ok(())
1411 }
1412
1413 pub async fn rebase_descendants_with_options(
1429 &mut self,
1430 options: &RebaseOptions,
1431 mut progress: impl FnMut(Commit, RebasedCommit),
1432 ) -> BackendResult<()> {
1433 let roots = self.parent_mapping.keys().cloned().collect();
1434 self.transform_descendants_with_options(
1435 roots,
1436 &HashMap::new(),
1437 &options.rewrite_refs,
1438 async |rewriter| {
1439 if rewriter.parents_changed() {
1440 let old_commit = rewriter.old_commit().clone();
1441 let rebased_commit = rebase_commit_with_options(rewriter, options).await?;
1442 progress(old_commit, rebased_commit);
1443 }
1444 Ok(())
1445 },
1446 )
1447 .await?;
1448 self.parent_mapping.clear();
1449 Ok(())
1450 }
1451
1452 pub async fn rebase_descendants(&mut self) -> BackendResult<usize> {
1462 let options = RebaseOptions::default();
1463 let mut num_rebased = 0;
1464 self.rebase_descendants_with_options(&options, |_old_commit, _rebased_commit| {
1465 num_rebased += 1;
1466 })
1467 .await?;
1468 Ok(num_rebased)
1469 }
1470
1471 pub async fn reparent_descendants(&mut self) -> BackendResult<usize> {
1478 let roots = self.parent_mapping.keys().cloned().collect_vec();
1479 let mut num_reparented = 0;
1480 self.transform_descendants(roots, async |rewriter| {
1481 if rewriter.parents_changed() {
1482 let builder = rewriter.reparent();
1483 builder.write().await?;
1484 num_reparented += 1;
1485 }
1486 Ok(())
1487 })
1488 .await?;
1489 self.parent_mapping.clear();
1490 Ok(num_reparented)
1491 }
1492
1493 pub fn set_wc_commit(
1494 &mut self,
1495 name: WorkspaceNameBuf,
1496 commit_id: CommitId,
1497 ) -> Result<(), RewriteRootCommit> {
1498 if &commit_id == self.store().root_commit_id() {
1499 return Err(RewriteRootCommit);
1500 }
1501 self.view_mut().set_wc_commit(name, commit_id);
1502 Ok(())
1503 }
1504
1505 pub async fn remove_wc_commit(&mut self, name: &WorkspaceName) -> Result<(), EditCommitError> {
1506 self.maybe_abandon_wc_commit(name).await?;
1507 self.view_mut().remove_wc_commit(name);
1508 Ok(())
1509 }
1510
1511 fn merge_wc_commit(
1514 &mut self,
1515 name: &WorkspaceName,
1516 base_id: Option<&CommitId>,
1517 other_id: Option<&CommitId>,
1518 ) {
1519 let view = self.view.get_mut();
1520 let self_id = view.get_wc_commit_id(name);
1521 let new_id = if let Some(resolved) =
1525 trivial_merge(&[self_id, base_id, other_id], SameChange::Accept)
1526 {
1527 resolved.cloned()
1528 } else if self_id.is_none() || other_id.is_none() {
1529 None
1532 } else {
1533 self_id.cloned()
1534 };
1535 match new_id {
1536 Some(id) => view.set_wc_commit(name.to_owned(), id),
1537 None => view.remove_wc_commit(name),
1538 }
1539 }
1540
1541 pub fn rename_workspace(
1542 &mut self,
1543 old_name: &WorkspaceName,
1544 new_name: WorkspaceNameBuf,
1545 ) -> Result<(), RenameWorkspaceError> {
1546 self.view_mut().rename_workspace(old_name, new_name)
1547 }
1548
1549 pub async fn check_out(
1550 &mut self,
1551 name: WorkspaceNameBuf,
1552 commit: &Commit,
1553 ) -> Result<Commit, CheckOutCommitError> {
1554 let wc_commit = self
1555 .new_commit(vec![commit.id().clone()], commit.tree())
1556 .write()
1557 .await?;
1558 self.edit(name, &wc_commit).await?;
1559 Ok(wc_commit)
1560 }
1561
1562 pub async fn edit(
1563 &mut self,
1564 name: WorkspaceNameBuf,
1565 commit: &Commit,
1566 ) -> Result<(), EditCommitError> {
1567 self.maybe_abandon_wc_commit(&name).await?;
1568 self.add_head(commit).await?;
1569 Ok(self.set_wc_commit(name, commit.id().clone())?)
1570 }
1571
1572 async fn maybe_abandon_wc_commit(
1573 &mut self,
1574 workspace_name: &WorkspaceName,
1575 ) -> Result<(), EditCommitError> {
1576 let is_commit_referenced = |view: &View, commit_id: &CommitId| -> bool {
1577 itertools::chain!(
1578 view.wc_commit_ids()
1579 .iter()
1580 .filter(|&(name, _)| name != workspace_name)
1581 .map(|(_, wc_id)| wc_id),
1582 view.local_bookmarks()
1583 .flat_map(|(_, target)| target.added_ids()),
1584 view.local_tags().flat_map(|(_, target)| target.added_ids()),
1585 )
1586 .any(|id| id == commit_id)
1587 };
1588
1589 let maybe_wc_commit_id = self
1590 .view
1591 .with_ref(|v| v.get_wc_commit_id(workspace_name).cloned());
1592 if let Some(wc_commit_id) = maybe_wc_commit_id {
1593 let wc_commit = self
1594 .store()
1595 .get_commit_async(&wc_commit_id)
1596 .await
1597 .map_err(EditCommitError::WorkingCopyCommitNotFound)?;
1598 if wc_commit.is_discardable(self).await?
1599 && self
1600 .view
1601 .with_ref(|v| !is_commit_referenced(v, wc_commit.id()))
1602 && self.view().heads().contains(wc_commit.id())
1603 {
1604 self.record_abandoned_commit(&wc_commit);
1608 }
1609 }
1610
1611 Ok(())
1612 }
1613
1614 fn enforce_view_invariants(&self, view: &mut View) {
1615 let view = view.store_view_mut();
1616 let root_commit_id = self.store().root_commit_id();
1617 if view.head_ids.is_empty() {
1618 view.head_ids.insert(root_commit_id.clone());
1619 } else if view.head_ids.len() > 1 {
1620 view.head_ids.remove(root_commit_id);
1623 view.head_ids = self
1627 .index()
1628 .heads(&mut view.head_ids.iter())
1629 .unwrap()
1630 .into_iter()
1631 .collect();
1632 }
1633 assert!(!view.head_ids.is_empty());
1634 }
1635
1636 pub async fn add_head(&mut self, head: &Commit) -> BackendResult<()> {
1639 self.add_heads(slice::from_ref(head)).await
1640 }
1641
1642 pub async fn add_heads(&mut self, heads: &[Commit]) -> BackendResult<()> {
1649 let current_heads = self.view.get_mut().heads();
1650 match heads {
1654 [] => {}
1655 [head]
1656 if head
1657 .parent_ids()
1658 .iter()
1659 .all(|parent_id| current_heads.contains(parent_id)) =>
1660 {
1661 self.index
1662 .add_commit(head)
1663 .await
1664 .map_err(|err| BackendError::Other(err.into()))?;
1666 self.view.get_mut().add_head(head.id());
1667 for parent_id in head.parent_ids() {
1668 self.view.get_mut().remove_head(parent_id);
1669 }
1670 }
1671 _ => {
1672 self.index_commits(heads).await?;
1673 for head in heads {
1674 self.view.get_mut().add_head(head.id());
1675 }
1676 self.view.mark_dirty();
1677 }
1678 }
1679 Ok(())
1680 }
1681
1682 pub fn remove_head(&mut self, head: &CommitId) {
1683 self.view_mut().remove_head(head);
1684 self.view.mark_dirty();
1685 }
1686
1687 pub async fn index_commits(&mut self, heads: &[Commit]) -> BackendResult<Vec<Commit>> {
1690 let missing_commits = dag_walk_async::topo_order_reverse_ord(
1691 heads
1692 .iter()
1693 .filter_map(|commit| match self.index().has_id(commit.id()) {
1694 Ok(false) => Some(Ok(CommitByCommitterTimestamp(commit.clone()))),
1695 Ok(true) => None,
1696 Err(err) => Some(Err(BackendError::Other(err.into()))),
1698 }),
1699 |CommitByCommitterTimestamp(commit)| commit.id().clone(),
1700 async |CommitByCommitterTimestamp(commit)| {
1701 stream::iter(commit.parent_ids())
1702 .filter_map(async |id| match self.index().has_id(id) {
1703 Ok(false) => Some(
1704 self.store()
1705 .get_commit_async(id)
1706 .await
1707 .map(CommitByCommitterTimestamp),
1708 ),
1709 Ok(true) => None,
1710 Err(err) => Some(Err(BackendError::Other(err.into()))),
1712 })
1713 .collect::<Vec<_>>()
1714 .await
1715 },
1716 |_| panic!("graph has cycle"),
1717 )
1718 .await?;
1719 for CommitByCommitterTimestamp(missing_commit) in missing_commits.iter().rev() {
1720 self.index
1721 .add_commit(missing_commit)
1722 .await
1723 .map_err(|err| BackendError::Other(err.into()))?;
1725 }
1726 let indexed_commits = missing_commits
1727 .into_iter()
1728 .map(|CommitByCommitterTimestamp(commit)| commit)
1729 .collect();
1730 Ok(indexed_commits)
1731 }
1732
1733 pub fn get_local_bookmark(&self, name: &RefName) -> RefTarget {
1734 self.view.with_ref(|v| v.get_local_bookmark(name).clone())
1735 }
1736
1737 pub fn set_local_bookmark_target(&mut self, name: &RefName, target: RefTarget) {
1738 let view = self.view_mut();
1739 for id in target.added_ids() {
1740 view.add_head(id);
1741 }
1742 view.set_local_bookmark_target(name, target);
1743 self.view.mark_dirty();
1744 }
1745
1746 pub fn merge_local_bookmark(
1747 &mut self,
1748 name: &RefName,
1749 base_target: &RefTarget,
1750 other_target: &RefTarget,
1751 ) -> IndexResult<()> {
1752 let view = self.view.get_mut();
1753 let index = self.index.as_index();
1754 let self_target = view.get_local_bookmark(name);
1755 let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1756 self.set_local_bookmark_target(name, new_target);
1757 Ok(())
1758 }
1759
1760 pub fn get_remote_bookmark(&self, symbol: RemoteRefSymbol<'_>) -> RemoteRef {
1761 self.view
1762 .with_ref(|v| v.get_remote_bookmark(symbol).clone())
1763 }
1764
1765 pub fn set_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {
1766 self.view_mut().set_remote_bookmark(symbol, remote_ref);
1767 }
1768
1769 fn merge_remote_bookmark(
1770 &mut self,
1771 symbol: RemoteRefSymbol<'_>,
1772 base_ref: &RemoteRef,
1773 other_ref: &RemoteRef,
1774 ) -> IndexResult<()> {
1775 let view = self.view.get_mut();
1776 let index = self.index.as_index();
1777 let self_ref = view.get_remote_bookmark(symbol);
1778 let new_ref = merge_remote_refs(index, self_ref, base_ref, other_ref)?;
1779 view.set_remote_bookmark(symbol, new_ref);
1780 Ok(())
1781 }
1782
1783 pub fn track_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>) -> IndexResult<()> {
1786 let mut remote_ref = self.get_remote_bookmark(symbol);
1787 let base_target = remote_ref.tracked_target();
1788 self.merge_local_bookmark(symbol.name, base_target, &remote_ref.target)?;
1789 remote_ref.state = RemoteRefState::Tracked;
1790 self.set_remote_bookmark(symbol, remote_ref);
1791 Ok(())
1792 }
1793
1794 pub fn untrack_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>) {
1796 let mut remote_ref = self.get_remote_bookmark(symbol);
1797 remote_ref.state = RemoteRefState::New;
1798 self.set_remote_bookmark(symbol, remote_ref);
1799 }
1800
1801 pub fn ensure_remote(&mut self, remote_name: &RemoteName) {
1802 self.view_mut().ensure_remote(remote_name);
1803 }
1804
1805 pub fn remove_remote(&mut self, remote_name: &RemoteName) {
1806 self.view_mut().remove_remote(remote_name);
1807 }
1808
1809 pub fn rename_remote(&mut self, old: &RemoteName, new: &RemoteName) {
1810 self.view_mut().rename_remote(old, new);
1811 }
1812
1813 pub fn get_local_tag(&self, name: &RefName) -> RefTarget {
1814 self.view.with_ref(|v| v.get_local_tag(name).clone())
1815 }
1816
1817 pub fn set_local_tag_target(&mut self, name: &RefName, target: RefTarget) {
1818 self.view_mut().set_local_tag_target(name, target);
1819 }
1820
1821 pub fn merge_local_tag(
1822 &mut self,
1823 name: &RefName,
1824 base_target: &RefTarget,
1825 other_target: &RefTarget,
1826 ) -> IndexResult<()> {
1827 let view = self.view.get_mut();
1828 let index = self.index.as_index();
1829 let self_target = view.get_local_tag(name);
1830 let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1831 view.set_local_tag_target(name, new_target);
1832 Ok(())
1833 }
1834
1835 pub fn get_remote_tag(&self, symbol: RemoteRefSymbol<'_>) -> RemoteRef {
1836 self.view.with_ref(|v| v.get_remote_tag(symbol).clone())
1837 }
1838
1839 pub fn set_remote_tag(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {
1840 self.view_mut().set_remote_tag(symbol, remote_ref);
1841 }
1842
1843 fn merge_remote_tag(
1844 &mut self,
1845 symbol: RemoteRefSymbol<'_>,
1846 base_ref: &RemoteRef,
1847 other_ref: &RemoteRef,
1848 ) -> IndexResult<()> {
1849 let view = self.view.get_mut();
1850 let index = self.index.as_index();
1851 let self_ref = view.get_remote_tag(symbol);
1852 let new_ref = merge_remote_refs(index, self_ref, base_ref, other_ref)?;
1853 view.set_remote_tag(symbol, new_ref);
1854 Ok(())
1855 }
1856
1857 pub fn get_git_ref(&self, name: &GitRefName) -> RefTarget {
1858 self.view.with_ref(|v| v.get_git_ref(name).clone())
1859 }
1860
1861 pub fn set_git_ref_target(&mut self, name: &GitRefName, target: RefTarget) {
1862 self.view_mut().set_git_ref_target(name, target);
1863 }
1864
1865 fn merge_git_ref(
1866 &mut self,
1867 name: &GitRefName,
1868 base_target: &RefTarget,
1869 other_target: &RefTarget,
1870 ) -> IndexResult<()> {
1871 let view = self.view.get_mut();
1872 let index = self.index.as_index();
1873 let self_target = view.get_git_ref(name);
1874 let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1875 view.set_git_ref_target(name, new_target);
1876 Ok(())
1877 }
1878
1879 pub fn git_head(&self) -> RefTarget {
1880 self.view.with_ref(|v| v.git_head().clone())
1881 }
1882
1883 pub fn set_git_head_target(&mut self, target: RefTarget) {
1884 self.view_mut().set_git_head_target(target);
1885 }
1886
1887 pub fn set_view(&mut self, data: op_store::View) {
1888 self.view_mut().set_view(data);
1889 self.view.mark_dirty();
1890 }
1891
1892 pub async fn merge(
1893 &mut self,
1894 base_repo: &ReadonlyRepo,
1895 other_repo: &ReadonlyRepo,
1896 ) -> Result<(), RepoLoaderError> {
1897 self.index.merge_in(base_repo.readonly_index())?;
1902 self.index.merge_in(other_repo.readonly_index())?;
1903
1904 self.view.ensure_clean(|v| self.enforce_view_invariants(v));
1905 self.merge_view(&base_repo.view, &other_repo.view).await?;
1906 self.view.mark_dirty();
1907 Ok(())
1908 }
1909
1910 pub fn merge_index(&mut self, other_repo: &ReadonlyRepo) -> IndexResult<()> {
1911 self.index.merge_in(other_repo.readonly_index())
1912 }
1913
1914 async fn merge_view(&mut self, base: &View, other: &View) -> Result<(), RepoLoaderError> {
1915 let changed_wc_commits = diff_named_commit_ids(base.wc_commit_ids(), other.wc_commit_ids());
1916 for (name, (base_id, other_id)) in changed_wc_commits {
1917 self.merge_wc_commit(name, base_id, other_id);
1918 }
1919
1920 let base_heads = base.heads().iter().cloned().collect_vec();
1921 let own_heads = self.view().heads().iter().cloned().collect_vec();
1922 let other_heads = other.heads().iter().cloned().collect_vec();
1923
1924 if self.is_backed_by_default_index() {
1931 self.record_rewrites(&base_heads, &own_heads).await?;
1932 self.record_rewrites(&base_heads, &other_heads).await?;
1933 } else {
1936 for removed_head in base.heads().difference(other.heads()) {
1937 self.view_mut().remove_head(removed_head);
1938 }
1939 }
1940 for added_head in other.heads().difference(base.heads()) {
1941 self.view_mut().add_head(added_head);
1942 }
1943
1944 let changed_local_bookmarks =
1945 diff_named_ref_targets(base.local_bookmarks(), other.local_bookmarks());
1946 for (name, (base_target, other_target)) in changed_local_bookmarks {
1947 self.merge_local_bookmark(name, base_target, other_target)?;
1948 }
1949
1950 let changed_local_tags = diff_named_ref_targets(base.local_tags(), other.local_tags());
1951 for (name, (base_target, other_target)) in changed_local_tags {
1952 self.merge_local_tag(name, base_target, other_target)?;
1953 }
1954
1955 let changed_git_refs = diff_named_ref_targets(base.git_refs(), other.git_refs());
1956 for (name, (base_target, other_target)) in changed_git_refs {
1957 self.merge_git_ref(name, base_target, other_target)?;
1958 }
1959
1960 let changed_remote_bookmarks =
1961 diff_named_remote_refs(base.all_remote_bookmarks(), other.all_remote_bookmarks());
1962 for (symbol, (base_ref, other_ref)) in changed_remote_bookmarks {
1963 self.merge_remote_bookmark(symbol, base_ref, other_ref)?;
1964 }
1965
1966 let changed_remote_tags =
1967 diff_named_remote_refs(base.all_remote_tags(), other.all_remote_tags());
1968 for (symbol, (base_ref, other_ref)) in changed_remote_tags {
1969 self.merge_remote_tag(symbol, base_ref, other_ref)?;
1970 }
1971
1972 let new_git_head_target = merge_ref_targets(
1973 self.index(),
1974 self.view().git_head(),
1975 base.git_head(),
1976 other.git_head(),
1977 )?;
1978 self.set_git_head_target(new_git_head_target);
1979
1980 Ok(())
1981 }
1982
1983 async fn record_rewrites(
1986 &mut self,
1987 old_heads: &[CommitId],
1988 new_heads: &[CommitId],
1989 ) -> BackendResult<()> {
1990 let mut removed_changes: HashMap<ChangeId, Vec<CommitId>> = HashMap::new();
1991 {
1992 let mut stream = revset::walk_revs(self, old_heads, new_heads)
1993 .map_err(|err| err.into_backend_error())?
1994 .commit_change_ids();
1995 while let Some((commit_id, change_id)) = stream
1996 .try_next()
1997 .await
1998 .map_err(|err| err.into_backend_error())?
1999 {
2000 removed_changes
2001 .entry(change_id)
2002 .or_default()
2003 .push(commit_id);
2004 }
2005 }
2006 if removed_changes.is_empty() {
2007 return Ok(());
2008 }
2009
2010 let mut rewritten_changes = HashSet::new();
2011 let mut rewritten_commits: HashMap<CommitId, Vec<CommitId>> = HashMap::new();
2012 {
2013 let mut stream = revset::walk_revs(self, new_heads, old_heads)
2014 .map_err(|err| err.into_backend_error())?
2015 .commit_change_ids();
2016 while let Some((commit_id, change_id)) = stream
2017 .try_next()
2018 .await
2019 .map_err(|err| err.into_backend_error())?
2020 {
2021 if let Some(old_commits) = removed_changes.get(&change_id) {
2022 for old_commit in old_commits {
2023 rewritten_commits
2024 .entry(old_commit.clone())
2025 .or_default()
2026 .push(commit_id.clone());
2027 }
2028 }
2029 rewritten_changes.insert(change_id);
2030 }
2031 }
2032 for (old_commit, new_commits) in rewritten_commits {
2033 if new_commits.len() == 1 {
2034 self.set_rewritten_commit(
2035 old_commit.clone(),
2036 new_commits.into_iter().next().unwrap(),
2037 );
2038 } else {
2039 self.set_divergent_rewrite(old_commit.clone(), new_commits);
2040 }
2041 }
2042
2043 for (change_id, removed_commit_ids) in &removed_changes {
2044 if !rewritten_changes.contains(change_id) {
2045 for id in removed_commit_ids {
2046 let commit = self.store().get_commit_async(id).await?;
2047 self.record_abandoned_commit(&commit);
2048 }
2049 }
2050 }
2051
2052 Ok(())
2053 }
2054}
2055
2056impl Repo for MutableRepo {
2057 fn base_repo(&self) -> &ReadonlyRepo {
2058 &self.base_repo
2059 }
2060
2061 fn store(&self) -> &Arc<Store> {
2062 self.base_repo.store()
2063 }
2064
2065 fn op_store(&self) -> &Arc<dyn OpStore> {
2066 self.base_repo.op_store()
2067 }
2068
2069 fn index(&self) -> &dyn Index {
2070 self.index.as_index()
2071 }
2072
2073 fn view(&self) -> &View {
2074 self.view
2075 .get_or_ensure_clean(|v| self.enforce_view_invariants(v))
2076 }
2077
2078 fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
2079 self.base_repo.submodule_store()
2080 }
2081
2082 fn resolve_change_id_prefix(
2083 &self,
2084 prefix: &HexPrefix,
2085 ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>> {
2086 let change_id_index = self.index.change_id_index(&mut self.view().heads().iter());
2087 change_id_index.resolve_prefix(prefix)
2088 }
2089
2090 fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> IndexResult<usize> {
2091 let change_id_index = self.index.change_id_index(&mut self.view().heads().iter());
2092 change_id_index.shortest_unique_prefix_len(target_id)
2093 }
2094}
2095
2096#[derive(Debug, Error)]
2098#[error("Cannot rewrite the root commit")]
2099pub struct RewriteRootCommit;
2100
2101#[derive(Debug, Error)]
2103pub enum EditCommitError {
2104 #[error("Current working-copy commit not found")]
2105 WorkingCopyCommitNotFound(#[source] BackendError),
2106 #[error(transparent)]
2107 RewriteRootCommit(#[from] RewriteRootCommit),
2108 #[error(transparent)]
2109 BackendError(#[from] BackendError),
2110}
2111
2112#[derive(Debug, Error)]
2114pub enum CheckOutCommitError {
2115 #[error("Failed to create new working-copy commit")]
2116 CreateCommit(#[from] BackendError),
2117 #[error("Failed to edit commit")]
2118 EditCommit(#[from] EditCommitError),
2119}
2120
2121mod dirty_cell {
2122 use std::cell::OnceCell;
2123 use std::cell::RefCell;
2124
2125 #[derive(Clone, Debug)]
2129 pub struct DirtyCell<T> {
2130 clean: OnceCell<Box<T>>,
2133 dirty: RefCell<Option<Box<T>>>,
2134 }
2135
2136 impl<T> DirtyCell<T> {
2137 pub fn with_clean(value: T) -> Self {
2138 Self {
2139 clean: OnceCell::from(Box::new(value)),
2140 dirty: RefCell::new(None),
2141 }
2142 }
2143
2144 pub fn get_or_ensure_clean(&self, f: impl FnOnce(&mut T)) -> &T {
2145 self.clean.get_or_init(|| {
2146 let mut value = self.dirty.borrow_mut().take().unwrap();
2148 f(&mut value);
2149 value
2150 })
2151 }
2152
2153 pub fn ensure_clean(&self, f: impl FnOnce(&mut T)) {
2154 self.get_or_ensure_clean(f);
2155 }
2156
2157 pub fn into_inner(self) -> T {
2158 *self
2159 .clean
2160 .into_inner()
2161 .or_else(|| self.dirty.into_inner())
2162 .unwrap()
2163 }
2164
2165 pub fn with_ref<R>(&self, f: impl FnOnce(&T) -> R) -> R {
2166 if let Some(value) = self.clean.get() {
2167 f(value)
2168 } else {
2169 f(self.dirty.borrow().as_ref().unwrap())
2170 }
2171 }
2172
2173 pub fn get_mut(&mut self) -> &mut T {
2174 self.clean
2175 .get_mut()
2176 .or_else(|| self.dirty.get_mut().as_mut())
2177 .unwrap()
2178 }
2179
2180 pub fn mark_dirty(&mut self) {
2181 if let Some(value) = self.clean.take() {
2182 *self.dirty.get_mut() = Some(value);
2183 }
2184 }
2185 }
2186}