diff options
author | Junio C Hamano <gitster@pobox.com> | 2010-03-28 21:52:28 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2010-03-28 21:52:28 -0700 |
commit | 99f5b0845ac53f9f6e845fe4401e68fcef154690 (patch) | |
tree | 5647dacc82fa90cb8de24ec4f4b9abea249707cf | |
parent | 3b37d9c17efd199a237435f7d8573008f6aa68c1 (diff) | |
parent | 28db756feead84929cdfaaee8bccd301384daab4 (diff) | |
download | git-99f5b0845ac53f9f6e845fe4401e68fcef154690.tar.gz git-99f5b0845ac53f9f6e845fe4401e68fcef154690.tar.xz |
Merge branch 'cc/cherry-pick-ff'
* cc/cherry-pick-ff:
revert: fix tiny memory leak in cherry-pick --ff
rebase -i: use new --ff cherry-pick option
Documentation: describe new cherry-pick --ff option
cherry-pick: add tests for new --ff option
revert: add --ff option to allow fast forward when cherry-picking
builtin/merge: make checkout_fast_forward() non static
parse-options: add parse_options_concat() to concat options
-rw-r--r-- | Documentation/git-cherry-pick.txt | 6 | ||||
-rw-r--r-- | builtin/merge.c | 2 | ||||
-rw-r--r-- | builtin/revert.c | 46 | ||||
-rw-r--r-- | cache.h | 3 | ||||
-rwxr-xr-x | git-rebase--interactive.sh | 15 | ||||
-rw-r--r-- | parse-options.c | 15 | ||||
-rw-r--r-- | parse-options.h | 1 | ||||
-rwxr-xr-x | t/t3506-cherry-pick-ff.sh | 98 |
8 files changed, 168 insertions, 18 deletions
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 78f4714da..d71607a85 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] <commit> +'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit> 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 ------ diff --git a/builtin/merge.c b/builtin/merge.c index 3aaec7bed..c04306684 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 eff52687a..9a3c14c32 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]; @@ -251,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; @@ -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)); @@ -329,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); @@ -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, ' '); @@ -1058,4 +1058,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/git-rebase--interactive.sh b/git-rebase--interactive.sh index 8a2771f1f..1d116bfae 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -230,8 +230,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 @@ -240,16 +240,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 () { diff --git a/parse-options.c b/parse-options.c index c83035d01..8546d8526 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 9429f7e36..7581e931d 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); diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh new file mode 100755 index 000000000..e17ae712b --- /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 |