Skip to content

Commit ae45511

Browse files
committed
complete-merge
Finish the entire merge implementation and cover everything with tests.
1 parent fd23d96 commit ae45511

File tree

11 files changed

+2146
-429
lines changed

11 files changed

+2146
-429
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crate-status.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,11 @@ Check out the [performance discussion][gix-diff-performance] as well.
340340

341341
* [x] three-way merge analysis of **blobs** with choice of how to resolve conflicts
342342
- [ ] choose how to resolve conflicts on the data-structure
343-
- [ ] produce a new blob based on data-structure containing possible resolutions
343+
- [x] produce a new blob based on data-structure containing possible resolutions
344344
- [x] `merge` style
345345
- [x] `diff3` style
346346
- [x] `zdiff` style
347+
- [ ] various newlines-related options during the merge (see https://git-scm.com/docs/git-merge#Documentation/git-merge.txt-ignore-space-change).
347348
- [ ] a way to control inter-hunk merging based on proximity (maybe via `gix-diff` feature which could use the same)
348349
* [ ] diff-heuristics match Git perfectly
349350
* [x] API documentation

gix-merge/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ document-features = { version = "0.2.0", optional = true }
4343
[dev-dependencies]
4444
gix-testtools = { path = "../tests/tools" }
4545
gix-odb = { path = "../gix-odb" }
46+
gix-utils = { version = "^0.1.12", path = "../gix-utils" }
47+
termtree = "0.5.1"
4648
pretty_assertions = "1.4.0"
4749

4850
[package.metadata.docs.rs]

gix-merge/src/commit.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ pub enum Error {
1818
}
1919

2020
/// A way to configure [`commit()`](crate::commit()).
21-
#[derive(Default, Debug, Copy, Clone)]
21+
#[derive(Default, Debug, Clone)]
2222
pub struct Options {
2323
/// If `true`, merging unrelated commits is allowed, with the merge-base being assumed as empty tree.
2424
pub allow_missing_merge_base: bool,
2525
/// Options to define how trees should be merged.
2626
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,
27+
/// If `true`, do not merge multiple merge-bases into one. Instead, just use the first one.
28+
// TODO: test
29+
#[doc(alias = "no_recursive", alias = "git2")]
30+
pub use_first_merge_base: bool,
3131
}
3232

3333
pub(super) mod function {
@@ -51,21 +51,33 @@ pub(super) mod function {
5151
///
5252
/// Note that `objects` *should* have an object cache to greatly accelerate tree-retrieval.
5353
#[allow(clippy::too_many_arguments)]
54-
pub fn commit<'objects>(
54+
pub fn commit<'objects, E>(
5555
our_commit: gix_hash::ObjectId,
5656
their_commit: gix_hash::ObjectId,
5757
mut labels: crate::blob::builtin_driver::text::Labels<'_>,
5858
graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,
5959
diff_resource_cache: &mut gix_diff::blob::Platform,
6060
blob_merge: &mut crate::blob::Platform,
6161
objects: &'objects impl gix_object::FindObjectOrHeader,
62+
write_blob_to_odb: impl FnMut(&[u8]) -> Result<gix_hash::ObjectId, E>,
6263
options: Options,
63-
) -> Result<crate::tree::Outcome<'objects>, Error> {
64+
) -> Result<crate::tree::Outcome<'objects>, Error>
65+
where
66+
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
67+
{
6468
let merge_bases_commit_ids = gix_revision::merge_base(our_commit, &[their_commit], graph)?;
65-
let (merge_base_commit_id, ancestor_name) = match merge_bases_commit_ids {
66-
Some(base_commit) if base_commit.len() == 1 => (base_commit[0], None),
69+
let mut state = gix_diff::tree::State::default();
70+
let (merge_base_tree_id, ancestor_name) = match merge_bases_commit_ids {
71+
Some(base_commit) if base_commit.len() == 1 => {
72+
(objects.find_commit(&base_commit[0], &mut state.buf1)?.tree(), None)
73+
}
6774
Some(base_commits) => {
68-
let virtual_base_tree = *base_commits.first().expect("TODO: merge multiple bases into one");
75+
let virtual_base_tree = if options.use_first_merge_base {
76+
let first = *base_commits.first().expect("TODO: merge multiple bases into one");
77+
objects.find_commit(&first, &mut state.buf1)?.tree()
78+
} else {
79+
todo!("merge multiple merge bases")
80+
};
6981
(virtual_base_tree, Some("merged common ancestors".into()))
7082
}
7183
None => {
@@ -86,8 +98,6 @@ pub(super) mod function {
8698
labels.ancestor = ancestor_name;
8799
}
88100

89-
let mut state = gix_diff::tree::State::default();
90-
let merge_base_tree_id = objects.find_commit(&merge_base_commit_id, &mut state.buf1)?.tree();
91101
let our_tree_id = objects.find_commit(&our_commit, &mut state.buf1)?.tree();
92102
let their_tree_id = objects.find_commit(&their_commit, &mut state.buf1)?.tree();
93103

@@ -97,6 +107,7 @@ pub(super) mod function {
97107
&their_tree_id,
98108
labels,
99109
objects,
110+
write_blob_to_odb,
100111
&mut state,
101112
diff_resource_cache,
102113
blob_merge,

0 commit comments

Comments
 (0)