mirror of
https://github.com/git/git.git
synced 2026-01-29 03:52:00 +00:00
Merge 'prepare-sequencer' into HEAD
This commit is contained in:
@@ -173,7 +173,7 @@ static void determine_whence(struct wt_status *s)
|
||||
whence = FROM_MERGE;
|
||||
else if (file_exists(git_path_cherry_pick_head())) {
|
||||
whence = FROM_CHERRY_PICK;
|
||||
if (file_exists(git_path(SEQ_DIR)))
|
||||
if (file_exists(git_path_seq_dir()))
|
||||
sequencer_in_use = 1;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -71,7 +71,7 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
|
||||
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
|
||||
}
|
||||
|
||||
static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||
static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
|
||||
{
|
||||
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
|
||||
const char *me = action_name(opts);
|
||||
@@ -115,25 +115,15 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||
if (opts->keep_redundant_commits)
|
||||
opts->allow_empty = 1;
|
||||
|
||||
/* Set the subcommand */
|
||||
if (cmd == 'q')
|
||||
opts->subcommand = REPLAY_REMOVE_STATE;
|
||||
else if (cmd == 'c')
|
||||
opts->subcommand = REPLAY_CONTINUE;
|
||||
else if (cmd == 'a')
|
||||
opts->subcommand = REPLAY_ROLLBACK;
|
||||
else
|
||||
opts->subcommand = REPLAY_NONE;
|
||||
|
||||
/* Check for incompatible command line arguments */
|
||||
if (opts->subcommand != REPLAY_NONE) {
|
||||
if (cmd) {
|
||||
char *this_operation;
|
||||
if (opts->subcommand == REPLAY_REMOVE_STATE)
|
||||
if (cmd == 'q')
|
||||
this_operation = "--quit";
|
||||
else if (opts->subcommand == REPLAY_CONTINUE)
|
||||
else if (cmd == 'c')
|
||||
this_operation = "--continue";
|
||||
else {
|
||||
assert(opts->subcommand == REPLAY_ROLLBACK);
|
||||
assert(cmd == 'a');
|
||||
this_operation = "--abort";
|
||||
}
|
||||
|
||||
@@ -156,7 +146,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||
"--edit", opts->edit,
|
||||
NULL);
|
||||
|
||||
if (opts->subcommand != REPLAY_NONE) {
|
||||
if (cmd) {
|
||||
opts->revs = NULL;
|
||||
} else {
|
||||
struct setup_revision_opt s_r_opt;
|
||||
@@ -174,20 +164,26 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
|
||||
|
||||
if (argc > 1)
|
||||
usage_with_options(usage_str, options);
|
||||
|
||||
if (cmd == 'q')
|
||||
return sequencer_remove_state(opts);
|
||||
if (cmd == 'c')
|
||||
return sequencer_continue(opts);
|
||||
if (cmd == 'a')
|
||||
return sequencer_rollback(opts);
|
||||
return sequencer_pick_revisions(opts);
|
||||
}
|
||||
|
||||
int cmd_revert(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct replay_opts opts;
|
||||
struct replay_opts opts = REPLAY_OPTS_INIT;
|
||||
int res;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
if (isatty(0))
|
||||
opts.edit = 1;
|
||||
opts.action = REPLAY_REVERT;
|
||||
git_config(git_default_config, NULL);
|
||||
parse_args(argc, argv, &opts);
|
||||
res = sequencer_pick_revisions(&opts);
|
||||
res = run_sequencer(argc, argv, &opts);
|
||||
if (res < 0)
|
||||
die(_("revert failed"));
|
||||
return res;
|
||||
@@ -195,14 +191,12 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
|
||||
|
||||
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct replay_opts opts;
|
||||
struct replay_opts opts = REPLAY_OPTS_INIT;
|
||||
int res;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.action = REPLAY_PICK;
|
||||
git_config(git_default_config, NULL);
|
||||
parse_args(argc, argv, &opts);
|
||||
res = sequencer_pick_revisions(&opts);
|
||||
res = run_sequencer(argc, argv, &opts);
|
||||
if (res < 0)
|
||||
die(_("cherry-pick failed"));
|
||||
return res;
|
||||
|
||||
634
sequencer.c
634
sequencer.c
@@ -15,16 +15,46 @@
|
||||
#include "merge-recursive.h"
|
||||
#include "refs.h"
|
||||
#include "argv-array.h"
|
||||
#include "quote.h"
|
||||
|
||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||
|
||||
const char sign_off_header[] = "Signed-off-by: ";
|
||||
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
|
||||
|
||||
static GIT_PATH_FUNC(git_path_todo_file, SEQ_TODO_FILE)
|
||||
static GIT_PATH_FUNC(git_path_opts_file, SEQ_OPTS_FILE)
|
||||
static GIT_PATH_FUNC(git_path_seq_dir, SEQ_DIR)
|
||||
static GIT_PATH_FUNC(git_path_head_file, SEQ_HEAD_FILE)
|
||||
GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
|
||||
|
||||
static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
|
||||
static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
|
||||
static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
|
||||
|
||||
/*
|
||||
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
|
||||
* GIT_AUTHOR_DATE that will be used for the commit that is currently
|
||||
* being rebased.
|
||||
*/
|
||||
static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
|
||||
/*
|
||||
* The following files are written by git-rebase just after parsing the
|
||||
* command-line (and are only consumed, not modified, by the sequencer).
|
||||
*/
|
||||
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
|
||||
|
||||
/* We will introduce the 'interactive rebase' mode later */
|
||||
static inline int is_rebase_i(const struct replay_opts *opts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *get_dir(const struct replay_opts *opts)
|
||||
{
|
||||
return git_path_seq_dir();
|
||||
}
|
||||
|
||||
static const char *get_todo_path(const struct replay_opts *opts)
|
||||
{
|
||||
return git_path_todo_file();
|
||||
}
|
||||
|
||||
static int is_rfc2822_line(const char *buf, int len)
|
||||
{
|
||||
@@ -108,18 +138,45 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void remove_sequencer_state(void)
|
||||
static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
|
||||
{
|
||||
struct strbuf seq_dir = STRBUF_INIT;
|
||||
static struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
strbuf_addstr(&seq_dir, git_path(SEQ_DIR));
|
||||
remove_dir_recursively(&seq_dir, 0);
|
||||
strbuf_release(&seq_dir);
|
||||
strbuf_reset(&buf);
|
||||
if (opts->gpg_sign)
|
||||
sq_quotef(&buf, "-S%s", opts->gpg_sign);
|
||||
return buf.buf;
|
||||
}
|
||||
|
||||
void *sequencer_entrust(struct replay_opts *opts, void *to_free)
|
||||
{
|
||||
ALLOC_GROW(opts->owned, opts->owned_nr + 1, opts->owned_alloc);
|
||||
opts->owned[opts->owned_nr++] = to_free;
|
||||
|
||||
return to_free;
|
||||
}
|
||||
|
||||
int sequencer_remove_state(struct replay_opts *opts)
|
||||
{
|
||||
struct strbuf dir = STRBUF_INIT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < opts->owned_nr; i++)
|
||||
free(opts->owned[i]);
|
||||
free(opts->owned);
|
||||
|
||||
free(opts->xopts);
|
||||
|
||||
strbuf_addf(&dir, "%s", get_dir(opts));
|
||||
remove_dir_recursively(&dir, 0);
|
||||
strbuf_release(&dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *action_name(const struct replay_opts *opts)
|
||||
{
|
||||
return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
|
||||
return opts->action == REPLAY_REVERT ? N_("revert") : N_("cherry-pick");
|
||||
}
|
||||
|
||||
struct commit_message {
|
||||
@@ -129,13 +186,18 @@ struct commit_message {
|
||||
const char *message;
|
||||
};
|
||||
|
||||
static const char *short_commit_name(struct commit *commit)
|
||||
{
|
||||
return find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
|
||||
}
|
||||
|
||||
static int get_message(struct commit *commit, struct commit_message *out)
|
||||
{
|
||||
const char *abbrev, *subject;
|
||||
int subject_len;
|
||||
|
||||
out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
|
||||
abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
|
||||
abbrev = short_commit_name(commit);
|
||||
|
||||
subject_len = find_commit_subject(out->message, &subject);
|
||||
|
||||
@@ -180,22 +242,68 @@ static void print_advice(int show_hint, struct replay_opts *opts)
|
||||
}
|
||||
}
|
||||
|
||||
static int write_message(struct strbuf *msgbuf, const char *filename)
|
||||
static int write_with_lock_file(const char *filename,
|
||||
const void *buf, size_t len, int append_eol)
|
||||
{
|
||||
static struct lock_file msg_file;
|
||||
|
||||
int msg_fd = hold_lock_file_for_update(&msg_file, filename, 0);
|
||||
if (msg_fd < 0)
|
||||
return error_errno(_("Could not lock '%s'"), filename);
|
||||
if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
|
||||
return error_errno(_("Could not write to %s"), filename);
|
||||
strbuf_release(msgbuf);
|
||||
if (write_in_full(msg_fd, buf, len) < 0)
|
||||
return error_errno(_("Could not write to '%s'"), filename);
|
||||
if (append_eol && write(msg_fd, "\n", 1) < 0)
|
||||
return error_errno(_("Could not write eol to '%s"), filename);
|
||||
if (commit_lock_file(&msg_file) < 0)
|
||||
return error(_("Error wrapping up %s."), filename);
|
||||
return error(_("Error wrapping up '%s'."), filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_message(struct strbuf *msgbuf, const char *filename)
|
||||
{
|
||||
int res = write_with_lock_file(filename, msgbuf->buf, msgbuf->len, 0);
|
||||
strbuf_release(msgbuf);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int write_file_gently(const char *filename,
|
||||
const char *text, int append_eol)
|
||||
{
|
||||
return write_with_lock_file(filename, text, strlen(text), append_eol);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a file that was presumably written by a shell script, i.e.
|
||||
* with an end-of-line marker that needs to be stripped.
|
||||
*
|
||||
* Returns 1 if the file was read, 0 if it could not be read or does not exist.
|
||||
*/
|
||||
static int read_oneliner(struct strbuf *buf,
|
||||
const char *path, int skip_if_empty)
|
||||
{
|
||||
int orig_len = buf->len;
|
||||
|
||||
if (!file_exists(path))
|
||||
return 0;
|
||||
|
||||
if (strbuf_read_file(buf, path, 0) < 0) {
|
||||
warning_errno("could not read '%s'", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
|
||||
if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
|
||||
--buf->len;
|
||||
buf->buf[buf->len] = '\0';
|
||||
}
|
||||
|
||||
if (skip_if_empty && buf->len == orig_len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct tree *empty_tree(void)
|
||||
{
|
||||
return lookup_tree(EMPTY_TREE_SHA1_BIN);
|
||||
@@ -204,13 +312,10 @@ static struct tree *empty_tree(void)
|
||||
static int error_dirty_index(struct replay_opts *opts)
|
||||
{
|
||||
if (read_cache_unmerged())
|
||||
return error_resolve_conflict(action_name(opts));
|
||||
return error_resolve_conflict(_(action_name(opts)));
|
||||
|
||||
/* Different translation strings for cherry-pick and revert */
|
||||
if (opts->action == REPLAY_PICK)
|
||||
error(_("Your local changes would be overwritten by cherry-pick."));
|
||||
else
|
||||
error(_("Your local changes would be overwritten by revert."));
|
||||
error(_("Your local changes would be overwritten by %s."),
|
||||
_(action_name(opts)));
|
||||
|
||||
if (advice_commit_before_merge)
|
||||
advise(_("Commit your changes or stash them to proceed."));
|
||||
@@ -228,7 +333,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
|
||||
if (checkout_fast_forward(from, to, 1))
|
||||
return -1; /* the callee should have complained already */
|
||||
|
||||
strbuf_addf(&sb, _("%s: fast-forward"), action_name(opts));
|
||||
strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
|
||||
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
@@ -304,7 +409,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||
write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
|
||||
/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
|
||||
return error(_("%s: Unable to write new index file"),
|
||||
action_name(opts));
|
||||
_(action_name(opts)));
|
||||
rollback_lock_file(&index_lock);
|
||||
|
||||
if (opts->signoff)
|
||||
@@ -347,36 +452,100 @@ static int is_index_unchanged(void)
|
||||
return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
|
||||
}
|
||||
|
||||
static char **read_author_script(void)
|
||||
{
|
||||
struct strbuf script = STRBUF_INIT;
|
||||
int i, count = 0;
|
||||
char *p, *p2, **env;
|
||||
size_t env_size;
|
||||
|
||||
if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
|
||||
return NULL;
|
||||
|
||||
for (p = script.buf; *p; p++)
|
||||
if (skip_prefix(p, "'\\\\''", (const char **)&p2))
|
||||
strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
|
||||
else if (*p == '\'')
|
||||
strbuf_splice(&script, p-- - script.buf, 1, "", 0);
|
||||
else if (*p == '\n') {
|
||||
*p = '\0';
|
||||
count++;
|
||||
}
|
||||
|
||||
env_size = (count + 1) * sizeof(*env);
|
||||
strbuf_grow(&script, env_size);
|
||||
memmove(script.buf + env_size, script.buf, script.len);
|
||||
p = script.buf + env_size;
|
||||
env = (char **)strbuf_detach(&script, NULL);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
env[i] = p;
|
||||
p += strlen(p) + 1;
|
||||
}
|
||||
env[count] = NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are cherry-pick, and if the merge did not result in
|
||||
* hand-editing, we will hit this commit and inherit the original
|
||||
* author date and name.
|
||||
*
|
||||
* If we are revert, or if our cherry-pick results in a hand merge,
|
||||
* we had better say that the current user is responsible for that.
|
||||
*
|
||||
* An exception is when sequencer_commit() is called during an
|
||||
* interactive rebase: in that case, we will want to retain the
|
||||
* author metadata.
|
||||
*/
|
||||
static int run_git_commit(const char *defmsg, struct replay_opts *opts,
|
||||
int allow_empty)
|
||||
int sequencer_commit(const char *defmsg, struct replay_opts *opts,
|
||||
int allow_empty, int edit, int amend,
|
||||
int cleanup_commit_message)
|
||||
{
|
||||
char **env = NULL;
|
||||
struct argv_array array;
|
||||
int rc;
|
||||
const char *value;
|
||||
|
||||
if (is_rebase_i(opts)) {
|
||||
env = read_author_script();
|
||||
if (!env) {
|
||||
const char *gpg_opt = gpg_sign_opt_quoted(opts);
|
||||
|
||||
return error("You have staged changes in your working "
|
||||
"tree. If these changes are meant to be\n"
|
||||
"squashed into the previous commit, run:\n\n"
|
||||
" git commit --amend %s\n\n"
|
||||
"If they are meant to go into a new commit, "
|
||||
"run:\n\n"
|
||||
" git commit %s\n\n"
|
||||
"In both cases, once you're done, continue "
|
||||
"with:\n\n"
|
||||
" git rebase --continue\n", gpg_opt, gpg_opt);
|
||||
}
|
||||
}
|
||||
|
||||
argv_array_init(&array);
|
||||
argv_array_push(&array, "commit");
|
||||
argv_array_push(&array, "-n");
|
||||
|
||||
if (amend)
|
||||
argv_array_push(&array, "--amend");
|
||||
if (opts->gpg_sign)
|
||||
argv_array_pushf(&array, "-S%s", opts->gpg_sign);
|
||||
if (opts->signoff)
|
||||
argv_array_push(&array, "-s");
|
||||
if (!opts->edit) {
|
||||
argv_array_push(&array, "-F");
|
||||
argv_array_push(&array, defmsg);
|
||||
if (!opts->signoff &&
|
||||
!opts->record_origin &&
|
||||
git_config_get_value("commit.cleanup", &value))
|
||||
argv_array_push(&array, "--cleanup=verbatim");
|
||||
}
|
||||
if (defmsg)
|
||||
argv_array_pushl(&array, "-F", defmsg, NULL);
|
||||
if (cleanup_commit_message)
|
||||
argv_array_push(&array, "--cleanup=strip");
|
||||
if (edit)
|
||||
argv_array_push(&array, "-e");
|
||||
else if (!cleanup_commit_message &&
|
||||
!opts->signoff && !opts->record_origin &&
|
||||
git_config_get_value("commit.cleanup", &value))
|
||||
argv_array_push(&array, "--cleanup=verbatim");
|
||||
|
||||
if (allow_empty)
|
||||
argv_array_push(&array, "--allow-empty");
|
||||
@@ -384,8 +553,11 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
|
||||
if (opts->allow_empty_message)
|
||||
argv_array_push(&array, "--allow-empty-message");
|
||||
|
||||
rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
|
||||
rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
|
||||
(const char *const *)env);
|
||||
argv_array_clear(&array);
|
||||
free(env);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -447,14 +619,33 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
enum todo_command {
|
||||
TODO_PICK = 0,
|
||||
TODO_REVERT
|
||||
};
|
||||
|
||||
static const char *todo_command_strings[] = {
|
||||
"pick",
|
||||
"revert"
|
||||
};
|
||||
|
||||
static const char *command_to_string(const enum todo_command command)
|
||||
{
|
||||
if (command < ARRAY_SIZE(todo_command_strings))
|
||||
return todo_command_strings[command];
|
||||
die("Unknown command: %d", command);
|
||||
}
|
||||
|
||||
|
||||
static int do_pick_commit(enum todo_command command, struct commit *commit,
|
||||
struct replay_opts *opts)
|
||||
{
|
||||
unsigned char head[20];
|
||||
struct commit *base, *next, *parent;
|
||||
const char *base_label, *next_label;
|
||||
struct commit_message msg = { NULL, NULL, NULL, NULL };
|
||||
struct strbuf msgbuf = STRBUF_INIT;
|
||||
int res, unborn = 0, allow;
|
||||
int res = 0, unborn = 0, allow;
|
||||
|
||||
if (opts->no_commit) {
|
||||
/*
|
||||
@@ -506,10 +697,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
return fast_forward_to(commit->object.oid.hash, head, unborn, opts);
|
||||
|
||||
if (parent && parse_commit(parent) < 0)
|
||||
/* TRANSLATORS: The first %s will be "revert" or
|
||||
"cherry-pick", the second %s a SHA1 */
|
||||
return error(_("%s: cannot parse parent commit %s"),
|
||||
action_name(opts), oid_to_hex(&parent->object.oid));
|
||||
command_to_string(command),
|
||||
oid_to_hex(&parent->object.oid));
|
||||
|
||||
if (get_message(commit, &msg) != 0)
|
||||
return error(_("Cannot get commit message for %s"),
|
||||
@@ -522,7 +712,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
* reverse of it if we are revert.
|
||||
*/
|
||||
|
||||
if (opts->action == REPLAY_REVERT) {
|
||||
if (command == TODO_REVERT) {
|
||||
base = commit;
|
||||
base_label = msg.label;
|
||||
next = parent;
|
||||
@@ -563,8 +753,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
|
||||
res = do_recursive_merge(base, next, base_label, next_label,
|
||||
if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
|
||||
res |= do_recursive_merge(base, next, base_label, next_label,
|
||||
head, &msgbuf, opts);
|
||||
if (res < 0)
|
||||
return res;
|
||||
@@ -573,7 +763,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
struct commit_list *common = NULL;
|
||||
struct commit_list *remotes = NULL;
|
||||
|
||||
res = write_message(&msgbuf, git_path_merge_msg());
|
||||
res |= write_message(&msgbuf, git_path_merge_msg());
|
||||
|
||||
commit_list_insert(base, &common);
|
||||
commit_list_insert(next, &remotes);
|
||||
@@ -589,21 +779,20 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
* However, if the merge did not even start, then we don't want to
|
||||
* write it at all.
|
||||
*/
|
||||
if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1) &&
|
||||
if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
|
||||
update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
|
||||
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
|
||||
res = -1;
|
||||
if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
|
||||
if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
|
||||
update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
|
||||
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
|
||||
res = -1;
|
||||
|
||||
if (res) {
|
||||
error(opts->action == REPLAY_REVERT
|
||||
error(command == TODO_REVERT
|
||||
? _("could not revert %s... %s")
|
||||
: _("could not apply %s... %s"),
|
||||
find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV),
|
||||
msg.subject);
|
||||
short_commit_name(commit), msg.subject);
|
||||
print_advice(res == 1, opts);
|
||||
rerere(opts->allow_rerere_auto);
|
||||
goto leave;
|
||||
@@ -611,11 +800,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
|
||||
allow = allow_empty(opts, commit);
|
||||
if (allow < 0) {
|
||||
res = allow;
|
||||
res |= allow;
|
||||
goto leave;
|
||||
}
|
||||
if (!opts->no_commit)
|
||||
res = run_git_commit(git_path_merge_msg(), opts, allow);
|
||||
res |= sequencer_commit(opts->edit ?
|
||||
NULL : git_path_merge_msg(),
|
||||
opts, allow, opts->edit, 0, 0);
|
||||
|
||||
leave:
|
||||
free_message(commit, &msg);
|
||||
@@ -647,133 +838,146 @@ static int read_and_refresh_cache(struct replay_opts *opts)
|
||||
if (read_index_preload(&the_index, NULL) < 0) {
|
||||
rollback_lock_file(&index_lock);
|
||||
return error(_("git %s: failed to read the index"),
|
||||
action_name(opts));
|
||||
_(action_name(opts)));
|
||||
}
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
|
||||
if (the_index.cache_changed && index_fd >= 0) {
|
||||
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
|
||||
rollback_lock_file(&index_lock);
|
||||
return error(_("git %s: failed to refresh the index"),
|
||||
action_name(opts));
|
||||
_(action_name(opts)));
|
||||
}
|
||||
}
|
||||
rollback_lock_file(&index_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
|
||||
struct replay_opts *opts)
|
||||
{
|
||||
struct commit_list *cur = NULL;
|
||||
const char *sha1_abbrev = NULL;
|
||||
const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
|
||||
const char *subject;
|
||||
int subject_len;
|
||||
struct todo_item {
|
||||
enum todo_command command;
|
||||
struct commit *commit;
|
||||
const char *arg;
|
||||
int arg_len;
|
||||
size_t offset_in_buf;
|
||||
};
|
||||
|
||||
for (cur = todo_list; cur; cur = cur->next) {
|
||||
const char *commit_buffer = get_commit_buffer(cur->item, NULL);
|
||||
sha1_abbrev = find_unique_abbrev(cur->item->object.oid.hash, DEFAULT_ABBREV);
|
||||
subject_len = find_commit_subject(commit_buffer, &subject);
|
||||
strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
|
||||
subject_len, subject);
|
||||
unuse_commit_buffer(cur->item, commit_buffer);
|
||||
}
|
||||
return 0;
|
||||
struct todo_list {
|
||||
struct strbuf buf;
|
||||
struct todo_item *items;
|
||||
int nr, alloc, current;
|
||||
};
|
||||
|
||||
#define TODO_LIST_INIT { STRBUF_INIT }
|
||||
|
||||
static void todo_list_release(struct todo_list *todo_list)
|
||||
{
|
||||
strbuf_release(&todo_list->buf);
|
||||
free(todo_list->items);
|
||||
todo_list->items = NULL;
|
||||
todo_list->nr = todo_list->alloc = 0;
|
||||
}
|
||||
|
||||
static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
|
||||
struct todo_item *append_new_todo(struct todo_list *todo_list)
|
||||
{
|
||||
ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
|
||||
return todo_list->items + todo_list->nr++;
|
||||
}
|
||||
|
||||
static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
|
||||
{
|
||||
unsigned char commit_sha1[20];
|
||||
enum replay_action action;
|
||||
char *end_of_object_name;
|
||||
int saved, status, padding;
|
||||
int i, saved, status, padding;
|
||||
|
||||
if (starts_with(bol, "pick")) {
|
||||
action = REPLAY_PICK;
|
||||
bol += strlen("pick");
|
||||
} else if (starts_with(bol, "revert")) {
|
||||
action = REPLAY_REVERT;
|
||||
bol += strlen("revert");
|
||||
} else
|
||||
return NULL;
|
||||
/* left-trim */
|
||||
bol += strspn(bol, " \t");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)
|
||||
if (skip_prefix(bol, todo_command_strings[i], &bol)) {
|
||||
item->command = i;
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(todo_command_strings))
|
||||
return -1;
|
||||
|
||||
/* Eat up extra spaces/ tabs before object name */
|
||||
padding = strspn(bol, " \t");
|
||||
if (!padding)
|
||||
return NULL;
|
||||
return -1;
|
||||
bol += padding;
|
||||
|
||||
end_of_object_name = bol + strcspn(bol, " \t\n");
|
||||
end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
|
||||
saved = *end_of_object_name;
|
||||
*end_of_object_name = '\0';
|
||||
status = get_sha1(bol, commit_sha1);
|
||||
*end_of_object_name = saved;
|
||||
|
||||
/*
|
||||
* Verify that the action matches up with the one in
|
||||
* opts; we don't support arbitrary instructions
|
||||
*/
|
||||
if (action != opts->action) {
|
||||
if (action == REPLAY_REVERT)
|
||||
error((opts->action == REPLAY_REVERT)
|
||||
? _("Cannot revert during another revert.")
|
||||
: _("Cannot revert during a cherry-pick."));
|
||||
else
|
||||
error((opts->action == REPLAY_REVERT)
|
||||
? _("Cannot cherry-pick during a revert.")
|
||||
: _("Cannot cherry-pick during another cherry-pick."));
|
||||
return NULL;
|
||||
}
|
||||
item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
|
||||
item->arg_len = (int)(eol - item->arg);
|
||||
|
||||
if (status < 0)
|
||||
return NULL;
|
||||
return -1;
|
||||
|
||||
return lookup_commit_reference(commit_sha1);
|
||||
item->commit = lookup_commit_reference(commit_sha1);
|
||||
return !item->commit;
|
||||
}
|
||||
|
||||
static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
|
||||
struct replay_opts *opts)
|
||||
static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
|
||||
{
|
||||
struct commit_list **next = todo_list;
|
||||
struct commit *commit;
|
||||
struct todo_item *item;
|
||||
char *p = buf;
|
||||
int i;
|
||||
int i, res = 0;
|
||||
|
||||
for (i = 1; *p; i++) {
|
||||
char *eol = strchrnul(p, '\n');
|
||||
commit = parse_insn_line(p, eol, opts);
|
||||
if (!commit)
|
||||
return error(_("Could not parse line %d."), i);
|
||||
next = commit_list_append(commit, next);
|
||||
|
||||
item = append_new_todo(todo_list);
|
||||
item->offset_in_buf = p - todo_list->buf.buf;
|
||||
if (parse_insn_line(item, p, eol)) {
|
||||
res |= error(_("Invalid line %d: %.*s"),
|
||||
i, (int)(eol - p), p);
|
||||
item->command = -1;
|
||||
}
|
||||
p = *eol ? eol + 1 : eol;
|
||||
}
|
||||
if (!*todo_list)
|
||||
if (!todo_list->nr)
|
||||
return error(_("No commits parsed."));
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int read_populate_todo(struct commit_list **todo_list,
|
||||
static int read_populate_todo(struct todo_list *todo_list,
|
||||
struct replay_opts *opts)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
const char *todo_file = get_todo_path(opts);
|
||||
int fd, res;
|
||||
|
||||
fd = open(git_path_todo_file(), O_RDONLY);
|
||||
strbuf_reset(&todo_list->buf);
|
||||
fd = open(todo_file, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return error_errno(_("Could not open %s"),
|
||||
git_path_todo_file());
|
||||
if (strbuf_read(&buf, fd, 0) < 0) {
|
||||
return error_errno(_("Could not open '%s'"), todo_file);
|
||||
if (strbuf_read(&todo_list->buf, fd, 0) < 0) {
|
||||
close(fd);
|
||||
strbuf_release(&buf);
|
||||
return error(_("Could not read %s."), git_path_todo_file());
|
||||
return error(_("Could not read '%s'."), todo_file);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
res = parse_insn_buffer(buf.buf, todo_list, opts);
|
||||
strbuf_release(&buf);
|
||||
res = parse_insn_buffer(todo_list->buf.buf, todo_list);
|
||||
if (res)
|
||||
return error(_("Unusable instruction sheet: %s"),
|
||||
git_path_todo_file());
|
||||
return error(_("Unusable instruction sheet: '%s'"), todo_file);
|
||||
|
||||
if (!is_rebase_i(opts)) {
|
||||
enum todo_command valid =
|
||||
opts->action == REPLAY_PICK ? TODO_PICK : TODO_REVERT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < todo_list->nr; i++)
|
||||
if (valid == todo_list->items[i].command)
|
||||
continue;
|
||||
else if (valid == TODO_PICK)
|
||||
return error(_("Cannot cherry-pick during a revert."));
|
||||
else
|
||||
return error(_("Cannot revert during a cherry-pick."));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -796,13 +1000,18 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
|
||||
opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
|
||||
else if (!strcmp(key, "options.mainline"))
|
||||
opts->mainline = git_config_int(key, value);
|
||||
else if (!strcmp(key, "options.strategy"))
|
||||
else if (!strcmp(key, "options.strategy")) {
|
||||
git_config_string(&opts->strategy, key, value);
|
||||
else if (!strcmp(key, "options.gpg-sign"))
|
||||
sequencer_entrust(opts, (char *) opts->strategy);
|
||||
}
|
||||
else if (!strcmp(key, "options.gpg-sign")) {
|
||||
git_config_string(&opts->gpg_sign, key, value);
|
||||
sequencer_entrust(opts, (char *) opts->gpg_sign);
|
||||
}
|
||||
else if (!strcmp(key, "options.strategy-option")) {
|
||||
ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
|
||||
opts->xopts[opts->xopts_nr++] = xstrdup(value);
|
||||
opts->xopts[opts->xopts_nr++] =
|
||||
sequencer_entrust(opts, xstrdup(value));
|
||||
} else
|
||||
return error(_("Invalid key: %s"), key);
|
||||
|
||||
@@ -812,8 +1021,24 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_populate_opts(struct replay_opts **opts)
|
||||
static int read_populate_opts(struct replay_opts *opts)
|
||||
{
|
||||
if (is_rebase_i(opts)) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
|
||||
if (!starts_with(buf.buf, "-S"))
|
||||
strbuf_reset(&buf);
|
||||
else {
|
||||
opts->gpg_sign = buf.buf + 2;
|
||||
sequencer_entrust(opts,
|
||||
strbuf_detach(&buf, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!file_exists(git_path_opts_file()))
|
||||
return 0;
|
||||
/*
|
||||
@@ -822,24 +1047,39 @@ static int read_populate_opts(struct replay_opts **opts)
|
||||
* about this case, though, because we wrote that file ourselves, so we
|
||||
* are pretty certain that it is syntactically correct.
|
||||
*/
|
||||
if (git_config_from_file(populate_opts_cb, git_path_opts_file(), *opts) < 0)
|
||||
return error(_("Malformed options sheet: %s"),
|
||||
if (git_config_from_file(populate_opts_cb, git_path_opts_file(), opts) < 0)
|
||||
return error(_("Malformed options sheet: '%s'"),
|
||||
git_path_opts_file());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int walk_revs_populate_todo(struct commit_list **todo_list,
|
||||
static int walk_revs_populate_todo(struct todo_list *todo_list,
|
||||
struct replay_opts *opts)
|
||||
{
|
||||
enum todo_command command = opts->action == REPLAY_PICK ?
|
||||
TODO_PICK : TODO_REVERT;
|
||||
const char *command_string = todo_command_strings[command];
|
||||
struct commit *commit;
|
||||
struct commit_list **next;
|
||||
|
||||
if (prepare_revs(opts))
|
||||
return -1;
|
||||
|
||||
next = todo_list;
|
||||
while ((commit = get_revision(opts->revs)))
|
||||
next = commit_list_append(commit, next);
|
||||
while ((commit = get_revision(opts->revs))) {
|
||||
struct todo_item *item = append_new_todo(todo_list);
|
||||
const char *commit_buffer = get_commit_buffer(commit, NULL);
|
||||
const char *subject;
|
||||
int subject_len;
|
||||
|
||||
item->command = command;
|
||||
item->commit = commit;
|
||||
item->arg = NULL;
|
||||
item->arg_len = 0;
|
||||
item->offset_in_buf = todo_list->buf.len;
|
||||
subject_len = find_commit_subject(commit_buffer, &subject);
|
||||
strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string,
|
||||
short_commit_name(commit), subject_len, subject);
|
||||
unuse_commit_buffer(commit, commit_buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -851,7 +1091,7 @@ static int create_seq_dir(void)
|
||||
return -1;
|
||||
}
|
||||
else if (mkdir(git_path_seq_dir(), 0777) < 0)
|
||||
return error_errno(_("Could not create sequencer directory %s"),
|
||||
return error_errno(_("Could not create sequencer directory '%s'"),
|
||||
git_path_seq_dir());
|
||||
return 0;
|
||||
}
|
||||
@@ -870,12 +1110,12 @@ static int save_head(const char *head)
|
||||
strbuf_addf(&buf, "%s\n", head);
|
||||
if (write_in_full(fd, buf.buf, buf.len) < 0) {
|
||||
rollback_lock_file(&head_lock);
|
||||
return error_errno(_("Could not write to %s"),
|
||||
return error_errno(_("Could not write to '%s'"),
|
||||
git_path_head_file());
|
||||
}
|
||||
if (commit_lock_file(&head_lock) < 0) {
|
||||
rollback_lock_file(&head_lock);
|
||||
return error(_("Error wrapping up %s."), git_path_head_file());
|
||||
return error(_("Error wrapping up '%s'."), git_path_head_file());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -904,7 +1144,7 @@ static int rollback_single_pick(void)
|
||||
return reset_for_rollback(head_sha1);
|
||||
}
|
||||
|
||||
static int sequencer_rollback(struct replay_opts *opts)
|
||||
int sequencer_rollback(struct replay_opts *opts)
|
||||
{
|
||||
FILE *f;
|
||||
unsigned char sha1[20];
|
||||
@@ -920,9 +1160,9 @@ static int sequencer_rollback(struct replay_opts *opts)
|
||||
return rollback_single_pick();
|
||||
}
|
||||
if (!f)
|
||||
return error_errno(_("cannot open %s"), git_path_head_file());
|
||||
return error_errno(_("cannot open '%s'"), git_path_head_file());
|
||||
if (strbuf_getline_lf(&buf, f)) {
|
||||
error(_("cannot read %s: %s"), git_path_head_file(),
|
||||
error(_("cannot read '%s': %s"), git_path_head_file(),
|
||||
ferror(f) ? strerror(errno) : _("unexpected end of file"));
|
||||
fclose(f);
|
||||
goto fail;
|
||||
@@ -939,38 +1179,29 @@ static int sequencer_rollback(struct replay_opts *opts)
|
||||
}
|
||||
if (reset_for_rollback(sha1))
|
||||
goto fail;
|
||||
remove_sequencer_state();
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
return sequencer_remove_state(opts);
|
||||
fail:
|
||||
strbuf_release(&buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int save_todo(struct commit_list *todo_list, struct replay_opts *opts)
|
||||
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
{
|
||||
static struct lock_file todo_lock;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int fd;
|
||||
const char *todo_path = get_todo_path(opts);
|
||||
int next = todo_list->current, offset, fd;
|
||||
|
||||
fd = hold_lock_file_for_update(&todo_lock, git_path_todo_file(), 0);
|
||||
fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
|
||||
if (fd < 0)
|
||||
return error_errno(_("Could not lock '%s'"),
|
||||
git_path_todo_file());
|
||||
if (format_todo(&buf, todo_list, opts) < 0) {
|
||||
strbuf_release(&buf);
|
||||
return error(_("Could not format %s."), git_path_todo_file());
|
||||
}
|
||||
if (write_in_full(fd, buf.buf, buf.len) < 0) {
|
||||
strbuf_release(&buf);
|
||||
return error_errno(_("Could not write to %s"),
|
||||
git_path_todo_file());
|
||||
}
|
||||
if (commit_lock_file(&todo_lock) < 0) {
|
||||
strbuf_release(&buf);
|
||||
return error(_("Error wrapping up %s."), git_path_todo_file());
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
return error_errno(_("Could not lock '%s'"), todo_path);
|
||||
offset = next < todo_list->nr ?
|
||||
todo_list->items[next].offset_in_buf : todo_list->buf.len;
|
||||
if (write_in_full(fd, todo_list->buf.buf + offset,
|
||||
todo_list->buf.len - offset) < 0)
|
||||
return error_errno(_("Could not write to '%s'"), todo_path);
|
||||
if (commit_lock_file(&todo_lock) < 0)
|
||||
return error(_("Error wrapping up '%s'."), todo_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1009,9 +1240,8 @@ static int save_opts(struct replay_opts *opts)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
|
||||
static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
{
|
||||
struct commit_list *cur;
|
||||
int res;
|
||||
|
||||
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
|
||||
@@ -1021,10 +1251,12 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
|
||||
if (read_and_refresh_cache(opts))
|
||||
return -1;
|
||||
|
||||
for (cur = todo_list; cur; cur = cur->next) {
|
||||
if (save_todo(cur, opts))
|
||||
while (todo_list->current < todo_list->nr) {
|
||||
struct todo_item *item = todo_list->items + todo_list->current;
|
||||
if (save_todo(todo_list, opts))
|
||||
return -1;
|
||||
res = do_pick_commit(cur->item, opts);
|
||||
res = do_pick_commit(item->command, item->commit, opts);
|
||||
todo_list->current++;
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
@@ -1033,8 +1265,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
|
||||
* Sequence of picks finished successfully; cleanup by
|
||||
* removing the .git/sequencer directory
|
||||
*/
|
||||
remove_sequencer_state();
|
||||
return 0;
|
||||
return sequencer_remove_state(opts);
|
||||
}
|
||||
|
||||
static int continue_single_pick(void)
|
||||
@@ -1047,61 +1278,56 @@ static int continue_single_pick(void)
|
||||
return run_command_v_opt(argv, RUN_GIT_CMD);
|
||||
}
|
||||
|
||||
static int sequencer_continue(struct replay_opts *opts)
|
||||
int sequencer_continue(struct replay_opts *opts)
|
||||
{
|
||||
struct commit_list *todo_list = NULL;
|
||||
struct todo_list todo_list = TODO_LIST_INIT;
|
||||
int res;
|
||||
|
||||
if (!file_exists(git_path_todo_file()))
|
||||
return continue_single_pick();
|
||||
if (read_populate_opts(&opts) ||
|
||||
read_populate_todo(&todo_list, opts))
|
||||
if (read_and_refresh_cache(opts))
|
||||
return -1;
|
||||
|
||||
if (!file_exists(get_todo_path(opts)))
|
||||
return continue_single_pick();
|
||||
if (read_populate_opts(opts))
|
||||
return -1;
|
||||
if ((res = read_populate_todo(&todo_list, opts)))
|
||||
goto release_todo_list;
|
||||
|
||||
/* 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;
|
||||
res = continue_single_pick();
|
||||
if (res)
|
||||
goto release_todo_list;
|
||||
}
|
||||
if (index_differs_from("HEAD", 0))
|
||||
return error_dirty_index(opts);
|
||||
todo_list = todo_list->next;
|
||||
return pick_commits(todo_list, opts);
|
||||
if (index_differs_from("HEAD", 0)) {
|
||||
res = error_dirty_index(opts);
|
||||
goto release_todo_list;
|
||||
}
|
||||
todo_list.current++;
|
||||
res = pick_commits(&todo_list, opts);
|
||||
release_todo_list:
|
||||
todo_list_release(&todo_list);
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
return do_pick_commit(opts->action == REPLAY_PICK ?
|
||||
TODO_PICK : TODO_REVERT, cmit, opts);
|
||||
}
|
||||
|
||||
int sequencer_pick_revisions(struct replay_opts *opts)
|
||||
{
|
||||
struct commit_list *todo_list = NULL;
|
||||
struct todo_list todo_list = TODO_LIST_INIT;
|
||||
unsigned char sha1[20];
|
||||
int i;
|
||||
|
||||
if (opts->subcommand == REPLAY_NONE)
|
||||
assert(opts->revs);
|
||||
int i, res;
|
||||
|
||||
assert(opts->revs);
|
||||
if (read_and_refresh_cache(opts))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Decide what to do depending on the arguments; a fresh
|
||||
* cherry-pick should be handled differently from an existing
|
||||
* one that is being continued
|
||||
*/
|
||||
if (opts->subcommand == REPLAY_REMOVE_STATE) {
|
||||
remove_sequencer_state();
|
||||
return 0;
|
||||
}
|
||||
if (opts->subcommand == REPLAY_ROLLBACK)
|
||||
return sequencer_rollback(opts);
|
||||
if (opts->subcommand == REPLAY_CONTINUE)
|
||||
return sequencer_continue(opts);
|
||||
|
||||
for (i = 0; i < opts->revs->pending.nr; i++) {
|
||||
unsigned char sha1[20];
|
||||
const char *name = opts->revs->pending.objects[i].name;
|
||||
@@ -1155,7 +1381,9 @@ int sequencer_pick_revisions(struct replay_opts *opts)
|
||||
return -1;
|
||||
if (save_opts(opts))
|
||||
return -1;
|
||||
return pick_commits(todo_list, opts);
|
||||
res = pick_commits(&todo_list, opts);
|
||||
todo_list_release(&todo_list);
|
||||
return res;
|
||||
}
|
||||
|
||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
|
||||
|
||||
31
sequencer.h
31
sequencer.h
@@ -1,10 +1,7 @@
|
||||
#ifndef SEQUENCER_H
|
||||
#define SEQUENCER_H
|
||||
|
||||
#define SEQ_DIR "sequencer"
|
||||
#define SEQ_HEAD_FILE "sequencer/head"
|
||||
#define SEQ_TODO_FILE "sequencer/todo"
|
||||
#define SEQ_OPTS_FILE "sequencer/opts"
|
||||
const char *git_path_seq_dir(void);
|
||||
|
||||
#define APPEND_SIGNOFF_DEDUP (1u << 0)
|
||||
|
||||
@@ -13,16 +10,8 @@ enum replay_action {
|
||||
REPLAY_PICK
|
||||
};
|
||||
|
||||
enum replay_subcommand {
|
||||
REPLAY_NONE,
|
||||
REPLAY_REMOVE_STATE,
|
||||
REPLAY_CONTINUE,
|
||||
REPLAY_ROLLBACK
|
||||
};
|
||||
|
||||
struct replay_opts {
|
||||
enum replay_action action;
|
||||
enum replay_subcommand subcommand;
|
||||
|
||||
/* Boolean options */
|
||||
int edit;
|
||||
@@ -46,9 +35,27 @@ struct replay_opts {
|
||||
|
||||
/* Only used by REPLAY_NONE */
|
||||
struct rev_info *revs;
|
||||
|
||||
/* malloc()ed data entrusted to the sequencer */
|
||||
void **owned;
|
||||
int owned_nr, owned_alloc;
|
||||
};
|
||||
#define REPLAY_OPTS_INIT { -1 }
|
||||
|
||||
/*
|
||||
* Make it the duty of sequencer_remove_state() to release the memory;
|
||||
* For ease of use, return the same pointer.
|
||||
*/
|
||||
void *sequencer_entrust(struct replay_opts *opts, void *to_free);
|
||||
|
||||
int sequencer_pick_revisions(struct replay_opts *opts);
|
||||
int sequencer_continue(struct replay_opts *opts);
|
||||
int sequencer_rollback(struct replay_opts *opts);
|
||||
int sequencer_remove_state(struct replay_opts *opts);
|
||||
|
||||
int sequencer_commit(const char *defmsg, struct replay_opts *opts,
|
||||
int allow_empty, int edit, int amend,
|
||||
int cleanup_commit_message);
|
||||
|
||||
extern const char sign_off_header[];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user