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 14/45] builtin-am: implement --abort
Date: Mon, 20 Jul 2015 00:10:06 +0800	[thread overview]
Message-ID: <1437322237-29863-15-git-send-email-pyokagan@gmail.com> (raw)
In-Reply-To: <1437322237-29863-1-git-send-email-pyokagan@gmail.com>

Since 3e5057a (git am --abort, 2008-07-16), git-am supported the --abort
option that will rewind HEAD back to the original commit. Re-implement
this through am_abort().

Since 7b3b7e3 (am --abort: keep unrelated commits since the last failure
and warn, 2010-12-21), to prevent commits made since the last failure
from being lost, git-am will not rewind HEAD back to the original
commit if HEAD moved since the last failure. Re-implement this through
safe_to_abort().

Signed-off-by: Paul Tan <pyokagan@gmail.com>
---
 builtin/am.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 99 insertions(+), 3 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 765844b..8a054ee 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -490,6 +490,8 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
 static void am_setup(struct am_state *state, enum patch_format patch_format,
 			const char **paths)
 {
+	unsigned char curr_head[GIT_SHA1_RAWSZ];
+
 	if (!patch_format)
 		patch_format = detect_patch_format(paths);
 
@@ -506,6 +508,14 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
 		die(_("Failed to split patches."));
 	}
 
+	if (!get_sha1("HEAD", curr_head)) {
+		write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(curr_head));
+		update_ref("am", "ORIG_HEAD", curr_head, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	} else {
+		write_file(am_path(state, "abort-safety"), 1, "%s", "");
+		delete_ref("ORIG_HEAD", NULL, 0);
+	}
+
 	/*
 	 * NOTE: Since the "next" and "last" files determine if an am_state
 	 * session is in progress, they should be written last.
@@ -522,6 +532,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
  */
 static void am_next(struct am_state *state)
 {
+	unsigned char head[GIT_SHA1_RAWSZ];
+
 	free(state->author_name);
 	state->author_name = NULL;
 
@@ -538,6 +550,11 @@ static void am_next(struct am_state *state)
 	unlink(am_path(state, "author-script"));
 	unlink(am_path(state, "final-commit"));
 
+	if (!get_sha1("HEAD", head))
+		write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(head));
+	else
+		write_file(am_path(state, "abort-safety"), 1, "%s", "");
+
 	state->cur++;
 	write_file(am_path(state, "next"), 1, "%d", state->cur);
 }
@@ -788,10 +805,14 @@ static void am_run(struct am_state *state, int resume)
 	const char *argv_gc_auto[] = {"gc", "--auto", NULL};
 	struct strbuf sb = STRBUF_INIT;
 
+	unlink(am_path(state, "dirtyindex"));
+
 	refresh_and_write_cache();
 
-	if (index_has_changes(&sb))
+	if (index_has_changes(&sb)) {
+		write_file(am_path(state, "dirtyindex"), 1, "t");
 		die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
+	}
 
 	strbuf_release(&sb);
 
@@ -980,6 +1001,74 @@ static void am_skip(struct am_state *state)
 }
 
 /**
+ * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
+ *
+ * It is not safe to reset HEAD when:
+ * 1. git-am previously failed because the index was dirty.
+ * 2. HEAD has moved since git-am previously failed.
+ */
+static int safe_to_abort(const struct am_state *state)
+{
+	struct strbuf sb = STRBUF_INIT;
+	unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
+
+	if (file_exists(am_path(state, "dirtyindex")))
+		return 0;
+
+	if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
+		if (get_sha1_hex(sb.buf, abort_safety))
+			die(_("could not parse %s"), am_path(state, "abort_safety"));
+	} else
+		hashclr(abort_safety);
+
+	if (get_sha1("HEAD", head))
+		hashclr(head);
+
+	if (!hashcmp(head, abort_safety))
+		return 1;
+
+	error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+		"Not rewinding to ORIG_HEAD"));
+
+	return 0;
+}
+
+/**
+ * Aborts the current am session if it is safe to do so.
+ */
+static void am_abort(struct am_state *state)
+{
+	unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+	int has_curr_head, has_orig_head;
+	const char *curr_branch;
+
+	if (!safe_to_abort(state)) {
+		am_destroy(state);
+		return;
+	}
+
+	curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
+	has_curr_head = !is_null_sha1(curr_head);
+	if (!has_curr_head)
+		hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+
+	has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
+	if (!has_orig_head)
+		hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+
+	clean_index(curr_head, orig_head);
+
+	if (has_orig_head)
+		update_ref("am --abort", "HEAD", orig_head,
+				has_curr_head ? curr_head : NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
+	else if (curr_branch)
+		delete_ref(curr_branch, NULL, REF_NODEREF);
+
+	am_destroy(state);
+}
+
+/**
  * parse_options() callback that validates and sets opt->value to the
  * PATCH_FORMAT_* enum value corresponding to `arg`.
  */
@@ -998,7 +1087,8 @@ enum resume_mode {
 	RESUME_FALSE = 0,
 	RESUME_APPLY,
 	RESUME_RESOLVED,
-	RESUME_SKIP
+	RESUME_SKIP,
+	RESUME_ABORT
 };
 
 int cmd_am(int argc, const char **argv, const char *prefix)
@@ -1009,7 +1099,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
 	const char * const usage[] = {
 		N_("git am [options] [(<mbox>|<Maildir>)...]"),
-		N_("git am [options] (--continue | --skip)"),
+		N_("git am [options] (--continue | --skip | --abort)"),
 		NULL
 	};
 
@@ -1026,6 +1116,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		OPT_CMDMODE(0, "skip", &resume,
 			N_("skip the current patch"),
 			RESUME_SKIP),
+		OPT_CMDMODE(0, "abort", &resume,
+			N_("restore the original branch and abort the patching operation."),
+			RESUME_ABORT),
 		OPT_END()
 	};
 
@@ -1090,6 +1183,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 	case RESUME_SKIP:
 		am_skip(&state);
 		break;
+	case RESUME_ABORT:
+		am_abort(&state);
+		break;
 	default:
 		die("BUG: invalid resume value");
 	}
-- 
2.5.0.rc2.110.gb39b692

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

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-19 16:09 [PATCH v6 00/45] Make git-am a builtin Paul Tan
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 ` Paul Tan [this message]
2015-07-31 23:42   ` [PATCH v6 14/45] builtin-am: implement --abort 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-15-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.