Skip to content

Commit a43e563

Browse files
committed
feat!: Repository::merge_trees() now takes portable version of Options.
1 parent 589ee8e commit a43e563

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

gix/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ document-features = { version = "0.2.0", optional = true }
399399

400400
[dev-dependencies]
401401
# For additional features that aren't enabled by default due to MSRV
402-
gix = { path = ".", default-features = false, features = ["tree-editor"] }
402+
gix = { path = ".", default-features = false, features = ["need-more-recent-msrv"] }
403403
pretty_assertions = "1.4.0"
404404
gix-testtools = { path = "../tests/tools" }
405405
is_ci = "1.1.1"

gix/src/merge.rs

+86
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub use gix_merge::blob;
44

55
///
66
pub mod tree {
7+
use gix_merge::blob::builtin_driver;
78
pub use gix_merge::tree::{Conflict, ContentMerge, Resolution, ResolutionFailure, UnresolvedConflict};
89

910
/// The outcome produced by [`Repository::merge_trees()`](crate::Repository::merge_trees()).
@@ -31,4 +32,89 @@ pub mod tree {
3132
self.conflicts.iter().any(|c| c.is_unresolved(how))
3233
}
3334
}
35+
36+
/// A way to configure [`Repository::merge_trees()`](crate::Repository::merge_trees()).
37+
#[derive(Default, Debug, Clone)]
38+
pub struct Options {
39+
inner: gix_merge::tree::Options,
40+
file_favor: Option<FileFavor>,
41+
}
42+
43+
impl From<gix_merge::tree::Options> for Options {
44+
fn from(opts: gix_merge::tree::Options) -> Self {
45+
Options {
46+
inner: opts,
47+
file_favor: None,
48+
}
49+
}
50+
}
51+
52+
impl From<Options> for gix_merge::tree::Options {
53+
fn from(value: Options) -> Self {
54+
let mut opts = value.inner;
55+
if let Some(file_favor) = value.file_favor {
56+
let (resolve_binary, resolve_text) = match file_favor {
57+
FileFavor::Ours => (
58+
builtin_driver::binary::ResolveWith::Ours,
59+
builtin_driver::text::Conflict::ResolveWithOurs,
60+
),
61+
FileFavor::Theirs => (
62+
builtin_driver::binary::ResolveWith::Theirs,
63+
builtin_driver::text::Conflict::ResolveWithTheirs,
64+
),
65+
};
66+
67+
opts.symlink_conflicts = Some(resolve_binary);
68+
opts.blob_merge.resolve_binary_with = Some(resolve_binary);
69+
opts.blob_merge.text.conflict = resolve_text;
70+
}
71+
opts
72+
}
73+
}
74+
75+
/// Identify how files should be resolved in case of conflicts.
76+
///
77+
/// This works for…
78+
///
79+
/// * content merges
80+
/// * binary files
81+
/// * symlinks (a form of file after all)
82+
///
83+
/// Note that that union merges aren't available as they aren't available for binaries or symlinks.
84+
#[derive(Debug, Copy, Clone)]
85+
pub enum FileFavor {
86+
/// Choose *our* side in case of a conflict.
87+
/// Note that this choice is precise, so *ours* hunk will only be chosen if they conflict with *theirs*,
88+
/// so *their* hunks may still show up in the merged result.
89+
Ours,
90+
/// Choose *their* side in case of a conflict.
91+
/// Note that this choice is precise, so *ours* hunk will only be chosen if they conflict with *theirs*,
92+
/// so *their* hunks may still show up in the merged result.
93+
Theirs,
94+
}
95+
96+
/// Builder
97+
impl Options {
98+
/// If *not* `None`, rename tracking will be performed when determining the changes of each side of the merge.
99+
pub fn with_rewrites(mut self, rewrites: Option<gix_diff::Rewrites>) -> Self {
100+
self.inner.rewrites = rewrites;
101+
self
102+
}
103+
104+
/// If `Some(what-is-unresolved)`, the first unresolved conflict will cause the entire merge to stop.
105+
/// This is useful to see if there is any conflict, without performing the whole operation, something
106+
/// that can be very relevant during merges that would cause a lot of blob-diffs.
107+
pub fn with_fail_on_conflict(mut self, fail_on_conflict: Option<UnresolvedConflict>) -> Self {
108+
self.inner.fail_on_conflict = fail_on_conflict;
109+
self
110+
}
111+
112+
/// When `None`, the default, both sides will be treated equally, and in case of conflict an unbiased representation
113+
/// is chosen both for content and for trees, causing a conflict.
114+
/// When `Some(favor)` one can choose a side to prefer in order to automatically resolve a conflict meaningfully.
115+
pub fn with_file_favor(mut self, file_favor: Option<FileFavor>) -> Self {
116+
self.file_favor = file_favor;
117+
self
118+
}
119+
}
34120
}

gix/src/repository/merge.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl Repository {
8383
}
8484

8585
/// Read all relevant configuration options to instantiate options for use in [`merge_trees()`](Self::merge_trees).
86-
pub fn tree_merge_options(&self) -> Result<gix_merge::tree::Options, tree_merge_options::Error> {
86+
pub fn tree_merge_options(&self) -> Result<crate::merge::tree::Options, tree_merge_options::Error> {
8787
Ok(gix_merge::tree::Options {
8888
rewrites: crate::diff::utils::new_rewrites_inner(
8989
&self.config.resolved,
@@ -97,7 +97,8 @@ impl Repository {
9797
marker_size_multiplier: 0,
9898
symlink_conflicts: None,
9999
allow_lossy_resolution: false,
100-
})
100+
}
101+
.into())
101102
}
102103

103104
/// Merge `our_tree` and `their_tree` together, assuming they have the same `ancestor_tree`, to yield a new tree
@@ -116,14 +117,13 @@ impl Repository {
116117
///
117118
/// It's highly recommended to [set an object cache](crate::Repository::compute_object_cache_size_for_tree_diffs)
118119
/// to avoid extracting the same object multiple times.
119-
// TODO: Use `crate::merge::Options` here and add niceties such as setting the resolution strategy.
120120
pub fn merge_trees(
121121
&self,
122122
ancestor_tree: impl AsRef<gix_hash::oid>,
123123
our_tree: impl AsRef<gix_hash::oid>,
124124
their_tree: impl AsRef<gix_hash::oid>,
125125
labels: gix_merge::blob::builtin_driver::text::Labels<'_>,
126-
options: gix_merge::tree::Options,
126+
options: crate::merge::tree::Options,
127127
) -> Result<crate::merge::tree::Outcome<'_>, merge_trees::Error> {
128128
let mut diff_cache = self.diff_resource_cache_for_tree_diff()?;
129129
let mut blob_merge = self.merge_resource_cache(Default::default())?;
@@ -141,7 +141,7 @@ impl Repository {
141141
&mut Default::default(),
142142
&mut diff_cache,
143143
&mut blob_merge,
144-
options,
144+
options.into(),
145145
)?;
146146

147147
let validate = self.config.protect_options()?;

0 commit comments

Comments
 (0)