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