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_state_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.
Signed-off-by: Paul Tan pyoka...@gmail.com
---
Makefile | 2 +-
builtin.h| 1 +
builtin/am.c | 167 +++
git.c| 1 +
4 files changed, 170 insertions(+), 1 deletion(-)
create mode 100644 builtin/am.c
diff --git a/Makefile b/Makefile
index 323c401..57a7c8c 100644
--- a/Makefile
+++ b/Makefile
@@ -466,7 +466,6 @@ TEST_PROGRAMS_NEED_X =
# interactive shell sessions without exporting it.
unexport CDPATH
-SCRIPT_SH += git-am.sh
SCRIPT_SH += git-bisect.sh
SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh
@@ -812,6 +811,7 @@ LIB_OBJS += xdiff-interface.o
LIB_OBJS += zlib.o
BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/am.o
BUILTIN_OBJS += builtin/annotate.o
BUILTIN_OBJS += builtin/apply.o
BUILTIN_OBJS += builtin/archive.o
diff --git a/builtin.h b/builtin.h
index b87df70..d50c9d1 100644
--- a/builtin.h
+++ b/builtin.h
@@ -30,6 +30,7 @@ extern int textconv_object(const char *path, unsigned mode,
const unsigned char
extern int is_builtin(const char *s);
extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_am(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_apply(int argc, const char **argv, const char *prefix);
extern int cmd_archive(int argc, const char **argv, const char *prefix);
diff --git a/builtin/am.c b/builtin/am.c
new file mode 100644
index 000..6c9
--- /dev/null
+++ b/builtin/am.c
@@ -0,0 +1,167 @@
+/*
+ * Builtin git am
+ *
+ * Based on git-am.sh by Junio C Hamano.
+ */
+#include cache.h
+#include parse-options.h
+#include dir.h
+
+struct am_state {
+ struct strbuf dir;/* state directory path */
+ int cur; /* current patch number */
+ int last; /* last patch number */
+};
+
+/**
+ * Initializes am_state with the default values.
+ */
+static void am_state_init(struct am_state *state)
+{
+ memset(state, 0, sizeof(*state));
+
+ strbuf_init(state-dir, 0);
+}
+
+/**
+ * Release memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+ strbuf_release(state-dir);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char
*path)
+{
+ return mkpath(%s/%s, state-dir.buf, 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.buf, 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`. The third argument can be used to give a hint
+ * about the file size, to avoid reallocs. Returns 0 on success, -1 if the file
+ * does not exist.
+ */
+static int read_state_file(struct strbuf *sb, const char *file, size_t hint) {
+ strbuf_reset(sb);
+
+ if (!strbuf_read_file(sb, file, hint))
+ return 0;
+
+ if (errno == ENOENT)
+ return -1;
+
+ die_errno(_(could not read '%s'), file);
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_state_load(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ read_state_file(sb, am_path(state, next), 8);
+ state-cur = strtol(sb.buf, NULL, 10);
+
+ read_state_file(sb, am_path(state, last), 8);
+ state-last = strtol(sb.buf, NULL, 10);
+
+ strbuf_release(sb);
+}
+
+/**
+ * Remove the am_state directory.
+ */
+static