Merge branch 'dl/merge-autostash'
"git merge" learns the "--autostash" option. * dl/merge-autostash: (22 commits) pull: pass --autostash to merge t5520: make test_pull_autostash() accept expect_parent_num merge: teach --autostash option sequencer: implement apply_autostash_oid() sequencer: implement save_autostash() sequencer: unlink autostash in apply_autostash() sequencer: extract perform_autostash() from rebase rebase: generify create_autostash() rebase: extract create_autostash() reset: extract reset_head() from rebase rebase: generify reset_head() rebase: use apply_autostash() from sequencer.c sequencer: rename stash_sha1 to stash_oid sequencer: make apply_autostash() accept a path rebase: use read_oneliner() sequencer: make read_oneliner() extern sequencer: configurably warn on non-existent files sequencer: make read_oneliner() accept flags sequencer: make file exists check more efficient sequencer: stop leaking buf ...
This commit is contained in:
176
sequencer.c
176
sequencer.c
@ -32,6 +32,7 @@
|
||||
#include "alias.h"
|
||||
#include "commit-reach.h"
|
||||
#include "rebase-interactive.h"
|
||||
#include "reset.h"
|
||||
|
||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||
|
||||
@ -419,25 +420,15 @@ static int write_message(const void *buf, size_t len, const char *filename,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a file that was presumably written by a shell script, i.e. with an
|
||||
* end-of-line marker that needs to be stripped.
|
||||
*
|
||||
* Note that only the last end-of-line marker is stripped, consistent with the
|
||||
* behavior of "$(cat path)" in a shell script.
|
||||
*
|
||||
* 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 read_oneliner(struct strbuf *buf,
|
||||
const char *path, unsigned flags)
|
||||
{
|
||||
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);
|
||||
if ((flags & READ_ONELINER_WARN_MISSING) ||
|
||||
(errno != ENOENT && errno != ENOTDIR))
|
||||
warning_errno(_("could not read '%s'"), path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -447,7 +438,7 @@ static int read_oneliner(struct strbuf *buf,
|
||||
buf->buf[buf->len] = '\0';
|
||||
}
|
||||
|
||||
if (skip_if_empty && buf->len == orig_len)
|
||||
if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@ -2504,8 +2495,10 @@ static int read_populate_opts(struct replay_opts *opts)
|
||||
{
|
||||
if (is_rebase_i(opts)) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret = 0;
|
||||
|
||||
if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
|
||||
if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
|
||||
READ_ONELINER_SKIP_IF_EMPTY)) {
|
||||
if (!starts_with(buf.buf, "-S"))
|
||||
strbuf_reset(&buf);
|
||||
else {
|
||||
@ -2515,7 +2508,8 @@ static int read_populate_opts(struct replay_opts *opts)
|
||||
strbuf_reset(&buf);
|
||||
}
|
||||
|
||||
if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
|
||||
if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),
|
||||
READ_ONELINER_SKIP_IF_EMPTY)) {
|
||||
if (!strcmp(buf.buf, "--rerere-autoupdate"))
|
||||
opts->allow_rerere_auto = RERERE_AUTOUPDATE;
|
||||
else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
|
||||
@ -2544,10 +2538,11 @@ static int read_populate_opts(struct replay_opts *opts)
|
||||
opts->keep_redundant_commits = 1;
|
||||
|
||||
read_strategy_opts(opts, &buf);
|
||||
strbuf_release(&buf);
|
||||
strbuf_reset(&buf);
|
||||
|
||||
if (read_oneliner(&opts->current_fixups,
|
||||
rebase_path_current_fixups(), 1)) {
|
||||
rebase_path_current_fixups(),
|
||||
READ_ONELINER_SKIP_IF_EMPTY)) {
|
||||
const char *p = opts->current_fixups.buf;
|
||||
opts->current_fixup_count = 1;
|
||||
while ((p = strchr(p, '\n'))) {
|
||||
@ -2557,12 +2552,16 @@ static int read_populate_opts(struct replay_opts *opts)
|
||||
}
|
||||
|
||||
if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
|
||||
if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
|
||||
return error(_("unusable squash-onto"));
|
||||
if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {
|
||||
ret = error(_("unusable squash-onto"));
|
||||
goto done_rebase_i;
|
||||
}
|
||||
opts->have_squash_onto = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
done_rebase_i:
|
||||
strbuf_release(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!file_exists(git_path_opts_file()))
|
||||
@ -3677,25 +3676,71 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_autostash(struct replay_opts *opts)
|
||||
void create_autostash(struct repository *r, const char *path,
|
||||
const char *default_reflog_action)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct lock_file lock_file = LOCK_INIT;
|
||||
int fd;
|
||||
|
||||
fd = repo_hold_locked_index(r, &lock_file, 0);
|
||||
refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
|
||||
if (0 <= fd)
|
||||
repo_update_index_if_able(r, &lock_file);
|
||||
rollback_lock_file(&lock_file);
|
||||
|
||||
if (has_unstaged_changes(r, 1) ||
|
||||
has_uncommitted_changes(r, 1)) {
|
||||
struct child_process stash = CHILD_PROCESS_INIT;
|
||||
struct object_id oid;
|
||||
|
||||
argv_array_pushl(&stash.args,
|
||||
"stash", "create", "autostash", NULL);
|
||||
stash.git_cmd = 1;
|
||||
stash.no_stdin = 1;
|
||||
strbuf_reset(&buf);
|
||||
if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
|
||||
die(_("Cannot autostash"));
|
||||
strbuf_trim_trailing_newline(&buf);
|
||||
if (get_oid(buf.buf, &oid))
|
||||
die(_("Unexpected stash response: '%s'"),
|
||||
buf.buf);
|
||||
strbuf_reset(&buf);
|
||||
strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
|
||||
|
||||
if (safe_create_leading_directories_const(path))
|
||||
die(_("Could not create directory for '%s'"),
|
||||
path);
|
||||
write_file(path, "%s", oid_to_hex(&oid));
|
||||
printf(_("Created autostash: %s\n"), buf.buf);
|
||||
if (reset_head(r, NULL, "reset --hard",
|
||||
NULL, RESET_HEAD_HARD, NULL, NULL,
|
||||
default_reflog_action) < 0)
|
||||
die(_("could not reset --hard"));
|
||||
|
||||
if (discard_index(r->index) < 0 ||
|
||||
repo_read_index(r) < 0)
|
||||
die(_("could not read index"));
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
|
||||
{
|
||||
struct strbuf stash_sha1 = STRBUF_INIT;
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
int ret = 0;
|
||||
|
||||
if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
|
||||
strbuf_release(&stash_sha1);
|
||||
return 0;
|
||||
if (attempt_apply) {
|
||||
child.git_cmd = 1;
|
||||
child.no_stdout = 1;
|
||||
child.no_stderr = 1;
|
||||
argv_array_push(&child.args, "stash");
|
||||
argv_array_push(&child.args, "apply");
|
||||
argv_array_push(&child.args, stash_oid);
|
||||
ret = run_command(&child);
|
||||
}
|
||||
strbuf_trim(&stash_sha1);
|
||||
|
||||
child.git_cmd = 1;
|
||||
child.no_stdout = 1;
|
||||
child.no_stderr = 1;
|
||||
argv_array_push(&child.args, "stash");
|
||||
argv_array_push(&child.args, "apply");
|
||||
argv_array_push(&child.args, stash_sha1.buf);
|
||||
if (!run_command(&child))
|
||||
if (attempt_apply && !ret)
|
||||
fprintf(stderr, _("Applied autostash.\n"));
|
||||
else {
|
||||
struct child_process store = CHILD_PROCESS_INIT;
|
||||
@ -3706,21 +3751,57 @@ static int apply_autostash(struct replay_opts *opts)
|
||||
argv_array_push(&store.args, "-m");
|
||||
argv_array_push(&store.args, "autostash");
|
||||
argv_array_push(&store.args, "-q");
|
||||
argv_array_push(&store.args, stash_sha1.buf);
|
||||
argv_array_push(&store.args, stash_oid);
|
||||
if (run_command(&store))
|
||||
ret = error(_("cannot store %s"), stash_sha1.buf);
|
||||
ret = error(_("cannot store %s"), stash_oid);
|
||||
else
|
||||
fprintf(stderr,
|
||||
_("Applying autostash resulted in conflicts.\n"
|
||||
_("%s\n"
|
||||
"Your changes are safe in the stash.\n"
|
||||
"You can run \"git stash pop\" or"
|
||||
" \"git stash drop\" at any time.\n"));
|
||||
" \"git stash drop\" at any time.\n"),
|
||||
attempt_apply ?
|
||||
_("Applying autostash resulted in conflicts.") :
|
||||
_("Autostash exists; creating a new stash entry."));
|
||||
}
|
||||
|
||||
strbuf_release(&stash_sha1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int apply_save_autostash(const char *path, int attempt_apply)
|
||||
{
|
||||
struct strbuf stash_oid = STRBUF_INIT;
|
||||
int ret = 0;
|
||||
|
||||
if (!read_oneliner(&stash_oid, path,
|
||||
READ_ONELINER_SKIP_IF_EMPTY)) {
|
||||
strbuf_release(&stash_oid);
|
||||
return 0;
|
||||
}
|
||||
strbuf_trim(&stash_oid);
|
||||
|
||||
ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
|
||||
|
||||
unlink(path);
|
||||
strbuf_release(&stash_oid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int save_autostash(const char *path)
|
||||
{
|
||||
return apply_save_autostash(path, 0);
|
||||
}
|
||||
|
||||
int apply_autostash(const char *path)
|
||||
{
|
||||
return apply_save_autostash(path, 1);
|
||||
}
|
||||
|
||||
int apply_autostash_oid(const char *stash_oid)
|
||||
{
|
||||
return apply_save_autostash_oid(stash_oid, 1);
|
||||
}
|
||||
|
||||
static const char *reflog_message(struct replay_opts *opts,
|
||||
const char *sub_action, const char *fmt, ...)
|
||||
{
|
||||
@ -3776,7 +3857,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
|
||||
return error(_("%s: not a valid OID"), orig_head);
|
||||
|
||||
if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
return error(_("could not detach HEAD"));
|
||||
}
|
||||
@ -4095,7 +4176,7 @@ cleanup_head_ref:
|
||||
run_command(&hook);
|
||||
}
|
||||
}
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
|
||||
if (!opts->quiet) {
|
||||
if (!opts->verbose)
|
||||
@ -4313,7 +4394,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct object_id oid;
|
||||
|
||||
if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
|
||||
if (read_oneliner(&buf, rebase_path_stopped_sha(),
|
||||
READ_ONELINER_SKIP_IF_EMPTY) &&
|
||||
!get_oid_committish(buf.buf, &oid))
|
||||
record_in_rewritten(&oid, peek_command(&todo_list, 0));
|
||||
strbuf_release(&buf);
|
||||
@ -5118,7 +5200,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
|
||||
todo_list_add_exec_commands(todo_list, commands);
|
||||
|
||||
if (count_commands(todo_list) == 0) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
|
||||
return error(_("nothing to do"));
|
||||
@ -5129,12 +5211,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
|
||||
if (res == -1)
|
||||
return -1;
|
||||
else if (res == -2) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
|
||||
return -1;
|
||||
} else if (res == -3) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
todo_list_release(&new_todo);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user