jj_lib/
rewrite.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#![allow(missing_docs)]
16
17use std::collections::HashMap;
18use std::collections::HashSet;
19use std::slice;
20use std::sync::Arc;
21
22use futures::StreamExt as _;
23use futures::future::try_join_all;
24use futures::try_join;
25use indexmap::IndexMap;
26use indexmap::IndexSet;
27use itertools::Itertools as _;
28use pollster::FutureExt as _;
29use tracing::instrument;
30
31use crate::backend::BackendError;
32use crate::backend::BackendResult;
33use crate::backend::CommitId;
34use crate::backend::MergedTreeId;
35use crate::commit::Commit;
36use crate::commit::CommitIteratorExt as _;
37use crate::commit_builder::CommitBuilder;
38use crate::index::Index;
39use crate::index::IndexError;
40use crate::matchers::Matcher;
41use crate::matchers::Visit;
42use crate::merge::Merge;
43use crate::merged_tree::MergedTree;
44use crate::merged_tree::MergedTreeBuilder;
45use crate::merged_tree::TreeDiffEntry;
46use crate::repo::MutableRepo;
47use crate::repo::Repo;
48use crate::repo_path::RepoPath;
49use crate::revset::RevsetExpression;
50use crate::revset::RevsetIteratorExt as _;
51use crate::store::Store;
52
53/// Merges `commits` and tries to resolve any conflicts recursively.
54#[instrument(skip(repo))]
55pub async fn merge_commit_trees(repo: &dyn Repo, commits: &[Commit]) -> BackendResult<MergedTree> {
56    if let [commit] = commits {
57        commit.tree_async().await
58    } else {
59        merge_commit_trees_no_resolve_without_repo(repo.store(), repo.index(), commits)
60            .await?
61            .resolve()
62            .await
63    }
64}
65
66/// Merges `commits` without attempting to resolve file conflicts.
67#[instrument(skip(index))]
68pub async fn merge_commit_trees_no_resolve_without_repo(
69    store: &Arc<Store>,
70    index: &dyn Index,
71    commits: &[Commit],
72) -> BackendResult<MergedTree> {
73    let commit_ids = commits
74        .iter()
75        .map(|commit| commit.id().clone())
76        .collect_vec();
77    let commit_id_merge = find_recursive_merge_commits(store, index, commit_ids)?;
78    let tree_merge = commit_id_merge
79        .try_map_async(async |commit_id| {
80            let commit = store.get_commit_async(commit_id).await?;
81            let tree = commit.tree_async().await?;
82            Ok::<_, BackendError>(tree.take())
83        })
84        .await?;
85    Ok(MergedTree::new(tree_merge.flatten().simplify()))
86}
87
88/// Find the commits to use as input to the recursive merge algorithm.
89pub fn find_recursive_merge_commits(
90    store: &Arc<Store>,
91    index: &dyn Index,
92    mut commit_ids: Vec<CommitId>,
93) -> BackendResult<Merge<CommitId>> {
94    if commit_ids.is_empty() {
95        Ok(Merge::resolved(store.root_commit_id().clone()))
96    } else if commit_ids.len() == 1 {
97        Ok(Merge::resolved(commit_ids.pop().unwrap()))
98    } else {
99        let mut result = Merge::resolved(commit_ids[0].clone());
100        for (i, other_commit_id) in commit_ids.iter().enumerate().skip(1) {
101            let ancestor_ids = index.common_ancestors(&commit_ids[0..i], &commit_ids[i..][..1]);
102            let ancestor_merge = find_recursive_merge_commits(store, index, ancestor_ids)?;
103            result = Merge::from_vec(vec![
104                result,
105                ancestor_merge,
106                Merge::resolved(other_commit_id.clone()),
107            ])
108            .flatten();
109        }
110        Ok(result)
111    }
112}
113
114/// Restore matching paths from the source into the destination.
115pub async fn restore_tree(
116    source: &MergedTree,
117    destination: &MergedTree,
118    matcher: &dyn Matcher,
119) -> BackendResult<MergedTreeId> {
120    if matcher.visit(RepoPath::root()) == Visit::AllRecursively {
121        // Optimization for a common case
122        Ok(source.id())
123    } else {
124        // TODO: We should be able to not traverse deeper in the diff if the matcher
125        // matches an entire subtree.
126        let mut tree_builder = MergedTreeBuilder::new(destination.id().clone());
127        // TODO: handle copy tracking
128        let mut diff_stream = source.diff_stream(destination, matcher);
129        while let Some(TreeDiffEntry {
130            path: repo_path,
131            values,
132        }) = diff_stream.next().await
133        {
134            let (source_value, _destination_value) = values?;
135            tree_builder.set_or_remove(repo_path, source_value);
136        }
137        tree_builder.write_tree(destination.store())
138    }
139}
140
141pub async fn rebase_commit(
142    mut_repo: &mut MutableRepo,
143    old_commit: Commit,
144    new_parents: Vec<CommitId>,
145) -> BackendResult<Commit> {
146    let rewriter = CommitRewriter::new(mut_repo, old_commit, new_parents);
147    let builder = rewriter.rebase().await?;
148    builder.write()
149}
150
151/// Helps rewrite a commit.
152pub struct CommitRewriter<'repo> {
153    mut_repo: &'repo mut MutableRepo,
154    old_commit: Commit,
155    new_parents: Vec<CommitId>,
156}
157
158impl<'repo> CommitRewriter<'repo> {
159    /// Create a new instance.
160    pub fn new(
161        mut_repo: &'repo mut MutableRepo,
162        old_commit: Commit,
163        new_parents: Vec<CommitId>,
164    ) -> Self {
165        Self {
166            mut_repo,
167            old_commit,
168            new_parents,
169        }
170    }
171
172    /// Returns the `MutableRepo`.
173    pub fn repo_mut(&mut self) -> &mut MutableRepo {
174        self.mut_repo
175    }
176
177    /// The commit we're rewriting.
178    pub fn old_commit(&self) -> &Commit {
179        &self.old_commit
180    }
181
182    /// Get the old commit's intended new parents.
183    pub fn new_parents(&self) -> &[CommitId] {
184        &self.new_parents
185    }
186
187    /// Set the old commit's intended new parents.
188    pub fn set_new_parents(&mut self, new_parents: Vec<CommitId>) {
189        self.new_parents = new_parents;
190    }
191
192    /// Set the old commit's intended new parents to be the rewritten versions
193    /// of the given parents.
194    pub fn set_new_rewritten_parents(&mut self, unrewritten_parents: &[CommitId]) {
195        self.new_parents = self.mut_repo.new_parents(unrewritten_parents);
196    }
197
198    /// Update the intended new parents by replacing any occurrence of
199    /// `old_parent` by `new_parents`.
200    pub fn replace_parent<'a>(
201        &mut self,
202        old_parent: &CommitId,
203        new_parents: impl IntoIterator<Item = &'a CommitId>,
204    ) {
205        if let Some(i) = self.new_parents.iter().position(|p| p == old_parent) {
206            self.new_parents
207                .splice(i..i + 1, new_parents.into_iter().cloned());
208            let mut unique = HashSet::new();
209            self.new_parents.retain(|p| unique.insert(p.clone()));
210        }
211    }
212
213    /// Checks if the intended new parents are different from the old commit's
214    /// parents.
215    pub fn parents_changed(&self) -> bool {
216        self.new_parents != self.old_commit.parent_ids()
217    }
218
219    /// If a merge commit would end up with one parent being an ancestor of the
220    /// other, then filter out the ancestor.
221    pub fn simplify_ancestor_merge(&mut self) -> Result<(), IndexError> {
222        let head_set: HashSet<_> = self
223            .mut_repo
224            .index()
225            .heads(&mut self.new_parents.iter())?
226            .into_iter()
227            .collect();
228        self.new_parents.retain(|parent| head_set.contains(parent));
229        Ok(())
230    }
231
232    /// Records the old commit as abandoned with the new parents.
233    ///
234    /// This is equivalent to `reparent(settings).abandon()`, but is cheaper.
235    pub fn abandon(self) {
236        let old_commit_id = self.old_commit.id().clone();
237        let new_parents = self.new_parents;
238        self.mut_repo
239            .record_abandoned_commit_with_parents(old_commit_id, new_parents);
240    }
241
242    /// Rebase the old commit onto the new parents. Returns a `CommitBuilder`
243    /// for the new commit. Returns `None` if the commit was abandoned.
244    pub async fn rebase_with_empty_behavior(
245        self,
246        empty: EmptyBehavior,
247    ) -> BackendResult<Option<CommitBuilder<'repo>>> {
248        let old_parents_fut = self.old_commit.parents_async();
249        let new_parents_fut = try_join_all(
250            self.new_parents
251                .iter()
252                .map(|new_parent_id| self.mut_repo.store().get_commit_async(new_parent_id)),
253        );
254        let (old_parents, new_parents) = try_join!(old_parents_fut, new_parents_fut)?;
255        let old_parent_trees = old_parents
256            .iter()
257            .map(|parent| parent.tree_id().clone())
258            .collect_vec();
259        let new_parent_trees = new_parents
260            .iter()
261            .map(|parent| parent.tree_id().clone())
262            .collect_vec();
263
264        let (was_empty, new_tree_id) = if new_parent_trees == old_parent_trees {
265            (
266                // Optimization: was_empty is only used for newly empty, but when the
267                // parents haven't changed it can't be newly empty.
268                true,
269                // Optimization: Skip merging.
270                self.old_commit.tree_id().clone(),
271            )
272        } else {
273            let old_base_tree_fut = merge_commit_trees(self.mut_repo, &old_parents);
274            let new_base_tree_fut = merge_commit_trees(self.mut_repo, &new_parents);
275            let old_tree_fut = self.old_commit.tree_async();
276            let (old_base_tree, new_base_tree, old_tree) =
277                try_join!(old_base_tree_fut, new_base_tree_fut, old_tree_fut)?;
278            (
279                old_base_tree.id() == *self.old_commit.tree_id(),
280                new_base_tree.merge(old_base_tree, old_tree).await?.id(),
281            )
282        };
283        // Ensure we don't abandon commits with multiple parents (merge commits), even
284        // if they're empty.
285        if let [parent] = &new_parents[..] {
286            let should_abandon = match empty {
287                EmptyBehavior::Keep => false,
288                EmptyBehavior::AbandonNewlyEmpty => *parent.tree_id() == new_tree_id && !was_empty,
289                EmptyBehavior::AbandonAllEmpty => *parent.tree_id() == new_tree_id,
290            };
291            if should_abandon {
292                self.abandon();
293                return Ok(None);
294            }
295        }
296
297        let builder = self
298            .mut_repo
299            .rewrite_commit(&self.old_commit)
300            .set_parents(self.new_parents)
301            .set_tree_id(new_tree_id);
302        Ok(Some(builder))
303    }
304
305    /// Rebase the old commit onto the new parents. Returns a `CommitBuilder`
306    /// for the new commit.
307    pub async fn rebase(self) -> BackendResult<CommitBuilder<'repo>> {
308        let builder = self.rebase_with_empty_behavior(EmptyBehavior::Keep).await?;
309        Ok(builder.unwrap())
310    }
311
312    /// Rewrite the old commit onto the new parents without changing its
313    /// contents. Returns a `CommitBuilder` for the new commit.
314    pub fn reparent(self) -> CommitBuilder<'repo> {
315        self.mut_repo
316            .rewrite_commit(&self.old_commit)
317            .set_parents(self.new_parents)
318    }
319}
320
321pub enum RebasedCommit {
322    Rewritten(Commit),
323    Abandoned { parent_id: CommitId },
324}
325
326pub fn rebase_commit_with_options(
327    mut rewriter: CommitRewriter<'_>,
328    options: &RebaseOptions,
329) -> BackendResult<RebasedCommit> {
330    // If specified, don't create commit where one parent is an ancestor of another.
331    if options.simplify_ancestor_merge {
332        // TODO: BackendError is not the right error here because
333        // the error does not come from `Backend`, but `Index`.
334        rewriter
335            .simplify_ancestor_merge()
336            .map_err(|err| BackendError::Other(err.into()))?;
337    }
338
339    let single_parent = match &rewriter.new_parents[..] {
340        [parent_id] => Some(parent_id.clone()),
341        _ => None,
342    };
343    let new_parents_len = rewriter.new_parents.len();
344    if let Some(builder) = rewriter
345        .rebase_with_empty_behavior(options.empty)
346        .block_on()?
347    {
348        let new_commit = builder.write()?;
349        Ok(RebasedCommit::Rewritten(new_commit))
350    } else {
351        assert_eq!(new_parents_len, 1);
352        Ok(RebasedCommit::Abandoned {
353            parent_id: single_parent.unwrap(),
354        })
355    }
356}
357
358/// Moves changes from `sources` to the `destination` parent, returns new tree.
359pub fn rebase_to_dest_parent(
360    repo: &dyn Repo,
361    sources: &[Commit],
362    destination: &Commit,
363) -> BackendResult<MergedTree> {
364    if let [source] = sources {
365        if source.parent_ids() == destination.parent_ids() {
366            return source.tree();
367        }
368    }
369    sources.iter().try_fold(
370        destination.parent_tree(repo)?,
371        |destination_tree, source| {
372            let source_parent_tree = source.parent_tree(repo)?;
373            let source_tree = source.tree()?;
374            destination_tree
375                .merge(source_parent_tree, source_tree)
376                .block_on()
377        },
378    )
379}
380
381#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
382pub enum EmptyBehavior {
383    /// Always keep empty commits
384    #[default]
385    Keep,
386    /// Skips commits that would be empty after the rebase, but that were not
387    /// originally empty.
388    /// Will never skip merge commits with multiple non-empty parents.
389    AbandonNewlyEmpty,
390    /// Skips all empty commits, including ones that were empty before the
391    /// rebase.
392    /// Will never skip merge commits with multiple non-empty parents.
393    AbandonAllEmpty,
394}
395
396/// Controls the configuration of a rebase.
397// If we wanted to add a flag similar to `git rebase --ignore-date`, then this
398// makes it much easier by ensuring that the only changes required are to
399// change the RebaseOptions construction in the CLI, and changing the
400// rebase_commit function to actually use the flag, and ensure we don't need to
401// plumb it in.
402#[derive(Clone, Debug, Default)]
403pub struct RebaseOptions {
404    pub empty: EmptyBehavior,
405    pub rewrite_refs: RewriteRefsOptions,
406    /// If a merge commit would end up with one parent being an ancestor of the
407    /// other, then filter out the ancestor.
408    pub simplify_ancestor_merge: bool,
409}
410
411/// Configuration for [`MutableRepo::update_rewritten_references()`].
412#[derive(Clone, Debug, Default)]
413pub struct RewriteRefsOptions {
414    /// Whether or not delete bookmarks pointing to the abandoned commits.
415    ///
416    /// If false, bookmarks will be moved to the parents of the abandoned
417    /// commit.
418    pub delete_abandoned_bookmarks: bool,
419}
420
421pub struct MoveCommitsStats {
422    /// The number of commits in the target set which were rebased.
423    pub num_rebased_targets: u32,
424    /// The number of descendant commits which were rebased.
425    pub num_rebased_descendants: u32,
426    /// The number of commits for which rebase was skipped, due to the commit
427    /// already being in place.
428    pub num_skipped_rebases: u32,
429    /// The number of commits which were abandoned due to being empty.
430    pub num_abandoned_empty: u32,
431    /// The rebased commits
432    pub rebased_commits: HashMap<CommitId, RebasedCommit>,
433}
434
435/// Target and destination commits to be rebased by [`move_commits()`].
436#[derive(Clone, Debug)]
437pub struct MoveCommitsLocation {
438    pub new_parent_ids: Vec<CommitId>,
439    pub new_child_ids: Vec<CommitId>,
440    pub target: MoveCommitsTarget,
441}
442
443#[derive(Clone, Debug)]
444pub enum MoveCommitsTarget {
445    /// The commits to be moved. Commits should be mutable and in reverse
446    /// topological order.
447    Commits(Vec<CommitId>),
448    /// The root commits to be moved, along with all their descendants.
449    Roots(Vec<CommitId>),
450}
451
452#[derive(Clone, Debug)]
453pub struct ComputedMoveCommits {
454    target_commit_ids: IndexSet<CommitId>,
455    descendants: Vec<Commit>,
456    commit_new_parents_map: HashMap<CommitId, Vec<CommitId>>,
457    to_abandon: HashSet<CommitId>,
458}
459
460impl ComputedMoveCommits {
461    fn empty() -> Self {
462        Self {
463            target_commit_ids: IndexSet::new(),
464            descendants: vec![],
465            commit_new_parents_map: HashMap::new(),
466            to_abandon: HashSet::new(),
467        }
468    }
469
470    /// Records a set of commits to abandon while rebasing.
471    ///
472    /// Abandoning these commits while rebasing ensures that their descendants
473    /// are still rebased properly. [`MutableRepo::record_abandoned_commit`] is
474    /// similar, but it can lead to issues when abandoning a target commit
475    /// before the rebase.
476    pub fn record_to_abandon(&mut self, commit_ids: impl IntoIterator<Item = CommitId>) {
477        self.to_abandon.extend(commit_ids);
478    }
479
480    pub fn apply(
481        self,
482        mut_repo: &mut MutableRepo,
483        options: &RebaseOptions,
484    ) -> BackendResult<MoveCommitsStats> {
485        apply_move_commits(mut_repo, self, options)
486    }
487}
488
489/// Moves `loc.target` commits from their current location to a new location in
490/// the graph.
491///
492/// Commits in `target` are rebased onto the new parents given by
493/// `new_parent_ids`, while the `new_child_ids` commits are rebased onto the
494/// heads of the commits in `targets`. This assumes that commits in `target` and
495/// `new_child_ids` can be rewritten, and there will be no cycles in the
496/// resulting graph. Commits in `target` should be in reverse topological order.
497pub fn move_commits(
498    mut_repo: &mut MutableRepo,
499    loc: &MoveCommitsLocation,
500    options: &RebaseOptions,
501) -> BackendResult<MoveCommitsStats> {
502    compute_move_commits(mut_repo, loc)?.apply(mut_repo, options)
503}
504
505pub fn compute_move_commits(
506    repo: &MutableRepo,
507    loc: &MoveCommitsLocation,
508) -> BackendResult<ComputedMoveCommits> {
509    let target_commit_ids: IndexSet<CommitId>;
510    let connected_target_commits: Vec<Commit>;
511    let connected_target_commits_internal_parents: HashMap<CommitId, IndexSet<CommitId>>;
512    let target_roots: HashSet<CommitId>;
513
514    match &loc.target {
515        MoveCommitsTarget::Commits(commit_ids) => {
516            if commit_ids.is_empty() {
517                return Ok(ComputedMoveCommits::empty());
518            }
519
520            target_commit_ids = commit_ids.iter().cloned().collect();
521
522            connected_target_commits = RevsetExpression::commits(commit_ids.clone())
523                .connected()
524                .evaluate(repo)
525                .map_err(|err| err.into_backend_error())?
526                .iter()
527                .commits(repo.store())
528                .try_collect()
529                .map_err(|err| err.into_backend_error())?;
530            connected_target_commits_internal_parents =
531                compute_internal_parents_within(&target_commit_ids, &connected_target_commits);
532
533            target_roots = connected_target_commits_internal_parents
534                .iter()
535                .filter(|&(commit_id, parents)| {
536                    target_commit_ids.contains(commit_id) && parents.is_empty()
537                })
538                .map(|(commit_id, _)| commit_id.clone())
539                .collect();
540        }
541        MoveCommitsTarget::Roots(root_ids) => {
542            if root_ids.is_empty() {
543                return Ok(ComputedMoveCommits::empty());
544            }
545
546            target_commit_ids = RevsetExpression::commits(root_ids.clone())
547                .descendants()
548                .evaluate(repo)
549                .map_err(|err| err.into_backend_error())?
550                .iter()
551                .try_collect()
552                .map_err(|err| err.into_backend_error())?;
553
554            connected_target_commits = target_commit_ids
555                .iter()
556                .map(|id| repo.store().get_commit(id))
557                .try_collect()?;
558            // We don't have to compute the internal parents for the connected target set,
559            // since the connected target set is the same as the target set.
560            connected_target_commits_internal_parents = HashMap::new();
561            target_roots = root_ids.iter().cloned().collect();
562        }
563    }
564
565    // If a commit outside the target set has a commit in the target set as a
566    // parent, then - after the transformation - it should have that commit's
567    // ancestors which are not in the target set as parents.
568    let mut target_commits_external_parents: HashMap<CommitId, IndexSet<CommitId>> = HashMap::new();
569    for id in target_commit_ids.iter().rev() {
570        let commit = repo.store().get_commit(id)?;
571        let mut new_parents = IndexSet::new();
572        for old_parent in commit.parent_ids() {
573            if let Some(parents) = target_commits_external_parents.get(old_parent) {
574                new_parents.extend(parents.iter().cloned());
575            } else {
576                new_parents.insert(old_parent.clone());
577            }
578        }
579        target_commits_external_parents.insert(commit.id().clone(), new_parents);
580    }
581
582    // If the new parents include a commit in the target set, replace it with the
583    // commit's ancestors which are outside the set.
584    // e.g. `jj rebase -r A --before A`
585    let new_parent_ids: Vec<_> = loc
586        .new_parent_ids
587        .iter()
588        .flat_map(|parent_id| {
589            if let Some(parent_ids) = target_commits_external_parents.get(parent_id) {
590                parent_ids.iter().cloned().collect_vec()
591            } else {
592                vec![parent_id.clone()]
593            }
594        })
595        .collect();
596
597    // If the new children include a commit in the target set, replace it with the
598    // commit's descendants which are outside the set.
599    // e.g. `jj rebase -r A --after A`
600    let new_children: Vec<_> = if loc
601        .new_child_ids
602        .iter()
603        .any(|id| target_commit_ids.contains(id))
604    {
605        let target_commits_descendants: Vec<_> =
606            RevsetExpression::commits(target_commit_ids.iter().cloned().collect_vec())
607                .union(
608                    &RevsetExpression::commits(target_commit_ids.iter().cloned().collect_vec())
609                        .children(),
610                )
611                .evaluate(repo)
612                .map_err(|err| err.into_backend_error())?
613                .iter()
614                .commits(repo.store())
615                .try_collect()
616                .map_err(|err| err.into_backend_error())?;
617
618        // For all commits in the target set, compute its transitive descendant commits
619        // which are outside of the target set by up to 1 generation.
620        let mut target_commit_external_descendants: HashMap<CommitId, IndexSet<Commit>> =
621            HashMap::new();
622        // Iterate through all descendants of the target set, going through children
623        // before parents.
624        for commit in &target_commits_descendants {
625            if !target_commit_external_descendants.contains_key(commit.id()) {
626                let children = if target_commit_ids.contains(commit.id()) {
627                    IndexSet::new()
628                } else {
629                    IndexSet::from([commit.clone()])
630                };
631                target_commit_external_descendants.insert(commit.id().clone(), children);
632            }
633
634            let children = target_commit_external_descendants
635                .get(commit.id())
636                .unwrap()
637                .iter()
638                .cloned()
639                .collect_vec();
640            for parent_id in commit.parent_ids() {
641                if target_commit_ids.contains(parent_id) {
642                    if let Some(target_children) =
643                        target_commit_external_descendants.get_mut(parent_id)
644                    {
645                        target_children.extend(children.iter().cloned());
646                    } else {
647                        target_commit_external_descendants
648                            .insert(parent_id.clone(), children.iter().cloned().collect());
649                    }
650                };
651            }
652        }
653
654        let mut new_children = Vec::new();
655        for id in &loc.new_child_ids {
656            if let Some(children) = target_commit_external_descendants.get(id) {
657                new_children.extend(children.iter().cloned());
658            } else {
659                new_children.push(repo.store().get_commit(id)?);
660            }
661        }
662        new_children
663    } else {
664        loc.new_child_ids
665            .iter()
666            .map(|id| repo.store().get_commit(id))
667            .try_collect()?
668    };
669
670    // Compute the parents of the new children, which will include the heads of the
671    // target set.
672    let new_children_parents: HashMap<_, _> = if !new_children.is_empty() {
673        // Compute the heads of the target set, which will be used as the parents of
674        // `new_children`.
675        let target_heads = compute_commits_heads(&target_commit_ids, &connected_target_commits);
676
677        new_children
678            .iter()
679            .map(|child_commit| {
680                let mut new_child_parent_ids = IndexSet::new();
681                for old_child_parent_id in child_commit.parent_ids() {
682                    // Replace target commits with their parents outside the target set.
683                    let old_child_parent_ids = if let Some(parents) =
684                        target_commits_external_parents.get(old_child_parent_id)
685                    {
686                        parents.iter().collect_vec()
687                    } else {
688                        vec![old_child_parent_id]
689                    };
690
691                    // If the original parents of the new children are the new parents of the
692                    // `target_heads`, replace them with the target heads since we are "inserting"
693                    // the target commits in between the new parents and the new children.
694                    for id in old_child_parent_ids {
695                        if new_parent_ids.contains(id) {
696                            new_child_parent_ids.extend(target_heads.clone());
697                        } else {
698                            new_child_parent_ids.insert(id.clone());
699                        };
700                    }
701                }
702
703                // If not already present, add `target_heads` as parents of the new child
704                // commit.
705                new_child_parent_ids.extend(target_heads.clone());
706
707                (
708                    child_commit.id().clone(),
709                    new_child_parent_ids.into_iter().collect_vec(),
710                )
711            })
712            .collect()
713    } else {
714        HashMap::new()
715    };
716
717    // Compute the set of commits to visit, which includes the target commits, the
718    // new children commits (if any), and their descendants.
719    let mut roots = target_roots.iter().cloned().collect_vec();
720    roots.extend(new_children.iter().ids().cloned());
721
722    let descendants = repo.find_descendants_for_rebase(roots.clone())?;
723    let commit_new_parents_map = descendants
724        .iter()
725        .map(|commit| {
726            let commit_id = commit.id();
727            let new_parent_ids =
728
729            // New child of the rebased target commits.
730            if let Some(new_child_parents) = new_children_parents.get(commit_id) {
731                new_child_parents.clone()
732            }
733            // Commit is in the target set.
734            else if target_commit_ids.contains(commit_id) {
735                // If the commit is a root of the target set, it should be rebased onto the new destination.
736                if target_roots.contains(commit_id) {
737                    new_parent_ids.clone()
738                }
739                // Otherwise:
740                // 1. Keep parents which are within the target set.
741                // 2. Replace parents which are outside the target set but are part of the
742                //    connected target set with their ancestor commits which are in the target
743                //    set.
744                // 3. Keep other parents outside the target set if they are not descendants of the
745                //    new children of the target set.
746                else {
747                    let mut new_parents = vec![];
748                    for parent_id in commit.parent_ids() {
749                        if target_commit_ids.contains(parent_id) {
750                            new_parents.push(parent_id.clone());
751                        } else if let Some(parents) =
752                                connected_target_commits_internal_parents.get(parent_id) {
753                            new_parents.extend(parents.iter().cloned());
754                        } else if !new_children.iter().any(|new_child| {
755                                repo.index().is_ancestor(new_child.id(), parent_id) }) {
756                            new_parents.push(parent_id.clone());
757                        }
758                    }
759                   new_parents
760                }
761            }
762            // Commits outside the target set should have references to commits inside the set
763            // replaced.
764            else if commit
765                .parent_ids()
766                .iter()
767                .any(|id| target_commits_external_parents.contains_key(id))
768            {
769                let mut new_parents = vec![];
770                for parent in commit.parent_ids() {
771                    if let Some(parents) = target_commits_external_parents.get(parent) {
772                        new_parents.extend(parents.iter().cloned());
773                    } else {
774                        new_parents.push(parent.clone());
775                    }
776                }
777                new_parents
778            } else {
779                commit.parent_ids().iter().cloned().collect_vec()
780            };
781            (commit.id().clone(), new_parent_ids)
782        })
783        .collect();
784
785    Ok(ComputedMoveCommits {
786        target_commit_ids,
787        descendants,
788        commit_new_parents_map,
789        to_abandon: HashSet::new(),
790    })
791}
792
793fn apply_move_commits(
794    mut_repo: &mut MutableRepo,
795    commits: ComputedMoveCommits,
796    options: &RebaseOptions,
797) -> BackendResult<MoveCommitsStats> {
798    let mut num_rebased_targets = 0;
799    let mut num_rebased_descendants = 0;
800    let mut num_skipped_rebases = 0;
801    let mut num_abandoned_empty = 0;
802
803    // Always keep empty commits when rebasing descendants.
804    let rebase_descendant_options = &RebaseOptions {
805        empty: EmptyBehavior::Keep,
806        rewrite_refs: options.rewrite_refs.clone(),
807        simplify_ancestor_merge: options.simplify_ancestor_merge,
808    };
809
810    let mut rebased_commits: HashMap<CommitId, RebasedCommit> = HashMap::new();
811    mut_repo.transform_commits(
812        commits.descendants,
813        &commits.commit_new_parents_map,
814        &options.rewrite_refs,
815        async |rewriter| {
816            let old_commit_id = rewriter.old_commit().id().clone();
817            if commits.to_abandon.contains(&old_commit_id) {
818                rewriter.abandon();
819            } else if rewriter.parents_changed() {
820                let is_target_commit = commits.target_commit_ids.contains(&old_commit_id);
821                let rebased_commit = rebase_commit_with_options(
822                    rewriter,
823                    if is_target_commit {
824                        options
825                    } else {
826                        rebase_descendant_options
827                    },
828                )?;
829                if let RebasedCommit::Abandoned { .. } = rebased_commit {
830                    num_abandoned_empty += 1;
831                } else if is_target_commit {
832                    num_rebased_targets += 1;
833                } else {
834                    num_rebased_descendants += 1;
835                }
836                rebased_commits.insert(old_commit_id, rebased_commit);
837            } else {
838                num_skipped_rebases += 1;
839            }
840
841            Ok(())
842        },
843    )?;
844
845    Ok(MoveCommitsStats {
846        num_rebased_targets,
847        num_rebased_descendants,
848        num_skipped_rebases,
849        num_abandoned_empty,
850        rebased_commits,
851    })
852}
853
854#[derive(Default)]
855pub struct DuplicateCommitsStats {
856    /// Map of original commit ID to newly duplicated commit.
857    pub duplicated_commits: IndexMap<CommitId, Commit>,
858    /// The number of descendant commits which were rebased onto the duplicated
859    /// commits.
860    pub num_rebased: u32,
861}
862
863/// Duplicates the given `target_commit_ids` onto a new location in the graph.
864///
865/// The roots of `target_commit_ids` are duplicated on top of the new
866/// `parent_commit_ids`, whilst other commits in `target_commit_ids` are
867/// duplicated on top of the newly duplicated commits in the target set. If
868/// `children_commit_ids` is not empty, the `children_commit_ids` will be
869/// rebased onto the heads of the duplicated target commits.
870///
871/// If `target_descriptions` is not empty, it will be consulted to retrieve the
872/// new descriptions of the target commits, falling back to the original if the
873/// map does not contain an entry for a given commit.
874///
875/// This assumes that commits in `children_commit_ids` can be rewritten. There
876/// should also be no cycles in the resulting graph, i.e. `children_commit_ids`
877/// should not be ancestors of `parent_commit_ids`. Commits in
878/// `target_commit_ids` should be in reverse topological order (children before
879/// parents).
880pub async fn duplicate_commits(
881    mut_repo: &mut MutableRepo,
882    target_commit_ids: &[CommitId],
883    target_descriptions: &HashMap<CommitId, String>,
884    parent_commit_ids: &[CommitId],
885    children_commit_ids: &[CommitId],
886) -> BackendResult<DuplicateCommitsStats> {
887    if target_commit_ids.is_empty() {
888        return Ok(DuplicateCommitsStats::default());
889    }
890
891    let mut duplicated_old_to_new: IndexMap<CommitId, Commit> = IndexMap::new();
892    let mut num_rebased = 0;
893
894    let target_commit_ids: IndexSet<_> = target_commit_ids.iter().cloned().collect();
895
896    let connected_target_commits: Vec<_> =
897        RevsetExpression::commits(target_commit_ids.iter().cloned().collect_vec())
898            .connected()
899            .evaluate(mut_repo)
900            .map_err(|err| err.into_backend_error())?
901            .iter()
902            .commits(mut_repo.store())
903            .try_collect()
904            .map_err(|err| err.into_backend_error())?;
905
906    // Commits in the target set should only have other commits in the set as
907    // parents, except the roots of the set, which persist their original
908    // parents.
909    // If a commit in the target set has a parent which is not in the set, but has
910    // an ancestor which is in the set, then the commit will have that ancestor
911    // as a parent instead.
912    let target_commits_internal_parents = {
913        let mut target_commits_internal_parents =
914            compute_internal_parents_within(&target_commit_ids, &connected_target_commits);
915        target_commits_internal_parents.retain(|id, _| target_commit_ids.contains(id));
916        target_commits_internal_parents
917    };
918
919    // Compute the roots of `target_commits`.
920    let target_root_ids: HashSet<_> = target_commits_internal_parents
921        .iter()
922        .filter(|(_, parents)| parents.is_empty())
923        .map(|(commit_id, _)| commit_id.clone())
924        .collect();
925
926    // Compute the heads of the target set, which will be used as the parents of
927    // the children commits.
928    let target_head_ids = if !children_commit_ids.is_empty() {
929        compute_commits_heads(&target_commit_ids, &connected_target_commits)
930    } else {
931        vec![]
932    };
933
934    // Topological order ensures that any parents of the original commit are
935    // either not in `target_commits` or were already duplicated.
936    for original_commit_id in target_commit_ids.iter().rev() {
937        let original_commit = mut_repo
938            .store()
939            .get_commit_async(original_commit_id)
940            .await?;
941        let new_parent_ids = if target_root_ids.contains(original_commit_id) {
942            parent_commit_ids.to_vec()
943        } else {
944            target_commits_internal_parents
945                .get(original_commit_id)
946                .unwrap()
947                .iter()
948                // Replace parent IDs with their new IDs if they were duplicated.
949                .map(|id| {
950                    duplicated_old_to_new
951                        .get(id)
952                        .map_or(id, |commit| commit.id())
953                        .clone()
954                })
955                .collect()
956        };
957        let mut new_commit_builder = CommitRewriter::new(mut_repo, original_commit, new_parent_ids)
958            .rebase()
959            .await?
960            .clear_rewrite_source()
961            .generate_new_change_id();
962        if let Some(desc) = target_descriptions.get(original_commit_id) {
963            new_commit_builder = new_commit_builder.set_description(desc);
964        }
965        duplicated_old_to_new.insert(original_commit_id.clone(), new_commit_builder.write()?);
966    }
967
968    // Replace the original commit IDs in `target_head_ids` with the duplicated
969    // commit IDs.
970    let target_head_ids = target_head_ids
971        .into_iter()
972        .map(|commit_id| {
973            duplicated_old_to_new
974                .get(&commit_id)
975                .map_or(commit_id, |commit| commit.id().clone())
976        })
977        .collect_vec();
978
979    // Rebase new children onto the target heads.
980    let children_commit_ids_set: HashSet<CommitId> = children_commit_ids.iter().cloned().collect();
981    mut_repo.transform_descendants(children_commit_ids.to_vec(), async |mut rewriter| {
982        if children_commit_ids_set.contains(rewriter.old_commit().id()) {
983            let mut child_new_parent_ids = IndexSet::new();
984            for old_parent_id in rewriter.old_commit().parent_ids() {
985                // If the original parents of the new children are the new parents of
986                // `target_head_ids`, replace them with `target_head_ids` since we are
987                // "inserting" the target commits in between the new parents and the new
988                // children.
989                if parent_commit_ids.contains(old_parent_id) {
990                    child_new_parent_ids.extend(target_head_ids.clone());
991                } else {
992                    child_new_parent_ids.insert(old_parent_id.clone());
993                }
994            }
995            // If not already present, add `target_head_ids` as parents of the new child
996            // commit.
997            child_new_parent_ids.extend(target_head_ids.clone());
998            rewriter.set_new_parents(child_new_parent_ids.into_iter().collect());
999        }
1000        num_rebased += 1;
1001        rewriter.rebase().await?.write()?;
1002        Ok(())
1003    })?;
1004
1005    Ok(DuplicateCommitsStats {
1006        duplicated_commits: duplicated_old_to_new,
1007        num_rebased,
1008    })
1009}
1010
1011/// Duplicates the given `target_commits` onto their original parents or other
1012/// duplicated commits.
1013///
1014/// Commits in `target_commits` should be in reverse topological order (children
1015/// before parents).
1016///
1017/// If `target_descriptions` is not empty, it will be consulted to retrieve the
1018/// new descriptions of the target commits, falling back to the original if
1019/// the map does not contain an entry for a given commit.
1020pub fn duplicate_commits_onto_parents(
1021    mut_repo: &mut MutableRepo,
1022    target_commits: &[CommitId],
1023    target_descriptions: &HashMap<CommitId, String>,
1024) -> BackendResult<DuplicateCommitsStats> {
1025    if target_commits.is_empty() {
1026        return Ok(DuplicateCommitsStats::default());
1027    }
1028
1029    let mut duplicated_old_to_new: IndexMap<CommitId, Commit> = IndexMap::new();
1030
1031    // Topological order ensures that any parents of the original commit are
1032    // either not in `target_commits` or were already duplicated.
1033    for original_commit_id in target_commits.iter().rev() {
1034        let original_commit = mut_repo.store().get_commit(original_commit_id)?;
1035        let new_parent_ids = original_commit
1036            .parent_ids()
1037            .iter()
1038            .map(|id| {
1039                duplicated_old_to_new
1040                    .get(id)
1041                    .map_or(id, |commit| commit.id())
1042                    .clone()
1043            })
1044            .collect();
1045        let mut new_commit_builder = mut_repo
1046            .rewrite_commit(&original_commit)
1047            .clear_rewrite_source()
1048            .generate_new_change_id()
1049            .set_parents(new_parent_ids);
1050        if let Some(desc) = target_descriptions.get(original_commit_id) {
1051            new_commit_builder = new_commit_builder.set_description(desc);
1052        }
1053        duplicated_old_to_new.insert(original_commit_id.clone(), new_commit_builder.write()?);
1054    }
1055
1056    Ok(DuplicateCommitsStats {
1057        duplicated_commits: duplicated_old_to_new,
1058        num_rebased: 0,
1059    })
1060}
1061
1062/// Computes the internal parents of all commits in a connected commit graph,
1063/// allowing only commits in the target set as parents.
1064///
1065/// The parents of each commit are identical to the ones found using a preorder
1066/// DFS of the node's ancestors, starting from the node itself, and avoiding
1067/// traversing an edge if the parent is in the target set. `graph_commits`
1068/// should be in reverse topological order.
1069fn compute_internal_parents_within(
1070    target_commit_ids: &IndexSet<CommitId>,
1071    graph_commits: &[Commit],
1072) -> HashMap<CommitId, IndexSet<CommitId>> {
1073    let mut internal_parents: HashMap<CommitId, IndexSet<CommitId>> = HashMap::new();
1074    for commit in graph_commits.iter().rev() {
1075        // The roots of the set will not have any parents found in `internal_parents`,
1076        // and will be stored as an empty vector.
1077        let mut new_parents = IndexSet::new();
1078        for old_parent in commit.parent_ids() {
1079            if target_commit_ids.contains(old_parent) {
1080                new_parents.insert(old_parent.clone());
1081            } else if let Some(parents) = internal_parents.get(old_parent) {
1082                new_parents.extend(parents.iter().cloned());
1083            }
1084        }
1085        internal_parents.insert(commit.id().clone(), new_parents);
1086    }
1087    internal_parents
1088}
1089
1090/// Computes the heads of commits in the target set, given the list of
1091/// `target_commit_ids` and a connected graph of commits.
1092///
1093/// `connected_target_commits` should be in reverse topological order (children
1094/// before parents).
1095fn compute_commits_heads(
1096    target_commit_ids: &IndexSet<CommitId>,
1097    connected_target_commits: &[Commit],
1098) -> Vec<CommitId> {
1099    let mut target_head_ids: HashSet<CommitId> = HashSet::new();
1100    for commit in connected_target_commits.iter().rev() {
1101        target_head_ids.insert(commit.id().clone());
1102        for old_parent in commit.parent_ids() {
1103            target_head_ids.remove(old_parent);
1104        }
1105    }
1106    connected_target_commits
1107        .iter()
1108        .rev()
1109        .filter(|commit| {
1110            target_head_ids.contains(commit.id()) && target_commit_ids.contains(commit.id())
1111        })
1112        .map(|commit| commit.id().clone())
1113        .collect_vec()
1114}
1115
1116pub struct CommitWithSelection {
1117    pub commit: Commit,
1118    pub selected_tree: MergedTree,
1119    pub parent_tree: MergedTree,
1120}
1121
1122impl CommitWithSelection {
1123    /// Returns true if the selection contains all changes in the commit.
1124    pub fn is_full_selection(&self) -> bool {
1125        &self.selected_tree.id() == self.commit.tree_id()
1126    }
1127
1128    /// Returns true if the selection matches the parent tree (contains no
1129    /// changes from the commit).
1130    ///
1131    /// Both `is_full_selection()` and `is_empty_selection()`
1132    /// can be true if the commit is itself empty.
1133    pub fn is_empty_selection(&self) -> bool {
1134        self.selected_tree.id() == self.parent_tree.id()
1135    }
1136}
1137
1138/// Resulting commit builder and stats to be returned by [`squash_commits()`].
1139#[must_use]
1140pub struct SquashedCommit<'repo> {
1141    /// New destination commit will be created by this builder.
1142    pub commit_builder: CommitBuilder<'repo>,
1143    /// List of abandoned source commits.
1144    pub abandoned_commits: Vec<Commit>,
1145}
1146
1147/// Squash `sources` into `destination` and return a [`SquashedCommit`] for the
1148/// resulting commit. Caller is responsible for setting the description and
1149/// finishing the commit.
1150pub fn squash_commits<'repo>(
1151    repo: &'repo mut MutableRepo,
1152    sources: &[CommitWithSelection],
1153    destination: &Commit,
1154    keep_emptied: bool,
1155) -> BackendResult<Option<SquashedCommit<'repo>>> {
1156    struct SourceCommit<'a> {
1157        commit: &'a CommitWithSelection,
1158        abandon: bool,
1159    }
1160    let mut source_commits = vec![];
1161    for source in sources {
1162        let abandon = !keep_emptied && source.is_full_selection();
1163        if !abandon && source.is_empty_selection() {
1164            // Nothing selected from this commit. If it's abandoned (i.e. already empty), we
1165            // still include it so `jj squash` can be used for abandoning an empty commit in
1166            // the middle of a stack.
1167            continue;
1168        }
1169
1170        // TODO: Do we want to optimize the case of moving to the parent commit (`jj
1171        // squash -r`)? The source tree will be unchanged in that case.
1172        source_commits.push(SourceCommit {
1173            commit: source,
1174            abandon,
1175        });
1176    }
1177
1178    if source_commits.is_empty() {
1179        return Ok(None);
1180    }
1181
1182    let mut abandoned_commits = vec![];
1183    for source in &source_commits {
1184        if source.abandon {
1185            repo.record_abandoned_commit(&source.commit.commit);
1186            abandoned_commits.push(source.commit.commit.clone());
1187        } else {
1188            let source_tree = source.commit.commit.tree()?;
1189            // Apply the reverse of the selected changes onto the source
1190            let new_source_tree = source_tree
1191                .merge(
1192                    source.commit.selected_tree.clone(),
1193                    source.commit.parent_tree.clone(),
1194                )
1195                .block_on()?;
1196            repo.rewrite_commit(&source.commit.commit)
1197                .set_tree_id(new_source_tree.id().clone())
1198                .write()?;
1199        }
1200    }
1201
1202    let mut rewritten_destination = destination.clone();
1203    if sources.iter().any(|source| {
1204        repo.index()
1205            .is_ancestor(source.commit.id(), destination.id())
1206    }) {
1207        // If we're moving changes to a descendant, first rebase descendants onto the
1208        // rewritten sources. Otherwise it will likely already have the content
1209        // changes we're moving, so applying them will have no effect and the
1210        // changes will disappear.
1211        let options = RebaseOptions::default();
1212        repo.rebase_descendants_with_options(&options, |old_commit, rebased_commit| {
1213            if old_commit.id() != destination.id() {
1214                return;
1215            }
1216            rewritten_destination = match rebased_commit {
1217                RebasedCommit::Rewritten(commit) => commit,
1218                RebasedCommit::Abandoned { .. } => panic!("all commits should be kept"),
1219            };
1220        })?;
1221    }
1222    // Apply the selected changes onto the destination
1223    let mut destination_tree = rewritten_destination.tree()?;
1224    for source in &source_commits {
1225        destination_tree = destination_tree
1226            .merge(
1227                source.commit.parent_tree.clone(),
1228                source.commit.selected_tree.clone(),
1229            )
1230            .block_on()?;
1231    }
1232    let mut predecessors = vec![destination.id().clone()];
1233    predecessors.extend(
1234        source_commits
1235            .iter()
1236            .map(|source| source.commit.commit.id().clone()),
1237    );
1238
1239    let commit_builder = repo
1240        .rewrite_commit(&rewritten_destination)
1241        .set_tree_id(destination_tree.id().clone())
1242        .set_predecessors(predecessors);
1243    Ok(Some(SquashedCommit {
1244        commit_builder,
1245        abandoned_commits,
1246    }))
1247}
1248
1249/// Find divergent commits from the target that are already present with
1250/// identical contents in the destination. These commits should be able to be
1251/// safely abandoned.
1252pub fn find_duplicate_divergent_commits(
1253    repo: &dyn Repo,
1254    new_parent_ids: &[CommitId],
1255    target: &MoveCommitsTarget,
1256) -> BackendResult<Vec<Commit>> {
1257    let target_commits: Vec<Commit> = match target {
1258        MoveCommitsTarget::Commits(commit_ids) => commit_ids
1259            .iter()
1260            .map(|commit_id| repo.store().get_commit(commit_id))
1261            .try_collect()?,
1262        MoveCommitsTarget::Roots(root_ids) => RevsetExpression::commits(root_ids.clone())
1263            .descendants()
1264            .evaluate(repo)
1265            .map_err(|err| err.into_backend_error())?
1266            .iter()
1267            .commits(repo.store())
1268            .try_collect()
1269            .map_err(|err| err.into_backend_error())?,
1270    };
1271    let target_commit_ids: HashSet<&CommitId> = target_commits.iter().map(Commit::id).collect();
1272
1273    // For each divergent change being rebased, we want to find all of the other
1274    // commits with the same change ID which are not being rebased.
1275    let divergent_changes: Vec<(&Commit, Vec<CommitId>)> = target_commits
1276        .iter()
1277        .map(|target_commit| {
1278            let mut ancestor_candidates = repo
1279                .resolve_change_id(target_commit.change_id())
1280                .unwrap_or_default();
1281            ancestor_candidates.retain(|commit_id| !target_commit_ids.contains(commit_id));
1282            (target_commit, ancestor_candidates)
1283        })
1284        .filter(|(_, candidates)| !candidates.is_empty())
1285        .collect();
1286    if divergent_changes.is_empty() {
1287        return Ok(Vec::new());
1288    }
1289
1290    let target_root_ids = match target {
1291        MoveCommitsTarget::Commits(commit_ids) => commit_ids,
1292        MoveCommitsTarget::Roots(root_ids) => root_ids,
1293    };
1294
1295    // We only care about divergent changes which are new ancestors of the rebased
1296    // commits, not ones which were already ancestors of the rebased commits.
1297    let is_new_ancestor = RevsetExpression::commits(target_root_ids.clone())
1298        .range(&RevsetExpression::commits(new_parent_ids.to_owned()))
1299        .evaluate(repo)
1300        .map_err(|err| err.into_backend_error())?
1301        .containing_fn();
1302
1303    let mut duplicate_divergent = Vec::new();
1304    // Checking every pair of commits between these two sets could be expensive if
1305    // there are several commits with the same change ID. However, it should be
1306    // uncommon to have more than a couple commits with the same change ID being
1307    // rebased at the same time, so it should be good enough in practice.
1308    for (target_commit, ancestor_candidates) in divergent_changes {
1309        for ancestor_candidate_id in ancestor_candidates {
1310            if !is_new_ancestor(&ancestor_candidate_id).map_err(|err| err.into_backend_error())? {
1311                continue;
1312            }
1313
1314            let ancestor_candidate = repo.store().get_commit(&ancestor_candidate_id)?;
1315            let new_tree =
1316                rebase_to_dest_parent(repo, slice::from_ref(target_commit), &ancestor_candidate)?;
1317            // Check whether the rebased commit would have the same tree as the existing
1318            // commit if they had the same parents. If so, we can skip this rebased commit.
1319            if new_tree.id() == *ancestor_candidate.tree_id() {
1320                duplicate_divergent.push(target_commit.clone());
1321                break;
1322            }
1323        }
1324    }
1325    Ok(duplicate_divergent)
1326}