cherry-pick/revert: add support for -X/--strategy-option
For example, this would allow cherry-picking or reverting patches from a piece of history with a different end-of-line style, like so: $ git revert -Xrenormalize old-problematic-commit Currently that is possible with manual use of merge-recursive but the cherry-pick/revert porcelain does not expose the functionality. While at it, document the existing support for --strategy. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
73e7b2ef6c
commit
67ac1e1d57
@ -79,6 +79,16 @@ effect to your index in a row.
|
|||||||
cherry-pick'ed commit, then a fast forward to this commit will
|
cherry-pick'ed commit, then a fast forward to this commit will
|
||||||
be performed.
|
be performed.
|
||||||
|
|
||||||
|
--strategy=<strategy>::
|
||||||
|
Use the given merge strategy. Should only be used once.
|
||||||
|
See the MERGE STRATEGIES section in linkgit:git-merge[1]
|
||||||
|
for details.
|
||||||
|
|
||||||
|
-X<option>::
|
||||||
|
--strategy-option=<option>::
|
||||||
|
Pass the merge strategy-specific option through to the
|
||||||
|
merge strategy. See linkgit:git-merge[1] for details.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
git cherry-pick master::
|
git cherry-pick master::
|
||||||
@ -120,6 +130,28 @@ git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
|
|||||||
so the result can be inspected and made into a single new
|
so the result can be inspected and made into a single new
|
||||||
commit if suitable.
|
commit if suitable.
|
||||||
|
|
||||||
|
The following sequence attempts to backport a patch, bails out because
|
||||||
|
the code the patch applies to has changed too much, and then tries
|
||||||
|
again, this time exercising more care about matching up context lines.
|
||||||
|
|
||||||
|
------------
|
||||||
|
$ git cherry-pick topic^ <1>
|
||||||
|
$ git diff <2>
|
||||||
|
$ git reset --merge ORIG_HEAD <3>
|
||||||
|
$ git cherry-pick -Xpatience topic^ <4>
|
||||||
|
------------
|
||||||
|
<1> apply the change that would be shown by `git show topic^`.
|
||||||
|
In this example, the patch does not apply cleanly, so
|
||||||
|
information about the conflict is written to the index and
|
||||||
|
working tree and no new commit results.
|
||||||
|
<2> summarize changes to be reconciled
|
||||||
|
<3> cancel the cherry-pick. In other words, return to the
|
||||||
|
pre-cherry-pick state, preserving any local modifications you had in
|
||||||
|
the working tree.
|
||||||
|
<4> try to apply the change introduced by `topic^` again,
|
||||||
|
spending extra time to avoid mistakes based on incorrectly matching
|
||||||
|
context lines.
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
Written by Junio C Hamano <gitster@pobox.com>
|
Written by Junio C Hamano <gitster@pobox.com>
|
||||||
|
@ -80,6 +80,16 @@ effect to your index in a row.
|
|||||||
--signoff::
|
--signoff::
|
||||||
Add Signed-off-by line at the end of the commit message.
|
Add Signed-off-by line at the end of the commit message.
|
||||||
|
|
||||||
|
--strategy=<strategy>::
|
||||||
|
Use the given merge strategy. Should only be used once.
|
||||||
|
See the MERGE STRATEGIES section in linkgit:git-merge[1]
|
||||||
|
for details.
|
||||||
|
|
||||||
|
-X<option>::
|
||||||
|
--strategy-option=<option>::
|
||||||
|
Pass the merge strategy-specific option through to the
|
||||||
|
merge strategy. See linkgit:git-merge[1] for details.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
git revert HEAD~3::
|
git revert HEAD~3::
|
||||||
|
@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1)
|
|||||||
die("git write-tree failed to write a tree");
|
die("git write-tree failed to write a tree");
|
||||||
}
|
}
|
||||||
|
|
||||||
int try_merge_command(const char *strategy, struct commit_list *common,
|
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||||
|
const char **xopts, struct commit_list *common,
|
||||||
const char *head_arg, struct commit_list *remotes)
|
const char *head_arg, struct commit_list *remotes)
|
||||||
{
|
{
|
||||||
const char **args;
|
const char **args;
|
||||||
@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
|||||||
rollback_lock_file(lock);
|
rollback_lock_file(lock);
|
||||||
return clean ? 0 : 1;
|
return clean ? 0 : 1;
|
||||||
} else {
|
} else {
|
||||||
return try_merge_command(strategy, common, head_arg, remoteheads);
|
return try_merge_command(strategy, xopts_nr, xopts,
|
||||||
|
common, head_arg, remoteheads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,11 @@ static const char **commit_argv;
|
|||||||
static int allow_rerere_auto;
|
static int allow_rerere_auto;
|
||||||
|
|
||||||
static const char *me;
|
static const char *me;
|
||||||
|
|
||||||
|
/* Merge strategy. */
|
||||||
static const char *strategy;
|
static const char *strategy;
|
||||||
|
static const char **xopts;
|
||||||
|
static size_t xopts_nr, xopts_alloc;
|
||||||
|
|
||||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||||
|
|
||||||
@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void)
|
|||||||
return action == REVERT ? revert_usage : cherry_pick_usage;
|
return action == REVERT ? revert_usage : cherry_pick_usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int option_parse_x(const struct option *opt,
|
||||||
|
const char *arg, int unset)
|
||||||
|
{
|
||||||
|
if (unset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
|
||||||
|
xopts[xopts_nr++] = xstrdup(arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_args(int argc, const char **argv)
|
static void parse_args(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char * const * usage_str = revert_or_cherry_pick_usage();
|
const char * const * usage_str = revert_or_cherry_pick_usage();
|
||||||
@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv)
|
|||||||
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
|
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
|
||||||
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
|
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
|
||||||
OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
|
OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
|
||||||
|
OPT_CALLBACK('X', "strategy-option", &xopts, "option",
|
||||||
|
"option for merge strategy", option_parse_x),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
|||||||
struct merge_options o;
|
struct merge_options o;
|
||||||
struct tree *result, *next_tree, *base_tree, *head_tree;
|
struct tree *result, *next_tree, *base_tree, *head_tree;
|
||||||
int clean, index_fd;
|
int clean, index_fd;
|
||||||
|
const char **xopt;
|
||||||
static struct lock_file index_lock;
|
static struct lock_file index_lock;
|
||||||
|
|
||||||
index_fd = hold_locked_index(&index_lock, 1);
|
index_fd = hold_locked_index(&index_lock, 1);
|
||||||
|
|
||||||
read_cache();
|
read_cache();
|
||||||
|
|
||||||
/*
|
|
||||||
* NEEDSWORK: cherry-picking between branches with
|
|
||||||
* different end-of-line normalization is a pain;
|
|
||||||
* plumb in an option to set o.renormalize?
|
|
||||||
* (or better: arbitrary -X options)
|
|
||||||
*/
|
|
||||||
init_merge_options(&o);
|
init_merge_options(&o);
|
||||||
o.ancestor = base ? base_label : "(empty tree)";
|
o.ancestor = base ? base_label : "(empty tree)";
|
||||||
o.branch1 = "HEAD";
|
o.branch1 = "HEAD";
|
||||||
@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
|||||||
next_tree = next ? next->tree : empty_tree();
|
next_tree = next ? next->tree : empty_tree();
|
||||||
base_tree = base ? base->tree : empty_tree();
|
base_tree = base ? base->tree : empty_tree();
|
||||||
|
|
||||||
|
for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
|
||||||
|
parse_merge_opt(&o, *xopt);
|
||||||
|
|
||||||
clean = merge_trees(&o,
|
clean = merge_trees(&o,
|
||||||
head_tree,
|
head_tree,
|
||||||
next_tree, base_tree, &result);
|
next_tree, base_tree, &result);
|
||||||
@ -503,7 +518,7 @@ static int do_pick_commit(void)
|
|||||||
|
|
||||||
commit_list_insert(base, &common);
|
commit_list_insert(base, &common);
|
||||||
commit_list_insert(next, &remotes);
|
commit_list_insert(next, &remotes);
|
||||||
res = try_merge_command(strategy, common,
|
res = try_merge_command(strategy, xopts_nr, xopts, common,
|
||||||
sha1_to_hex(head), remotes);
|
sha1_to_hex(head), remotes);
|
||||||
free_commit_list(common);
|
free_commit_list(common);
|
||||||
free_commit_list(remotes);
|
free_commit_list(remotes);
|
||||||
|
@ -26,6 +26,7 @@ require_work_tree
|
|||||||
cd_to_toplevel
|
cd_to_toplevel
|
||||||
|
|
||||||
no_commit=
|
no_commit=
|
||||||
|
xopt=
|
||||||
while case "$#" in 0) break ;; esac
|
while case "$#" in 0) break ;; esac
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -44,6 +45,16 @@ do
|
|||||||
-x|--i-really-want-to-expose-my-private-commit-object-name)
|
-x|--i-really-want-to-expose-my-private-commit-object-name)
|
||||||
replay=
|
replay=
|
||||||
;;
|
;;
|
||||||
|
-X?*)
|
||||||
|
xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
|
||||||
|
;;
|
||||||
|
--strategy-option=*)
|
||||||
|
xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
|
||||||
|
;;
|
||||||
|
-X|--strategy-option)
|
||||||
|
shift
|
||||||
|
xopt="$xopt$(git rev-parse --sq-quote "--$1")"
|
||||||
|
;;
|
||||||
-*)
|
-*)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
@ -159,7 +170,7 @@ export GITHEAD_$head GITHEAD_$next
|
|||||||
# and $prev on top of us (when reverting), or the change between
|
# and $prev on top of us (when reverting), or the change between
|
||||||
# $prev and $commit on top of us (when cherry-picking or replaying).
|
# $prev and $commit on top of us (when cherry-picking or replaying).
|
||||||
|
|
||||||
git-merge-recursive $base -- $head $next &&
|
eval "git merge-recursive $xopt $base -- $head $next" &&
|
||||||
result=$(git-write-tree 2>/dev/null) || {
|
result=$(git-write-tree 2>/dev/null) || {
|
||||||
mv -f .msg "$GIT_DIR/MERGE_MSG"
|
mv -f .msg "$GIT_DIR/MERGE_MSG"
|
||||||
{
|
{
|
||||||
|
@ -57,6 +57,8 @@ struct tree *write_tree_from_memory(struct merge_options *o);
|
|||||||
int parse_merge_opt(struct merge_options *out, const char *s);
|
int parse_merge_opt(struct merge_options *out, const char *s);
|
||||||
|
|
||||||
/* builtin/merge.c */
|
/* builtin/merge.c */
|
||||||
int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
|
int try_merge_command(const char *strategy, size_t xopts_nr,
|
||||||
|
const char **xopts, struct commit_list *common,
|
||||||
|
const char *head_arg, struct commit_list *remotes);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -107,6 +107,20 @@ test_expect_success '--ignore-space-change makes merge succeed' '
|
|||||||
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
|
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'naive cherry-pick fails' '
|
||||||
|
git read-tree --reset -u HEAD &&
|
||||||
|
test_must_fail git cherry-pick --no-commit remote &&
|
||||||
|
git read-tree --reset -u HEAD &&
|
||||||
|
test_must_fail git cherry-pick remote &&
|
||||||
|
test_must_fail git update-index --refresh &&
|
||||||
|
grep "<<<<<<" text.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
|
||||||
|
git read-tree --reset -u HEAD &&
|
||||||
|
git cherry-pick --no-commit -Xignore-space-change remote
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '--ignore-space-change: our w/s-only change wins' '
|
test_expect_success '--ignore-space-change: our w/s-only change wins' '
|
||||||
q_to_cr <<-\EOF >expected &&
|
q_to_cr <<-\EOF >expected &&
|
||||||
justice and holiness and is the nurse of his age and theQ
|
justice and holiness and is the nurse of his age and theQ
|
||||||
|
Loading…
Reference in New Issue
Block a user