Skip to content

Commit 67ac1e1

Browse files
jrngitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 73e7b2e commit 67ac1e1

File tree

7 files changed

+97
-11
lines changed

7 files changed

+97
-11
lines changed

Documentation/git-cherry-pick.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ effect to your index in a row.
7979
cherry-pick'ed commit, then a fast forward to this commit will
8080
be performed.
8181

82+
--strategy=<strategy>::
83+
Use the given merge strategy. Should only be used once.
84+
See the MERGE STRATEGIES section in linkgit:git-merge[1]
85+
for details.
86+
87+
-X<option>::
88+
--strategy-option=<option>::
89+
Pass the merge strategy-specific option through to the
90+
merge strategy. See linkgit:git-merge[1] for details.
91+
8292
EXAMPLES
8393
--------
8494
git cherry-pick master::
@@ -120,6 +130,28 @@ git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
120130
so the result can be inspected and made into a single new
121131
commit if suitable.
122132

133+
The following sequence attempts to backport a patch, bails out because
134+
the code the patch applies to has changed too much, and then tries
135+
again, this time exercising more care about matching up context lines.
136+
137+
------------
138+
$ git cherry-pick topic^ <1>
139+
$ git diff <2>
140+
$ git reset --merge ORIG_HEAD <3>
141+
$ git cherry-pick -Xpatience topic^ <4>
142+
------------
143+
<1> apply the change that would be shown by `git show topic^`.
144+
In this example, the patch does not apply cleanly, so
145+
information about the conflict is written to the index and
146+
working tree and no new commit results.
147+
<2> summarize changes to be reconciled
148+
<3> cancel the cherry-pick. In other words, return to the
149+
pre-cherry-pick state, preserving any local modifications you had in
150+
the working tree.
151+
<4> try to apply the change introduced by `topic^` again,
152+
spending extra time to avoid mistakes based on incorrectly matching
153+
context lines.
154+
123155
Author
124156
------
125157
Written by Junio C Hamano <[email protected]>

Documentation/git-revert.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ effect to your index in a row.
8080
--signoff::
8181
Add Signed-off-by line at the end of the commit message.
8282

83+
--strategy=<strategy>::
84+
Use the given merge strategy. Should only be used once.
85+
See the MERGE STRATEGIES section in linkgit:git-merge[1]
86+
for details.
87+
88+
-X<option>::
89+
--strategy-option=<option>::
90+
Pass the merge strategy-specific option through to the
91+
merge strategy. See linkgit:git-merge[1] for details.
92+
8393
EXAMPLES
8494
--------
8595
git revert HEAD~3::

builtin/merge.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1)
582582
die("git write-tree failed to write a tree");
583583
}
584584

585-
int try_merge_command(const char *strategy, struct commit_list *common,
585+
int try_merge_command(const char *strategy, size_t xopts_nr,
586+
const char **xopts, struct commit_list *common,
586587
const char *head_arg, struct commit_list *remotes)
587588
{
588589
const char **args;
@@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
680681
rollback_lock_file(lock);
681682
return clean ? 0 : 1;
682683
} else {
683-
return try_merge_command(strategy, common, head_arg, remoteheads);
684+
return try_merge_command(strategy, xopts_nr, xopts,
685+
common, head_arg, remoteheads);
684686
}
685687
}
686688

builtin/revert.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ static const char **commit_argv;
4444
static int allow_rerere_auto;
4545

4646
static const char *me;
47+
48+
/* Merge strategy. */
4749
static const char *strategy;
50+
static const char **xopts;
51+
static size_t xopts_nr, xopts_alloc;
4852

4953
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
5054

@@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void)
5559
return action == REVERT ? revert_usage : cherry_pick_usage;
5660
}
5761

62+
static int option_parse_x(const struct option *opt,
63+
const char *arg, int unset)
64+
{
65+
if (unset)
66+
return 0;
67+
68+
ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
69+
xopts[xopts_nr++] = xstrdup(arg);
70+
return 0;
71+
}
72+
5873
static void parse_args(int argc, const char **argv)
5974
{
6075
const char * const * usage_str = revert_or_cherry_pick_usage();
@@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv)
6782
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
6883
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
6984
OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
85+
OPT_CALLBACK('X', "strategy-option", &xopts, "option",
86+
"option for merge strategy", option_parse_x),
7087
OPT_END(),
7188
OPT_END(),
7289
OPT_END(),
@@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
311328
struct merge_options o;
312329
struct tree *result, *next_tree, *base_tree, *head_tree;
313330
int clean, index_fd;
331+
const char **xopt;
314332
static struct lock_file index_lock;
315333

316334
index_fd = hold_locked_index(&index_lock, 1);
317335

318336
read_cache();
319337

320-
/*
321-
* NEEDSWORK: cherry-picking between branches with
322-
* different end-of-line normalization is a pain;
323-
* plumb in an option to set o.renormalize?
324-
* (or better: arbitrary -X options)
325-
*/
326338
init_merge_options(&o);
327339
o.ancestor = base ? base_label : "(empty tree)";
328340
o.branch1 = "HEAD";
@@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
332344
next_tree = next ? next->tree : empty_tree();
333345
base_tree = base ? base->tree : empty_tree();
334346

347+
for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
348+
parse_merge_opt(&o, *xopt);
349+
335350
clean = merge_trees(&o,
336351
head_tree,
337352
next_tree, base_tree, &result);
@@ -503,7 +518,7 @@ static int do_pick_commit(void)
503518

504519
commit_list_insert(base, &common);
505520
commit_list_insert(next, &remotes);
506-
res = try_merge_command(strategy, common,
521+
res = try_merge_command(strategy, xopts_nr, xopts, common,
507522
sha1_to_hex(head), remotes);
508523
free_commit_list(common);
509524
free_commit_list(remotes);

contrib/examples/git-revert.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require_work_tree
2626
cd_to_toplevel
2727

2828
no_commit=
29+
xopt=
2930
while case "$#" in 0) break ;; esac
3031
do
3132
case "$1" in
@@ -44,6 +45,16 @@ do
4445
-x|--i-really-want-to-expose-my-private-commit-object-name)
4546
replay=
4647
;;
48+
-X?*)
49+
xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
50+
;;
51+
--strategy-option=*)
52+
xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
53+
;;
54+
-X|--strategy-option)
55+
shift
56+
xopt="$xopt$(git rev-parse --sq-quote "--$1")"
57+
;;
4758
-*)
4859
usage
4960
;;
@@ -159,7 +170,7 @@ export GITHEAD_$head GITHEAD_$next
159170
# and $prev on top of us (when reverting), or the change between
160171
# $prev and $commit on top of us (when cherry-picking or replaying).
161172

162-
git-merge-recursive $base -- $head $next &&
173+
eval "git merge-recursive $xopt $base -- $head $next" &&
163174
result=$(git-write-tree 2>/dev/null) || {
164175
mv -f .msg "$GIT_DIR/MERGE_MSG"
165176
{

merge-recursive.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ struct tree *write_tree_from_memory(struct merge_options *o);
5757
int parse_merge_opt(struct merge_options *out, const char *s);
5858

5959
/* builtin/merge.c */
60-
int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
60+
int try_merge_command(const char *strategy, size_t xopts_nr,
61+
const char **xopts, struct commit_list *common,
62+
const char *head_arg, struct commit_list *remotes);
6163

6264
#endif

t/t3032-merge-recursive-options.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ test_expect_success '--ignore-space-change makes merge succeed' '
107107
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
108108
'
109109

110+
test_expect_success 'naive cherry-pick fails' '
111+
git read-tree --reset -u HEAD &&
112+
test_must_fail git cherry-pick --no-commit remote &&
113+
git read-tree --reset -u HEAD &&
114+
test_must_fail git cherry-pick remote &&
115+
test_must_fail git update-index --refresh &&
116+
grep "<<<<<<" text.txt
117+
'
118+
119+
test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
120+
git read-tree --reset -u HEAD &&
121+
git cherry-pick --no-commit -Xignore-space-change remote
122+
'
123+
110124
test_expect_success '--ignore-space-change: our w/s-only change wins' '
111125
q_to_cr <<-\EOF >expected &&
112126
justice and holiness and is the nurse of his age and theQ

0 commit comments

Comments
 (0)