Skip to content

Commit ff0975c

Browse files
committed
Add the first test for an octopus merge
1 parent 4b202bd commit ff0975c

File tree

6 files changed

+1197
-0
lines changed

6 files changed

+1197
-0
lines changed

Cargo.lock

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

gix-merge/Cargo.toml

+26
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,31 @@ workspace = true
1414
[lib]
1515
doctest = false
1616

17+
[features]
18+
default = ["blob"]
19+
## Enable diffing of blobs using imara-diff, which also allows for a generic rewrite tracking implementation.
20+
blob = ["dep:imara-diff", "dep:gix-filter", "dep:gix-worktree", "dep:gix-path", "dep:gix-fs", "dep:gix-command", "dep:gix-tempfile", "dep:gix-trace"]
21+
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
22+
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde"]
23+
1724
[dependencies]
25+
gix-hash = { version = "^0.14.2", path = "../gix-hash" }
26+
gix-object = { version = "^0.44.0", path = "../gix-object" }
27+
gix-filter = { version = "^0.13.0", path = "../gix-filter", optional = true }
28+
gix-worktree = { version = "^0.36.0", path = "../gix-worktree", default-features = false, features = ["attributes"], optional = true }
29+
gix-command = { version = "^0.3.9", path = "../gix-command", optional = true }
30+
gix-path = { version = "^0.10.11", path = "../gix-path", optional = true }
31+
gix-fs = { version = "^0.11.3", path = "../gix-fs", optional = true }
32+
gix-tempfile = { version = "^14.0.0", path = "../gix-tempfile", optional = true }
33+
gix-trace = { version = "^0.1.10", path = "../gix-trace", optional = true }
34+
35+
thiserror = "1.0.63"
36+
imara-diff = { version = "0.1.7", optional = true }
37+
bstr = { version = "1.5.0", default-features = false }
38+
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
39+
40+
document-features = { version = "0.2.0", optional = true }
1841

42+
[package.metadata.docs.rs]
43+
all-features = true
44+
features = ["document-features"]

gix-merge/src/blob/mod.rs

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
use bstr::BString;
2+
use std::collections::HashMap;
3+
use std::path::PathBuf;
4+
5+
///
6+
pub mod pipeline;
7+
8+
///
9+
pub mod platform;
10+
11+
/// A way to classify a resource suitable for merging.
12+
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
13+
pub enum ResourceKind {
14+
/// Our side of the state.
15+
CurrentOrOurs,
16+
/// Their side of the state.
17+
OtherOrTheirs,
18+
/// The state of the common base of both ours and theirs.
19+
CommonAncestorOrBase,
20+
}
21+
22+
/// Define a driver program that merges
23+
///
24+
/// Some values are related to diffing, some are related to conversions.
25+
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
26+
pub enum BuiltinDriver {
27+
/// Perform a merge between text-sources such that conflicts are marked according to
28+
/// `merge.conflictStyle` in the Git configuration.
29+
///
30+
/// If any of the inputs, *base*, *ours* or *theirs* looks like non-text/binary,
31+
/// the [`Binary`](Self::Binary) driver will be used instead.
32+
///
33+
/// Also see [`TextDriverConflictStyle`].
34+
#[default]
35+
Text,
36+
/// Merge 'unmergable' content by choosing *ours* or *theirs*, without performing
37+
/// an actual merge.
38+
///
39+
/// Note that if the merge operation is for virtual ancestor (a merge for merge-bases),
40+
/// then *ours* will always be chosen.
41+
Binary,
42+
/// Merge text-sources and resolve conflicts by adding conflicting lines one after another,
43+
/// in random order, without adding conflict markers either.
44+
///
45+
/// This can be useful for files that change a lot, but will remain usable merely by adding
46+
/// all changed lines.
47+
Union,
48+
}
49+
50+
/// The way the built-in [text driver](BuiltinDriver::Text) will express merge conflicts in the
51+
/// resulting file.
52+
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
53+
pub enum TextDriverConflictStyle {
54+
/// Only show the zealously minified conflicting lines of the local changes and the incoming (other) changes,
55+
/// hiding the base version entirely.
56+
///
57+
/// ```
58+
/// line1-changed-by-both
59+
/// <<<<<<< local
60+
/// line2-to-be-changed-in-incoming
61+
/// =======
62+
/// line2-changed
63+
/// >>>>>>> incoming
64+
///```
65+
#[default]
66+
Merge,
67+
/// Show non-minimized hunks of local changes, the base, and the incoming (other) changes.
68+
///
69+
/// This mode does not hide any information.
70+
/// ```
71+
/// <<<<<<< local
72+
/// line1-changed-by-both
73+
/// line2-to-be-changed-in-incoming
74+
/// ||||||| 9a8d80c
75+
/// line1-to-be-changed-by-both
76+
/// line2-to-be-changed-in-incoming
77+
/// =======
78+
/// line1-changed-by-both
79+
/// line2-changed
80+
/// >>>>>>> incoming
81+
///```
82+
Diff3,
83+
/// Like [`Diff3](Self::Diff3), but will show *minimized* hunks of local change and the incoming (other) changes,
84+
/// as well as non-minimized hunks of the base.
85+
///
86+
/// ```
87+
/// line1-changed-by-both
88+
/// <<<<<<< local
89+
/// line2-to-be-changed-in-incoming
90+
/// ||||||| 9a8d80c
91+
/// line1-to-be-changed-by-both
92+
/// line2-to-be-changed-in-incoming
93+
/// =======
94+
/// line2-changed
95+
/// >>>>>>> incoming
96+
/// ```
97+
ZealousDiff3,
98+
}
99+
100+
impl BuiltinDriver {
101+
/// Return the name of this instance.
102+
pub fn as_str(&self) -> &str {
103+
match self {
104+
BuiltinDriver::Text => "text",
105+
BuiltinDriver::Binary => "binary",
106+
BuiltinDriver::Union => "union",
107+
}
108+
}
109+
110+
/// Get all available built-in drivers.
111+
pub fn all() -> &'static [Self] {
112+
&[BuiltinDriver::Text, BuiltinDriver::Binary, BuiltinDriver::Union]
113+
}
114+
115+
/// Try to match one of our variants to `name`, case-sensitive, and return its instance.
116+
pub fn by_name(name: &str) -> Option<Self> {
117+
Self::all().iter().find(|variant| variant.as_str() == name).copied()
118+
}
119+
}
120+
121+
/// Define a driver program that merges
122+
///
123+
/// Some values are related to diffing, some are related to conversions.
124+
#[derive(Default, Debug, Clone, PartialEq, Eq)]
125+
pub struct Driver {
126+
/// The name of the driver, as referred to by `[merge "name"]` in the git configuration.
127+
pub name: BString,
128+
/// The human-readable version of `name`, only to be used for displaying driver-information to the user.
129+
pub display_name: BString,
130+
/// The command to execute to perform the merge entirely like `<command> %O %A %B %L %P %S %X %Y`.
131+
///
132+
/// * **%O**
133+
/// - the common ancestor version, or *base*.
134+
/// * **%A**
135+
/// - the current version, or *ours*.
136+
/// * **%B**
137+
/// - the other version, or *theirs*.
138+
/// * **%L**
139+
/// - The conflict-marker size as positive number.
140+
/// * **%P**
141+
/// - The path in which the merged result will be stored.
142+
/// * **%S**
143+
/// - The conflict-label for the common ancestor or *base*.
144+
/// * **%X**
145+
/// - The conflict-label for the current version or *ours*.
146+
/// * **%Y**
147+
/// - The conflict-label for the other version or *theirs*.
148+
///
149+
/// Note that conflict-labels are behind the conflict markers, to annotate them
150+
pub command: BString,
151+
/// If `true`, this is the `name` of the driver to use when a virtual-merge-base is created, as a merge of all
152+
/// available merge-bases if there are more than one.
153+
///
154+
/// This value can also be special built-in drivers named `text`, `binary` or `union`. Note that user-defined
155+
/// drivers with the same name will be preferred over built-in ones, but only for files whose git attributes
156+
/// specified the driver by *name*.
157+
pub recursive: Option<BString>,
158+
}
159+
160+
/// A conversion pipeline to take an object or path from what's stored in Git to what can be merged, while
161+
/// following the guidance of git-attributes at the respective path to learn how the merge should be performed.
162+
///
163+
/// Depending on the source, different conversions are performed:
164+
///
165+
/// * `worktree on disk` -> `object for storage in git`
166+
/// * `object` -> `possibly renormalized object`
167+
/// - Renormalization means that the `object` is converted to what would be checked out into the work-tree,
168+
/// just to turn it back into an object.
169+
#[derive(Clone)]
170+
pub struct Pipeline {
171+
/// A way to read data directly from the worktree.
172+
pub roots: pipeline::WorktreeRoots,
173+
/// A pipeline to convert objects from the worktree to Git, and also from Git to the worktree, and back to Git.
174+
pub filter: gix_filter::Pipeline,
175+
/// Options affecting the way we read files.
176+
pub options: pipeline::Options,
177+
/// All available merge drivers.
178+
///
179+
/// They are referenced in git-attributes by name, and we hand out indices into this array.
180+
drivers: Vec<Driver>,
181+
/// Pre-configured attributes to obtain additional merge-related information.
182+
attrs: gix_filter::attributes::search::Outcome,
183+
/// A buffer to produce disk-accessible paths from worktree roots.
184+
path: PathBuf,
185+
}
186+
187+
/// A utility for gathering and processing all state necessary to perform a three-way merge.
188+
///
189+
/// It can re-use buffers if all three parts of participating in the merge are
190+
/// set repeatedly.
191+
#[derive(Clone)]
192+
pub struct Platform {
193+
/// The current version (ours).
194+
current: Option<platform::ResourceCache>,
195+
/// The ancestor version (base).
196+
ancestor: Option<platform::ResourceCache>,
197+
/// The other version (theirs).
198+
other: Option<platform::ResourceCache>,
199+
200+
/// A way to convert objects into a diff-able format.
201+
pub filter: Pipeline,
202+
/// A way to access `.gitattributes`
203+
pub attr_stack: gix_worktree::Stack,
204+
205+
/// The way we convert resources into mergeable states.
206+
filter_mode: pipeline::Mode,
207+
}

0 commit comments

Comments
 (0)