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