Skip to content

Commit 686823c

Browse files
committed
feat: Repository::merge_files() to trigger blob-merge directly.
1 parent 7831c95 commit 686823c

File tree

7 files changed

+162
-1
lines changed

7 files changed

+162
-1
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix/Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ extras = [
6464
"credentials",
6565
"interrupt",
6666
"status",
67-
"dirwalk"
67+
"dirwalk",
68+
"blob-merge"
6869
]
6970

7071
## A collection of features that need a larger MSRV, and thus are disabled by default.
@@ -137,6 +138,9 @@ revparse-regex = ["regex", "revision"]
137138
## which relies on line-by-line diffs in some cases.
138139
blob-diff = ["gix-diff/blob", "attributes"]
139140

141+
## Add functions to specifically merge files, using the standard three-way merge that git offers.
142+
blob-merge = ["dep:gix-merge", "gix-merge/blob", "attributes"]
143+
140144
## Make it possible to turn a tree into a stream of bytes, which can be decoded to entries and turned into various other formats.
141145
worktree-stream = ["gix-worktree-stream", "attributes"]
142146

@@ -337,6 +341,7 @@ gix-path = { version = "^0.10.11", path = "../gix-path" }
337341
gix-url = { version = "^0.27.5", path = "../gix-url" }
338342
gix-traverse = { version = "^0.41.0", path = "../gix-traverse" }
339343
gix-diff = { version = "^0.46.0", path = "../gix-diff", default-features = false }
344+
gix-merge = { version = "^0.0.0", path = "../gix-merge", default-features = false, optional = true }
340345
gix-mailmap = { version = "^0.24.0", path = "../gix-mailmap", optional = true }
341346
gix-features = { version = "^0.38.2", path = "../gix-features", features = [
342347
"progress",

gix/src/config/cache/access.rs

+45
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,51 @@ impl Cache {
100100
Ok(out)
101101
}
102102

103+
#[cfg(feature = "blob-merge")]
104+
pub(crate) fn merge_drivers(&self) -> Result<Vec<gix_merge::blob::Driver>, config::merge::drivers::Error> {
105+
let mut out = Vec::<gix_merge::blob::Driver>::new();
106+
for section in self
107+
.resolved
108+
.sections_by_name("merge")
109+
.into_iter()
110+
.flatten()
111+
.filter(|s| (self.filter_config_section)(s.meta()))
112+
{
113+
let Some(name) = section.header().subsection_name().filter(|n| !n.is_empty()) else {
114+
continue;
115+
};
116+
117+
let driver = match out.iter_mut().find(|d| d.name == name) {
118+
Some(existing) => existing,
119+
None => {
120+
out.push(gix_merge::blob::Driver {
121+
name: name.into(),
122+
display_name: name.into(),
123+
..Default::default()
124+
});
125+
out.last_mut().expect("just pushed")
126+
}
127+
};
128+
129+
if let Some(command) = section.value(config::tree::Merge::DRIVER_COMMAND.name) {
130+
driver.command = command.into_owned().into();
131+
}
132+
if let Some(recursive_name) = section.value(config::tree::Merge::DRIVER_RECURSIVE.name) {
133+
driver.recursive = Some(recursive_name.into_owned().into());
134+
}
135+
}
136+
Ok(out)
137+
}
138+
139+
#[cfg(feature = "blob-merge")]
140+
pub(crate) fn merge_pipeline_options(
141+
&self,
142+
) -> Result<gix_merge::blob::pipeline::Options, config::merge::pipeline_options::Error> {
143+
Ok(gix_merge::blob::pipeline::Options {
144+
large_file_threshold_bytes: self.big_file_threshold()?,
145+
})
146+
}
147+
103148
#[cfg(feature = "blob-diff")]
104149
pub(crate) fn diff_pipeline_options(
105150
&self,

gix/src/config/mod.rs

+25
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,31 @@ pub enum Error {
109109
},
110110
}
111111

112+
///
113+
pub mod merge {
114+
///
115+
pub mod pipeline_options {
116+
/// The error produced when obtaining options needed to fill in [gix_merge::blob::pipeline::Options].
117+
#[derive(Debug, thiserror::Error)]
118+
#[allow(missing_docs)]
119+
pub enum Error {
120+
#[error(transparent)]
121+
BigFileThreshold(#[from] crate::config::unsigned_integer::Error),
122+
}
123+
}
124+
125+
///
126+
pub mod drivers {
127+
/// The error produced when obtaining a list of [Drivers](gix_merge::blob::Driver).
128+
#[derive(Debug, thiserror::Error)]
129+
#[allow(missing_docs)]
130+
pub enum Error {
131+
#[error(transparent)]
132+
ConfigBoolean(#[from] crate::config::boolean::Error),
133+
}
134+
}
135+
}
136+
112137
///
113138
pub mod diff {
114139
///

gix/src/config/tree/sections/merge.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl Section for Merge {
2929
fn keys(&self) -> &[&dyn Key] {
3030
&[
3131
&Self::RENORMALIZE,
32+
&Self::DEFAULT,
3233
&Self::DRIVER_NAME,
3334
&Self::DRIVER_COMMAND,
3435
&Self::DRIVER_RECURSIVE,

gix/src/repository/merge.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::config::cache::util::ApplyLeniencyDefault;
2+
use crate::config::tree;
3+
use crate::repository::merge_resource_cache;
4+
use crate::Repository;
5+
6+
/// Diff-utilities
7+
impl Repository {
8+
/// Create a resource cache that can hold the three resources needed for a three-way merge. `worktree_roots`
9+
/// determines which side of the merge is read from the worktree, or from which worktree.
10+
///
11+
/// The platform can be used to setup resources and finally perform a merge.
12+
pub fn merge_resource_cache(
13+
&self,
14+
worktree_roots: gix_merge::blob::pipeline::WorktreeRoots,
15+
) -> Result<gix_merge::blob::Platform, merge_resource_cache::Error> {
16+
let index = self.index_or_load_from_head()?;
17+
let mode = {
18+
let renormalize = self
19+
.config
20+
.resolved
21+
.boolean(&tree::Merge::RENORMALIZE)
22+
.map(|res| {
23+
tree::Merge::RENORMALIZE
24+
.enrich_error(res)
25+
.with_lenient_default(self.config.lenient_config)
26+
})
27+
.transpose()?
28+
.unwrap_or_default();
29+
if renormalize {
30+
gix_merge::blob::pipeline::Mode::Renormalize
31+
} else {
32+
gix_merge::blob::pipeline::Mode::ToGit
33+
}
34+
};
35+
let attrs = self
36+
.attributes_only(
37+
&index,
38+
if worktree_roots.is_unset() {
39+
gix_worktree::stack::state::attributes::Source::IdMapping
40+
} else {
41+
gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping
42+
},
43+
)?
44+
.inner;
45+
let filter = gix_filter::Pipeline::new(self.command_context()?, crate::filter::Pipeline::options(self)?);
46+
let filter = gix_merge::blob::Pipeline::new(worktree_roots, filter, self.config.merge_pipeline_options()?);
47+
let options = gix_merge::blob::platform::Options {
48+
default_driver: self
49+
.config
50+
.resolved
51+
.string(&tree::Merge::DEFAULT)
52+
.map(|name| name.into_owned()),
53+
};
54+
let drivers = self.config.merge_drivers()?;
55+
Ok(gix_merge::blob::Platform::new(filter, mode, attrs, drivers, options))
56+
}
57+
}

gix/src/repository/mod.rs

+27
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ mod kind;
4242
mod location;
4343
#[cfg(feature = "mailmap")]
4444
mod mailmap;
45+
///
46+
#[cfg(feature = "blob-merge")]
47+
mod merge;
4548
mod object;
4649
#[cfg(feature = "attributes")]
4750
mod pathspec;
@@ -55,6 +58,30 @@ mod submodule;
5558
mod thread_safe;
5659
mod worktree;
5760

61+
///
62+
#[cfg(feature = "blob-merge")]
63+
pub mod merge_resource_cache {
64+
/// The error returned by [Repository::merge_resource_cache()](crate::Repository::merge_resource_cache()).
65+
#[derive(Debug, thiserror::Error)]
66+
#[allow(missing_docs)]
67+
pub enum Error {
68+
#[error(transparent)]
69+
RenormalizeConfig(#[from] crate::config::boolean::Error),
70+
#[error(transparent)]
71+
PipelineOptions(#[from] crate::config::merge::pipeline_options::Error),
72+
#[error(transparent)]
73+
Index(#[from] crate::repository::index_or_load_from_head::Error),
74+
#[error(transparent)]
75+
AttributeStack(#[from] crate::config::attribute_stack::Error),
76+
#[error(transparent)]
77+
CommandContext(#[from] crate::config::command_context::Error),
78+
#[error(transparent)]
79+
FilterPipeline(#[from] crate::filter::pipeline::options::Error),
80+
#[error(transparent)]
81+
DriversConfig(#[from] crate::config::merge::drivers::Error),
82+
}
83+
}
84+
5885
///
5986
#[cfg(feature = "tree-editor")]
6087
pub mod edit_tree {

0 commit comments

Comments
 (0)