From 8b74d75cd26d28d38d2ff88267585fc308b89cef Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 6 Mar 2010 21:34:40 +0100 Subject: parse-options: add parse_options_concat() to concat options Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- parse-options.c | 15 +++++++++++++++ parse-options.h | 1 + 2 files changed, 16 insertions(+) diff --git a/parse-options.c b/parse-options.c index c83035d013..8546d8526f 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 9429f7e361..7581e931da 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); -- cgit v1.2.3 From cac42b266a5868fc54b2e54986fa2689676ec447 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 6 Mar 2010 21:34:41 +0100 Subject: builtin/merge: make checkout_fast_forward() non static and also export it in "cache.h". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-merge.c | 2 +- cache.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin-merge.c b/builtin-merge.c index 3aaec7bed7..c043066845 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/cache.h b/cache.h index d454b7e686..3cce9077cc 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 */ -- cgit v1.2.3 From c62f6ec341b1a768be7c9adf84d07c5b3b113a81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 6 Mar 2010 21:34:42 +0100 Subject: revert: add --ff option to allow fast forward when cherry-picking As "git merge" fast forwards if possible, it seems sensible to have such a feature for "git cherry-pick" too, especially as it could be used in git-rebase--interactive.sh. Maybe this option could be made the default in the long run, with another --no-ff option to disable this default behavior, but that could make some scripts backward incompatible and/or that would require testing if some GIT_AUTHOR_* environment variables are set. So we don't do that for now. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-revert.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/builtin-revert.c b/builtin-revert.c index eff52687a8..476f41e760 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 (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,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) else parent = commit->parents->item; + if (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 +378,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, ' '); -- cgit v1.2.3 From b5c1a28b4c7d7e1324fa3e88ae03801d4fe9f6bb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 6 Mar 2010 21:34:43 +0100 Subject: cherry-pick: add tests for new --ff option Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t3506-cherry-pick-ff.sh | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100755 t/t3506-cherry-pick-ff.sh diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh new file mode 100755 index 0000000000..e17ae712b1 --- /dev/null +++ b/t/t3506-cherry-pick-ff.sh @@ -0,0 +1,98 @@ +#!/bin/sh + +test_description='test cherry-picking with --ff option' + +. ./test-lib.sh + +test_expect_success setup ' + echo first > file1 && + git add file1 && + test_tick && + git commit -m "first" && + git tag first && + + git checkout -b other && + echo second >> file1 && + git add file1 && + test_tick && + git commit -m "second" && + git tag second +' + +test_expect_success 'cherry-pick using --ff fast forwards' ' + git checkout master && + git reset --hard first && + test_tick && + git cherry-pick --ff second && + test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify second)" +' + +test_expect_success 'cherry-pick not using --ff does not fast forwards' ' + git checkout master && + git reset --hard first && + test_tick && + git cherry-pick second && + test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify second)" +' + +# +# We setup the following graph: +# +# B---C +# / / +# first---A +# +# (This has been taken from t3502-cherry-pick-merge.sh) +# +test_expect_success 'merge setup' ' + git checkout master && + git reset --hard first && + echo new line >A && + git add A && + test_tick && + git commit -m "add line to A" A && + git tag A && + git checkout -b side first && + echo new line >B && + git add B && + test_tick && + git commit -m "add line to B" B && + git tag B && + git checkout master && + git merge side && + git tag C && + git checkout -b new A +' + +test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' ' + git reset --hard A -- && + test_must_fail git cherry-pick --ff -m 1 B && + git diff --exit-code A -- +' + +test_expect_success 'cherry pick a merge with --ff but without -m should fail' ' + git reset --hard A -- && + test_must_fail git cherry-pick --ff C && + git diff --exit-code A -- +' + +test_expect_success 'cherry pick with --ff a merge (1)' ' + git reset --hard A -- && + git cherry-pick --ff -m 1 C && + git diff --exit-code C && + test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)" +' + +test_expect_success 'cherry pick with --ff a merge (2)' ' + git reset --hard B -- && + git cherry-pick --ff -m 2 C && + git diff --exit-code C && + test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)" +' + +test_expect_success 'cherry pick a merge relative to nonexistent parent with --ff should fail' ' + git reset --hard B -- && + test_must_fail git cherry-pick --ff -m 3 C +' + +test_done -- cgit v1.2.3 From ab7e63e85f2307447f8fc6679e1f355385881236 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 6 Mar 2010 21:34:44 +0100 Subject: Documentation: describe new cherry-pick --ff option Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-cherry-pick.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 78f4714da0..d71607a85d 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] +'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] DESCRIPTION ----------- @@ -70,6 +70,10 @@ effect to your index in a row. --signoff:: Add Signed-off-by line at the end of the commit message. +--ff:: + If the current HEAD is the same as the parent of the + cherry-pick'ed commit, then a fast forward to this commit will + be performed. Author ------ -- cgit v1.2.3 From 8e75abfd8dc6649d800b8877304eaa0fd404c10f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 6 Mar 2010 21:34:46 +0100 Subject: rebase -i: use new --ff cherry-pick option This simplifies rebase -i a little bit. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3e4fd1456f..92d19f5903 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -222,8 +222,8 @@ do_with_author () { } pick_one () { - no_ff= - case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac + ff=--ff + case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return @@ -232,16 +232,7 @@ pick_one () { output git cherry-pick "$@" return fi - parent_sha1=$(git rev-parse --verify $sha1^) || - die "Could not get the parent of $sha1" - current_sha1=$(git rev-parse --verify HEAD) - if test -z "$no_ff" && test "$current_sha1" = "$parent_sha1" - then - output git reset --hard $sha1 - output warn Fast-forward to $(git rev-parse --short $sha1) - else - output git cherry-pick "$@" - fi + output git cherry-pick $ff "$@" } pick_one_preserving_merges () { -- cgit v1.2.3 From 28db756feead84929cdfaaee8bccd301384daab4 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sat, 20 Mar 2010 21:01:20 -0500 Subject: revert: fix tiny memory leak in cherry-pick --ff We forgot to free defmsg when returning early for a fast-forward. Fixing this should reduce noise during test suite runs with valgrind. More importantly, once cherry-pick learns to pick multiple commits, the amount of memory leaked would start to add up. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin-revert.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-revert.c b/builtin-revert.c index 476f41e760..9a3c14c329 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -274,7 +274,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) int i, index_fd, clean; char *oneline, *reencoded_message = NULL; const char *message, *encoding; - char *defmsg = git_pathdup("MERGE_MSG"); + char *defmsg = NULL; struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; static struct lock_file index_lock; @@ -364,6 +364,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) * reverse of it if we are revert. */ + defmsg = git_pathdup("MERGE_MSG"); msg_fd = hold_lock_file_for_update(&msg_file, defmsg, LOCK_DIE_ON_ERROR); -- cgit v1.2.3