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

[PATCH v4 04/44] builtin-am: implement patch queue mechanism

From
Paul Tan <pyokagan@gmail.com>
Date
Jun 28, 2015, 14:05 UTC
Message-ID
<1435500366-31700-5-git-send-email-pyokagan@gmail.com>
In-Reply-To
<1435500366-31700-1-git-send-email-pyokagan@gmail.com>

git-am applies a series of patches. If the process terminates abnormally, we want to be able to resume applying the series of patches. This requires the session state to be saved in a persistent location.

Implement the mechanism of a "patch queue", represented by 2 integers -- the index of the current patch we are applying and the index of the last patch, as well as its lifecycle through the following functions:

* am_setup(), which will set up the state directory
  $GIT_DIR/rebase-apply. As such, even if the process exits abnormally,
  the last-known state will still persist.
* am_load(), which is called if there is an am session in
  progress, to load the last known state from the state directory so we
  can resume applying patches.
* am_run(), which will do the actual patch application. After applying a
  patch, it calls am_next() to increment the current patch index. The
  logic for applying and committing a patch is not implemented yet.
* am_destroy(), which is finally called when we successfully applied all
  the patches in the queue, to clean up by removing the state directory
  and its contents.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Stefan Beller <sbeller@google.com>
Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Paul Tan <pyokagan@gmail.com>
---
Notes:
    v4
    
    * Corrected docstring of read_state_file()
    
    * Corrected docstring of am_state_release()
    
    * am_state's "dir" field is now a char*. To help API users,
      am_state_init() takes an additional const char *dir argument.
    
    * The opt_* option variables, am_options[] and am_usage[] have been
      moved into cmd_am()'s scope.
    
    * signature of read_state_file() has been changed to
      read_state_file(strbuf, state, file, trim)
 builtin/am.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 180 insertions(+)
diff --git a/builtin/am.c b/builtin/am.c
index fd32caf..5b4e9af 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -6,9 +6,174 @@
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
+#include "parse-options.h"
+#include "dir.h"
+
+struct am_state {
+	/* state directory path */
+	char *dir;
+
+	/* current and last patch numbers, 1-indexed */
+	int cur;
+	int last;
+};
+
+/**
+ * Initializes am_state with the default values. The state directory is set to
+ * dir.
+ */
+static void am_state_init(struct am_state *state, const char *dir)
+{
+	memset(state, 0, sizeof(*state));
+
+	assert(dir);
+	state->dir = xstrdup(dir);
+}
+
+/**
+ * Releases memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+	if (state->dir)
+		free(state->dir);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char *path)
+{
+	assert(state->dir);
+	assert(path);
+	return mkpath("%s/%s", state->dir, path);
+}
+
+/**
+ * Returns 1 if there is an am session in progress, 0 otherwise.
+ */
+static int am_in_progress(const struct am_state *state)
+{
+	struct stat st;
+
+	if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode))
+		return 0;
+	if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode))
+		return 0;
+	if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode))
+		return 0;
+	return 1;
+}
+
+/**
+ * Reads the contents of `file` in the `state` directory into `sb`. Returns the
+ * number of bytes read on success, -1 if the file does not exist. If `trim` is
+ * set, trailing whitespace will be removed.
+ */
+static int read_state_file(struct strbuf *sb, const struct am_state *state,
+			const char *file, int trim)
+{
+	strbuf_reset(sb);
+
+	if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) {
+		if (trim)
+			strbuf_trim(sb);
+
+		return sb->len;
+	}
+
+	if (errno == ENOENT)
+		return -1;
+
+	die_errno(_("could not read '%s'"), am_path(state, file));
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_load(struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (read_state_file(&sb, state, "next", 1) < 0)
+		die("BUG: state file 'next' does not exist");
+	state->cur = strtol(sb.buf, NULL, 10);
+
+	if (read_state_file(&sb, state, "last", 1) < 0)
+		die("BUG: state file 'last' does not exist");
+	state->last = strtol(sb.buf, NULL, 10);
+
+	strbuf_release(&sb);
+}
+
+/**
+ * Removes the am_state directory, forcefully terminating the current am
+ * session.
+ */
+static void am_destroy(const struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_addstr(&sb, state->dir);
+	remove_dir_recursively(&sb, 0);
+	strbuf_release(&sb);
+}
+
+/**
+ * Setup a new am session for applying patches
+ */
+static void am_setup(struct am_state *state)
+{
+	if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
+		die_errno(_("failed to create directory '%s'"), state->dir);
+
+	/*
+	 * NOTE: Since the "next" and "last" files determine if an am_state
+	 * session is in progress, they should be written last.
+	 */
+
+	write_file(am_path(state, "next"), 1, "%d", state->cur);
+
+	write_file(am_path(state, "last"), 1, "%d", state->last);
+}
+
+/**
+ * Increments the patch pointer, and cleans am_state for the application of the
+ * next patch.
+ */
+static void am_next(struct am_state *state)
+{
+	state->cur++;
+	write_file(am_path(state, "next"), 1, "%d", state->cur);
+}
+
+/**
+ * Applies all queued mail.
+ */
+static void am_run(struct am_state *state)
+{
+	while (state->cur <= state->last) {
+
+		/* TODO: Patch application not implemented yet */
+
+		am_next(state);
+	}
+
+	am_destroy(state);
+}
 
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
+	struct am_state state;
+
+	const char * const usage[] = {
+		N_("git am [options] [(<mbox>|<Maildir>)...]"),
+		NULL
+	};
+
+	struct option options[] = {
+		OPT_END()
+	};
 
 	/*
 	 * NEEDSWORK: Once all the features of git-am.sh have been
@@ -25,5 +190,20 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		setup_work_tree();
 	}
 
+	git_config(git_default_config, NULL);
+
+	am_state_init(&state, git_path("rebase-apply"));
+
+	argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+	if (am_in_progress(&state))
+		am_load(&state);
+	else
+		am_setup(&state);
+
+	am_run(&state);
+
+	am_state_release(&state);
+
 	return 0;
 }
-- 
2.5.0.rc0.76.gb2c6e93
Previous: Paul TanNext: Stefan Beller
Message 10 of 74 in “Make git-am a builtin”
  1. 00/44 Make git-am a builtinPaul Tan, Jun 28, 2015
  2. 01/44 wrapper: implement xopen()Paul Tan, Jun 28, 2015
  3. Torsten BögershausenJun 29, 2015
  4. Stefan BellerJun 29, 2015
  5. Junio C HamanoJun 29, 2015
  6. Paul TanJul 1, 2015
  7. Paul TanJul 1, 2015
  8. 02/44 wrapper: implement xfopen()Paul Tan, Jun 28, 2015
  9. 03/44 builtin-am: implement skeletal builtin amPaul Tan, Jun 28, 2015
  10. 04/44 builtin-am: implement patch queue mechanismPaul Tan, Jun 28, 2015
  11. Stefan BellerJun 29, 2015
  12. Paul TanJul 7, 2015
  13. 05/44 builtin-am: split out mbox/maildir patches with git-mailsplitPaul Tan, Jun 28, 2015
  14. 06/44 builtin-am: auto-detect mbox patchesPaul Tan, Jun 28, 2015
  15. 07/44 builtin-am: extract patch and commit info with git-mailinfoPaul Tan, Jun 28, 2015
  16. 08/44 builtin-am: apply patch with git-applyPaul Tan, Jun 28, 2015
  17. 09/44 builtin-am: implement committing applied patchPaul Tan, Jun 28, 2015
  18. 10/44 builtin-am: refuse to apply patches if index is dirtyPaul Tan, Jun 28, 2015
  19. 11/44 builtin-am: implement --resolved/--continuePaul Tan, Jun 28, 2015
  20. 12/44 builtin-am: implement --skipPaul Tan, Jun 28, 2015
  21. 13/44 builtin-am: implement --abortPaul Tan, Jun 28, 2015
  22. 14/44 builtin-am: reject patches when there's a session in progressPaul Tan, Jun 28, 2015
  23. 15/44 builtin-am: implement -q/--quiet, GIT_QUIETPaul Tan, Jun 28, 2015
  24. 16/44 builtin-am: exit with user friendly message on failurePaul Tan, Jun 28, 2015
  25. 17/44 builtin-am: implement -s/--signoffPaul Tan, Jun 28, 2015
  26. 18/44 cache-tree: introduce write_index_as_tree()Paul Tan, Jun 28, 2015
  27. 19/44 builtin-am: implement --3way, am.threewayPaul Tan, Jun 28, 2015
  28. Stefan BellerJun 30, 2015
  29. 20/44 builtin-am: implement --rebasing modePaul Tan, Jun 28, 2015
  30. 21/44 builtin-am: bypass git-mailinfo when --rebasingPaul Tan, Jun 28, 2015
  31. 22/44 builtin-am: handle stray state directoryPaul Tan, Jun 28, 2015
  32. 23/44 builtin-am: implement -u/--utf8Paul Tan, Jun 28, 2015
  33. 24/44 builtin-am: implement -k/--keep, --keep-non-patchPaul Tan, Jun 28, 2015
  34. 25/44 builtin-am: implement --[no-]message-id, am.messageidPaul Tan, Jun 28, 2015
  35. 26/44 builtin-am: support --keep-cr, am.keepcrPaul Tan, Jun 28, 2015
  36. 27/44 builtin-am: implement --[no-]scissorsPaul Tan, Jun 28, 2015
  37. Paul TanJul 7, 2015
  38. 28/44 builtin-am: pass git-apply's options to git-applyPaul Tan, Jun 28, 2015
  39. Stefan BellerJun 29, 2015
  40. Paul TanJul 1, 2015
  41. Stefan BellerJul 1, 2015
  42. 29/44 builtin-am: implement --ignore-datePaul Tan, Jun 28, 2015
  43. 30/44 builtin-am: implement --committer-date-is-author-datePaul Tan, Jun 28, 2015
  44. 31/44 builtin-am: implement -S/--gpg-sign, commit.gpgsignPaul Tan, Jun 28, 2015
  45. Stefan BellerJun 29, 2015
  46. Paul TanJul 1, 2015
  47. Stefan BellerJul 1, 2015
  48. 32/44 builtin-am: invoke post-rewrite hookPaul Tan, Jun 28, 2015
  49. 33/44 builtin-am: support automatic notes copyingPaul Tan, Jun 28, 2015
  50. 34/44 builtin-am: invoke applypatch-msg hookPaul Tan, Jun 28, 2015
  51. 35/44 builtin-am: invoke pre-applypatch hookPaul Tan, Jun 28, 2015
  52. 36/44 builtin-am: invoke post-applypatch hookPaul Tan, Jun 28, 2015
  53. 37/44 builtin-am: rerere supportPaul Tan, Jun 28, 2015
  54. 38/44 builtin-am: support and auto-detect StGit patchesPaul Tan, Jun 28, 2015
  55. Stefan BellerJun 29, 2015
  56. Eric SunshineJun 29, 2015
  57. Junio C HamanoJun 29, 2015
  58. Paul TanJul 1, 2015
  59. 39/44 builtin-am: support and auto-detect StGit series filesPaul Tan, Jun 28, 2015
  60. 40/44 builtin-am: support and auto-detect mercurial patchesPaul Tan, Jun 28, 2015
  61. Stefan BellerJun 29, 2015
  62. Stefan BellerJun 29, 2015
  63. Paul TanJul 1, 2015
  64. Paul TanJul 1, 2015
  65. 41/44 builtin-am: implement -i/--interactivePaul Tan, Jun 28, 2015
  66. 42/44 builtin-am: implement legacy -b/--binary optionPaul Tan, Jun 28, 2015
  67. Stefan BellerJun 29, 2015
  68. 43/44 builtin-am: check for valid committer identPaul Tan, Jun 28, 2015
  69. Stefan BellerJun 29, 2015
  70. Paul TanJul 1, 2015
  71. 44/44 builtin-am: remove redirection to git-am.shPaul Tan, Jun 28, 2015
  72. Stefan BellerJun 29, 2015
  73. Stefan BellerJun 30, 2015
  74. Paul TanJul 14, 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.