Skip to content

Commit

Permalink
builtin-am: implement -i/--interactive
Browse files Browse the repository at this point in the history
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh supported the --interactive mode. After parsing the patch mail
and extracting the patch, commit message and authorship info, an
interactive session will begin that allows the user to choose between:

* applying the patch

* applying the patch and all subsequent patches (by disabling
  interactive mode in subsequent patches)

* skipping the patch

* editing the commit message

Since f89ad67 (Add [v]iew patch in git-am interactive., 2005-10-25),
git-am.sh --interactive also supported viewing the patch to be applied.

When --resolved-ing in --interactive mode, we need to take care to
update the patch with the contents of the index, such that the correct
patch will be displayed when the patch is viewed in interactive mode.

Re-implement the above in builtin/am.c

Signed-off-by: Paul Tan <pyokagan@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Paul Tan authored and Junio C Hamano committed Aug 5, 2015
1 parent 94cd175 commit 7ff2683
Showing 1 changed file with 104 additions and 1 deletion.
105 changes: 104 additions & 1 deletion builtin/am.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "log-tree.h"
#include "notes-utils.h"
#include "rerere.h"
#include "prompt.h"

/**
* Returns 1 if the file is empty or does not exist, 0 otherwise.
Expand Down Expand Up @@ -119,6 +120,7 @@ struct am_state {
int prec;

/* various operating modes and command line options */
int interactive;
int threeway;
int quiet;
int signoff;
Expand Down Expand Up @@ -1171,7 +1173,7 @@ static void NORETURN die_user_resolve(const struct am_state *state)
if (state->resolvemsg) {
printf_ln("%s", state->resolvemsg);
} else {
const char *cmdline = "git am";
const char *cmdline = state->interactive ? "git am -i" : "git am";

printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
Expand Down Expand Up @@ -1403,6 +1405,36 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
log_tree_commit(&rev_info, commit);
}

/**
* Writes the diff of the index against HEAD as a patch to the state
* directory's "patch" file.
*/
static void write_index_patch(const struct am_state *state)
{
struct tree *tree;
unsigned char head[GIT_SHA1_RAWSZ];
struct rev_info rev_info;
FILE *fp;

if (!get_sha1_tree("HEAD", head))
tree = lookup_tree(head);
else
tree = lookup_tree(EMPTY_TREE_SHA1_BIN);

fp = xfopen(am_path(state, "patch"), "w");
init_revisions(&rev_info, NULL);
rev_info.diff = 1;
rev_info.disable_stdin = 1;
rev_info.no_commit_id = 1;
rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
rev_info.diffopt.use_color = 0;
rev_info.diffopt.file = fp;
rev_info.diffopt.close_file = 1;
add_pending_object(&rev_info, &tree->object, "");
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
}

/**
* Like parse_mail(), but parses the mail by looking up its commit ID
* directly. This is used in --rebasing mode to bypass git-mailinfo's munging
Expand Down Expand Up @@ -1654,6 +1686,65 @@ static void validate_resume_state(const struct am_state *state)
am_path(state, "author-script"));
}

/**
* Interactively prompt the user on whether the current patch should be
* applied.
*
* Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
* skip it.
*/
static int do_interactive(struct am_state *state)
{
assert(state->msg);

if (!isatty(0))
die(_("cannot be interactive without stdin connected to a terminal."));

for (;;) {
const char *reply;

puts(_("Commit Body is:"));
puts("--------------------------");
printf("%s", state->msg);
puts("--------------------------");

/*
* TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
* in your translation. The program will only accept English
* input at this point.
*/
reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);

if (!reply) {
continue;
} else if (*reply == 'y' || *reply == 'Y') {
return 0;
} else if (*reply == 'a' || *reply == 'A') {
state->interactive = 0;
return 0;
} else if (*reply == 'n' || *reply == 'N') {
return 1;
} else if (*reply == 'e' || *reply == 'E') {
struct strbuf msg = STRBUF_INIT;

if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) {
free(state->msg);
state->msg = strbuf_detach(&msg, &state->msg_len);
}
strbuf_release(&msg);
} else if (*reply == 'v' || *reply == 'V') {
const char *pager = git_pager(1);
struct child_process cp = CHILD_PROCESS_INIT;

if (!pager)
pager = "cat";
argv_array_push(&cp.args, pager);
argv_array_push(&cp.args, am_path(state, "patch"));
run_command(&cp);
}
}
}

/**
* Applies all queued mail.
*
Expand Down Expand Up @@ -1702,6 +1793,9 @@ static void am_run(struct am_state *state, int resume)
write_commit_msg(state);
}

if (state->interactive && do_interactive(state))
goto next;

if (run_applypatch_msg_hook(state))
exit(1);

Expand Down Expand Up @@ -1787,10 +1881,17 @@ static void am_resolve(struct am_state *state)
die_user_resolve(state);
}

if (state->interactive) {
write_index_patch(state);
if (do_interactive(state))
goto next;
}

rerere(0);

do_commit(state);

next:
am_next(state);
am_run(state, 0);
}
Expand Down Expand Up @@ -2036,6 +2137,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
};

struct option options[] = {
OPT_BOOL('i', "interactive", &state.interactive,
N_("run interactively")),
OPT_BOOL('3', "3way", &state.threeway,
N_("allow fall back on 3way merging if needed")),
OPT__QUIET(&state.quiet, N_("be quiet")),
Expand Down

0 comments on commit 7ff2683

Please sign in to comment.