git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v6 42/45] builtin-am: implement -i/--interactive

From
Paul Tan <pyokagan@gmail.com>
Date
Jul 19, 2015, 16:10 UTC
Message-ID
<1437322237-29863-43-git-send-email-pyokagan@gmail.com>
In-Reply-To
<1437322237-29863-1-git-send-email-pyokagan@gmail.com>

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>
---
Notes:
    Can't be tested because even with test_terminal isatty(0) still returns
    false.
 builtin/am.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 104 insertions(+), 1 deletion(-)
diff --git a/builtin/am.c b/builtin/am.c
index 8fe616e..39ea3c7 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -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.
@@ -119,6 +120,7 @@ struct am_state {
 	int prec;
 
 	/* various operating modes and command line options */
+	int interactive;
 	int threeway;
 	int quiet;
 	int append_signoff;
@@ -1173,7 +1175,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);
@@ -1406,6 +1408,36 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 }
 
 /**
+ * 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
  * of patches.
@@ -1657,6 +1689,65 @@ static void validate_resume_state(const struct am_state *state)
 }
 
 /**
+ * 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.
  *
  * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
@@ -1704,6 +1795,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);
 
@@ -1789,10 +1883,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);
 }
@@ -2037,6 +2138,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")),
-- 
2.5.0.rc2.110.gb39b692
Previous: Paul TanNext: Paul Tan
Message 44 of 47 in “Make git-am a builtin”
  1. 00/45 Make git-am a builtinPaul Tan, Jul 19, 2015
  2. 01/45 wrapper: implement xopen()Paul Tan, Jul 19, 2015
  3. 02/45 wrapper: implement xfopen()Paul Tan, Jul 19, 2015
  4. 03/45 builtin-am: implement skeletal builtin amPaul Tan, Jul 19, 2015
  5. 04/45 builtin-am: implement patch queue mechanismPaul Tan, Jul 19, 2015
  6. 05/45 builtin-am: split out mbox/maildir patches with git-mailsplitPaul Tan, Jul 19, 2015
  7. 06/45 builtin-am: auto-detect mbox patchesPaul Tan, Jul 19, 2015
  8. 07/45 builtin-am: extract patch and commit info with git-mailinfoPaul Tan, Jul 19, 2015
  9. 08/45 builtin-am: apply patch with git-applyPaul Tan, Jul 19, 2015
  10. 09/45 builtin-am: implement committing applied patchPaul Tan, Jul 19, 2015
  11. 10/45 builtin-am: refuse to apply patches if index is dirtyPaul Tan, Jul 19, 2015
  12. 11/45 builtin-am: implement --resolved/--continuePaul Tan, Jul 19, 2015
  13. 12/45 builtin-am: don't parse mail when resumingPaul Tan, Jul 19, 2015
  14. 13/45 builtin-am: implement --skipPaul Tan, Jul 19, 2015
  15. 14/45 builtin-am: implement --abortPaul Tan, Jul 19, 2015
  16. Stefan BellerJul 31, 2015
  17. 15/45 builtin-am: reject patches when there's a session in progressPaul Tan, Jul 19, 2015
  18. 16/45 builtin-am: implement -q/--quietPaul Tan, Jul 19, 2015
  19. 17/45 builtin-am: exit with user friendly message on failurePaul Tan, Jul 19, 2015
  20. 18/45 builtin-am: implement -s/--signoffPaul Tan, Jul 19, 2015
  21. 19/45 cache-tree: introduce write_index_as_tree()Paul Tan, Jul 19, 2015
  22. 20/45 builtin-am: implement --3way, am.threeWayPaul Tan, Jul 19, 2015
  23. 21/45 builtin-am: implement --rebasing modePaul Tan, Jul 19, 2015
  24. 22/45 builtin-am: bypass git-mailinfo when --rebasingPaul Tan, Jul 19, 2015
  25. 23/45 builtin-am: handle stray state directoryPaul Tan, Jul 19, 2015
  26. 24/45 builtin-am: implement -u/--utf8Paul Tan, Jul 19, 2015
  27. 25/45 builtin-am: implement -k/--keep, --keep-non-patchPaul Tan, Jul 19, 2015
  28. 26/45 builtin-am: implement --[no-]message-id, am.messageidPaul Tan, Jul 19, 2015
  29. 27/45 builtin-am: support --keep-cr, am.keepcrPaul Tan, Jul 19, 2015
  30. 28/45 builtin-am: implement --[no-]scissorsPaul Tan, Jul 19, 2015
  31. 29/45 builtin-am: pass git-apply's options to git-applyPaul Tan, Jul 19, 2015
  32. 30/45 builtin-am: implement --ignore-datePaul Tan, Jul 19, 2015
  33. 31/45 builtin-am: implement --committer-date-is-author-datePaul Tan, Jul 19, 2015
  34. 32/45 builtin-am: implement -S/--gpg-sign, commit.gpgsignPaul Tan, Jul 19, 2015
  35. 33/45 builtin-am: invoke post-rewrite hookPaul Tan, Jul 19, 2015
  36. 34/45 builtin-am: support automatic notes copyingPaul Tan, Jul 19, 2015
  37. 35/45 builtin-am: invoke applypatch-msg hookPaul Tan, Jul 19, 2015
  38. 36/45 builtin-am: invoke pre-applypatch hookPaul Tan, Jul 19, 2015
  39. 37/45 builtin-am: invoke post-applypatch hookPaul Tan, Jul 19, 2015
  40. 38/45 builtin-am: rerere supportPaul Tan, Jul 19, 2015
  41. 39/45 builtin-am: support and auto-detect StGit patchesPaul Tan, Jul 19, 2015
  42. 40/45 builtin-am: support and auto-detect StGit series filesPaul Tan, Jul 19, 2015
  43. 41/45 builtin-am: support and auto-detect mercurial patchesPaul Tan, Jul 19, 2015
  44. 42/45 builtin-am: implement -i/--interactivePaul Tan, Jul 19, 2015
  45. 43/45 builtin-am: implement legacy -b/--binary optionPaul Tan, Jul 19, 2015
  46. 44/45 builtin-am: check for valid committer identPaul Tan, Jul 19, 2015
  47. 45/45 builtin-am: remove redirection to git-am.shPaul Tan, Jul 19, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.