Skip to content

Commit

Permalink
Merge branch 'jn/maint-sequencer-fixes'
Browse files Browse the repository at this point in the history
* 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 function
  • Loading branch information
Junio C Hamano committed Dec 20, 2011
2 parents ea4ef30 + d596118 commit b8fc5ab
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 126 deletions.
2 changes: 0 additions & 2 deletions branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include "refs.h"
#include "remote.h"
#include "commit.h"
#include "sequencer.h"

struct tracking {
struct refspec spec;
Expand Down Expand Up @@ -280,5 +279,4 @@ void remove_branch_state(void)
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
unlink(git_path("SQUASH_MSG"));
remove_sequencer_state(0);
}
140 changes: 92 additions & 48 deletions builtin/revert.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ struct replay_opts {
int allow_rerere_auto;

int mainline;
int commit_argc;
const char **commit_argv;

/* Merge strategy */
const char *strategy;
const char **xopts;
size_t xopts_nr, xopts_alloc;

/* Only used by REPLAY_NONE */
struct rev_info *revs;
};

#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
Expand Down Expand Up @@ -169,9 +170,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
die(_("program error"));
}

opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN);
argc = parse_options(argc, argv, NULL, options, usage_str,
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN);

/* Check for incompatible subcommands */
verify_opt_mutually_compatible(me,
Expand Down Expand Up @@ -213,17 +214,27 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
NULL);
}

else if (opts->commit_argc < 2)
usage_with_options(usage_str, options);

if (opts->allow_ff)
verify_opt_compatible(me, "--ff",
"--signoff", opts->signoff,
"--no-commit", opts->no_commit,
"-x", opts->record_origin,
"--edit", opts->edit,
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 {
Expand Down Expand Up @@ -631,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
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)
revs->reverse = 1;

argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
if (argc > 1)
usage(*revert_or_cherry_pick_usage(opts));
opts->revs->reverse ^= 1;

if (prepare_revision_walk(revs))
if (prepare_revision_walk(opts->revs))
die(_("revision walk setup failed"));

if (!revs->commits)
if (!opts->revs->commits)
die(_("empty commit set passed"));
}

Expand Down Expand Up @@ -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,
struct replay_opts *opts)
{
struct rev_info revs;
struct commit *commit;
struct commit_list **next;

prepare_revs(&revs, opts);
prepare_revs(opts);

next = todo_list;
while ((commit = get_revision(&revs)))
while ((commit = get_revision(opts->revs)))
next = commit_list_append(commit, next);
}

Expand Down Expand Up @@ -942,7 +944,7 @@ static int sequencer_rollback(struct replay_opts *opts)
}
if (reset_for_rollback(sha1))
goto fail;
remove_sequencer_state(1);
remove_sequencer_state();
strbuf_release(&buf);
return 0;
fail:
Expand Down Expand Up @@ -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) {
save_todo(cur, opts);
res = do_pick_commit(cur->item, opts);
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);
if (res)
return res;
}
}

/*
* Sequence of picks finished successfully; cleanup by
* removing the .git/sequencer directory
*/
remove_sequencer_state(1);
remove_sequencer_state();
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)
{
struct commit_list *todo_list = NULL;
unsigned char sha1[20];

if (opts->subcommand == REPLAY_NONE)
assert(opts->revs);

read_and_refresh_cache(opts);

/*
Expand All @@ -1051,21 +1084,32 @@ static int pick_revisions(struct replay_opts *opts)
* one that is being continued
*/
if (opts->subcommand == REPLAY_REMOVE_STATE) {
remove_sequencer_state(1);
remove_sequencer_state();
return 0;
}
if (opts->subcommand == REPLAY_ROLLBACK)
return sequencer_rollback(opts);
if (opts->subcommand == REPLAY_CONTINUE) {
if (!file_exists(git_path(SEQ_TODO_FILE)))
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))
todo_list = todo_list->next;
return pick_commits(todo_list, opts);
if (opts->subcommand == REPLAY_CONTINUE)
return sequencer_continue(opts);

/*
* If we were called as "git cherry-pick <commit>", just
* cherry-pick/revert it, set CHERRY_PICK_HEAD /
* 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);
}

/*
Expand Down
10 changes: 2 additions & 8 deletions sequencer.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,11 @@
#include "strbuf.h"
#include "dir.h"

void remove_sequencer_state(int aggressive)
void remove_sequencer_state(void)
{
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_old_dir, "%s", git_path(SEQ_OLD_DIR));
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);
remove_dir_recursively(&seq_dir, 0);
strbuf_release(&seq_dir);
strbuf_release(&seq_old_dir);
}
12 changes: 2 additions & 10 deletions sequencer.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@
#define SEQUENCER_H

#define SEQ_DIR "sequencer"
#define SEQ_OLD_DIR "sequencer-old"
#define SEQ_HEAD_FILE "sequencer/head"
#define SEQ_TODO_FILE "sequencer/todo"
#define SEQ_OPTS_FILE "sequencer/opts"

/*
* Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring
* 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);
/* Removes SEQ_DIR. */
extern void remove_sequencer_state(void);

#endif
Loading

0 comments on commit b8fc5ab

Please sign in to comment.