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