All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paul Tan <pyokagan@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>,
	Stefan Beller <sbeller@google.com>, Paul Tan <pyokagan@gmail.com>
Subject: [PATCH v6 00/45] Make git-am a builtin
Date: Mon, 20 Jul 2015 00:09:52 +0800	[thread overview]
Message-ID: <1437322237-29863-1-git-send-email-pyokagan@gmail.com> (raw)

This patch series depends on pt/pull-builtin.

This is a re-roll of [v5]. The changes are as follows:

* Compacted the struct am_state declaration, so there are no ugly newlines
  between every field.

* Removed unnecessary assert()s.

* Simplified "if (x) free(x)" to just free(x)

* Only alloc memory for lock_file just before calling hold_locked_index() to
  prevent memory leaks.

* Added a patch to re-implement 271440e (git-am: make it easier after fixing up
  an unapplicable patch., 2005-10-25).

Thanks Junio and Stefan for the reviews last round. Interdiff below.

Previous versions:

[WIP v1] http://thread.gmane.org/gmane.comp.version-control.git/270048
[WIP v2] http://thread.gmane.org/gmane.comp.version-control.git/271381
[WIP v3] http://thread.gmane.org/gmane.comp.version-control.git/271967
[v4] http://thread.gmane.org/gmane.comp.version-control.git/272876
[v5] http://thread.gmane.org/gmane.comp.version-control.git/273520

git-am is a commonly used command for applying a series of patches from a
mailbox to the current branch. Currently, it is implemented by the shell script
git-am.sh. However, compared to C, shell scripts have certain deficiencies:
they need to spawn a lot of processes, introduce a lot of dependencies and
cannot take advantage of git's internal caches.

This patch series rewrites git-am.sh into C builtin/am.c, and is part of my
GSoC project to rewrite git-pull and git-am into C builtins[1].

[1] https://gist.github.com/pyokagan/1b7b0d1f4dab6ba3cef1


Paul Tan (45):
  wrapper: implement xopen()
  wrapper: implement xfopen()
  builtin-am: implement skeletal builtin am
  builtin-am: implement patch queue mechanism
  builtin-am: split out mbox/maildir patches with git-mailsplit
  builtin-am: auto-detect mbox patches
  builtin-am: extract patch and commit info with git-mailinfo
  builtin-am: apply patch with git-apply
  builtin-am: implement committing applied patch
  builtin-am: refuse to apply patches if index is dirty
  builtin-am: implement --resolved/--continue
  builtin-am: don't parse mail when resuming
  builtin-am: implement --skip
  builtin-am: implement --abort
  builtin-am: reject patches when there's a session in progress
  builtin-am: implement -q/--quiet
  builtin-am: exit with user friendly message on failure
  builtin-am: implement -s/--signoff
  cache-tree: introduce write_index_as_tree()
  builtin-am: implement --3way, am.threeWay
  builtin-am: implement --rebasing mode
  builtin-am: bypass git-mailinfo when --rebasing
  builtin-am: handle stray state directory
  builtin-am: implement -u/--utf8
  builtin-am: implement -k/--keep, --keep-non-patch
  builtin-am: implement --[no-]message-id, am.messageid
  builtin-am: support --keep-cr, am.keepcr
  builtin-am: implement --[no-]scissors
  builtin-am: pass git-apply's options to git-apply
  builtin-am: implement --ignore-date
  builtin-am: implement --committer-date-is-author-date
  builtin-am: implement -S/--gpg-sign, commit.gpgsign
  builtin-am: invoke post-rewrite hook
  builtin-am: support automatic notes copying
  builtin-am: invoke applypatch-msg hook
  builtin-am: invoke pre-applypatch hook
  builtin-am: invoke post-applypatch hook
  builtin-am: rerere support
  builtin-am: support and auto-detect StGit patches
  builtin-am: support and auto-detect StGit series files
  builtin-am: support and auto-detect mercurial patches
  builtin-am: implement -i/--interactive
  builtin-am: implement legacy -b/--binary option
  builtin-am: check for valid committer ident
  builtin-am: remove redirection to git-am.sh

 Makefile                                |    2 +-
 builtin.h                               |    1 +
 builtin/am.c                            | 2320 +++++++++++++++++++++++++++++++
 cache-tree.c                            |   29 +-
 cache-tree.h                            |    1 +
 git-am.sh => contrib/examples/git-am.sh |    0
 git-compat-util.h                       |    2 +
 git.c                                   |    1 +
 wrapper.c                               |   56 +
 9 files changed, 2399 insertions(+), 13 deletions(-)
 create mode 100644 builtin/am.c
 rename git-am.sh => contrib/examples/git-am.sh (100%)

diff --git a/builtin/am.c b/builtin/am.c
index c548129..1116304 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -119,38 +119,21 @@ struct am_state {
 	/* number of digits in patch filename */
 	int prec;
 
+	/* various operating modes and command line options */
 	int interactive;
-
 	int threeway;
-
 	int quiet;
-
 	int append_signoff;
-
 	int utf8;
-
-	/* one of the enum keep_type values */
-	int keep;
-
-	/* pass -m flag to git-mailinfo */
+	int keep; /* enum keep_type */
 	int message_id;
-
-	/* one of the enum scissors_type values */
-	int scissors;
-
+	int scissors; /* enum scissors_type */
 	struct argv_array git_apply_opts;
-
-	/* override error message when patch failure occurs */
 	const char *resolvemsg;
-
 	int committer_date_is_author_date;
-
 	int ignore_date;
-
 	int allow_rerere_autoupdate;
-
 	const char *sign_commit;
-
 	int rebasing;
 };
 
@@ -188,21 +171,11 @@ static void am_state_init(struct am_state *state, const char *dir)
  */
 static void am_state_release(struct am_state *state)
 {
-	if (state->dir)
-		free(state->dir);
-
-	if (state->author_name)
-		free(state->author_name);
-
-	if (state->author_email)
-		free(state->author_email);
-
-	if (state->author_date)
-		free(state->author_date);
-
-	if (state->msg)
-		free(state->msg);
-
+	free(state->dir);
+	free(state->author_name);
+	free(state->author_email);
+	free(state->author_date);
+	free(state->msg);
 	argv_array_clear(&state->git_apply_opts);
 }
 
@@ -211,8 +184,6 @@ static void am_state_release(struct am_state *state)
  */
 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);
 }
 
@@ -373,10 +344,6 @@ static void write_author_script(const struct am_state *state)
 {
 	struct strbuf sb = STRBUF_INIT;
 
-	assert(state->author_name);
-	assert(state->author_email);
-	assert(state->author_date);
-
 	strbuf_addstr(&sb, "GIT_AUTHOR_NAME=");
 	sq_quote_buf(&sb, state->author_name);
 	strbuf_addch(&sb, '\n');
@@ -424,8 +391,6 @@ static void write_commit_msg(const struct am_state *state)
 	int fd;
 	const char *filename = am_path(state, "final-commit");
 
-	assert(state->msg);
-
 	fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
 	if (write_in_full(fd, state->msg, state->msg_len) < 0)
 		die_errno(_("could not write to %s"), filename);
@@ -1110,20 +1075,16 @@ static void am_next(struct am_state *state)
 {
 	unsigned char head[GIT_SHA1_RAWSZ];
 
-	if (state->author_name)
-		free(state->author_name);
+	free(state->author_name);
 	state->author_name = NULL;
 
-	if (state->author_email)
-		free(state->author_email);
+	free(state->author_email);
 	state->author_email = NULL;
 
-	if (state->author_date)
-		free(state->author_date);
+	free(state->author_date);
 	state->author_date = NULL;
 
-	if (state->msg)
-		free(state->msg);
+	free(state->msg);
 	state->msg = NULL;
 	state->msg_len = 0;
 
@@ -1160,13 +1121,12 @@ static const char *msgnum(const struct am_state *state)
  */
 static void refresh_and_write_cache(void)
 {
-	static struct lock_file lock_file;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-	hold_locked_index(&lock_file, 1);
+	hold_locked_index(lock_file, 1);
 	refresh_cache(REFRESH_QUIET);
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 		die(_("unable to write index file"));
-	rollback_lock_file(&lock_file);
 }
 
 /**
@@ -1714,6 +1674,21 @@ static void do_commit(const struct am_state *state)
 }
 
 /**
+ * Validates the am_state for resuming -- the "msg" and authorship fields must
+ * be filled up.
+ */
+static void validate_resume_state(const struct am_state *state)
+{
+	if (!state->msg)
+		die(_("cannot resume: %s does not exist."),
+			am_path(state, "final-commit"));
+
+	if (!state->author_name || !state->author_email || !state->author_date)
+		die(_("cannot resume: %s does not exist."),
+			am_path(state, "author-script"));
+}
+
+/**
  * Interactively prompt the user on whether the current patch should be
  * applied.
  *
@@ -1774,8 +1749,12 @@ static int do_interactive(struct am_state *state)
 
 /**
  * Applies all queued mail.
+ *
+ * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
+ * well as the state directory's "patch" file is used as-is for applying the
+ * patch and committing it.
  */
-static void am_run(struct am_state *state)
+static void am_run(struct am_state *state, int resume)
 {
 	const char *argv_gc_auto[] = {"gc", "--auto", NULL};
 	struct strbuf sb = STRBUF_INIT;
@@ -1793,21 +1772,28 @@ static void am_run(struct am_state *state)
 
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
-		int apply_status, skip;
+		int apply_status;
 
 		if (!file_exists(mail))
 			goto next;
 
-		if (state->rebasing)
-			skip = parse_mail_rebase(state, mail);
-		else
-			skip = parse_mail(state, mail);
+		if (resume) {
+			validate_resume_state(state);
+			resume = 0;
+		} else {
+			int skip;
 
-		if (skip)
-			goto next; /* mail should be skipped */
+			if (state->rebasing)
+				skip = parse_mail_rebase(state, mail);
+			else
+				skip = parse_mail(state, mail);
+
+			if (skip)
+				goto next; /* mail should be skipped */
 
-		write_author_script(state);
-		write_commit_msg(state);
+			write_author_script(state);
+			write_commit_msg(state);
+		}
 
 		if (state->interactive && do_interactive(state))
 			goto next;
@@ -1880,13 +1866,7 @@ next:
  */
 static void am_resolve(struct am_state *state)
 {
-	if (!state->msg)
-		die(_("cannot resume: %s does not exist."),
-			am_path(state, "final-commit"));
-
-	if (!state->author_name || !state->author_email || !state->author_date)
-		die(_("cannot resume: %s does not exist."),
-			am_path(state, "author-script"));
+	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1915,7 +1895,7 @@ static void am_resolve(struct am_state *state)
 
 next:
 	am_next(state);
-	am_run(state);
+	am_run(state, 0);
 }
 
 /**
@@ -1925,13 +1905,14 @@ next:
  */
 static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 {
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+	struct lock_file *lock_file;
 	struct unpack_trees_options opts;
 	struct tree_desc t[2];
 
 	if (parse_tree(head) || parse_tree(remote))
 		return -1;
 
+	lock_file = xcalloc(1, sizeof(struct lock_file));
 	hold_locked_index(lock_file, 1);
 
 	refresh_cache(REFRESH_QUIET);
@@ -1964,7 +1945,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
  */
 static int clean_index(const unsigned char *head, const unsigned char *remote)
 {
-	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+	struct lock_file *lock_file;
 	struct tree *head_tree, *remote_tree, *index_tree;
 	unsigned char index[GIT_SHA1_RAWSZ];
 	struct pathspec pathspec;
@@ -1994,6 +1975,7 @@ static int clean_index(const unsigned char *head, const unsigned char *remote)
 
 	memset(&pathspec, 0, sizeof(pathspec));
 
+	lock_file = xcalloc(1, sizeof(struct lock_file));
 	hold_locked_index(lock_file, 1);
 
 	if (read_tree(remote_tree, 0, &pathspec)) {
@@ -2040,7 +2022,7 @@ static void am_skip(struct am_state *state)
 		die(_("failed to clean index"));
 
 	am_next(state);
-	am_run(state);
+	am_run(state, 0);
 }
 
 /**
@@ -2136,6 +2118,7 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
 
 enum resume_mode {
 	RESUME_FALSE = 0,
+	RESUME_APPLY,
 	RESUME_RESOLVED,
 	RESUME_SKIP,
 	RESUME_ABORT
@@ -2271,6 +2254,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 			die(_("previous rebase directory %s still exists but mbox given."),
 				state.dir);
 
+		if (resume == RESUME_FALSE)
+			resume = RESUME_APPLY;
+
 		am_load(&state);
 	} else {
 		struct argv_array paths = ARGV_ARRAY_INIT;
@@ -2310,7 +2296,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
 	switch (resume) {
 	case RESUME_FALSE:
-		am_run(&state);
+		am_run(&state, 0);
+		break;
+	case RESUME_APPLY:
+		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
 		am_resolve(&state);

-- 
2.5.0.rc2.110.gb39b692

             reply	other threads:[~2015-07-19 16:10 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-19 16:09 Paul Tan [this message]
2015-07-19 16:09 ` [PATCH v6 01/45] wrapper: implement xopen() Paul Tan
2015-07-19 16:09 ` [PATCH v6 02/45] wrapper: implement xfopen() Paul Tan
2015-07-19 16:09 ` [PATCH v6 03/45] builtin-am: implement skeletal builtin am Paul Tan
2015-07-19 16:09 ` [PATCH v6 04/45] builtin-am: implement patch queue mechanism Paul Tan
2015-07-19 16:09 ` [PATCH v6 05/45] builtin-am: split out mbox/maildir patches with git-mailsplit Paul Tan
2015-07-19 16:09 ` [PATCH v6 06/45] builtin-am: auto-detect mbox patches Paul Tan
2015-07-19 16:09 ` [PATCH v6 07/45] builtin-am: extract patch and commit info with git-mailinfo Paul Tan
2015-07-19 16:10 ` [PATCH v6 08/45] builtin-am: apply patch with git-apply Paul Tan
2015-07-19 16:10 ` [PATCH v6 09/45] builtin-am: implement committing applied patch Paul Tan
2015-07-19 16:10 ` [PATCH v6 10/45] builtin-am: refuse to apply patches if index is dirty Paul Tan
2015-07-19 16:10 ` [PATCH v6 11/45] builtin-am: implement --resolved/--continue Paul Tan
2015-07-19 16:10 ` [PATCH v6 12/45] builtin-am: don't parse mail when resuming Paul Tan
2015-07-19 16:10 ` [PATCH v6 13/45] builtin-am: implement --skip Paul Tan
2015-07-19 16:10 ` [PATCH v6 14/45] builtin-am: implement --abort Paul Tan
2015-07-31 23:42   ` Stefan Beller
2015-07-19 16:10 ` [PATCH v6 15/45] builtin-am: reject patches when there's a session in progress Paul Tan
2015-07-19 16:10 ` [PATCH v6 16/45] builtin-am: implement -q/--quiet Paul Tan
2015-07-19 16:10 ` [PATCH v6 17/45] builtin-am: exit with user friendly message on failure Paul Tan
2015-07-19 16:10 ` [PATCH v6 18/45] builtin-am: implement -s/--signoff Paul Tan
2015-07-19 16:10 ` [PATCH v6 19/45] cache-tree: introduce write_index_as_tree() Paul Tan
2015-07-19 16:10 ` [PATCH v6 20/45] builtin-am: implement --3way, am.threeWay Paul Tan
2015-07-19 16:10 ` [PATCH v6 21/45] builtin-am: implement --rebasing mode Paul Tan
2015-07-19 16:10 ` [PATCH v6 22/45] builtin-am: bypass git-mailinfo when --rebasing Paul Tan
2015-07-19 16:10 ` [PATCH v6 23/45] builtin-am: handle stray state directory Paul Tan
2015-07-19 16:10 ` [PATCH v6 24/45] builtin-am: implement -u/--utf8 Paul Tan
2015-07-19 16:10 ` [PATCH v6 25/45] builtin-am: implement -k/--keep, --keep-non-patch Paul Tan
2015-07-19 16:10 ` [PATCH v6 26/45] builtin-am: implement --[no-]message-id, am.messageid Paul Tan
2015-07-19 16:10 ` [PATCH v6 27/45] builtin-am: support --keep-cr, am.keepcr Paul Tan
2015-07-19 16:10 ` [PATCH v6 28/45] builtin-am: implement --[no-]scissors Paul Tan
2015-07-19 16:10 ` [PATCH v6 29/45] builtin-am: pass git-apply's options to git-apply Paul Tan
2015-07-19 16:10 ` [PATCH v6 30/45] builtin-am: implement --ignore-date Paul Tan
2015-07-19 16:10 ` [PATCH v6 31/45] builtin-am: implement --committer-date-is-author-date Paul Tan
2015-07-19 16:10 ` [PATCH v6 32/45] builtin-am: implement -S/--gpg-sign, commit.gpgsign Paul Tan
2015-07-19 16:10 ` [PATCH v6 33/45] builtin-am: invoke post-rewrite hook Paul Tan
2015-07-19 16:10 ` [PATCH v6 34/45] builtin-am: support automatic notes copying Paul Tan
2015-07-19 16:10 ` [PATCH v6 35/45] builtin-am: invoke applypatch-msg hook Paul Tan
2015-07-19 16:10 ` [PATCH v6 36/45] builtin-am: invoke pre-applypatch hook Paul Tan
2015-07-19 16:10 ` [PATCH v6 37/45] builtin-am: invoke post-applypatch hook Paul Tan
2015-07-19 16:10 ` [PATCH v6 38/45] builtin-am: rerere support Paul Tan
2015-07-19 16:10 ` [PATCH v6 39/45] builtin-am: support and auto-detect StGit patches Paul Tan
2015-07-19 16:10 ` [PATCH v6 40/45] builtin-am: support and auto-detect StGit series files Paul Tan
2015-07-19 16:10 ` [PATCH v6 41/45] builtin-am: support and auto-detect mercurial patches Paul Tan
2015-07-19 16:10 ` [PATCH v6 42/45] builtin-am: implement -i/--interactive Paul Tan
2015-07-19 16:10 ` [PATCH v6 43/45] builtin-am: implement legacy -b/--binary option Paul Tan
2015-07-19 16:10 ` [PATCH v6 44/45] builtin-am: check for valid committer ident Paul Tan
2015-07-19 16:10 ` [PATCH v6 45/45] builtin-am: remove redirection to git-am.sh Paul Tan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1437322237-29863-1-git-send-email-pyokagan@gmail.com \
    --to=pyokagan@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=johannes.schindelin@gmx.de \
    --cc=sbeller@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.