| 
 | 1 | +/// The error returned by [`commit()`](crate::commit()).  | 
 | 2 | +#[derive(Debug, thiserror::Error)]  | 
 | 3 | +#[allow(missing_docs)]  | 
 | 4 | +pub enum Error {  | 
 | 5 | +    #[error(transparent)]  | 
 | 6 | +    MergeBase(#[from] gix_revision::merge_base::Error),  | 
 | 7 | +    #[error(transparent)]  | 
 | 8 | +    MergeTree(#[from] crate::tree::Error),  | 
 | 9 | +    #[error("No common ancestor between {our_commit_id} and {their_commit_id}")]  | 
 | 10 | +    NoMergeBase {  | 
 | 11 | +        /// The commit on our side that was to be merged.  | 
 | 12 | +        our_commit_id: gix_hash::ObjectId,  | 
 | 13 | +        /// The commit on their side that was to be merged.  | 
 | 14 | +        their_commit_id: gix_hash::ObjectId,  | 
 | 15 | +    },  | 
 | 16 | +    #[error("Could not find ancestor, our or their commit to extract tree from")]  | 
 | 17 | +    FindCommit(#[from] gix_object::find::existing_object::Error),  | 
 | 18 | +}  | 
 | 19 | + | 
 | 20 | +/// A way to configure [`commit()`](crate::commit()).  | 
 | 21 | +#[derive(Default, Debug, Copy, Clone)]  | 
 | 22 | +pub struct Options {  | 
 | 23 | +    /// If `true`, merging unrelated commits is allowed, with the merge-base being assumed as empty tree.  | 
 | 24 | +    pub allow_missing_merge_base: bool,  | 
 | 25 | +    /// Options to define how trees should be merged.  | 
 | 26 | +    pub tree_merge: crate::tree::Options,  | 
 | 27 | +    /// Options to define how to merge blobs.  | 
 | 28 | +    ///  | 
 | 29 | +    /// Note that these are temporarily overwritten if multiple merge-bases are merged into one.  | 
 | 30 | +    pub blob_merge: crate::blob::platform::merge::Options,  | 
 | 31 | +}  | 
 | 32 | + | 
 | 33 | +pub(super) mod function {  | 
 | 34 | +    use crate::commit::{Error, Options};  | 
 | 35 | +    use gix_object::FindExt;  | 
 | 36 | + | 
 | 37 | +    /// Like [`tree()`](crate::tree()), but it takes only two commits, `our_commit` and `their_commit` to automatically  | 
 | 38 | +    /// compute the merge-bases among them.  | 
 | 39 | +    /// If there are multiple merge bases, these will be auto-merged into one, recursively, if  | 
 | 40 | +    /// [`allow_missing_merge_base`](Options::allow_missing_merge_base) is `true`.  | 
 | 41 | +    ///  | 
 | 42 | +    /// `labels` are names where [`current`](crate::blob::builtin_driver::text::Labels::current) is a name for `our_commit`  | 
 | 43 | +    /// and [`other`](crate::blob::builtin_driver::text::Labels::other) is a name for `their_commit`.  | 
 | 44 | +    /// If [`ancestor`](crate::blob::builtin_driver::text::Labels::ancestor) is unset, it will be set by us based on the  | 
 | 45 | +    /// merge-bases of `our_commit` and `their_commit`.  | 
 | 46 | +    ///  | 
 | 47 | +    /// The `graph` is used to find the merge-base between `our_commit` and `their_commit`, and can also act as cache  | 
 | 48 | +    /// to speed up subsequent merge-base queries.  | 
 | 49 | +    ///  | 
 | 50 | +    /// ### Performance  | 
 | 51 | +    ///  | 
 | 52 | +    /// Note that `objects` *should* have an object cache to greatly accelerate tree-retrieval.  | 
 | 53 | +    pub fn commit<'a>(  | 
 | 54 | +        our_commit: gix_hash::ObjectId,  | 
 | 55 | +        their_commit: gix_hash::ObjectId,  | 
 | 56 | +        mut labels: crate::blob::builtin_driver::text::Labels<'_>,  | 
 | 57 | +        graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,  | 
 | 58 | +        diff_resource_cache: &mut gix_diff::blob::Platform,  | 
 | 59 | +        blob_merge: &mut crate::blob::Platform,  | 
 | 60 | +        objects: &impl gix_object::FindObjectOrHeader,  | 
 | 61 | +        options: Options,  | 
 | 62 | +    ) -> Result<crate::tree::Outcome<'a>, Error> {  | 
 | 63 | +        let merge_bases_commit_ids = gix_revision::merge_base(our_commit, &[their_commit], graph)?;  | 
 | 64 | +        let (merge_base_commit_id, ancestor_name) = match merge_bases_commit_ids {  | 
 | 65 | +            Some(base_commit) if base_commit.len() == 1 => (base_commit[0], None),  | 
 | 66 | +            Some(_base_commits) => {  | 
 | 67 | +                todo!("merge multiple bases into one");  | 
 | 68 | +                (our_commit.kind().null(), Some("merged common ancestors".into()))  | 
 | 69 | +            }  | 
 | 70 | +            None => {  | 
 | 71 | +                if options.allow_missing_merge_base {  | 
 | 72 | +                    (  | 
 | 73 | +                        gix_hash::ObjectId::empty_tree(our_commit.kind()),  | 
 | 74 | +                        Some("empty tree".into()),  | 
 | 75 | +                    )  | 
 | 76 | +                } else {  | 
 | 77 | +                    return Err(Error::NoMergeBase {  | 
 | 78 | +                        our_commit_id: our_commit,  | 
 | 79 | +                        their_commit_id: their_commit,  | 
 | 80 | +                    });  | 
 | 81 | +                }  | 
 | 82 | +            }  | 
 | 83 | +        };  | 
 | 84 | +        if labels.ancestor.is_none() {  | 
 | 85 | +            labels.ancestor = ancestor_name;  | 
 | 86 | +        }  | 
 | 87 | + | 
 | 88 | +        let mut state = gix_diff::tree::State::default();  | 
 | 89 | +        let merge_base_tree_id = objects.find_commit(&merge_base_commit_id, &mut state.buf1)?.tree();  | 
 | 90 | +        let our_tree_id = objects.find_commit(&our_commit, &mut state.buf1)?.tree();  | 
 | 91 | +        let their_tree_id = objects.find_commit(&their_commit, &mut state.buf1)?.tree();  | 
 | 92 | + | 
 | 93 | +        Ok(crate::tree(  | 
 | 94 | +            &merge_base_tree_id,  | 
 | 95 | +            &our_tree_id,  | 
 | 96 | +            &their_tree_id,  | 
 | 97 | +            labels,  | 
 | 98 | +            objects,  | 
 | 99 | +            &mut state,  | 
 | 100 | +            diff_resource_cache,  | 
 | 101 | +            blob_merge,  | 
 | 102 | +            options.tree_merge,  | 
 | 103 | +        )?)  | 
 | 104 | +    }  | 
 | 105 | +}  | 
0 commit comments