From: "Kyle Zhao via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Elijah Newren <newren@gmail.com>, kylezhao <kylezhao@tencent.com>,
Kyle Zhao <kylezhao@tencent.com>,
Kyle Zhao <kylezhao@tencent.com>
Subject: [PATCH v3] merge-tree.c: add --merge-base=<commit> option
Date: Sat, 29 Oct 2022 03:42:54 +0000 [thread overview]
Message-ID: <pull.1397.v3.git.1667014975042.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1397.v2.git.1666958144017.gitgitgadget@gmail.com>
From: Kyle Zhao <kylezhao@tencent.com>
This patch will give our callers more flexibility to use `git merge-tree`,
such as:
git merge-tree --write-tree --merge-base=branch^ HEAD branch
This does a merge of HEAD and branch, but uses branch^ as the merge-base.
And the reason why using an option flag instead of a positional argument
is to allow additional commits passed to merge-tree to be handled via an
octopus merge in the future.
Signed-off-by: Kyle Zhao <kylezhao@tencent.com>
---
merge-tree: allow specifying a base commit when --write-tree is passed
Thanks for Elijah's work. I'm very excited that merge-ort is integrated
into the git merge-tree, which means that we can use merge-ort in bare
repositories to optimize merge performance.
In this patch, I introduce a new --merge-base=<commit> option to allow
callers to specify a merge-base for the merge. This may allow users to
implement git cherry-pick and git rebase in bare repositories with git
merge-tree cmd.
Changes since v1:
* Changed merge_incore_recursive() to merge_incore_nonrecursive() when
merge-base is specified.
* Fixed c style problem.
* Moved commit lookup/die logic out to the parsing logic in
cmd_merge_tree().
* use test_commit for test
Changes since v2:
* commit message
* Rebased on top of en/merge-tree-sequence.
* Set opt.ancestor to o->merge_base. Because opt.ancestor is a *char.
To make it easier to pass parameters, I moved
lookup_commit_reference_by_name() to real_ merge() again.
* Added test comment.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1397%2Fkeyu98%2Fkz%2Fmerge-tree-option-merge-base-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1397/keyu98/kz/merge-tree-option-merge-base-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1397
Range-diff vs v2:
1: ab4e5d5ad08 ! 1: 554cbde49e8 merge-tree.c: add --merge-base=<commit> option
@@ Commit message
git merge-tree --write-tree --merge-base=branch^ HEAD branch
- It would cherry-pick the commit at the tip of the branch on top of the
- current commit even if the repository is bare.
+ This does a merge of HEAD and branch, but uses branch^ as the merge-base.
+
+ And the reason why using an option flag instead of a positional argument
+ is to allow additional commits passed to merge-tree to be handled via an
+ octopus merge in the future.
Signed-off-by: Kyle Zhao <kylezhao@tencent.com>
@@ builtin/merge-tree.c
#include "merge-ort.h"
#include "object-store.h"
@@ builtin/merge-tree.c: struct merge_tree_options {
- int allow_unrelated_histories;
int show_messages;
int name_only;
-+ const struct commit *base_commit;
+ int use_stdin;
++ const char *merge_base;
};
static int real_merge(struct merge_tree_options *o,
@@ builtin/merge-tree.c: static int real_merge(struct merge_tree_options *o,
- if (!merge_bases && !o->allow_unrelated_histories)
- die(_("refusing to merge unrelated histories"));
- merge_bases = reverse_commit_list(merge_bases);
-+ if (o->base_commit) {
++ if (o->merge_base) {
++ struct commit *base_commit;
+ struct tree *base_tree, *parent1_tree, *parent2_tree;
+
-+ opt.ancestor = "specified merge base";
-+ base_tree = get_commit_tree(o->base_commit);
++ base_commit = lookup_commit_reference_by_name(o->merge_base);
++ if (!base_commit)
++ die(_("could not lookup commit %s"), o->merge_base);
++
++ opt.ancestor = o->merge_base;
++ base_tree = get_commit_tree(base_commit);
+ parent1_tree = get_commit_tree(parent1);
+ parent2_tree = get_commit_tree(parent2);
+ merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
@@ builtin/merge-tree.c: static int real_merge(struct merge_tree_options *o,
if (result.clean < 0)
die(_("failure to merge"));
-@@ builtin/merge-tree.c: static int real_merge(struct merge_tree_options *o,
-
- int cmd_merge_tree(int argc, const char **argv, const char *prefix)
- {
-+ const char *merge_base = NULL;
- struct merge_tree_options o = { .show_messages = -1 };
- int expected_remaining_argc;
- int original_argc;
@@ builtin/merge-tree.c: int cmd_merge_tree(int argc, const char **argv, const char *prefix)
- &o.allow_unrelated_histories,
- N_("allow merging unrelated histories"),
+ &o.use_stdin,
+ N_("perform multiple merges, one per line of input"),
PARSE_OPT_NONEG),
+ OPT_STRING(0, "merge-base",
-+ &merge_base,
-+ N_("commit"),
-+ N_("specify a merge-base for the merge")),
++ &o.merge_base,
++ N_("commit"),
++ N_("specify a merge-base for the merge")),
OPT_END()
};
-@@ builtin/merge-tree.c: int cmd_merge_tree(int argc, const char **argv, const char *prefix)
- usage_with_options(merge_tree_usage, mt_options);
-
- /* Do the relevant type of merge */
-- if (o.mode == MODE_REAL)
-+ if (o.mode == MODE_REAL) {
-+ if (merge_base) {
-+ o.base_commit = lookup_commit_reference_by_name(merge_base);
-+ if (!o.base_commit)
-+ die(_("could not lookup commit %s"), merge_base);
-+ }
- return real_merge(&o, argv[0], argv[1], prefix);
-+ }
- else
- return trivial_merge(argv[0], argv[1], argv[2]);
- }
## t/t4301-merge-tree-write-tree.sh ##
-@@ t/t4301-merge-tree-write-tree.sh: test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository
- test_must_fail git -C read-only merge-tree side1 side2
+@@ t/t4301-merge-tree-write-tree.sh: test_expect_success '--stdin with both a successful and a conflicted merge' '
+ test_cmp expect actual
'
++# specify merge-base as parent of branch2
++# git merge-tree --write-tree --merge-base=c2 c1 c3
++# Commit c1: add file1
++# Commit c2: add file2 after c1
++# Commit c3: add file3 after c2
++# Expected: add file3, and file2 does NOT appear
++
+test_expect_success 'specify merge-base as parent of branch2' '
+ # Setup
+ git init base-b2-p && (
Documentation/git-merge-tree.txt | 4 ++++
builtin/merge-tree.c | 39 ++++++++++++++++++++++++--------
t/t4301-merge-tree-write-tree.sh | 30 ++++++++++++++++++++++++
3 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index 04bcc416e6e..d9dacb2ce54 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -64,6 +64,10 @@ OPTIONS
share no common history. This flag can be given to override that
check and make the merge proceed anyway.
+--merge-base=<commit>::
+ Instead of finding the merge-bases for <branch1> and <branch2>,
+ specify a merge-base for the merge.
+
[[OUTPUT]]
OUTPUT
------
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index fe853aa8f91..cd25aac6ba6 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -3,6 +3,7 @@
#include "tree-walk.h"
#include "xdiff-interface.h"
#include "help.h"
+#include "commit.h"
#include "commit-reach.h"
#include "merge-ort.h"
#include "object-store.h"
@@ -403,6 +404,7 @@ struct merge_tree_options {
int show_messages;
int name_only;
int use_stdin;
+ const char *merge_base;
};
static int real_merge(struct merge_tree_options *o,
@@ -432,16 +434,31 @@ static int real_merge(struct merge_tree_options *o,
opt.branch1 = branch1;
opt.branch2 = branch2;
- /*
- * Get the merge bases, in reverse order; see comment above
- * merge_incore_recursive in merge-ort.h
- */
- merge_bases = get_merge_bases(parent1, parent2);
- if (!merge_bases && !o->allow_unrelated_histories)
- die(_("refusing to merge unrelated histories"));
- merge_bases = reverse_commit_list(merge_bases);
+ if (o->merge_base) {
+ struct commit *base_commit;
+ struct tree *base_tree, *parent1_tree, *parent2_tree;
+
+ base_commit = lookup_commit_reference_by_name(o->merge_base);
+ if (!base_commit)
+ die(_("could not lookup commit %s"), o->merge_base);
+
+ opt.ancestor = o->merge_base;
+ base_tree = get_commit_tree(base_commit);
+ parent1_tree = get_commit_tree(parent1);
+ parent2_tree = get_commit_tree(parent2);
+ merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
+ } else {
+ /*
+ * Get the merge bases, in reverse order; see comment above
+ * merge_incore_recursive in merge-ort.h
+ */
+ merge_bases = get_merge_bases(parent1, parent2);
+ if (!merge_bases && !o->allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ merge_bases = reverse_commit_list(merge_bases);
+ merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+ }
- merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
if (result.clean < 0)
die(_("failure to merge"));
@@ -515,6 +532,10 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
&o.use_stdin,
N_("perform multiple merges, one per line of input"),
PARSE_OPT_NONEG),
+ OPT_STRING(0, "merge-base",
+ &o.merge_base,
+ N_("commit"),
+ N_("specify a merge-base for the merge")),
OPT_END()
};
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index cac85591b52..5b0073d3dcd 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -860,4 +860,34 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
test_cmp expect actual
'
+# specify merge-base as parent of branch2
+# git merge-tree --write-tree --merge-base=c2 c1 c3
+# Commit c1: add file1
+# Commit c2: add file2 after c1
+# Commit c3: add file3 after c2
+# Expected: add file3, and file2 does NOT appear
+
+test_expect_success 'specify merge-base as parent of branch2' '
+ # Setup
+ git init base-b2-p && (
+ cd base-b2-p &&
+ test_commit c1 file1 &&
+ test_commit c2 file2 &&
+ test_commit c3 file3
+ ) &&
+ # Testing
+ (
+ cd base-b2-p &&
+ TREE_OID=$(git merge-tree --write-tree --merge-base=c2 c1 c3) &&
+
+ q_to_tab <<-EOF >expect &&
+ 100644 blob $(git rev-parse c1:file1)Qfile1
+ 100644 blob $(git rev-parse c3:file3)Qfile3
+ EOF
+
+ git ls-tree $TREE_OID >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
base-commit: ec1edbcb56ac05e9980299b05924c5c1b51d68b4
--
gitgitgadget
next prev parent reply other threads:[~2022-10-29 3:43 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-27 12:12 [PATCH] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-10-27 18:09 ` Junio C Hamano
2022-10-28 8:20 ` Elijah Newren
2022-10-28 16:03 ` Junio C Hamano
2022-10-29 1:52 ` Elijah Newren
2022-10-28 11:43 ` [Internet]Re: " kylezhao(赵柯宇)
2022-10-28 8:13 ` Elijah Newren
2022-10-28 11:54 ` [Internet]Re: " kylezhao(赵柯宇)
2022-10-28 11:55 ` [PATCH v2] " Kyle Zhao via GitGitGadget
2022-10-29 1:32 ` Elijah Newren
2022-10-29 3:42 ` Kyle Zhao via GitGitGadget [this message]
2022-11-01 1:08 ` [PATCH v3] " Elijah Newren
2022-11-01 8:55 ` [PATCH v4 0/2] merge-tree: allow specifying a base commit when --write-tree is passed Kyle Zhao via GitGitGadget
2022-11-01 8:55 ` [PATCH v4 1/2] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-11-01 8:55 ` [PATCH v4 2/2] merge-tree.c: support --merge-base in conjunction with --stdin Kyle Zhao via GitGitGadget
2022-11-03 1:13 ` Elijah Newren
2022-11-03 3:28 ` [Internet]Re: " kylezhao(赵柯宇)
2022-11-01 21:19 ` [PATCH v4 0/2] merge-tree: allow specifying a base commit when --write-tree is passed Taylor Blau
2022-11-03 3:29 ` [PATCH v5 " Kyle Zhao via GitGitGadget
2022-11-03 3:29 ` [PATCH v5 1/2] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-11-03 8:36 ` Ævar Arnfjörð Bjarmason
2022-11-03 9:43 ` [Internet]Re: " kylezhao(赵柯宇)
2022-11-03 3:29 ` [PATCH v5 2/2] merge-tree.c: allow specifying the merge-base when --stdin is passed Kyle Zhao via GitGitGadget
2022-11-03 10:50 ` [PATCH v6 0/2] merge-tree: allow specifying a base commit when --write-tree " Kyle Zhao via GitGitGadget
2022-11-03 10:50 ` [PATCH v6 1/2] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-11-03 10:50 ` [PATCH v6 2/2] merge-tree.c: allow specifying the merge-base when --stdin is passed Kyle Zhao via GitGitGadget
2022-11-11 19:44 ` Elijah Newren
2022-11-11 23:45 ` [PATCH v7 0/2] merge-tree: allow specifying a base commit when --write-tree " Kyle Zhao via GitGitGadget
2022-11-11 23:45 ` [PATCH v7 1/2] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-11-22 3:04 ` Junio C Hamano
2022-11-22 3:52 ` [Internet]Re: " kylezhao(赵柯宇)
2022-11-22 4:28 ` Junio C Hamano
2022-11-22 5:42 ` Junio C Hamano
2022-11-22 7:47 ` [Internet]Re: " kylezhao(赵柯宇)
2022-11-11 23:45 ` [PATCH v7 2/2] merge-tree.c: allow specifying the merge-base when --stdin is passed Kyle Zhao via GitGitGadget
2022-11-12 0:32 ` [PATCH v7 0/2] merge-tree: allow specifying a base commit when --write-tree " Elijah Newren
2022-11-13 4:53 ` Taylor Blau
2022-11-13 4:54 ` Taylor Blau
2022-11-24 3:37 ` [PATCH v8 0/3] " Kyle Zhao via GitGitGadget
2022-11-24 3:37 ` [PATCH v8 1/3] merge-tree.c: add --merge-base=<commit> option Kyle Zhao via GitGitGadget
2022-11-24 3:37 ` [PATCH v8 2/3] merge-tree.c: allow specifying the merge-base when --stdin is passed Kyle Zhao via GitGitGadget
2022-11-24 3:37 ` [PATCH v8 3/3] docs: fix description of the `--merge-base` option Kyle Zhao via GitGitGadget
2022-11-25 5:28 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.1397.v3.git.1667014975042.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=kylezhao@tencent.com \
--cc=newren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.