Merge branch 'jn/maint-sequencer-fixes'
* jn/maint-sequencer-fixes: revert: stop creating and removing sequencer-old directory Revert "reset: Make reset remove the sequencer state" revert: do not remove state until sequence is finished revert: allow single-pick in the middle of cherry-pick sequence revert: pass around rev-list args in already-parsed form revert: allow cherry-pick --continue to commit before resuming revert: give --continue handling its own functionmaint
commit
b8fc5abd73
2
branch.c
2
branch.c
|
@ -3,7 +3,6 @@
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "sequencer.h"
|
|
||||||
|
|
||||||
struct tracking {
|
struct tracking {
|
||||||
struct refspec spec;
|
struct refspec spec;
|
||||||
|
@ -280,5 +279,4 @@ void remove_branch_state(void)
|
||||||
unlink(git_path("MERGE_MSG"));
|
unlink(git_path("MERGE_MSG"));
|
||||||
unlink(git_path("MERGE_MODE"));
|
unlink(git_path("MERGE_MODE"));
|
||||||
unlink(git_path("SQUASH_MSG"));
|
unlink(git_path("SQUASH_MSG"));
|
||||||
remove_sequencer_state(0);
|
|
||||||
}
|
}
|
||||||
|
|
138
builtin/revert.c
138
builtin/revert.c
|
@ -60,13 +60,14 @@ struct replay_opts {
|
||||||
int allow_rerere_auto;
|
int allow_rerere_auto;
|
||||||
|
|
||||||
int mainline;
|
int mainline;
|
||||||
int commit_argc;
|
|
||||||
const char **commit_argv;
|
|
||||||
|
|
||||||
/* Merge strategy */
|
/* Merge strategy */
|
||||||
const char *strategy;
|
const char *strategy;
|
||||||
const char **xopts;
|
const char **xopts;
|
||||||
size_t xopts_nr, xopts_alloc;
|
size_t xopts_nr, xopts_alloc;
|
||||||
|
|
||||||
|
/* Only used by REPLAY_NONE */
|
||||||
|
struct rev_info *revs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||||
|
@ -169,9 +170,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||||
die(_("program error"));
|
die(_("program error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
|
argc = parse_options(argc, argv, NULL, options, usage_str,
|
||||||
PARSE_OPT_KEEP_ARGV0 |
|
PARSE_OPT_KEEP_ARGV0 |
|
||||||
PARSE_OPT_KEEP_UNKNOWN);
|
PARSE_OPT_KEEP_UNKNOWN);
|
||||||
|
|
||||||
/* Check for incompatible subcommands */
|
/* Check for incompatible subcommands */
|
||||||
verify_opt_mutually_compatible(me,
|
verify_opt_mutually_compatible(me,
|
||||||
|
@ -213,9 +214,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (opts->commit_argc < 2)
|
|
||||||
usage_with_options(usage_str, options);
|
|
||||||
|
|
||||||
if (opts->allow_ff)
|
if (opts->allow_ff)
|
||||||
verify_opt_compatible(me, "--ff",
|
verify_opt_compatible(me, "--ff",
|
||||||
"--signoff", opts->signoff,
|
"--signoff", opts->signoff,
|
||||||
|
@ -223,7 +221,20 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||||
"-x", opts->record_origin,
|
"-x", opts->record_origin,
|
||||||
"--edit", opts->edit,
|
"--edit", opts->edit,
|
||||||
NULL);
|
NULL);
|
||||||
opts->commit_argv = argv;
|
|
||||||
|
if (opts->subcommand != REPLAY_NONE) {
|
||||||
|
opts->revs = NULL;
|
||||||
|
} else {
|
||||||
|
opts->revs = xmalloc(sizeof(*opts->revs));
|
||||||
|
init_revisions(opts->revs, NULL);
|
||||||
|
opts->revs->no_walk = 1;
|
||||||
|
if (argc < 2)
|
||||||
|
usage_with_options(usage_str, options);
|
||||||
|
argc = setup_revisions(argc, argv, opts->revs, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
usage_with_options(usage_str, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct commit_message {
|
struct commit_message {
|
||||||
|
@ -631,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
|
static void prepare_revs(struct replay_opts *opts)
|
||||||
{
|
{
|
||||||
int argc;
|
|
||||||
|
|
||||||
init_revisions(revs, NULL);
|
|
||||||
revs->no_walk = 1;
|
|
||||||
if (opts->action != REVERT)
|
if (opts->action != REVERT)
|
||||||
revs->reverse = 1;
|
opts->revs->reverse ^= 1;
|
||||||
|
|
||||||
argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
|
if (prepare_revision_walk(opts->revs))
|
||||||
if (argc > 1)
|
|
||||||
usage(*revert_or_cherry_pick_usage(opts));
|
|
||||||
|
|
||||||
if (prepare_revision_walk(revs))
|
|
||||||
die(_("revision walk setup failed"));
|
die(_("revision walk setup failed"));
|
||||||
|
|
||||||
if (!revs->commits)
|
if (!opts->revs->commits)
|
||||||
die(_("empty commit set passed"));
|
die(_("empty commit set passed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,14 +847,13 @@ static void read_populate_opts(struct replay_opts **opts_ptr)
|
||||||
static void walk_revs_populate_todo(struct commit_list **todo_list,
|
static void walk_revs_populate_todo(struct commit_list **todo_list,
|
||||||
struct replay_opts *opts)
|
struct replay_opts *opts)
|
||||||
{
|
{
|
||||||
struct rev_info revs;
|
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
struct commit_list **next;
|
struct commit_list **next;
|
||||||
|
|
||||||
prepare_revs(&revs, opts);
|
prepare_revs(opts);
|
||||||
|
|
||||||
next = todo_list;
|
next = todo_list;
|
||||||
while ((commit = get_revision(&revs)))
|
while ((commit = get_revision(opts->revs)))
|
||||||
next = commit_list_append(commit, next);
|
next = commit_list_append(commit, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,7 +944,7 @@ static int sequencer_rollback(struct replay_opts *opts)
|
||||||
}
|
}
|
||||||
if (reset_for_rollback(sha1))
|
if (reset_for_rollback(sha1))
|
||||||
goto fail;
|
goto fail;
|
||||||
remove_sequencer_state(1);
|
remove_sequencer_state();
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
|
@ -1016,33 +1018,64 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
|
||||||
for (cur = todo_list; cur; cur = cur->next) {
|
for (cur = todo_list; cur; cur = cur->next) {
|
||||||
save_todo(cur, opts);
|
save_todo(cur, opts);
|
||||||
res = do_pick_commit(cur->item, opts);
|
res = do_pick_commit(cur->item, opts);
|
||||||
if (res) {
|
if (res)
|
||||||
if (!cur->next)
|
|
||||||
/*
|
|
||||||
* An error was encountered while
|
|
||||||
* picking the last commit; the
|
|
||||||
* sequencer state is useless now --
|
|
||||||
* the user simply needs to resolve
|
|
||||||
* the conflict and commit
|
|
||||||
*/
|
|
||||||
remove_sequencer_state(0);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sequence of picks finished successfully; cleanup by
|
* Sequence of picks finished successfully; cleanup by
|
||||||
* removing the .git/sequencer directory
|
* removing the .git/sequencer directory
|
||||||
*/
|
*/
|
||||||
remove_sequencer_state(1);
|
remove_sequencer_state();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int continue_single_pick(void)
|
||||||
|
{
|
||||||
|
const char *argv[] = { "commit", NULL };
|
||||||
|
|
||||||
|
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
|
||||||
|
!file_exists(git_path("REVERT_HEAD")))
|
||||||
|
return error(_("no cherry-pick or revert in progress"));
|
||||||
|
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sequencer_continue(struct replay_opts *opts)
|
||||||
|
{
|
||||||
|
struct commit_list *todo_list = NULL;
|
||||||
|
|
||||||
|
if (!file_exists(git_path(SEQ_TODO_FILE)))
|
||||||
|
return continue_single_pick();
|
||||||
|
read_populate_opts(&opts);
|
||||||
|
read_populate_todo(&todo_list, opts);
|
||||||
|
|
||||||
|
/* Verify that the conflict has been resolved */
|
||||||
|
if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
|
||||||
|
file_exists(git_path("REVERT_HEAD"))) {
|
||||||
|
int ret = continue_single_pick();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (index_differs_from("HEAD", 0))
|
||||||
|
return error_dirty_index(opts);
|
||||||
|
todo_list = todo_list->next;
|
||||||
|
return pick_commits(todo_list, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int single_pick(struct commit *cmit, struct replay_opts *opts)
|
||||||
|
{
|
||||||
|
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
|
||||||
|
return do_pick_commit(cmit, opts);
|
||||||
|
}
|
||||||
|
|
||||||
static int pick_revisions(struct replay_opts *opts)
|
static int pick_revisions(struct replay_opts *opts)
|
||||||
{
|
{
|
||||||
struct commit_list *todo_list = NULL;
|
struct commit_list *todo_list = NULL;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
|
|
||||||
|
if (opts->subcommand == REPLAY_NONE)
|
||||||
|
assert(opts->revs);
|
||||||
|
|
||||||
read_and_refresh_cache(opts);
|
read_and_refresh_cache(opts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1051,21 +1084,32 @@ static int pick_revisions(struct replay_opts *opts)
|
||||||
* one that is being continued
|
* one that is being continued
|
||||||
*/
|
*/
|
||||||
if (opts->subcommand == REPLAY_REMOVE_STATE) {
|
if (opts->subcommand == REPLAY_REMOVE_STATE) {
|
||||||
remove_sequencer_state(1);
|
remove_sequencer_state();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (opts->subcommand == REPLAY_ROLLBACK)
|
if (opts->subcommand == REPLAY_ROLLBACK)
|
||||||
return sequencer_rollback(opts);
|
return sequencer_rollback(opts);
|
||||||
if (opts->subcommand == REPLAY_CONTINUE) {
|
if (opts->subcommand == REPLAY_CONTINUE)
|
||||||
if (!file_exists(git_path(SEQ_TODO_FILE)))
|
return sequencer_continue(opts);
|
||||||
return error(_("No %s in progress"), action_name(opts));
|
|
||||||
read_populate_opts(&opts);
|
|
||||||
read_populate_todo(&todo_list, opts);
|
|
||||||
|
|
||||||
/* Verify that the conflict has been resolved */
|
/*
|
||||||
if (!index_differs_from("HEAD", 0))
|
* If we were called as "git cherry-pick <commit>", just
|
||||||
todo_list = todo_list->next;
|
* cherry-pick/revert it, set CHERRY_PICK_HEAD /
|
||||||
return pick_commits(todo_list, opts);
|
* REVERT_HEAD, and don't touch the sequencer state.
|
||||||
|
* This means it is possible to cherry-pick in the middle
|
||||||
|
* of a cherry-pick sequence.
|
||||||
|
*/
|
||||||
|
if (opts->revs->cmdline.nr == 1 &&
|
||||||
|
opts->revs->cmdline.rev->whence == REV_CMD_REV &&
|
||||||
|
opts->revs->no_walk &&
|
||||||
|
!opts->revs->cmdline.rev->flags) {
|
||||||
|
struct commit *cmit;
|
||||||
|
if (prepare_revision_walk(opts->revs))
|
||||||
|
die(_("revision walk setup failed"));
|
||||||
|
cmit = get_revision(opts->revs);
|
||||||
|
if (!cmit || get_revision(opts->revs))
|
||||||
|
die("BUG: expected exactly one commit from walk");
|
||||||
|
return single_pick(cmit, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
10
sequencer.c
10
sequencer.c
|
@ -3,17 +3,11 @@
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
|
||||||
void remove_sequencer_state(int aggressive)
|
void remove_sequencer_state(void)
|
||||||
{
|
{
|
||||||
struct strbuf seq_dir = STRBUF_INIT;
|
struct strbuf seq_dir = STRBUF_INIT;
|
||||||
struct strbuf seq_old_dir = STRBUF_INIT;
|
|
||||||
|
|
||||||
strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
|
strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
|
||||||
strbuf_addf(&seq_old_dir, "%s", git_path(SEQ_OLD_DIR));
|
remove_dir_recursively(&seq_dir, 0);
|
||||||
remove_dir_recursively(&seq_old_dir, 0);
|
|
||||||
rename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR));
|
|
||||||
if (aggressive)
|
|
||||||
remove_dir_recursively(&seq_old_dir, 0);
|
|
||||||
strbuf_release(&seq_dir);
|
strbuf_release(&seq_dir);
|
||||||
strbuf_release(&seq_old_dir);
|
|
||||||
}
|
}
|
||||||
|
|
12
sequencer.h
12
sequencer.h
|
@ -2,19 +2,11 @@
|
||||||
#define SEQUENCER_H
|
#define SEQUENCER_H
|
||||||
|
|
||||||
#define SEQ_DIR "sequencer"
|
#define SEQ_DIR "sequencer"
|
||||||
#define SEQ_OLD_DIR "sequencer-old"
|
|
||||||
#define SEQ_HEAD_FILE "sequencer/head"
|
#define SEQ_HEAD_FILE "sequencer/head"
|
||||||
#define SEQ_TODO_FILE "sequencer/todo"
|
#define SEQ_TODO_FILE "sequencer/todo"
|
||||||
#define SEQ_OPTS_FILE "sequencer/opts"
|
#define SEQ_OPTS_FILE "sequencer/opts"
|
||||||
|
|
||||||
/*
|
/* Removes SEQ_DIR. */
|
||||||
* Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring
|
extern void remove_sequencer_state(void);
|
||||||
* any errors. Intended to be used by 'git reset'.
|
|
||||||
*
|
|
||||||
* With the aggressive flag, it additionally removes SEQ_OLD_DIR,
|
|
||||||
* ignoring any errors. Inteded to be used by the sequencer's
|
|
||||||
* '--quit' subcommand.
|
|
||||||
*/
|
|
||||||
void remove_sequencer_state(int aggressive);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
test_description='Test cherry-pick continuation features
|
test_description='Test cherry-pick continuation features
|
||||||
|
|
||||||
|
+ conflicting: rewrites unrelated to conflicting
|
||||||
+ yetanotherpick: rewrites foo to e
|
+ yetanotherpick: rewrites foo to e
|
||||||
+ anotherpick: rewrites foo to d
|
+ anotherpick: rewrites foo to d
|
||||||
+ picked: rewrites foo to c
|
+ picked: rewrites foo to c
|
||||||
|
@ -27,6 +28,7 @@ test_cmp_rev () {
|
||||||
}
|
}
|
||||||
|
|
||||||
test_expect_success setup '
|
test_expect_success setup '
|
||||||
|
git config advice.detachedhead false
|
||||||
echo unrelated >unrelated &&
|
echo unrelated >unrelated &&
|
||||||
git add unrelated &&
|
git add unrelated &&
|
||||||
test_commit initial foo a &&
|
test_commit initial foo a &&
|
||||||
|
@ -35,8 +37,8 @@ test_expect_success setup '
|
||||||
test_commit picked foo c &&
|
test_commit picked foo c &&
|
||||||
test_commit anotherpick foo d &&
|
test_commit anotherpick foo d &&
|
||||||
test_commit yetanotherpick foo e &&
|
test_commit yetanotherpick foo e &&
|
||||||
git config advice.detachedhead false
|
pristine_detach initial &&
|
||||||
|
test_commit conflicting unrelated
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'cherry-pick persists data on failure' '
|
test_expect_success 'cherry-pick persists data on failure' '
|
||||||
|
@ -48,6 +50,18 @@ test_expect_success 'cherry-pick persists data on failure' '
|
||||||
test_path_is_file .git/sequencer/opts
|
test_path_is_file .git/sequencer/opts
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick base..anotherpick &&
|
||||||
|
test_cmp_rev picked CHERRY_PICK_HEAD &&
|
||||||
|
# "oops, I forgot that these patches rely on the change from base"
|
||||||
|
git checkout HEAD foo &&
|
||||||
|
git cherry-pick base &&
|
||||||
|
git cherry-pick picked &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
git diff --exit-code anotherpick
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'cherry-pick persists opts correctly' '
|
test_expect_success 'cherry-pick persists opts correctly' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
|
test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
|
||||||
|
@ -189,10 +203,10 @@ test_expect_success '--abort refuses to clobber unrelated change, harder case' '
|
||||||
test_cmp_rev initial HEAD
|
test_cmp_rev initial HEAD
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
|
test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick base..picked &&
|
test_must_fail git cherry-pick base..picked &&
|
||||||
test_path_is_missing .git/sequencer &&
|
test_path_is_dir .git/sequencer &&
|
||||||
echo "resolved" >foo &&
|
echo "resolved" >foo &&
|
||||||
git add foo &&
|
git add foo &&
|
||||||
git commit &&
|
git commit &&
|
||||||
|
@ -213,7 +227,7 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_failure '--abort after last commit in sequence' '
|
test_expect_success '--abort after last commit in sequence' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick base..picked &&
|
test_must_fail git cherry-pick base..picked &&
|
||||||
git cherry-pick --abort &&
|
git cherry-pick --abort &&
|
||||||
|
@ -243,7 +257,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' '
|
||||||
test_must_fail git cherry-pick --continue
|
test_must_fail git cherry-pick --continue
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success '--continue continues after conflicts are resolved' '
|
test_expect_success '--continue of single cherry-pick' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
echo c >expect &&
|
||||||
|
test_must_fail git cherry-pick picked &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
|
||||||
|
test_cmp expect foo &&
|
||||||
|
test_cmp_rev initial HEAD^ &&
|
||||||
|
git diff --exit-code HEAD &&
|
||||||
|
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue of single revert' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
echo resolved >expect &&
|
||||||
|
echo "Revert \"picked\"" >expect.msg &&
|
||||||
|
test_must_fail git revert picked &&
|
||||||
|
echo resolved >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
|
||||||
|
git diff --exit-code HEAD &&
|
||||||
|
test_cmp expect foo &&
|
||||||
|
test_cmp_rev initial HEAD^ &&
|
||||||
|
git diff-tree -s --pretty=tformat:%s HEAD >msg &&
|
||||||
|
test_cmp expect.msg msg &&
|
||||||
|
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
|
||||||
|
test_must_fail git rev-parse --verify REVERT_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue after resolving conflicts' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
echo d >expect &&
|
||||||
|
cat >expect.log <<-\EOF &&
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M foo
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M foo
|
||||||
|
OBJID
|
||||||
|
:100644 100644 OBJID OBJID M unrelated
|
||||||
|
OBJID
|
||||||
|
:000000 100644 OBJID OBJID A foo
|
||||||
|
:000000 100644 OBJID OBJID A unrelated
|
||||||
|
EOF
|
||||||
|
test_must_fail git cherry-pick base..anotherpick &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
{
|
||||||
|
git rev-list HEAD |
|
||||||
|
git diff-tree --root --stdin |
|
||||||
|
sed "s/$_x40/OBJID/g"
|
||||||
|
} >actual.log &&
|
||||||
|
test_cmp expect foo &&
|
||||||
|
test_cmp expect.log actual.log
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue after resolving conflicts and committing' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick base..anotherpick &&
|
test_must_fail git cherry-pick base..anotherpick &&
|
||||||
echo "c" >foo &&
|
echo "c" >foo &&
|
||||||
|
@ -270,6 +343,29 @@ test_expect_success '--continue continues after conflicts are resolved' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue asks for help after resolving patch to nil' '
|
||||||
|
pristine_detach conflicting &&
|
||||||
|
test_must_fail git cherry-pick initial..picked &&
|
||||||
|
|
||||||
|
test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
|
||||||
|
git checkout HEAD -- unrelated &&
|
||||||
|
test_must_fail git cherry-pick --continue 2>msg &&
|
||||||
|
test_i18ngrep "The previous cherry-pick is now empty" msg
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'follow advice and skip nil patch' '
|
||||||
|
pristine_detach conflicting &&
|
||||||
|
test_must_fail git cherry-pick initial..picked &&
|
||||||
|
|
||||||
|
git checkout HEAD -- unrelated &&
|
||||||
|
test_must_fail git cherry-pick --continue &&
|
||||||
|
git reset &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
|
||||||
|
git rev-list initial..HEAD >commits &&
|
||||||
|
test_line_count = 3 commits
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '--continue respects opts' '
|
test_expect_success '--continue respects opts' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick -x base..anotherpick &&
|
test_must_fail git cherry-pick -x base..anotherpick &&
|
||||||
|
@ -288,6 +384,29 @@ test_expect_success '--continue respects opts' '
|
||||||
grep "cherry picked from" anotherpick_msg
|
grep "cherry picked from" anotherpick_msg
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue of single-pick respects -x' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick -x picked &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
test_path_is_missing .git/sequencer &&
|
||||||
|
git cat-file commit HEAD >msg &&
|
||||||
|
grep "cherry picked from" msg
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--continue respects -x in first commit in multi-pick' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick -x picked anotherpick &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
test_path_is_missing .git/sequencer &&
|
||||||
|
git cat-file commit HEAD^ >msg &&
|
||||||
|
picked=$(git rev-parse --verify picked) &&
|
||||||
|
grep "cherry picked from.*$picked" msg
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success '--signoff is not automatically propagated to resolved conflict' '
|
test_expect_success '--signoff is not automatically propagated to resolved conflict' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick --signoff base..anotherpick &&
|
test_must_fail git cherry-pick --signoff base..anotherpick &&
|
||||||
|
@ -306,6 +425,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
|
||||||
grep "Signed-off-by:" anotherpick_msg
|
grep "Signed-off-by:" anotherpick_msg
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick -s picked anotherpick &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
|
||||||
|
git diff --exit-code HEAD &&
|
||||||
|
test_cmp_rev initial HEAD^^ &&
|
||||||
|
git cat-file commit HEAD^ >msg &&
|
||||||
|
! grep Signed-off-by: msg
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_must_fail git cherry-pick -s picked &&
|
||||||
|
echo c >foo &&
|
||||||
|
git add foo &&
|
||||||
|
git cherry-pick --continue &&
|
||||||
|
|
||||||
|
git diff --exit-code HEAD &&
|
||||||
|
test_cmp_rev initial HEAD^ &&
|
||||||
|
git cat-file commit HEAD >msg &&
|
||||||
|
! grep Signed-off-by: msg
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'malformed instruction sheet 1' '
|
test_expect_success 'malformed instruction sheet 1' '
|
||||||
pristine_detach initial &&
|
pristine_detach initial &&
|
||||||
test_must_fail git cherry-pick base..anotherpick &&
|
test_must_fail git cherry-pick base..anotherpick &&
|
||||||
|
@ -328,4 +473,9 @@ test_expect_success 'malformed instruction sheet 2' '
|
||||||
test_must_fail git cherry-pick --continue
|
test_must_fail git cherry-pick --continue
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'empty commit set' '
|
||||||
|
pristine_detach initial &&
|
||||||
|
test_expect_code 128 git cherry-pick base..base
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
test_description='Test interaction of reset --hard with sequencer
|
|
||||||
|
|
||||||
+ anotherpick: rewrites foo to d
|
|
||||||
+ picked: rewrites foo to c
|
|
||||||
+ unrelatedpick: rewrites unrelated to reallyunrelated
|
|
||||||
+ base: rewrites foo to b
|
|
||||||
+ initial: writes foo as a, unrelated as unrelated
|
|
||||||
'
|
|
||||||
|
|
||||||
. ./test-lib.sh
|
|
||||||
|
|
||||||
pristine_detach () {
|
|
||||||
git cherry-pick --quit &&
|
|
||||||
git checkout -f "$1^0" &&
|
|
||||||
git read-tree -u --reset HEAD &&
|
|
||||||
git clean -d -f -f -q -x
|
|
||||||
}
|
|
||||||
|
|
||||||
test_expect_success setup '
|
|
||||||
echo unrelated >unrelated &&
|
|
||||||
git add unrelated &&
|
|
||||||
test_commit initial foo a &&
|
|
||||||
test_commit base foo b &&
|
|
||||||
test_commit unrelatedpick unrelated reallyunrelated &&
|
|
||||||
test_commit picked foo c &&
|
|
||||||
test_commit anotherpick foo d &&
|
|
||||||
git config advice.detachedhead false
|
|
||||||
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' '
|
|
||||||
pristine_detach initial &&
|
|
||||||
test_must_fail git cherry-pick base..anotherpick &&
|
|
||||||
test_path_is_dir .git/sequencer &&
|
|
||||||
git reset --hard &&
|
|
||||||
test_path_is_missing .git/sequencer &&
|
|
||||||
test_path_is_dir .git/sequencer-old &&
|
|
||||||
git reset --hard &&
|
|
||||||
test_path_is_missing .git/sequencer-old
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'cherry-pick --abort does not leave sequencer-old dir' '
|
|
||||||
pristine_detach initial &&
|
|
||||||
test_must_fail git cherry-pick base..anotherpick &&
|
|
||||||
git cherry-pick --abort &&
|
|
||||||
test_path_is_missing .git/sequencer &&
|
|
||||||
test_path_is_missing .git/sequencer-old
|
|
||||||
'
|
|
||||||
|
|
||||||
test_done
|
|
Loading…
Reference in New Issue