Skip to content

Commit 7aee32a

Browse files
committed
feat: add Repository::virtual_merge_base() and Repository::virtual_merge_base_with_graph().
1 parent 5f3f63a commit 7aee32a

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

gix/src/merge.rs

Lines changed: 18 additions & 0 deletions
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

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
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+
virtual_merge_base_with_graph,
7+
};
58
use crate::Repository;
69
use gix_merge::blob::builtin_driver::text;
710
use gix_object::Write;
@@ -223,4 +226,69 @@ impl Repository {
223226
virtual_merge_bases,
224227
})
225228
}
229+
230+
/// Create a single virtual merge-base by merging all `merge_bases` into one.
231+
/// If the list is empty, an error will be returned as the histories are then unrelated.
232+
/// If there is only one commit in the list, it is returned directly with this case clearly marked in the outcome.
233+
///
234+
/// Note that most of `options` are overwritten to match the requirements of a merge-base merge, but they can be useful
235+
/// to control the diff algorithm or rewrite tracking, for example.
236+
// TODO: test
237+
pub fn virtual_merge_base(
238+
&self,
239+
merge_bases: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
240+
options: crate::merge::tree::Options,
241+
) -> Result<crate::merge::virtual_merge_base::Outcome<'_>, virtual_merge_base::Error> {
242+
let commit_graph = self.commit_graph_if_enabled()?;
243+
let mut graph = self.revision_graph(commit_graph.as_ref());
244+
Ok(self.virtual_merge_base_with_graph(merge_bases, &mut graph, options)?)
245+
}
246+
247+
/// Like [`Self::virtual_merge_base()`], but also allows to reuse a `graph` for faster merge-base calculation,
248+
/// particularly if `graph` was used to find the `merge_bases`.
249+
pub fn virtual_merge_base_with_graph(
250+
&self,
251+
merge_bases: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
252+
graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,
253+
options: crate::merge::tree::Options,
254+
) -> Result<crate::merge::virtual_merge_base::Outcome<'_>, virtual_merge_base_with_graph::Error> {
255+
let mut merge_bases: Vec<_> = merge_bases.into_iter().map(Into::into).collect();
256+
let first = merge_bases
257+
.pop()
258+
.ok_or(virtual_merge_base_with_graph::Error::MissingCommit)?;
259+
let Some(second) = merge_bases.pop() else {
260+
let tree_id = self.find_commit(first)?.tree_id()?;
261+
let commit_id = first.attach(self);
262+
return Ok(crate::merge::virtual_merge_base::Outcome {
263+
virtual_merge_bases: Vec::new(),
264+
commit_id,
265+
tree_id,
266+
});
267+
};
268+
269+
let mut diff_cache = self.diff_resource_cache_for_tree_diff()?;
270+
let mut blob_merge = self.merge_resource_cache(Default::default())?;
271+
272+
let gix_merge::commit::virtual_merge_base::Outcome {
273+
virtual_merge_bases,
274+
commit_id,
275+
tree_id,
276+
} = gix_merge::commit::virtual_merge_base(
277+
first,
278+
second,
279+
merge_bases,
280+
graph,
281+
&mut diff_cache,
282+
&mut blob_merge,
283+
self,
284+
&mut |id| id.to_owned().attach(self).shorten_or_id().to_string(),
285+
options.into(),
286+
)?;
287+
288+
Ok(crate::merge::virtual_merge_base::Outcome {
289+
virtual_merge_bases: virtual_merge_bases.into_iter().map(|id| id.attach(self)).collect(),
290+
commit_id: commit_id.attach(self),
291+
tree_id: tree_id.attach(self),
292+
})
293+
}
226294
}

gix/src/repository/mod.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,42 @@ 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(transparent)]
161+
OpenCommitGraph(#[from] super::commit_graph_if_enabled::Error),
162+
#[error(transparent)]
163+
VirtualMergeBase(#[from] super::virtual_merge_base_with_graph::Error),
164+
}
165+
}
166+
167+
///
168+
#[cfg(feature = "merge")]
169+
pub mod virtual_merge_base_with_graph {
170+
/// The error returned by [Repository::virtual_merge_base_with_graph()](crate::Repository::virtual_merge_base_with_graph()).
171+
#[derive(Debug, thiserror::Error)]
172+
#[allow(missing_docs)]
173+
pub enum Error {
174+
#[error("Not commit was provided as merge-base")]
175+
MissingCommit,
176+
#[error(transparent)]
177+
MergeResourceCache(#[from] super::merge_resource_cache::Error),
178+
#[error(transparent)]
179+
DiffResourceCache(#[from] super::diff_resource_cache::Error),
180+
#[error(transparent)]
181+
CommitMerge(#[from] gix_merge::commit::Error),
182+
#[error(transparent)]
183+
FindCommit(#[from] crate::object::find::existing::with_conversion::Error),
184+
#[error(transparent)]
185+
DecodeCommit(#[from] gix_object::decode::Error),
186+
}
187+
}
188+
153189
///
154190
#[cfg(feature = "merge")]
155191
pub mod tree_merge_options {

0 commit comments

Comments
 (0)