git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [RFC PATCH 0/8] sequencer: dont't fork git commit
@ 2017-09-25 10:10 Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 1/8] commit: move empty message checks to libgit Phillip Wood
                   ` (12 more replies)
  0 siblings, 13 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.

Phillip Wood (8):
  commit: move empty message checks to libgit
  commit: move code to update HEAD to libgit
  sequencer: refactor update_head()
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'

 builtin/commit.c         | 269 ++--------------------------
 builtin/rebase--helper.c |  13 +-
 builtin/revert.c         |  15 +-
 sequencer.c              | 447 ++++++++++++++++++++++++++++++++++++++++++++++-
 sequencer.h              |  20 +++
 5 files changed, 503 insertions(+), 261 deletions(-)

-- 
2.14.1


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [RFC PATCH 1/8] commit: move empty message checks to libgit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 2/8] commit: move code to update HEAD " Phillip Wood
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 70 +++-----------------------------------------------------
 sequencer.c      | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      | 10 ++++++++
 3 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index b3b04f5dd3a94d1661e877c5019cc56ac46854ef..0b8c1ef6f57cfed328d12255e6834adb4bda4137 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -978,65 +973,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1744,12 +1680,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (cleanup_mode != CLEANUP_NONE)
 		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index fcceabb80f4261006cdd65bc0ec95ac54ea42e7c..319208afb3de36c97b6c62d4ecf6e641245e7a54 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -690,6 +690,66 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index f885b68395f4bff1ded96c0ab84ed87d164f0c7d..dd071cfcd82d165bd23726814b74cbf3384e1a17 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -50,4 +50,14 @@ extern const char sign_off_header[];
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
 
+enum cleanup_mode {
+	CLEANUP_SPACE,
+	CLEANUP_NONE,
+	CLEANUP_SCISSORS,
+	CLEANUP_ALL
+};
+
+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum cleanup_mode cleanup_mode);
 #endif
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 2/8] commit: move code to update HEAD to libgit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 1/8] commit: move empty message checks to libgit Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-10-07  9:54   ` Junio C Hamano
  2017-09-25 10:10 ` [RFC PATCH 3/8] sequencer: refactor update_head() Phillip Wood
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 31 +++++++------------------------
 sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  2 ++
 3 files changed, 47 insertions(+), 25 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	reflog_msg = getenv("GIT_REFLOG_ACTION");
 	if (!current_head) {
 		if (!reflog_msg)
-			reflog_msg = "commit (initial)";
+			setenv ("GIT_REFLOG_ACTION", "commit (initial)", 1);
 	} else if (amend) {
 		if (!reflog_msg)
-			reflog_msg = "commit (amend)";
+			setenv("GIT_REFLOG_ACTION", "commit (amend)", 1);
 		parents = copy_commit_list(current_head->parents);
 	} else if (whence == FROM_MERGE) {
 		struct strbuf m = STRBUF_INIT;
@@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		struct commit_list **pptr = &parents;
 
 		if (!reflog_msg)
-			reflog_msg = "commit (merge)";
+			setenv("GIT_REFLOG_ACTION", "commit (merge)", 1);
 		pptr = commit_list_append(current_head, pptr);
 		fp = xfopen(git_path_merge_head(), "r");
 		while (strbuf_getline_lf(&m, fp) != EOF) {
@@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			parents = reduce_heads(parents);
 	} else {
 		if (!reflog_msg)
-			reflog_msg = (whence == FROM_CHERRY_PICK)
-					? "commit (cherry-pick)"
-					: "commit";
+			setenv("GIT_REFLOG_ACTION", (whence == FROM_CHERRY_PICK)
+						? "commit (cherry-pick)"
+						: "commit", 1);
 		commit_list_insert(current_head, &parents);
 	}
 
@@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", oid.hash,
-				   current_head
-				   ? current_head->object.oid.hash : null_sha1,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head (current_head, &oid, &sb, &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head(const struct commit *old_head, const struct object_id *new_head,
+		const struct strbuf *msg, struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl, *reflog_msg;
+	int ret = 0;
+
+	reflog_msg = getenv("GIT_REFLOG_ACTION");
+	if (!reflog_msg)
+		reflog_msg="";
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
+	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head->hash,
+				   old_head
+				   ? old_head->object.oid.hash : null_sha1,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -60,4 +60,6 @@ enum cleanup_mode {
 int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum cleanup_mode cleanup_mode);
+int update_head(const struct commit *old_head, const struct object_id *new_head,
+		const struct strbuf *msg, struct strbuf *err);
 #endif
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 3/8] sequencer: refactor update_head()
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 1/8] commit: move empty message checks to libgit Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 2/8] commit: move code to update HEAD " Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 4/8] commit: move post-rewrite code to libgit Phillip Wood
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

The previous commit was a mechanical translation of the code from
builtin/commit.c. Now that it uses its own strbuf for the reflog
message it can be simplified slightly.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 917ad4a16216b30adb2c2c9650217926d8db8ba7..1795a4df2a0021b2419d941c6083e49cd6647314 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -759,8 +759,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 	int ret = 0;
 
 	reflog_msg = getenv("GIT_REFLOG_ACTION");
-	if (!reflog_msg)
-		reflog_msg="";
+	if (reflog_msg)
+		strbuf_addstr(&sb, reflog_msg);
+	strbuf_addstr(&sb, ": ");
 
 	nl = strchr(msg->buf, '\n');
 	if (nl) {
@@ -769,8 +770,6 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 		strbuf_addbuf(&sb, msg);
 		strbuf_addch(&sb, '\n');
 	}
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
 
 	transaction = ref_transaction_begin(err);
 	if (!transaction ||
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 4/8] commit: move post-rewrite code to libgit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (2 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 3/8] sequencer: refactor update_head() Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 5/8] commit: move print_commit_summary() " Phillip Wood
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 49 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 497778ba2c02afdd4a337969a27ca781e8389040..9d621098823d196643180226491e43c806154c13 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1465,37 +1463,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1725,14 +1692,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index 1795a4df2a0021b2419d941c6083e49cd6647314..81bd0810df6bcf2e078abc220bb8984345bf467f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -20,6 +20,8 @@
 #include "trailer.h"
 #include "log-tree.h"
 #include "wt-status.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -786,6 +788,50 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so current_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 87edf40e5274d59f48d5af57678100ea220d2c8a..45def684ad751d0b8dc62b6cfdfb819ddf183c89 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -62,4 +62,6 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum cleanup_mode cleanup_mode);
 int update_head(const struct commit *old_head, const struct object_id *new_head,
 		const struct strbuf *msg, struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 5/8] commit: move print_commit_summary() to libgit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (3 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 4/8] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 126 ++++---------------------------------------------------
 sequencer.c      | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 129 insertions(+), 118 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 9d621098823d196643180226491e43c806154c13..fac392216c4dc4d4a2b9fbee1d9ee14da0a14209 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1343,97 +1318,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	struct object_id junk_oid;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1694,9 +1578,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		int flags = 0;
 
+		if (!current_head)
+			flags|= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 	strbuf_release(&err);
 	return 0;
 }
diff --git a/sequencer.c b/sequencer.c
index 81bd0810df6bcf2e078abc220bb8984345bf467f..65031353f01d75624951222c2de280c427e26ac5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -832,6 +832,122 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	struct object_id junk_oid;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 45def684ad751d0b8dc62b6cfdfb819ddf183c89..d491f5cae2e6152859d114a08bbd7c935d1e80a6 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -64,4 +64,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 		const struct strbuf *msg, struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  int flags);
 #endif
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (4 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 5/8] commit: move print_commit_summary() " Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 7/8] sequencer: load commit related config Phillip Wood
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and then again when
committing. This means that if there are conflicts when merging with a
strategy other than 'recursive' the Signed-off-by: trailer will be
added if the user commits the resolution themselves without passing
'--signoff' to 'git commit'. It also simplifies the in-process commit
that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 65031353f01d75624951222c2de280c427e26ac5..e9060e3ca50777687c578ff09c62cd901efcfb0e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -476,9 +476,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -656,8 +653,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1339,6 +1334,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 7/8] sequencer: load commit related config
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (5 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-09-25 10:10 ` [RFC PATCH 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup and gpg signing of commits in
preparation for committing without forking 'git commit'.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/rebase--helper.c | 13 ++++++++++++-
 builtin/revert.c         | 15 +++++++++++++--
 sequencer.c              | 30 ++++++++++++++++++++++++++++++
 sequencer.h              |  1 +
 4 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index c82b4dce6838fa039e9c2cfc769c7cd17a8ef562..3a99f9f8f1396ee5a040c7c4e9367688fe04c9a4 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
+static int git_rebase_helper_config (const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config (k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -24,7 +35,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_rebase_helper_config, NULL);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..2883f9eb71be1c2b205fd0f1d962748438f4c4b8 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
+static int git_revert_config (const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config (k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	git_config(git_revert_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	git_config(git_revert_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index e9060e3ca50777687c578ff09c62cd901efcfb0e..230bdb8535a422b1263429e5894e3f8b1733e844 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -687,6 +687,36 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;
+static char *default_gpg_sign;
+
+int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "commit.cleanup")) {
+		int status;
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (!strcmp(s, "verbatim"))
+			default_msg_cleanup = CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			default_msg_cleanup = CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			default_msg_cleanup = CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			default_msg_cleanup = CLEANUP_NONE;
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	return git_gpg_config(k, v, NULL);
+}
+
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
diff --git a/sequencer.h b/sequencer.h
index d491f5cae2e6152859d114a08bbd7c935d1e80a6..2759ca2f7d1aba1268c88dc8bd94eb3e593897fe 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -49,6 +49,7 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
+int git_sequencer_config(const char *k, const char *v, void *cb);
 
 enum cleanup_mode {
 	CLEANUP_SPACE,
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [RFC PATCH 8/8] sequencer: try to commit without forking 'git commit'
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (6 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 7/8] sequencer: load commit related config Phillip Wood
@ 2017-09-25 10:10 ` Phillip Wood
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-09-25 10:10 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 26% (from 319ms to 235ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 44% (from 376ms to 211ms) on
my computer running linux. The greater saving for rebase is because it
longer wastes time creating the commit summary just to throw it away.

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD/REVERT_HEAD are created unnecessarily. This
could be eliminated in future. I hacked up a version that does not
write these files and just passed an strbuf (with the wrong message
for fixup and squash commands) to do_commit() but I couldn't measure
any significant time difference when running cherry-pick or rebase. I
think eliminating the writes properly for rebase would require a bit
of effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

I wonder if this should reparse the author it gets from the existing
commit rather than just reusing it.

 sequencer.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 147 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 230bdb8535a422b1263429e5894e3f8b1733e844..ebba53583727e947ed767d6181b14254e23fc146 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -591,6 +591,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char* message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -973,6 +985,130 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	strbuf_release(&format);
 }
 
+static int try_to_commit(struct strbuf *msgbuf, const char *author,
+			 struct replay_opts *opts, unsigned int flags)
+{
+	struct object_id tree;
+	struct object_id oid;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	struct strbuf *msg;
+	char *amend_author = NULL;
+	const char *gpg_sign;
+	enum cleanup_mode cleanup;
+	int res = 0;
+
+	msg = msgbuf;
+	if (flags & EDIT_MSG || flags & VERIFY_MSG)
+		return 1;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_or_die(&oid, "HEAD");
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+
+	if (flags & AMEND_MSG) {
+		const char *body;
+		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		msg = &amend_msg;
+		if (find_commit_subject(message, &body))
+			strbuf_addstr(&amend_msg, body);
+		author = amend_author = get_author (message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;
+	if (cleanup != CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1;
+		goto out;
+	}
+
+	gpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1;
+		goto out;
+	}
+
+	res = commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				   oid.hash, author, gpg_sign, extra);
+	if (res) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	res = update_head(current_head, &oid, msg, &err);
+	if (res) {
+		error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, &oid);
+
+	if (!is_rebase_i(opts))
+		print_commit_summary(NULL, &oid, SUMMARY_SHOW_AUTHOR_DATE);
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	if (amend_author)
+		free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *defmsg, const char* author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res;
+	struct strbuf sb = STRBUF_INIT;
+
+	if (defmsg && strbuf_read_file(&sb, defmsg, 2048) < 0)
+		return error_errno(_("unable to read commit message from '%s'"),
+				   defmsg);
+	res = try_to_commit(&sb, author, opts, flags);
+	strbuf_release(&sb);
+
+	if (res == 0) {
+		unlink(git_path_cherry_pick_head());
+		unlink(git_path_revert_head());
+		unlink(git_path_merge_msg());
+		return res;
+	} else if (res == 1) {
+		return run_git_commit(defmsg, opts, flags);
+	}
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1224,6 +1360,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1339,6 +1476,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup (command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1424,9 +1563,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1435,6 +1578,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	if (author)
+		free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit
  2017-09-25 10:10 ` [RFC PATCH 2/8] commit: move code to update HEAD " Phillip Wood
@ 2017-10-07  9:54   ` Junio C Hamano
  2017-10-24 10:01     ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-10-07  9:54 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---

This seems to do a lot more than just moving code, most notably, it
uses setenv() to affect what happens in any subprocesses we may
spawn, and it is unclear if it was verified that this patch is free
of unwanted consequences due to that change (and any others I may
have missed while reading this patch, if any).

I suspect that it would be sufficient to make update_head() helper
function take the reflog action message as another parameter
instead to fix the above, but there may be other reasons why you
chose to do it this way---I cannot read it in your empty log
message, though.

I will not give line-by-line style nitpick but in general we do not
leave a SP between function name and the open parenthesis that
starts its argument list.  New code in this patch seems to use
mixture of styles.

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  	struct strbuf sb = STRBUF_INIT;
>  	struct strbuf author_ident = STRBUF_INIT;
>  	const char *index_file, *reflog_msg;
> -	char *nl;
>  	struct object_id oid;
>  	struct commit_list *parents = NULL;
>  	struct stat statbuf;
>  	struct commit *current_head = NULL;
>  	struct commit_extra_header *extra = NULL;
> -	struct ref_transaction *transaction;
>  	struct strbuf err = STRBUF_INIT;
>  
>  	if (argc == 2 && !strcmp(argv[1], "-h"))
> @@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  	reflog_msg = getenv("GIT_REFLOG_ACTION");
>  	if (!current_head) {
>  		if (!reflog_msg)
> -			reflog_msg = "commit (initial)";
> +			setenv ("GIT_REFLOG_ACTION", "commit (initial)", 1);
>  	} else if (amend) {
>  		if (!reflog_msg)
> -			reflog_msg = "commit (amend)";
> +			setenv("GIT_REFLOG_ACTION", "commit (amend)", 1);
>  		parents = copy_commit_list(current_head->parents);
>  	} else if (whence == FROM_MERGE) {
>  		struct strbuf m = STRBUF_INIT;
> @@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  		struct commit_list **pptr = &parents;
>  
>  		if (!reflog_msg)
> -			reflog_msg = "commit (merge)";
> +			setenv("GIT_REFLOG_ACTION", "commit (merge)", 1);
>  		pptr = commit_list_append(current_head, pptr);
>  		fp = xfopen(git_path_merge_head(), "r");
>  		while (strbuf_getline_lf(&m, fp) != EOF) {
> @@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  			parents = reduce_heads(parents);
>  	} else {
>  		if (!reflog_msg)
> -			reflog_msg = (whence == FROM_CHERRY_PICK)
> -					? "commit (cherry-pick)"
> -					: "commit";
> +			setenv("GIT_REFLOG_ACTION", (whence == FROM_CHERRY_PICK)
> +						? "commit (cherry-pick)"
> +						: "commit", 1);
>  		commit_list_insert(current_head, &parents);
>  	}
>  
> @@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  	strbuf_release(&author_ident);
>  	free_commit_extra_headers(extra);
>  
> -	nl = strchr(sb.buf, '\n');
> -	if (nl)
> -		strbuf_setlen(&sb, nl + 1 - sb.buf);
> -	else
> -		strbuf_addch(&sb, '\n');
> -	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
> -	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
> -
> -	transaction = ref_transaction_begin(&err);
> -	if (!transaction ||
> -	    ref_transaction_update(transaction, "HEAD", oid.hash,
> -				   current_head
> -				   ? current_head->object.oid.hash : null_sha1,
> -				   0, sb.buf, &err) ||
> -	    ref_transaction_commit(transaction, &err)) {
> +	if (update_head (current_head, &oid, &sb, &err)) {
>  		rollback_index_files();
>  		die("%s", err.buf);
>  	}
> -	ref_transaction_free(transaction);
>  
>  	unlink(git_path_cherry_pick_head());
>  	unlink(git_path_revert_head());
> diff --git a/sequencer.c b/sequencer.c
> index 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -1,10 +1,10 @@
>  #include "cache.h"
>  #include "config.h"
>  #include "lockfile.h"
> -#include "sequencer.h"
>  #include "dir.h"
>  #include "object.h"
>  #include "commit.h"
> +#include "sequencer.h"
>  #include "tag.h"
>  #include "run-command.h"
>  #include "exec_cmd.h"
> @@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
>  	return rest_is_empty(sb, start - sb->buf);
>  }
>  
> +int update_head(const struct commit *old_head, const struct object_id *new_head,
> +		const struct strbuf *msg, struct strbuf *err)
> +{
> +	struct ref_transaction *transaction;
> +	struct strbuf sb = STRBUF_INIT;
> +	const char *nl, *reflog_msg;
> +	int ret = 0;
> +
> +	reflog_msg = getenv("GIT_REFLOG_ACTION");
> +	if (!reflog_msg)
> +		reflog_msg="";
> +
> +	nl = strchr(msg->buf, '\n');
> +	if (nl) {
> +		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
> +	} else {
> +		strbuf_addbuf(&sb, msg);
> +		strbuf_addch(&sb, '\n');
> +	}
> +	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
> +	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
> +
> +	transaction = ref_transaction_begin(err);
> +	if (!transaction ||
> +	    ref_transaction_update(transaction, "HEAD", new_head->hash,
> +				   old_head
> +				   ? old_head->object.oid.hash : null_sha1,
> +				   0, sb.buf, err) ||
> +	    ref_transaction_commit(transaction, err)) {
> +		ret = -1;
> +	}
> +	ref_transaction_free(transaction);
> +	strbuf_release(&sb);
> +
> +	return ret;
> +}
> +
>  static int is_original_commit_empty(struct commit *commit)
>  {
>  	const struct object_id *ptree_oid;
> diff --git a/sequencer.h b/sequencer.h
> index dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -60,4 +60,6 @@ enum cleanup_mode {
>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
>  int template_untouched(const struct strbuf *sb, const char *template_file,
>  		       enum cleanup_mode cleanup_mode);
> +int update_head(const struct commit *old_head, const struct object_id *new_head,
> +		const struct strbuf *msg, struct strbuf *err);
>  #endif

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit
  2017-10-07  9:54   ` Junio C Hamano
@ 2017-10-24 10:01     ` Phillip Wood
  2017-10-24 12:41       ` Junio C Hamano
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-10-24 10:01 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 07/10/17 10:54, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>> ---
> 
> This seems to do a lot more than just moving code, most notably, it
> uses setenv() to affect what happens in any subprocesses we may
> spawn, and it is unclear if it was verified that this patch is free
> of unwanted consequences due to that change (and any others I may
> have missed while reading this patch, if any).
>
> I suspect that it would be sufficient to make update_head() helper
> function take the reflog action message as another parameter
> instead to fix the above, but there may be other reasons why you
> chose to do it this way---I cannot read it in your empty log
> message, though.

Good point, sorry I should have added some explanation about that. I
went with using setenv() rather than passing a reflog message to
update_head() as it meant there were no changes needed on the sequencer
side as it already sets GIT_REFLOG_ACTION. As the sequencer already sets
GIT_REFLOG_ACTION, and git-commit does not fork any subprocesses I don't
think this change has any unwanted consequences (I pushed a branch to
github before submitting the patches and the test suite passes on
travis). It would however be clearer to add a parameter to update_head()
for the reflog message as you suggested.

> 
> I will not give line-by-line style nitpick but in general we do not
> leave a SP between function name and the open parenthesis that
> starts its argument list.  New code in this patch seems to use
> mixture of styles.

Sorry I should have spotted those before I posted this series, I go
though all the patches and fix them (this would be a good opportunity
for me to try using git-clang-format from next)

Thanks for looking at this, did you have time to look at the other
changes in this series or did this patch put you off looking further?
I'll update and repost probably towards the end of next week. If I
continue to base these patches on master then I think the patch that
moves the code to print the commit summary will have (trivial) conflicts
with the changes in ao/check-resolve-ref-unsafe-result in pu do you want
the new patches based pu or are you happy with them based on master?

Best Wishes

Phillip

>> diff --git a/builtin/commit.c b/builtin/commit.c
>> index 0b8c1ef6f57cfed328d12255e6834adb4bda4137..497778ba2c02afdd4a337969a27ca781e8389040 100644
>> --- a/builtin/commit.c
>> +++ b/builtin/commit.c
>> @@ -1578,13 +1578,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>  	struct strbuf sb = STRBUF_INIT;
>>  	struct strbuf author_ident = STRBUF_INIT;
>>  	const char *index_file, *reflog_msg;
>> -	char *nl;
>>  	struct object_id oid;
>>  	struct commit_list *parents = NULL;
>>  	struct stat statbuf;
>>  	struct commit *current_head = NULL;
>>  	struct commit_extra_header *extra = NULL;
>> -	struct ref_transaction *transaction;
>>  	struct strbuf err = STRBUF_INIT;
>>  
>>  	if (argc == 2 && !strcmp(argv[1], "-h"))
>> @@ -1625,10 +1623,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>  	reflog_msg = getenv("GIT_REFLOG_ACTION");
>>  	if (!current_head) {
>>  		if (!reflog_msg)
>> -			reflog_msg = "commit (initial)";
>> +			setenv ("GIT_REFLOG_ACTION", "commit (initial)", 1);
>>  	} else if (amend) {
>>  		if (!reflog_msg)
>> -			reflog_msg = "commit (amend)";
>> +			setenv("GIT_REFLOG_ACTION", "commit (amend)", 1);
>>  		parents = copy_commit_list(current_head->parents);
>>  	} else if (whence == FROM_MERGE) {
>>  		struct strbuf m = STRBUF_INIT;
>> @@ -1637,7 +1635,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>  		struct commit_list **pptr = &parents;
>>  
>>  		if (!reflog_msg)
>> -			reflog_msg = "commit (merge)";
>> +			setenv("GIT_REFLOG_ACTION", "commit (merge)", 1);
>>  		pptr = commit_list_append(current_head, pptr);
>>  		fp = xfopen(git_path_merge_head(), "r");
>>  		while (strbuf_getline_lf(&m, fp) != EOF) {
>> @@ -1660,9 +1658,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>  			parents = reduce_heads(parents);
>>  	} else {
>>  		if (!reflog_msg)
>> -			reflog_msg = (whence == FROM_CHERRY_PICK)
>> -					? "commit (cherry-pick)"
>> -					: "commit";
>> +			setenv("GIT_REFLOG_ACTION", (whence == FROM_CHERRY_PICK)
>> +						? "commit (cherry-pick)"
>> +						: "commit", 1);
>>  		commit_list_insert(current_head, &parents);
>>  	}
>>  
>> @@ -1707,25 +1705,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>  	strbuf_release(&author_ident);
>>  	free_commit_extra_headers(extra);
>>  
>> -	nl = strchr(sb.buf, '\n');
>> -	if (nl)
>> -		strbuf_setlen(&sb, nl + 1 - sb.buf);
>> -	else
>> -		strbuf_addch(&sb, '\n');
>> -	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
>> -	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
>> -
>> -	transaction = ref_transaction_begin(&err);
>> -	if (!transaction ||
>> -	    ref_transaction_update(transaction, "HEAD", oid.hash,
>> -				   current_head
>> -				   ? current_head->object.oid.hash : null_sha1,
>> -				   0, sb.buf, &err) ||
>> -	    ref_transaction_commit(transaction, &err)) {
>> +	if (update_head (current_head, &oid, &sb, &err)) {
>>  		rollback_index_files();
>>  		die("%s", err.buf);
>>  	}
>> -	ref_transaction_free(transaction);
>>  
>>  	unlink(git_path_cherry_pick_head());
>>  	unlink(git_path_revert_head());
>> diff --git a/sequencer.c b/sequencer.c
>> index 319208afb3de36c97b6c62d4ecf6e641245e7a54..917ad4a16216b30adb2c2c9650217926d8db8ba7 100644
>> --- a/sequencer.c
>> +++ b/sequencer.c
>> @@ -1,10 +1,10 @@
>>  #include "cache.h"
>>  #include "config.h"
>>  #include "lockfile.h"
>> -#include "sequencer.h"
>>  #include "dir.h"
>>  #include "object.h"
>>  #include "commit.h"
>> +#include "sequencer.h"
>>  #include "tag.h"
>>  #include "run-command.h"
>>  #include "exec_cmd.h"
>> @@ -750,6 +750,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
>>  	return rest_is_empty(sb, start - sb->buf);
>>  }
>>  
>> +int update_head(const struct commit *old_head, const struct object_id *new_head,
>> +		const struct strbuf *msg, struct strbuf *err)
>> +{
>> +	struct ref_transaction *transaction;
>> +	struct strbuf sb = STRBUF_INIT;
>> +	const char *nl, *reflog_msg;
>> +	int ret = 0;
>> +
>> +	reflog_msg = getenv("GIT_REFLOG_ACTION");
>> +	if (!reflog_msg)
>> +		reflog_msg="";
>> +
>> +	nl = strchr(msg->buf, '\n');
>> +	if (nl) {
>> +		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
>> +	} else {
>> +		strbuf_addbuf(&sb, msg);
>> +		strbuf_addch(&sb, '\n');
>> +	}
>> +	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
>> +	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
>> +
>> +	transaction = ref_transaction_begin(err);
>> +	if (!transaction ||
>> +	    ref_transaction_update(transaction, "HEAD", new_head->hash,
>> +				   old_head
>> +				   ? old_head->object.oid.hash : null_sha1,
>> +				   0, sb.buf, err) ||
>> +	    ref_transaction_commit(transaction, err)) {
>> +		ret = -1;
>> +	}
>> +	ref_transaction_free(transaction);
>> +	strbuf_release(&sb);
>> +
>> +	return ret;
>> +}
>> +
>>  static int is_original_commit_empty(struct commit *commit)
>>  {
>>  	const struct object_id *ptree_oid;
>> diff --git a/sequencer.h b/sequencer.h
>> index dd071cfcd82d165bd23726814b74cbf3384e1a17..87edf40e5274d59f48d5af57678100ea220d2c8a 100644
>> --- a/sequencer.h
>> +++ b/sequencer.h
>> @@ -60,4 +60,6 @@ enum cleanup_mode {
>>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
>>  int template_untouched(const struct strbuf *sb, const char *template_file,
>>  		       enum cleanup_mode cleanup_mode);
>> +int update_head(const struct commit *old_head, const struct object_id *new_head,
>> +		const struct strbuf *msg, struct strbuf *err);
>>  #endif


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [RFC PATCH 2/8] commit: move code to update HEAD to libgit
  2017-10-24 10:01     ` Phillip Wood
@ 2017-10-24 12:41       ` Junio C Hamano
  0 siblings, 0 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-10-24 12:41 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

>> I suspect that it would be sufficient to make update_head() helper
>> function take the reflog action message as another parameter
>> instead to fix the above, but there may be other reasons why you
>> chose to do it this way---I cannot read it in your empty log
>> message, though.
>
> Good point, sorry I should have added some explanation about that. I
> went with using setenv() rather than passing a reflog message to
> update_head() as it meant there were no changes needed on the sequencer
> side as it already sets GIT_REFLOG_ACTION. As the sequencer already sets
> GIT_REFLOG_ACTION, and git-commit does not fork any subprocesses I don't

Doesn't "git commit" run number of hooks?  Is it just the current
code does not run any hooks (by chance) after these new setenv()
calls are made and we happen to be safe?

^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v1 0/8] sequencer: dont't fork git commit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (7 preceding siblings ...)
  2017-09-25 10:10 ` [RFC PATCH 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-11-06 11:27 ` Phillip Wood
  2017-11-06 11:27   ` [PATCH v1 1/8] commit: move empty message checks to libgit Phillip Wood
                     ` (7 more replies)
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                   ` (3 subsequent siblings)
  12 siblings, 8 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Changes since the last version
 - reworked the second patch based on Junio's feedback so it no longer sets
   GIT_REFLOG_ACTION
 - reworded commit messages
 - print_commit_summary() no longer dies but returns an error so the sequencer
   can exit cleanly
 - reworked the last patch to return errors rather than dying when creating a
   commit and to use the correct commit message with intermediate squashes.
 - rebased onto next as there were some conflicting changes in builtin/commit.c
 - updated benchmarks compared to v2.15.0 (the percentage difference is
   essentially the same)
 - fixed some style issues

Here's the summary from the previous version
These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.

Phillip Wood (8):
  commit: move empty message checks to libgit
  Add a function to update HEAD after creating a commit
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: don't die in print_commit_summary()
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'

 builtin/commit.c         | 260 ++------------------------
 builtin/rebase--helper.c |  13 +-
 builtin/revert.c         |  15 +-
 sequencer.c              | 478 ++++++++++++++++++++++++++++++++++++++++++++++-
 sequencer.h              |  21 +++
 5 files changed, 531 insertions(+), 256 deletions(-)

-- 
2.14.3


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v1 1/8] commit: move empty message checks to libgit
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  0:43     ` Johannes Schindelin
  2017-11-06 11:27   ` [PATCH v1 2/8] Add a function to update HEAD after creating a commit Phillip Wood
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move the functions that check for empty messages from bulitin/commit.c
to sequencer.c so they can be shared with other commands. The
functions are refactored to take an explicit cleanup mode and template
filename passed by the caller.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 70 +++-----------------------------------------------------
 sequencer.c      | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      | 10 ++++++++
 3 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 06ab495ae1d2baa5eef6b42eb61909883c681f4c..fab512b668af07a1fa927f713eca71c9f783b422 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -983,65 +978,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1772,12 +1708,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (cleanup_mode != CLEANUP_NONE)
 		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index 1eb2c4669d529485a045de66d6711039d19a2442..f4a04c913c0d60adbf78d68ca87db739c8e3a280 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -691,6 +691,66 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -58,4 +58,14 @@ extern const char sign_off_header[];
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
 
+enum cleanup_mode {
+	CLEANUP_SPACE,
+	CLEANUP_NONE,
+	CLEANUP_SCISSORS,
+	CLEANUP_ALL
+};
+
+int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum cleanup_mode cleanup_mode);
 #endif
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 2/8] Add a function to update HEAD after creating a commit
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
  2017-11-06 11:27   ` [PATCH v1 1/8] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  2:56     ` Junio C Hamano
  2017-11-06 11:27   ` [PATCH v1 3/8] commit: move post-rewrite code to libgit Phillip Wood
                     ` (5 subsequent siblings)
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add update_head() based on the code that updates HEAD after committing
in builtin/commit.c that can be called by 'git commit' and other
commands.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 19 +------------------
 sequencer.c      | 38 +++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  3 +++
 3 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index fab512b668af07a1fa927f713eca71c9f783b422..e82754f1fb9f80b8e5b83546d2b2b010daaba522 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1606,13 +1606,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1735,25 +1733,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", &oid,
-				   current_head
-				   ? &current_head->object.oid : &null_oid,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head(current_head, &oid, reflog_msg, &sb, &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index f4a04c913c0d60adbf78d68ca87db739c8e3a280..a85ad659114248b9b5215641cd2911bc4d02e4df 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head(const struct commit *old_head, const struct object_id *new_head,
+		const char *action, const struct strbuf *msg,
+		struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl;
+	int ret = 0;
+
+	if (action) {
+		strbuf_addstr(&sb, action);
+		strbuf_addstr(&sb, ": ");
+	}
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head,
+				   old_head ? &old_head->object.oid : &null_oid,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 65a4b0c25185d7ad5115035abb766d1b95df9a62..1db06caea35bed556dfaabca1c6be8a80857ed5e 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -68,4 +68,7 @@ enum cleanup_mode {
 int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum cleanup_mode cleanup_mode);
+int update_head(const struct commit *old_head, const struct object_id *new_head,
+		const char* action, const struct strbuf *msg,
+		struct strbuf *err);
 #endif
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 3/8] commit: move post-rewrite code to libgit
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
  2017-11-06 11:27   ` [PATCH v1 1/8] commit: move empty message checks to libgit Phillip Wood
  2017-11-06 11:27   ` [PATCH v1 2/8] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  3:03     ` Junio C Hamano
  2017-11-06 11:27   ` [PATCH v1 4/8] commit: move print_commit_summary() " Phillip Wood
                     ` (4 subsequent siblings)
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
be shared with other commands and add a new function
commit_post_rewrite() based on the code in builtin/commit.c that
encapsulates rewriting notes and running the post-rewrite hook.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index e82754f1fb9f80b8e5b83546d2b2b010daaba522..4f0092b91b9553855fe16864b16b7779b7d1f330 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1493,37 +1491,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1753,14 +1720,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index a85ad659114248b9b5215641cd2911bc4d02e4df..6d9e393f8368ac03d92019189f9fccd6a3ba1d77 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -21,6 +21,8 @@
 #include "log-tree.h"
 #include "wt-status.h"
 #include "hashmap.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -787,6 +789,51 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so old_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 1db06caea35bed556dfaabca1c6be8a80857ed5e..5734c8a3d869564354e5193c52bf45c119a8b840 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -71,4 +71,6 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 int update_head(const struct commit *old_head, const struct object_id *new_head,
 		const char* action, const struct strbuf *msg,
 		struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 4/8] commit: move print_commit_summary() to libgit
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                     ` (2 preceding siblings ...)
  2017-11-06 11:27   ` [PATCH v1 3/8] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  3:38     ` Junio C Hamano
  2017-11-06 11:27   ` [PATCH v1 5/8] sequencer: don't die in print_commit_summary() Phillip Wood
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move print_commit_summary() from builtin/commit.c to sequencer.c so it
can be shared with other commands. The function is modified by
changing the last argument to a flag so callers can specify whether
they want to show the author date in addition to specifying if this is
an initial commit.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c | 128 ++++---------------------------------------------------
 sequencer.c      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 131 insertions(+), 119 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 4f0092b91b9553855fe16864b16b7779b7d1f330..b109feaca11e3e43b1a59dee1868244824eaf345 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1370,98 +1345,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1722,8 +1605,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 
 	UNLEAK(err);
 	UNLEAK(sb);
diff --git a/sequencer.c b/sequencer.c
index 6d9e393f8368ac03d92019189f9fccd6a3ba1d77..d4f77a5f24c89479a4a18c2b89a3cd4e7ba7ba6c 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -834,6 +834,123 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	if (!head)
+		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 5734c8a3d869564354e5193c52bf45c119a8b840..c7989f93fcf08f979f5869cd4ec27f0dd0b88c82 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -73,4 +73,9 @@ int update_head(const struct commit *old_head, const struct object_id *new_head,
 		struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  int flags);
 #endif
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                     ` (3 preceding siblings ...)
  2017-11-06 11:27   ` [PATCH v1 4/8] commit: move print_commit_summary() " Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  4:18     ` Junio C Hamano
  2017-11-06 11:27   ` [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                     ` (2 subsequent siblings)
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Return an error rather than dying so that the sequencer can exit
cleanly once it starts committing without forking 'git commit'

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c |  3 ++-
 sequencer.c      | 17 +++++++++++------
 sequencer.h      |  4 ++--
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index b109feaca11e3e43b1a59dee1868244824eaf345..c924cbac9e0d55941e984b3cce3c1e565cd8cf3c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1612,7 +1612,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			flags |= SUMMARY_INITIAL_COMMIT;
 		if (author_date_is_interesting())
 			flags |= SUMMARY_SHOW_AUTHOR_DATE;
-		print_commit_summary(prefix, &oid, flags);
+		if (print_commit_summary(prefix, &oid, flags))
+			exit(128);
 	}
 
 	UNLEAK(err);
diff --git a/sequencer.c b/sequencer.c
index d4f77a5f24c89479a4a18c2b89a3cd4e7ba7ba6c..ae24405c23d021ed7916e5e2d9df6de27f867a2e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -875,8 +875,8 @@ static const char *implicit_ident_advice(void)
 
 }
 
-void print_commit_summary(const char *prefix, const struct object_id *oid,
-			  int flags)
+int print_commit_summary(const char *prefix, const struct object_id *oid,
+			 int flags)
 {
 	struct rev_info rev;
 	struct commit *commit;
@@ -885,12 +885,13 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	struct pretty_print_context pctx = {0};
 	struct strbuf author_ident = STRBUF_INIT;
 	struct strbuf committer_ident = STRBUF_INIT;
+	int ret = 0;
 
 	commit = lookup_commit(oid);
 	if (!commit)
-		die(_("couldn't look up newly created commit"));
+		return error(_("couldn't look up newly created commit"));
 	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
+		return error(_("could not parse newly created commit"));
 
 	strbuf_addstr(&format, "format:%h] %s");
 
@@ -934,8 +935,10 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	diff_setup_done(&rev.diffopt);
 
 	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!head) {
+		ret = error_errno(_("unable to resolve HEAD after creating commit"));
+		goto out;
+	}
 	if (!strcmp(head, "HEAD"))
 		head = _("detached HEAD");
 	else
@@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 		log_tree_commit(&rev, commit);
 	}
 
+out:
 	strbuf_release(&format);
+	return ret;
 }
 
 static int is_original_commit_empty(struct commit *commit)
diff --git a/sequencer.h b/sequencer.h
index c7989f93fcf08f979f5869cd4ec27f0dd0b88c82..0e3c2c9fd416349fb704a7ebc72c93a9b9a67703 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -76,6 +76,6 @@ void commit_post_rewrite(const struct commit *current_head,
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
-void print_commit_summary(const char *prefix, const struct object_id *oid,
-			  int flags);
+int print_commit_summary(const char *prefix, const struct object_id *oid,
+			 int flags);
 #endif
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                     ` (4 preceding siblings ...)
  2017-11-06 11:27   ` [PATCH v1 5/8] sequencer: don't die in print_commit_summary() Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  0:52     ` Johannes Schindelin
  2017-11-07  4:52     ` Junio C Hamano
  2017-11-06 11:27   ` [PATCH v1 7/8] sequencer: load commit related config Phillip Wood
  2017-11-06 11:27   ` [PATCH v1 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
  7 siblings, 2 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and specifying '--signoff'
when running 'git commit'. This means that if there are conflicts when
merging with a strategy other than 'recursive' the Signed-off-by:
trailer will be added if the user commits the resolution themselves
without passing '--signoff' to 'git commit'. It also simplifies the
in-process commit that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 7/8] sequencer: load commit related config
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                     ` (5 preceding siblings ...)
  2017-11-06 11:27   ` [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  1:02     ` Johannes Schindelin
  2017-11-06 11:27   ` [PATCH v1 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup and gpg signing of commits in
preparation for committing without forking 'git commit'.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/rebase--helper.c | 13 ++++++++++++-
 builtin/revert.c         | 15 +++++++++++++--
 sequencer.c              | 33 +++++++++++++++++++++++++++++++++
 sequencer.h              |  1 +
 4 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
+static int git_rebase_helper_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_rebase_helper_config, NULL);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
+static int git_revert_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	git_config(git_revert_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	git_config(git_revert_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;
+static char *default_gpg_sign;
+
+int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "commit.cleanup")) {
+		int status;
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			default_msg_cleanup = CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			default_msg_cleanup = CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			default_msg_cleanup = CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			default_msg_cleanup = CLEANUP_NONE;
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	return git_gpg_config(k, v, NULL);
+}
+
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
diff --git a/sequencer.h b/sequencer.h
index 0e3c2c9fd416349fb704a7ebc72c93a9b9a67703..944ba50e96d160caac241291377aeece4af2558e 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -57,6 +57,7 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
+int git_sequencer_config(const char *k, const char *v, void *cb);
 
 enum cleanup_mode {
 	CLEANUP_SPACE,
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
                     ` (6 preceding siblings ...)
  2017-11-06 11:27   ` [PATCH v1 7/8] sequencer: load commit related config Phillip Wood
@ 2017-11-06 11:27   ` Phillip Wood
  2017-11-07  1:36     ` Johannes Schindelin
  7 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-06 11:27 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 27% (from 282ms to 204ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
my computer running linux. Some of greater saving for rebase is
because it longer wastes time creating the commit summary just to
throw it away.

The code to create the commit is based on builtin/commit.c. It is
slightly simplified as it doesn't have to deal with merges and
modified so try and return an error rather than dying so that the
sequencer exits cleanly, as it would when forking 'git commit'.

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD is created unnecessarily. This could be
eliminated in future. I hacked up a version that does not write these
files and just passed an strbuf (with the wrong message for fixup and
squash commands) to do_commit() but I couldn't measure any significant
time difference when running cherry-pick or rebase. I think
eliminating the writes properly for rebase would require a bit of
effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 168 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char* message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,
 	return ret;
 }
 
+static int parse_head(struct commit **head)
+{
+	struct commit *current_head;
+	struct object_id oid;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_reference(&oid);
+		if (!current_head)
+			return error(_("could not parse HEAD"));
+		if (oidcmp(&oid, &current_head->object.oid)) {
+			warning(_("HEAD %s is not a commit!"),
+				oid_to_hex(&oid));
+		}
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+	*head = current_head;
+
+	return 0;
+}
+
+static int try_to_commit(struct strbuf *msg, const char *author,
+			 struct replay_opts *opts, unsigned int flags,
+			 struct object_id *oid)
+{
+	struct object_id tree;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	char *amend_author = NULL;
+	const char *gpg_sign;
+	enum cleanup_mode cleanup;
+	int res = 0;
+
+	if (parse_head(&current_head))
+		return -1;
+
+	if (flags & AMEND_MSG) {
+		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		if (!msg) {
+			const char *body = NULL;
+
+			find_commit_subject(message, &body);
+			msg = &amend_msg;
+			strbuf_addstr(msg, body);
+		}
+		author = amend_author = get_author (message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;
+	if (cleanup != CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1;
+		goto out;
+	}
+
+	gpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1;
+		goto out;
+	}
+
+	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				 oid->hash, author, gpg_sign, extra)) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	if (update_head(current_head, oid, getenv("GIT_REFLOG_ACTION"), msg,
+			&err)){
+		res = error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, oid);
+
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	if (amend_author)
+		free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *msg_file, const char* author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res = 1;
+
+	if (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {
+		struct object_id oid;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
+			return error_errno(_("unable to read commit message "
+					     "from '%s'"),
+					   msg_file);
+
+		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
+				    &oid);
+		strbuf_release(&sb);
+		if (res == 0) {
+			unlink(git_path_cherry_pick_head());
+			unlink(git_path_merge_msg());
+			if (!is_rebase_i(opts))
+				res = print_commit_summary(NULL, &oid,
+						SUMMARY_SHOW_AUTHOR_DATE);
+			return res;
+		}
+	}
+	if (res == 1)
+		return run_git_commit(msg_file, opts, flags);
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup (command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	if (author)
+		free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 1/8] commit: move empty message checks to libgit
  2017-11-06 11:27   ` [PATCH v1 1/8] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-07  0:43     ` Johannes Schindelin
  2017-11-07 14:24       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07  0:43 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Junio C Hamano, Phillip Wood

Hi Phillip,

On Mon, 6 Nov 2017, Phillip Wood wrote:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Move the functions that check for empty messages from bulitin/commit.c
> to sequencer.c so they can be shared with other commands. The
> functions are refactored to take an explicit cleanup mode and template
> filename passed by the caller.
> 
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>

Good rationale. Just one thing:

> diff --git a/sequencer.h b/sequencer.h
> index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -58,4 +58,14 @@ extern const char sign_off_header[];
>  void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
>  void append_conflicts_hint(struct strbuf *msgbuf);
>  
> +enum cleanup_mode {
> +	CLEANUP_SPACE,
> +	CLEANUP_NONE,
> +	CLEANUP_SCISSORS,
> +	CLEANUP_ALL
> +};

When it was file-local, `cleanup_mode` was okay (although far from great).
Now that we want to make it more widely available, I fear we have to make
the name much longer, e.g. `commit_msg_cleanup_mode`.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-11-06 11:27   ` [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-11-07  0:52     ` Johannes Schindelin
  2017-11-07  4:52     ` Junio C Hamano
  1 sibling, 0 replies; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07  0:52 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

Hi Phillip,

On Mon, 6 Nov 2017, Phillip Wood wrote:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Add the Signed-off-by: trailer in one place rather than adding it to
> the message when doing a recursive merge and specifying '--signoff'
> when running 'git commit'.

I would find this slightly easier to understand if it was written like
this (and separated into its own paragraph):

	Add the Signed-off-by: trailer by passing the `--signoff` option
	to `git commit` instead of adding the trailer manually (and only
	when the `recursive` merge strategy is in effect).

> This means that if there are conflicts when merging with a strategy
> other than 'recursive' the Signed-off-by: trailer will be added if the
> user commits the resolution themselves without passing '--signoff' to
> 'git commit'.

Nice!

> It also simplifies the in-process commit that is about to be added to
> the sequencer.

Also nice!

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 7/8] sequencer: load commit related config
  2017-11-06 11:27   ` [PATCH v1 7/8] sequencer: load commit related config Phillip Wood
@ 2017-11-07  1:02     ` Johannes Schindelin
  2017-11-07 10:50       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07  1:02 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

Hi Phillip,

On Mon, 6 Nov 2017, Phillip Wood wrote:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Load default values for message cleanup and gpg signing of commits in
> preparation for committing without forking 'git commit'.

Nicely explained.

> diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
> index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
> --- a/builtin/rebase--helper.c
> +++ b/builtin/rebase--helper.c
> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>  	NULL
>  };
>  
> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
> +{
> +	int status;
> +
> +	status = git_sequencer_config(k, v, NULL);
> +	if (status)
> +		return status;
> +
> +	return git_default_config(k, v, NULL);

It's more a matter of taste than anything else, but this one would be a
little bit shorter:

	return git_sequencer_config(k, v, NULL) ||
		git_default_config(k, v, NULL);

A more important question would be whether this `git_default_config()`
call could be folded into `git_sequencer_config()` right away, so that the
same pattern does not have to be repeated in rebase--helper as well as in
revert/cherry-pick.

> diff --git a/builtin/revert.c b/builtin/revert.c
> index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
>  	NULL
>  };
>  
> +static int git_revert_config(const char *k, const char *v, void *cb)

Seeing as it is used also by `cmd_cherry_pick()`, and that it is
file-local anyway, maybe `common_config()` is a better name?

This point is moot if we can call `git_default_config()` in
`git_sequencer_config()` directly, though.

> diff --git a/sequencer.c b/sequencer.c
> index 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
>  	return run_command(&cmd);
>  }
>  
> +static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;
> +static char *default_gpg_sign;

I was ready to shout about global state not meshing well with libified
code, but as long as we're sure that these values are set only while Git
executes single-threaded, still, it is the correct way to do it: these
settings reflect the config, and therefore *are* kinda global (at least
until the day when the submodule fans try to call `git commit` in a
submodule using the `struct repository` data structure).

In short: this code is good (and I was just describing a little bit of my
thinking, to demonstrate that I tried to be a diligent reviewer :-)).

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'
  2017-11-06 11:27   ` [PATCH v1 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-11-07  1:36     ` Johannes Schindelin
  2017-11-07 11:16       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07  1:36 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

Hi Phillip,

On Mon, 6 Nov 2017, Phillip Wood wrote:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> If the commit message does not need to be edited then create the
> commit without forking 'git commit'. Taking the best time of ten runs
> with a warm cache this reduces the time taken to cherry-pick 10
> commits by 27% (from 282ms to 204ms), and the time taken by 'git
> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
> my computer running linux. Some of greater saving for rebase is
> because it longer wastes time creating the commit summary just to

I usually leave the grammar reviews to people who prefer to review grammar
over code, but in this case I think the "no" in "no longer" is rather
crucial.

> throw it away.

Those are impressive improvements, and I am certain that they will be even
more noticable on Windows, where creating processes is a lot more
expensive than on Linux (which is the reason why you will find a lot more
multi-threaded processes on Windows...).

> The code to create the commit is based on builtin/commit.c. It is
> slightly simplified as it doesn't have to deal with merges and
> modified so try and return an error rather than dying so that the
> sequencer exits cleanly, as it would when forking 'git commit'.
> 
> Even when not forking 'git commit' the commit message is written to a
> file and CHERRY_PICK_HEAD is created unnecessarily. This could be
> eliminated in future. I hacked up a version that does not write these
> files and just passed an strbuf (with the wrong message for fixup and
> squash commands) to do_commit() but I couldn't measure any significant
> time difference when running cherry-pick or rebase. I think
> eliminating the writes properly for rebase would require a bit of
> effort as the code would need to be restructured.

True. And it totally makes sense to go for the big bucks.

> diff --git a/sequencer.c b/sequencer.c
> index b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
>  	return 0;
>  }
>  
> +static char *get_author(const char* message)
> +{
> +	size_t len;
> +	const char *a;
> +
> +	a = find_commit_header(message, "author", &len);
> +	if (a)
> +		return xmemdupz(a, len);
> +
> +	return NULL;
> +}

I was surprised that there is no helper for that yet, so I looked, but it
seems that the three existing callers of `find_commit_header(...,
"author", ...)` want to split the ident line right away, and do not need
the duplicated buffer.

In short: the code added here is necessary.

> @@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,
>  	return ret;
>  }
>  
> +static int parse_head(struct commit **head)
> +{
> +	struct commit *current_head;
> +	struct object_id oid;
> +
> +	if (get_oid("HEAD", &oid)) {
> +		current_head = NULL;
> +	} else {
> +		current_head = lookup_commit_reference(&oid);
> +		if (!current_head)
> +			return error(_("could not parse HEAD"));
> +		if (oidcmp(&oid, &current_head->object.oid)) {
> +			warning(_("HEAD %s is not a commit!"),
> +				oid_to_hex(&oid));
> +		}
> +		if (parse_commit(current_head))
> +			return error(_("could not parse HEAD commit"));
> +	}
> +	*head = current_head;
> +
> +	return 0;
> +}
> +
> +static int try_to_commit(struct strbuf *msg, const char *author,
> +			 struct replay_opts *opts, unsigned int flags,
> +			 struct object_id *oid)

Since this is a file-local function, i.e. not in any way tied to a
process exit status, it should probably return -1 in the case of errors,
as Git does elsewhere, too.

> +{
> +	struct object_id tree;
> +	struct commit *current_head;
> +	struct commit_list *parents = NULL;
> +	struct commit_extra_header *extra = NULL;
> +	struct strbuf err = STRBUF_INIT;
> +	struct strbuf amend_msg = STRBUF_INIT;
> +	char *amend_author = NULL;
> +	const char *gpg_sign;
> +	enum cleanup_mode cleanup;
> +	int res = 0;
> +
> +	if (parse_head(&current_head))
> +		return -1;
> +
> +	if (flags & AMEND_MSG) {
> +		const char *exclude_gpgsig[2] = { "gpgsig", NULL };

Git's current source code seems to prefer to infer the array length; The
`2` is unnecessary here.

> +		const char *out_enc = get_commit_output_encoding();
> +		const char *message = logmsg_reencode(current_head, NULL,
> +						      out_enc);
> +
> +		if (!msg) {
> +			const char *body = NULL;
> +
> +			find_commit_subject(message, &body);

Maybe `orig_message` would be better here; I expected `body` to refer to
the part of the commit message *after* the subject, but reading the code
of `find_commit_subject()`, I find that it stores the beginning of the
commit message.

Dunno.

> +			msg = &amend_msg;
> +			strbuf_addstr(msg, body);
> +		}
> +		author = amend_author = get_author (message);

Please lose the space after the function name.

> +		unuse_commit_buffer(current_head, message);
> +		if (!author) {
> +			res = error(_("unable to parse commit author"));
> +			goto out;
> +		}
> +		parents = copy_commit_list(current_head->parents);
> +		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
> +	} else if (current_head) {
> +		commit_list_insert(current_head, &parents);
> +	}
> +
> +	cleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;
> +	if (cleanup != CLEANUP_NONE)
> +		strbuf_stripspace(msg, cleanup == CLEANUP_ALL);
> +	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
> +		res = 1;
> +		goto out;
> +	}
> +
> +	gpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;

Others will probably complain about those extra parentheses. I am not
offended by them, though.

> +	if (write_cache_as_tree(tree.hash, 0, NULL)) {
> +		res = error(_("git write-tree failed to write a tree"));
> +		goto out;
> +	}
> +
> +	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
> +					      &current_head->tree->object.oid :
> +					      &empty_tree_oid, &tree)) {

I'll leave it to Junio to comment on the formatting here.

> +		res = 1;
> +		goto out;
> +	}
> +
> +	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
> +				 oid->hash, author, gpg_sign, extra)) {
> +		res = error(_("failed to write commit object"));
> +		goto out;
> +	}
> +
> +	if (update_head(current_head, oid, getenv("GIT_REFLOG_ACTION"), msg,
> +			&err)){
> +		res = error("%s", err.buf);
> +		goto out;
> +	}
> +
> +	if (flags & AMEND_MSG)
> +		commit_post_rewrite(current_head, oid);
> +
> +out:
> +	free_commit_extra_headers(extra);
> +	strbuf_release(&err);
> +	strbuf_release(&amend_msg);
> +	if (amend_author)
> +		free(amend_author);

Git's source code uses the fact that `free(NULL);` is essentially a no-op
(and certainly allowed) to avoid conditionals in such cases.

That would make the `if (amend_author)` unnecessary.

> +
> +	return res;
> +}
> +
> +static int do_commit(const char *msg_file, const char* author,
> +		     struct replay_opts *opts, unsigned int flags)
> +{
> +	int res = 1;

Same as above, the error code should most likely be -1 instead.

> +	if (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {

I *think* it is more common to write `!(flags & EDIT_MSG)` in Git's source
code.

> +		struct object_id oid;
> +		struct strbuf sb = STRBUF_INIT;
> +
> +		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
> +			return error_errno(_("unable to read commit message "
> +					     "from '%s'"),
> +					   msg_file);
> +
> +		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
> +				    &oid);
> +		strbuf_release(&sb);
> +		if (res == 0) {

Usually, Git's source code uses `if (!res)` in such cases.

> +			unlink(git_path_cherry_pick_head());
> +			unlink(git_path_merge_msg());
> +			if (!is_rebase_i(opts))
> +				res = print_commit_summary(NULL, &oid,
> +						SUMMARY_SHOW_AUTHOR_DATE);
> +			return res;
> +		}

I wonder whether we should move the `return res;` one line lower, to avoid
falling through to call `run_git_commit()` if `try_to_commit()` failed...

> +	}
> +	if (res == 1)
> +		return run_git_commit(msg_file, opts, flags);

Maybe this code could be simplified even further by moving this
conditional to the beginning of the function, as:

	if ((flags & (EDIT_MSG | VERIFY_MSG)))
		return run_git_commit(msg_file, opts, flags);

But maybe I misunderstood and you really wanted to fall back on
`run_git_commit()` if `try_to_commit()` failed?

> +
> +	return res;
> +}
> +
>  static int is_original_commit_empty(struct commit *commit)
>  {
>  	const struct object_id *ptree_oid;
> @@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  	struct object_id head;
>  	struct commit *base, *next, *parent;
>  	const char *base_label, *next_label;
> +	char *author = NULL;
>  	struct commit_message msg = { NULL, NULL, NULL, NULL };
>  	struct strbuf msgbuf = STRBUF_INIT;
>  	int res, unborn = 0, allow;
> @@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
>  			strbuf_addstr(&msgbuf, ")\n");
>  		}
> +		if (!is_fixup (command))
> +			author = get_author(msg.message);
>  	}
>  
>  	if (command == TODO_REWORD)
> @@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  		goto leave;
>  	} else if (allow)
>  		flags |= ALLOW_EMPTY;
> -	if (!opts->no_commit)
> +	if (!opts->no_commit) {
>  fast_forward_edit:
> -		res = run_git_commit(msg_file, opts, flags);
> +		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
> +			res = do_commit(msg_file, author, opts, flags);
> +		else
> +			res = error(_("unable to parse commit author"));

Would this be a bug here? Or do we expect `get_author()` to possibly fail?

> +	}
>  
>  	if (!res && final_fixup) {
>  		unlink(rebase_path_fixup_msg());
> @@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  
>  leave:
>  	free_message(commit, &msg);
> +	if (author)
> +		free(author);

As above, please write an unconditional `free(author);` here.

All in all, this patch series was a nice an pleasant read. I am impressed
by the performance wins.

Thank you very much,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit
  2017-11-06 11:27   ` [PATCH v1 2/8] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-07  2:56     ` Junio C Hamano
  2017-11-07  3:02       ` Johannes Schindelin
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07  2:56 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> @@ -1735,25 +1733,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>  	strbuf_release(&author_ident);
>  	free_commit_extra_headers(extra);
>  
> -	nl = strchr(sb.buf, '\n');
> -	if (nl)
> -		strbuf_setlen(&sb, nl + 1 - sb.buf);
> -	else
> -		strbuf_addch(&sb, '\n');
> -	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
> -	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);

The old code treated sb (which has the log message we gave to
commit_tree_extended() to create the commit) as expendable at this
point and (1) truncated it to the title line, and (2) prepended the
reflog action prefix, so that it can pass it to the ref transaction
code to use it as the reflog message.

Which was quite ugly X-<.

> -	transaction = ref_transaction_begin(&err);
> -	if (!transaction ||
> -	    ref_transaction_update(transaction, "HEAD", &oid,
> -				   current_head
> -				   ? &current_head->object.oid : &null_oid,
> -				   0, sb.buf, &err) ||
> -	    ref_transaction_commit(transaction, &err)) {
> +	if (update_head(current_head, &oid, reflog_msg, &sb, &err)) {
>  		rollback_index_files();
>  		die("%s", err.buf);
>  	}

> @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
>  	return rest_is_empty(sb, start - sb->buf);
>  }
>  
> +int update_head(const struct commit *old_head, const struct object_id *new_head,
> +		const char *action, const struct strbuf *msg,
> +		struct strbuf *err)
> +{
> +	struct ref_transaction *transaction;
> +	struct strbuf sb = STRBUF_INIT;

It no longer is necessary to call this variable "sb"; the original
had a single instance of strbuf that was reused for different
purposes and could not give it a more specific name, but we can
afford to call this one reflog_message or something.

> +	const char *nl;
> +	int ret = 0;
> +
> +	if (action) {
> +		strbuf_addstr(&sb, action);
> +		strbuf_addstr(&sb, ": ");
> +	}
> +
> +	nl = strchr(msg->buf, '\n');
> +	if (nl) {
> +		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
> +	} else {
> +		strbuf_addbuf(&sb, msg);
> +		strbuf_addch(&sb, '\n');
> +	}

The updated code is a lot more natural and straight-forward.  I
quite like it.

I however do not think update_head() is such a good name for a
helper function in the global scope.  builtin/clone.c has a static
one that has quite different semantics with the same name (I am not
saying that builtin/clone.c will in the future start including the
sequencer.h header file; I am pointing out that update_head() is not
a good global name that will be understood by everybody).

> diff --git a/sequencer.h b/sequencer.h
> index 65a4b0c25185d7ad5115035abb766d1b95df9a62..1db06caea35bed556dfaabca1c6be8a80857ed5e 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -68,4 +68,7 @@ enum cleanup_mode {
>  int message_is_empty(const struct strbuf *sb, enum cleanup_mode cleanup_mode);
>  int template_untouched(const struct strbuf *sb, const char *template_file,
>  		       enum cleanup_mode cleanup_mode);
> +int update_head(const struct commit *old_head, const struct object_id *new_head,
> +		const char* action, const struct strbuf *msg,
> +		struct strbuf *err);
>  #endif

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit
  2017-11-07  2:56     ` Junio C Hamano
@ 2017-11-07  3:02       ` Johannes Schindelin
  2017-11-07 14:26         ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07  3:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, Git Mailing List, Phillip Wood

Hi Junio,

On Tue, 7 Nov 2017, Junio C Hamano wrote:

> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
> > @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
> >  	return rest_is_empty(sb, start - sb->buf);
> >  }
> >  
> > +int update_head(const struct commit *old_head, const struct object_id *new_head,
> > +		const char *action, const struct strbuf *msg,
> > +		struct strbuf *err)
> > +{
> 
> [...]
>
> I however do not think update_head() is such a good name for a
> helper function in the global scope.  builtin/clone.c has a static
> one that has quite different semantics with the same name (I am not
> saying that builtin/clone.c will in the future start including the
> sequencer.h header file; I am pointing out that update_head() is not
> a good global name that will be understood by everybody).

Please try to always accompany a "Don't Do That" by a "How About This
Instead".

In this case, I could imagine that `update_head_with_reflog()` would be a
better name. If you disagree, I invite you to propose an alternative that
strikes your liking.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 3/8] commit: move post-rewrite code to libgit
  2017-11-06 11:27   ` [PATCH v1 3/8] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-11-07  3:03     ` Junio C Hamano
  2017-11-07 14:28       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07  3:03 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
> be shared with other commands and add a new function
> commit_post_rewrite() based on the code in builtin/commit.c that
> encapsulates rewriting notes and running the post-rewrite hook.

This, especially the part that rips out the notes rewriting from
builtin/commit.c and moves it to elsewhere, is a bit curious
separation of labor.  I guess we'll see why in later steps in the
series.

The change itself looks like a regression-free no-op, which is good.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit
  2017-11-06 11:27   ` [PATCH v1 4/8] commit: move print_commit_summary() " Phillip Wood
@ 2017-11-07  3:38     ` Junio C Hamano
  2017-11-07 14:32       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07  3:38 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Move print_commit_summary() from builtin/commit.c to sequencer.c so it
> can be shared with other commands. The function is modified by
> changing the last argument to a flag so callers can specify whether
> they want to show the author date in addition to specifying if this is
> an initial commit.

A movement of a long function like this one really is easier if you
did not make any other unnecessary change in the same patch and then
made the change as a follow-up.

The end result seemed sane.  

Do not use signed int as a collection of bits "flags", as it makes
readers wonder if you are going to do some clever thing by treating
the topmost bit somewhat special (e.g. "if (flags < 0)").  Unless
you are indeed doing something clever like that, use "unsigned int"
instead.


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-06 11:27   ` [PATCH v1 5/8] sequencer: don't die in print_commit_summary() Phillip Wood
@ 2017-11-07  4:18     ` Junio C Hamano
  2017-11-07 10:24       ` Johannes Schindelin
  2017-11-07 15:13       ` Junio C Hamano
  0 siblings, 2 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07  4:18 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Return an error rather than dying so that the sequencer can exit
> cleanly once it starts committing without forking 'git commit'
>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>...
> @@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
>  		log_tree_commit(&rev, commit);
>  	}

Is that call to log_tree_commit() a big elephant in the room?

All of the die() we see above you are making into error() are rather
unlikely conditions (e.g. you created a commit, and try to look it
up immediately after that, and you somehow fail to find it);
log_tree_commit() makes tons more object accesses, any of which
would be equally likely to fail and die.

It definitely is a good thing to eventually make a direct in-process
call into the commit machinery, and we should aim for that endgame.

And this step is going in the right direction, but I am not sure if
this made the function safe enough to be called repeatedly from the
rebase machinery and we are ready to unleash this to the end users
and tell them it is safe to use it.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-11-06 11:27   ` [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
  2017-11-07  0:52     ` Johannes Schindelin
@ 2017-11-07  4:52     ` Junio C Hamano
  2017-11-07 14:46       ` Phillip Wood
  1 sibling, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07  4:52 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> diff --git a/sequencer.c b/sequencer.c
> index ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
>  			_(action_name(opts)));
>  	rollback_lock_file(&index_lock);
>  
> -	if (opts->signoff)
> -		append_signoff(msgbuf, 0, 0);
> -
>  	if (!clean)
>  		append_conflicts_hint(msgbuf);
>  

This function is called from only one place,  do_pick_commit(), and
then the message returned from here in msgbuf is written to
merge_msg(), even when the merge conflicted.

And when the merge conflicts, sequencer would stop and gives the
control back to you---the MERGE_MSG file would have had the sign-off
when you conclude the conflict resolution.

With the new code, we instead add the sign-off before calling the
function to compensate for the above change, so MERGE_MSG file would
have the sign-off as before, when the sequencer stops.

> @@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
>  		argv_array_push(&cmd.args, "--amend");
>  	if (opts->gpg_sign)
>  		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
> -	if (opts->signoff)
> -		argv_array_push(&cmd.args, "-s");
>  	if (defmsg)
>  		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
>  	if ((flags & CLEANUP_MSG))

This has two callers.  

The caller in do_pick_commit() is a bit curious; as we saw already,
the message file should already have the sign-off and then we used
to give another "-s" here.  Were we depending on "-s" to become
no-op when the last sign-off is by the same person, I wonder?  In
any case, the removal of "-s" from here won't hurt that caller.

The other caller is commit_staged_changes() which is called when
doing "rebase -i continue".  I am not quite sure where the contents
stored in the file rebase_path_message() comes from.  The function
error_failed_squash() moves SQUASH_MSG to it and then makes a copy
of it to MERGE_MSG, but that should only be relevant for squashed
commit and no other cases, so...?

I need to block a bit more time to read the relevant code to comment
on this step, especially on this removal.

Thanks for working on this, anyway.

> @@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  		}
>  	}
>  
> +	if (opts->signoff)
> +		append_signoff(&msgbuf, 0, 0);
> +
>  	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
>  		res = -1;
>  	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-07  4:18     ` Junio C Hamano
@ 2017-11-07 10:24       ` Johannes Schindelin
  2017-11-07 15:13       ` Junio C Hamano
  1 sibling, 0 replies; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07 10:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, Git Mailing List, Phillip Wood

Hi Junio,

On Tue, 7 Nov 2017, Junio C Hamano wrote:

> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
> > From: Phillip Wood <phillip.wood@dunelm.org.uk>
> >
> > Return an error rather than dying so that the sequencer can exit
> > cleanly once it starts committing without forking 'git commit'
> >
> > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> > ---
> >...
> > @@ -948,7 +951,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
> >  		log_tree_commit(&rev, commit);
> >  	}
> 
> Is that call to log_tree_commit() a big elephant in the room?

Or maybe not so big an elephant, given that we *already* use it in
sequencer.c, in the `make_patch()` function. I did spend a substantial
amount of time to try to libify all of those calls back when I worked on
the rebase helper.

Also, bisect.c (not builtin/bisect.c) seems to use that
`log_tree_commit()` function, so it better be libified.

Granted, those two callers are *pretty* close to the end of the process,
`bisect_next_all()` is exit(10)-ing right after calling the caller of
`log_tree_commit()` (*shudder* what were we thinking, allowing this
completely unelegant and inconvenient pattern of exit()ing from
libgit.a?), and `make_patch()` is what the sequencer uses to fake the
patch that it never tried to apply (just to be consistent with
non-interactive rebase) just before erroring out.

And I am quite embarrassed for not having spotted the
`maybe_flush_or_die()` call in `log_tree_commit()`.

However, from a not-quite-as-quick-as-I-wanted look, it would appear that
this call is the only hard `exit()` code path in `log_tree_commit()`, and
it should be easily fixed.

Please also note that this is our mess, not Phillip's, as we let these
die()/exit() calls creep into libgit.a. It would be no fair to ask Phillip
to clean it up for us. *We* let this slide for over a decade: 06f59e9f5da
(Don't fflush(stdout) when it's not helpful, 2007-06-29).

> It definitely is a good thing to eventually make a direct in-process
> call into the commit machinery, and we should aim for that endgame.
> 
> And this step is going in the right direction, but I am not sure if
> this made the function safe enough to be called repeatedly from the
> rebase machinery and we are ready to unleash this to the end users
> and tell them it is safe to use it.

Well, holding it up won't fix it faster, it will just delay the fix.

I mean, this is Git, right? This is the same Git where we knowingly
introduced a BUG() call into released versions via b1ef400eece
(setup_git_env: avoid blind fall-back to ".git", 2016-10-20) that were
*prone* to hit end users' use cases that we did not think about, just so
we could fix those code paths.

Sure, we tried to do our best to avoid having end users see those BUG
reports, by investigating the code paths that eventually call
`setup_git_env()`. We can just as easily do the same here: investigate as
well as is reasonable the code paths that eventually call `die()` or
`exit()` from `log_tree_commit()`, and then flesh out the remaining
problems in production.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 7/8] sequencer: load commit related config
  2017-11-07  1:02     ` Johannes Schindelin
@ 2017-11-07 10:50       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 10:50 UTC (permalink / raw)
  To: Johannes Schindelin, Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

Thanks for looking at these patches

On 07/11/17 01:02, Johannes Schindelin wrote:
> Hi Phillip,
> 
> On Mon, 6 Nov 2017, Phillip Wood wrote:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Load default values for message cleanup and gpg signing of commits in
>> preparation for committing without forking 'git commit'.
> 
> Nicely explained.
> 
>> diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
>> index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
>> --- a/builtin/rebase--helper.c
>> +++ b/builtin/rebase--helper.c
>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>>  	NULL
>>  };
>>  
>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
>> +{
>> +	int status;
>> +
>> +	status = git_sequencer_config(k, v, NULL);
>> +	if (status)
>> +		return status;
>> +
>> +	return git_default_config(k, v, NULL);
> 
> It's more a matter of taste than anything else, but this one would be a
> little bit shorter:
> 
> 	return git_sequencer_config(k, v, NULL) ||
> 		git_default_config(k, v, NULL);

I'd do that in python or perl but in C it changes the return value which
may not matter but if the git convention is to return -1 for errors then
this deviates from that.

> A more important question would be whether this `git_default_config()`
> call could be folded into `git_sequencer_config()` right away, so that the
> same pattern does not have to be repeated in rebase--helper as well as in
> revert/cherry-pick.

I kept it separate to be more flexible, imagining that in the future
there maybe other commands that want to call git_sequencer_config()
followed by some_othor_config() before git_default_config(). I don't
have a strong opinion either way.

>> diff --git a/builtin/revert.c b/builtin/revert.c
>> index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..b700dc7f7fd8657ed8cd2450a8537fe98371783f 100644
>> --- a/builtin/revert.c
>> +++ b/builtin/revert.c
>> @@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
>>  	NULL
>>  };
>>  
>> +static int git_revert_config(const char *k, const char *v, void *cb)
> 
> Seeing as it is used also by `cmd_cherry_pick()`, and that it is
> file-local anyway, maybe `common_config()` is a better name?

Yes that would be better, the old name came from the file it was in.

> 
> This point is moot if we can call `git_default_config()` in
> `git_sequencer_config()` directly, though.
> 
>> diff --git a/sequencer.c b/sequencer.c
>> index 3e4c3bbb265db58df22cfcb5a321fb74d822327e..b8cf679751449591d6f97102904e060ebee9d7a1 100644
>> --- a/sequencer.c
>> +++ b/sequencer.c
>> @@ -688,6 +688,39 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
>>  	return run_command(&cmd);
>>  }
>>  
>> +static enum cleanup_mode default_msg_cleanup = CLEANUP_NONE;
>> +static char *default_gpg_sign;
> 
> I was ready to shout about global state not meshing well with libified
> code, but as long as we're sure that these values are set only while Git
> executes single-threaded, still, it is the correct way to do it: these
> settings reflect the config, and therefore *are* kinda global (at least
> until the day when the submodule fans try to call `git commit` in a
> submodule using the `struct repository` data structure).
> 
> In short: this code is good (and I was just describing a little bit of my
> thinking, to demonstrate that I tried to be a diligent reviewer :-)).
> 
> Thanks,
> Dscho
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'
  2017-11-07  1:36     ` Johannes Schindelin
@ 2017-11-07 11:16       ` Phillip Wood
  2017-11-07 14:09         ` Johannes Schindelin
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 11:16 UTC (permalink / raw)
  To: Johannes Schindelin, Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

On 07/11/17 01:36, Johannes Schindelin wrote:
> Hi Phillip,
> 
> On Mon, 6 Nov 2017, Phillip Wood wrote:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> If the commit message does not need to be edited then create the
>> commit without forking 'git commit'. Taking the best time of ten runs
>> with a warm cache this reduces the time taken to cherry-pick 10
>> commits by 27% (from 282ms to 204ms), and the time taken by 'git
>> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
>> my computer running linux. Some of greater saving for rebase is
>> because it longer wastes time creating the commit summary just to
> 
> I usually leave the grammar reviews to people who prefer to review grammar
> over code, but in this case I think the "no" in "no longer" is rather
> crucial.

Yes it is, well spotted

>> throw it away.
> 
> Those are impressive improvements, and I am certain that they will be even
> more noticable on Windows, where creating processes is a lot more
> expensive than on Linux (which is the reason why you will find a lot more
> multi-threaded processes on Windows...).

Yes, I was surprised how big the difference was. Rerunning the same
commands arguably over emphasizes the time spent in forking git commit
as after the first run all the objects are already on disk but I don't
know how much difference that makes.

>> The code to create the commit is based on builtin/commit.c. It is
>> slightly simplified as it doesn't have to deal with merges and
>> modified so try and return an error rather than dying so that the
>> sequencer exits cleanly, as it would when forking 'git commit'.
>>
>> Even when not forking 'git commit' the commit message is written to a
>> file and CHERRY_PICK_HEAD is created unnecessarily. This could be
>> eliminated in future. I hacked up a version that does not write these
>> files and just passed an strbuf (with the wrong message for fixup and
>> squash commands) to do_commit() but I couldn't measure any significant
>> time difference when running cherry-pick or rebase. I think
>> eliminating the writes properly for rebase would require a bit of
>> effort as the code would need to be restructured.
> 
> True. And it totally makes sense to go for the big bucks.
> 
>> diff --git a/sequencer.c b/sequencer.c
>> index b8cf679751449591d6f97102904e060ebee9d7a1..0636d027e9e1cdebaab4802e5becd89e8398a425 100644
>> --- a/sequencer.c
>> +++ b/sequencer.c
>> @@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
>>  	return 0;
>>  }
>>  
>> +static char *get_author(const char* message)
>> +{
>> +	size_t len;
>> +	const char *a;
>> +
>> +	a = find_commit_header(message, "author", &len);
>> +	if (a)
>> +		return xmemdupz(a, len);
>> +
>> +	return NULL;
>> +}
> 
> I was surprised that there is no helper for that yet, so I looked, but it
> seems that the three existing callers of `find_commit_header(...,
> "author", ...)` want to split the ident line right away, and do not need
> the duplicated buffer.
> 
> In short: the code added here is necessary.
> 
>> @@ -984,6 +996,151 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,
>>  	return ret;
>>  }
>>  
>> +static int parse_head(struct commit **head)
>> +{
>> +	struct commit *current_head;
>> +	struct object_id oid;
>> +
>> +	if (get_oid("HEAD", &oid)) {
>> +		current_head = NULL;
>> +	} else {
>> +		current_head = lookup_commit_reference(&oid);
>> +		if (!current_head)
>> +			return error(_("could not parse HEAD"));
>> +		if (oidcmp(&oid, &current_head->object.oid)) {
>> +			warning(_("HEAD %s is not a commit!"),
>> +				oid_to_hex(&oid));
>> +		}
>> +		if (parse_commit(current_head))
>> +			return error(_("could not parse HEAD commit"));
>> +	}
>> +	*head = current_head;
>> +
>> +	return 0;
>> +}
>> +
>> +static int try_to_commit(struct strbuf *msg, const char *author,
>> +			 struct replay_opts *opts, unsigned int flags,
>> +			 struct object_id *oid)
> 
> Since this is a file-local function, i.e. not in any way tied to a
> process exit status, it should probably return -1 in the case of errors,
> as Git does elsewhere, too.

It returns -1 in case of error and 1 if it wants git commit to be run.
There are some error messages in git commit that weren't completely
straight forward to move to libgit as they were tied up with some git
status config values so I opted just to test for the error condition
here and fork git commit to display the error message.

>> +{
>> +	struct object_id tree;
>> +	struct commit *current_head;
>> +	struct commit_list *parents = NULL;
>> +	struct commit_extra_header *extra = NULL;
>> +	struct strbuf err = STRBUF_INIT;
>> +	struct strbuf amend_msg = STRBUF_INIT;
>> +	char *amend_author = NULL;
>> +	const char *gpg_sign;
>> +	enum cleanup_mode cleanup;
>> +	int res = 0;
>> +
>> +	if (parse_head(&current_head))
>> +		return -1;
>> +
>> +	if (flags & AMEND_MSG) {
>> +		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
> 
> Git's current source code seems to prefer to infer the array length; The
> `2` is unnecessary here.

Right, I copied it from builtin/commit.c but I can change it

>> +		const char *out_enc = get_commit_output_encoding();
>> +		const char *message = logmsg_reencode(current_head, NULL,
>> +						      out_enc);
>> +
>> +		if (!msg) {
>> +			const char *body = NULL;
>> +
>> +			find_commit_subject(message, &body);
> 
> Maybe `orig_message` would be better here; I expected `body` to refer to
> the part of the commit message *after* the subject, but reading the code
> of `find_commit_subject()`, I find that it stores the beginning of the
> commit message.
> 
> Dunno.

Yes body is kind of misleading, as that it storing the whole message
with the subject as well.

>> +			msg = &amend_msg;
>> +			strbuf_addstr(msg, body);
>> +		}
>> +		author = amend_author = get_author (message);
> 
> Please lose the space after the function name.

Well spotted, I thought I'd seen a space between a function name and '('
in one of the patches but then I couldn't find it.

>> +		unuse_commit_buffer(current_head, message);
>> +		if (!author) {
>> +			res = error(_("unable to parse commit author"));
>> +			goto out;
>> +		}
>> +		parents = copy_commit_list(current_head->parents);
>> +		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
>> +	} else if (current_head) {
>> +		commit_list_insert(current_head, &parents);
>> +	}
>> +
>> +	cleanup = (flags & CLEANUP_MSG) ? CLEANUP_ALL : default_msg_cleanup;
>> +	if (cleanup != CLEANUP_NONE)
>> +		strbuf_stripspace(msg, cleanup == CLEANUP_ALL);
>> +	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
>> +		res = 1;
>> +		goto out;
>> +	}
>> +
>> +	gpg_sign = (opts->gpg_sign) ? opts->gpg_sign : default_gpg_sign;
> 
> Others will probably complain about those extra parentheses. I am not
> offended by them, though.
> 
>> +	if (write_cache_as_tree(tree.hash, 0, NULL)) {
>> +		res = error(_("git write-tree failed to write a tree"));
>> +		goto out;
>> +	}
>> +
>> +	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
>> +					      &current_head->tree->object.oid :
>> +					      &empty_tree_oid, &tree)) {
> 
> I'll leave it to Junio to comment on the formatting here.
> 
>> +		res = 1;
>> +		goto out;
>> +	}
>> +
>> +	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
>> +				 oid->hash, author, gpg_sign, extra)) {
>> +		res = error(_("failed to write commit object"));
>> +		goto out;
>> +	}

Looking more deeply, this can die in write_loose_object(), hopefully
that is unlikely. git am commits without forking as well so I think it
is subject to the same problem.

>> +
>> +	if (update_head(current_head, oid, getenv("GIT_REFLOG_ACTION"), msg,
>> +			&err)){
>> +		res = error("%s", err.buf);
>> +		goto out;
>> +	}
>> +
>> +	if (flags & AMEND_MSG)
>> +		commit_post_rewrite(current_head, oid);
>> +
>> +out:
>> +	free_commit_extra_headers(extra);
>> +	strbuf_release(&err);
>> +	strbuf_release(&amend_msg);
>> +	if (amend_author)
>> +		free(amend_author);
> 
> Git's source code uses the fact that `free(NULL);` is essentially a no-op
> (and certainly allowed) to avoid conditionals in such cases.
> 
> That would make the `if (amend_author)` unnecessary.

Thanks, I'll change it

>> +	return res;
>> +}
>> +
>> +static int do_commit(const char *msg_file, const char* author,
>> +		     struct replay_opts *opts, unsigned int flags)
>> +{
>> +	int res = 1;
> 
> Same as above, the error code should most likely be -1 instead.

1 means run git commit, -1 means there was an error. I should document that.

>> +	if (~flags & EDIT_MSG && ~flags & VERIFY_MSG) {
> 
> I *think* it is more common to write `!(flags & EDIT_MSG)` in Git's source
> code.

Yes looking though the other code in sequencer.c that seems to be the
common idiom.

>> +		struct object_id oid;
>> +		struct strbuf sb = STRBUF_INIT;
>> +
>> +		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
>> +			return error_errno(_("unable to read commit message "
>> +					     "from '%s'"),
>> +					   msg_file);
>> +
>> +		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
>> +				    &oid);
>> +		strbuf_release(&sb);
>> +		if (res == 0) {
> 
> Usually, Git's source code uses `if (!res)` in such cases.
> 
>> +			unlink(git_path_cherry_pick_head());
>> +			unlink(git_path_merge_msg());
>> +			if (!is_rebase_i(opts))
>> +				res = print_commit_summary(NULL, &oid,
>> +						SUMMARY_SHOW_AUTHOR_DATE);
>> +			return res;
>> +		}
> 
> I wonder whether we should move the `return res;` one line lower, to avoid
> falling through to call `run_git_commit()` if `try_to_commit()` failed...

No, that is what I want it to do

>> +	}
>> +	if (res == 1)
>> +		return run_git_commit(msg_file, opts, flags);
> 
> Maybe this code could be simplified even further by moving this
> conditional to the beginning of the function, as:
> 
> 	if ((flags & (EDIT_MSG | VERIFY_MSG)))
> 		return run_git_commit(msg_file, opts, flags);
> 
> But maybe I misunderstood and you really wanted to fall back on
> `run_git_commit()` if `try_to_commit()` failed?

Exactly

>> +
>> +	return res;
>> +}
>> +
>>  static int is_original_commit_empty(struct commit *commit)
>>  {
>>  	const struct object_id *ptree_oid;
>> @@ -1235,6 +1392,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>>  	struct object_id head;
>>  	struct commit *base, *next, *parent;
>>  	const char *base_label, *next_label;
>> +	char *author = NULL;
>>  	struct commit_message msg = { NULL, NULL, NULL, NULL };
>>  	struct strbuf msgbuf = STRBUF_INIT;
>>  	int res, unborn = 0, allow;
>> @@ -1350,6 +1508,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>>  			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
>>  			strbuf_addstr(&msgbuf, ")\n");
>>  		}
>> +		if (!is_fixup (command))
>> +			author = get_author(msg.message);
>>  	}
>>  
>>  	if (command == TODO_REWORD)
>> @@ -1435,9 +1595,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>>  		goto leave;
>>  	} else if (allow)
>>  		flags |= ALLOW_EMPTY;
>> -	if (!opts->no_commit)
>> +	if (!opts->no_commit) {
>>  fast_forward_edit:
>> -		res = run_git_commit(msg_file, opts, flags);
>> +		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
>> +			res = do_commit(msg_file, author, opts, flags);
>> +		else
>> +			res = error(_("unable to parse commit author"));
> 
> Would this be a bug here? Or do we expect `get_author()` to possibly fail?

I'm not sure, if the commit has somehow been created by a buggy
implementation without an author then we should complain. git commit
reparses the author details to check they look reasonable before reusing
them, maybe this should as well rather than just checking that there is
something there.

>> +	}
>>  
>>  	if (!res && final_fixup) {
>>  		unlink(rebase_path_fixup_msg());
>> @@ -1446,6 +1610,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>>  
>>  leave:
>>  	free_message(commit, &msg);
>> +	if (author)
>> +		free(author);
> 
> As above, please write an unconditional `free(author);` here.

Will do

> All in all, this patch series was a nice an pleasant read. I am impressed
> by the performance wins.
>
Thanks that's nice to hear, thanks also for taking the time to comment
on them.

Best Wishes

Phillip

> Thank you very much,
> Dscho





^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 8/8] sequencer: try to commit without forking 'git commit'
  2017-11-07 11:16       ` Phillip Wood
@ 2017-11-07 14:09         ` Johannes Schindelin
  0 siblings, 0 replies; 120+ messages in thread
From: Johannes Schindelin @ 2017-11-07 14:09 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

Hi Phillip,

On Tue, 7 Nov 2017, Phillip Wood wrote:

> On 07/11/17 01:36, Johannes Schindelin wrote:
> > 
> > On Mon, 6 Nov 2017, Phillip Wood wrote:
> > 
> >> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> >>
> >> +static int try_to_commit(struct strbuf *msg, const char *author,
> >> +			 struct replay_opts *opts, unsigned int flags,
> >> +			 struct object_id *oid)
> > 
> > Since this is a file-local function, i.e. not in any way tied to a
> > process exit status, it should probably return -1 in the case of errors,
> > as Git does elsewhere, too.
> 
> It returns -1 in case of error and 1 if it wants git commit to be run.

Ah, that explains a lot! I would like to ask for a code comment above the
`try_to_commit()` function to explain that, so that future me will avoid
confusion when staring at this code. I would also like to ask for a code
comment at the `return 1;` statements, saying e.g. We could not commit
in-process, caller should try forking `git commit`.

> >> +	if (flags & AMEND_MSG) {
> >> +		const char *exclude_gpgsig[2] = { "gpgsig", NULL };
> > 
> > Git's current source code seems to prefer to infer the array length; The
> > `2` is unnecessary here.
> 
> Right, I copied it from builtin/commit.c but I can change it

Sorry about that. It still would be good to change it, I just wish that
you had better code at your fingertips to copy/edit ;-)

> >> +	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
> >> +				 oid->hash, author, gpg_sign, extra)) {
> >> +		res = error(_("failed to write commit object"));
> >> +		goto out;
> >> +	}
> 
> Looking more deeply, this can die in write_loose_object(), hopefully
> that is unlikely. git am commits without forking as well so I think it
> is subject to the same problem.

Yes. We will have to address those die() issues. But not necessarily in
your patch series; as I said before, this mess is not your fault.

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 1/8] commit: move empty message checks to libgit
  2017-11-07  0:43     ` Johannes Schindelin
@ 2017-11-07 14:24       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 14:24 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Junio C Hamano, Phillip Wood

On 07/11/17 00:43, Johannes Schindelin wrote:
> Hi Phillip,
> 
> On Mon, 6 Nov 2017, Phillip Wood wrote:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Move the functions that check for empty messages from bulitin/commit.c
>> to sequencer.c so they can be shared with other commands. The
>> functions are refactored to take an explicit cleanup mode and template
>> filename passed by the caller.
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Good rationale. Just one thing:
> 
>> diff --git a/sequencer.h b/sequencer.h
>> index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..65a4b0c25185d7ad5115035abb766d1b95df9a62 100644
>> --- a/sequencer.h
>> +++ b/sequencer.h
>> @@ -58,4 +58,14 @@ extern const char sign_off_header[];
>>  void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
>>  void append_conflicts_hint(struct strbuf *msgbuf);
>>  
>> +enum cleanup_mode {
>> +	CLEANUP_SPACE,
>> +	CLEANUP_NONE,
>> +	CLEANUP_SCISSORS,
>> +	CLEANUP_ALL
>> +};
> 
> When it was file-local, `cleanup_mode` was okay (although far from great).
> Now that we want to make it more widely available, I fear we have to make
> the name much longer, e.g. `commit_msg_cleanup_mode`.

That's certainly less ambiguous! I'll bite the bullet and extend the name.


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 2/8] Add a function to update HEAD after creating a commit
  2017-11-07  3:02       ` Johannes Schindelin
@ 2017-11-07 14:26         ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 14:26 UTC (permalink / raw)
  To: Johannes Schindelin, Junio C Hamano; +Cc: Git Mailing List, Phillip Wood

On 07/11/17 03:02, Johannes Schindelin wrote:
> Hi Junio,
> 
> On Tue, 7 Nov 2017, Junio C Hamano wrote:
> 
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>>
>>> @@ -751,6 +751,42 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
>>>  	return rest_is_empty(sb, start - sb->buf);
>>>  }
>>>  
>>> +int update_head(const struct commit *old_head, const struct object_id *new_head,
>>> +		const char *action, const struct strbuf *msg,
>>> +		struct strbuf *err)
>>> +{
>>
>> [...]
>>
>> I however do not think update_head() is such a good name for a
>> helper function in the global scope.  builtin/clone.c has a static
>> one that has quite different semantics with the same name (I am not
>> saying that builtin/clone.c will in the future start including the
>> sequencer.h header file; I am pointing out that update_head() is not
>> a good global name that will be understood by everybody).

Good point, I'll go with the name Dscho suggests if that's OK with you.

> Please try to always accompany a "Don't Do That" by a "How About This
> Instead".
> 
> In this case, I could imagine that `update_head_with_reflog()` would be a
> better name. If you disagree, I invite you to propose an alternative that
> strikes your liking.
> 
> Ciao,
> Dscho
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 3/8] commit: move post-rewrite code to libgit
  2017-11-07  3:03     ` Junio C Hamano
@ 2017-11-07 14:28       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 14:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 07/11/17 03:03, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
>> be shared with other commands and add a new function
>> commit_post_rewrite() based on the code in builtin/commit.c that
>> encapsulates rewriting notes and running the post-rewrite hook.
> 
> This, especially the part that rips out the notes rewriting from
> builtin/commit.c and moves it to elsewhere, is a bit curious
> separation of labor.  I guess we'll see why in later steps in the
> series.

It's so the sequencer can have a built in version of 'git commit
--amend' when processing fixup/squash commands. I'll expand the commit
message to explain why this is needed later.


> The change itself looks like a regression-free no-op, which is good.
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit
  2017-11-07  3:38     ` Junio C Hamano
@ 2017-11-07 14:32       ` Phillip Wood
  2017-11-08  1:04         ` Junio C Hamano
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 14:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 07/11/17 03:38, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Move print_commit_summary() from builtin/commit.c to sequencer.c so it
>> can be shared with other commands. The function is modified by
>> changing the last argument to a flag so callers can specify whether
>> they want to show the author date in addition to specifying if this is
>> an initial commit.
> 
> A movement of a long function like this one really is easier if you
> did not make any other unnecessary change in the same patch and then
> made the change as a follow-up.

I'm not sure what you mean by unnecessary, the original code called a
file-local function author_date_is_interesting(). That had to be changed
in order to move the code, I guess it would have been clearer to make
that change first and then move the modified code to sequencer.c in a
separate commit.

> The end result seemed sane.  
> 
> Do not use signed int as a collection of bits "flags", as it makes
> readers wonder if you are going to do some clever thing by treating
> the topmost bit somewhat special (e.g. "if (flags < 0)").  Unless
> you are indeed doing something clever like that, use "unsigned int"
> instead.
> 

Thanks, I'll change it to an unsigned int.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-11-07  4:52     ` Junio C Hamano
@ 2017-11-07 14:46       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-07 14:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 07/11/17 04:52, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> diff --git a/sequencer.c b/sequencer.c
>> index ae24405c23d021ed7916e5e2d9df6de27f867a2e..3e4c3bbb265db58df22cfcb5a321fb74d822327e 100644
>> --- a/sequencer.c
>> +++ b/sequencer.c
>> @@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
>>  			_(action_name(opts)));
>>  	rollback_lock_file(&index_lock);
>>  
>> -	if (opts->signoff)
>> -		append_signoff(msgbuf, 0, 0);
>> -
>>  	if (!clean)
>>  		append_conflicts_hint(msgbuf);
>>  
> 
> This function is called from only one place,  do_pick_commit(), and
> then the message returned from here in msgbuf is written to
> merge_msg(), even when the merge conflicted.
> 
> And when the merge conflicts, sequencer would stop and gives the
> control back to you---the MERGE_MSG file would have had the sign-off
> when you conclude the conflict resolution.
> 
> With the new code, we instead add the sign-off before calling the
> function to compensate for the above change, so MERGE_MSG file would
> have the sign-off as before, when the sequencer stops.
> 
>> @@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
>>  		argv_array_push(&cmd.args, "--amend");
>>  	if (opts->gpg_sign)
>>  		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
>> -	if (opts->signoff)
>> -		argv_array_push(&cmd.args, "-s");
>>  	if (defmsg)
>>  		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
>>  	if ((flags & CLEANUP_MSG))
> 
> This has two callers.  
> 
> The caller in do_pick_commit() is a bit curious; as we saw already,
> the message file should already have the sign-off and then we used
> to give another "-s" here.  Were we depending on "-s" to become
> no-op when the last sign-off is by the same person, I wonder?  In
> any case, the removal of "-s" from here won't hurt that caller.

That was more or less the conclusion I came to as well, though if the
user specifies a merge strategy other than recursive, then we were
relying on the "-s" passed to 'git commit' to add the sign-off. Now we
add the sign-off to the message ourselves in that case.

> The other caller is commit_staged_changes() which is called when
> doing "rebase -i continue".  I am not quite sure where the contents
> stored in the file rebase_path_message() comes from.  The function
> error_failed_squash() moves SQUASH_MSG to it and then makes a copy
> of it to MERGE_MSG, but that should only be relevant for squashed
> commit and no other cases, so...?

The interactive version of rebase does not support '--signoff' so this
is moot at the moment. I think that for conflicts with pick/edit/reword
then the sign-off is added to MERGE_MSG  and that file is then picked up
by 'git commit'. For squash/fixup then the sign-off should have already
been added to the commit whose message is used for the first message in
SQUASH_MSG, but that will not be at the end of the message where we
expect Signed-off-by: to be. I'd need to check properly but I suspect we
also end up with a Signed-off-by: added at the end of SQUASH_MSG as well.

> 
> I need to block a bit more time to read the relevant code to comment
> on this step, especially on this removal.
> 
> Thanks for working on this, anyway.

Thanks for looking at these patches and your comments on them, I'll get
on with making the changes you and Johannes have suggested and wait to
hear from you about this one.

Best Wishes

Phillip
> 
>> @@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>>  		}
>>  	}
>>  
>> +	if (opts->signoff)
>> +		append_signoff(&msgbuf, 0, 0);
>> +
>>  	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
>>  		res = -1;
>>  	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-07  4:18     ` Junio C Hamano
  2017-11-07 10:24       ` Johannes Schindelin
@ 2017-11-07 15:13       ` Junio C Hamano
  2017-11-10 14:53         ` Phillip Wood
  1 sibling, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-07 15:13 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Junio C Hamano <gitster@pobox.com> writes:

> And this step is going in the right direction, but I am not sure if
> this made the function safe enough to be called repeatedly from the
> rebase machinery and we are ready to unleash this to the end users
> and tell them it is safe to use it.

Another possibility perhaps is that the function is safe to reuse
already even without this patch, of course ;-).

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 4/8] commit: move print_commit_summary() to libgit
  2017-11-07 14:32       ` Phillip Wood
@ 2017-11-08  1:04         ` Junio C Hamano
  0 siblings, 0 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-11-08  1:04 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> On 07/11/17 03:38, Junio C Hamano wrote:
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>> 
>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>>
>>> Move print_commit_summary() from builtin/commit.c to sequencer.c so it
>>> can be shared with other commands. The function is modified by
>>> changing the last argument to a flag so callers can specify whether
>>> they want to show the author date in addition to specifying if this is
>>> an initial commit.
>> 
>> A movement of a long function like this one really is easier if you
>> did not make any other unnecessary change in the same patch and then
>> made the change as a follow-up.
>
> I'm not sure what you mean by unnecessary, the original code called a
> file-local function author_date_is_interesting().

Yes, sorry, I missed the fact that author_date_is_interesting() is
not moving (and it is not moving for obvious reasons).


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v2 0/9] sequencer: dont't fork git commit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (8 preceding siblings ...)
  2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
@ 2017-11-10 11:09 ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 1/9] t3404: check intermediate squash messages Phillip Wood
                     ` (9 more replies)
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                   ` (2 subsequent siblings)
  12 siblings, 10 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Thanks for the feedback on v1 I've updated the patches as
suggested. See the comments on each patch for what has changed. I've
added a patch to the start of the series to test the commit messages
of intermediate squashes. I've added this as the RFC version of this
series did not create these correctly but the test suite passed.

Here's the summary from the previous version
These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.

Phillip Wood (9):
  t3404: check intermediate squash messages
  commit: move empty message checks to libgit
  Add a function to update HEAD after creating a commit
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: don't die in print_commit_summary()
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'

 builtin/commit.c              | 290 +++----------------------
 builtin/rebase--helper.c      |  13 +-
 builtin/revert.c              |  15 +-
 sequencer.c                   | 489 +++++++++++++++++++++++++++++++++++++++++-
 sequencer.h                   |  23 ++
 t/t3404-rebase-interactive.sh |   4 +
 6 files changed, 565 insertions(+), 269 deletions(-)

-- 
2.15.0


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v2 1/9] t3404: check intermediate squash messages
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 2/9] commit: move empty message checks to libgit Phillip Wood
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

When there is more than one squash/fixup command in a row check the
intermediate messages are correct.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 t/t3404-rebase-interactive.sh | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
 		git rebase -i $base &&
 	git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
 	test_cmp expect-squash-fixup actual-squash-fixup &&
+	git cat-file commit HEAD@{2} |
+		grep "^# This is a combination of 3 commits\."  &&
+	git cat-file commit HEAD@{3} |
+		grep "^# This is a combination of 2 commits\."  &&
 	git checkout to-be-rebased &&
 	git branch -D squash-fixup
 '
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 2/9] commit: move empty message checks to libgit
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 1/9] t3404: check intermediate squash messages Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 18:51     ` Ramsay Jones
  2017-11-10 11:09   ` [PATCH v2 3/9] Add a function to update HEAD after creating a commit Phillip Wood
                     ` (7 subsequent siblings)
  9 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move the functions that check for empty messages from bulitin/commit.c
to sequencer.c so they can be shared with other commands. The
functions are refactored to take an explicit cleanup mode and template
filename passed by the caller.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - prefix cleanup_mode enum and constants with commit_msg_

 builtin/commit.c | 99 +++++++++++---------------------------------------------
 sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
 sequencer.h      | 11 +++++++
 3 files changed, 91 insertions(+), 80 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 605ea8c0e9663726c64950dba07afe21516c9b26..dbc160c525e7a9249b7c7df2180495a4c7102296 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	struct strbuf sb = STRBUF_INIT;
 	const char *hook_arg1 = NULL;
 	const char *hook_arg2 = NULL;
-	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
 
 	/* This checks and barfs if author is badly specified */
@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct ident_split ci, ai;
 
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == CLEANUP_SCISSORS)
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 				wt_status_add_cut_line(s->fp);
 			status_printf_ln(s, GIT_COLOR_NORMAL,
 			    whence == FROM_MERGE
@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		}
 
 		fprintf(s->fp, "\n");
-		if (cleanup_mode == CLEANUP_ALL)
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\nwith '%c' will be ignored, and an empty"
 				  " message aborts the commit.\n"), comment_line_char);
-		else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+			 whence == FROM_COMMIT)
 			wt_status_add_cut_line(s->fp);
-		else /* CLEANUP_SPACE, that is. */
+		else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\n"
@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1214,15 +1151,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
 	if (argc == 0 && (also || (only && !amend && !allow_empty)))
 		die(_("No paths with --include/--only does not make sense."));
 	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = CLEANUP_NONE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = CLEANUP_SPACE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = CLEANUP_ALL;
+		cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 	else if (!strcmp(cleanup_arg, "scissors"))
-		cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else
 		die(_("Invalid cleanup mode %s"), cleanup_arg);
 
@@ -1749,17 +1688,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	}
 
 	if (verbose || /* Truncate the message just before the diff, if any. */
-	    cleanup_mode == CLEANUP_SCISSORS)
+	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 		strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-	if (cleanup_mode != CLEANUP_NONE)
-		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index 6d027b06c8d8dc69b14d05752637a65aa121ab24..23c250f16cfb7620215bb9c99b8e12d726dc9191 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -58,4 +58,15 @@ extern const char sign_off_header[];
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
 
+enum commit_msg_cleanup_mode {
+	COMMIT_MSG_CLEANUP_SPACE,
+	COMMIT_MSG_CLEANUP_NONE,
+	COMMIT_MSG_CLEANUP_SCISSORS,
+	COMMIT_MSG_CLEANUP_ALL
+};
+
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 3/9] Add a function to update HEAD after creating a commit
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 1/9] t3404: check intermediate squash messages Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 2/9] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 18:36     ` Junio C Hamano
  2017-11-10 11:09   ` [PATCH v2 4/9] commit: move post-rewrite code to libgit Phillip Wood
                     ` (6 subsequent siblings)
  9 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add update_head() based on the code that updates HEAD after committing
in builtin/commit.c that can be called by 'git commit' and other
commands.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - rename update_head() to update_head_with_reflog()

 builtin/commit.c | 20 ++------------------
 sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  4 ++++
 3 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index dbc160c525e7a9249b7c7df2180495a4c7102296..7c28144446644a665f7b7d8f4b6c0a5855aef01b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1591,13 +1591,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1720,25 +1718,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", &oid,
-				   current_head
-				   ? &current_head->object.oid : &null_oid,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+				    &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index 23c250f16cfb7620215bb9c99b8e12d726dc9191..fcd8e92531a3fb1f0bc1294925e87b3624ada909 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl;
+	int ret = 0;
+
+	if (action) {
+		strbuf_addstr(&sb, action);
+		strbuf_addstr(&sb, ": ");
+	}
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head,
+				   old_head ? &old_head->object.oid : &null_oid,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 82e57713a2940c5d65ccac013c3f42c55cc12baf..82035ced129d35ff8ba64710477531a9a713b631 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum commit_msg_cleanup_mode cleanup_mode);
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char* action, const struct strbuf *msg,
+			    struct strbuf *err);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 4/9] commit: move post-rewrite code to libgit
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (2 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 3/9] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 5/9] commit: move print_commit_summary() " Phillip Wood
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
be shared with other commands and add a new function
commit_post_rewrite() based on the code in builtin/commit.c that
encapsulates rewriting notes and running the post-rewrite hook. Once
the sequencer learns how to create commits without forking 'git
commit' these functions will be used when squashing commits.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - reword commit message to explain why the code is being moved

 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 7c28144446644a665f7b7d8f4b6c0a5855aef01b..3bc5dff2c00c9556e6c032381d5baf18246bf8dc 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1478,37 +1476,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1739,14 +1706,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index fcd8e92531a3fb1f0bc1294925e87b3624ada909..5529e5df1cbe9045b000ecbff6c28a0bee736ccc 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -21,6 +21,8 @@
 #include "log-tree.h"
 #include "wt-status.h"
 #include "hashmap.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so old_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 82035ced129d35ff8ba64710477531a9a713b631..6a69e4a7a42551cbc798b8eb93e447c0f50b234f 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,
 			    const struct object_id *new_head,
 			    const char* action, const struct strbuf *msg,
 			    struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 5/9] commit: move print_commit_summary() to libgit
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (3 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 4/9] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 6/9] sequencer: don't die in print_commit_summary() Phillip Wood
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move print_commit_summary() from builtin/commit.c to sequencer.c so it
can be shared with other commands. The function is modified by
changing the last argument to a flag so callers can specify whether
they want to show the author date in addition to specifying if this is
an initial commit.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - convert flags passed to print_commit_summary() to unsigned int

 builtin/commit.c | 128 ++++---------------------------------------------------
 sequencer.c      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 131 insertions(+), 119 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 3bc5dff2c00c9556e6c032381d5baf18246bf8dc..22d260197e84a828f984575cfa92789391531ca3 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1355,98 +1330,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1708,8 +1591,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		unsigned int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 
 	UNLEAK(err);
 	UNLEAK(sb);
diff --git a/sequencer.c b/sequencer.c
index 5529e5df1cbe9045b000ecbff6c28a0bee736ccc..03207db71821c0c3d1c442d3b01b11e3b010367e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -836,6 +836,123 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	if (!head)
+		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ? _(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6a69e4a7a42551cbc798b8eb93e447c0f50b234f..8e0dbe59bbc1ccd3847454faf8bd09d5ca1ff93a 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,
 			    struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 6/9] sequencer: don't die in print_commit_summary()
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (4 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 5/9] commit: move print_commit_summary() " Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 7/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Return an error rather than dying so that the sequencer can exit
cleanly once it starts committing without forking 'git commit'

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/commit.c |  3 ++-
 sequencer.c      | 17 +++++++++++------
 sequencer.h      |  4 ++--
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 22d260197e84a828f984575cfa92789391531ca3..e924d8a14c8532a78d072420aeb662ce616181a4 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1598,7 +1598,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			flags |= SUMMARY_INITIAL_COMMIT;
 		if (author_date_is_interesting())
 			flags |= SUMMARY_SHOW_AUTHOR_DATE;
-		print_commit_summary(prefix, &oid, flags);
+		if (print_commit_summary(prefix, &oid, flags))
+			exit(128);
 	}
 
 	UNLEAK(err);
diff --git a/sequencer.c b/sequencer.c
index 03207db71821c0c3d1c442d3b01b11e3b010367e..61bb75352202cf896ffec41e972f366e94d569c1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -877,8 +877,8 @@ static const char *implicit_ident_advice(void)
 
 }
 
-void print_commit_summary(const char *prefix, const struct object_id *oid,
-			  unsigned int flags)
+int print_commit_summary(const char *prefix, const struct object_id *oid,
+			 unsigned int flags)
 {
 	struct rev_info rev;
 	struct commit *commit;
@@ -887,12 +887,13 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	struct pretty_print_context pctx = {0};
 	struct strbuf author_ident = STRBUF_INIT;
 	struct strbuf committer_ident = STRBUF_INIT;
+	int ret = 0;
 
 	commit = lookup_commit(oid);
 	if (!commit)
-		die(_("couldn't look up newly created commit"));
+		return error(_("couldn't look up newly created commit"));
 	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
+		return error(_("could not parse newly created commit"));
 
 	strbuf_addstr(&format, "format:%h] %s");
 
@@ -936,8 +937,10 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	diff_setup_done(&rev.diffopt);
 
 	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!head) {
+		ret = error_errno(_("unable to resolve HEAD after creating commit"));
+		goto out;
+	}
 	if (!strcmp(head, "HEAD"))
 		head = _("detached HEAD");
 	else
@@ -950,7 +953,9 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 		log_tree_commit(&rev, commit);
 	}
 
+out:
 	strbuf_release(&format);
+	return ret;
 }
 
 static int is_original_commit_empty(struct commit *commit)
diff --git a/sequencer.h b/sequencer.h
index 8e0dbe59bbc1ccd3847454faf8bd09d5ca1ff93a..e4040cac08cb69c9b91db3fe77ae392c8d6d0f50 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -78,6 +78,6 @@ void commit_post_rewrite(const struct commit *current_head,
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
-void print_commit_summary(const char *prefix, const struct object_id *oid,
-			  unsigned int flags);
+int print_commit_summary(const char *prefix, const struct object_id *oid,
+			 unsigned int flags);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 7/9] sequencer: simplify adding Signed-off-by: trailer
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (5 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 6/9] sequencer: don't die in print_commit_summary() Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 8/9] sequencer: load commit related config Phillip Wood
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and specifying '--signoff'
when running 'git commit'. This means that if there are conflicts when
merging with a strategy other than 'recursive' the Signed-off-by:
trailer will be added if the user commits the resolution themselves
without passing '--signoff' to 'git commit'. It also simplifies the
in-process commit that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 61bb75352202cf896ffec41e972f366e94d569c1..497764ea5db3a3ba2802387316c1d5024d47d7eb 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1350,6 +1345,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 8/9] sequencer: load commit related config
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (6 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 7/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 11:09   ` [PATCH v2 9/9] sequencer: try to commit without forking 'git commit' Phillip Wood
  2017-11-10 19:21   ` [PATCH v2 0/9] sequencer: dont't fork git commit Junio C Hamano
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup and gpg signing of commits in
preparation for committing without forking 'git commit'.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - renamed git_revert_config() to common_config()
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 builtin/rebase--helper.c | 13 ++++++++++++-
 builtin/revert.c         | 15 +++++++++++++--
 sequencer.c              | 34 ++++++++++++++++++++++++++++++++++
 sequencer.h              |  1 +
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
+static int git_rebase_helper_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_rebase_helper_config, NULL);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
+static int common_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 497764ea5db3a3ba2802387316c1d5024d47d7eb..0fd98391cb7dfcff6976271f44a56e897593e2c0 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static enum commit_msg_cleanup_mode default_msg_cleanup =
+						COMMIT_MSG_CLEANUP_NONE;
+static char *default_gpg_sign;
+
+int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "commit.cleanup")) {
+		int status;
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	return git_gpg_config(k, v, NULL);
+}
+
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
diff --git a/sequencer.h b/sequencer.h
index e4040cac08cb69c9b91db3fe77ae392c8d6d0f50..27f34be4003589790be686c901a19059f4642c22 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -57,6 +57,7 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
+int git_sequencer_config(const char *k, const char *v, void *cb);
 
 enum commit_msg_cleanup_mode {
 	COMMIT_MSG_CLEANUP_SPACE,
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v2 9/9] sequencer: try to commit without forking 'git commit'
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (7 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 8/9] sequencer: load commit related config Phillip Wood
@ 2017-11-10 11:09   ` Phillip Wood
  2017-11-10 19:21   ` [PATCH v2 0/9] sequencer: dont't fork git commit Junio C Hamano
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 11:09 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 27% (from 282ms to 204ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
my computer running linux. Some of greater saving for rebase is
because it no longer wastes time creating the commit summary just to
throw it away.

The code to create the commit is based on builtin/commit.c. It is
simplified as it doesn't have to deal with merges and modified so that
it does not die but returns an error to make sure the sequencer exits
cleanly, as it would when forking 'git commit'

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD is created unnecessarily. This could be
eliminated in future. I hacked up a version that does not write these
files and just passed an strbuf (with the wrong message for fixup and
squash commands) to do_commit() but I couldn't measure any significant
time difference when running cherry-pick or rebase. I think
eliminating the writes properly for rebase would require a bit of
effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - added comments to explain return value of try_to_commit()
     - removed unnecessary NULL tests before calling free()
     - style cleanups
     - corrected commit message
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 176 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 0fd98391cb7dfcff6976271f44a56e897593e2c0..1f65e82696043fd9fb5ebb9f2b78445aa5a9599e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char* message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -987,6 +999,160 @@ int print_commit_summary(const char *prefix, const struct object_id *oid,
 	return ret;
 }
 
+static int parse_head(struct commit **head)
+{
+	struct commit *current_head;
+	struct object_id oid;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_reference(&oid);
+		if (!current_head)
+			return error(_("could not parse HEAD"));
+		if (oidcmp(&oid, &current_head->object.oid)) {
+			warning(_("HEAD %s is not a commit!"),
+				oid_to_hex(&oid));
+		}
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+	*head = current_head;
+
+	return 0;
+}
+
+/*
+ * Try to commit without forking 'git commit'. In some cases we need
+ * to run 'git commit' to display an error message
+ *
+ * Returns:
+ *  -1 - error unable to commit
+ *   0 - success
+ *   1 - run 'git commit'
+ */
+static int try_to_commit(struct strbuf *msg, const char *author,
+			 struct replay_opts *opts, unsigned int flags,
+			 struct object_id *oid)
+{
+	struct object_id tree;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	char *amend_author = NULL;
+	const char *gpg_sign;
+	enum commit_msg_cleanup_mode cleanup;
+	int res = 0;
+
+	if (parse_head(&current_head))
+		return -1;
+
+	if (flags & AMEND_MSG) {
+		const char *exclude_gpgsig[] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		if (!msg) {
+			const char *orig_message = NULL;
+
+			find_commit_subject(message, &orig_message);
+			msg = &amend_msg;
+			strbuf_addstr(msg, orig_message);
+		}
+		author = amend_author = get_author(message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
+					  default_msg_cleanup;
+	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	gpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				 oid->hash, author, gpg_sign, extra)) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	if (update_head_with_reflog(current_head, oid,
+				    getenv("GIT_REFLOG_ACTION"), msg, &err)){
+		res = error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, oid);
+
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *msg_file, const char* author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res = 1;
+
+	if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
+		struct object_id oid;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
+			return error_errno(_("unable to read commit message "
+					     "from '%s'"),
+					   msg_file);
+
+		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
+				    &oid);
+		strbuf_release(&sb);
+		if (!res) {
+			unlink(git_path_cherry_pick_head());
+			unlink(git_path_merge_msg());
+			if (!is_rebase_i(opts))
+				res = print_commit_summary(NULL, &oid,
+						SUMMARY_SHOW_AUTHOR_DATE);
+			return res;
+		}
+	}
+	if (res == 1)
+		return run_git_commit(msg_file, opts, flags);
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1238,6 +1404,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1354,6 +1521,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup (command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1439,9 +1608,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1450,6 +1623,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-07 15:13       ` Junio C Hamano
@ 2017-11-10 14:53         ` Phillip Wood
  2017-11-10 18:05           ` Junio C Hamano
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-10 14:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 07/11/17 15:13, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> And this step is going in the right direction, but I am not sure if
>> this made the function safe enough to be called repeatedly from the
>> rebase machinery and we are ready to unleash this to the end users
>> and tell them it is safe to use it.
> 
> Another possibility perhaps is that the function is safe to reuse
> already even without this patch, of course ;-).
> 
Hmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the
sequencer dies in print_commit_summary() (which can only happen when
cherry-picking or reverting) then neither the todo list or the abort
safety file are updated to reflect the commit that was just made.

As I understand it print_commit_summary() dies because: (i) it cannot
resolve HEAD either because some other process is updating it (which is
bad news in the middle of a cherry-pick); (ii) because something went
wrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some
objects. In all those cases dying will leave the sequencer in a sane
state for aborting - 'git cherry-pick --abort' will rewind HEAD to the
last successful commit before there was a problem with HEAD or the
object database. If the user somehow fixes the problem and runs 'git
cherry-pick --continue' then the sequencer will try and pick the same
commit again which may or may not be what the user wants depending on
what caused print_commit_summary() to die.



^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-10 14:53         ` Phillip Wood
@ 2017-11-10 18:05           ` Junio C Hamano
  2017-11-13 11:11             ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-10 18:05 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> On 07/11/17 15:13, Junio C Hamano wrote:
> ...
>> Another possibility perhaps is that the function is safe to reuse
>> already even without this patch, of course ;-).
>> 
> Hmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the
> sequencer dies in print_commit_summary() (which can only happen when
> cherry-picking or reverting) then neither the todo list or the abort
> safety file are updated to reflect the commit that was just made.
> 
> As I understand it print_commit_summary() dies because: (i) it cannot
> resolve HEAD either because some other process is updating it (which is
> bad news in the middle of a cherry-pick); (ii) because something went
> wrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some
> objects. In all those cases dying will leave the sequencer in a sane
> state for aborting - 'git cherry-pick --abort' will rewind HEAD to the
> last successful commit before there was a problem with HEAD or the
> object database. If the user somehow fixes the problem and runs 'git
> cherry-pick --continue' then the sequencer will try and pick the same
> commit again which may or may not be what the user wants depending on
> what caused print_commit_summary() to die.

The above is all good analysis---thanks for your diligence.  Perhaps
some if not all of it can go to the log message?


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 3/9] Add a function to update HEAD after creating a commit
  2017-11-10 11:09   ` [PATCH v2 3/9] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-10 18:36     ` Junio C Hamano
  2017-11-13 11:25       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-10 18:36 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Add update_head() based on the code that updates HEAD after committing
> in builtin/commit.c that can be called by 'git commit' and other
> commands.
>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>
> Notes:
>     changes since v1:
>      - rename update_head() to update_head_with_reflog()

The above proposed log message still calls it with the old name
though.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 2/9] commit: move empty message checks to libgit
  2017-11-10 11:09   ` [PATCH v2 2/9] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-10 18:51     ` Ramsay Jones
  2017-11-13 11:08       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Ramsay Jones @ 2017-11-10 18:51 UTC (permalink / raw)
  To: Phillip Wood, Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano



On 10/11/17 11:09, Phillip Wood wrote:
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Move the functions that check for empty messages from bulitin/commit.c
> to sequencer.c so they can be shared with other commands. The
> functions are refactored to take an explicit cleanup mode and template
> filename passed by the caller.
> 
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
> 
> Notes:
>     changes since v1:
>      - prefix cleanup_mode enum and constants with commit_msg_
> 
>  builtin/commit.c | 99 +++++++++++---------------------------------------------
>  sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
>  sequencer.h      | 11 +++++++
>  3 files changed, 91 insertions(+), 80 deletions(-)
> 

Just an idle thought - why are these functions moving to
sequencer.[ch] rather than commit[.ch]?

Similar comments for other patches in the series which moves
code from builtin/commit.c to sequencer.[ch].

ATB,
Ramsay Jones



^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 0/9] sequencer: dont't fork git commit
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
                     ` (8 preceding siblings ...)
  2017-11-10 11:09   ` [PATCH v2 9/9] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-11-10 19:21   ` Junio C Hamano
  2017-11-13 11:24     ` Phillip Wood
  9 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-10 19:21 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> Here's the summary from the previous version
> These patches teach the sequencer to create commits without forking
> git commit when the commit message does not need to be edited. This
> speeds up cherry picking 10 commits by 26% and picking 10 commits with
> rebase --continue by 44%. The first few patches move bits of
> builtin/commit.c to sequencer.c. The last two patches actually
> implement creating commits in sequencer.c.

Thanks.  The changes since the initial iteration seems quite small
and I didn't find much objectionable.

Here are some style fixes I needed to add on top to make the output
of "diff master HEAD" checkpatch.pl-clean.  I think 3/9 and 9/9 are
the culprits.

diff --git a/sequencer.c b/sequencer.c
index 1f65e82696..a989588ee5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -592,7 +592,7 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
-static char *get_author(const char* message)
+static char *get_author(const char *message)
 {
 	size_t len;
 	const char *a;
@@ -1104,7 +1104,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 	}
 
 	if (update_head_with_reflog(current_head, oid,
-				    getenv("GIT_REFLOG_ACTION"), msg, &err)){
+				    getenv("GIT_REFLOG_ACTION"), msg, &err)) {
 		res = error("%s", err.buf);
 		goto out;
 	}
@@ -1121,7 +1121,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 	return res;
 }
 
-static int do_commit(const char *msg_file, const char* author,
+static int do_commit(const char *msg_file, const char *author,
 		     struct replay_opts *opts, unsigned int flags)
 {
 	int res = 1;
@@ -1521,7 +1521,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
-		if (!is_fixup (command))
+		if (!is_fixup(command))
 			author = get_author(msg.message);
 	}
 
diff --git a/sequencer.h b/sequencer.h
index 27f34be400..e0be354301 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -72,7 +72,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum commit_msg_cleanup_mode cleanup_mode);
 int update_head_with_reflog(const struct commit *old_head,
 			    const struct object_id *new_head,
-			    const char* action, const struct strbuf *msg,
+			    const char *action, const struct strbuf *msg,
 			    struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);



^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 2/9] commit: move empty message checks to libgit
  2017-11-10 18:51     ` Ramsay Jones
@ 2017-11-13 11:08       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-13 11:08 UTC (permalink / raw)
  To: Ramsay Jones, Phillip Wood, Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano

On 10/11/17 18:51, Ramsay Jones wrote:
> 
> 
> On 10/11/17 11:09, Phillip Wood wrote:
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Move the functions that check for empty messages from bulitin/commit.c
>> to sequencer.c so they can be shared with other commands. The
>> functions are refactored to take an explicit cleanup mode and template
>> filename passed by the caller.
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>> ---
>>
>> Notes:
>>     changes since v1:
>>      - prefix cleanup_mode enum and constants with commit_msg_
>>
>>  builtin/commit.c | 99 +++++++++++---------------------------------------------
>>  sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
>>  sequencer.h      | 11 +++++++
>>  3 files changed, 91 insertions(+), 80 deletions(-)
>>
> 
> Just an idle thought - why are these functions moving to
> sequencer.[ch] rather than commit[.ch]?
> 
> Similar comments for other patches in the series which moves
> code from builtin/commit.c to sequencer.[ch].

I did think about putting them in commit.[ch] but I felt they where
higher level than the existing functions in those files and as they're
used by the sequencer code I just put them in there in the end.

Best Wishes

Phillip

> ATB,
> Ramsay Jones
> 
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v1 5/8] sequencer: don't die in print_commit_summary()
  2017-11-10 18:05           ` Junio C Hamano
@ 2017-11-13 11:11             ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-13 11:11 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 10/11/17 18:05, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> On 07/11/17 15:13, Junio C Hamano wrote:
>> ...
>>> Another possibility perhaps is that the function is safe to reuse
>>> already even without this patch, of course ;-).
>>>
>> Hmm, maybe it is. Looking at pick_commits() and do_pick_commit() if the
>> sequencer dies in print_commit_summary() (which can only happen when
>> cherry-picking or reverting) then neither the todo list or the abort
>> safety file are updated to reflect the commit that was just made.
>>
>> As I understand it print_commit_summary() dies because: (i) it cannot
>> resolve HEAD either because some other process is updating it (which is
>> bad news in the middle of a cherry-pick); (ii) because something went
>> wrong HEAD is corrupt; or (iii) log_tree_commit() cannot read some
>> objects. In all those cases dying will leave the sequencer in a sane
>> state for aborting - 'git cherry-pick --abort' will rewind HEAD to the
>> last successful commit before there was a problem with HEAD or the
>> object database. If the user somehow fixes the problem and runs 'git
>> cherry-pick --continue' then the sequencer will try and pick the same
>> commit again which may or may not be what the user wants depending on
>> what caused print_commit_summary() to die.
> 
> The above is all good analysis---thanks for your diligence.  Perhaps
> some if not all of it can go to the log message?
> 
Thanks, that's a good idea. I see the above as a reason to drop this
commit as returning an error will try and update the abort safety file
which we don't want to do if there is a problem with HEAD so I'll add it
to the previous commit to explain why it is okay to die.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 0/9] sequencer: dont't fork git commit
  2017-11-10 19:21   ` [PATCH v2 0/9] sequencer: dont't fork git commit Junio C Hamano
@ 2017-11-13 11:24     ` Phillip Wood
  2017-11-14  1:15       ` Junio C Hamano
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-13 11:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 10/11/17 19:21, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> Here's the summary from the previous version
>> These patches teach the sequencer to create commits without forking
>> git commit when the commit message does not need to be edited. This
>> speeds up cherry picking 10 commits by 26% and picking 10 commits with
>> rebase --continue by 44%. The first few patches move bits of
>> builtin/commit.c to sequencer.c. The last two patches actually
>> implement creating commits in sequencer.c.
> 
> Thanks.  The changes since the initial iteration seems quite small
> and I didn't find much objectionable.
> 
> Here are some style fixes I needed to add on top to make the output
> of "diff master HEAD" checkpatch.pl-clean.  I think 3/9 and 9/9 are
> the culprits.

Thanks, I'll update the patches.

Are you happy with the '--signoff' is handled (I didn't modify my
changes in the last iteration as you were still thinking about it)?

In the last patch commit_tree_extended() can die in write_loose_object()
which means that the current command is not rescheduled as it should be
when rebasing. write_loose_object() already an error if there is a
problem opening the file, but dies if there is a problem creating the
file contents or writing that contents.

Best Wishes

Phillip

> diff --git a/sequencer.c b/sequencer.c
> index 1f65e82696..a989588ee5 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -592,7 +592,7 @@ static int read_env_script(struct argv_array *env)
>  	return 0;
>  }
>  
> -static char *get_author(const char* message)
> +static char *get_author(const char *message)
>  {
>  	size_t len;
>  	const char *a;
> @@ -1104,7 +1104,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
>  	}
>  
>  	if (update_head_with_reflog(current_head, oid,
> -				    getenv("GIT_REFLOG_ACTION"), msg, &err)){
> +				    getenv("GIT_REFLOG_ACTION"), msg, &err)) {
>  		res = error("%s", err.buf);
>  		goto out;
>  	}
> @@ -1121,7 +1121,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
>  	return res;
>  }
>  
> -static int do_commit(const char *msg_file, const char* author,
> +static int do_commit(const char *msg_file, const char *author,
>  		     struct replay_opts *opts, unsigned int flags)
>  {
>  	int res = 1;
> @@ -1521,7 +1521,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
>  			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
>  			strbuf_addstr(&msgbuf, ")\n");
>  		}
> -		if (!is_fixup (command))
> +		if (!is_fixup(command))
>  			author = get_author(msg.message);
>  	}
>  
> diff --git a/sequencer.h b/sequencer.h
> index 27f34be400..e0be354301 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -72,7 +72,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
>  		       enum commit_msg_cleanup_mode cleanup_mode);
>  int update_head_with_reflog(const struct commit *old_head,
>  			    const struct object_id *new_head,
> -			    const char* action, const struct strbuf *msg,
> +			    const char *action, const struct strbuf *msg,
>  			    struct strbuf *err);
>  void commit_post_rewrite(const struct commit *current_head,
>  			 const struct object_id *new_head);
> 
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 3/9] Add a function to update HEAD after creating a commit
  2017-11-10 18:36     ` Junio C Hamano
@ 2017-11-13 11:25       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-13 11:25 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 10/11/17 18:36, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Add update_head() based on the code that updates HEAD after committing
>> in builtin/commit.c that can be called by 'git commit' and other
>> commands.
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>> ---
>>
>> Notes:
>>     changes since v1:
>>      - rename update_head() to update_head_with_reflog()
> 
> The above proposed log message still calls it with the old name
> though.
> 
Oops, sorry I'll fix that

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v2 0/9] sequencer: dont't fork git commit
  2017-11-13 11:24     ` Phillip Wood
@ 2017-11-14  1:15       ` Junio C Hamano
  0 siblings, 0 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-11-14  1:15 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> Are you happy with the '--signoff' is handled (I didn't modify my
> changes in the last iteration as you were still thinking about it)?

I am not, but I am not unhappy, either.

As I understand from your responses, it seems that the existing code
had an untriggerable mess and the patch replaced it with another
that is also untriggerable, and if that is the case, well, we do not
have an immediate need to clean it up either way, so... ;-)


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v3 0/8] sequencer: don't fork git commit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (9 preceding siblings ...)
  2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
@ 2017-11-17 11:34 ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 1/8] t3404: check intermediate squash messages Phillip Wood
                     ` (8 more replies)
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
  12 siblings, 9 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

I've updated these based on the feedback for v2. I've dropped the
patch that stopped print_commit_summary() from dying as I think it is
better to die than return an error (see the commit message of the
patch that adds print_commit_summary() for the reasoning). Apart from
that they're minor changes - style fixes and a reworded a commit message.

Here's the original summary:
These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.


Phillip Wood (8):
  t3404: check intermediate squash messages
  commit: move empty message checks to libgit
  Add a function to update HEAD after creating a commit
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'

 builtin/commit.c              | 289 +++----------------------
 builtin/rebase--helper.c      |  13 +-
 builtin/revert.c              |  15 +-
 sequencer.c                   | 486 +++++++++++++++++++++++++++++++++++++++++-
 sequencer.h                   |  23 ++
 t/t3404-rebase-interactive.sh |   4 +
 6 files changed, 561 insertions(+), 269 deletions(-)

-- 
2.15.0


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v3 1/8] t3404: check intermediate squash messages
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 2/8] commit: move empty message checks to libgit Phillip Wood
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

When there is more than one squash/fixup command in a row check the
intermediate messages are correct.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 t/t3404-rebase-interactive.sh | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
 		git rebase -i $base &&
 	git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
 	test_cmp expect-squash-fixup actual-squash-fixup &&
+	git cat-file commit HEAD@{2} |
+		grep "^# This is a combination of 3 commits\."  &&
+	git cat-file commit HEAD@{3} |
+		grep "^# This is a combination of 2 commits\."  &&
 	git checkout to-be-rebased &&
 	git branch -D squash-fixup
 '
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 2/8] commit: move empty message checks to libgit
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 1/8] t3404: check intermediate squash messages Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 3/8] Add a function to update HEAD after creating a commit Phillip Wood
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move the functions that check for empty messages from bulitin/commit.c
to sequencer.c so they can be shared with other commands. The
functions are refactored to take an explicit cleanup mode and template
filename passed by the caller.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - prefix cleanup_mode enum and constants with commit_msg_

 builtin/commit.c | 99 +++++++++++---------------------------------------------
 sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
 sequencer.h      | 11 +++++++
 3 files changed, 91 insertions(+), 80 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 8a877014145435516930c787dec37b8c4ac3da90..d958c2eb2adc9a29dab29340ce9b56daea41fecd 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	struct strbuf sb = STRBUF_INIT;
 	const char *hook_arg1 = NULL;
 	const char *hook_arg2 = NULL;
-	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
 
 	/* This checks and barfs if author is badly specified */
@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct ident_split ci, ai;
 
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == CLEANUP_SCISSORS)
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 				wt_status_add_cut_line(s->fp);
 			status_printf_ln(s, GIT_COLOR_NORMAL,
 			    whence == FROM_MERGE
@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		}
 
 		fprintf(s->fp, "\n");
-		if (cleanup_mode == CLEANUP_ALL)
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\nwith '%c' will be ignored, and an empty"
 				  " message aborts the commit.\n"), comment_line_char);
-		else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+			 whence == FROM_COMMIT)
 			wt_status_add_cut_line(s->fp);
-		else /* CLEANUP_SPACE, that is. */
+		else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\n"
@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
 	if (argc == 0 && (also || (only && !amend && !allow_empty)))
 		die(_("No paths with --include/--only does not make sense."));
 	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = CLEANUP_NONE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = CLEANUP_SPACE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = CLEANUP_ALL;
+		cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 	else if (!strcmp(cleanup_arg, "scissors"))
-		cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else
 		die(_("Invalid cleanup mode %s"), cleanup_arg);
 
@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	}
 
 	if (verbose || /* Truncate the message just before the diff, if any. */
-	    cleanup_mode == CLEANUP_SCISSORS)
+	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 		strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-	if (cleanup_mode != CLEANUP_NONE)
-		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index 19dd575ed9b3b280a3fdabc9121a2e193d6984db..36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -58,4 +58,15 @@ extern const char sign_off_header[];
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
 
+enum commit_msg_cleanup_mode {
+	COMMIT_MSG_CLEANUP_SPACE,
+	COMMIT_MSG_CLEANUP_NONE,
+	COMMIT_MSG_CLEANUP_SCISSORS,
+	COMMIT_MSG_CLEANUP_ALL
+};
+
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 3/8] Add a function to update HEAD after creating a commit
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 1/8] t3404: check intermediate squash messages Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 2/8] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 4/8] commit: move post-rewrite code to libgit Phillip Wood
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add update_head_with_reflog() based on the code that updates HEAD
after committing in builtin/commit.c that can be called by 'git
commit' and other commands.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - updated commit message to reflect the change in function name
     - style fixes
    
    changes since v1:
     - rename update_head() to update_head_with_reflog()

 builtin/commit.c | 20 ++------------------
 sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  4 ++++
 3 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index d958c2eb2adc9a29dab29340ce9b56daea41fecd..eb144556bf37b7bf357bd976b94305171b4fd159 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", &oid,
-				   current_head
-				   ? &current_head->object.oid : &null_oid,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+				    &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index 36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70..ef262980c5255d90ee023c0b29c6c1c628b3c7d2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl;
+	int ret = 0;
+
+	if (action) {
+		strbuf_addstr(&sb, action);
+		strbuf_addstr(&sb, ": ");
+	}
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head,
+				   old_head ? &old_head->object.oid : &null_oid,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 82e57713a2940c5d65ccac013c3f42c55cc12baf..81a2098e900f0aca30e45ed7f19ae4bf3ce682f0 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum commit_msg_cleanup_mode cleanup_mode);
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 4/8] commit: move post-rewrite code to libgit
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (2 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 3/8] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 5/8] commit: move print_commit_summary() " Phillip Wood
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
be shared with other commands and add a new function
commit_post_rewrite() based on the code in builtin/commit.c that
encapsulates rewriting notes and running the post-rewrite hook. Once
the sequencer learns how to create commits without forking 'git
commit' these functions will be used when squashing commits.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - reword commit message to explain why the code is being moved

 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index eb144556bf37b7bf357bd976b94305171b4fd159..d251cfcebad3476c365492d83803e7821fdfdf2b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index ef262980c5255d90ee023c0b29c6c1c628b3c7d2..6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -21,6 +21,8 @@
 #include "log-tree.h"
 #include "wt-status.h"
 #include "hashmap.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so old_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 81a2098e900f0aca30e45ed7f19ae4bf3ce682f0..ec13b679c40dc4012a84a761ea856d6e75953490 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,
 			    const struct object_id *new_head,
 			    const char *action, const struct strbuf *msg,
 			    struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 5/8] commit: move print_commit_summary() to libgit
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (3 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 4/8] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move print_commit_summary() from builtin/commit.c to sequencer.c so it
can be shared with other commands. The function is modified by
changing the last argument to a flag so callers can specify whether
they want to show the author date in addition to specifying if this is
an initial commit.

If the sequencer dies in print_commit_summary() (which can only happen
when cherry-picking or reverting) then neither the todo list nor the
abort safety file are updated to reflect the commit that was just
made. print_commit_summary() can die if:

 - HEAD cannot be resolved either because some other process is
   updating it (which is bad news in the middle of a cherry-pick) or
   because it is corrupt.

 - log_tree_commit() cannot read some objects.

In all those cases dying will leave the sequencer in a sane state for
aborting; 'git cherry-pick --abort' will rewind HEAD to the last
successful commit before there was a problem with HEAD or the object
database. If the user somehow fixes the problem and runs 'git
cherry-pick --continue' then the sequencer will try and pick the same
commit again which may or may not be what the user wants depending on
what caused print_commit_summary() to die. If print_commit_summary()
returned an error instead then update_abort_safety_file() would try to
resolve HEAD which may or may not be successful. If it is successful
then running 'git rebase --abort' would not rewind HEAD to the last
successful commit which is not what we want.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - expanded commit message to explain why it is ok to die in
       print_commit_summary() and dropped the next patch which made it
       return an error instead.
     - style fixes.
    
    changes since v1:
     - convert flags passed to print_commit_summary() to unsigned int

 builtin/commit.c | 128 ++++---------------------------------------------------
 sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 133 insertions(+), 119 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index d251cfcebad3476c365492d83803e7821fdfdf2b..2043479d37873671d43124dc0cb509d6d9247baa 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		unsigned int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 
 	UNLEAK(err);
 	UNLEAK(sb);
diff --git a/sequencer.c b/sequencer.c
index 6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914..a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -836,6 +836,125 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	if (!head)
+		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ?
+						_(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index ec13b679c40dc4012a84a761ea856d6e75953490..4f616c61a3f3869daf9f427b978c308d6094a978 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,
 			    struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 6/8] sequencer: simplify adding Signed-off-by: trailer
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (4 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 5/8] commit: move print_commit_summary() " Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 7/8] sequencer: load commit related config Phillip Wood
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and specifying '--signoff'
when running 'git commit'. This means that if there are conflicts when
merging with a strategy other than 'recursive' the Signed-off-by:
trailer will be added if the user commits the resolution themselves
without passing '--signoff' to 'git commit'. It also simplifies the
in-process commit that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f..7400df5522037583108534755af6f542117667c2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 7/8] sequencer: load commit related config
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (5 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-17 11:34   ` [PATCH v3 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
  2017-11-18  3:41   ` [PATCH v3 0/8] sequencer: don't fork git commit Junio C Hamano
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup and gpg signing of commits in
preparation for committing without forking 'git commit'.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - renamed git_revert_config() to common_config()
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 builtin/rebase--helper.c | 13 ++++++++++++-
 builtin/revert.c         | 15 +++++++++++++--
 sequencer.c              | 34 ++++++++++++++++++++++++++++++++++
 sequencer.h              |  1 +
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
+static int git_rebase_helper_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_rebase_helper_config, NULL);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
+static int common_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 7400df5522037583108534755af6f542117667c2..2c487f9feb83806e5db54a8675c9eb55cc62a5ec 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static enum commit_msg_cleanup_mode default_msg_cleanup =
+						COMMIT_MSG_CLEANUP_NONE;
+static char *default_gpg_sign;
+
+int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "commit.cleanup")) {
+		int status;
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	return git_gpg_config(k, v, NULL);
+}
+
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
diff --git a/sequencer.h b/sequencer.h
index 4f616c61a3f3869daf9f427b978c308d6094a978..77cb174b2aaf3972ebb9e6ec379252be96dedd3d 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -57,6 +57,7 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
+int git_sequencer_config(const char *k, const char *v, void *cb);
 
 enum commit_msg_cleanup_mode {
 	COMMIT_MSG_CLEANUP_SPACE,
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v3 8/8] sequencer: try to commit without forking 'git commit'
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (6 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 7/8] sequencer: load commit related config Phillip Wood
@ 2017-11-17 11:34   ` Phillip Wood
  2017-11-18  3:41   ` [PATCH v3 0/8] sequencer: don't fork git commit Junio C Hamano
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-17 11:34 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 27% (from 282ms to 204ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
my computer running linux. Some of greater saving for rebase is
because it no longer wastes time creating the commit summary just to
throw it away.

The code to create the commit is based on builtin/commit.c. It is
simplified as it doesn't have to deal with merges and modified so that
it does not die but returns an error to make sure the sequencer exits
cleanly, as it would when forking 'git commit'

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD is created unnecessarily. This could be
eliminated in future. I hacked up a version that does not write these
files and just passed an strbuf (with the wrong message for fixup and
squash commands) to do_commit() but I couldn't measure any significant
time difference when running cherry-pick or rebase. I think
eliminating the writes properly for rebase would require a bit of
effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - style fixes
    
    changes since v1:
     - added comments to explain return value of try_to_commit()
     - removed unnecessary NULL tests before calling free()
     - style cleanups
     - corrected commit message
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 176 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 2c487f9feb83806e5db54a8675c9eb55cc62a5ec..8362d8fe9f19c61e6339b7a325771344c7a136a8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char *message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	strbuf_release(&format);
 }
 
+static int parse_head(struct commit **head)
+{
+	struct commit *current_head;
+	struct object_id oid;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_reference(&oid);
+		if (!current_head)
+			return error(_("could not parse HEAD"));
+		if (oidcmp(&oid, &current_head->object.oid)) {
+			warning(_("HEAD %s is not a commit!"),
+				oid_to_hex(&oid));
+		}
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+	*head = current_head;
+
+	return 0;
+}
+
+/*
+ * Try to commit without forking 'git commit'. In some cases we need
+ * to run 'git commit' to display an error message
+ *
+ * Returns:
+ *  -1 - error unable to commit
+ *   0 - success
+ *   1 - run 'git commit'
+ */
+static int try_to_commit(struct strbuf *msg, const char *author,
+			 struct replay_opts *opts, unsigned int flags,
+			 struct object_id *oid)
+{
+	struct object_id tree;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	char *amend_author = NULL;
+	const char *gpg_sign;
+	enum commit_msg_cleanup_mode cleanup;
+	int res = 0;
+
+	if (parse_head(&current_head))
+		return -1;
+
+	if (flags & AMEND_MSG) {
+		const char *exclude_gpgsig[] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		if (!msg) {
+			const char *orig_message = NULL;
+
+			find_commit_subject(message, &orig_message);
+			msg = &amend_msg;
+			strbuf_addstr(msg, orig_message);
+		}
+		author = amend_author = get_author(message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
+					  default_msg_cleanup;
+	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	gpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				 oid->hash, author, gpg_sign, extra)) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	if (update_head_with_reflog(current_head, oid,
+				    getenv("GIT_REFLOG_ACTION"), msg, &err)) {
+		res = error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, oid);
+
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *msg_file, const char *author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res = 1;
+
+	if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
+		struct object_id oid;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
+			return error_errno(_("unable to read commit message "
+					     "from '%s'"),
+					   msg_file);
+
+		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
+				    &oid);
+		strbuf_release(&sb);
+		if (!res) {
+			unlink(git_path_cherry_pick_head());
+			unlink(git_path_merge_msg());
+			if (!is_rebase_i(opts))
+				res = print_commit_summary(NULL, &oid,
+						SUMMARY_SHOW_AUTHOR_DATE);
+			return res;
+		}
+	}
+	if (res == 1)
+		return run_git_commit(msg_file, opts, flags);
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1235,6 +1401,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1351,6 +1518,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup(command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1436,9 +1605,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1447,6 +1620,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v3 0/8] sequencer: don't fork git commit
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
                     ` (7 preceding siblings ...)
  2017-11-17 11:34   ` [PATCH v3 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-11-18  3:41   ` Junio C Hamano
  2017-11-18  3:57     ` Junio C Hamano
  8 siblings, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-18  3:41 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> I've updated these based on the feedback for v2. I've dropped the
> patch that stopped print_commit_summary() from dying as I think it is
> better to die than return an error (see the commit message of the
> patch that adds print_commit_summary() for the reasoning). Apart from
> that they're minor changes - style fixes and a reworded a commit message.

Thanks for further polishing this topic; I found nothing in the
update that was questionable.  Will replace.

With this, perhaps it is ready for 'next'?

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v3 0/8] sequencer: don't fork git commit
  2017-11-18  3:41   ` [PATCH v3 0/8] sequencer: don't fork git commit Junio C Hamano
@ 2017-11-18  3:57     ` Junio C Hamano
  2017-11-18 11:32       ` Phillip Wood
  2017-11-18 14:33       ` Phillip Wood
  0 siblings, 2 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-11-18  3:57 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

Junio C Hamano <gitster@pobox.com> writes:

> Phillip Wood <phillip.wood@talktalk.net> writes:
>
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> I've updated these based on the feedback for v2. I've dropped the
>> patch that stopped print_commit_summary() from dying as I think it is
>> better to die than return an error (see the commit message of the
>> patch that adds print_commit_summary() for the reasoning). Apart from
>> that they're minor changes - style fixes and a reworded a commit message.
>
> Thanks for further polishing this topic; I found nothing in the
> update that was questionable.  Will replace.
>
> With this, perhaps it is ready for 'next'?

Not really.  I needed at least this to get it even compile, which
hints that I do not yet know what _else_ I missed by skimming this
round of the series.

 sequencer.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 37460db6b1..63cfb6ddd9 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,
 			unlink(git_path_cherry_pick_head());
 			unlink(git_path_merge_msg());
 			if (!is_rebase_i(opts))
-				res = print_commit_summary(NULL, &oid,
-						SUMMARY_SHOW_AUTHOR_DATE);
+				print_commit_summary(NULL, &oid,
+						     SUMMARY_SHOW_AUTHOR_DATE);
 			return res;
 		}
 	}
-- 
2.15.0-372-g9a6f8facfd


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v3 0/8] sequencer: don't fork git commit
  2017-11-18  3:57     ` Junio C Hamano
@ 2017-11-18 11:32       ` Phillip Wood
  2017-11-18 14:33       ` Phillip Wood
  1 sibling, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-18 11:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 18/11/17 03:57, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>>
>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>>
>>> I've updated these based on the feedback for v2. I've dropped the
>>> patch that stopped print_commit_summary() from dying as I think it is
>>> better to die than return an error (see the commit message of the
>>> patch that adds print_commit_summary() for the reasoning). Apart from
>>> that they're minor changes - style fixes and a reworded a commit message.
>>
>> Thanks for further polishing this topic; I found nothing in the
>> update that was questionable.  Will replace.
>>
>> With this, perhaps it is ready for 'next'?
> 
> Not really.  I needed at least this to get it even compile, which
> hints that I do not yet know what _else_ I missed by skimming this
> round of the series.

Sorry I'm not sure what happened there, by branch has the missing 'res =
' something must have happened to the patches. I'll sort it out and resend

Phillip

>  sequencer.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 37460db6b1..63cfb6ddd9 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,
>  			unlink(git_path_cherry_pick_head());
>  			unlink(git_path_merge_msg());
>  			if (!is_rebase_i(opts))
> -				res = print_commit_summary(NULL, &oid,
> -						SUMMARY_SHOW_AUTHOR_DATE);
> +				print_commit_summary(NULL, &oid,
> +						     SUMMARY_SHOW_AUTHOR_DATE);
>  			return res;
>  		}
>  	}
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v3 0/8] sequencer: don't fork git commit
  2017-11-18  3:57     ` Junio C Hamano
  2017-11-18 11:32       ` Phillip Wood
@ 2017-11-18 14:33       ` Phillip Wood
  1 sibling, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-18 14:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Johannes Schindelin, Phillip Wood

On 18/11/17 03:57, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>>
>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>>
>>> I've updated these based on the feedback for v2. I've dropped the
>>> patch that stopped print_commit_summary() from dying as I think it is
>>> better to die than return an error (see the commit message of the
>>> patch that adds print_commit_summary() for the reasoning). Apart from
>>> that they're minor changes - style fixes and a reworded a commit message.
>>
>> Thanks for further polishing this topic; I found nothing in the
>> update that was questionable.  Will replace.
>>
>> With this, perhaps it is ready for 'next'?
> 
> Not really.  I needed at least this to get it even compile, which
> hints that I do not yet know what _else_ I missed by skimming this
> round of the series.

My apologies, it seems that I was half alseep yesterday morning and in
my haste to update these patches I wasn't paying proper attention to
what I was doing. For some reason after I dropped the patch that
converted print_commit_summary() to return an error rather than die I
forgot to amend the last patch to match and then compounded the mistake
by forgetting to compile and test properly before sending them. I think
they're okay now but I'll double check the changes before sending again
in case there are any other embarrassing mistakes lurking.

As the white rabbit said "The hurrier I go the behinder I get"

Phillip


>  sequencer.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 37460db6b1..63cfb6ddd9 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -1139,8 +1139,8 @@ static int do_commit(const char *msg_file, const char *author,
>  			unlink(git_path_cherry_pick_head());
>  			unlink(git_path_merge_msg());
>  			if (!is_rebase_i(opts))
> -				res = print_commit_summary(NULL, &oid,
> -						SUMMARY_SHOW_AUTHOR_DATE);
> +				print_commit_summary(NULL, &oid,
> +						     SUMMARY_SHOW_AUTHOR_DATE);
>  			return res;
>  		}
>  	}
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v4 0/9] sequencer: don't fork git commit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (10 preceding siblings ...)
  2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
@ 2017-11-24 11:07 ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 1/9] t3404: check intermediate squash messages Phillip Wood
                     ` (8 more replies)
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
  12 siblings, 9 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

I've updated the patches to fix the embarassing build failure in
v3. I've also added a patch to remove the known breakage from some of
the tests in t3512/t3513 that now pass - someone who knows about
submodules should check this. The only other change is to interpret
commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE when loading
the default value for message cleanups to be consistent with 'git
commit'. (I can't imagine many people have that value set in their
config)

Here's the original summary:
These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.

Phillip Wood (9):
  t3404: check intermediate squash messages
  commit: move empty message checks to libgit
  Add a function to update HEAD after creating a commit
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'
  t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1

 builtin/commit.c                 | 289 +++--------------------
 builtin/rebase--helper.c         |  13 +-
 builtin/revert.c                 |  15 +-
 sequencer.c                      | 486 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h                      |  23 ++
 t/t3404-rebase-interactive.sh    |   4 +
 t/t3512-cherry-pick-submodule.sh |   1 -
 t/t3513-revert-submodule.sh      |   1 -
 8 files changed, 561 insertions(+), 271 deletions(-)

-- 
2.15.0


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v4 1/9] t3404: check intermediate squash messages
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 2/9] commit: move empty message checks to libgit Phillip Wood
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

When there is more than one squash/fixup command in a row check the
intermediate messages are correct.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 t/t3404-rebase-interactive.sh | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
 		git rebase -i $base &&
 	git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
 	test_cmp expect-squash-fixup actual-squash-fixup &&
+	git cat-file commit HEAD@{2} |
+		grep "^# This is a combination of 3 commits\."  &&
+	git cat-file commit HEAD@{3} |
+		grep "^# This is a combination of 2 commits\."  &&
 	git checkout to-be-rebased &&
 	git branch -D squash-fixup
 '
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 2/9] commit: move empty message checks to libgit
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 1/9] t3404: check intermediate squash messages Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 3/9] Add a function to update HEAD after creating a commit Phillip Wood
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move the functions that check for empty messages from bulitin/commit.c
to sequencer.c so they can be shared with other commands. The
functions are refactored to take an explicit cleanup mode and template
filename passed by the caller.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - prefix cleanup_mode enum and constants with commit_msg_

 builtin/commit.c | 99 +++++++++++---------------------------------------------
 sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
 sequencer.h      | 11 +++++++
 3 files changed, 91 insertions(+), 80 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 8a877014145435516930c787dec37b8c4ac3da90..d958c2eb2adc9a29dab29340ce9b56daea41fecd 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	struct strbuf sb = STRBUF_INIT;
 	const char *hook_arg1 = NULL;
 	const char *hook_arg2 = NULL;
-	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
 
 	/* This checks and barfs if author is badly specified */
@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct ident_split ci, ai;
 
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == CLEANUP_SCISSORS)
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 				wt_status_add_cut_line(s->fp);
 			status_printf_ln(s, GIT_COLOR_NORMAL,
 			    whence == FROM_MERGE
@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		}
 
 		fprintf(s->fp, "\n");
-		if (cleanup_mode == CLEANUP_ALL)
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\nwith '%c' will be ignored, and an empty"
 				  " message aborts the commit.\n"), comment_line_char);
-		else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+			 whence == FROM_COMMIT)
 			wt_status_add_cut_line(s->fp);
-		else /* CLEANUP_SPACE, that is. */
+		else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\n"
@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
 	if (argc == 0 && (also || (only && !amend && !allow_empty)))
 		die(_("No paths with --include/--only does not make sense."));
 	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = CLEANUP_NONE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = CLEANUP_SPACE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = CLEANUP_ALL;
+		cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 	else if (!strcmp(cleanup_arg, "scissors"))
-		cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else
 		die(_("Invalid cleanup mode %s"), cleanup_arg);
 
@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	}
 
 	if (verbose || /* Truncate the message just before the diff, if any. */
-	    cleanup_mode == CLEANUP_SCISSORS)
+	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 		strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-	if (cleanup_mode != CLEANUP_NONE)
-		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index 19dd575ed9b3b280a3fdabc9121a2e193d6984db..36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -691,6 +691,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..82e57713a2940c5d65ccac013c3f42c55cc12baf 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -58,4 +58,15 @@ extern const char sign_off_header[];
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
 
+enum commit_msg_cleanup_mode {
+	COMMIT_MSG_CLEANUP_SPACE,
+	COMMIT_MSG_CLEANUP_NONE,
+	COMMIT_MSG_CLEANUP_SCISSORS,
+	COMMIT_MSG_CLEANUP_ALL
+};
+
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 3/9] Add a function to update HEAD after creating a commit
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 1/9] t3404: check intermediate squash messages Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 2/9] commit: move empty message checks to libgit Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 4/9] commit: move post-rewrite code to libgit Phillip Wood
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add update_head_with_reflog() based on the code that updates HEAD
after committing in builtin/commit.c that can be called by 'git
commit' and other commands.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - updated commit message to reflect the change in function name
     - style fixes
    
    changes since v1:
     - rename update_head() to update_head_with_reflog()

 builtin/commit.c | 20 ++------------------
 sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  4 ++++
 3 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index d958c2eb2adc9a29dab29340ce9b56daea41fecd..eb144556bf37b7bf357bd976b94305171b4fd159 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", &oid,
-				   current_head
-				   ? &current_head->object.oid : &null_oid,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+				    &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index 36e03d041f32bcc0fdd1fddebb33b23c7e4d8a70..ef262980c5255d90ee023c0b29c6c1c628b3c7d2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -752,6 +752,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl;
+	int ret = 0;
+
+	if (action) {
+		strbuf_addstr(&sb, action);
+		strbuf_addstr(&sb, ": ");
+	}
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head,
+				   old_head ? &old_head->object.oid : &null_oid,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 82e57713a2940c5d65ccac013c3f42c55cc12baf..81a2098e900f0aca30e45ed7f19ae4bf3ce682f0 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -69,4 +69,8 @@ int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum commit_msg_cleanup_mode cleanup_mode);
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 4/9] commit: move post-rewrite code to libgit
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (2 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 3/9] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 5/9] commit: move print_commit_summary() " Phillip Wood
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
be shared with other commands and add a new function
commit_post_rewrite() based on the code in builtin/commit.c that
encapsulates rewriting notes and running the post-rewrite hook. Once
the sequencer learns how to create commits without forking 'git
commit' these functions will be used when squashing commits.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - reword commit message to explain why the code is being moved

 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index eb144556bf37b7bf357bd976b94305171b4fd159..d251cfcebad3476c365492d83803e7821fdfdf2b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index ef262980c5255d90ee023c0b29c6c1c628b3c7d2..6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -21,6 +21,8 @@
 #include "log-tree.h"
 #include "wt-status.h"
 #include "hashmap.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -789,6 +791,51 @@ int update_head_with_reflog(const struct commit *old_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so old_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 81a2098e900f0aca30e45ed7f19ae4bf3ce682f0..ec13b679c40dc4012a84a761ea856d6e75953490 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -73,4 +73,6 @@ int update_head_with_reflog(const struct commit *old_head,
 			    const struct object_id *new_head,
 			    const char *action, const struct strbuf *msg,
 			    struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 5/9] commit: move print_commit_summary() to libgit
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (3 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 4/9] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move print_commit_summary() from builtin/commit.c to sequencer.c so it
can be shared with other commands. The function is modified by
changing the last argument to a flag so callers can specify whether
they want to show the author date in addition to specifying if this is
an initial commit.

If the sequencer dies in print_commit_summary() (which can only happen
when cherry-picking or reverting) then neither the todo list nor the
abort safety file are updated to reflect the commit that was just
made. print_commit_summary() can die if:

 - The commit that was just created cannot be found or parsed.

 - HEAD cannot be resolved either because some other process is
   updating it (which is bad news in the middle of a cherry-pick) or
   because it is corrupt.

 - log_tree_commit() cannot read some objects.

In all those cases dying will leave the sequencer in a sane state for
aborting; 'git cherry-pick --abort' will rewind HEAD to the last
successful commit before there was a problem with HEAD or the object
database. If the user somehow fixes the problem and runs 'git
cherry-pick --continue' then the sequencer will try and pick the same
commit again which may or may not be what the user wants depending on
what caused print_commit_summary() to die. If print_commit_summary()
returned an error instead then update_abort_safety_file() would try to
resolve HEAD which may or may not be successful. If it is successful
then running 'git rebase --abort' would not rewind HEAD to the last
successful commit which is not what we want.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - expanded commit message to explain why it is ok to die in
       print_commit_summary() and dropped the next patch which made it
       return an error instead.
     - style fixes.
    
    changes since v1:
     - convert flags passed to print_commit_summary() to unsigned int

 builtin/commit.c | 128 ++++---------------------------------------------------
 sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 133 insertions(+), 119 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index d251cfcebad3476c365492d83803e7821fdfdf2b..2043479d37873671d43124dc0cb509d6d9247baa 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		unsigned int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 
 	UNLEAK(err);
 	UNLEAK(sb);
diff --git a/sequencer.c b/sequencer.c
index 6bc8346d42bb3cb1d2dc6a2238dd1b38e4308914..a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -836,6 +836,125 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	if (!head)
+		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ?
+						_(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index ec13b679c40dc4012a84a761ea856d6e75953490..4f616c61a3f3869daf9f427b978c308d6094a978 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -75,4 +75,9 @@ int update_head_with_reflog(const struct commit *old_head,
 			    struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags);
 #endif
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 6/9] sequencer: simplify adding Signed-off-by: trailer
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (4 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 5/9] commit: move print_commit_summary() " Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 7/9] sequencer: load commit related config Phillip Wood
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and specifying '--signoff'
when running 'git commit'. This means that if there are conflicts when
merging with a strategy other than 'recursive' the Signed-off-by:
trailer will be added if the user commits the resolution themselves
without passing '--signoff' to 'git commit'. It also simplifies the
in-process commit that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index a2cf6f5e06ffec5108f0faf43d1a4cb605264c3f..7400df5522037583108534755af6f542117667c2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -477,9 +477,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -657,8 +654,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1347,6 +1342,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 7/9] sequencer: load commit related config
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (5 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 13:48     ` Junio C Hamano
  2017-12-04 18:30     ` Junio C Hamano
  2017-11-24 11:07   ` [PATCH v4 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
  8 siblings, 2 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup and gpg signing of commits in
preparation for committing without forking 'git commit'. Note that we
interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to
be consistent with 'git commit'

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v3:
     - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE
       to match 'git commit'
    
    changes since v1:
     - renamed git_revert_config() to common_config()
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 builtin/rebase--helper.c | 13 ++++++++++++-
 builtin/revert.c         | 15 +++++++++++++--
 sequencer.c              | 34 ++++++++++++++++++++++++++++++++++
 sequencer.h              |  1 +
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a393862b6857acab037e74367c7f2134..68194d3aed950f327a8bc624fa1991478dfea01e 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
+static int git_rebase_helper_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -39,7 +50,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_rebase_helper_config, NULL);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..1938825efa6ad20ede5aba57f097863aeb33d1d5 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,6 +31,17 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
+static int common_config(const char *k, const char *v, void *cb)
+{
+	int status;
+
+	status = git_sequencer_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_default_config(k, v, NULL);
+}
+
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -208,7 +219,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +232,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	git_config(common_config, NULL);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 7400df5522037583108534755af6f542117667c2..40461a41e3798e267813656de6b669c297b521c6 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -688,6 +688,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static enum commit_msg_cleanup_mode default_msg_cleanup =
+						COMMIT_MSG_CLEANUP_NONE;
+static char *default_gpg_sign;
+
+int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "commit.cleanup")) {
+		int status;
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	return git_gpg_config(k, v, NULL);
+}
+
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
diff --git a/sequencer.h b/sequencer.h
index 4f616c61a3f3869daf9f427b978c308d6094a978..77cb174b2aaf3972ebb9e6ec379252be96dedd3d 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -57,6 +57,7 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
+int git_sequencer_config(const char *k, const char *v, void *cb);
 
 enum commit_msg_cleanup_mode {
 	COMMIT_MSG_CLEANUP_SPACE,
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 8/9] sequencer: try to commit without forking 'git commit'
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (6 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 7/9] sequencer: load commit related config Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-11-24 11:07   ` [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
  8 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 27% (from 282ms to 204ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
my computer running linux. Some of greater saving for rebase is
because it no longer wastes time creating the commit summary just to
throw it away.

The code to create the commit is based on builtin/commit.c. It is
simplified as it doesn't have to deal with merges and modified so that
it does not die but returns an error to make sure the sequencer exits
cleanly, as it would when forking 'git commit'

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD is created unnecessarily. This could be
eliminated in future. I hacked up a version that does not write these
files and just passed an strbuf (with the wrong message for fixup and
squash commands) to do_commit() but I couldn't measure any significant
time difference when running cherry-pick or rebase. I think
eliminating the writes properly for rebase would require a bit of
effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v3:
     - take account of change print_commit_summary() return type after
       dropping the patch that made it return an error instead of dying.
    
    changes since v2:
     - style fixes
    
    changes since v1:
     - added comments to explain return value of try_to_commit()
     - removed unnecessary NULL tests before calling free()
     - style cleanups
     - corrected commit message
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 sequencer.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 176 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 40461a41e3798e267813656de6b669c297b521c6..99095a28c6732ef697b5b763b347751bd8a440cf 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -592,6 +592,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char *message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -984,6 +996,160 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	strbuf_release(&format);
 }
 
+static int parse_head(struct commit **head)
+{
+	struct commit *current_head;
+	struct object_id oid;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_reference(&oid);
+		if (!current_head)
+			return error(_("could not parse HEAD"));
+		if (oidcmp(&oid, &current_head->object.oid)) {
+			warning(_("HEAD %s is not a commit!"),
+				oid_to_hex(&oid));
+		}
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+	*head = current_head;
+
+	return 0;
+}
+
+/*
+ * Try to commit without forking 'git commit'. In some cases we need
+ * to run 'git commit' to display an error message
+ *
+ * Returns:
+ *  -1 - error unable to commit
+ *   0 - success
+ *   1 - run 'git commit'
+ */
+static int try_to_commit(struct strbuf *msg, const char *author,
+			 struct replay_opts *opts, unsigned int flags,
+			 struct object_id *oid)
+{
+	struct object_id tree;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	char *amend_author = NULL;
+	const char *gpg_sign;
+	enum commit_msg_cleanup_mode cleanup;
+	int res = 0;
+
+	if (parse_head(&current_head))
+		return -1;
+
+	if (flags & AMEND_MSG) {
+		const char *exclude_gpgsig[] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		if (!msg) {
+			const char *orig_message = NULL;
+
+			find_commit_subject(message, &orig_message);
+			msg = &amend_msg;
+			strbuf_addstr(msg, orig_message);
+		}
+		author = amend_author = get_author(message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
+					  default_msg_cleanup;
+	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	gpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				 oid->hash, author, gpg_sign, extra)) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	if (update_head_with_reflog(current_head, oid,
+				    getenv("GIT_REFLOG_ACTION"), msg, &err)) {
+		res = error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, oid);
+
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *msg_file, const char *author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res = 1;
+
+	if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
+		struct object_id oid;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
+			return error_errno(_("unable to read commit message "
+					     "from '%s'"),
+					   msg_file);
+
+		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
+				    &oid);
+		strbuf_release(&sb);
+		if (!res) {
+			unlink(git_path_cherry_pick_head());
+			unlink(git_path_merge_msg());
+			if (!is_rebase_i(opts))
+				print_commit_summary(NULL, &oid,
+						SUMMARY_SHOW_AUTHOR_DATE);
+			return res;
+		}
+	}
+	if (res == 1)
+		return run_git_commit(msg_file, opts, flags);
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1235,6 +1401,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1351,6 +1518,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup(command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1436,9 +1605,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1447,6 +1620,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
                     ` (7 preceding siblings ...)
  2017-11-24 11:07   ` [PATCH v4 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-11-24 11:07   ` Phillip Wood
  2017-12-04 19:24     ` Stefan Beller
  8 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 11:07 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Now that the sequencer creates commits without forking 'git commit' it
does not see an empty commit in these tests which fixes the known
breakage. Note that logic for handling
KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from
lib-submodule-update.sh as it is still used by other tests.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    The output of the tests with after the previous patch looks like the
    output of the merge tests (see below), so I'm hopeful that this is a
    genuine fix, but someone who knows about submodules should check that
    things are in fact working properly now.
    
    t3512-cherry-pick-submodule.sh
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t modify_sub1 origin/modify_sub1 &&
    			$command modify_sub1 &&
    			test_superproject_content origin/modify_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			git submodule update &&
    			test_submodule_content sub1 origin/modify_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'
    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.
    [add_sub1 e57a25c] Modify sub1
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:45 2017 +0000
    Submodule path 'sub1': checked out '7c9cd6d138a7bb3145fc0c7fca1f403cbe89010e'
    ok 11 - git cherry-pick: modified submodule does not update submodule work tree
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t invalid_sub1 origin/invalid_sub1 &&
    			$command invalid_sub1 &&
    			test_superproject_content origin/invalid_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			test_must_fail git submodule update &&
    			test_submodule_content sub1 origin/add_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    [add_sub1 155695c] Invalid sub1 commit
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:45 2017 +0000
    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789
    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.
    ok 12 - git cherry-pick: modified submodule does not update submodule work tree to invalid commit
    
    expecting success:
    		prolog &&
    		reset_work_tree_to invalid_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t valid_sub1 origin/valid_sub1 &&
    			$command valid_sub1 &&
    			test_superproject_content origin/valid_sub1 &&
    			test_dir_is_empty sub1 &&
    			git submodule update --init --recursive &&
    			test_submodule_content sub1 origin/valid_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'invalid_sub1'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    fatal: Needed a single revision
    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.
    [invalid_sub1 497299e] Revert "Invalid sub1 commit"
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:46 2017 +0000
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'sub1'
    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/sub1'...
    done.
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3512-cherry-pick-submodule/submodule_update/uninitialized_sub'...
    done.
    Submodule path 'sub1': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'
    Submodule path 'uninitialized_sub': checked out 'ce9efb76b6ff5beb56e70d3662695f3ecbd38330'
    ok 13 - git cherry-pick: modified submodule does not update submodule work tree from invalid commit
    
    t3513-revert-submodule.sh
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t modify_sub1 origin/modify_sub1 &&
    			$command modify_sub1 &&
    			test_superproject_content origin/modify_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			git submodule update &&
    			test_submodule_content sub1 origin/modify_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'
    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.
    Switched to branch 'modify_sub1'
    Your branch is up to date with 'origin/modify_sub1'.
    [modify_sub1 1c2c464] Revert "Modify sub1"
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    [modify_sub1 cde56a6] Revert "Revert "Modify sub1""
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    Submodule path 'sub1': checked out 'a66b894bbfed816edf59cf901620978a64419731'
    ok 11 - git_revert: modified submodule does not update submodule work tree
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t invalid_sub1 origin/invalid_sub1 &&
    			$command invalid_sub1 &&
    			test_superproject_content origin/invalid_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			test_must_fail git submodule update &&
    			test_submodule_content sub1 origin/add_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    Switched to branch 'invalid_sub1'
    Your branch is up to date with 'origin/invalid_sub1'.
    [invalid_sub1 cec26d9] Revert "Invalid sub1 commit"
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    [invalid_sub1 d017764] Revert "Revert "Invalid sub1 commit""
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789
    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.
    ok 12 - git_revert: modified submodule does not update submodule work tree to invalid commit
    
    expecting success:
    		prolog &&
    		reset_work_tree_to invalid_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t valid_sub1 origin/valid_sub1 &&
    			$command valid_sub1 &&
    			test_superproject_content origin/valid_sub1 &&
    			test_dir_is_empty sub1 &&
    			git submodule update --init --recursive &&
    			test_submodule_content sub1 origin/valid_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'invalid_sub1'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    fatal: Needed a single revision
    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.
    Switched to branch 'valid_sub1'
    Your branch is up to date with 'origin/valid_sub1'.
    [valid_sub1 ca047d9] Revert "Revert "Invalid sub1 commit""
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    [valid_sub1 e1e9178] Revert "Revert "Revert "Invalid sub1 commit"""
     Author: A U Thor <author@example.com>
     Date: Fri Nov 24 10:48:51 2017 +0000
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'sub1'
    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/sub1'...
    done.
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t3513-revert-submodule/submodule_update/uninitialized_sub'...
    done.
    Submodule path 'sub1': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'
    Submodule path 'uninitialized_sub': checked out '4497aa8f9341f6e5f178dbb536a7093e8f093d8a'
    ok 13 - git_revert: modified submodule does not update submodule work tree from invalid commit
    
    t7613-merge-submodule.sh
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t modify_sub1 origin/modify_sub1 &&
    			$command modify_sub1 &&
    			test_superproject_content origin/modify_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			git submodule update &&
    			test_submodule_content sub1 origin/modify_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'
    Branch 'modify_sub1' set up to track remote branch 'modify_sub1' from 'origin'.
    Updating ec3f722..c933e8b
    Fast-forward
    Submodule path 'sub1': checked out '614c78931e0df85cee80e90bbf96ed92504cd886'
    ok 11 - git merge: modified submodule does not update submodule work tree
    
    expecting success:
    		prolog &&
    		reset_work_tree_to add_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t invalid_sub1 origin/invalid_sub1 &&
    			$command invalid_sub1 &&
    			test_superproject_content origin/invalid_sub1 &&
    			test_submodule_content sub1 origin/add_sub1 &&
    			test_must_fail git submodule update &&
    			test_submodule_content sub1 origin/add_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'add_sub1'
    Branch 'add_sub1' set up to track remote branch 'add_sub1' from 'origin'.
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...
    done.
    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    Updating ec3f722..5c6fb5f
    Fast-forward
    error: Server does not allow request for unadvertised object 0123456789012345678901234567890123456789
    Fetched in submodule path 'sub1', but it did not contain 0123456789012345678901234567890123456789. Direct fetching of that commit failed.
    ok 12 - git merge: modified submodule does not update submodule work tree to invalid commit
    
    expecting success:
    		prolog &&
    		reset_work_tree_to invalid_sub1 &&
    		(
    			cd submodule_update &&
    			git branch -t valid_sub1 origin/valid_sub1 &&
    			$command valid_sub1 &&
    			test_superproject_content origin/valid_sub1 &&
    			test_dir_is_empty sub1 &&
    			git submodule update --init --recursive &&
    			test_submodule_content sub1 origin/valid_sub1
    		)
    
    Cloning into 'submodule_update'...
    done.
    Switched to a new branch 'invalid_sub1'
    Branch 'invalid_sub1' set up to track remote branch 'invalid_sub1' from 'origin'.
    fatal: Needed a single revision
    Branch 'valid_sub1' set up to track remote branch 'valid_sub1' from 'origin'.
    Updating 5c6fb5f..07412aa
    Fast-forward
    Submodule 'sub1' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'sub1'
    Submodule 'uninitialized_sub' (/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update_sub1) registered for path 'uninitialized_sub'
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/sub1'...
    done.
    Cloning into '/home/phil/Documents/src/git/t/trash directory.t7613-merge-submodule/submodule_update/uninitialized_sub'...
    done.
    Submodule path 'sub1': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'
    Submodule path 'uninitialized_sub': checked out 'c97e5093cc535f043494394509e7ad784b5931ce'
    ok 13 - git merge: modified submodule does not update submodule work tree from invalid commit

 t/t3512-cherry-pick-submodule.sh | 1 -
 t/t3513-revert-submodule.sh      | 1 -
 2 files changed, 2 deletions(-)

diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index 6863b7bb6fd94cbbd3fcc8f29ab02e344cac23c9..059213767e089e69ad61d14c329930ef097813e7 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -5,7 +5,6 @@ test_description='cherry-pick can handle submodules'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-submodule-update.sh
 
-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
 KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
 KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
 test_submodule_switch "git cherry-pick"
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index db9378142a93338d2988f40e2748bc476490bcd5..5e39fcdb66c0c7c4b112c1bbe941d886db237693 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -25,7 +25,6 @@ git_revert () {
 	git revert HEAD
 }
 
-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
 KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
 test_submodule_switch "git_revert"
 
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-11-24 11:07   ` [PATCH v4 7/9] sequencer: load commit related config Phillip Wood
@ 2017-11-24 13:48     ` Junio C Hamano
  2017-11-24 14:38       ` Phillip Wood
  2017-12-04 18:30     ` Junio C Hamano
  1 sibling, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-11-24 13:48 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Load default values for message cleanup and gpg signing of commits in
> preparation for committing without forking 'git commit'. Note that we
> interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to
> be consistent with 'git commit'

Hmph, is that because we never invoke the editor to edit the commit
log message?  Over there, scissors is demoted to space when the
editor is not in use, but otherwise this demotion does not occur.

Just being curious.

>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>
> Notes:
>     changes since v3:
>      - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE
>        to match 'git commit'

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-11-24 13:48     ` Junio C Hamano
@ 2017-11-24 14:38       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-11-24 14:38 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

On 24/11/17 13:48, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Load default values for message cleanup and gpg signing of commits in
>> preparation for committing without forking 'git commit'. Note that we
>> interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE to
>> be consistent with 'git commit'
> 
> Hmph, is that because we never invoke the editor to edit the commit
> log message?  Over there, scissors is demoted to space when the
> editor is not in use, but otherwise this demotion does not occur.

Yes that's right. In fact I'm fairly we always specify an explicit
cleanup mode when calling try_to_commit() so the default is there in
case that changes in the future.

> Just being curious.
> 
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>> ---
>>
>> Notes:
>>     changes since v3:
>>      - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE
>>        to match 'git commit'


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-11-24 11:07   ` [PATCH v4 7/9] sequencer: load commit related config Phillip Wood
  2017-11-24 13:48     ` Junio C Hamano
@ 2017-12-04 18:30     ` Junio C Hamano
  2017-12-05 11:21       ` Phillip Wood
  1 sibling, 1 reply; 120+ messages in thread
From: Junio C Hamano @ 2017-12-04 18:30 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> --- a/builtin/rebase--helper.c
> +++ b/builtin/rebase--helper.c
> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>  	NULL
>  };
>  
> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
> +{
> +	int status;
> +
> +	status = git_sequencer_config(k, v, NULL);
> +	if (status)
> +		return status;
> +
> +	return git_default_config(k, v, NULL);
> +}
> +

Sorry for spotting the problem this late, but this code is
unfortunate and we will need to revisit it later; we may want to do
so sooner rather than later.

When k,v is a valid configuration that is handled by
sequencer_config() successfully, this code still needs to call into
default_config() with the same k,v, only to get it ignored.

The problem lies in the (mis)design of git_sequencer_config().  The
function should either allow the caller to notice that (k,v) has
already been handled, or be the last one in the callback by making a
call to default_config() itself.

For the former, because this helper does not have to be called
directly as a git_config() callback, but instead it is always meant
to be called indirectly from another git_config() callback (like
git_rebase_helper_config() here, and common_config() in
builtin/revert.c like we see below), it does *not* have to be
constrained by the function signature required for it to be a config
callback.  It could take a pointer to an int that stores if 'k' was
handled inside the function,

    int git_sequencer_config_helper(char *k, char *v, int *handled);

for example.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
  2017-11-24 11:07   ` [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
@ 2017-12-04 19:24     ` Stefan Beller
  2017-12-05 12:13       ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Stefan Beller @ 2017-12-04 19:24 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Junio C Hamano,
	Ramsay Jones, Adam Dinwoodie

On Fri, Nov 24, 2017 at 3:07 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> Now that the sequencer creates commits without forking 'git commit' it
> does not see an empty commit in these tests which fixes the known
> breakage. Note that logic for handling
> KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from
> lib-submodule-update.sh as it is still used by other tests.
>
> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
>
> Notes:
>     The output of the tests with after the previous patch looks like the
>     output of the merge tests (see below), so I'm hopeful that this is a
>     genuine fix, but someone who knows about submodules should check that
>     things are in fact working properly now.

Looking at the patch only (in combination with the history of the
submodule tests,
283f56a40b (cherry-pick: add t3512 for submodule updates, 2014-06-15))
this patch
looks good to me. I wonder though if this needs to be squashed in another commit
to keep the test suite working for each patch and have no intermittent
failure in the
series.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-12-04 18:30     ` Junio C Hamano
@ 2017-12-05 11:21       ` Phillip Wood
  2017-12-05 12:10         ` Phillip Wood
  2017-12-09 19:05         ` Phillip Wood
  0 siblings, 2 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-05 11:21 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

On 04/12/17 18:30, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> --- a/builtin/rebase--helper.c
>> +++ b/builtin/rebase--helper.c
>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>>  	NULL
>>  };
>>  
>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
>> +{
>> +	int status;
>> +
>> +	status = git_sequencer_config(k, v, NULL);
>> +	if (status)
>> +		return status;
>> +
>> +	return git_default_config(k, v, NULL);
>> +}
>> +
> 
> Sorry for spotting the problem this late, but this code is
> unfortunate and we will need to revisit it later; we may want to do
> so sooner rather than later.

If it needs fixing then doing it before it hits next makes sense.

> When k,v is a valid configuration that is handled by
> sequencer_config() successfully, this code still needs to call into
> default_config() with the same k,v, only to get it ignored.

I'm a bit confused by this sentence. Do you mean that when k,v is a
valid configuration that is successfully handled by sequencer_config()
this code unnecessarily calls default_config() as there is no need to
call default_config() if k,v have already been handled?

> The problem lies in the (mis)design of git_sequencer_config().  The
> function should either allow the caller to notice that (k,v) has
> already been handled, or be the last one in the callback by making a
> call to default_config() itself.

The problem is that git_gpg_config() provides no indication if it has
handled k,v so there's no way to avoid the call to default_config() in
that case. builtin/am.c and builtin/commit.c both do something like

static int git_am_config(const char *k, const char *v, void *cb)
{
	int status;

	status = git_gpg_config(k, v, NULL);
	if (status)
		return status;

	return git_default_config(k, v, NULL);
}


Looking more generally at sequencer_config() I wonder if we should be
providing a warning or an error if the config contains an invalid
cleanup mode. Also should it be calling git_diff_ui_config() to set
things up for print_commit_summary()? (I'm not sure if anything in that
function is affected by diff config settings)

Let me know what you think. I should have time to update this patch set
later in the week.

Best Wishes

Phillip

> For the former, because this helper does not have to be called
> directly as a git_config() callback, but instead it is always meant
> to be called indirectly from another git_config() callback (like
> git_rebase_helper_config() here, and common_config() in
> builtin/revert.c like we see below), it does *not* have to be
> constrained by the function signature required for it to be a config
> callback.  It could take a pointer to an int that stores if 'k' was
> handled inside the function,
> 
>     int git_sequencer_config_helper(char *k, char *v, int *handled);
> 
> for example.
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-12-05 11:21       ` Phillip Wood
@ 2017-12-05 12:10         ` Phillip Wood
  2017-12-09 19:05         ` Phillip Wood
  1 sibling, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-05 12:10 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

On 05/12/17 11:21, Phillip Wood wrote:
> On 04/12/17 18:30, Junio C Hamano wrote:
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>>
>>> --- a/builtin/rebase--helper.c
>>> +++ b/builtin/rebase--helper.c
>>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>>>  	NULL
>>>  };
>>>  
>>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
>>> +{
>>> +	int status;
>>> +
>>> +	status = git_sequencer_config(k, v, NULL);
>>> +	if (status)
>>> +		return status;
>>> +
>>> +	return git_default_config(k, v, NULL);
>>> +}
>>> +
>>
>> Sorry for spotting the problem this late, but this code is
>> unfortunate and we will need to revisit it later; we may want to do
>> so sooner rather than later.
> 
> If it needs fixing then doing it before it hits next makes sense.
> 
>> When k,v is a valid configuration that is handled by
>> sequencer_config() successfully, this code still needs to call into
>> default_config() with the same k,v, only to get it ignored.
> 
> I'm a bit confused by this sentence. Do you mean that when k,v is a
> valid configuration that is successfully handled by sequencer_config()
> this code unnecessarily calls default_config() as there is no need to
> call default_config() if k,v have already been handled?
> 
>> The problem lies in the (mis)design of git_sequencer_config().  The
>> function should either allow the caller to notice that (k,v) has
>> already been handled, or be the last one in the callback by making a
>> call to default_config() itself.
> 
> The problem is that git_gpg_config() provides no indication if it has
> handled k,v so there's no way to avoid the call to default_config() in
> that case. builtin/am.c and builtin/commit.c both do something like
> 
> static int git_am_config(const char *k, const char *v, void *cb)
> {
> 	int status;
> 
> 	status = git_gpg_config(k, v, NULL);
> 	if (status)
> 		return status;
> 
> 	return git_default_config(k, v, NULL);
> }
> 

Looking through the callers of gpg_config() it should be fairly easy to
convert it to take an extra parameter in the same way as you suggested
for sequencer_config()

> Looking more generally at sequencer_config() I wonder if we should be
> providing a warning or an error if the config contains an invalid
> cleanup mode. Also should it be calling git_diff_ui_config() to set
> things up for print_commit_summary()? (I'm not sure if anything in that
> function is affected by diff config settings)
> 
> Let me know what you think. I should have time to update this patch set
> later in the week.
> 
> Best Wishes
> 
> Phillip
> 
>> For the former, because this helper does not have to be called
>> directly as a git_config() callback, but instead it is always meant
>> to be called indirectly from another git_config() callback (like
>> git_rebase_helper_config() here, and common_config() in
>> builtin/revert.c like we see below), it does *not* have to be
>> constrained by the function signature required for it to be a config
>> callback.  It could take a pointer to an int that stores if 'k' was
>> handled inside the function,
>>
>>     int git_sequencer_config_helper(char *k, char *v, int *handled);
>>
>> for example.
>>
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
  2017-12-04 19:24     ` Stefan Beller
@ 2017-12-05 12:13       ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-05 12:13 UTC (permalink / raw)
  To: Stefan Beller, Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Junio C Hamano,
	Ramsay Jones, Adam Dinwoodie

On 04/12/17 19:24, Stefan Beller wrote:
> On Fri, Nov 24, 2017 at 3:07 AM, Phillip Wood <phillip.wood@talktalk.net> wrote:
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> Now that the sequencer creates commits without forking 'git commit' it
>> does not see an empty commit in these tests which fixes the known
>> breakage. Note that logic for handling
>> KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from
>> lib-submodule-update.sh as it is still used by other tests.
>>
>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
>> ---
>>
>> Notes:
>>     The output of the tests with after the previous patch looks like the
>>     output of the merge tests (see below), so I'm hopeful that this is a
>>     genuine fix, but someone who knows about submodules should check that
>>     things are in fact working properly now.
> 
> Looking at the patch only (in combination with the history of the
> submodule tests,
> 283f56a40b (cherry-pick: add t3512 for submodule updates, 2014-06-15))
> this patch
> looks good to me. I wonder though if this needs to be squashed in another commit
> to keep the test suite working for each patch and have no intermittent
> failure in the
> series.

Hi Stefan

Thanks for looking at this, it's good to know you think it's OK. I'll
leave it to Junio's discretion whether to squash this into the previous
patch

Thanks again

Phillip

> Thanks,
> Stefan
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v4 7/9] sequencer: load commit related config
  2017-12-05 11:21       ` Phillip Wood
  2017-12-05 12:10         ` Phillip Wood
@ 2017-12-09 19:05         ` Phillip Wood
  1 sibling, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-09 19:05 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

On 05/12/17 11:21, Phillip Wood wrote:
> On 04/12/17 18:30, Junio C Hamano wrote:
>> Phillip Wood <phillip.wood@talktalk.net> writes:
>>
>>> --- a/builtin/rebase--helper.c
>>> +++ b/builtin/rebase--helper.c
>>> @@ -9,6 +9,17 @@ static const char * const builtin_rebase_helper_usage[] = {
>>>  	NULL
>>>  };
>>>  
>>> +static int git_rebase_helper_config(const char *k, const char *v, void *cb)
>>> +{
>>> +	int status;
>>> +
>>> +	status = git_sequencer_config(k, v, NULL);
>>> +	if (status)
>>> +		return status;
>>> +
>>> +	return git_default_config(k, v, NULL);
>>> +}
>>> +
>>
>> Sorry for spotting the problem this late, but this code is
>> unfortunate and we will need to revisit it later; we may want to do
>> so sooner rather than later.
> 
> If it needs fixing then doing it before it hits next makes sense.
> 
>> When k,v is a valid configuration that is handled by
>> sequencer_config() successfully, this code still needs to call into
>> default_config() with the same k,v, only to get it ignored.
> 
> I'm a bit confused by this sentence. Do you mean that when k,v is a
> valid configuration that is successfully handled by sequencer_config()
> this code unnecessarily calls default_config() as there is no need to
> call default_config() if k,v have already been handled?
> 
>> The problem lies in the (mis)design of git_sequencer_config().  The
>> function should either allow the caller to notice that (k,v) has
>> already been handled, or be the last one in the callback by making a
>> call to default_config() itself.
> 
> The problem is that git_gpg_config() provides no indication if it has
> handled k,v so there's no way to avoid the call to default_config() in
> that case. builtin/am.c and builtin/commit.c both do something like
> 
> static int git_am_config(const char *k, const char *v, void *cb)
> {
> 	int status;
> 
> 	status = git_gpg_config(k, v, NULL);
> 	if (status)
> 		return status;
> 
> 	return git_default_config(k, v, NULL);
> }
> 
> 
> Looking more generally at sequencer_config() I wonder if we should be
> providing a warning or an error if the config contains an invalid
> cleanup mode. Also should it be calling git_diff_ui_config() to set
> things up for print_commit_summary()? (I'm not sure if anything in that
> function is affected by diff config settings)

I think the answer maybe yes so that it loads the setting for external
diff commands and textconv filters but I'm not sure, perhaps someone
more familiar with the diff code would know? Also I think we should be
loading the basic diff config for creating the patch when we stop with
conflicts?

If anyone has any thought on this, please share them!

Best Wishes

Phillip
> 
> Let me know what you think. I should have time to update this patch set
> later in the week.
> 
> Best Wishes
> 
> Phillip
> 
>> For the former, because this helper does not have to be called
>> directly as a git_config() callback, but instead it is always meant
>> to be called indirectly from another git_config() callback (like
>> git_rebase_helper_config() here, and common_config() in
>> builtin/revert.c like we see below), it does *not* have to be
>> constrained by the function signature required for it to be a config
>> callback.  It could take a pointer to an int that stores if 'k' was
>> handled inside the function,
>>
>>     int git_sequencer_config_helper(char *k, char *v, int *handled);
>>
>> for example.
>>
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v5 0/9] sequencer: don't fork git commit
  2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
                   ` (11 preceding siblings ...)
  2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
@ 2017-12-11 14:13 ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 1/9] t3404: check intermediate squash messages Phillip Wood
                     ` (9 more replies)
  12 siblings, 10 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

I've reworked the config handling since v4. It now stores the default
values in struct replay_opt rather than using global variables and
calls git_diff_basic_config(). Unfortunately I've not had time to
modify git_gpg_config() to indicate if it successfully handled the key
so git_diff_basic_config() is called unnecessarily in that case. Within
git_diff_basic_config() userdiff_config() also suffers from the same
problem of not indicating if it has handled the key.

Here's the original summary:
These patches teach the sequencer to create commits without forking
git commit when the commit message does not need to be edited. This
speeds up cherry picking 10 commits by 26% and picking 10 commits with
rebase --continue by 44%. The first few patches move bits of
builtin/commit.c to sequencer.c. The last two patches actually
implement creating commits in sequencer.c.

Phillip Wood (9):
  t3404: check intermediate squash messages
  commit: move empty message checks to libgit
  Add a function to update HEAD after creating a commit
  commit: move post-rewrite code to libgit
  commit: move print_commit_summary() to libgit
  sequencer: simplify adding Signed-off-by: trailer
  sequencer: load commit related config
  sequencer: try to commit without forking 'git commit'
  t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1

 builtin/commit.c                 | 289 +++--------------------
 builtin/rebase--helper.c         |   2 +-
 builtin/revert.c                 |   4 +-
 sequencer.c                      | 495 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h                      |  24 ++
 t/t3404-rebase-interactive.sh    |   4 +
 t/t3512-cherry-pick-submodule.sh |   1 -
 t/t3513-revert-submodule.sh      |   1 -
 8 files changed, 549 insertions(+), 271 deletions(-)

-- 
2.15.1


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH v5 1/9] t3404: check intermediate squash messages
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 2/9] commit: move empty message checks to libgit Phillip Wood
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

When there is more than one squash/fixup command in a row check the
intermediate messages are correct.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 t/t3404-rebase-interactive.sh | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 6a82d1ed876dd5d1073dc63be8ba5720adbf12e3..9ed0a244e6cdf34c7caca8232f0c0a8cf4864c42 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -453,6 +453,10 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
 		git rebase -i $base &&
 	git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
 	test_cmp expect-squash-fixup actual-squash-fixup &&
+	git cat-file commit HEAD@{2} |
+		grep "^# This is a combination of 3 commits\."  &&
+	git cat-file commit HEAD@{3} |
+		grep "^# This is a combination of 2 commits\."  &&
 	git checkout to-be-rebased &&
 	git branch -D squash-fixup
 '
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 2/9] commit: move empty message checks to libgit
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 1/9] t3404: check intermediate squash messages Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 3/9] Add a function to update HEAD after creating a commit Phillip Wood
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move the functions that check for empty messages from bulitin/commit.c
to sequencer.c so they can be shared with other commands. The
functions are refactored to take an explicit cleanup mode and template
filename passed by the caller.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v4:
     - move the definition of cleanup mode enum so it can be referenced in
       struct replay_opts by a later commit
    
    changes since v1:
     - prefix cleanup_mode enum and constants with commit_msg_

 builtin/commit.c | 99 +++++++++++---------------------------------------------
 sequencer.c      | 61 ++++++++++++++++++++++++++++++++++
 sequencer.h      | 12 ++++++-
 3 files changed, 91 insertions(+), 81 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 2de75882b847be1a100931f6218e7e14dc01c4bd..2175dac8036c465a73c4c782f061e85ae6d1a629 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -128,12 +128,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-	CLEANUP_SPACE,
-	CLEANUP_NONE,
-	CLEANUP_SCISSORS,
-	CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -673,7 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	struct strbuf sb = STRBUF_INIT;
 	const char *hook_arg1 = NULL;
 	const char *hook_arg2 = NULL;
-	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
 
 	/* This checks and barfs if author is badly specified */
@@ -812,7 +807,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		struct ident_split ci, ai;
 
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == CLEANUP_SCISSORS)
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 				wt_status_add_cut_line(s->fp);
 			status_printf_ln(s, GIT_COLOR_NORMAL,
 			    whence == FROM_MERGE
@@ -832,14 +827,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 		}
 
 		fprintf(s->fp, "\n");
-		if (cleanup_mode == CLEANUP_ALL)
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\nwith '%c' will be ignored, and an empty"
 				  " message aborts the commit.\n"), comment_line_char);
-		else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+			 whence == FROM_COMMIT)
 			wt_status_add_cut_line(s->fp);
-		else /* CLEANUP_SPACE, that is. */
+		else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\n"
@@ -984,65 +980,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 	return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-	int i, eol;
-	const char *nl;
-
-	/* Check if the rest is just whitespace and Signed-off-by's. */
-	for (i = start; i < sb->len; i++) {
-		nl = memchr(sb->buf + i, '\n', sb->len - i);
-		if (nl)
-			eol = nl - sb->buf;
-		else
-			eol = sb->len;
-
-		if (strlen(sign_off_header) <= eol - i &&
-		    starts_with(sb->buf + i, sign_off_header)) {
-			i = eol;
-			continue;
-		}
-		while (i < eol)
-			if (!isspace(sb->buf[i++]))
-				return 0;
-	}
-
-	return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-	return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-	struct strbuf tmpl = STRBUF_INIT;
-	const char *start;
-
-	if (cleanup_mode == CLEANUP_NONE && sb->len)
-		return 0;
-
-	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-		return 0;
-
-	strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-	if (!skip_prefix(sb->buf, tmpl.buf, &start))
-		start = sb->buf;
-	strbuf_release(&tmpl);
-	return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
 	struct rev_info revs;
@@ -1227,15 +1164,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
 	if (argc == 0 && (also || (only && !amend && !allow_empty)))
 		die(_("No paths with --include/--only does not make sense."));
 	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = CLEANUP_NONE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
 	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = CLEANUP_SPACE;
+		cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
 	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = CLEANUP_ALL;
+		cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
 	else if (!strcmp(cleanup_arg, "scissors"))
-		cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+					    COMMIT_MSG_CLEANUP_SPACE;
 	else
 		die(_("Invalid cleanup mode %s"), cleanup_arg);
 
@@ -1768,17 +1707,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	}
 
 	if (verbose || /* Truncate the message just before the diff, if any. */
-	    cleanup_mode == CLEANUP_SCISSORS)
+	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 		strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-	if (cleanup_mode != CLEANUP_NONE)
-		strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 
-	if (message_is_empty(&sb) && !allow_empty_message) {
+	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
 		exit(1);
 	}
-	if (template_untouched(&sb) && !allow_empty_message) {
+	if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
 		fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
 		exit(1);
diff --git a/sequencer.c b/sequencer.c
index fa94ed652d2ce87fddf824aa516456b5f42735e7..168da5093e71f50a4d70af7288cf761110e69e87 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -692,6 +692,67 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
+static int rest_is_empty(const struct strbuf *sb, int start)
+{
+	int i, eol;
+	const char *nl;
+
+	/* Check if the rest is just whitespace and Signed-off-by's. */
+	for (i = start; i < sb->len; i++) {
+		nl = memchr(sb->buf + i, '\n', sb->len - i);
+		if (nl)
+			eol = nl - sb->buf;
+		else
+			eol = sb->len;
+
+		if (strlen(sign_off_header) <= eol - i &&
+		    starts_with(sb->buf + i, sign_off_header)) {
+			i = eol;
+			continue;
+		}
+		while (i < eol)
+			if (!isspace(sb->buf[i++]))
+				return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode)
+{
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+	return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode)
+{
+	struct strbuf tmpl = STRBUF_INIT;
+	const char *start;
+
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_NONE && sb->len)
+		return 0;
+
+	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+		return 0;
+
+	strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	if (!skip_prefix(sb->buf, tmpl.buf, &start))
+		start = sb->buf;
+	strbuf_release(&tmpl);
+	return rest_is_empty(sb, start - sb->buf);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 6f3d3df82c0ade64b7b125acd49bf3f5e15c53af..2040773c7b6fd3966d5a1a14f410ea8ff53843c8 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -11,6 +11,13 @@ enum replay_action {
 	REPLAY_INTERACTIVE_REBASE
 };
 
+enum commit_msg_cleanup_mode {
+	COMMIT_MSG_CLEANUP_SPACE,
+	COMMIT_MSG_CLEANUP_NONE,
+	COMMIT_MSG_CLEANUP_SCISSORS,
+	COMMIT_MSG_CLEANUP_ALL
+};
+
 struct replay_opts {
 	enum replay_action action;
 
@@ -57,5 +64,8 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
-
+int message_is_empty(const struct strbuf *sb,
+		     enum commit_msg_cleanup_mode cleanup_mode);
+int template_untouched(const struct strbuf *sb, const char *template_file,
+		       enum commit_msg_cleanup_mode cleanup_mode);
 #endif
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 3/9] Add a function to update HEAD after creating a commit
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 1/9] t3404: check intermediate squash messages Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 2/9] commit: move empty message checks to libgit Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 4/9] commit: move post-rewrite code to libgit Phillip Wood
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add update_head_with_reflog() based on the code that updates HEAD
after committing in builtin/commit.c that can be called by 'git
commit' and other commands.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - updated commit message to reflect the change in function name
     - style fixes
    
    changes since v1:
     - rename update_head() to update_head_with_reflog()

 builtin/commit.c | 20 ++------------------
 sequencer.c      | 39 ++++++++++++++++++++++++++++++++++++++-
 sequencer.h      |  4 ++++
 3 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 2175dac8036c465a73c4c782f061e85ae6d1a629..340cc2988ebdb92ef222b677ee12c94fa53aa1ff 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1610,13 +1610,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf author_ident = STRBUF_INIT;
 	const char *index_file, *reflog_msg;
-	char *nl;
 	struct object_id oid;
 	struct commit_list *parents = NULL;
 	struct stat statbuf;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
-	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1739,25 +1737,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_release(&author_ident);
 	free_commit_extra_headers(extra);
 
-	nl = strchr(sb.buf, '\n');
-	if (nl)
-		strbuf_setlen(&sb, nl + 1 - sb.buf);
-	else
-		strbuf_addch(&sb, '\n');
-	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, "HEAD", &oid,
-				   current_head
-				   ? &current_head->object.oid : &null_oid,
-				   0, sb.buf, &err) ||
-	    ref_transaction_commit(transaction, &err)) {
+	if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+				    &err)) {
 		rollback_index_files();
 		die("%s", err.buf);
 	}
-	ref_transaction_free(transaction);
 
 	unlink(git_path_cherry_pick_head());
 	unlink(git_path_revert_head());
diff --git a/sequencer.c b/sequencer.c
index 168da5093e71f50a4d70af7288cf761110e69e87..5fe6ef3512566622f0423a09cbffe1adf1e65957 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,10 +1,10 @@
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
-#include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #include "commit.h"
+#include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
 #include "exec_cmd.h"
@@ -753,6 +753,43 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
 	return rest_is_empty(sb, start - sb->buf);
 }
 
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err)
+{
+	struct ref_transaction *transaction;
+	struct strbuf sb = STRBUF_INIT;
+	const char *nl;
+	int ret = 0;
+
+	if (action) {
+		strbuf_addstr(&sb, action);
+		strbuf_addstr(&sb, ": ");
+	}
+
+	nl = strchr(msg->buf, '\n');
+	if (nl) {
+		strbuf_add(&sb, msg->buf, nl + 1 - msg->buf);
+	} else {
+		strbuf_addbuf(&sb, msg);
+		strbuf_addch(&sb, '\n');
+	}
+
+	transaction = ref_transaction_begin(err);
+	if (!transaction ||
+	    ref_transaction_update(transaction, "HEAD", new_head,
+				   old_head ? &old_head->object.oid : &null_oid,
+				   0, sb.buf, err) ||
+	    ref_transaction_commit(transaction, err)) {
+		ret = -1;
+	}
+	ref_transaction_free(transaction);
+	strbuf_release(&sb);
+
+	return ret;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 2040773c7b6fd3966d5a1a14f410ea8ff53843c8..3757a7aecb5a7795d7b9b45964f3328ee852e14b 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -68,4 +68,8 @@ int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
 		       enum commit_msg_cleanup_mode cleanup_mode);
+int update_head_with_reflog(const struct commit *old_head,
+			    const struct object_id *new_head,
+			    const char *action, const struct strbuf *msg,
+			    struct strbuf *err);
 #endif
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 4/9] commit: move post-rewrite code to libgit
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (2 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 3/9] Add a function to update HEAD after creating a commit Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 5/9] commit: move print_commit_summary() " Phillip Wood
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move run_rewrite_hook() from bulitin/commit.c to sequencer.c so it can
be shared with other commands and add a new function
commit_post_rewrite() based on the code in builtin/commit.c that
encapsulates rewriting notes and running the post-rewrite hook. Once
the sequencer learns how to create commits without forking 'git
commit' these functions will be used when squashing commits.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v1:
     - reword commit message to explain why the code is being moved

 builtin/commit.c | 42 +-----------------------------------------
 sequencer.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 340cc2988ebdb92ef222b677ee12c94fa53aa1ff..d376d282cda95d491f2fcbee5063709d02aed77a 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [<options>] [--] <pathspec>..."),
@@ -1497,37 +1495,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 	return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-			    const struct object_id *newoid)
-{
-	struct child_process proc = CHILD_PROCESS_INIT;
-	const char *argv[3];
-	int code;
-	struct strbuf sb = STRBUF_INIT;
-
-	argv[0] = find_hook("post-rewrite");
-	if (!argv[0])
-		return 0;
-
-	argv[1] = "amend";
-	argv[2] = NULL;
-
-	proc.argv = argv;
-	proc.in = -1;
-	proc.stdout_to_stderr = 1;
-
-	code = start_command(&proc);
-	if (code)
-		return code;
-	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-	sigchain_push(SIGPIPE, SIG_IGN);
-	write_in_full(proc.in, sb.buf, sb.len);
-	close(proc.in);
-	strbuf_release(&sb);
-	sigchain_pop(SIGPIPE);
-	return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
 	struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1758,14 +1725,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	rerere(0);
 	run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
 	if (amend && !no_post_rewrite) {
-		struct notes_rewrite_cfg *cfg;
-		cfg = init_copy_notes_for_rewrite("amend");
-		if (cfg) {
-			/* we are amending, so current_head is not NULL */
-			copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-			finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-		}
-		run_rewrite_hook(&current_head->object.oid, &oid);
+		commit_post_rewrite(current_head, &oid);
 	}
 	if (!quiet)
 		print_summary(prefix, &oid, !current_head);
diff --git a/sequencer.c b/sequencer.c
index 5fe6ef3512566622f0423a09cbffe1adf1e65957..132319be0dbc15ed34f87af769235a4c6f3c6159 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -21,6 +21,8 @@
 #include "log-tree.h"
 #include "wt-status.h"
 #include "hashmap.h"
+#include "notes-utils.h"
+#include "sigchain.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -790,6 +792,51 @@ int update_head_with_reflog(const struct commit *old_head,
 	return ret;
 }
 
+static int run_rewrite_hook(const struct object_id *oldoid,
+			    const struct object_id *newoid)
+{
+	struct child_process proc = CHILD_PROCESS_INIT;
+	const char *argv[3];
+	int code;
+	struct strbuf sb = STRBUF_INIT;
+
+	argv[0] = find_hook("post-rewrite");
+	if (!argv[0])
+		return 0;
+
+	argv[1] = "amend";
+	argv[2] = NULL;
+
+	proc.argv = argv;
+	proc.in = -1;
+	proc.stdout_to_stderr = 1;
+
+	code = start_command(&proc);
+	if (code)
+		return code;
+	strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
+	sigchain_push(SIGPIPE, SIG_IGN);
+	write_in_full(proc.in, sb.buf, sb.len);
+	close(proc.in);
+	strbuf_release(&sb);
+	sigchain_pop(SIGPIPE);
+	return finish_command(&proc);
+}
+
+void commit_post_rewrite(const struct commit *old_head,
+			 const struct object_id *new_head)
+{
+	struct notes_rewrite_cfg *cfg;
+
+	cfg = init_copy_notes_for_rewrite("amend");
+	if (cfg) {
+		/* we are amending, so old_head is not NULL */
+		copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
+		finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+	}
+	run_rewrite_hook(&old_head->object.oid, new_head);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index 3757a7aecb5a7795d7b9b45964f3328ee852e14b..e97fb288498bf524e43db4217087cb66682fad52 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -72,4 +72,6 @@ int update_head_with_reflog(const struct commit *old_head,
 			    const struct object_id *new_head,
 			    const char *action, const struct strbuf *msg,
 			    struct strbuf *err);
+void commit_post_rewrite(const struct commit *current_head,
+			 const struct object_id *new_head);
 #endif
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 5/9] commit: move print_commit_summary() to libgit
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (3 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 4/9] commit: move post-rewrite code to libgit Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Move print_commit_summary() from builtin/commit.c to sequencer.c so it
can be shared with other commands. The function is modified by
changing the last argument to a flag so callers can specify whether
they want to show the author date in addition to specifying if this is
an initial commit.

If the sequencer dies in print_commit_summary() (which can only happen
when cherry-picking or reverting) then neither the todo list nor the
abort safety file are updated to reflect the commit that was just
made. print_commit_summary() can die if:

 - The commit that was just created cannot be found or parsed.

 - HEAD cannot be resolved either because some other process is
   updating it (which is bad news in the middle of a cherry-pick) or
   because it is corrupt.

 - log_tree_commit() cannot read some objects.

In all those cases dying will leave the sequencer in a sane state for
aborting; 'git cherry-pick --abort' will rewind HEAD to the last
successful commit before there was a problem with HEAD or the object
database. If the user somehow fixes the problem and runs 'git
cherry-pick --continue' then the sequencer will try and pick the same
commit again which may or may not be what the user wants depending on
what caused print_commit_summary() to die. If print_commit_summary()
returned an error instead then update_abort_safety_file() would try to
resolve HEAD which may or may not be successful. If it is successful
then running 'git rebase --abort' would not rewind HEAD to the last
successful commit which is not what we want.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v2:
     - expanded commit message to explain why it is ok to die in
       print_commit_summary() and dropped the next patch which made it
       return an error instead.
     - style fixes.
    
    changes since v1:
     - convert flags passed to print_commit_summary() to unsigned int

 builtin/commit.c | 128 ++++---------------------------------------------------
 sequencer.c      | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |   5 +++
 3 files changed, 133 insertions(+), 119 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index d376d282cda95d491f2fcbee5063709d02aed77a..0f78958c89cdd91c54f0154102b08e1a8121cce0 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -43,31 +43,6 @@ static const char * const builtin_status_usage[] = {
 	NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -1374,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-	char *user_config = expand_user_path("~/.gitconfig", 0);
-	char *xdg_config = xdg_config_home("config");
-	int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-	free(user_config);
-	free(xdg_config);
-
-	if (config_exists)
-		return _(implicit_ident_advice_config);
-	else
-		return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-			  int initial_commit)
-{
-	struct rev_info rev;
-	struct commit *commit;
-	struct strbuf format = STRBUF_INIT;
-	const char *head;
-	struct pretty_print_context pctx = {0};
-	struct strbuf author_ident = STRBUF_INIT;
-	struct strbuf committer_ident = STRBUF_INIT;
-
-	commit = lookup_commit(oid);
-	if (!commit)
-		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
-		die(_("could not parse newly created commit"));
-
-	strbuf_addstr(&format, "format:%h] %s");
-
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-	if (strbuf_cmp(&author_ident, &committer_ident)) {
-		strbuf_addstr(&format, "\n Author: ");
-		strbuf_addbuf_percentquote(&format, &author_ident);
-	}
-	if (author_date_is_interesting()) {
-		struct strbuf date = STRBUF_INIT;
-		format_commit_message(commit, "%ad", &date, &pctx);
-		strbuf_addstr(&format, "\n Date: ");
-		strbuf_addbuf_percentquote(&format, &date);
-		strbuf_release(&date);
-	}
-	if (!committer_ident_sufficiently_given()) {
-		strbuf_addstr(&format, "\n Committer: ");
-		strbuf_addbuf_percentquote(&format, &committer_ident);
-		if (advice_implicit_identity) {
-			strbuf_addch(&format, '\n');
-			strbuf_addstr(&format, implicit_ident_advice());
-		}
-	}
-	strbuf_release(&author_ident);
-	strbuf_release(&committer_ident);
-
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-
-	rev.diff = 1;
-	rev.diffopt.output_format =
-		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-	rev.verbose_header = 1;
-	rev.show_root_diff = 1;
-	get_commit_format(format.buf, &rev);
-	rev.always_show_header = 0;
-	rev.diffopt.detect_rename = 1;
-	rev.diffopt.break_opt = 0;
-	diff_setup_done(&rev.diffopt);
-
-	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-	if (!head)
-		die_errno(_("unable to resolve HEAD after creating commit"));
-	if (!strcmp(head, "HEAD"))
-		head = _("detached HEAD");
-	else
-		skip_prefix(head, "refs/heads/", &head);
-	printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-	if (!log_tree_commit(&rev, commit)) {
-		rev.always_show_header = 1;
-		rev.use_terminator = 1;
-		log_tree_commit(&rev, commit);
-	}
-
-	strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
 	struct wt_status *s = cb;
@@ -1727,8 +1610,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	if (amend && !no_post_rewrite) {
 		commit_post_rewrite(current_head, &oid);
 	}
-	if (!quiet)
-		print_summary(prefix, &oid, !current_head);
+	if (!quiet) {
+		unsigned int flags = 0;
+
+		if (!current_head)
+			flags |= SUMMARY_INITIAL_COMMIT;
+		if (author_date_is_interesting())
+			flags |= SUMMARY_SHOW_AUTHOR_DATE;
+		print_commit_summary(prefix, &oid, flags);
+	}
 
 	UNLEAK(err);
 	UNLEAK(sb);
diff --git a/sequencer.c b/sequencer.c
index 132319be0dbc15ed34f87af769235a4c6f3c6159..b4ff2a4a973b2733cca7bb65fcb7947cb8d08988 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -837,6 +837,125 @@ void commit_post_rewrite(const struct commit *old_head,
 	run_rewrite_hook(&old_head->object.oid, new_head);
 }
 
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n");
+
+static const char *implicit_ident_advice(void)
+{
+	char *user_config = expand_user_path("~/.gitconfig", 0);
+	char *xdg_config = xdg_config_home("config");
+	int config_exists = file_exists(user_config) || file_exists(xdg_config);
+
+	free(user_config);
+	free(xdg_config);
+
+	if (config_exists)
+		return _(implicit_ident_advice_config);
+	else
+		return _(implicit_ident_advice_noconfig);
+
+}
+
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf format = STRBUF_INIT;
+	const char *head;
+	struct pretty_print_context pctx = {0};
+	struct strbuf author_ident = STRBUF_INIT;
+	struct strbuf committer_ident = STRBUF_INIT;
+
+	commit = lookup_commit(oid);
+	if (!commit)
+		die(_("couldn't look up newly created commit"));
+	if (parse_commit(commit))
+		die(_("could not parse newly created commit"));
+
+	strbuf_addstr(&format, "format:%h] %s");
+
+	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
+	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	if (strbuf_cmp(&author_ident, &committer_ident)) {
+		strbuf_addstr(&format, "\n Author: ");
+		strbuf_addbuf_percentquote(&format, &author_ident);
+	}
+	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
+		struct strbuf date = STRBUF_INIT;
+
+		format_commit_message(commit, "%ad", &date, &pctx);
+		strbuf_addstr(&format, "\n Date: ");
+		strbuf_addbuf_percentquote(&format, &date);
+		strbuf_release(&date);
+	}
+	if (!committer_ident_sufficiently_given()) {
+		strbuf_addstr(&format, "\n Committer: ");
+		strbuf_addbuf_percentquote(&format, &committer_ident);
+		if (advice_implicit_identity) {
+			strbuf_addch(&format, '\n');
+			strbuf_addstr(&format, implicit_ident_advice());
+		}
+	}
+	strbuf_release(&author_ident);
+	strbuf_release(&committer_ident);
+
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+
+	rev.diff = 1;
+	rev.diffopt.output_format =
+		DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+	rev.verbose_header = 1;
+	rev.show_root_diff = 1;
+	get_commit_format(format.buf, &rev);
+	rev.always_show_header = 0;
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.break_opt = 0;
+	diff_setup_done(&rev.diffopt);
+
+	head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+	if (!head)
+		die_errno(_("unable to resolve HEAD after creating commit"));
+	if (!strcmp(head, "HEAD"))
+		head = _("detached HEAD");
+	else
+		skip_prefix(head, "refs/heads/", &head);
+	printf("[%s%s ", head, (flags & SUMMARY_INITIAL_COMMIT) ?
+						_(" (root-commit)") : "");
+
+	if (!log_tree_commit(&rev, commit)) {
+		rev.always_show_header = 1;
+		rev.use_terminator = 1;
+		log_tree_commit(&rev, commit);
+	}
+
+	strbuf_release(&format);
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
diff --git a/sequencer.h b/sequencer.h
index e97fb288498bf524e43db4217087cb66682fad52..bf72e339adbb81900283d8811ed51569aa3e05ee 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -74,4 +74,9 @@ int update_head_with_reflog(const struct commit *old_head,
 			    struct strbuf *err);
 void commit_post_rewrite(const struct commit *current_head,
 			 const struct object_id *new_head);
+
+#define SUMMARY_INITIAL_COMMIT   (1 << 0)
+#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
+void print_commit_summary(const char *prefix, const struct object_id *oid,
+			  unsigned int flags);
 #endif
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 6/9] sequencer: simplify adding Signed-off-by: trailer
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (4 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 5/9] commit: move print_commit_summary() " Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 7/9] sequencer: load commit related config Phillip Wood
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Add the Signed-off-by: trailer in one place rather than adding it to
the message when doing a recursive merge and specifying '--signoff'
when running 'git commit'. This means that if there are conflicts when
merging with a strategy other than 'recursive' the Signed-off-by:
trailer will be added if the user commits the resolution themselves
without passing '--signoff' to 'git commit'. It also simplifies the
in-process commit that is about to be added to the sequencer.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 sequencer.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index b4ff2a4a973b2733cca7bb65fcb7947cb8d08988..4966dd1b9359aaa82064608c05a7f5b18cea2d7a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -478,9 +478,6 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 			_(action_name(opts)));
 	rollback_lock_file(&index_lock);
 
-	if (opts->signoff)
-		append_signoff(msgbuf, 0, 0);
-
 	if (!clean)
 		append_conflicts_hint(msgbuf);
 
@@ -658,8 +655,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
-	if (opts->signoff)
-		argv_array_push(&cmd.args, "-s");
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	if ((flags & CLEANUP_MSG))
@@ -1348,6 +1343,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		}
 	}
 
+	if (opts->signoff)
+		append_signoff(&msgbuf, 0, 0);
+
 	if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
 		res = -1;
 	else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 7/9] sequencer: load commit related config
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (5 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 18:53     ` Phillip Wood
  2017-12-11 14:13   ` [PATCH v5 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
                     ` (2 subsequent siblings)
  9 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Load default values for message cleanup, gpg signing of commits and
basic diff configuration in preparation for committing without forking
'git commit'. Note that we interpret commit.cleanup=scissors to mean
COMMIT_MSG_CLEANUP_SPACE to be consistent with 'git commit'.

The sequencer should probably have been calling
git_diff_basic_config() before as it creates a patch when there are
conflicts. The shell version uses 'diff-index' to create the patch so
calling git_diff_basic_config() should match that. Although 'git
commit' calls git_diff_ui_config() I don't think the output of
print_commit_summary() is affected by anything that is loaded by that
as print_commit_summary() always turns on rename detection so would
ignore the value in the user's configuration anyway. The other values
loaded by git_diff_ui_config() are about the formatting of patches so
are not relevant to print_commit_summary().

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v4:
     - reworked config handling to call git_diff_basic_config() and store
       defaults in struct replay_opts rather than using global variables.
     - added a warning if there is an invalid value for commit.cleanup.
    
    changes since v3:
     - interpret commit.cleanup=scissors to mean COMMIT_MSG_CLEANUP_SPACE
       to match 'git commit'
    
    changes since v1:
     - renamed git_revert_config() to common_config()
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 builtin/rebase--helper.c |  2 +-
 builtin/revert.c         |  4 ++--
 sequencer.c              | 45 +++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h              |  3 +++
 4 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f8519363a393862b6857acab037e74367c7f2134..decb8f7a09e42eb94bed264164985e54e13a32f6 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -39,7 +39,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	sequencer_init_config(&opts);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index b9d927eb09c9ed87c84681df1396f4e6d9b13c97..76f0a35b074b858ab4cb3e3894bc7c877401b7e8 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -208,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(git_default_config, NULL);
+	sequencer_init_config(&opts);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -221,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(git_default_config, NULL);
+	sequencer_init_config(&opts);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 4966dd1b9359aaa82064608c05a7f5b18cea2d7a..3ce1e5b71474f1cd25b232a319fb7b0e13dc6e14 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -132,6 +132,51 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 
+static int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	struct replay_opts *opts = cb;
+	int status;
+
+	if (!strcmp(k, "commit.cleanup")) {
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else
+			warning(_("invalid commit message cleanup mode '%s'"),
+				  s);
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		opts->gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_diff_basic_config(k, v, NULL);
+}
+
+void sequencer_init_config(struct replay_opts *opts)
+{
+	opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+	git_config(git_sequencer_config, opts);
+}
+
 static inline int is_rebase_i(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_INTERACTIVE_REBASE;
diff --git a/sequencer.h b/sequencer.h
index bf72e339adbb81900283d8811ed51569aa3e05ee..3a5072c2ab9088c237b83d92deae3c801289e543 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -36,6 +36,7 @@ struct replay_opts {
 	int mainline;
 
 	char *gpg_sign;
+	enum commit_msg_cleanup_mode default_msg_cleanup;
 
 	/* Merge strategy */
 	char *strategy;
@@ -47,6 +48,8 @@ struct replay_opts {
 };
 #define REPLAY_OPTS_INIT { -1 }
 
+/* Call this to setup defaults before parsing command line options */
+void sequencer_init_config(struct replay_opts *opts);
 int sequencer_pick_revisions(struct replay_opts *opts);
 int sequencer_continue(struct replay_opts *opts);
 int sequencer_rollback(struct replay_opts *opts);
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (6 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 7/9] sequencer: load commit related config Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2018-01-10 20:53     ` Jonathan Nieder
  2017-12-11 14:13   ` [PATCH v5 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
  2017-12-11 23:44   ` [PATCH v5 0/9] sequencer: don't fork git commit Junio C Hamano
  9 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If the commit message does not need to be edited then create the
commit without forking 'git commit'. Taking the best time of ten runs
with a warm cache this reduces the time taken to cherry-pick 10
commits by 27% (from 282ms to 204ms), and the time taken by 'git
rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
my computer running linux. Some of greater saving for rebase is
because it no longer wastes time creating the commit summary just to
throw it away.

The code to create the commit is based on builtin/commit.c. It is
simplified as it doesn't have to deal with merges and modified so that
it does not die but returns an error to make sure the sequencer exits
cleanly, as it would when forking 'git commit'

Even when not forking 'git commit' the commit message is written to a
file and CHERRY_PICK_HEAD is created unnecessarily. This could be
eliminated in future. I hacked up a version that does not write these
files and just passed an strbuf (with the wrong message for fixup and
squash commands) to do_commit() but I couldn't measure any significant
time difference when running cherry-pick or rebase. I think
eliminating the writes properly for rebase would require a bit of
effort as the code would need to be restructured.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    changes since v4:
     - changed cleanup and gpg handling to reflect the changes in the last patch
    
    changes since v3:
     - take account of change print_commit_summary() return type after
       dropping the patch that made it return an error instead of dying.
    
    changes since v2:
     - style fixes
    
    changes since v1:
     - added comments to explain return value of try_to_commit()
     - removed unnecessary NULL tests before calling free()
     - style cleanups
     - corrected commit message
     - prefixed cleanup_mode constants to reflect the changes to patch 2
       in this series

 sequencer.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 174 insertions(+), 2 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 3ce1e5b71474f1cd25b232a319fb7b0e13dc6e14..74770bd00cc3840573057a1868e0a3acb05a71bb 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -638,6 +638,18 @@ static int read_env_script(struct argv_array *env)
 	return 0;
 }
 
+static char *get_author(const char *message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
 static const char staged_changes_advice[] =
 N_("you have staged changes in your working tree\n"
 "If these changes are meant to be squashed into the previous commit, run:\n"
@@ -996,6 +1008,158 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
 	strbuf_release(&format);
 }
 
+static int parse_head(struct commit **head)
+{
+	struct commit *current_head;
+	struct object_id oid;
+
+	if (get_oid("HEAD", &oid)) {
+		current_head = NULL;
+	} else {
+		current_head = lookup_commit_reference(&oid);
+		if (!current_head)
+			return error(_("could not parse HEAD"));
+		if (oidcmp(&oid, &current_head->object.oid)) {
+			warning(_("HEAD %s is not a commit!"),
+				oid_to_hex(&oid));
+		}
+		if (parse_commit(current_head))
+			return error(_("could not parse HEAD commit"));
+	}
+	*head = current_head;
+
+	return 0;
+}
+
+/*
+ * Try to commit without forking 'git commit'. In some cases we need
+ * to run 'git commit' to display an error message
+ *
+ * Returns:
+ *  -1 - error unable to commit
+ *   0 - success
+ *   1 - run 'git commit'
+ */
+static int try_to_commit(struct strbuf *msg, const char *author,
+			 struct replay_opts *opts, unsigned int flags,
+			 struct object_id *oid)
+{
+	struct object_id tree;
+	struct commit *current_head;
+	struct commit_list *parents = NULL;
+	struct commit_extra_header *extra = NULL;
+	struct strbuf err = STRBUF_INIT;
+	struct strbuf amend_msg = STRBUF_INIT;
+	char *amend_author = NULL;
+	enum commit_msg_cleanup_mode cleanup;
+	int res = 0;
+
+	if (parse_head(&current_head))
+		return -1;
+
+	if (flags & AMEND_MSG) {
+		const char *exclude_gpgsig[] = { "gpgsig", NULL };
+		const char *out_enc = get_commit_output_encoding();
+		const char *message = logmsg_reencode(current_head, NULL,
+						      out_enc);
+
+		if (!msg) {
+			const char *orig_message = NULL;
+
+			find_commit_subject(message, &orig_message);
+			msg = &amend_msg;
+			strbuf_addstr(msg, orig_message);
+		}
+		author = amend_author = get_author(message);
+		unuse_commit_buffer(current_head, message);
+		if (!author) {
+			res = error(_("unable to parse commit author"));
+			goto out;
+		}
+		parents = copy_commit_list(current_head->parents);
+		extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+	} else if (current_head) {
+		commit_list_insert(current_head, &parents);
+	}
+
+	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
+					  opts->default_msg_cleanup;
+
+	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	if (write_cache_as_tree(tree.hash, 0, NULL)) {
+		res = error(_("git write-tree failed to write a tree"));
+		goto out;
+	}
+
+	if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ?
+					      &current_head->tree->object.oid :
+					      &empty_tree_oid, &tree)) {
+		res = 1; /* run 'git commit' to display error message */
+		goto out;
+	}
+
+	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
+				 oid->hash, author, opts->gpg_sign, extra)) {
+		res = error(_("failed to write commit object"));
+		goto out;
+	}
+
+	if (update_head_with_reflog(current_head, oid,
+				    getenv("GIT_REFLOG_ACTION"), msg, &err)) {
+		res = error("%s", err.buf);
+		goto out;
+	}
+
+	if (flags & AMEND_MSG)
+		commit_post_rewrite(current_head, oid);
+
+out:
+	free_commit_extra_headers(extra);
+	strbuf_release(&err);
+	strbuf_release(&amend_msg);
+	free(amend_author);
+
+	return res;
+}
+
+static int do_commit(const char *msg_file, const char *author,
+		     struct replay_opts *opts, unsigned int flags)
+{
+	int res = 1;
+
+	if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
+		struct object_id oid;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (msg_file && strbuf_read_file(&sb, msg_file, 2048) < 0)
+			return error_errno(_("unable to read commit message "
+					     "from '%s'"),
+					   msg_file);
+
+		res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags,
+				    &oid);
+		strbuf_release(&sb);
+		if (!res) {
+			unlink(git_path_cherry_pick_head());
+			unlink(git_path_merge_msg());
+			if (!is_rebase_i(opts))
+				print_commit_summary(NULL, &oid,
+						SUMMARY_SHOW_AUTHOR_DATE);
+			return res;
+		}
+	}
+	if (res == 1)
+		return run_git_commit(msg_file, opts, flags);
+
+	return res;
+}
+
 static int is_original_commit_empty(struct commit *commit)
 {
 	const struct object_id *ptree_oid;
@@ -1247,6 +1411,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 	struct object_id head;
 	struct commit *base, *next, *parent;
 	const char *base_label, *next_label;
+	char *author = NULL;
 	struct commit_message msg = { NULL, NULL, NULL, NULL };
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res, unborn = 0, allow;
@@ -1363,6 +1528,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
+		if (!is_fixup(command))
+			author = get_author(msg.message);
 	}
 
 	if (command == TODO_REWORD)
@@ -1448,9 +1615,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 		goto leave;
 	} else if (allow)
 		flags |= ALLOW_EMPTY;
-	if (!opts->no_commit)
+	if (!opts->no_commit) {
 fast_forward_edit:
-		res = run_git_commit(msg_file, opts, flags);
+		if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+			res = do_commit(msg_file, author, opts, flags);
+		else
+			res = error(_("unable to parse commit author"));
+	}
 
 	if (!res && final_fixup) {
 		unlink(rebase_path_fixup_msg());
@@ -1459,6 +1630,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
 	free_message(commit, &msg);
+	free(author);
 	update_abort_safety_file();
 
 	return res;
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* [PATCH v5 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (7 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2017-12-11 14:13   ` Phillip Wood
  2017-12-11 23:44   ` [PATCH v5 0/9] sequencer: don't fork git commit Junio C Hamano
  9 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 14:13 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Now that the sequencer creates commits without forking 'git commit' it
does not see an empty commit in these tests which fixes the known
breakage. Note that logic for handling
KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 is not removed from
lib-submodule-update.sh as it is still used by other tests.

Reviewed-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Notes:
    Changes since v4
     - Added reviewed-by trailer

 t/t3512-cherry-pick-submodule.sh | 1 -
 t/t3513-revert-submodule.sh      | 1 -
 2 files changed, 2 deletions(-)

diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index ce48c4fcca80b183927292cc1e5902cfe286f994..bd78287841ee053fd56a44a268f8077a222cc266 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -5,7 +5,6 @@ test_description='cherry-pick can handle submodules'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-submodule-update.sh
 
-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
 KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
 KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
 test_submodule_switch "git cherry-pick"
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index db9378142a93338d2988f40e2748bc476490bcd5..5e39fcdb66c0c7c4b112c1bbe941d886db237693 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -25,7 +25,6 @@ git_revert () {
 	git revert HEAD
 }
 
-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
 KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
 test_submodule_switch "git_revert"
 
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 7/9] sequencer: load commit related config
  2017-12-11 14:13   ` [PATCH v5 7/9] sequencer: load commit related config Phillip Wood
@ 2017-12-11 18:53     ` Phillip Wood
  0 siblings, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-11 18:53 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Johannes Schindelin, Junio C Hamano, Ramsay Jones, Adam Dinwoodie,
	Stefan Beller, Phillip Wood

On 11/12/17 14:13, Phillip Wood wrote:
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
> 
> Load default values for message cleanup, gpg signing of commits and
> basic diff configuration in preparation for committing without forking
> 'git commit'. Note that we interpret commit.cleanup=scissors to mean
> COMMIT_MSG_CLEANUP_SPACE to be consistent with 'git commit'.
> 
> The sequencer should probably have been calling
> git_diff_basic_config() before as it creates a patch when there are
> conflicts. The shell version uses 'diff-index' to create the patch so

s/diff-index/diff-tree/

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 0/9] sequencer: don't fork git commit
  2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
                     ` (8 preceding siblings ...)
  2017-12-11 14:13   ` [PATCH v5 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
@ 2017-12-11 23:44   ` Junio C Hamano
  2017-12-12 10:32     ` Phillip Wood
  2017-12-13 11:46     ` [PATCH] sequencer: improve config handling Phillip Wood
  9 siblings, 2 replies; 120+ messages in thread
From: Junio C Hamano @ 2017-12-11 23:44 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

Phillip Wood <phillip.wood@talktalk.net> writes:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> I've reworked the config handling since v4. It now stores the default
> values in struct replay_opt rather than using global variables and
> calls git_diff_basic_config(). Unfortunately I've not had time to
> modify git_gpg_config() to indicate if it successfully handled the key
> so git_diff_basic_config() is called unnecessarily in that case. Within
> git_diff_basic_config() userdiff_config() also suffers from the same
> problem of not indicating if it has handled the key.

Ouch.  I thought we agreed that we were ready to go incremental and
the topic was merged to 'next' earlier last week.

After scanning the difference between the two rounds, it seems that
the more important difference is the rework of the configuration,
which looks better thought out than the previous round, and with
associated change to use replay_opts fields instead of free variables
to carry gpg-sign and cleanup-mode settings around, which also looks
sensible to me.

Can you make these differences into incremental "that earlier one
was suboptimal for this and that reasons, let's make it better by
doing this" patches to queue them on top?

Thanks.

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 0/9] sequencer: don't fork git commit
  2017-12-11 23:44   ` [PATCH v5 0/9] sequencer: don't fork git commit Junio C Hamano
@ 2017-12-12 10:32     ` Phillip Wood
  2017-12-13 11:46     ` [PATCH] sequencer: improve config handling Phillip Wood
  1 sibling, 0 replies; 120+ messages in thread
From: Phillip Wood @ 2017-12-12 10:32 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Johannes Schindelin, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Phillip Wood

On 11/12/17 23:44, Junio C Hamano wrote:
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> I've reworked the config handling since v4. It now stores the default
>> values in struct replay_opt rather than using global variables and
>> calls git_diff_basic_config(). Unfortunately I've not had time to
>> modify git_gpg_config() to indicate if it successfully handled the key
>> so git_diff_basic_config() is called unnecessarily in that case. Within
>> git_diff_basic_config() userdiff_config() also suffers from the same
>> problem of not indicating if it has handled the key.
> 
> Ouch.  I thought we agreed that we were ready to go incremental and
> the topic was merged to 'next' earlier last week.

Sorry, I thought we were waiting, not to worry.
> 
> After scanning the difference between the two rounds, it seems that
> the more important difference is the rework of the configuration,
> which looks better thought out than the previous round, and with
> associated change to use replay_opts fields instead of free variables
> to carry gpg-sign and cleanup-mode settings around, which also looks
> sensible to me.

Yes the configuration is the major change, I'm glad you think it is an
improvement

> Can you make these differences into incremental "that earlier one
> was suboptimal for this and that reasons, let's make it better by
> doing this" patches to queue them on top?

Will do, though if I don't get round to it today or tomorrow it may be
the New Year before I send them, I hope that's OK

Best Wishes

Phillip

> Thanks.
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* [PATCH] sequencer: improve config handling
  2017-12-11 23:44   ` [PATCH v5 0/9] sequencer: don't fork git commit Junio C Hamano
  2017-12-12 10:32     ` Phillip Wood
@ 2017-12-13 11:46     ` Phillip Wood
  2017-12-20 18:33       ` Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling) Kaartic Sivaraam
  1 sibling, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2017-12-13 11:46 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano, Phillip Wood

From: Phillip Wood <phillip.wood@dunelm.org.uk>

The previous config handling relied on global variables, called
git_default_config() even when the key had already been handled by
git_sequencer_config() and did not initialize the diff configuration
variables. Improve this by: i) loading the default values for message
cleanup and gpg signing of commits into struct replay_opts;
ii) restructuring the code to return immediately once a key is
handled; and iii) calling git_diff_basic_config(). Note that
unfortunately it is not possible to return early if the key is handled
by git_gpg_config() as it does not indicate to the caller if the key
has been handled or not.

The sequencer should probably have been calling
git_diff_basic_config() before as it creates a patch when there are
conflicts. The shell version uses 'diff-tree' to create the patch so
calling git_diff_basic_config() should match that. Although 'git
commit' calls git_diff_ui_config() I don't think the output of
print_commit_summary() is affected by anything that is loaded by that
as print_commit_summary() always turns on rename detection so would
ignore the value in the user's configuration anyway. The other values
loaded by git_diff_ui_config() are about the formatting of patches so
are not relevant to print_commit_summary().

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 builtin/rebase--helper.c | 13 +-------
 builtin/revert.c         | 15 ++-------
 sequencer.c              | 87 ++++++++++++++++++++++++++----------------------
 sequencer.h              | 19 ++++++-----
 4 files changed, 61 insertions(+), 73 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 68194d3aed950f327a8bc624fa1991478dfea01e..decb8f7a09e42eb94bed264164985e54e13a32f6 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -9,17 +9,6 @@ static const char * const builtin_rebase_helper_usage[] = {
 	NULL
 };
 
-static int git_rebase_helper_config(const char *k, const char *v, void *cb)
-{
-	int status;
-
-	status = git_sequencer_config(k, v, NULL);
-	if (status)
-		return status;
-
-	return git_default_config(k, v, NULL);
-}
-
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
 	struct replay_opts opts = REPLAY_OPTS_INIT;
@@ -50,7 +39,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_rebase_helper_config, NULL);
+	sequencer_init_config(&opts);
 
 	opts.action = REPLAY_INTERACTIVE_REBASE;
 	opts.allow_ff = 1;
diff --git a/builtin/revert.c b/builtin/revert.c
index 1938825efa6ad20ede5aba57f097863aeb33d1d5..76f0a35b074b858ab4cb3e3894bc7c877401b7e8 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -31,17 +31,6 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
-static int common_config(const char *k, const char *v, void *cb)
-{
-	int status;
-
-	status = git_sequencer_config(k, v, NULL);
-	if (status)
-		return status;
-
-	return git_default_config(k, v, NULL);
-}
-
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
@@ -219,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REPLAY_REVERT;
-	git_config(common_config, NULL);
+	sequencer_init_config(&opts);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
@@ -232,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	int res;
 
 	opts.action = REPLAY_PICK;
-	git_config(common_config, NULL);
+	sequencer_init_config(&opts);
 	res = run_sequencer(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
diff --git a/sequencer.c b/sequencer.c
index 0f17b4d32580aa637ddfeedfaec68468a9995e3d..c3035bb5f3d8bb1c6a6be8f3bc8d1bb29cf4383c 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -132,6 +132,51 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 
+static int git_sequencer_config(const char *k, const char *v, void *cb)
+{
+	struct replay_opts *opts = cb;
+	int status;
+
+	if (!strcmp(k, "commit.cleanup")) {
+		const char *s;
+
+		status = git_config_string(&s, k, v);
+		if (status)
+			return status;
+
+		if (!strcmp(s, "verbatim"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+		else if (!strcmp(s, "whitespace"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else if (!strcmp(s, "strip"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
+		else if (!strcmp(s, "scissors"))
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
+		else
+			warning(_("invalid commit message cleanup mode '%s'"),
+				  s);
+
+		return status;
+	}
+
+	if (!strcmp(k, "commit.gpgsign")) {
+		opts->gpg_sign = git_config_bool(k, v) ? "" : NULL;
+		return 0;
+	}
+
+	status = git_gpg_config(k, v, NULL);
+	if (status)
+		return status;
+
+	return git_diff_basic_config(k, v, NULL);
+}
+
+void sequencer_init_config(struct replay_opts *opts)
+{
+	opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
+	git_config(git_sequencer_config, opts);
+}
+
 static inline int is_rebase_i(const struct replay_opts *opts)
 {
 	return opts->action == REPLAY_INTERACTIVE_REBASE;
@@ -703,40 +748,6 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 	return run_command(&cmd);
 }
 
-static enum commit_msg_cleanup_mode default_msg_cleanup =
-						COMMIT_MSG_CLEANUP_NONE;
-static char *default_gpg_sign;
-
-int git_sequencer_config(const char *k, const char *v, void *cb)
-{
-	if (!strcmp(k, "commit.cleanup")) {
-		int status;
-		const char *s;
-
-		status = git_config_string(&s, k, v);
-		if (status)
-			return status;
-
-		if (!strcmp(s, "verbatim"))
-			default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
-		else if (!strcmp(s, "whitespace"))
-			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-		else if (!strcmp(s, "strip"))
-			default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
-		else if (!strcmp(s, "scissors"))
-			default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-
-		return status;
-	}
-
-	if (!strcmp(k, "commit.gpgsign")) {
-		default_gpg_sign = git_config_bool(k, v) ? "" : NULL;
-		return 0;
-	}
-
-	return git_gpg_config(k, v, NULL);
-}
-
 static int rest_is_empty(const struct strbuf *sb, int start)
 {
 	int i, eol;
@@ -1042,7 +1053,6 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 	struct strbuf err = STRBUF_INIT;
 	struct strbuf amend_msg = STRBUF_INIT;
 	char *amend_author = NULL;
-	const char *gpg_sign;
 	enum commit_msg_cleanup_mode cleanup;
 	int res = 0;
 
@@ -1075,7 +1085,8 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 	}
 
 	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
-					  default_msg_cleanup;
+					  opts->default_msg_cleanup;
+
 	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
 		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
 	if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
@@ -1083,8 +1094,6 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 		goto out;
 	}
 
-	gpg_sign = opts->gpg_sign ? opts->gpg_sign : default_gpg_sign;
-
 	if (write_cache_as_tree(tree.hash, 0, NULL)) {
 		res = error(_("git write-tree failed to write a tree"));
 		goto out;
@@ -1098,7 +1107,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 	}
 
 	if (commit_tree_extended(msg->buf, msg->len, tree.hash, parents,
-				 oid->hash, author, gpg_sign, extra)) {
+				 oid->hash, author, opts->gpg_sign, extra)) {
 		res = error(_("failed to write commit object"));
 		goto out;
 	}
diff --git a/sequencer.h b/sequencer.h
index 77cb174b2aaf3972ebb9e6ec379252be96dedd3d..3a5072c2ab9088c237b83d92deae3c801289e543 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -11,6 +11,13 @@ enum replay_action {
 	REPLAY_INTERACTIVE_REBASE
 };
 
+enum commit_msg_cleanup_mode {
+	COMMIT_MSG_CLEANUP_SPACE,
+	COMMIT_MSG_CLEANUP_NONE,
+	COMMIT_MSG_CLEANUP_SCISSORS,
+	COMMIT_MSG_CLEANUP_ALL
+};
+
 struct replay_opts {
 	enum replay_action action;
 
@@ -29,6 +36,7 @@ struct replay_opts {
 	int mainline;
 
 	char *gpg_sign;
+	enum commit_msg_cleanup_mode default_msg_cleanup;
 
 	/* Merge strategy */
 	char *strategy;
@@ -40,6 +48,8 @@ struct replay_opts {
 };
 #define REPLAY_OPTS_INIT { -1 }
 
+/* Call this to setup defaults before parsing command line options */
+void sequencer_init_config(struct replay_opts *opts);
 int sequencer_pick_revisions(struct replay_opts *opts);
 int sequencer_continue(struct replay_opts *opts);
 int sequencer_rollback(struct replay_opts *opts);
@@ -57,15 +67,6 @@ extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 void append_conflicts_hint(struct strbuf *msgbuf);
-int git_sequencer_config(const char *k, const char *v, void *cb);
-
-enum commit_msg_cleanup_mode {
-	COMMIT_MSG_CLEANUP_SPACE,
-	COMMIT_MSG_CLEANUP_NONE,
-	COMMIT_MSG_CLEANUP_SCISSORS,
-	COMMIT_MSG_CLEANUP_ALL
-};
-
 int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
-- 
2.15.1


^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-13 11:46     ` [PATCH] sequencer: improve config handling Phillip Wood
@ 2017-12-20 18:33       ` Kaartic Sivaraam
  2017-12-21 14:06         ` Johannes Schindelin
                           ` (2 more replies)
  0 siblings, 3 replies; 120+ messages in thread
From: Kaartic Sivaraam @ 2017-12-20 18:33 UTC (permalink / raw)
  To: Phillip Wood, Git Mailing List; +Cc: Johannes Schindelin, Junio C Hamano

I recently encountered that error when trying to do an interactive
rebase after using filter-branch to remove a file completely in a
repository. I bisected this issue which pointed at this patch. I'm not
sure how it is related as I'm not too familiar with the sequencer code.
I could help in case any specific information is needed. As a first
step, I've posted the output of "strace /mnt/Source//Git/git rebase -i
HEAD~10" below.


-- 8< --
execve("/mnt/Source//Git/git-next/git", ["/mnt/Source//Git/git-next/git", "rebase", "-i", "HEAD~10"], [/* 62 vars */]) = 0
brk(NULL)                               = 0x55a494d30000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25bd940000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=114620, ...}) = 0
mmap(NULL, 114620, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f25bd924000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300!\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=105088, ...}) = 0
mmap(NULL, 2200072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd506000
mprotect(0x7f25bd51f000, 2093056, PROT_NONE) = 0
mmap(0x7f25bd71e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f25bd71e000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0Pa\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=135440, ...}) = 0
mmap(NULL, 2212936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd2e9000
mprotect(0x7f25bd301000, 2093056, PROT_NONE) = 0
mmap(0x7f25bd500000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f25bd500000
mmap(0x7f25bd502000, 13384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25bd502000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340 \0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=31744, ...}) = 0
mmap(NULL, 2128832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bd0e1000
mprotect(0x7f25bd0e8000, 2093056, PROT_NONE) = 0
mmap(0x7f25bd2e7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f25bd2e7000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\4\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0
mmap(NULL, 3795296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25bcd42000
mprotect(0x7f25bced7000, 2097152, PROT_NONE) = 0
mmap(0x7f25bd0d7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7f25bd0d7000
mmap(0x7f25bd0dd000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25bd0dd000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25bd922000
arch_prctl(ARCH_SET_FS, 0x7f25bd922e80) = 0
mprotect(0x7f25bd0d7000, 16384, PROT_READ) = 0
mprotect(0x7f25bd500000, 4096, PROT_READ) = 0
mprotect(0x7f25bd2e7000, 4096, PROT_READ) = 0
mprotect(0x7f25bd71e000, 4096, PROT_READ) = 0
mprotect(0x55a49424b000, 12288, PROT_READ) = 0
mprotect(0x7f25bd943000, 4096, PROT_READ) = 0
munmap(0x7f25bd924000, 114620)          = 0
set_tid_address(0x7f25bd923150)         = 9667
set_robust_list(0x7f25bd923160, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7f25bd2eebd0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f25bd2fa0c0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7f25bd2eec60, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f25bd2fa0c0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
open("/dev/null", O_RDWR)               = 3
close(3)                                = 0
rt_sigprocmask(SIG_UNBLOCK, [PIPE], NULL, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
brk(NULL)                               = 0x55a494d30000
brk(0x55a494d51000)                     = 0x55a494d51000
getcwd("/mnt/Source/bash/lxconf-bash", 129) = 29
stat("/mnt/Source/bash/lxconf-bash", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/mnt/Source/bash/lxconf-bash/.git", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/mnt/Source/bash/lxconf-bash/.git/HEAD", {st_mode=S_IFREG|0644, st_size=23, ...}) = 0
open("/mnt/Source/bash/lxconf-bash/.git/HEAD", O_RDONLY) = 3
read(3, "ref: refs/heads/public\n", 255) = 23
read(3, "", 232)                        = 0
close(3)                                = 0
lstat("/mnt/Source/bash/lxconf-bash/.git/commondir", 0x7ffcd3d12100) = -1 ENOENT (No such file or directory)
access("/mnt/Source/bash/lxconf-bash/.git/objects", X_OK) = 0
access("/mnt/Source/bash/lxconf-bash/.git/refs", X_OK) = 0
lstat(".git/commondir", 0x7ffcd3d12270) = -1 ENOENT (No such file or directory)
open(".git/config", O_RDONLY)           = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0
read(3, "[core]\n\trepositoryformatversion "..., 4096) = 738
read(3, "", 4096)                       = 0
close(3)                                = 0
access("/home/unique/.local/etc/gitconfig", R_OK) = -1 ENOENT (No such file or directory)
access("/home/unique/.config/git/config", R_OK) = -1 ENOENT (No such file or directory)
access("/home/unique/.gitconfig", R_OK) = 0
open("/home/unique/.gitconfig", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=866, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=866, ...}) = 0
read(3, "[user]\n\temail = kaartic.sivaraam"..., 4096) = 866
read(3, "", 4096)                       = 0
close(3)                                = 0
access(".git/config", R_OK)             = 0
open(".git/config", O_RDONLY)           = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=738, ...}) = 0
read(3, "[core]\n\trepositoryformatversion "..., 4096) = 738
read(3, "", 4096)                       = 0
close(3)                                = 0
pipe([3, 4])                            = 0
stat("/home/unique/.local/libexec/git-core/git-rebase", {st_mode=S_IFREG|0755, st_size=16486, ...}) = 0
rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f25bd923150) = 9668
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0x55a493f78a70, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=0x55a493f78a70, sa_mask=[HUP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x55a493f78a70, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=0x55a493f78a70, sa_mask=[QUIT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x55a493f78a70, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f25bcd75060}, 8) = 0
close(4)                                = 0
read(3, "", 8)                          = 0
close(3)                                = 0
wait4(9668, *** Error in `git': free(): invalid pointer: 0x0000557bf5cdf98a ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7fcb647d1bfb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7fcb647d7fc6]
/lib/x86_64-linux-gnu/libc.so.6(+0x7780e)[0x7fcb647d880e]
git(+0x1463f9)[0x557bf5c853f9]
git(+0x6b5dd)[0x557bf5baa5dd]
git(+0x13848)[0x557bf5b52848]
git(+0x13b06)[0x557bf5b52b06]
git(+0x12c0c)[0x557bf5b51c0c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fcb647812e1]
git(+0x12c4a)[0x557bf5b51c4a]
======= Memory map: ========
557bf5b3f000-557bf5d4e000 r-xp 00000000 08:09 1178454                    /home/unique/.local/libexec/git-core/git
557bf5f4d000-557bf5f50000 r--p 0020e000 08:09 1178454                    /home/unique/.local/libexec/git-core/git
557bf5f50000-557bf5f5c000 rw-p 00211000 08:09 1178454                    /home/unique/.local/libexec/git-core/git
557bf5f5c000-557bf5f9f000 rw-p 00000000 00:00 0 
557bf7917000-557bf795d000 rw-p 00000000 00:00 0                          [heap]
7fcb60000000-7fcb60021000 rw-p 00000000 00:00 0 
7fcb60021000-7fcb64000000 ---p 00000000 00:00 0 
7fcb6454a000-7fcb64560000 r-xp 00000000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcb64560000-7fcb6475f000 ---p 00016000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcb6475f000-7fcb64760000 r--p 00015000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcb64760000-7fcb64761000 rw-p 00016000 08:0b 263716                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcb64761000-7fcb648f6000 r-xp 00000000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcb648f6000-7fcb64af6000 ---p 00195000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcb64af6000-7fcb64afa000 r--p 00195000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcb64afa000-7fcb64afc000 rw-p 00199000 08:0b 268114                     /lib/x86_64-linux-gnu/libc-2.24.so
7fcb64afc000-7fcb64b00000 rw-p 00000000 00:00 0 
7fcb64b00000-7fcb64b07000 r-xp 00000000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so
7fcb64b07000-7fcb64d06000 ---p 00007000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so
7fcb64d06000-7fcb64d07000 r--p 00006000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so
7fcb64d07000-7fcb64d08000 rw-p 00007000 08:0b 268132                     /lib/x86_64-linux-gnu/librt-2.24.so
7fcb64d08000-7fcb64d20000 r-xp 00000000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so
7fcb64d20000-7fcb64f1f000 ---p 00018000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so
7fcb64f1f000-7fcb64f20000 r--p 00017000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so
7fcb64f20000-7fcb64f21000 rw-p 00018000 08:0b 268130                     /lib/x86_64-linux-gnu/libpthread-2.24.so
7fcb64f21000-7fcb64f25000 rw-p 00000000 00:00 0 
7fcb64f25000-7fcb64f3e000 r-xp 00000000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fcb64f3e000-7fcb6513d000 ---p 00019000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fcb6513d000-7fcb6513e000 r--p 00018000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fcb6513e000-7fcb6513f000 rw-p 00019000 08:0b 263835                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fcb6513f000-7fcb65162000 r-xp 00000000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcb652c1000-7fcb65343000 rw-p 00000000 00:00 0 
7fcb6535d000-7fcb6535e000 rw-p 00000000 00:00 0 
7fcb6535e000-7fcb6535f000 r--p 00000000 08:06 57439                      /mnt/Source/bash/lxconf-bash/.git/packed-refs
7fcb6535f000-7fcb65362000 rw-p 00000000 00:00 0 
7fcb65362000-7fcb65363000 r--p 00023000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcb65363000-7fcb65364000 rw-p 00024000 08:0b 263494                     /lib/x86_64-linux-gnu/ld-2.24.so
7fcb65364000-7fcb65365000 rw-p 00000000 00:00 0 
7ffdf94a1000-7ffdf94c2000 rw-p 00000000 00:00 0                          [stack]
7ffdf95cf000-7ffdf95d1000 r--p 00000000 00:00 0                          [vvar]
7ffdf95d1000-7ffdf95d3000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
[{WIFSIGNALED(s) && WTERMSIG(s) == SIGABRT}], 0, NULL) = 9668
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=9668, si_uid=1000, si_status=SIGABRT, si_utime=0, si_stime=1} ---
write(2, "error: git-rebase died of signal"..., 35error: git-rebase died of signal 6
) = 35
exit_group(134)                         = ?
+++ exited with 134 +++

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-20 18:33       ` Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling) Kaartic Sivaraam
@ 2017-12-21 14:06         ` Johannes Schindelin
  2017-12-21 16:42           ` Kaartic Sivaraam
       [not found]         ` <18737953.1042351513802399608.JavaMail.defaultUser@defaultHost>
  2017-12-21 16:53         ` phillip.wood
  2 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-12-21 14:06 UTC (permalink / raw)
  To: Kaartic Sivaraam; +Cc: Phillip Wood, Git Mailing List, Junio C Hamano

Hi Kaartic,

On Thu, 21 Dec 2017, Kaartic Sivaraam wrote:

> I recently encountered that error when trying to do an interactive
> rebase after using filter-branch to remove a file completely in a
> repository. I bisected this issue which pointed at this patch. I'm not
> sure how it is related as I'm not too familiar with the sequencer code.
> I could help in case any specific information is needed. As a first
> step, I've posted the output of "strace /mnt/Source//Git/git rebase -i
> HEAD~10" below.

I fear that the strace does not cover the `git-rebase` process nor the
`git-rebase--helper` process (which must have been part of your run, as
the commit affected only that part of the rebase operation).

If you have valgrind installed, you may want to give it a try. Since
git-rebase is (still, much to my dislike) a Unix shell script, you will
have to work quite hard to get it to valgrind correctly.

This is how I typically do it: I edit the
/home/unique/.local/libexec/git-core/git-rebase file directly (it is in a
different path for me, of course), first adding a `set -x` as 2nd or 3rd
line (not the 1st, to avoid messing with Git for Windows' shell script
detection that requires a shebang, and I do that also on other platforms
to spare some brain cycles), then treat git-rebase--interactive
identically, then run the offending command to figure out which call
really fails.

Then, I look for that call (I imagine it is the `exec git rebase--helper
${force_rebase:+--no-ff} --continue` line), then copy-edit it and guard it
by an environment variable:

	test -z "$DDDD" || {
		valgrind git rebase--helper ${force_rebase:+--no-ff} \
			--continue
		exit
	}
	exec git rebase--helper ${force_rebase:+--no-ff} --continue

After that, you can run the same command, and trigger the new code path by
that environment variable:

	DDDD=1 /mnt/Source/Git/git rebase -i HEAD~10

That way, you can keep the rest of your Git calls be unaffected by the
patch.

BTW from your invocation, I imagine that you wanted to actually run your
Git just-built-from-source, in-place. But for that, you would need to pass
the --exec-path option, too, like so:

	DDDD=1 /mnt/Source/Git/git --exec-path=/mnt/Source/Git \
		rebase -i HEAD~10

That way, you could edit the git-rebase--interactive file that is *not*
installed, and avoid affecting other Git operations (you would also not
need to guard the new code path behind a conditional).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
       [not found]         ` <18737953.1042351513802399608.JavaMail.defaultUser@defaultHost>
@ 2017-12-21 15:02           ` Kaartic Sivaraam
  0 siblings, 0 replies; 120+ messages in thread
From: Kaartic Sivaraam @ 2017-12-21 15:02 UTC (permalink / raw)
  To: phillip.wood@talktalk.net
  Cc: Git Mailing List, Johannes Schindelin, Junio C Hamano

On Thursday 21 December 2017 02:09 AM, phillip.wood@talktalk.net wrote:
> 
> 
>     Hi Kaartic
> 
>     I'm replying off list as I've only got access to webmail at the
>     moment.

No issues brought the CCs including the list to ensure we don't miss things.


>     Are you able to do a backtrace with debugging symbols
>     please, I'm finding it hard to glean anything from the backtrace
>     here. 

That's confusing, I already had the following in my `config.mak` while 
compiling,


DEVELOPER=1
DEVELOPERS=1
CFLAGS += -g -O2
CFLAGS += -DFLEX_ARRAY=2048

NO_GETTEXT=1
prefix=/home/$(USER)/.local
#CFLAGS += -Wno-unused-value


As you can see the `-g` flag is there in CFLAGS.

If this output doesn't give much sense could you be specific about what 
output you want i.e., by mentioning the command, showing a sample etc., 
I'm not sure how to go about debugging with "git rebase" as it's a script.


> Also does it definitely bisect to this patch or the merge of
>     this branch?
> 

"git bisect" speaking,

28d6daed4f119940ace31e523b3b272d3d153d04 is the first bad commit
commit 28d6daed4f119940ace31e523b3b272d3d153d04
Author: Phillip Wood <phillip.wood@dunelm.org.uk>
Date:   Wed Dec 13 11:46:21 2017 +0000

    sequencer: improve config handling
    
    The previous config handling relied on global variables, called
    git_default_config() even when the key had already been handled by
    git_sequencer_config() and did not initialize the diff configuration
    variables. Improve this by: i) loading the default values for message
    cleanup and gpg signing of commits into struct replay_opts;
    ii) restructuring the code to return immediately once a key is
    handled; and iii) calling git_diff_basic_config(). Note that
    unfortunately it is not possible to return early if the key is handled
    by git_gpg_config() as it does not indicate to the caller if the key
    has been handled or not.
    
    The sequencer should probably have been calling
    git_diff_basic_config() before as it creates a patch when there are
    conflicts. The shell version uses 'diff-tree' to create the patch so
    calling git_diff_basic_config() should match that. Although 'git
    commit' calls git_diff_ui_config() I don't think the output of
    print_commit_summary() is affected by anything that is loaded by that
    as print_commit_summary() always turns on rename detection so would
    ignore the value in the user's configuration anyway. The other values
    loaded by git_diff_ui_config() are about the formatting of patches so
    are not relevant to print_commit_summary().
    
    Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
    Signed-off-by: Junio C Hamano <gitster@pobox.com>

:040000 040000 bbd487cc285ae12cefb97a9661de4ff27d182727 9460ac528bf68f331088412f62e5d6a39305854b M	builtin
:100644 100644 99452a0e89777999418c116386f924be396ed04a 7051b20b76292e3b539875f0dc38108a734d2164 M	sequencer.c
:100644 100644 77cb174b2aaf3972ebb9e6ec379252be96dedd3d 3a5072c2ab9088c237b83d92deae3c801289e543 M	sequencer.h


FWIW, I started the bisection with `v2.11.0`  as good (because "git
rebase -i HEAD~10 worked there) and the tip of `next` (cfbfd45ee (Sync
with master, 2017-12-19)) as bad.

Here's the bisect log for a shorter range of bisection (which was
possible after I discovered the bad commit in the previous bisect)

git bisect start
# bad: [cfbfd45ee6e49007fdeb00008904064ba98f65e0] Sync with master
git bisect bad cfbfd45ee6e49007fdeb00008904064ba98f65e0
# good: [a4212f7ebd7158a1e237d776fb4bbd7a295d0bda] Merge branch 'pw/sequencer-in-process-commit' into next
git bisect good a4212f7ebd7158a1e237d776fb4bbd7a295d0bda
# good: [bdae4af87053490adad2dc9fb184d6d050d46a4c] Merge branch 'sg/setup-doc-update'
git bisect good bdae4af87053490adad2dc9fb184d6d050d46a4c
# bad: [6d12e08217360cfc042ec484129771f73ad7b97f] Merge branch 'rs/strbuf-read-once-reset-length' into next
git bisect bad 6d12e08217360cfc042ec484129771f73ad7b97f
# bad: [1b791d2503742b060f91b8159b85c06335634bd8] Merge branch 'ab/simplify-perl-makefile' into next
git bisect bad 1b791d2503742b060f91b8159b85c06335634bd8
# bad: [abfed699db9f0f0e6f03f5ee3e9d137944ba4216] Merge branch 'sb/clone-recursive-submodule-doc' into next
git bisect bad abfed699db9f0f0e6f03f5ee3e9d137944ba4216
# bad: [ec4d2b9c843b8b338ffa8906eea70095b3098c1e] Merge branch 'pw/sequencer-in-process-commit' into next
git bisect bad ec4d2b9c843b8b338ffa8906eea70095b3098c1e
# good: [5279b80103bde3ec5d6108fa8f43de2017668039] Merge branch 'tb/check-crlf-for-safe-crlf' into next
git bisect good 5279b80103bde3ec5d6108fa8f43de2017668039
# bad: [28d6daed4f119940ace31e523b3b272d3d153d04] sequencer: improve config handling
git bisect bad 28d6daed4f119940ace31e523b3b272d3d153d04
# first bad commit: [28d6daed4f119940ace31e523b3b272d3d153d04] sequencer: improve config handling

-- 
Kaartic

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-21 14:06         ` Johannes Schindelin
@ 2017-12-21 16:42           ` Kaartic Sivaraam
  2017-12-22 11:49             ` Johannes Schindelin
  0 siblings, 1 reply; 120+ messages in thread
From: Kaartic Sivaraam @ 2017-12-21 16:42 UTC (permalink / raw)
  To: Johannes Schindelin, Phillip Wood; +Cc: Git Mailing List, Junio C Hamano

On Thu, 2017-12-21 at 15:06 +0100, Johannes Schindelin wrote:
> Hi Kaartic,
> I fear that the strace does not cover the `git-rebase` process nor the
> `git-rebase--helper` process (which must have been part of your run, as
> the commit affected only that part of the rebase operation).
> 

Yep. It striked me only later that the entry point of "git rebase" is
essentially a script.


> If you have valgrind installed, you may want to give it a try. Since
> git-rebase is (still, much to my dislike) a Unix shell script, you will
> have to work quite hard to get it to valgrind correctly.
> 

You're right about that it's quite hard to get to the right point of
the issue when scripts are involved.


> This is how I typically do it: 
<snip>
> Then, I look for that call (I imagine it is the `exec git rebase--helper
> ${force_rebase:+--no-ff} --continue` line), then copy-edit it and guard it
> by an environment variable:
> 
> 	test -z "$DDDD" || {
> 		valgrind git rebase--helper ${force_rebase:+--no-ff} \
> 			--continue
> 		exit
> 	}
> 	exec git rebase--helper ${force_rebase:+--no-ff} --continue
> 
> After that, you can run the same command, and trigger the new code path by
> that environment variable:
> 
> 	DDDD=1 /mnt/Source/Git/git rebase -i HEAD~10
> 
> That way, you can keep the rest of your Git calls be unaffected by the
> patch.
> 

Thanks for detailing the way to get the right information for this
issue. That was intriguing.


> BTW from your invocation, I imagine that you wanted to actually run your
> Git just-built-from-source, in-place. But for that, you would need to pass
> the --exec-path option, too, like so:
> 
> 	DDDD=1 /mnt/Source/Git/git --exec-path=/mnt/Source/Git \
> 		rebase -i HEAD~10
> 
> That way, you could edit the git-rebase--interactive file that is *not*
> installed, and avoid affecting other Git operations (you would also not
> need to guard the new code path behind a conditional).
> 

Makes sense. (I had actually used incorrect directories in my previous
mail as I did part of it manually (big mistake!) so the trace itself
might not have much sense in some parts, sorry)

Speaking of trace, (thanks to Dscho!) the one I got using 'valgrind'
(with `--leak-check=full` option) can be found below. I've kept only
relevant parts of it to avoid unwanted noise of `set -x`. Let me know
if that's needed too.

...

+ git diff-files --quiet --ignore-submodules
+ git diff-index --cached --quiet --ignore-submodules HEAD --
+ test 0 = 1
+ test -z 1
+ valgrind --leak-check=full git rebase--helper --continue
==10384== Memcheck, a memory error detector
==10384== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10384== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==10384== Command: git rebase--helper --continue
==10384== 
==10384== Invalid free() / delete / delete[] / realloc()
==10384==    at 0x4C2CDDB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x24E3F8: read_populate_opts (sequencer.c:1964)
==10384==    by 0x24E3F8: sequencer_continue (sequencer.c:2753)
==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)
==10384==    by 0x11B847: run_builtin (git.c:346)
==10384==    by 0x11B847: handle_builtin (git.c:554)
==10384==    by 0x11BB05: run_argv (git.c:606)
==10384==    by 0x11BB05: cmd_main (git.c:683)
==10384==    by 0x11AC0B: main (common-main.c:43)
==10384==  Address 0x2a898a is in a r-x mapped file /mnt/Source/Git/git-next/git segment
==10384== 
Successfully rebased and updated refs/heads/public.
==10384== 
==10384== HEAP SUMMARY:
==10384==     in use at exit: 680,882 bytes in 332 blocks
==10384==   total heap usage: 717 allocs, 386 frees, 1,709,293 bytes allocated
==10384== 
==10384== 2,053 (2,048 direct, 5 indirect) bytes in 1 blocks are definitely lost in loss record 75 of 83
==10384==    at 0x4C2BADF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x4C2DE5F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x27E0FE: xrealloc (wrapper.c:138)
==10384==    by 0x2072A3: add_object_array_with_path (object.c:319)
==10384==    by 0x23D745: add_pending_object_with_path (revision.c:163)
==10384==    by 0x24030E: add_pending_object_with_mode (revision.c:170)
==10384==    by 0x24030E: add_pending_object (revision.c:176)
==10384==    by 0x24030E: add_head_to_pending (revision.c:188)
==10384==    by 0x280EFA: has_uncommitted_changes.part.17 (wt-status.c:2288)
==10384==    by 0x24DF88: commit_staged_changes (sequencer.c:2705)
==10384==    by 0x24DF88: sequencer_continue (sequencer.c:2749)
==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)
==10384==    by 0x11B847: run_builtin (git.c:346)
==10384==    by 0x11B847: handle_builtin (git.c:554)
==10384==    by 0x11BB05: run_argv (git.c:606)
==10384==    by 0x11BB05: cmd_main (git.c:683)
==10384==    by 0x11AC0B: main (common-main.c:43)
==10384== 
==10384== LEAK SUMMARY:
==10384==    definitely lost: 2,048 bytes in 1 blocks
==10384==    indirectly lost: 5 bytes in 1 blocks
==10384==      possibly lost: 0 bytes in 0 blocks
==10384==    still reachable: 678,829 bytes in 330 blocks
==10384==         suppressed: 0 bytes in 0 blocks
==10384== Reachable blocks (those to which a pointer was found) are not shown.
==10384== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==10384== 
==10384== For counts of detected and suppressed errors, rerun with: -v
==10384== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+ exit


I think I didn't mention I've set `commit.gpgsign` to `true` for that
repo, did I?

Let me know if anything else is needed.


HTH,
Kaartic

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-20 18:33       ` Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling) Kaartic Sivaraam
  2017-12-21 14:06         ` Johannes Schindelin
       [not found]         ` <18737953.1042351513802399608.JavaMail.defaultUser@defaultHost>
@ 2017-12-21 16:53         ` phillip.wood
  2017-12-21 17:14           ` Kaartic Sivaraam
  2 siblings, 1 reply; 120+ messages in thread
From: phillip.wood @ 2017-12-21 16:53 UTC (permalink / raw)
  To: kaartic.sivaraam, Phillip Wood, Git Mailing List; +Cc: Johannes Schindelin



>----Original Message----
>From: kaartic.sivaraam@gmail.com
>Date: 20/12/2017 18:33 
>To: "Phillip Wood"<phillip.wood@dunelm.org.uk>, "Git Mailing List"
<git@vger.kernel.org>
>Cc: "Johannes Schindelin"<Johannes.Schindelin@gmx.de>
>Subj: Error in `git&#39;: free(): invalid pointer (was Re: [PATCH] 
sequencer: improve config handling)
>
>I recently encountered that error when trying to do an interactive
>rebase after using filter-branch to remove a file completely in a
>repository. I bisected this issue which pointed at this patch. I'm not
>sure how it is related as I'm not too familiar with the sequencer 
code.
>I could help in case any specific information is needed. As a first
>step, I've posted the output of "strace /mnt/Source//Git/git rebase -i
>HEAD~10" below.
>


Hm, There is a problem with sequencer_remove_state() which does 

free(opts->gpg_sign)

however unless a gpg key was given on the commandline, opts->gpg is 
initialized to "" which is statically allocated. 

This untested diff should fix that, but I'm not sure if you're problem 
is related to it (I'm visiting relatives so don't have much time for 
working at the moment. also I'm on webmail so apologies if the patch is 
mangled)

Best Wishes

Phillip
>-- 8< --

diff --git a/sequencer.c b/sequencer.c
index 3bc487573..115ceba91 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -160,7 +160,7 @@ static int git_sequencer_config(const char *k, 
const char *v, void *cb)
        }
 
        if (!strcmp(k, "commit.gpgsign")) {
-               opts->gpg_sign = git_config_bool(k, v) ? "" : NULL;
+               opts->gpg_sign = git_config_bool(k, v) ? strdup("") : 
NULL;
                return 0;
        }




^ permalink raw reply related	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-21 16:53         ` phillip.wood
@ 2017-12-21 17:14           ` Kaartic Sivaraam
  2017-12-22 10:47             ` phillip.wood
  0 siblings, 1 reply; 120+ messages in thread
From: Kaartic Sivaraam @ 2017-12-21 17:14 UTC (permalink / raw)
  To: phillip.wood@talktalk.net, Phillip Wood, Git Mailing List
  Cc: Johannes Schindelin

On Thu, 2017-12-21 at 16:53 +0000, phillip.wood@talktalk.net wrote:
> Hm, There is a problem with sequencer_remove_state() which does 
> 
> free(opts->gpg_sign)
> 
> however unless a gpg key was given on the commandline, opts->gpg is 
> initialized to "" which is statically allocated. 
> 
> This untested diff should fix that,

It did seem to. I don't get that error any more.


>  but I'm not sure if you're problem 
> is related to it

As the fix you suggested did work, I think the problem is related. Did
you have anything else in mind when you said you're not sure whether or
not it's related?


> also I'm on webmail so apologies if the patch is 
> mangled)
> 

No issues. The patch seems quite small :)


Thanks,
Kaartic

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-21 17:14           ` Kaartic Sivaraam
@ 2017-12-22 10:47             ` phillip.wood
  0 siblings, 0 replies; 120+ messages in thread
From: phillip.wood @ 2017-12-22 10:47 UTC (permalink / raw)
  To: kaartic.sivaraam, Phillip Wood, Git Mailing List; +Cc: Johannes Schindelin

>----Original Message----
>From: kaartic.sivaraam@gmail.com
>Date: 21/12/2017 17:14 
>To: "phillip.wood@talktalk.net"<phillip.wood@talktalk.net>, "Phillip 
Wood"<phillip.wood@dunelm.org.uk>, "Git Mailing List"<git@vger.kernel.
org>
>Cc: "Johannes Schindelin"<Johannes.Schindelin@gmx.de>
>Subj: Re: Error in `git&#39;: free(): invalid pointer (was Re: [PATCH] 
sequencer: improve config handling)
>
>On Thu, 2017-12-21 at 16:53 +0000, phillip.wood@talktalk.net wrote:
>> Hm, There is a problem with sequencer_remove_state() which does 
>> 
>> free(opts->gpg_sign)
>> 
>> however unless a gpg key was given on the commandline, opts->gpg is 
>> initialized to "" which is statically allocated. 
>> 
>> This untested diff should fix that,
>
>It did seem to. I don't get that error any more.

That's good, thanks for testing it

>>  but I'm not sure if you're problem 
>> is related to it
>
>As the fix you suggested did work, I think the problem is related. Did
>you have anything else in mind when you said you're not sure whether 
or
>not it's related?

I didn't have anything else in mind, but at that point I hadn't noticed 
that one of your previous messages said you had gpg signing turned on - 
as you do it makes sense that the patch fixed it.


Thanks again for reporting this and testing the fix - it seems the test 
suite has a bit of a blind spot when it comes to gpg signing, I guess 
it's difficult to set up a key for tests

I'll send a proper patch when I have time in a few days

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-21 16:42           ` Kaartic Sivaraam
@ 2017-12-22 11:49             ` Johannes Schindelin
  2017-12-25  8:51               ` Kaartic Sivaraam
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2017-12-22 11:49 UTC (permalink / raw)
  To: Kaartic Sivaraam; +Cc: Phillip Wood, Git Mailing List, Junio C Hamano

Hi Kaartic,

On Thu, 21 Dec 2017, Kaartic Sivaraam wrote:

> Speaking of trace, (thanks to Dscho!) the one I got using 'valgrind'
> (with `--leak-check=full` option) can be found below. I've kept only
> relevant parts of it to avoid unwanted noise of `set -x`. Let me know
> if that's needed too.
> 
> ...
> 
> + git diff-files --quiet --ignore-submodules
> + git diff-index --cached --quiet --ignore-submodules HEAD --
> + test 0 = 1
> + test -z 1
> + valgrind --leak-check=full git rebase--helper --continue
> ==10384== Memcheck, a memory error detector
> ==10384== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
> ==10384== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
> ==10384== Command: git rebase--helper --continue
> ==10384== 
> ==10384== Invalid free() / delete / delete[] / realloc()
> ==10384==    at 0x4C2CDDB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==10384==    by 0x24E3F8: read_populate_opts (sequencer.c:1964)
> ==10384==    by 0x24E3F8: sequencer_continue (sequencer.c:2753)
> ==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)
> ==10384==    by 0x11B847: run_builtin (git.c:346)
> ==10384==    by 0x11B847: handle_builtin (git.c:554)
> ==10384==    by 0x11BB05: run_argv (git.c:606)
> ==10384==    by 0x11BB05: cmd_main (git.c:683)
> ==10384==    by 0x11AC0B: main (common-main.c:43)
> ==10384==  Address 0x2a898a is in a r-x mapped file /mnt/Source/Git/git-next/git segment
> ==10384== 
> Successfully rebased and updated refs/heads/public.
> ==10384== 
> ==10384== HEAP SUMMARY:
> ==10384==     in use at exit: 680,882 bytes in 332 blocks
> ==10384==   total heap usage: 717 allocs, 386 frees, 1,709,293 bytes allocated
> ==10384== 
> ==10384== 2,053 (2,048 direct, 5 indirect) bytes in 1 blocks are definitely lost in loss record 75 of 83
> ==10384==    at 0x4C2BADF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==10384==    by 0x4C2DE5F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==10384==    by 0x27E0FE: xrealloc (wrapper.c:138)
> ==10384==    by 0x2072A3: add_object_array_with_path (object.c:319)
> ==10384==    by 0x23D745: add_pending_object_with_path (revision.c:163)
> ==10384==    by 0x24030E: add_pending_object_with_mode (revision.c:170)
> ==10384==    by 0x24030E: add_pending_object (revision.c:176)
> ==10384==    by 0x24030E: add_head_to_pending (revision.c:188)
> ==10384==    by 0x280EFA: has_uncommitted_changes.part.17 (wt-status.c:2288)
> ==10384==    by 0x24DF88: commit_staged_changes (sequencer.c:2705)
> ==10384==    by 0x24DF88: sequencer_continue (sequencer.c:2749)
> ==10384==    by 0x1735DC: cmd_rebase__helper (rebase--helper.c:52)
> ==10384==    by 0x11B847: run_builtin (git.c:346)
> ==10384==    by 0x11B847: handle_builtin (git.c:554)
> ==10384==    by 0x11BB05: run_argv (git.c:606)
> ==10384==    by 0x11BB05: cmd_main (git.c:683)
> ==10384==    by 0x11AC0B: main (common-main.c:43)
> ==10384== 
> ==10384== LEAK SUMMARY:
> ==10384==    definitely lost: 2,048 bytes in 1 blocks
> ==10384==    indirectly lost: 5 bytes in 1 blocks
> ==10384==      possibly lost: 0 bytes in 0 blocks
> ==10384==    still reachable: 678,829 bytes in 330 blocks
> ==10384==         suppressed: 0 bytes in 0 blocks
> ==10384== Reachable blocks (those to which a pointer was found) are not shown.
> ==10384== To see them, rerun with: --leak-check=full --show-leak-kinds=all
> ==10384== 
> ==10384== For counts of detected and suppressed errors, rerun with: -v
> ==10384== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
> + exit
> 
> 
> I think I didn't mention I've set `commit.gpgsign` to `true` for that
> repo, did I?

Hah! I had troubles to associate the correct line in my versions of Git's
source code (the line numbers alone are only reliable if you also have a
commit from which the Git binaries were built), but I did this free() at
(https://github.com/git/git/blob/cd54ea2b18/sequencer.c#L1975:

	if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
		if (!starts_with(buf.buf, "-S"))
			strbuf_reset(&buf);
		else {
			free(opts->gpg_sign);
			^^^^^^^^^^^^^^^^^^^^^
			opts->gpg_sign = xstrdup(buf.buf + 2);
		}
		strbuf_reset(&buf);
	}

The culprit is that we have these really unclear ownership rules, where it
is not at all clear who is responsible for releasing allocated memory:
caller or callee? (Hannes would not rightfully point out that this would
be a non-issue if we would switch to C++). In C, the common pattern is to
use `const char *` for users that are *not* supposed to take ownership,
and `char *` for users that are supposed to take custody. And in this
instance, `gpg_sign` should be owned by `struct replay_opts` because of
this (https://github.com/git/git/blob/cd54ea2b18/sequencer.h#L38):

	char *gpg_sign;

Technically, using `char *` (instead of `const char *`) does not say
"you're now responsible for de-allocating this memory", of course, but in
practice it is often used like this (and the signature of `free(void *)`
certainly encourages that type of interpreting the `const` qualifier).

But. There is a problem when you set commit.gpgSign = true (see
https://github.com/git/git/blob/cd54ea2b18/sequencer.c#L162-L165):

	if (!strcmp(k, "commit.gpgsign")) {
		opts->gpg_sign = git_config_bool(k, v) ? "" : NULL;
		return 0;
	}

See how a "" is assigned to that field of type `char *`? It should not
even be possible, as the "" is implicitly read-only memory. And it
certainly was never malloc()ed.

So how to solve it? My suggestion would be to use xstrdup("") instead of
"" here.

I spent a little quality time with the code and came up with a patch that
I will send out in a moment.

By the way, Kaartic, thank you so much for focusing on correctness before
focusing on style issues. I know it is harder to review patches for
correctness than it is to concentrate on style issues, but in my mind it
is not only much more work, but also a lot more valuable.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling)
  2017-12-22 11:49             ` Johannes Schindelin
@ 2017-12-25  8:51               ` Kaartic Sivaraam
  0 siblings, 0 replies; 120+ messages in thread
From: Kaartic Sivaraam @ 2017-12-25  8:51 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Phillip Wood, Git Mailing List, Junio C Hamano

On Friday 22 December 2017 05:19 PM, Johannes Schindelin wrote:
> Hi Kaartic,
> 
>>
>> I think I didn't mention I've set `commit.gpgsign` to `true` for that
>> repo, did I?
> 
> Hah! I had troubles to associate the correct line in my versions of Git's
> source code (the line numbers alone are only reliable if you also have a
> commit from which the Git binaries were built), 

Should have mentioned that, sorry.


> but I did this free() at
> (https://github.com/git/git/blob/cd54ea2b18/sequencer.c#L1975:
> 
> 	if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
> 		if (!starts_with(buf.buf, "-S"))
> 			strbuf_reset(&buf);
> 		else {
> 			free(opts->gpg_sign);
> 			^^^^^^^^^^^^^^^^^^^^^
> 			opts->gpg_sign = xstrdup(buf.buf + 2);
> 		}
> 		strbuf_reset(&buf);
> 	}
> 

Seems you got the right one, regardless. :)


> The culprit is that we have these really unclear ownership rules, where it
> is not at all clear who is responsible for releasing allocated memory:
> caller or callee? (Hannes would not rightfully point out that this would
> be a non-issue if we would switch to C++). In C, the common pattern is to
> use `const char *` for users that are *not* supposed to take ownership,
> and `char *` for users that are supposed to take custody. And in this
> instance, `gpg_sign` should be owned by `struct replay_opts` because of
> this (https://github.com/git/git/blob/cd54ea2b18/sequencer.h#L38):
> 
> 	char *gpg_sign;
> 
> Technically, using `char *` (instead of `const char *`) does not say
> "you're now responsible for de-allocating this memory", of course, but in
> practice it is often used like this (and the signature of `free(void *)`
> certainly encourages that type of interpreting the `const` qualifier).
> 

Nice explanation.


> I spent a little quality time with the code and came up with a patch that
> I will send out in a moment.
>

That relieves Philip of the burden, I guess. :)


> By the way, Kaartic, thank you so much for focusing on correctness before
> focusing on style issues. I know it is harder to review patches for
> correctness than it is to concentrate on style issues, but in my mind it
> is not only much more work, but also a lot more valuable.
> 

Though it's good to hear these words and I do like to focus on 
correctness, there wasn't much I did to focus on correctness in this 
case ;-) It was you actually, after seeing such a clear explanation!.

I just used Git built from 'next' for my personal use and encountered 
the issue I stated in the start of this sub-thread.


-- 
Kaartic

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'
  2017-12-11 14:13   ` [PATCH v5 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
@ 2018-01-10 20:53     ` Jonathan Nieder
  2018-01-10 22:40       ` Johannes Schindelin
  0 siblings, 1 reply; 120+ messages in thread
From: Jonathan Nieder @ 2018-01-10 20:53 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Git Mailing List, Johannes Schindelin, Junio C Hamano,
	Ramsay Jones, Adam Dinwoodie, Stefan Beller, Dmitry Torokhov

Hi,

Phillip Wood wrote:

> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> If the commit message does not need to be edited then create the
> commit without forking 'git commit'. Taking the best time of ten runs
> with a warm cache this reduces the time taken to cherry-pick 10
> commits by 27% (from 282ms to 204ms), and the time taken by 'git
> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
> my computer running linux. Some of greater saving for rebase is
> because it no longer wastes time creating the commit summary just to
> throw it away.

Neat!  Dmitry Torokhov (cc-ed) noticed[1] that this causes the
prepare-commit-msg hook not to be invoked, which I think is
unintentional.  Should we check for such a hook and take the slowpath
when it is present?

Thanks,
Jonathan

[1] https://public-inbox.org/git/CAKdAkRQuj1hfKeckjuR2oP+8C1i+ZR36O-+aRYif4ufaS_zs+w@mail.gmail.com/

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'
  2018-01-10 20:53     ` Jonathan Nieder
@ 2018-01-10 22:40       ` Johannes Schindelin
  2018-01-11 10:41         ` Phillip Wood
  0 siblings, 1 reply; 120+ messages in thread
From: Johannes Schindelin @ 2018-01-10 22:40 UTC (permalink / raw)
  To: Jonathan Nieder
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Dmitry Torokhov

Hi,

On Wed, 10 Jan 2018, Jonathan Nieder wrote:

> Phillip Wood wrote:
> 
> > From: Phillip Wood <phillip.wood@dunelm.org.uk>
> >
> > If the commit message does not need to be edited then create the
> > commit without forking 'git commit'. Taking the best time of ten runs
> > with a warm cache this reduces the time taken to cherry-pick 10
> > commits by 27% (from 282ms to 204ms), and the time taken by 'git
> > rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
> > my computer running linux. Some of greater saving for rebase is
> > because it no longer wastes time creating the commit summary just to
> > throw it away.
> 
> Neat!  Dmitry Torokhov (cc-ed) noticed[1] that this causes the
> prepare-commit-msg hook not to be invoked, which I think is
> unintentional.  Should we check for such a hook and take the slowpath
> when it is present?

We could also easily recreate the functionality:

	if (find_hook("pre-commit")) {
		struct argv_array hook_env = ARGV_ARRAY_INIT;

		argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s",
			get_index_file());
		argv_array_push(&hook_env, "GIT_EDITOR=:");
		ret = run_hook_le(hook_env.argv, "pre-commit", NULL);
		argv_array_clear(&hook_env);
	}

(This assumes that the in-process try_to_commit() is only called if the
commit message is not to be edited interactively, which is currently the
case.)

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'
  2018-01-10 22:40       ` Johannes Schindelin
@ 2018-01-11 10:41         ` Phillip Wood
  2018-01-11 20:21           ` Johannes Schindelin
  0 siblings, 1 reply; 120+ messages in thread
From: Phillip Wood @ 2018-01-11 10:41 UTC (permalink / raw)
  To: Johannes Schindelin, Jonathan Nieder
  Cc: Phillip Wood, Git Mailing List, Junio C Hamano, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Dmitry Torokhov

On 10/01/18 22:40, Johannes Schindelin wrote:
> Hi,
> 
> On Wed, 10 Jan 2018, Jonathan Nieder wrote:
> 
>> Phillip Wood wrote:
>>
>>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>>
>>> If the commit message does not need to be edited then create the
>>> commit without forking 'git commit'. Taking the best time of ten runs
>>> with a warm cache this reduces the time taken to cherry-pick 10
>>> commits by 27% (from 282ms to 204ms), and the time taken by 'git
>>> rebase --continue' to pick 10 commits by 45% (from 386ms to 212ms) on
>>> my computer running linux. Some of greater saving for rebase is
>>> because it no longer wastes time creating the commit summary just to
>>> throw it away.
>>
>> Neat!  Dmitry Torokhov (cc-ed) noticed[1]

Thanks for reporting and bisecting this Dmitry. When I was preparing
this series I checked to see if it needed to run the 'pre-commit' hook
but missed the 'prepare-commit-msg' hook.

> that this causes the
>> prepare-commit-msg hook not to be invoked, which I think is
>> unintentional.  Should we check for such a hook and take the slowpath
>> when it is present?
> 
> We could also easily recreate the functionality:
> 
> 	if (find_hook("pre-commit")) {
> 		struct argv_array hook_env = ARGV_ARRAY_INIT;
> 
> 		argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s",
> 			get_index_file());
> 		argv_array_push(&hook_env, "GIT_EDITOR=:");
> 		ret = run_hook_le(hook_env.argv, "pre-commit", NULL);
> 		argv_array_clear(&hook_env);
> 	}

Thanks Johannes, though it needs to run the 'prepare-commit-msg' hook,
the current code in master only runs the 'pre-commit' hook when we edit
the message. I'll send a patch with a test.

Best Wishes

Phillip

> (This assumes that the in-process try_to_commit() is only called if the
> commit message is not to be edited interactively, which is currently the
> case.)
> 
> Ciao,
> Dscho
> 


^ permalink raw reply	[flat|nested] 120+ messages in thread

* Re: [PATCH v5 8/9] sequencer: try to commit without forking 'git commit'
  2018-01-11 10:41         ` Phillip Wood
@ 2018-01-11 20:21           ` Johannes Schindelin
  0 siblings, 0 replies; 120+ messages in thread
From: Johannes Schindelin @ 2018-01-11 20:21 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Jonathan Nieder, Git Mailing List, Junio C Hamano, Ramsay Jones,
	Adam Dinwoodie, Stefan Beller, Dmitry Torokhov

Hi Phillip,

On Thu, 11 Jan 2018, Phillip Wood wrote:

> On 10/01/18 22:40, Johannes Schindelin wrote:
> > Hi,
> > 
> > On Wed, 10 Jan 2018, Jonathan Nieder wrote:
> > 
> >> that this causes the prepare-commit-msg hook not to be invoked, which
> >> I think is unintentional.  Should we check for such a hook and take
> >> the slowpath when it is present?
> > 
> > We could also easily recreate the functionality:
> > 
> > 	if (find_hook("pre-commit")) {
> > 		struct argv_array hook_env = ARGV_ARRAY_INIT;
> > 
> > 		argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s",
> > 			get_index_file());
> > 		argv_array_push(&hook_env, "GIT_EDITOR=:");
> > 		ret = run_hook_le(hook_env.argv, "pre-commit", NULL);
> > 		argv_array_clear(&hook_env);
> > 	}
> 
> Thanks Johannes, though it needs to run the 'prepare-commit-msg' hook,
> the current code in master only runs the 'pre-commit' hook when we edit
> the message. I'll send a patch with a test.

Sorry, yes, that's the hook I meant ;-) the quoted text by Jonathan even
mentions it explicitly.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 120+ messages in thread

end of thread, other threads:[~2018-01-11 20:22 UTC | newest]

Thread overview: 120+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-25 10:10 [RFC PATCH 0/8] sequencer: dont't fork git commit Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 1/8] commit: move empty message checks to libgit Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 2/8] commit: move code to update HEAD " Phillip Wood
2017-10-07  9:54   ` Junio C Hamano
2017-10-24 10:01     ` Phillip Wood
2017-10-24 12:41       ` Junio C Hamano
2017-09-25 10:10 ` [RFC PATCH 3/8] sequencer: refactor update_head() Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 4/8] commit: move post-rewrite code to libgit Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 5/8] commit: move print_commit_summary() " Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 7/8] sequencer: load commit related config Phillip Wood
2017-09-25 10:10 ` [RFC PATCH 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
2017-11-06 11:27 ` [PATCH v1 0/8] sequencer: dont't fork git commit Phillip Wood
2017-11-06 11:27   ` [PATCH v1 1/8] commit: move empty message checks to libgit Phillip Wood
2017-11-07  0:43     ` Johannes Schindelin
2017-11-07 14:24       ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 2/8] Add a function to update HEAD after creating a commit Phillip Wood
2017-11-07  2:56     ` Junio C Hamano
2017-11-07  3:02       ` Johannes Schindelin
2017-11-07 14:26         ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 3/8] commit: move post-rewrite code to libgit Phillip Wood
2017-11-07  3:03     ` Junio C Hamano
2017-11-07 14:28       ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 4/8] commit: move print_commit_summary() " Phillip Wood
2017-11-07  3:38     ` Junio C Hamano
2017-11-07 14:32       ` Phillip Wood
2017-11-08  1:04         ` Junio C Hamano
2017-11-06 11:27   ` [PATCH v1 5/8] sequencer: don't die in print_commit_summary() Phillip Wood
2017-11-07  4:18     ` Junio C Hamano
2017-11-07 10:24       ` Johannes Schindelin
2017-11-07 15:13       ` Junio C Hamano
2017-11-10 14:53         ` Phillip Wood
2017-11-10 18:05           ` Junio C Hamano
2017-11-13 11:11             ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-11-07  0:52     ` Johannes Schindelin
2017-11-07  4:52     ` Junio C Hamano
2017-11-07 14:46       ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 7/8] sequencer: load commit related config Phillip Wood
2017-11-07  1:02     ` Johannes Schindelin
2017-11-07 10:50       ` Phillip Wood
2017-11-06 11:27   ` [PATCH v1 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
2017-11-07  1:36     ` Johannes Schindelin
2017-11-07 11:16       ` Phillip Wood
2017-11-07 14:09         ` Johannes Schindelin
2017-11-10 11:09 ` [PATCH v2 0/9] sequencer: dont't fork git commit Phillip Wood
2017-11-10 11:09   ` [PATCH v2 1/9] t3404: check intermediate squash messages Phillip Wood
2017-11-10 11:09   ` [PATCH v2 2/9] commit: move empty message checks to libgit Phillip Wood
2017-11-10 18:51     ` Ramsay Jones
2017-11-13 11:08       ` Phillip Wood
2017-11-10 11:09   ` [PATCH v2 3/9] Add a function to update HEAD after creating a commit Phillip Wood
2017-11-10 18:36     ` Junio C Hamano
2017-11-13 11:25       ` Phillip Wood
2017-11-10 11:09   ` [PATCH v2 4/9] commit: move post-rewrite code to libgit Phillip Wood
2017-11-10 11:09   ` [PATCH v2 5/9] commit: move print_commit_summary() " Phillip Wood
2017-11-10 11:09   ` [PATCH v2 6/9] sequencer: don't die in print_commit_summary() Phillip Wood
2017-11-10 11:09   ` [PATCH v2 7/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-11-10 11:09   ` [PATCH v2 8/9] sequencer: load commit related config Phillip Wood
2017-11-10 11:09   ` [PATCH v2 9/9] sequencer: try to commit without forking 'git commit' Phillip Wood
2017-11-10 19:21   ` [PATCH v2 0/9] sequencer: dont't fork git commit Junio C Hamano
2017-11-13 11:24     ` Phillip Wood
2017-11-14  1:15       ` Junio C Hamano
2017-11-17 11:34 ` [PATCH v3 0/8] sequencer: don't " Phillip Wood
2017-11-17 11:34   ` [PATCH v3 1/8] t3404: check intermediate squash messages Phillip Wood
2017-11-17 11:34   ` [PATCH v3 2/8] commit: move empty message checks to libgit Phillip Wood
2017-11-17 11:34   ` [PATCH v3 3/8] Add a function to update HEAD after creating a commit Phillip Wood
2017-11-17 11:34   ` [PATCH v3 4/8] commit: move post-rewrite code to libgit Phillip Wood
2017-11-17 11:34   ` [PATCH v3 5/8] commit: move print_commit_summary() " Phillip Wood
2017-11-17 11:34   ` [PATCH v3 6/8] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-11-17 11:34   ` [PATCH v3 7/8] sequencer: load commit related config Phillip Wood
2017-11-17 11:34   ` [PATCH v3 8/8] sequencer: try to commit without forking 'git commit' Phillip Wood
2017-11-18  3:41   ` [PATCH v3 0/8] sequencer: don't fork git commit Junio C Hamano
2017-11-18  3:57     ` Junio C Hamano
2017-11-18 11:32       ` Phillip Wood
2017-11-18 14:33       ` Phillip Wood
2017-11-24 11:07 ` [PATCH v4 0/9] " Phillip Wood
2017-11-24 11:07   ` [PATCH v4 1/9] t3404: check intermediate squash messages Phillip Wood
2017-11-24 11:07   ` [PATCH v4 2/9] commit: move empty message checks to libgit Phillip Wood
2017-11-24 11:07   ` [PATCH v4 3/9] Add a function to update HEAD after creating a commit Phillip Wood
2017-11-24 11:07   ` [PATCH v4 4/9] commit: move post-rewrite code to libgit Phillip Wood
2017-11-24 11:07   ` [PATCH v4 5/9] commit: move print_commit_summary() " Phillip Wood
2017-11-24 11:07   ` [PATCH v4 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-11-24 11:07   ` [PATCH v4 7/9] sequencer: load commit related config Phillip Wood
2017-11-24 13:48     ` Junio C Hamano
2017-11-24 14:38       ` Phillip Wood
2017-12-04 18:30     ` Junio C Hamano
2017-12-05 11:21       ` Phillip Wood
2017-12-05 12:10         ` Phillip Wood
2017-12-09 19:05         ` Phillip Wood
2017-11-24 11:07   ` [PATCH v4 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
2017-11-24 11:07   ` [PATCH v4 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
2017-12-04 19:24     ` Stefan Beller
2017-12-05 12:13       ` Phillip Wood
2017-12-11 14:13 ` [PATCH v5 0/9] sequencer: don't fork git commit Phillip Wood
2017-12-11 14:13   ` [PATCH v5 1/9] t3404: check intermediate squash messages Phillip Wood
2017-12-11 14:13   ` [PATCH v5 2/9] commit: move empty message checks to libgit Phillip Wood
2017-12-11 14:13   ` [PATCH v5 3/9] Add a function to update HEAD after creating a commit Phillip Wood
2017-12-11 14:13   ` [PATCH v5 4/9] commit: move post-rewrite code to libgit Phillip Wood
2017-12-11 14:13   ` [PATCH v5 5/9] commit: move print_commit_summary() " Phillip Wood
2017-12-11 14:13   ` [PATCH v5 6/9] sequencer: simplify adding Signed-off-by: trailer Phillip Wood
2017-12-11 14:13   ` [PATCH v5 7/9] sequencer: load commit related config Phillip Wood
2017-12-11 18:53     ` Phillip Wood
2017-12-11 14:13   ` [PATCH v5 8/9] sequencer: try to commit without forking 'git commit' Phillip Wood
2018-01-10 20:53     ` Jonathan Nieder
2018-01-10 22:40       ` Johannes Schindelin
2018-01-11 10:41         ` Phillip Wood
2018-01-11 20:21           ` Johannes Schindelin
2017-12-11 14:13   ` [PATCH v5 9/9] t3512/t3513: remove KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1 Phillip Wood
2017-12-11 23:44   ` [PATCH v5 0/9] sequencer: don't fork git commit Junio C Hamano
2017-12-12 10:32     ` Phillip Wood
2017-12-13 11:46     ` [PATCH] sequencer: improve config handling Phillip Wood
2017-12-20 18:33       ` Error in `git': free(): invalid pointer (was Re: [PATCH] sequencer: improve config handling) Kaartic Sivaraam
2017-12-21 14:06         ` Johannes Schindelin
2017-12-21 16:42           ` Kaartic Sivaraam
2017-12-22 11:49             ` Johannes Schindelin
2017-12-25  8:51               ` Kaartic Sivaraam
     [not found]         ` <18737953.1042351513802399608.JavaMail.defaultUser@defaultHost>
2017-12-21 15:02           ` Kaartic Sivaraam
2017-12-21 16:53         ` phillip.wood
2017-12-21 17:14           ` Kaartic Sivaraam
2017-12-22 10:47             ` phillip.wood

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).