Christian Couder <chriscool@xxxxxxxxxxxxx> writes: > I tried to use the checkout_fast_forward() function from builtin/merge.c but > unfortunately it doesn't work. It gives an error like that in the tests : > > error: Your local changes to 'file1' would be overwritten by merge. Aborting. > Please, commit your changes or stash them before you can merge. > > and I don't really understand why. (Though I didn't spend a lot of time on > this.) Shouldn't it be something like this? The patch is still unnecessarily large because I wanted to share options between revert and cherry-pick while giving --ff only to cherry-pick, but I don't understand why it needs a nine patch series worth of code churn. builtin-merge.c | 2 +- builtin-revert.c | 44 +++++++++++++++++++++++++++++++++++++++++--- cache.h | 3 +++ parse-options.c | 15 +++++++++++++++ parse-options.h | 1 + 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/builtin-merge.c b/builtin-merge.c index 3aaec7b..c043066 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -667,7 +667,7 @@ static int count_unmerged_entries(void) return ret; } -static int checkout_fast_forward(unsigned char *head, unsigned char *remote) +int checkout_fast_forward(const unsigned char *head, const unsigned char *remote) { struct tree *trees[MAX_UNPACK_TREES]; struct unpack_trees_options opts; diff --git a/builtin-revert.c b/builtin-revert.c index eff5268..bfe75c8 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -13,6 +13,7 @@ #include "revision.h" #include "rerere.h" #include "merge-recursive.h" +#include "refs.h" /* * This implements the builtins revert and cherry-pick. @@ -35,7 +36,7 @@ static const char * const cherry_pick_usage[] = { NULL }; -static int edit, no_replay, no_commit, mainline, signoff; +static int edit, no_replay, no_commit, mainline, signoff, allow_ff; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; static const char *commit_name; @@ -60,8 +61,19 @@ static void parse_args(int argc, const char **argv) OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_END(), + OPT_END(), + OPT_END(), }; + if (action == CHERRY_PICK) { + struct option cp_extra[] = { + OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"), + OPT_END(), + }; + if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra)) + die("program error"); + } + if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1) usage_with_options(usage_str, options); @@ -244,6 +256,17 @@ static NORETURN void die_dirty_index(const char *me) } } +static int fast_forward_to(const unsigned char *to, const unsigned char *from) +{ + struct ref_lock *ref_lock; + + read_cache(); + if (checkout_fast_forward(from, to)) + exit(1); /* the callee should have complained already */ + ref_lock = lock_any_ref_for_update("HEAD", from, 0); + return write_ref_sha1(ref_lock, to, "cherry-pick"); +} + static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; @@ -265,6 +288,17 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (action == REVERT && !no_replay) die("revert is incompatible with replay"); + if (action == CHERRY_PICK && allow_ff) { + if (signoff) + die("cherry-pick --ff cannot be used with --signoff"); + if (no_commit) + die("cherry-pick --ff cannot be used with --no-commit"); + if (no_replay) + die("cherry-pick --ff cannot be used with -x"); + if (edit) + die("cherry-pick --ff cannot be used with --edit"); + } + if (read_cache() < 0) die("git %s: failed to read the index", me); if (no_commit) { @@ -284,8 +318,6 @@ static int revert_or_cherry_pick(int argc, const char **argv) } discard_cache(); - index_fd = hold_locked_index(&index_lock, 1); - if (!commit->parents) { if (action == REVERT) die ("Cannot revert a root commit"); @@ -314,6 +346,10 @@ static int revert_or_cherry_pick(int argc, const char **argv) else parent = commit->parents->item; + if (action == CHERRY_PICK && allow_ff + && !hashcmp(parent->object.sha1, head)) + return fast_forward_to(commit->object.sha1, head); + if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); @@ -343,6 +379,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) oneline = get_oneline(message); + index_fd = hold_locked_index(&index_lock, 1); + if (action == REVERT) { char *oneline_body = strchr(oneline, ' '); diff --git a/cache.h b/cache.h index d454b7e..7272160 100644 --- a/cache.h +++ b/cache.h @@ -1040,4 +1040,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); +/* builtin-merge.c */ +int checkout_fast_forward(const unsigned char *from, const unsigned char *to); + #endif /* CACHE_H */ diff --git a/parse-options.c b/parse-options.c index c83035d..8546d85 100644 --- a/parse-options.c +++ b/parse-options.c @@ -659,3 +659,18 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset) *target = unset ? 2 : 1; return 0; } + +int parse_options_concat(struct option *dst, size_t dst_size, struct option *src) +{ + int i, j; + + for (i = 0; i < dst_size; i++) + if (dst[i].type == OPTION_END) + break; + for (j = 0; i < dst_size; i++, j++) { + dst[i] = src[j]; + if (src[j].type == OPTION_END) + return 0; + } + return -1; +} diff --git a/parse-options.h b/parse-options.h index 9429f7e..7581e93 100644 --- a/parse-options.h +++ b/parse-options.h @@ -187,6 +187,7 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx, extern int parse_options_end(struct parse_opt_ctx_t *ctx); +extern int parse_options_concat(struct option *dst, size_t, struct option *src); /*----- some often used options -----*/ extern int parse_opt_abbrev_cb(const struct option *, const char *, int); -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html