From a82f3f25bebe3b3143a0010aa008c66205dcb9f6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 1 Dec 2024 15:13:11 +0100 Subject: [PATCH] feat: add `--tree-favor` to `gix merge tree|commit`. With it one can decide which side to favor in case of irreconcilable tree-conflicts. --- gitoxide-core/src/repository/merge/commit.rs | 6 +- gitoxide-core/src/repository/merge/tree.rs | 7 ++- src/plumbing/main.rs | 22 +++++-- src/plumbing/options/mod.rs | 61 +++++++++++++------- 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/gitoxide-core/src/repository/merge/commit.rs b/gitoxide-core/src/repository/merge/commit.rs index ace09d5879f..6982cc2b8a7 100644 --- a/gitoxide-core/src/repository/merge/commit.rs +++ b/gitoxide-core/src/repository/merge/commit.rs @@ -17,6 +17,7 @@ pub fn commit( Options { format, file_favor, + tree_favor, in_memory, debug, }: Options, @@ -31,7 +32,10 @@ pub fn commit( let (ours_ref, ours_id) = refname_and_commit(&repo, ours)?; let (theirs_ref, theirs_id) = refname_and_commit(&repo, theirs)?; - let options = repo.tree_merge_options()?.with_file_favor(file_favor); + let options = repo + .tree_merge_options()? + .with_file_favor(file_favor) + .with_tree_favor(tree_favor); let ours_id_str = ours_id.to_string(); let theirs_id_str = theirs_id.to_string(); let labels = gix::merge::blob::builtin_driver::text::Labels { diff --git a/gitoxide-core/src/repository/merge/tree.rs b/gitoxide-core/src/repository/merge/tree.rs index 425ab578c14..0d6467b09da 100644 --- a/gitoxide-core/src/repository/merge/tree.rs +++ b/gitoxide-core/src/repository/merge/tree.rs @@ -3,6 +3,7 @@ use crate::OutputFormat; pub struct Options { pub format: OutputFormat, pub file_favor: Option, + pub tree_favor: Option, pub in_memory: bool, pub debug: bool, } @@ -29,6 +30,7 @@ pub(super) mod function { Options { format, file_favor, + tree_favor, in_memory, debug, }: Options, @@ -44,7 +46,10 @@ pub(super) mod function { let (ours_ref, ours_id) = refname_and_tree(&repo, ours)?; let (theirs_ref, theirs_id) = refname_and_tree(&repo, theirs)?; - let options = repo.tree_merge_options()?.with_file_favor(file_favor); + let options = repo + .tree_merge_options()? + .with_file_favor(file_favor) + .with_tree_favor(tree_favor); let base_id_str = base_id.to_string(); let ours_id_str = ours_id.to_string(); let theirs_id_str = theirs_id.to_string(); diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index 046de6e76da..b0a69339c29 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -172,9 +172,13 @@ pub fn main() -> Result<()> { }, ), merge::SubCommands::Tree { - in_memory, - file_favor, - debug, + opts: + merge::SharedOptions { + in_memory, + file_favor, + tree_favor, + debug, + }, ours, base, theirs, @@ -197,15 +201,20 @@ pub fn main() -> Result<()> { format, file_favor: file_favor.map(Into::into), in_memory, + tree_favor: tree_favor.map(Into::into), debug, }, ) }, ), merge::SubCommands::Commit { - in_memory, - file_favor, - debug, + opts: + merge::SharedOptions { + in_memory, + file_favor, + tree_favor, + debug, + }, ours, theirs, } => prepare_and_run( @@ -225,6 +234,7 @@ pub fn main() -> Result<()> { core::repository::merge::tree::Options { format, file_favor: file_favor.map(Into::into), + tree_favor: tree_favor.map(Into::into), in_memory, debug, }, diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 4d6722895ac..c1a3ec670d7 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -375,6 +375,14 @@ pub mod merge { Theirs, } + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] + pub enum TreeFavor { + /// Use only the previous tree entry in case of conflict. + Ancestor, + /// Use only ours tree entry in case of conflict. + Ours, + } + impl From for gix::merge::tree::FileFavor { fn from(value: FileFavor) -> Self { match value { @@ -384,6 +392,33 @@ pub mod merge { } } + impl From for gix::merge::tree::TreeFavor { + fn from(value: TreeFavor) -> Self { + match value { + TreeFavor::Ancestor => gix::merge::tree::TreeFavor::Ancestor, + TreeFavor::Ours => gix::merge::tree::TreeFavor::Ours, + } + } + } + + #[derive(Debug, clap::Parser)] + pub struct SharedOptions { + /// Keep all objects to be written in memory to avoid any disk IO. + /// + /// Note that the resulting tree won't be available or inspectable. + #[clap(long, short = 'm')] + pub in_memory: bool, + /// Decide how to resolve content conflicts in files. If unset, write conflict markers and fail. + #[clap(long, short = 'f')] + pub file_favor: Option, + /// Decide how to resolve conflicts in trees, i.e. modification/deletion. If unset, try to preserve both states and fail. + #[clap(long, short = 't')] + pub tree_favor: Option, + /// Print additional information about conflicts for debugging. + #[clap(long, short = 'd')] + pub debug: bool, + } + #[derive(Debug, clap::Parser)] #[command(about = "perform merges of various kinds")] pub struct Platform { @@ -412,17 +447,8 @@ pub mod merge { /// Merge a tree by specifying ours, base and theirs, writing it to the object database. Tree { - /// Keep all objects to be written in memory to avoid any disk IO. - /// - /// Note that the resulting tree won't be available or inspectable. - #[clap(long, short = 'm')] - in_memory: bool, - /// Decide how to resolve content conflicts in files. If unset, write conflict markers and fail. - #[clap(long, short = 'f')] - file_favor: Option, - /// Print additional information about conflicts for debugging. - #[clap(long, short = 'd')] - debug: bool, + #[clap(flatten)] + opts: SharedOptions, /// A revspec to our treeish. #[clap(value_name = "OURS", value_parser = crate::shared::AsBString)] @@ -436,17 +462,8 @@ pub mod merge { }, /// Merge a commits by specifying ours, and theirs, writing the tree to the object database. Commit { - /// Keep all objects to be written in memory to avoid any disk IO. - /// - /// Note that the resulting tree won't be available or inspectable. - #[clap(long, short = 'm')] - in_memory: bool, - /// Decide how to resolve content conflicts in files. If unset, write conflict markers and fail. - #[clap(long, short = 'f')] - file_favor: Option, - /// Print additional information about conflicts for debugging. - #[clap(long, short = 'd')] - debug: bool, + #[clap(flatten)] + opts: SharedOptions, /// A revspec to our committish. #[clap(value_name = "OURS", value_parser = crate::shared::AsBString)]