Skip to main content

jj_lib/
repo.rs

1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![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    /// Base repository that contains all committed data. Returns `self` if this
121    /// is a `ReadonlyRepo`,
122    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        // Replace this if we added more efficient lookup method.
139        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    // TODO: This should eventually become part of the index and not be stored fully in memory.
164    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            // If the root op index couldn't be read, the index backend wouldn't
281            // be initialized properly.
282            .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] // auto-formatted line would exceed the maximum width
388pub type OpStoreInitializer<'a> =
389    dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError>
390    + 'a;
391#[rustfmt::skip] // auto-formatted line would exceed the maximum width
392pub 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        // Backends
439        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        // OpStores
463        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        // OpHeadsStores
471        factories.add_op_heads_store(
472            SimpleOpHeadsStore::name(),
473            Box::new(|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::load(store_path)))),
474        );
475
476        // Index
477        factories.add_index_store(
478            DefaultIndexStore::name(),
479            Box::new(|_settings, store_path| Ok(Box::new(DefaultIndexStore::load(store_path)))),
480        );
481
482        // SubmoduleStores
483        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/// Helps create `ReadonlyRepo` instances of a repo at the head operation or at
670/// a given operation.
671#[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    /// Creates a `RepoLoader` for the repo at `repo_path` by reading the
701    /// various `.jj/repo/<backend>/type` files and loading the right
702    /// backends from `store_factories`.
703    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    // If we add a higher-level abstraction of OpStore, root_operation() and
798    // load_operation() will be moved there.
799
800    /// Returns the root operation.
801    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    /// Loads the specified operation from the operation store.
808    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    /// Merges the given `operations` into a single operation. Returns the root
814    /// operation if the `operations` is empty.
815    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    /// The old commit was rewritten as this new commit. Children should be
877    /// rebased onto the new commit.
878    Rewritten(CommitId),
879    /// The old commit was rewritten as multiple other commits. Children should
880    /// not be rebased.
881    Divergent(Vec<CommitId>),
882    /// The old commit was abandoned. Children should be rebased onto the given
883    /// commits (typically the parents of the old commit).
884    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    /// Mapping from new commit to its predecessors.
902    ///
903    /// This is similar to (the reverse of) `parent_mapping`, but
904    /// `commit_predecessors` will never be cleared on `rebase_descendants()`.
905    commit_predecessors: BTreeMap<CommitId, Vec<CommitId>>,
906    // The commit identified by the key has been replaced by all the ones in the value.
907    // * Bookmarks pointing to the old commit should be updated to the new commit, resulting in a
908    //   conflict if there multiple new commits.
909    // * Children of the old commit should be rebased onto the new commits. However, if the type is
910    //   `Divergent`, they should be left in place.
911    // * Working copies pointing to the old commit should be updated to the first of the new
912    //   commits. However, if the type is `Abandoned`, a new working-copy commit should be created
913    //   on top of all of the new commits instead.
914    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    /// Returns a [`CommitBuilder`] to write new commit to the repo.
965    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    /// Returns a [`CommitBuilder`] to rewrite an existing commit in the repo.
971    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        // CommitBuilder::write will record the rewrite in
975        // `self.rewritten_commits`
976    }
977
978    pub(crate) fn set_predecessors(&mut self, id: CommitId, predecessors: Vec<CommitId>) {
979        self.commit_predecessors.insert(id, predecessors);
980    }
981
982    /// Record a commit as having been rewritten to another commit in this
983    /// transaction.
984    ///
985    /// This record is used by `rebase_descendants` to know which commits have
986    /// children that need to be rebased, and where to rebase them to. See the
987    /// docstring for `record_rewritten_commit` for details.
988    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    /// Record a commit as being rewritten into multiple other commits in this
995    /// transaction.
996    ///
997    /// A later call to `rebase_descendants()` will update bookmarks pointing to
998    /// `old_id` be conflicted and pointing to all pf `new_ids`. Working copies
999    /// pointing to `old_id` will be updated to point to the first commit in
1000    /// `new_ids`. Descendants of `old_id` will be left alone.
1001    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    /// Record a commit as having been abandoned in this transaction.
1014    ///
1015    /// This record is used by `rebase_descendants` to know which commits have
1016    /// children that need to be rebased, and where to rebase the children to.
1017    ///
1018    /// The `rebase_descendants` logic will rebase the descendants of the old
1019    /// commit to become the descendants of parent(s) of the old commit. Any
1020    /// bookmarks at the old commit will be either moved to the parent(s) of the
1021    /// old commit or deleted depending on [`RewriteRefsOptions`].
1022    pub fn record_abandoned_commit(&mut self, old_commit: &Commit) {
1023        assert_ne!(old_commit.id(), self.store().root_commit_id());
1024        // Descendants should be rebased onto the commit's parents
1025        self.record_abandoned_commit_with_parents(
1026            old_commit.id().clone(),
1027            old_commit.parent_ids().iter().cloned(),
1028        );
1029    }
1030
1031    /// Record a commit as having been abandoned in this transaction.
1032    ///
1033    /// A later `rebase_descendants()` will rebase children of `old_id` onto
1034    /// `new_parent_ids`. A working copy pointing to `old_id` will point to a
1035    /// new commit on top of `new_parent_ids`.
1036    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    /// Calculates new parents for a commit that's currently based on the given
1053    /// parents. It does that by considering how previous commits have been
1054    /// rewritten and abandoned.
1055    ///
1056    /// If `parent_mapping` contains cycles, this function may either panic or
1057    /// drop parents that caused cycles.
1058    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                        // Each commit must have a parent, so a parent can
1083                        // not just be mapped to nothing. This assertion
1084                        // could be removed if this function is used for
1085                        // mapping something other than a commit's parents.
1086                        !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    /// Fully resolves transitive replacements in `parent_mapping`.
1102    ///
1103    /// Returns an error if `parent_mapping` contains cycles
1104    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(), // unique() not needed
1129                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    /// Updates bookmarks, working copies, and anonymous heads after rewriting
1141    /// and/or abandoning commits.
1142    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            // TODO: indexing error shouldn't be a "BackendError"
1157            .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                // We arbitrarily pick a new working-copy commit among the candidates.
1218                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    /// Find descendants of `root`, unless they've already been rewritten
1268    /// (according to `parent_mapping`).
1269    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    /// Order a set of commits in an order they should be rebased in. The result
1290    /// is in reverse order so the next value can be removed from the end.
1291    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        // Calculate an order where we rebase parents first, but if the parents were
1300        // rewritten, make sure we rebase the rewritten parent first.
1301        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    /// Rewrite descendants of the given roots.
1336    ///
1337    /// The callback will be called for each commit with the new parents
1338    /// prepopulated. The callback may change the parents and write the new
1339    /// commit, or it may abandon the commit, or it may leave the old commit
1340    /// unchanged.
1341    ///
1342    /// The set of commits to visit is determined at the start. If the callback
1343    /// adds new descendants, then the callback will not be called for those.
1344    /// Similarly, if the callback rewrites unrelated commits, then the callback
1345    /// will not be called for descendants of those commits.
1346    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    /// Rewrite descendants of the given roots with options.
1357    ///
1358    /// If a commit is in the `new_parents_map` is provided, it will be rebased
1359    /// onto the new parents provided in the map instead of its original
1360    /// parents.
1361    ///
1362    /// See [`Self::transform_descendants()`] for details.
1363    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    /// Rewrite the given commits in reverse topological order.
1376    ///
1377    /// `commits` should be a connected range.
1378    ///
1379    /// This function is similar to
1380    /// [`Self::transform_descendants_with_options()`], but only rewrites the
1381    /// `commits` provided, and does not rewrite their descendants.
1382    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        // Since we didn't necessarily visit all descendants of rewritten commits (e.g.
1402        // if they were rewritten in the callback), there can still be commits left to
1403        // rebase, so we don't clear `parent_mapping` here.
1404        // TODO: Should we make this stricter? We could check that there were no
1405        // rewrites before this function was called, and we can check that only
1406        // commits in the `to_visit` set were added by the callback. Then we
1407        // could clear `parent_mapping` here and not have to scan it again at
1408        // the end of the transaction when we call `rebase_descendants()`.
1409
1410        Ok(())
1411    }
1412
1413    /// Rebase descendants of the rewritten commits with options and callback.
1414    ///
1415    /// The descendants of the commits registered in `self.parent_mappings` will
1416    /// be recursively rebased onto the new version of their parents.
1417    ///
1418    /// If `options.empty` is the default (`EmptyBehavior::Keep`), all rebased
1419    /// descendant commits will be preserved even if they were emptied following
1420    /// the rebase operation. Otherwise, this function may rebase some commits
1421    /// and abandon others, based on the given `EmptyBehavior`. The behavior is
1422    /// such that only commits with a single parent will ever be abandoned. The
1423    /// parent will inherit the descendants and the bookmarks of the abandoned
1424    /// commit.
1425    ///
1426    /// The `progress` callback will be invoked for each rebase operation with
1427    /// `(old_commit, rebased_commit)` as arguments.
1428    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    /// Rebase descendants of the rewritten commits.
1453    ///
1454    /// The descendants of the commits registered in `self.parent_mappings` will
1455    /// be recursively rebased onto the new version of their parents.
1456    /// Returns the number of rebased descendants.
1457    ///
1458    /// All rebased descendant commits will be preserved even if they were
1459    /// emptied following the rebase operation. To customize the rebase
1460    /// behavior, use [`MutableRepo::rebase_descendants_with_options`].
1461    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    /// Reparent descendants of the rewritten commits.
1472    ///
1473    /// The descendants of the commits registered in `self.parent_mappings` will
1474    /// be recursively reparented onto the new version of their parents.
1475    /// The content of those descendants will remain untouched.
1476    /// Returns the number of reparented descendants.
1477    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    /// Merges working-copy commit. If there's a conflict, and if the workspace
1512    /// isn't removed at either side, we keep the self side.
1513    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        // Not using merge_ref_targets(). Since the working-copy pointer moves
1522        // towards random direction, it doesn't make sense to resolve conflict
1523        // based on ancestry.
1524        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            // We want to remove the workspace even if the self side changed the
1530            // working-copy commit.
1531            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                // Abandon the working-copy commit we're leaving if it's
1605                // discardable, not pointed by local bookmark, tag, or other
1606                // working copies, and is a head commit.
1607                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            // An empty head_ids set is padded with the root_commit_id, but the
1621            // root id is unwanted during the heads resolution.
1622            view.head_ids.remove(root_commit_id);
1623            // It is unclear if `heads` can never fail for default implementation,
1624            // but it can definitely fail for non-default implementations.
1625            // TODO: propagate errors.
1626            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    /// Ensures that the given `head` and ancestor commits are reachable from
1637    /// the visible heads.
1638    pub async fn add_head(&mut self, head: &Commit) -> BackendResult<()> {
1639        self.add_heads(slice::from_ref(head)).await
1640    }
1641
1642    /// Ensures that the given `heads` and ancestor commits are reachable from
1643    /// the visible heads.
1644    ///
1645    /// The `heads` may contain redundant commits such as already visible ones
1646    /// and ancestors of the other heads. The `heads` and ancestor commits
1647    /// should exist in the store.
1648    pub async fn add_heads(&mut self, heads: &[Commit]) -> BackendResult<()> {
1649        let current_heads = self.view.get_mut().heads();
1650        // Use incremental update for common case of adding a single commit on top a
1651        // current head. TODO: Also use incremental update when adding a single
1652        // commit on top a non-head.
1653        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                    // TODO: indexing error shouldn't be a "BackendError"
1665                    .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    /// Adds the given `heads` and ancestor commits to the index without making
1688    /// them visible. Returns newly-indexed commits.
1689    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                    // TODO: indexing error shouldn't be a "BackendError"
1697                    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                        // TODO: indexing error shouldn't be a "BackendError"
1711                        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                // TODO: indexing error shouldn't be a "BackendError"
1724                .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    /// Merges the specified remote bookmark in to local bookmark, and starts
1784    /// tracking it.
1785    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    /// Stops tracking the specified remote bookmark.
1795    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        // First, merge the index, so we can take advantage of a valid index when
1898        // merging the view. Merging in base_repo's index isn't typically
1899        // necessary, but it can be if base_repo is ahead of either self or other_repo
1900        // (e.g. because we're undoing an operation that hasn't been published).
1901        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        // HACK: Don't walk long ranges of commits to find rewrites when using other
1925        // custom implementations. The only custom index implementation we're currently
1926        // aware of is Google's. That repo has too high commit rate for it to be
1927        // feasible to walk all added and removed commits.
1928        // TODO: Fix this somehow. Maybe a method on `Index` to find rewritten commits
1929        // given `base_heads`, `own_heads` and `other_heads`?
1930        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            // No need to remove heads removed by `other` because we already
1934            // marked them abandoned or rewritten.
1935        } 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    /// Finds and records commits that were rewritten or abandoned between
1984    /// `old_heads` and `new_heads`.
1985    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/// Error from attempts to check out the root commit for editing
2097#[derive(Debug, Error)]
2098#[error("Cannot rewrite the root commit")]
2099pub struct RewriteRootCommit;
2100
2101/// Error from attempts to edit a commit
2102#[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/// Error from attempts to check out a commit
2113#[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    /// Cell that lazily updates the value after `mark_dirty()`.
2126    ///
2127    /// A clean value can be immutably borrowed within the `self` lifetime.
2128    #[derive(Clone, Debug)]
2129    pub struct DirtyCell<T> {
2130        // Either clean or dirty value is set. The value is boxed to reduce stack space
2131        // and memcopy overhead.
2132        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                // Panics if ensure_clean() is invoked from with_ref() callback for example.
2147                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}