Skip to content

Commit 17def54

Browse files
committed
feat: add Repository::virtual_merge_base().
1 parent 5f3f63a commit 17def54

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

gix/src/merge.rs

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@ pub use gix_merge as plumbing;
22

33
pub use gix_merge::blob;
44

5+
///
6+
pub mod virtual_merge_base {
7+
use crate::Id;
8+
9+
/// The outcome produced by [`Repository::virtual_merge_base()`](crate::Repository::virtual_merge_base()).
10+
pub struct Outcome<'repo> {
11+
/// The commit ids of all the virtual merge bases we have produced in the process of recursively merging the merge-bases.
12+
/// As they have been written to the object database, they are still available until they are garbage collected.
13+
/// The last one is the most recently produced and the one returned as `commit_id`.
14+
/// If this list is empty, this means that there was only one merge-base, which itself is already suitable the final merge-base.
15+
pub virtual_merge_bases: Vec<Id<'repo>>,
16+
/// The id of the commit that was created to hold the merged tree.
17+
pub commit_id: Id<'repo>,
18+
/// The hash of the merged tree.
19+
pub tree_id: Id<'repo>,
20+
}
21+
}
22+
523
///
624
pub mod commit {
725
/// The outcome produced by [`Repository::merge_commits()`](crate::Repository::merge_commits()).

gix/src/repository/merge.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::config::cache::util::ApplyLeniencyDefault;
22
use crate::config::tree;
33
use crate::prelude::ObjectIdExt;
4-
use crate::repository::{blob_merge_options, merge_commits, merge_resource_cache, merge_trees, tree_merge_options};
4+
use crate::repository::{
5+
blob_merge_options, merge_commits, merge_resource_cache, merge_trees, tree_merge_options, virtual_merge_base,
6+
};
57
use crate::Repository;
68
use gix_merge::blob::builtin_driver::text;
79
use gix_object::Write;
@@ -223,4 +225,56 @@ impl Repository {
223225
virtual_merge_bases,
224226
})
225227
}
228+
229+
/// Create a single virtual merge-base by merging all `merge_bases` into one.
230+
/// If the list is empty, an error will be returned as the histories are then unrelated.
231+
/// If there is only one commit in the list, it is returned directly with this case clearly marked in the outcome.
232+
///
233+
/// Note that most of `options` are overwritten to match the requirements of a merge-base merge, but they can be useful
234+
/// to control the diff algorithm or rewrite tracking, for example.
235+
// TODO: test
236+
pub fn virtual_merge_base(
237+
&self,
238+
merge_bases: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
239+
options: crate::merge::tree::Options,
240+
) -> Result<crate::merge::virtual_merge_base::Outcome<'_>, virtual_merge_base::Error> {
241+
let mut merge_bases: Vec<_> = merge_bases.into_iter().map(Into::into).collect();
242+
let first = merge_bases.pop().ok_or(virtual_merge_base::Error::MissingCommit)?;
243+
let Some(second) = merge_bases.pop() else {
244+
let tree_id = self.find_commit(first)?.tree_id()?;
245+
let commit_id = first.attach(self);
246+
return Ok(crate::merge::virtual_merge_base::Outcome {
247+
virtual_merge_bases: Vec::new(),
248+
commit_id,
249+
tree_id,
250+
});
251+
};
252+
253+
let mut diff_cache = self.diff_resource_cache_for_tree_diff()?;
254+
let mut blob_merge = self.merge_resource_cache(Default::default())?;
255+
let commit_graph = self.commit_graph_if_enabled()?;
256+
let mut graph = self.revision_graph(commit_graph.as_ref());
257+
258+
let gix_merge::commit::virtual_merge_base::Outcome {
259+
virtual_merge_bases,
260+
commit_id,
261+
tree_id,
262+
} = gix_merge::commit::virtual_merge_base(
263+
first,
264+
second,
265+
merge_bases,
266+
&mut graph,
267+
&mut diff_cache,
268+
&mut blob_merge,
269+
self,
270+
&mut |id| id.to_owned().attach(self).shorten_or_id().to_string(),
271+
options.into(),
272+
)?;
273+
274+
Ok(crate::merge::virtual_merge_base::Outcome {
275+
virtual_merge_bases: virtual_merge_bases.into_iter().map(|id| id.attach(self)).collect(),
276+
commit_id: commit_id.attach(self),
277+
tree_id: tree_id.attach(self),
278+
})
279+
}
226280
}

gix/src/repository/mod.rs

+24
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@ pub mod merge_commits {
150150
}
151151
}
152152

153+
///
154+
#[cfg(feature = "merge")]
155+
pub mod virtual_merge_base {
156+
/// The error returned by [Repository::virtual_merge_base()](crate::Repository::virtual_merge_base()).
157+
#[derive(Debug, thiserror::Error)]
158+
#[allow(missing_docs)]
159+
pub enum Error {
160+
#[error("Not commit was provided as merge-base")]
161+
MissingCommit,
162+
#[error(transparent)]
163+
OpenCommitGraph(#[from] super::commit_graph_if_enabled::Error),
164+
#[error(transparent)]
165+
MergeResourceCache(#[from] super::merge_resource_cache::Error),
166+
#[error(transparent)]
167+
DiffResourceCache(#[from] super::diff_resource_cache::Error),
168+
#[error(transparent)]
169+
CommitMerge(#[from] gix_merge::commit::Error),
170+
#[error(transparent)]
171+
FindCommit(#[from] crate::object::find::existing::with_conversion::Error),
172+
#[error(transparent)]
173+
DecodeCommit(#[from] gix_object::decode::Error),
174+
}
175+
}
176+
153177
///
154178
#[cfg(feature = "merge")]
155179
pub mod tree_merge_options {

0 commit comments

Comments
 (0)