Skip to content

Commit

Permalink
Add the first test for an octopus merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Sep 12, 2024
1 parent 3a1fe6a commit 0ae3791
Show file tree
Hide file tree
Showing 5 changed files with 660 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions gix-merge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,31 @@ workspace = true
[lib]
doctest = false

[features]
default = ["blob"]
## Enable diffing of blobs using imara-diff, which also allows for a generic rewrite tracking implementation.
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"]
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
serde = ["dep:serde", "gix-hash/serde", "gix-object/serde"]

[dependencies]
gix-hash = { version = "^0.14.2", path = "../gix-hash" }
gix-object = { version = "^0.44.0", path = "../gix-object" }
gix-filter = { version = "^0.13.0", path = "../gix-filter", optional = true }
gix-worktree = { version = "^0.36.0", path = "../gix-worktree", default-features = false, features = ["attributes"], optional = true }
gix-command = { version = "^0.3.9", path = "../gix-command", optional = true }
gix-path = { version = "^0.10.11", path = "../gix-path", optional = true }
gix-fs = { version = "^0.11.3", path = "../gix-fs", optional = true }
gix-tempfile = { version = "^14.0.0", path = "../gix-tempfile", optional = true }
gix-trace = { version = "^0.1.10", path = "../gix-trace", optional = true }

thiserror = "1.0.63"
imara-diff = { version = "0.1.7", optional = true }
bstr = { version = "1.5.0", default-features = false }
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }

document-features = { version = "0.2.0", optional = true }

[package.metadata.docs.rs]
all-features = true
features = ["document-features"]
181 changes: 181 additions & 0 deletions gix-merge/src/blob/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use bstr::BString;
use std::path::PathBuf;

///
pub mod pipeline;

/// A way to classify a resource suitable for merging.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum ResourceKind {
/// Our side of the state.
CurrentOrOurs,
/// Their side of the state.
OtherOrTheirs,
/// The state of the common base of both ours and theirs.
CommonAncestorOrBase,
}

/// Define a driver program that merges
///
/// Some values are related to diffing, some are related to conversions.
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum BuiltinDriver {
/// Perform a merge between text-sources such that conflicts are marked according to
/// `merge.conflictStyle` in the Git configuration.
///
/// If any of the inputs, *base*, *ours* or *theirs* looks like non-text/binary,
/// the [`Binary`](Self::Binary) driver will be used instead.
///
/// Also see [`TextDriverConflictStyle`].
#[default]
Text,
/// Merge 'unmergable' content by choosing *ours* or *theirs*, without performing
/// an actual merge.
///
/// Note that if the merge operation is for virtual ancestor (a merge for merge-bases),
/// then *ours* will always be chosen.
Binary,
/// Merge text-sources and resolve conflicts by adding conflicting lines one after another,
/// in random order, without adding conflict markers either.
///
/// This can be useful for files that change a lot, but will remain usable merely by adding
/// all changed lines.
Union,
}

/// The way the built-in [text driver](BuiltinDriver::Text) will express merge conflicts in the
/// resulting file.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextDriverConflictStyle {
/// Only show the zealously minified conflicting lines of the local changes and the incoming (other) changes,
/// hiding the base version entirely.
///
/// ```
/// line1-changed-by-both
/// <<<<<<< local
/// line2-to-be-changed-in-incoming
/// =======
/// line2-changed
/// >>>>>>> incoming
///```
#[default]
Merge,
/// Show non-minimized hunks of local changes, the base, and the incoming (other) changes.
///
/// This mode does not hide any information.
/// ```
/// <<<<<<< local
/// line1-changed-by-both
/// line2-to-be-changed-in-incoming
/// ||||||| 9a8d80c
/// line1-to-be-changed-by-both
/// line2-to-be-changed-in-incoming
/// =======
/// line1-changed-by-both
/// line2-changed
/// >>>>>>> incoming
///```
Diff3,
/// Like [`Diff3](Self::Diff3), but will show *minimized* hunks of local change and the incoming (other) changes,
/// as well as non-minimized hunks of the base.
///
/// ```
/// line1-changed-by-both
/// <<<<<<< local
/// line2-to-be-changed-in-incoming
/// ||||||| 9a8d80c
/// line1-to-be-changed-by-both
/// line2-to-be-changed-in-incoming
/// =======
/// line2-changed
/// >>>>>>> incoming
/// ```
ZealousDiff3,
}

impl BuiltinDriver {
/// Return the name of this instance.
pub fn as_str(&self) -> &str {
match self {
BuiltinDriver::Text => "text",
BuiltinDriver::Binary => "binary",
BuiltinDriver::Union => "union",
}
}

/// Get all available built-in drivers.
pub fn all() -> &'static [Self] {
&[BuiltinDriver::Text, BuiltinDriver::Binary, BuiltinDriver::Union]
}

/// Try to match one of our variants to `name`, case-sensitive, and return its instance.
pub fn by_name(name: &str) -> Option<Self> {
Self::all().iter().find(|variant| variant.as_str() == name).copied()
}
}

/// Define a driver program that merges
///
/// Some values are related to diffing, some are related to conversions.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Driver {
/// The name of the driver, as referred to by `[merge "name"]` in the git configuration.
pub name: BString,
/// The human-readable version of `name`, only to be used for displaying driver-information to the user.
pub display_name: BString,
/// The command to execute to perform the merge entirely like `<command> %O %A %B %L %P %S %X %Y`.
///
/// * **%O**
/// - the common ancestor version, or *base*.
/// * **%A**
/// - the current version, or *ours*.
/// * **%B**
/// - the other version, or *theirs*.
/// * **%L**
/// - The conflict-marker size as positive number.
/// * **%P**
/// - The path in which the merged result will be stored.
/// * **%S**
/// - The conflict-label for the common ancestor or *base*.
/// * **%X**
/// - The conflict-label for the current version or *ours*.
/// * **%Y**
/// - The conflict-label for the other version or *theirs*.
///
/// Note that conflict-labels are behind the conflict markers, to annotate them
pub command: BString,
/// If `true`, this is the `name` of the driver to use when a virtual-merge-base is created, as a merge of all
/// available merge-bases if there are more than one.
///
/// This value can also be special built-in drivers named `text`, `binary` or `union`. Note that user-defined
/// drivers with the same name will be preferred over built-in ones, but only for files whose git attributes
/// specified the driver by *name*.
pub recursive: Option<BString>,
}

/// A conversion pipeline to take an object or path from what's stored in Git to what can be merged, while
/// following the guidance of git-attributes at the respective path to learn how the merge should be performed.
///
/// Depending on the source, different conversions are performed:
///
/// * `worktree on disk` -> `object for storage in git`
/// * `object` -> `possibly renormalized object`
/// - Renormalization means that the `object` is converted to what would be checked out into the work-tree,
/// just to turn it back into an object.
#[derive(Clone)]
pub struct Pipeline {
/// A way to read data directly from the worktree.
pub roots: pipeline::WorktreeRoots,
/// A pipeline to convert objects from the worktree to Git, and also from Git to the worktree, and back to Git.
pub filter: gix_filter::Pipeline,
/// Options affecting the way we read files.
pub options: pipeline::Options,
/// All available merge drivers.
///
/// They are referenced in git-attributes by name, and we hand out indices into this array.
drivers: Vec<Driver>,
/// Pre-configured attributes to obtain additional merge-related information.
attrs: gix_filter::attributes::search::Outcome,
/// A buffer to produce disk-accessible paths from worktree roots.
path: PathBuf,
}
Loading

0 comments on commit 0ae3791

Please sign in to comment.