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::ResolvedRevsetExpression;
97use crate::revset::RevsetEvaluationError;
98use crate::revset::RevsetExpression;
99use crate::revset::RevsetStreamExt as _;
100use crate::rewrite::CommitRewriter;
101use crate::rewrite::RebaseOptions;
102use crate::rewrite::RebasedCommit;
103use crate::rewrite::RewriteRefsOptions;
104use crate::rewrite::merge_commit_trees;
105use crate::rewrite::rebase_commit_with_options;
106use crate::settings::UserSettings;
107use crate::signing::SignInitError;
108use crate::signing::Signer;
109use crate::simple_backend::SimpleBackend;
110use crate::simple_op_heads_store::SimpleOpHeadsStore;
111use crate::simple_op_store::SimpleOpStore;
112use crate::store::Store;
113use crate::submodule_store::SubmoduleStore;
114use crate::transaction::Transaction;
115use crate::transaction::TransactionCommitError;
116use crate::tree_merge::MergeOptions;
117use crate::view::RenameWorkspaceError;
118use crate::view::View;
119
120pub trait Repo {
121    /// Base repository that contains all committed data. Returns `self` if this
122    /// is a `ReadonlyRepo`,
123    fn base_repo(&self) -> &ReadonlyRepo;
124
125    fn store(&self) -> &Arc<Store>;
126
127    fn op_store(&self) -> &Arc<dyn OpStore>;
128
129    fn index(&self) -> &dyn Index;
130
131    fn view(&self) -> &View;
132
133    fn submodule_store(&self) -> &Arc<dyn SubmoduleStore>;
134
135    fn resolve_change_id(
136        &self,
137        change_id: &ChangeId,
138    ) -> IndexResult<Option<ResolvedChangeTargets>> {
139        // Replace this if we added more efficient lookup method.
140        let prefix = HexPrefix::from_id(change_id);
141        match self.resolve_change_id_prefix(&prefix)? {
142            PrefixResolution::NoMatch => Ok(None),
143            PrefixResolution::SingleMatch(entries) => Ok(Some(entries)),
144            PrefixResolution::AmbiguousMatch => panic!("complete change_id should be unambiguous"),
145        }
146    }
147
148    fn resolve_change_id_prefix(
149        &self,
150        prefix: &HexPrefix,
151    ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>>;
152
153    fn shortest_unique_change_id_prefix_len(
154        &self,
155        target_id_bytes: &ChangeId,
156    ) -> IndexResult<usize>;
157}
158
159pub struct ReadonlyRepo {
160    loader: RepoLoader,
161    operation: Operation,
162    index: Box<dyn ReadonlyIndex>,
163    change_id_index: OnceCell<Box<dyn ChangeIdIndex>>,
164    // TODO: This should eventually become part of the index and not be stored fully in memory.
165    view: View,
166}
167
168impl Debug for ReadonlyRepo {
169    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
170        f.debug_struct("ReadonlyRepo")
171            .field("store", &self.loader.store)
172            .finish_non_exhaustive()
173    }
174}
175
176#[derive(Error, Debug)]
177pub enum RepoInitError {
178    #[error(transparent)]
179    Backend(#[from] BackendInitError),
180    #[error(transparent)]
181    OpHeadsStore(#[from] OpHeadsStoreError),
182    #[error(transparent)]
183    Path(#[from] PathError),
184}
185
186impl ReadonlyRepo {
187    pub fn default_op_store_initializer() -> &'static OpStoreInitializer<'static> {
188        &|_settings, store_path, root_data| {
189            Ok(Box::new(SimpleOpStore::init(store_path, root_data)?))
190        }
191    }
192
193    pub fn default_op_heads_store_initializer() -> &'static OpHeadsStoreInitializer<'static> {
194        &|_settings, store_path, root_op_id| {
195            Ok(Box::new(SimpleOpHeadsStore::init(store_path, root_op_id)?))
196        }
197    }
198
199    pub fn default_index_store_initializer() -> &'static IndexStoreInitializer<'static> {
200        &|_settings, store_path| Ok(Box::new(DefaultIndexStore::init(store_path)?))
201    }
202
203    pub fn default_submodule_store_initializer() -> &'static SubmoduleStoreInitializer<'static> {
204        &|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::init(store_path)))
205    }
206
207    #[expect(clippy::too_many_arguments)]
208    pub async fn init(
209        settings: &UserSettings,
210        repo_path: &Path,
211        backend_initializer: &BackendInitializer<'_>,
212        signer: Signer,
213        op_store_initializer: &OpStoreInitializer<'_>,
214        op_heads_store_initializer: &OpHeadsStoreInitializer<'_>,
215        index_store_initializer: &IndexStoreInitializer<'_>,
216        submodule_store_initializer: &SubmoduleStoreInitializer<'_>,
217    ) -> Result<Arc<Self>, RepoInitError> {
218        let repo_path = dunce::canonicalize(repo_path).context(repo_path)?;
219
220        let store_path = repo_path.join("store");
221        fs::create_dir(&store_path).context(&store_path)?;
222        let backend = backend_initializer(settings, &store_path)?;
223        let backend_path = store_path.join("type");
224        fs::write(&backend_path, backend.name()).context(&backend_path)?;
225        let merge_options =
226            MergeOptions::from_settings(settings).map_err(|err| BackendInitError(err.into()))?;
227        let store = Store::new(backend, signer, merge_options);
228
229        let op_store_path = repo_path.join("op_store");
230        fs::create_dir(&op_store_path).context(&op_store_path)?;
231        let root_op_data = RootOperationData {
232            root_commit_id: store.root_commit_id().clone(),
233        };
234        let op_store = op_store_initializer(settings, &op_store_path, root_op_data)?;
235        let op_store_type_path = op_store_path.join("type");
236        fs::write(&op_store_type_path, op_store.name()).context(&op_store_type_path)?;
237        let op_store: Arc<dyn OpStore> = Arc::from(op_store);
238
239        let op_heads_path = repo_path.join("op_heads");
240        fs::create_dir(&op_heads_path).context(&op_heads_path)?;
241        let op_heads_store =
242            op_heads_store_initializer(settings, &op_heads_path, op_store.root_operation_id())?;
243        let op_heads_type_path = op_heads_path.join("type");
244        fs::write(&op_heads_type_path, op_heads_store.name()).context(&op_heads_type_path)?;
245        let op_heads_store: Arc<dyn OpHeadsStore> = Arc::from(op_heads_store);
246
247        let index_path = repo_path.join("index");
248        fs::create_dir(&index_path).context(&index_path)?;
249        let index_store = index_store_initializer(settings, &index_path)?;
250        let index_type_path = index_path.join("type");
251        fs::write(&index_type_path, index_store.name()).context(&index_type_path)?;
252        let index_store: Arc<dyn IndexStore> = Arc::from(index_store);
253
254        let submodule_store_path = repo_path.join("submodule_store");
255        fs::create_dir(&submodule_store_path).context(&submodule_store_path)?;
256        let submodule_store = submodule_store_initializer(settings, &submodule_store_path)?;
257        let submodule_store_type_path = submodule_store_path.join("type");
258        fs::write(&submodule_store_type_path, submodule_store.name())
259            .context(&submodule_store_type_path)?;
260        let submodule_store = Arc::from(submodule_store);
261
262        let loader = RepoLoader {
263            settings: settings.clone(),
264            store,
265            op_store,
266            op_heads_store,
267            index_store,
268            submodule_store,
269        };
270
271        let root_operation = loader.root_operation().await;
272        let root_view = root_operation
273            .view()
274            .await
275            .expect("failed to read root view");
276        assert!(!root_view.heads().is_empty());
277        let index = loader
278            .index_store
279            .get_index_at_op(&root_operation, &loader.store)
280            .await
281            // If the root op index couldn't be read, the index backend wouldn't
282            // be initialized properly.
283            .map_err(|err| BackendInitError(err.into()))?;
284        Ok(Arc::new(Self {
285            loader,
286            operation: root_operation,
287            index,
288            change_id_index: OnceCell::new(),
289            view: root_view,
290        }))
291    }
292
293    pub fn loader(&self) -> &RepoLoader {
294        &self.loader
295    }
296
297    pub fn op_id(&self) -> &OperationId {
298        self.operation.id()
299    }
300
301    pub fn operation(&self) -> &Operation {
302        &self.operation
303    }
304
305    pub fn view(&self) -> &View {
306        &self.view
307    }
308
309    pub fn readonly_index(&self) -> &dyn ReadonlyIndex {
310        self.index.as_ref()
311    }
312
313    fn change_id_index(&self) -> &dyn ChangeIdIndex {
314        self.change_id_index
315            .get_or_init(|| {
316                self.readonly_index()
317                    .change_id_index(&mut self.view().heads().iter())
318            })
319            .as_ref()
320    }
321
322    pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> {
323        self.loader.op_heads_store()
324    }
325
326    pub fn index_store(&self) -> &Arc<dyn IndexStore> {
327        self.loader.index_store()
328    }
329
330    pub fn settings(&self) -> &UserSettings {
331        self.loader.settings()
332    }
333
334    pub fn start_transaction(self: &Arc<Self>) -> Transaction {
335        let mut_repo = MutableRepo::new(self.clone(), self.readonly_index(), &self.view);
336        Transaction::new(mut_repo, self.settings())
337    }
338
339    pub async fn reload_at_head(&self) -> Result<Arc<Self>, RepoLoaderError> {
340        self.loader().load_at_head().await
341    }
342
343    #[instrument]
344    pub async fn reload_at(&self, operation: &Operation) -> Result<Arc<Self>, RepoLoaderError> {
345        self.loader().load_at(operation).await
346    }
347}
348
349impl Repo for ReadonlyRepo {
350    fn base_repo(&self) -> &ReadonlyRepo {
351        self
352    }
353
354    fn store(&self) -> &Arc<Store> {
355        self.loader.store()
356    }
357
358    fn op_store(&self) -> &Arc<dyn OpStore> {
359        self.loader.op_store()
360    }
361
362    fn index(&self) -> &dyn Index {
363        self.readonly_index().as_index()
364    }
365
366    fn view(&self) -> &View {
367        &self.view
368    }
369
370    fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
371        self.loader.submodule_store()
372    }
373
374    fn resolve_change_id_prefix(
375        &self,
376        prefix: &HexPrefix,
377    ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>> {
378        self.change_id_index().resolve_prefix(prefix)
379    }
380
381    fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> IndexResult<usize> {
382        self.change_id_index().shortest_unique_prefix_len(target_id)
383    }
384}
385
386pub type BackendInitializer<'a> =
387    dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendInitError> + 'a;
388#[rustfmt::skip] // auto-formatted line would exceed the maximum width
389pub type OpStoreInitializer<'a> =
390    dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError>
391    + 'a;
392#[rustfmt::skip] // auto-formatted line would exceed the maximum width
393pub type OpHeadsStoreInitializer<'a> = 
394    dyn Fn(&UserSettings, &Path, &OperationId)
395    -> Result<Box<dyn OpHeadsStore>, BackendInitError>
396    + 'a;
397pub type IndexStoreInitializer<'a> =
398    dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendInitError> + 'a;
399pub type SubmoduleStoreInitializer<'a> =
400    dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendInitError> + 'a;
401
402type BackendFactory =
403    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>;
404type OpStoreFactory = Box<
405    dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>,
406>;
407type OpHeadsStoreFactory =
408    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>;
409type IndexStoreFactory =
410    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>;
411type SubmoduleStoreFactory =
412    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>;
413
414pub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) {
415    for (name, factory) in ext {
416        match base.entry(name) {
417            Entry::Vacant(v) => {
418                v.insert(factory);
419            }
420            Entry::Occupied(o) => {
421                panic!("Conflicting factory definitions for '{}' factory", o.key())
422            }
423        }
424    }
425}
426
427pub struct StoreFactories {
428    backend_factories: HashMap<String, BackendFactory>,
429    op_store_factories: HashMap<String, OpStoreFactory>,
430    op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>,
431    index_store_factories: HashMap<String, IndexStoreFactory>,
432    submodule_store_factories: HashMap<String, SubmoduleStoreFactory>,
433}
434
435impl Default for StoreFactories {
436    fn default() -> Self {
437        let mut factories = Self::empty();
438
439        // Backends
440        factories.add_backend(
441            SimpleBackend::name(),
442            Box::new(|_settings, store_path| Ok(Box::new(SimpleBackend::load(store_path)))),
443        );
444        #[cfg(feature = "git")]
445        factories.add_backend(
446            crate::git_backend::GitBackend::name(),
447            Box::new(|settings, store_path| {
448                Ok(Box::new(crate::git_backend::GitBackend::load(
449                    settings, store_path,
450                )?))
451            }),
452        );
453        #[cfg(feature = "testing")]
454        factories.add_backend(
455            crate::secret_backend::SecretBackend::name(),
456            Box::new(|settings, store_path| {
457                Ok(Box::new(crate::secret_backend::SecretBackend::load(
458                    settings, store_path,
459                )?))
460            }),
461        );
462
463        // OpStores
464        factories.add_op_store(
465            SimpleOpStore::name(),
466            Box::new(|_settings, store_path, root_data| {
467                Ok(Box::new(SimpleOpStore::load(store_path, root_data)))
468            }),
469        );
470
471        // OpHeadsStores
472        factories.add_op_heads_store(
473            SimpleOpHeadsStore::name(),
474            Box::new(|_settings, store_path| Ok(Box::new(SimpleOpHeadsStore::load(store_path)))),
475        );
476
477        // Index
478        factories.add_index_store(
479            DefaultIndexStore::name(),
480            Box::new(|_settings, store_path| Ok(Box::new(DefaultIndexStore::load(store_path)))),
481        );
482
483        // SubmoduleStores
484        factories.add_submodule_store(
485            DefaultSubmoduleStore::name(),
486            Box::new(|_settings, store_path| Ok(Box::new(DefaultSubmoduleStore::load(store_path)))),
487        );
488
489        factories
490    }
491}
492
493#[derive(Debug, Error)]
494pub enum StoreLoadError {
495    #[error("Unsupported {store} backend type '{store_type}'")]
496    UnsupportedType {
497        store: &'static str,
498        store_type: String,
499    },
500    #[error("Failed to read {store} backend type")]
501    ReadError {
502        store: &'static str,
503        source: PathError,
504    },
505    #[error(transparent)]
506    Backend(#[from] BackendLoadError),
507    #[error(transparent)]
508    Signing(#[from] SignInitError),
509}
510
511impl StoreFactories {
512    pub fn empty() -> Self {
513        Self {
514            backend_factories: HashMap::new(),
515            op_store_factories: HashMap::new(),
516            op_heads_store_factories: HashMap::new(),
517            index_store_factories: HashMap::new(),
518            submodule_store_factories: HashMap::new(),
519        }
520    }
521
522    pub fn merge(&mut self, ext: Self) {
523        let Self {
524            backend_factories,
525            op_store_factories,
526            op_heads_store_factories,
527            index_store_factories,
528            submodule_store_factories,
529        } = ext;
530
531        merge_factories_map(&mut self.backend_factories, backend_factories);
532        merge_factories_map(&mut self.op_store_factories, op_store_factories);
533        merge_factories_map(&mut self.op_heads_store_factories, op_heads_store_factories);
534        merge_factories_map(&mut self.index_store_factories, index_store_factories);
535        merge_factories_map(
536            &mut self.submodule_store_factories,
537            submodule_store_factories,
538        );
539    }
540
541    pub fn add_backend(&mut self, name: &str, factory: BackendFactory) {
542        self.backend_factories.insert(name.to_string(), factory);
543    }
544
545    pub fn load_backend(
546        &self,
547        settings: &UserSettings,
548        store_path: &Path,
549    ) -> Result<Box<dyn Backend>, StoreLoadError> {
550        let backend_type = read_store_type("commit", store_path.join("type"))?;
551        let backend_factory = self.backend_factories.get(&backend_type).ok_or_else(|| {
552            StoreLoadError::UnsupportedType {
553                store: "commit",
554                store_type: backend_type.clone(),
555            }
556        })?;
557        Ok(backend_factory(settings, store_path)?)
558    }
559
560    pub fn add_op_store(&mut self, name: &str, factory: OpStoreFactory) {
561        self.op_store_factories.insert(name.to_string(), factory);
562    }
563
564    pub fn load_op_store(
565        &self,
566        settings: &UserSettings,
567        store_path: &Path,
568        root_data: RootOperationData,
569    ) -> Result<Box<dyn OpStore>, StoreLoadError> {
570        let op_store_type = read_store_type("operation", store_path.join("type"))?;
571        let op_store_factory = self.op_store_factories.get(&op_store_type).ok_or_else(|| {
572            StoreLoadError::UnsupportedType {
573                store: "operation",
574                store_type: op_store_type.clone(),
575            }
576        })?;
577        Ok(op_store_factory(settings, store_path, root_data)?)
578    }
579
580    pub fn add_op_heads_store(&mut self, name: &str, factory: OpHeadsStoreFactory) {
581        self.op_heads_store_factories
582            .insert(name.to_string(), factory);
583    }
584
585    pub fn load_op_heads_store(
586        &self,
587        settings: &UserSettings,
588        store_path: &Path,
589    ) -> Result<Box<dyn OpHeadsStore>, StoreLoadError> {
590        let op_heads_store_type = read_store_type("operation heads", store_path.join("type"))?;
591        let op_heads_store_factory = self
592            .op_heads_store_factories
593            .get(&op_heads_store_type)
594            .ok_or_else(|| StoreLoadError::UnsupportedType {
595                store: "operation heads",
596                store_type: op_heads_store_type.clone(),
597            })?;
598        Ok(op_heads_store_factory(settings, store_path)?)
599    }
600
601    pub fn add_index_store(&mut self, name: &str, factory: IndexStoreFactory) {
602        self.index_store_factories.insert(name.to_string(), factory);
603    }
604
605    pub fn load_index_store(
606        &self,
607        settings: &UserSettings,
608        store_path: &Path,
609    ) -> Result<Box<dyn IndexStore>, StoreLoadError> {
610        let index_store_type = read_store_type("index", store_path.join("type"))?;
611        let index_store_factory = self
612            .index_store_factories
613            .get(&index_store_type)
614            .ok_or_else(|| StoreLoadError::UnsupportedType {
615                store: "index",
616                store_type: index_store_type.clone(),
617            })?;
618        Ok(index_store_factory(settings, store_path)?)
619    }
620
621    pub fn add_submodule_store(&mut self, name: &str, factory: SubmoduleStoreFactory) {
622        self.submodule_store_factories
623            .insert(name.to_string(), factory);
624    }
625
626    pub fn load_submodule_store(
627        &self,
628        settings: &UserSettings,
629        store_path: &Path,
630    ) -> Result<Box<dyn SubmoduleStore>, StoreLoadError> {
631        let submodule_store_type = read_store_type("submodule_store", store_path.join("type"))?;
632        let submodule_store_factory = self
633            .submodule_store_factories
634            .get(&submodule_store_type)
635            .ok_or_else(|| StoreLoadError::UnsupportedType {
636                store: "submodule_store",
637                store_type: submodule_store_type.clone(),
638            })?;
639
640        Ok(submodule_store_factory(settings, store_path)?)
641    }
642}
643
644pub fn read_store_type(
645    store: &'static str,
646    path: impl AsRef<Path>,
647) -> Result<String, StoreLoadError> {
648    let path = path.as_ref();
649    fs::read_to_string(path)
650        .context(path)
651        .map_err(|source| StoreLoadError::ReadError { store, source })
652}
653
654#[derive(Debug, Error)]
655pub enum RepoLoaderError {
656    #[error(transparent)]
657    Backend(#[from] BackendError),
658    #[error(transparent)]
659    Index(#[from] IndexError),
660    #[error(transparent)]
661    IndexStore(#[from] IndexStoreError),
662    #[error(transparent)]
663    OpHeadsStoreError(#[from] OpHeadsStoreError),
664    #[error(transparent)]
665    OpStore(#[from] OpStoreError),
666    #[error(transparent)]
667    TransactionCommit(#[from] TransactionCommitError),
668}
669
670/// Helps create `ReadonlyRepo` instances of a repo at the head operation or at
671/// a given operation.
672#[derive(Clone)]
673pub struct RepoLoader {
674    settings: UserSettings,
675    store: Arc<Store>,
676    op_store: Arc<dyn OpStore>,
677    op_heads_store: Arc<dyn OpHeadsStore>,
678    index_store: Arc<dyn IndexStore>,
679    submodule_store: Arc<dyn SubmoduleStore>,
680}
681
682impl RepoLoader {
683    pub fn new(
684        settings: UserSettings,
685        store: Arc<Store>,
686        op_store: Arc<dyn OpStore>,
687        op_heads_store: Arc<dyn OpHeadsStore>,
688        index_store: Arc<dyn IndexStore>,
689        submodule_store: Arc<dyn SubmoduleStore>,
690    ) -> Self {
691        Self {
692            settings,
693            store,
694            op_store,
695            op_heads_store,
696            index_store,
697            submodule_store,
698        }
699    }
700
701    /// Creates a `RepoLoader` for the repo at `repo_path` by reading the
702    /// various `.jj/repo/<backend>/type` files and loading the right
703    /// backends from `store_factories`.
704    pub fn init_from_file_system(
705        settings: &UserSettings,
706        repo_path: &Path,
707        store_factories: &StoreFactories,
708    ) -> Result<Self, StoreLoadError> {
709        let merge_options =
710            MergeOptions::from_settings(settings).map_err(|err| BackendLoadError(err.into()))?;
711        let store = Store::new(
712            store_factories.load_backend(settings, &repo_path.join("store"))?,
713            Signer::from_settings(settings)?,
714            merge_options,
715        );
716        let root_op_data = RootOperationData {
717            root_commit_id: store.root_commit_id().clone(),
718        };
719        let op_store = Arc::from(store_factories.load_op_store(
720            settings,
721            &repo_path.join("op_store"),
722            root_op_data,
723        )?);
724        let op_heads_store =
725            Arc::from(store_factories.load_op_heads_store(settings, &repo_path.join("op_heads"))?);
726        let index_store =
727            Arc::from(store_factories.load_index_store(settings, &repo_path.join("index"))?);
728        let submodule_store = Arc::from(
729            store_factories.load_submodule_store(settings, &repo_path.join("submodule_store"))?,
730        );
731        Ok(Self {
732            settings: settings.clone(),
733            store,
734            op_store,
735            op_heads_store,
736            index_store,
737            submodule_store,
738        })
739    }
740
741    pub fn settings(&self) -> &UserSettings {
742        &self.settings
743    }
744
745    pub fn store(&self) -> &Arc<Store> {
746        &self.store
747    }
748
749    pub fn index_store(&self) -> &Arc<dyn IndexStore> {
750        &self.index_store
751    }
752
753    pub fn op_store(&self) -> &Arc<dyn OpStore> {
754        &self.op_store
755    }
756
757    pub fn op_heads_store(&self) -> &Arc<dyn OpHeadsStore> {
758        &self.op_heads_store
759    }
760
761    pub fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
762        &self.submodule_store
763    }
764
765    pub async fn load_at_head(&self) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
766        let op = op_heads_store::resolve_op_heads(
767            self.op_heads_store.as_ref(),
768            &self.op_store,
769            async |op_heads| {
770                assert!(!op_heads.is_empty());
771                self.merge_operations(op_heads, Some("reconcile divergent operations"))
772                    .await
773            },
774        )
775        .await?;
776        let view = op.view().await?;
777        self.finish_load(op, view).await
778    }
779
780    #[instrument(skip(self))]
781    pub async fn load_at(&self, op: &Operation) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
782        let view = op.view().await?;
783        self.finish_load(op.clone(), view).await
784    }
785
786    pub fn create_from(
787        &self,
788        operation: Operation,
789        view: View,
790        index: Box<dyn ReadonlyIndex>,
791    ) -> Arc<ReadonlyRepo> {
792        let repo = ReadonlyRepo {
793            loader: self.clone(),
794            operation,
795            index,
796            change_id_index: OnceCell::new(),
797            view,
798        };
799        Arc::new(repo)
800    }
801
802    // If we add a higher-level abstraction of OpStore, root_operation() and
803    // load_operation() will be moved there.
804
805    /// Returns the root operation.
806    pub async fn root_operation(&self) -> Operation {
807        self.load_operation(self.op_store.root_operation_id())
808            .await
809            .expect("failed to read root operation")
810    }
811
812    /// Loads the specified operation from the operation store.
813    pub async fn load_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
814        let data = self.op_store.read_operation(id).await?;
815        Ok(Operation::new(self.op_store.clone(), id.clone(), data))
816    }
817
818    /// Merges the given `operations` into a single operation. Returns the root
819    /// operation if the `operations` is empty.
820    pub async fn merge_operations(
821        &self,
822        operations: Vec<Operation>,
823        tx_description: Option<&str>,
824    ) -> Result<Operation, RepoLoaderError> {
825        let num_operations = operations.len();
826        let mut operations = operations.into_iter();
827        let Some(base_op) = operations.next() else {
828            return Ok(self.root_operation().await);
829        };
830        let final_op = if num_operations > 1 {
831            let base_repo = self.load_at(&base_op).await?;
832            let mut tx = base_repo.start_transaction();
833            for other_op in operations {
834                tx.merge_operation(other_op).await?;
835                tx.repo_mut().rebase_descendants().await?;
836            }
837            let tx_description = tx_description.map_or_else(
838                || format!("merge {num_operations} operations"),
839                |tx_description| tx_description.to_string(),
840            );
841            let merged_repo = tx.write(tx_description).await?.leave_unpublished();
842            merged_repo.operation().clone()
843        } else {
844            base_op
845        };
846
847        Ok(final_op)
848    }
849
850    async fn finish_load(
851        &self,
852        operation: Operation,
853        view: View,
854    ) -> Result<Arc<ReadonlyRepo>, RepoLoaderError> {
855        let index = self
856            .index_store
857            .get_index_at_op(&operation, &self.store)
858            .await?;
859        let repo = ReadonlyRepo {
860            loader: self.clone(),
861            operation,
862            index,
863            change_id_index: OnceCell::new(),
864            view,
865        };
866        Ok(Arc::new(repo))
867    }
868}
869
870#[derive(Clone, Debug, PartialEq, Eq)]
871enum Rewrite {
872    /// The old commit was rewritten as this new commit. Children should be
873    /// rebased onto the new commit.
874    Rewritten(CommitId),
875    /// The old commit was rewritten as multiple other commits. Children should
876    /// not be rebased.
877    Divergent(Vec<CommitId>),
878    /// The old commit was abandoned. Children should be rebased onto the given
879    /// commits (typically the parents of the old commit).
880    Abandoned(Vec<CommitId>),
881}
882
883impl Rewrite {
884    fn new_parent_ids(&self) -> &[CommitId] {
885        match self {
886            Self::Rewritten(new_parent_id) => std::slice::from_ref(new_parent_id),
887            Self::Divergent(new_parent_ids) => new_parent_ids.as_slice(),
888            Self::Abandoned(new_parent_ids) => new_parent_ids.as_slice(),
889        }
890    }
891}
892
893pub struct MutableRepo {
894    base_repo: Arc<ReadonlyRepo>,
895    index: Box<dyn MutableIndex>,
896    view: DirtyCell<View>,
897    /// Mapping from new commit to its predecessors.
898    ///
899    /// This is similar to (the reverse of) `parent_mapping`, but
900    /// `commit_predecessors` will never be cleared on `rebase_descendants()`.
901    commit_predecessors: BTreeMap<CommitId, Vec<CommitId>>,
902    // The commit identified by the key has been replaced by all the ones in the value.
903    // * Bookmarks pointing to the old commit should be updated to the new commit, resulting in a
904    //   conflict if there multiple new commits.
905    // * Children of the old commit should be rebased onto the new commits. However, if the type is
906    //   `Divergent`, they should be left in place.
907    // * Working copies pointing to the old commit should be updated to the first of the new
908    //   commits. However, if the type is `Abandoned`, a new working-copy commit should be created
909    //   on top of all of the new commits instead.
910    parent_mapping: HashMap<CommitId, Rewrite>,
911}
912
913impl MutableRepo {
914    pub fn new(base_repo: Arc<ReadonlyRepo>, index: &dyn ReadonlyIndex, view: &View) -> Self {
915        let mut_view = view.clone();
916        let mut_index = index.start_modification();
917        Self {
918            base_repo,
919            index: mut_index,
920            view: DirtyCell::with_clean(mut_view),
921            commit_predecessors: Default::default(),
922            parent_mapping: Default::default(),
923        }
924    }
925
926    pub fn base_repo(&self) -> &Arc<ReadonlyRepo> {
927        &self.base_repo
928    }
929
930    fn view_mut(&mut self) -> &mut View {
931        self.view.get_mut()
932    }
933
934    pub fn mutable_index(&self) -> &dyn MutableIndex {
935        self.index.as_ref()
936    }
937
938    pub(crate) fn is_backed_by_default_index(&self) -> bool {
939        self.index.downcast_ref::<DefaultMutableIndex>().is_some()
940    }
941
942    pub fn has_changes(&self) -> bool {
943        self.view.ensure_clean(|v| self.enforce_view_invariants(v));
944        !(self.commit_predecessors.is_empty()
945            && self.parent_mapping.is_empty()
946            && self.view() == &self.base_repo.view)
947    }
948
949    pub(crate) fn consume(
950        self,
951    ) -> (
952        Box<dyn MutableIndex>,
953        View,
954        BTreeMap<CommitId, Vec<CommitId>>,
955    ) {
956        self.view.ensure_clean(|v| self.enforce_view_invariants(v));
957        (self.index, self.view.into_inner(), self.commit_predecessors)
958    }
959
960    /// Returns a [`CommitBuilder`] to write new commit to the repo.
961    pub fn new_commit(&mut self, parents: Vec<CommitId>, tree: MergedTree) -> CommitBuilder<'_> {
962        let settings = self.base_repo.settings();
963        DetachedCommitBuilder::for_new_commit(self, settings, parents, tree).attach(self)
964    }
965
966    /// Returns a [`CommitBuilder`] to rewrite an existing commit in the repo.
967    pub fn rewrite_commit(&mut self, predecessor: &Commit) -> CommitBuilder<'_> {
968        let settings = self.base_repo.settings();
969        DetachedCommitBuilder::for_rewrite_from(self, settings, predecessor).attach(self)
970        // CommitBuilder::write will record the rewrite in
971        // `self.rewritten_commits`
972    }
973
974    pub(crate) fn set_predecessors(&mut self, id: CommitId, predecessors: Vec<CommitId>) {
975        self.commit_predecessors.insert(id, predecessors);
976    }
977
978    /// Record a commit as having been rewritten to another commit in this
979    /// transaction.
980    ///
981    /// This record is used by `rebase_descendants` to know which commits have
982    /// children that need to be rebased, and where to rebase them to. See the
983    /// docstring for `record_rewritten_commit` for details.
984    pub fn set_rewritten_commit(&mut self, old_id: CommitId, new_id: CommitId) {
985        assert_ne!(old_id, *self.store().root_commit_id());
986        self.parent_mapping
987            .insert(old_id, Rewrite::Rewritten(new_id));
988    }
989
990    /// Record a commit as being rewritten into multiple other commits in this
991    /// transaction.
992    ///
993    /// A later call to `rebase_descendants()` will update bookmarks pointing to
994    /// `old_id` be conflicted and pointing to all pf `new_ids`. Working copies
995    /// pointing to `old_id` will be updated to point to the first commit in
996    /// `new_ids`. Descendants of `old_id` will be left alone.
997    pub fn set_divergent_rewrite(
998        &mut self,
999        old_id: CommitId,
1000        new_ids: impl IntoIterator<Item = CommitId>,
1001    ) {
1002        assert_ne!(old_id, *self.store().root_commit_id());
1003        self.parent_mapping.insert(
1004            old_id.clone(),
1005            Rewrite::Divergent(new_ids.into_iter().collect()),
1006        );
1007    }
1008
1009    /// Record a commit as having been abandoned in this transaction.
1010    ///
1011    /// This record is used by `rebase_descendants` to know which commits have
1012    /// children that need to be rebased, and where to rebase the children to.
1013    ///
1014    /// The `rebase_descendants` logic will rebase the descendants of the old
1015    /// commit to become the descendants of parent(s) of the old commit. Any
1016    /// bookmarks at the old commit will be either moved to the parent(s) of the
1017    /// old commit or deleted depending on [`RewriteRefsOptions`].
1018    pub fn record_abandoned_commit(&mut self, old_commit: &Commit) {
1019        assert_ne!(old_commit.id(), self.store().root_commit_id());
1020        // Descendants should be rebased onto the commit's parents
1021        self.record_abandoned_commit_with_parents(
1022            old_commit.id().clone(),
1023            old_commit.parent_ids().iter().cloned(),
1024        );
1025    }
1026
1027    /// Record a commit as having been abandoned in this transaction.
1028    ///
1029    /// A later `rebase_descendants()` will rebase children of `old_id` onto
1030    /// `new_parent_ids`. A working copy pointing to `old_id` will point to a
1031    /// new commit on top of `new_parent_ids`.
1032    pub fn record_abandoned_commit_with_parents(
1033        &mut self,
1034        old_id: CommitId,
1035        new_parent_ids: impl IntoIterator<Item = CommitId>,
1036    ) {
1037        assert_ne!(old_id, *self.store().root_commit_id());
1038        self.parent_mapping.insert(
1039            old_id,
1040            Rewrite::Abandoned(new_parent_ids.into_iter().collect()),
1041        );
1042    }
1043
1044    pub fn has_rewrites(&self) -> bool {
1045        !self.parent_mapping.is_empty()
1046    }
1047
1048    /// Calculates new parents for a commit that's currently based on the given
1049    /// parents. It does that by considering how previous commits have been
1050    /// rewritten and abandoned.
1051    ///
1052    /// If `parent_mapping` contains cycles, this function may either panic or
1053    /// drop parents that caused cycles.
1054    pub fn new_parents(&self, old_ids: &[CommitId]) -> Vec<CommitId> {
1055        self.rewritten_ids_with(old_ids, |rewrite| !matches!(rewrite, Rewrite::Divergent(_)))
1056    }
1057
1058    fn rewritten_ids_with(
1059        &self,
1060        old_ids: &[CommitId],
1061        mut predicate: impl FnMut(&Rewrite) -> bool,
1062    ) -> Vec<CommitId> {
1063        assert!(!old_ids.is_empty());
1064        let mut new_ids = Vec::with_capacity(old_ids.len());
1065        let mut to_visit = old_ids.iter().rev().collect_vec();
1066        let mut visited = HashSet::new();
1067        while let Some(id) = to_visit.pop() {
1068            if !visited.insert(id) {
1069                continue;
1070            }
1071            match self.parent_mapping.get(id).filter(|&v| predicate(v)) {
1072                None => {
1073                    new_ids.push(id.clone());
1074                }
1075                Some(rewrite) => {
1076                    let replacements = rewrite.new_parent_ids();
1077                    assert!(
1078                        // Each commit must have a parent, so a parent can
1079                        // not just be mapped to nothing. This assertion
1080                        // could be removed if this function is used for
1081                        // mapping something other than a commit's parents.
1082                        !replacements.is_empty(),
1083                        "Found empty value for key {id:?} in the parent mapping",
1084                    );
1085                    to_visit.extend(replacements.iter().rev());
1086                }
1087            }
1088        }
1089        assert!(
1090            !new_ids.is_empty(),
1091            "new ids become empty because of cycle in the parent mapping"
1092        );
1093        debug_assert!(new_ids.iter().all_unique());
1094        new_ids
1095    }
1096
1097    /// Fully resolves transitive replacements in `parent_mapping`.
1098    ///
1099    /// Returns an error if `parent_mapping` contains cycles
1100    fn resolve_rewrite_mapping_with(
1101        &self,
1102        mut predicate: impl FnMut(&Rewrite) -> bool,
1103    ) -> BackendResult<HashMap<CommitId, Vec<CommitId>>> {
1104        let sorted_ids = dag_walk::topo_order_forward(
1105            self.parent_mapping.keys(),
1106            |&id| id,
1107            |&id| match self.parent_mapping.get(id).filter(|&v| predicate(v)) {
1108                None => &[],
1109                Some(rewrite) => rewrite.new_parent_ids(),
1110            },
1111            |id| {
1112                BackendError::Other(
1113                    format!("Cycle between rewritten commits involving commit {id}").into(),
1114                )
1115            },
1116        )?;
1117        let mut new_mapping: HashMap<CommitId, Vec<CommitId>> = HashMap::new();
1118        for old_id in sorted_ids {
1119            let Some(rewrite) = self.parent_mapping.get(old_id).filter(|&v| predicate(v)) else {
1120                continue;
1121            };
1122            let lookup = |id| new_mapping.get(id).map_or(slice::from_ref(id), |ids| ids);
1123            let new_ids = match rewrite.new_parent_ids() {
1124                [id] => lookup(id).to_vec(), // unique() not needed
1125                ids => ids.iter().flat_map(lookup).unique().cloned().collect(),
1126            };
1127            debug_assert_eq!(
1128                new_ids,
1129                self.rewritten_ids_with(slice::from_ref(old_id), &mut predicate)
1130            );
1131            new_mapping.insert(old_id.clone(), new_ids);
1132        }
1133        Ok(new_mapping)
1134    }
1135
1136    /// Updates bookmarks, working copies, and anonymous heads after rewriting
1137    /// and/or abandoning commits.
1138    pub async fn update_rewritten_references(
1139        &mut self,
1140        options: &RewriteRefsOptions,
1141    ) -> BackendResult<()> {
1142        self.update_all_references(options).await?;
1143        self.update_heads()
1144            .await
1145            .map_err(|err| err.into_backend_error())?;
1146        Ok(())
1147    }
1148
1149    async fn update_all_references(&mut self, options: &RewriteRefsOptions) -> BackendResult<()> {
1150        let rewrite_mapping = self.resolve_rewrite_mapping_with(|_| true)?;
1151        self.update_local_bookmarks(&rewrite_mapping, options)
1152            // TODO: indexing error shouldn't be a "BackendError"
1153            .map_err(|err| BackendError::Other(err.into()))?;
1154        self.update_wc_commits(&rewrite_mapping).await?;
1155        Ok(())
1156    }
1157
1158    fn update_local_bookmarks(
1159        &mut self,
1160        rewrite_mapping: &HashMap<CommitId, Vec<CommitId>>,
1161        options: &RewriteRefsOptions,
1162    ) -> IndexResult<()> {
1163        let changed_branches = self
1164            .view()
1165            .local_bookmarks()
1166            .flat_map(|(name, target)| {
1167                target.added_ids().filter_map(|id| {
1168                    let change = rewrite_mapping.get_key_value(id)?;
1169                    Some((name.to_owned(), change))
1170                })
1171            })
1172            .collect_vec();
1173        for (bookmark_name, (old_commit_id, new_commit_ids)) in changed_branches {
1174            let should_delete = options.delete_abandoned_bookmarks
1175                && matches!(
1176                    self.parent_mapping.get(old_commit_id),
1177                    Some(Rewrite::Abandoned(_))
1178                );
1179            let old_target = RefTarget::normal(old_commit_id.clone());
1180            let new_target = if should_delete {
1181                RefTarget::absent()
1182            } else {
1183                let ids = itertools::intersperse(new_commit_ids, old_commit_id)
1184                    .map(|id| Some(id.clone()));
1185                RefTarget::from_merge(MergeBuilder::from_iter(ids).build())
1186            };
1187
1188            self.merge_local_bookmark(&bookmark_name, &old_target, &new_target)?;
1189        }
1190        Ok(())
1191    }
1192
1193    async fn update_wc_commits(
1194        &mut self,
1195        rewrite_mapping: &HashMap<CommitId, Vec<CommitId>>,
1196    ) -> BackendResult<()> {
1197        let changed_wc_commits = self
1198            .view()
1199            .wc_commit_ids()
1200            .iter()
1201            .filter_map(|(name, commit_id)| {
1202                let change = rewrite_mapping.get_key_value(commit_id)?;
1203                Some((name.to_owned(), change))
1204            })
1205            .collect_vec();
1206        let mut recreated_wc_commits: HashMap<&CommitId, Commit> = HashMap::new();
1207        for (name, (old_commit_id, new_commit_ids)) in changed_wc_commits {
1208            let abandoned_old_commit = matches!(
1209                self.parent_mapping.get(old_commit_id),
1210                Some(Rewrite::Abandoned(_))
1211            );
1212            let new_wc_commit = if !abandoned_old_commit {
1213                // We arbitrarily pick a new working-copy commit among the candidates.
1214                self.store().get_commit_async(&new_commit_ids[0]).await?
1215            } else if let Some(commit) = recreated_wc_commits.get(old_commit_id) {
1216                commit.clone()
1217            } else {
1218                let new_commit_futures = new_commit_ids
1219                    .iter()
1220                    .map(async |id| self.store().get_commit_async(id).await);
1221                let new_commits = try_join_all(new_commit_futures).await?;
1222                let merged_parents_tree = merge_commit_trees(self, &new_commits).await?;
1223                let commit = self
1224                    .new_commit(new_commit_ids.clone(), merged_parents_tree)
1225                    .write()
1226                    .await?;
1227                recreated_wc_commits.insert(old_commit_id, commit.clone());
1228                commit
1229            };
1230            self.edit(name, &new_wc_commit)
1231                .await
1232                .map_err(|err| match err {
1233                    EditCommitError::BackendError(backend_error) => backend_error,
1234                    EditCommitError::WorkingCopyCommitNotFound(_)
1235                    | EditCommitError::RewriteRootCommit(_) => panic!("unexpected error: {err:?}"),
1236                })?;
1237        }
1238        Ok(())
1239    }
1240
1241    async fn update_heads(&mut self) -> Result<(), RevsetEvaluationError> {
1242        let old_commits_expression =
1243            RevsetExpression::commits(self.parent_mapping.keys().cloned().collect())
1244                .intersection(&RevsetExpression::visible_heads().ancestors());
1245        let heads_to_add_expression = old_commits_expression
1246            .parents()
1247            .minus(&old_commits_expression);
1248        let heads_to_add: Vec<_> = heads_to_add_expression
1249            .evaluate(self)?
1250            .stream()
1251            .try_collect()
1252            .await?;
1253
1254        let mut view = self.view().store_view().clone();
1255        for commit_id in self.parent_mapping.keys() {
1256            view.head_ids.remove(commit_id);
1257        }
1258        view.head_ids.extend(heads_to_add);
1259        self.set_view(view);
1260        Ok(())
1261    }
1262
1263    /// Find descendants of `root`, unless they've already been rewritten
1264    /// (according to `parent_mapping`) or are included in `immutable`.
1265    pub async fn find_descendants_for_rebase(
1266        &self,
1267        roots: Vec<CommitId>,
1268        immutable: &Arc<ResolvedRevsetExpression>,
1269    ) -> BackendResult<Vec<Commit>> {
1270        let to_visit_revset = RevsetExpression::commits(roots)
1271            .descendants()
1272            .minus(immutable)
1273            .minus(&RevsetExpression::commits(
1274                self.parent_mapping.keys().cloned().collect(),
1275            ))
1276            .evaluate(self)
1277            .map_err(|err| err.into_backend_error())?;
1278        let to_visit = to_visit_revset
1279            .stream()
1280            .commits(self.store())
1281            .try_collect()
1282            .await
1283            .map_err(|err| err.into_backend_error())?;
1284        Ok(to_visit)
1285    }
1286
1287    /// Order a set of commits in an order they should be rebased in. The result
1288    /// is in reverse order so the next value can be removed from the end.
1289    async fn order_commits_for_rebase(
1290        &self,
1291        to_visit: Vec<Commit>,
1292        new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1293    ) -> BackendResult<Vec<Commit>> {
1294        let to_visit_set: HashSet<CommitId> =
1295            to_visit.iter().map(|commit| commit.id().clone()).collect();
1296        let mut visited = HashSet::new();
1297        // Calculate an order where we rebase parents first, but if the parents were
1298        // rewritten, make sure we rebase the rewritten parent first.
1299        let store = self.store();
1300        dag_walk_async::topo_order_reverse(
1301            to_visit.into_iter().map(Ok),
1302            |commit| commit.id().clone(),
1303            async |commit| -> Vec<BackendResult<Commit>> {
1304                visited.insert(commit.id().clone());
1305                let mut dependents = vec![];
1306                let parent_ids = new_parents_map
1307                    .get(commit.id())
1308                    .map_or(commit.parent_ids(), |parent_ids| parent_ids);
1309                for parent_id in parent_ids {
1310                    let parent = store.get_commit_async(parent_id).await;
1311                    let Ok(parent) = parent else {
1312                        dependents.push(parent);
1313                        continue;
1314                    };
1315                    if let Some(rewrite) = self.parent_mapping.get(parent.id()) {
1316                        for target in rewrite.new_parent_ids() {
1317                            if to_visit_set.contains(target) && !visited.contains(target) {
1318                                dependents.push(store.get_commit_async(target).await);
1319                            }
1320                        }
1321                    }
1322                    if to_visit_set.contains(parent.id()) {
1323                        dependents.push(Ok(parent));
1324                    }
1325                }
1326                dependents
1327            },
1328            |_| panic!("graph has cycle"),
1329        )
1330        .await
1331    }
1332
1333    /// Rewrite descendants of the given roots.
1334    ///
1335    /// The callback will be called for each commit with the new parents
1336    /// prepopulated. The callback may change the parents and write the new
1337    /// commit, or it may abandon the commit, or it may leave the old commit
1338    /// unchanged.
1339    ///
1340    /// The set of commits to visit is determined at the start. If the callback
1341    /// adds new descendants, then the callback will not be called for those.
1342    /// Similarly, if the callback rewrites unrelated commits, then the callback
1343    /// will not be called for descendants of those commits.
1344    pub async fn transform_descendants(
1345        &mut self,
1346        roots: Vec<CommitId>,
1347        callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1348    ) -> BackendResult<()> {
1349        self.transform_descendants_with_options(
1350            roots,
1351            &RevsetExpression::none(),
1352            &HashMap::new(),
1353            &RewriteRefsOptions::default(),
1354            callback,
1355        )
1356        .await
1357    }
1358
1359    /// Rewrite descendants of the given roots with options.
1360    ///
1361    /// Commits within the `immutable` set are excluded.
1362    ///
1363    /// If a commit is in the `new_parents_map` is provided, it will be rebased
1364    /// onto the new parents provided in the map instead of its original
1365    /// parents.
1366    ///
1367    /// See [`Self::transform_descendants()`] for details.
1368    pub async fn transform_descendants_with_options(
1369        &mut self,
1370        roots: Vec<CommitId>,
1371        immutable: &Arc<ResolvedRevsetExpression>,
1372        new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1373        options: &RewriteRefsOptions,
1374        callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1375    ) -> BackendResult<()> {
1376        let descendants = self.find_descendants_for_rebase(roots, immutable).await?;
1377        self.transform_commits(descendants, new_parents_map, options, callback)
1378            .await
1379    }
1380
1381    /// Rewrite the given commits in reverse topological order.
1382    ///
1383    /// This function is similar to
1384    /// [`Self::transform_descendants_with_options()`], but only rewrites the
1385    /// `commits` provided, and does not rewrite their descendants.
1386    pub async fn transform_commits(
1387        &mut self,
1388        commits: Vec<Commit>,
1389        new_parents_map: &HashMap<CommitId, Vec<CommitId>>,
1390        options: &RewriteRefsOptions,
1391        mut callback: impl AsyncFnMut(CommitRewriter) -> BackendResult<()>,
1392    ) -> BackendResult<()> {
1393        let mut to_visit = self
1394            .order_commits_for_rebase(commits, new_parents_map)
1395            .await?;
1396        while let Some(old_commit) = to_visit.pop() {
1397            let parent_ids = new_parents_map
1398                .get(old_commit.id())
1399                .map_or(old_commit.parent_ids(), |parent_ids| parent_ids);
1400            let new_parent_ids = self.new_parents(parent_ids);
1401            let rewriter = CommitRewriter::new(self, old_commit, new_parent_ids);
1402            callback(rewriter).await?;
1403        }
1404        self.update_rewritten_references(options).await?;
1405        // Since we didn't necessarily visit all descendants of rewritten commits (e.g.
1406        // if they were rewritten in the callback), there can still be commits left to
1407        // rebase, so we don't clear `parent_mapping` here.
1408        // TODO: Should we make this stricter? We could check that there were no
1409        // rewrites before this function was called, and we can check that only
1410        // commits in the `to_visit` set were added by the callback. Then we
1411        // could clear `parent_mapping` here and not have to scan it again at
1412        // the end of the transaction when we call `rebase_descendants()`.
1413
1414        Ok(())
1415    }
1416
1417    /// Rebase descendants of the rewritten commits with options and callback.
1418    ///
1419    /// The descendants of the commits registered in `self.parent_mappings` will
1420    /// be recursively rebased onto the new version of their parents. Commits
1421    /// within the `immutable` set are left unchanged, which also prevents their
1422    /// further descendants from being rebased.
1423    ///
1424    /// If `options.empty` is the default (`EmptyBehavior::Keep`), all rebased
1425    /// descendant commits will be preserved even if they were emptied following
1426    /// the rebase operation. Otherwise, this function may rebase some commits
1427    /// and abandon others, based on the given `EmptyBehavior`. The behavior is
1428    /// such that only commits with a single parent will ever be abandoned. The
1429    /// parent will inherit the descendants and the bookmarks of the abandoned
1430    /// commit.
1431    ///
1432    /// The `progress` callback will be invoked for each rebase operation with
1433    /// `(old_commit, rebased_commit)` as arguments.
1434    pub async fn rebase_descendants_with_options(
1435        &mut self,
1436        immutable: &Arc<ResolvedRevsetExpression>,
1437        options: &RebaseOptions,
1438        mut progress: impl FnMut(Commit, RebasedCommit),
1439    ) -> BackendResult<()> {
1440        let roots = self.parent_mapping.keys().cloned().collect();
1441        self.transform_descendants_with_options(
1442            roots,
1443            immutable,
1444            &HashMap::new(),
1445            &options.rewrite_refs,
1446            async |rewriter| {
1447                if rewriter.parents_changed() {
1448                    let old_commit = rewriter.old_commit().clone();
1449                    let rebased_commit = rebase_commit_with_options(rewriter, options).await?;
1450                    progress(old_commit, rebased_commit);
1451                }
1452                Ok(())
1453            },
1454        )
1455        .await?;
1456        self.parent_mapping.clear();
1457        Ok(())
1458    }
1459
1460    /// Rebase descendants of the rewritten commits.
1461    ///
1462    /// The descendants of the commits registered in `self.parent_mappings` will
1463    /// be recursively rebased onto the new version of their parents.
1464    /// Returns the number of rebased descendants.
1465    ///
1466    /// All rebased descendant commits will be preserved even if they were
1467    /// emptied following the rebase operation. To customize the rebase
1468    /// behavior, use [`MutableRepo::rebase_descendants_with_options`].
1469    pub async fn rebase_descendants(&mut self) -> BackendResult<usize> {
1470        let mut num_rebased = 0;
1471        self.rebase_descendants_with_options(
1472            &RevsetExpression::none(),
1473            &RebaseOptions::default(),
1474            |_old_commit, _rebased_commit| {
1475                num_rebased += 1;
1476            },
1477        )
1478        .await?;
1479        Ok(num_rebased)
1480    }
1481
1482    /// Reparent descendants of the rewritten commits.
1483    ///
1484    /// The descendants of the commits registered in `self.parent_mappings` will
1485    /// be recursively reparented onto the new version of their parents.
1486    /// The content of those descendants will remain untouched.
1487    /// Returns the number of reparented descendants.
1488    pub async fn reparent_descendants(&mut self) -> BackendResult<usize> {
1489        let roots = self.parent_mapping.keys().cloned().collect_vec();
1490        let mut num_reparented = 0;
1491        self.transform_descendants(roots, async |rewriter| {
1492            if rewriter.parents_changed() {
1493                let builder = rewriter.reparent();
1494                builder.write().await?;
1495                num_reparented += 1;
1496            }
1497            Ok(())
1498        })
1499        .await?;
1500        self.parent_mapping.clear();
1501        Ok(num_reparented)
1502    }
1503
1504    pub fn set_wc_commit(
1505        &mut self,
1506        name: WorkspaceNameBuf,
1507        commit_id: CommitId,
1508    ) -> Result<(), RewriteRootCommit> {
1509        if &commit_id == self.store().root_commit_id() {
1510            return Err(RewriteRootCommit);
1511        }
1512        self.view_mut().set_wc_commit(name, commit_id);
1513        Ok(())
1514    }
1515
1516    pub async fn remove_wc_commit(&mut self, name: &WorkspaceName) -> Result<(), EditCommitError> {
1517        self.maybe_abandon_wc_commit(name).await?;
1518        self.view_mut().remove_wc_commit(name);
1519        Ok(())
1520    }
1521
1522    /// Merges working-copy commit. If there's a conflict, and if the workspace
1523    /// isn't removed at either side, we keep the self side.
1524    fn merge_wc_commit(
1525        &mut self,
1526        name: &WorkspaceName,
1527        base_id: Option<&CommitId>,
1528        other_id: Option<&CommitId>,
1529    ) {
1530        let view = self.view.get_mut();
1531        let self_id = view.get_wc_commit_id(name);
1532        // Not using merge_ref_targets(). Since the working-copy pointer moves
1533        // towards random direction, it doesn't make sense to resolve conflict
1534        // based on ancestry.
1535        let new_id = if let Some(resolved) =
1536            trivial_merge(&[self_id, base_id, other_id], SameChange::Accept)
1537        {
1538            resolved.cloned()
1539        } else if self_id.is_none() || other_id.is_none() {
1540            // We want to remove the workspace even if the self side changed the
1541            // working-copy commit.
1542            None
1543        } else {
1544            self_id.cloned()
1545        };
1546        match new_id {
1547            Some(id) => view.set_wc_commit(name.to_owned(), id),
1548            None => view.remove_wc_commit(name),
1549        }
1550    }
1551
1552    pub fn rename_workspace(
1553        &mut self,
1554        old_name: &WorkspaceName,
1555        new_name: WorkspaceNameBuf,
1556    ) -> Result<(), RenameWorkspaceError> {
1557        self.view_mut().rename_workspace(old_name, new_name)
1558    }
1559
1560    pub async fn check_out(
1561        &mut self,
1562        name: WorkspaceNameBuf,
1563        commit: &Commit,
1564    ) -> Result<Commit, CheckOutCommitError> {
1565        let wc_commit = self
1566            .new_commit(vec![commit.id().clone()], commit.tree())
1567            .write()
1568            .await?;
1569        self.edit(name, &wc_commit).await?;
1570        Ok(wc_commit)
1571    }
1572
1573    pub async fn edit(
1574        &mut self,
1575        name: WorkspaceNameBuf,
1576        commit: &Commit,
1577    ) -> Result<(), EditCommitError> {
1578        self.maybe_abandon_wc_commit(&name).await?;
1579        self.add_head(commit).await?;
1580        Ok(self.set_wc_commit(name, commit.id().clone())?)
1581    }
1582
1583    async fn maybe_abandon_wc_commit(
1584        &mut self,
1585        workspace_name: &WorkspaceName,
1586    ) -> Result<(), EditCommitError> {
1587        let is_commit_referenced = |view: &View, commit_id: &CommitId| -> bool {
1588            itertools::chain!(
1589                view.wc_commit_ids()
1590                    .iter()
1591                    .filter(|&(name, _)| name != workspace_name)
1592                    .map(|(_, wc_id)| wc_id),
1593                view.local_bookmarks()
1594                    .flat_map(|(_, target)| target.added_ids()),
1595                view.local_tags().flat_map(|(_, target)| target.added_ids()),
1596            )
1597            .any(|id| id == commit_id)
1598        };
1599
1600        let maybe_wc_commit_id = self
1601            .view
1602            .with_ref(|v| v.get_wc_commit_id(workspace_name).cloned());
1603        if let Some(wc_commit_id) = maybe_wc_commit_id {
1604            let wc_commit = self
1605                .store()
1606                .get_commit_async(&wc_commit_id)
1607                .await
1608                .map_err(EditCommitError::WorkingCopyCommitNotFound)?;
1609            if wc_commit.is_discardable(self).await?
1610                && self
1611                    .view
1612                    .with_ref(|v| !is_commit_referenced(v, wc_commit.id()))
1613                && self.view().heads().contains(wc_commit.id())
1614            {
1615                // Abandon the working-copy commit we're leaving if it's
1616                // discardable, not pointed by local bookmark, tag, or other
1617                // working copies, and is a head commit.
1618                self.record_abandoned_commit(&wc_commit);
1619            }
1620        }
1621
1622        Ok(())
1623    }
1624
1625    fn enforce_view_invariants(&self, view: &mut View) {
1626        let view = view.store_view_mut();
1627        let root_commit_id = self.store().root_commit_id();
1628        if view.head_ids.is_empty() {
1629            view.head_ids.insert(root_commit_id.clone());
1630        } else if view.head_ids.len() > 1 {
1631            // An empty head_ids set is padded with the root_commit_id, but the
1632            // root id is unwanted during the heads resolution.
1633            view.head_ids.remove(root_commit_id);
1634            // It is unclear if `heads` can never fail for default implementation,
1635            // but it can definitely fail for non-default implementations.
1636            // TODO: propagate errors.
1637            view.head_ids = self
1638                .index()
1639                .heads(&mut view.head_ids.iter())
1640                .unwrap()
1641                .into_iter()
1642                .collect();
1643        }
1644        assert!(!view.head_ids.is_empty());
1645    }
1646
1647    /// Ensures that the given `head` and ancestor commits are reachable from
1648    /// the visible heads.
1649    pub async fn add_head(&mut self, head: &Commit) -> BackendResult<()> {
1650        self.add_heads(slice::from_ref(head)).await
1651    }
1652
1653    /// Ensures that the given `heads` and ancestor commits are reachable from
1654    /// the visible heads.
1655    ///
1656    /// The `heads` may contain redundant commits such as already visible ones
1657    /// and ancestors of the other heads. The `heads` and ancestor commits
1658    /// should exist in the store.
1659    pub async fn add_heads(&mut self, heads: &[Commit]) -> BackendResult<()> {
1660        let current_heads = self.view.get_mut().heads();
1661        // Use incremental update for common case of adding a single commit on top a
1662        // current head. TODO: Also use incremental update when adding a single
1663        // commit on top a non-head.
1664        match heads {
1665            [] => {}
1666            [head]
1667                if head
1668                    .parent_ids()
1669                    .iter()
1670                    .all(|parent_id| current_heads.contains(parent_id)) =>
1671            {
1672                self.index
1673                    .add_commit(head)
1674                    .await
1675                    // TODO: indexing error shouldn't be a "BackendError"
1676                    .map_err(|err| BackendError::Other(err.into()))?;
1677                self.view.get_mut().add_head(head.id());
1678                for parent_id in head.parent_ids() {
1679                    self.view.get_mut().remove_head(parent_id);
1680                }
1681            }
1682            _ => {
1683                self.index_commits(heads).await?;
1684                for head in heads {
1685                    self.view.get_mut().add_head(head.id());
1686                }
1687                self.view.mark_dirty();
1688            }
1689        }
1690        Ok(())
1691    }
1692
1693    pub fn remove_head(&mut self, head: &CommitId) {
1694        self.view_mut().remove_head(head);
1695        self.view.mark_dirty();
1696    }
1697
1698    /// Adds the given `heads` and ancestor commits to the index without making
1699    /// them visible. Returns newly-indexed commits.
1700    pub async fn index_commits(&mut self, heads: &[Commit]) -> BackendResult<Vec<Commit>> {
1701        let missing_commits = dag_walk_async::topo_order_reverse_ord(
1702            heads
1703                .iter()
1704                .filter_map(|commit| match self.index().has_id(commit.id()) {
1705                    Ok(false) => Some(Ok(CommitByCommitterTimestamp(commit.clone()))),
1706                    Ok(true) => None,
1707                    // TODO: indexing error shouldn't be a "BackendError"
1708                    Err(err) => Some(Err(BackendError::Other(err.into()))),
1709                }),
1710            |CommitByCommitterTimestamp(commit)| commit.id().clone(),
1711            async |CommitByCommitterTimestamp(commit)| {
1712                stream::iter(commit.parent_ids())
1713                    .filter_map(async |id| match self.index().has_id(id) {
1714                        Ok(false) => Some(
1715                            self.store()
1716                                .get_commit_async(id)
1717                                .await
1718                                .map(CommitByCommitterTimestamp),
1719                        ),
1720                        Ok(true) => None,
1721                        // TODO: indexing error shouldn't be a "BackendError"
1722                        Err(err) => Some(Err(BackendError::Other(err.into()))),
1723                    })
1724                    .collect::<Vec<_>>()
1725                    .await
1726            },
1727            |_| panic!("graph has cycle"),
1728        )
1729        .await?;
1730        for CommitByCommitterTimestamp(missing_commit) in missing_commits.iter().rev() {
1731            self.index
1732                .add_commit(missing_commit)
1733                .await
1734                // TODO: indexing error shouldn't be a "BackendError"
1735                .map_err(|err| BackendError::Other(err.into()))?;
1736        }
1737        let indexed_commits = missing_commits
1738            .into_iter()
1739            .map(|CommitByCommitterTimestamp(commit)| commit)
1740            .collect();
1741        Ok(indexed_commits)
1742    }
1743
1744    pub fn get_local_bookmark(&self, name: &RefName) -> RefTarget {
1745        self.view.with_ref(|v| v.get_local_bookmark(name).clone())
1746    }
1747
1748    pub fn set_local_bookmark_target(&mut self, name: &RefName, target: RefTarget) {
1749        let view = self.view_mut();
1750        for id in target.added_ids() {
1751            view.add_head(id);
1752        }
1753        view.set_local_bookmark_target(name, target);
1754        self.view.mark_dirty();
1755    }
1756
1757    pub fn merge_local_bookmark(
1758        &mut self,
1759        name: &RefName,
1760        base_target: &RefTarget,
1761        other_target: &RefTarget,
1762    ) -> IndexResult<()> {
1763        let view = self.view.get_mut();
1764        let index = self.index.as_index();
1765        let self_target = view.get_local_bookmark(name);
1766        let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1767        self.set_local_bookmark_target(name, new_target);
1768        Ok(())
1769    }
1770
1771    pub fn get_remote_bookmark(&self, symbol: RemoteRefSymbol<'_>) -> RemoteRef {
1772        self.view
1773            .with_ref(|v| v.get_remote_bookmark(symbol).clone())
1774    }
1775
1776    pub fn set_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {
1777        self.view_mut().set_remote_bookmark(symbol, remote_ref);
1778    }
1779
1780    fn merge_remote_bookmark(
1781        &mut self,
1782        symbol: RemoteRefSymbol<'_>,
1783        base_ref: &RemoteRef,
1784        other_ref: &RemoteRef,
1785    ) -> IndexResult<()> {
1786        let view = self.view.get_mut();
1787        let index = self.index.as_index();
1788        let self_ref = view.get_remote_bookmark(symbol);
1789        let new_ref = merge_remote_refs(index, self_ref, base_ref, other_ref)?;
1790        view.set_remote_bookmark(symbol, new_ref);
1791        Ok(())
1792    }
1793
1794    /// Merges the specified remote bookmark in to local bookmark, and starts
1795    /// tracking it.
1796    pub fn track_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>) -> IndexResult<()> {
1797        let mut remote_ref = self.get_remote_bookmark(symbol);
1798        let base_target = remote_ref.tracked_target();
1799        self.merge_local_bookmark(symbol.name, base_target, &remote_ref.target)?;
1800        remote_ref.state = RemoteRefState::Tracked;
1801        self.set_remote_bookmark(symbol, remote_ref);
1802        Ok(())
1803    }
1804
1805    /// Stops tracking the specified remote bookmark.
1806    pub fn untrack_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>) {
1807        let mut remote_ref = self.get_remote_bookmark(symbol);
1808        remote_ref.state = RemoteRefState::New;
1809        self.set_remote_bookmark(symbol, remote_ref);
1810    }
1811
1812    pub fn ensure_remote(&mut self, remote_name: &RemoteName) {
1813        self.view_mut().ensure_remote(remote_name);
1814    }
1815
1816    pub fn remove_remote(&mut self, remote_name: &RemoteName) {
1817        self.view_mut().remove_remote(remote_name);
1818    }
1819
1820    pub fn rename_remote(&mut self, old: &RemoteName, new: &RemoteName) {
1821        self.view_mut().rename_remote(old, new);
1822    }
1823
1824    pub fn get_local_tag(&self, name: &RefName) -> RefTarget {
1825        self.view.with_ref(|v| v.get_local_tag(name).clone())
1826    }
1827
1828    pub fn set_local_tag_target(&mut self, name: &RefName, target: RefTarget) {
1829        self.view_mut().set_local_tag_target(name, target);
1830    }
1831
1832    pub fn merge_local_tag(
1833        &mut self,
1834        name: &RefName,
1835        base_target: &RefTarget,
1836        other_target: &RefTarget,
1837    ) -> IndexResult<()> {
1838        let view = self.view.get_mut();
1839        let index = self.index.as_index();
1840        let self_target = view.get_local_tag(name);
1841        let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1842        view.set_local_tag_target(name, new_target);
1843        Ok(())
1844    }
1845
1846    pub fn get_remote_tag(&self, symbol: RemoteRefSymbol<'_>) -> RemoteRef {
1847        self.view.with_ref(|v| v.get_remote_tag(symbol).clone())
1848    }
1849
1850    pub fn set_remote_tag(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {
1851        self.view_mut().set_remote_tag(symbol, remote_ref);
1852    }
1853
1854    fn merge_remote_tag(
1855        &mut self,
1856        symbol: RemoteRefSymbol<'_>,
1857        base_ref: &RemoteRef,
1858        other_ref: &RemoteRef,
1859    ) -> IndexResult<()> {
1860        let view = self.view.get_mut();
1861        let index = self.index.as_index();
1862        let self_ref = view.get_remote_tag(symbol);
1863        let new_ref = merge_remote_refs(index, self_ref, base_ref, other_ref)?;
1864        view.set_remote_tag(symbol, new_ref);
1865        Ok(())
1866    }
1867
1868    pub fn get_git_ref(&self, name: &GitRefName) -> RefTarget {
1869        self.view.with_ref(|v| v.get_git_ref(name).clone())
1870    }
1871
1872    pub fn set_git_ref_target(&mut self, name: &GitRefName, target: RefTarget) {
1873        self.view_mut().set_git_ref_target(name, target);
1874    }
1875
1876    fn merge_git_ref(
1877        &mut self,
1878        name: &GitRefName,
1879        base_target: &RefTarget,
1880        other_target: &RefTarget,
1881    ) -> IndexResult<()> {
1882        let view = self.view.get_mut();
1883        let index = self.index.as_index();
1884        let self_target = view.get_git_ref(name);
1885        let new_target = merge_ref_targets(index, self_target, base_target, other_target)?;
1886        view.set_git_ref_target(name, new_target);
1887        Ok(())
1888    }
1889
1890    pub fn git_head(&self) -> RefTarget {
1891        self.view.with_ref(|v| v.git_head().clone())
1892    }
1893
1894    pub fn set_git_head_target(&mut self, target: RefTarget) {
1895        self.view_mut().set_git_head_target(target);
1896    }
1897
1898    pub fn set_view(&mut self, data: op_store::View) {
1899        self.view_mut().set_view(data);
1900        self.view.mark_dirty();
1901    }
1902
1903    pub async fn merge(
1904        &mut self,
1905        base_repo: &ReadonlyRepo,
1906        other_repo: &ReadonlyRepo,
1907    ) -> Result<(), RepoLoaderError> {
1908        // First, merge the index, so we can take advantage of a valid index when
1909        // merging the view. Merging in base_repo's index isn't typically
1910        // necessary, but it can be if base_repo is ahead of either self or other_repo
1911        // (e.g. because we're undoing an operation that hasn't been published).
1912        self.index.merge_in(base_repo.readonly_index())?;
1913        self.index.merge_in(other_repo.readonly_index())?;
1914
1915        self.view.ensure_clean(|v| self.enforce_view_invariants(v));
1916        self.merge_view(&base_repo.view, &other_repo.view).await?;
1917        self.view.mark_dirty();
1918        Ok(())
1919    }
1920
1921    pub fn merge_index(&mut self, other_repo: &ReadonlyRepo) -> IndexResult<()> {
1922        self.index.merge_in(other_repo.readonly_index())
1923    }
1924
1925    async fn merge_view(&mut self, base: &View, other: &View) -> Result<(), RepoLoaderError> {
1926        let changed_wc_commits = diff_named_commit_ids(base.wc_commit_ids(), other.wc_commit_ids());
1927        for (name, (base_id, other_id)) in changed_wc_commits {
1928            self.merge_wc_commit(name, base_id, other_id);
1929        }
1930
1931        let base_heads = base.heads().iter().cloned().collect_vec();
1932        let own_heads = self.view().heads().iter().cloned().collect_vec();
1933        let other_heads = other.heads().iter().cloned().collect_vec();
1934
1935        // HACK: Don't walk long ranges of commits to find rewrites when using other
1936        // custom implementations. The only custom index implementation we're currently
1937        // aware of is Google's. That repo has too high commit rate for it to be
1938        // feasible to walk all added and removed commits.
1939        // TODO: Fix this somehow. Maybe a method on `Index` to find rewritten commits
1940        // given `base_heads`, `own_heads` and `other_heads`?
1941        if self.is_backed_by_default_index() {
1942            self.record_rewrites(&base_heads, &own_heads).await?;
1943            self.record_rewrites(&base_heads, &other_heads).await?;
1944            // No need to remove heads removed by `other` because we already
1945            // marked them abandoned or rewritten.
1946        } else {
1947            for removed_head in base.heads().difference(other.heads()) {
1948                self.view_mut().remove_head(removed_head);
1949            }
1950        }
1951        for added_head in other.heads().difference(base.heads()) {
1952            self.view_mut().add_head(added_head);
1953        }
1954
1955        let changed_local_bookmarks =
1956            diff_named_ref_targets(base.local_bookmarks(), other.local_bookmarks());
1957        for (name, (base_target, other_target)) in changed_local_bookmarks {
1958            self.merge_local_bookmark(name, base_target, other_target)?;
1959        }
1960
1961        let changed_local_tags = diff_named_ref_targets(base.local_tags(), other.local_tags());
1962        for (name, (base_target, other_target)) in changed_local_tags {
1963            self.merge_local_tag(name, base_target, other_target)?;
1964        }
1965
1966        let changed_git_refs = diff_named_ref_targets(base.git_refs(), other.git_refs());
1967        for (name, (base_target, other_target)) in changed_git_refs {
1968            self.merge_git_ref(name, base_target, other_target)?;
1969        }
1970
1971        let changed_remote_bookmarks =
1972            diff_named_remote_refs(base.all_remote_bookmarks(), other.all_remote_bookmarks());
1973        for (symbol, (base_ref, other_ref)) in changed_remote_bookmarks {
1974            self.merge_remote_bookmark(symbol, base_ref, other_ref)?;
1975        }
1976
1977        let changed_remote_tags =
1978            diff_named_remote_refs(base.all_remote_tags(), other.all_remote_tags());
1979        for (symbol, (base_ref, other_ref)) in changed_remote_tags {
1980            self.merge_remote_tag(symbol, base_ref, other_ref)?;
1981        }
1982
1983        let new_git_head_target = merge_ref_targets(
1984            self.index(),
1985            self.view().git_head(),
1986            base.git_head(),
1987            other.git_head(),
1988        )?;
1989        self.set_git_head_target(new_git_head_target);
1990
1991        Ok(())
1992    }
1993
1994    /// Finds and records commits that were rewritten or abandoned between
1995    /// `old_heads` and `new_heads`.
1996    async fn record_rewrites(
1997        &mut self,
1998        old_heads: &[CommitId],
1999        new_heads: &[CommitId],
2000    ) -> BackendResult<()> {
2001        let mut removed_changes: HashMap<ChangeId, Vec<CommitId>> = HashMap::new();
2002        {
2003            let mut stream = revset::walk_revs(self, old_heads, new_heads)
2004                .map_err(|err| err.into_backend_error())?
2005                .commit_change_ids();
2006            while let Some((commit_id, change_id)) = stream
2007                .try_next()
2008                .await
2009                .map_err(|err| err.into_backend_error())?
2010            {
2011                removed_changes
2012                    .entry(change_id)
2013                    .or_default()
2014                    .push(commit_id);
2015            }
2016        }
2017        if removed_changes.is_empty() {
2018            return Ok(());
2019        }
2020
2021        let mut rewritten_changes = HashSet::new();
2022        let mut rewritten_commits: HashMap<CommitId, Vec<CommitId>> = HashMap::new();
2023        {
2024            let mut stream = revset::walk_revs(self, new_heads, old_heads)
2025                .map_err(|err| err.into_backend_error())?
2026                .commit_change_ids();
2027            while let Some((commit_id, change_id)) = stream
2028                .try_next()
2029                .await
2030                .map_err(|err| err.into_backend_error())?
2031            {
2032                if let Some(old_commits) = removed_changes.get(&change_id) {
2033                    for old_commit in old_commits {
2034                        rewritten_commits
2035                            .entry(old_commit.clone())
2036                            .or_default()
2037                            .push(commit_id.clone());
2038                    }
2039                }
2040                rewritten_changes.insert(change_id);
2041            }
2042        }
2043        for (old_commit, new_commits) in rewritten_commits {
2044            if new_commits.len() == 1 {
2045                self.set_rewritten_commit(
2046                    old_commit.clone(),
2047                    new_commits.into_iter().next().unwrap(),
2048                );
2049            } else {
2050                self.set_divergent_rewrite(old_commit.clone(), new_commits);
2051            }
2052        }
2053
2054        for (change_id, removed_commit_ids) in &removed_changes {
2055            if !rewritten_changes.contains(change_id) {
2056                for id in removed_commit_ids {
2057                    let commit = self.store().get_commit_async(id).await?;
2058                    self.record_abandoned_commit(&commit);
2059                }
2060            }
2061        }
2062
2063        Ok(())
2064    }
2065}
2066
2067impl Repo for MutableRepo {
2068    fn base_repo(&self) -> &ReadonlyRepo {
2069        &self.base_repo
2070    }
2071
2072    fn store(&self) -> &Arc<Store> {
2073        self.base_repo.store()
2074    }
2075
2076    fn op_store(&self) -> &Arc<dyn OpStore> {
2077        self.base_repo.op_store()
2078    }
2079
2080    fn index(&self) -> &dyn Index {
2081        self.index.as_index()
2082    }
2083
2084    fn view(&self) -> &View {
2085        self.view
2086            .get_or_ensure_clean(|v| self.enforce_view_invariants(v))
2087    }
2088
2089    fn submodule_store(&self) -> &Arc<dyn SubmoduleStore> {
2090        self.base_repo.submodule_store()
2091    }
2092
2093    fn resolve_change_id_prefix(
2094        &self,
2095        prefix: &HexPrefix,
2096    ) -> IndexResult<PrefixResolution<ResolvedChangeTargets>> {
2097        let change_id_index = self.index.change_id_index(&mut self.view().heads().iter());
2098        change_id_index.resolve_prefix(prefix)
2099    }
2100
2101    fn shortest_unique_change_id_prefix_len(&self, target_id: &ChangeId) -> IndexResult<usize> {
2102        let change_id_index = self.index.change_id_index(&mut self.view().heads().iter());
2103        change_id_index.shortest_unique_prefix_len(target_id)
2104    }
2105}
2106
2107/// Error from attempts to check out the root commit for editing
2108#[derive(Debug, Error)]
2109#[error("Cannot rewrite the root commit")]
2110pub struct RewriteRootCommit;
2111
2112/// Error from attempts to edit a commit
2113#[derive(Debug, Error)]
2114pub enum EditCommitError {
2115    #[error("Current working-copy commit not found")]
2116    WorkingCopyCommitNotFound(#[source] BackendError),
2117    #[error(transparent)]
2118    RewriteRootCommit(#[from] RewriteRootCommit),
2119    #[error(transparent)]
2120    BackendError(#[from] BackendError),
2121}
2122
2123/// Error from attempts to check out a commit
2124#[derive(Debug, Error)]
2125pub enum CheckOutCommitError {
2126    #[error("Failed to create new working-copy commit")]
2127    CreateCommit(#[from] BackendError),
2128    #[error("Failed to edit commit")]
2129    EditCommit(#[from] EditCommitError),
2130}
2131
2132mod dirty_cell {
2133    use std::cell::OnceCell;
2134    use std::cell::RefCell;
2135
2136    /// Cell that lazily updates the value after `mark_dirty()`.
2137    ///
2138    /// A clean value can be immutably borrowed within the `self` lifetime.
2139    #[derive(Clone, Debug)]
2140    pub struct DirtyCell<T> {
2141        // Either clean or dirty value is set. The value is boxed to reduce stack space
2142        // and memcopy overhead.
2143        clean: OnceCell<Box<T>>,
2144        dirty: RefCell<Option<Box<T>>>,
2145    }
2146
2147    impl<T> DirtyCell<T> {
2148        pub fn with_clean(value: T) -> Self {
2149            Self {
2150                clean: OnceCell::from(Box::new(value)),
2151                dirty: RefCell::new(None),
2152            }
2153        }
2154
2155        pub fn get_or_ensure_clean(&self, f: impl FnOnce(&mut T)) -> &T {
2156            self.clean.get_or_init(|| {
2157                // Panics if ensure_clean() is invoked from with_ref() callback for example.
2158                let mut value = self.dirty.borrow_mut().take().unwrap();
2159                f(&mut value);
2160                value
2161            })
2162        }
2163
2164        pub fn ensure_clean(&self, f: impl FnOnce(&mut T)) {
2165            self.get_or_ensure_clean(f);
2166        }
2167
2168        pub fn into_inner(self) -> T {
2169            *self
2170                .clean
2171                .into_inner()
2172                .or_else(|| self.dirty.into_inner())
2173                .unwrap()
2174        }
2175
2176        pub fn with_ref<R>(&self, f: impl FnOnce(&T) -> R) -> R {
2177            if let Some(value) = self.clean.get() {
2178                f(value)
2179            } else {
2180                f(self.dirty.borrow().as_ref().unwrap())
2181            }
2182        }
2183
2184        pub fn get_mut(&mut self) -> &mut T {
2185            self.clean
2186                .get_mut()
2187                .or_else(|| self.dirty.get_mut().as_mut())
2188                .unwrap()
2189        }
2190
2191        pub fn mark_dirty(&mut self) {
2192            if let Some(value) = self.clean.take() {
2193                *self.dirty.get_mut() = Some(value);
2194            }
2195        }
2196    }
2197}