1#![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 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 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 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 .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] pub type OpStoreInitializer<'a> =
417 dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendInitError>
418 + 'a;
419#[rustfmt::skip] pub 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 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#[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 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 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 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 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 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 let mut merged_operations: HashMap<Vec<OperationId>, Operation> = HashMap::new();
901 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 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 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 Some(ancestor_op)
955 } else {
956 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 tx.merge_operation(merged_ancestor_op, other_op).await?;
966 num_rebased += tx.repo_mut().rebase_descendants().await?;
967 stack.push((index + 1, operations, tx));
969 continue;
970 }
971
972 stack.push((index, operations, tx));
976 let new_tx = self.load_at(&ancestor_ops[0]).await?.start_transaction();
979 stack.push((1, ancestor_ops.clone(), new_tx));
980 }
981
982 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 Rewritten(CommitId),
1012 Divergent(Vec<CommitId>),
1015 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 commit_predecessors: BTreeMap<CommitId, Vec<CommitId>>,
1039 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 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 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 }
1104
1105 pub(crate) fn set_predecessors(&mut self, id: CommitId, predecessors: Vec<CommitId>) {
1106 self.commit_predecessors.insert(id, predecessors);
1107 }
1108
1109 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 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 pub fn record_abandoned_commit(&mut self, old_commit: &Commit) {
1150 assert_ne!(old_commit.id(), self.store().root_commit_id());
1151 self.record_abandoned_commit_with_parents(
1153 old_commit.id().clone(),
1154 old_commit.parent_ids().iter().cloned(),
1155 );
1156 }
1157
1158 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 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 !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 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(), 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 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 .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 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 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 self.normalize_heads()
1408 .await
1409 .map_err(|err| RevsetEvaluationError::Other(Box::new(err)))?;
1410 Ok(())
1411 }
1412
1413 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 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 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 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 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 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 Ok(())
1565 }
1566
1567 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 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 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 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 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 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 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 self.record_abandoned_commit(&wc_commit);
1768 }
1769 }
1770
1771 Ok(())
1772 }
1773
1774 pub async fn add_head(&mut self, head: &Commit) -> BackendResult<()> {
1777 self.add_heads(slice::from_ref(head)).await
1778 }
1779
1780 pub async fn add_heads(&mut self, heads: &[Commit]) -> BackendResult<()> {
1787 let current_heads = self.view.heads();
1788 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 .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 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 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 .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 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 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 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 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 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 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 } 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 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#[derive(Debug, Error)]
2257#[error("Cannot rewrite the root commit")]
2258pub struct RewriteRootCommit;
2259
2260#[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#[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}