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

[PATCH v3 11/17] commit: copy merged signed tags to headers of merge commit

From
Junio C Hamano <gitster@pobox.com>
Date
Nov 9, 2011, 01:01 UTC
Message-ID
<1320800523-5407-12-git-send-email-gitster@pobox.com>
In-Reply-To
<1320800523-5407-1-git-send-email-gitster@pobox.com>

Now MERGE_HEAD records the tag objects without peeling, we could record the result of manual conflict resolution via "git commit" without losing the tag information. Introduce a new "mergetag" multi-line header field to the commit object, and use it to store the entire contents of each signed tag merged.

A commit header that has a multi-line payload begins with the header tag (e.g. "mergetag" in this case), SP, the first line of payload, LF, and all the remaining lines have a SP inserted at the beginning.

In hindsight, it would have been better to make "merge --continue" as the way to continue from such an interrupted merge, not "commit", but this is a backward compatibility baggage we would need to carry around for now.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/commit.c |   10 +++---
 commit.c         |   95 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 commit.h         |   21 +++++++++++-
 3 files changed, 115 insertions(+), 11 deletions(-)
diff --git a/builtin/commit.c b/builtin/commit.c
index c46f2d1..4688a73 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1425,7 +1425,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			pptr = &commit_list_insert(c->item, pptr)->next;
 	} else if (whence == FROM_MERGE) {
 		struct strbuf m = STRBUF_INIT;
-		struct commit *commit;
 		FILE *fp;
 
 		if (!reflog_msg)
@@ -1436,11 +1435,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 			die_errno(_("could not open '%s' for reading"),
 				  git_path("MERGE_HEAD"));
 		while (strbuf_getline(&m, fp, '\n') != EOF) {
-			unsigned char sha1[20];
-			if (get_sha1_hex(m.buf, sha1) < 0)
+			struct commit *parent;
+
+			parent = get_merge_parent(m.buf);
+			if (!parent)
 				die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
-			commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
-			pptr = &commit_list_insert(commit, pptr)->next;
+			pptr = &commit_list_insert(parent, pptr)->next;
 		}
 		fclose(fp);
 		strbuf_release(&m);
diff --git a/commit.c b/commit.c
index 83ff503..ee12eff 100644
--- a/commit.c
+++ b/commit.c
@@ -840,14 +840,94 @@ struct commit_list *reduce_heads(struct commit_list *heads)
 	return result;
 }
 
+static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+{
+	struct merge_remote_desc *desc;
+	struct commit_extra_header *mergetag;
+	char *buf;
+	unsigned long size, len;
+	enum object_type type;
+
+	desc = merge_remote_util(parent);
+	if (!desc || !desc->obj)
+		return;
+	buf = read_sha1_file(desc->obj->sha1, &type, &size);
+	if (!buf || type != OBJ_TAG)
+		goto free_return;
+	len = parse_signature(buf, size);
+	if (size == len)
+		goto free_return;
+	/*
+	 * We could verify this signature and either omit the tag when
+	 * it does not validate, but the integrator may not have the
+	 * public key of the signer of the tag he is merging, while a
+	 * later auditor may have it while auditing, so let's not run
+	 * verify-signed-buffer here for now...
+	 *
+	 * if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
+	 *	warn("warning: signed tag unverified.");
+	 */
+	mergetag = xcalloc(1, sizeof(*mergetag));
+	mergetag->key = "mergetag";
+	mergetag->value = buf;
+	mergetag->len = size;
+
+	**tail = mergetag;
+	*tail = &mergetag->next;
+	return;
+
+free_return:
+	free(buf);
+}
+
+void append_merge_tag_headers(struct commit_list *parents,
+			      struct commit_extra_header ***tail)
+{
+	while (parents) {
+		struct commit *parent = parents->item;
+		handle_signed_tag(parent, tail);
+		parents = parents->next;
+	}
+}
+
+static void add_extra_header(struct strbuf *buffer,
+			     struct commit_extra_header *extra)
+{
+	strbuf_addstr(buffer, extra->key);
+	strbuf_add_lines(buffer, " ", extra->value, extra->len);
+}
+
+void free_commit_extra_headers(struct commit_extra_header *extra)
+{
+	while (extra) {
+		struct commit_extra_header *next = extra->next;
+		free(extra->value);
+		free(extra);
+		extra = next;
+	}
+}
+
+int commit_tree(const char *msg, unsigned char *tree,
+		struct commit_list *parents, unsigned char *ret,
+		const char *author)
+{
+	struct commit_extra_header *extra = NULL, **tail = &extra;
+	int result;
+
+	append_merge_tag_headers(parents, &tail);
+	result = commit_tree_extended(msg, tree, parents, ret, author, extra);
+	free_commit_extra_headers(extra);
+	return result;
+}
+
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret,
-		const char *author)
+int commit_tree_extended(const char *msg, unsigned char *tree,
+			 struct commit_list *parents, unsigned char *ret,
+			 const char *author, struct commit_extra_header *extra)
 {
 	int result;
 	int encoding_is_utf8;
@@ -868,8 +948,10 @@ int commit_tree(const char *msg, unsigned char *tree,
 	 */
 	while (parents) {
 		struct commit_list *next = parents->next;
+		struct commit *parent = parents->item;
+
 		strbuf_addf(&buffer, "parent %s\n",
-			sha1_to_hex(parents->item->object.sha1));
+			    sha1_to_hex(parent->object.sha1));
 		free(parents);
 		parents = next;
 	}
@@ -881,6 +963,11 @@ int commit_tree(const char *msg, unsigned char *tree,
 	strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 	if (!encoding_is_utf8)
 		strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+
+	while (extra) {
+		add_extra_header(&buffer, extra);
+		extra = extra->next;
+	}
 	strbuf_addch(&buffer, '\n');
 
 	/* And add the comment */
diff --git a/commit.h b/commit.h
index 5b57eab..c1a723e 100644
--- a/commit.h
+++ b/commit.h
@@ -181,9 +181,26 @@ static inline int single_parent(struct commit *commit)
 
 struct commit_list *reduce_heads(struct commit_list *heads);
 
+struct commit_extra_header {
+	struct commit_extra_header *next;
+	char *key;
+	char *value;
+	size_t len;
+};
+
+extern void append_merge_tag_headers(struct commit_list *parents,
+				     struct commit_extra_header ***tail);
+
 extern int commit_tree(const char *msg, unsigned char *tree,
-		struct commit_list *parents, unsigned char *ret,
-		const char *author);
+		       struct commit_list *parents, unsigned char *ret,
+		       const char *author);
+
+extern int commit_tree_extended(const char *msg, unsigned char *tree,
+				struct commit_list *parents, unsigned char *ret,
+				const char *author,
+				struct commit_extra_header *);
+
+extern void free_commit_extra_headers(struct commit_extra_header *extra);
 
 struct merge_remote_desc {
 	struct object *obj; /* the named object, could be a tag */
-- 
1.7.8.rc1.82.g90e080
Previous: Junio C HamanoNext: Junio C Hamano
Message 12 of 21 in “Pulling signed tags”
  1. 00/17 Pulling signed tagsJunio C Hamano, Nov 9, 2011
  2. 01/17 Split GPG interface into its own helper libraryJunio C Hamano, Nov 9, 2011
  3. 02/17 fetch: do not store peeled tag object names in FETCH_HEADJunio C Hamano, Nov 9, 2011
  4. 03/17 merge: notice local merging of tags and keep it unwrappedJunio C Hamano, Nov 9, 2011
  5. 04/17 fetch: allow "git fetch $there v1.0" to fetch a tagJunio C Hamano, Nov 9, 2011
  6. 05/17 refs DWIMmery: use the same rule for both "git fetch" and othersJunio C Hamano, Nov 9, 2011
  7. 06/17 fmt-merge-msg: avoid early returnsJunio C Hamano, Nov 9, 2011
  8. 07/17 fmt-merge-msg: package options into a structureJunio C Hamano, Nov 9, 2011
  9. 08/17 fmt-merge-msg: Add contents of merged tag in the merge messageJunio C Hamano, Nov 9, 2011
  10. 09/17 merge: make usage of commit->util more extensibleJunio C Hamano, Nov 9, 2011
  11. 10/17 merge: record tag objects without peeling in MERGE_HEADJunio C Hamano, Nov 9, 2011
  12. 11/17 commit: copy merged signed tags to headers of merge commitJunio C Hamano, Nov 9, 2011
  13. 12/17 merge: force edit mode when merging a tag objectJunio C Hamano, Nov 9, 2011
  14. 13/17 commit: teach --amend to carry forward extra headersJunio C Hamano, Nov 9, 2011
  15. 14/17 commit: teach --gpg-sign optionJunio C Hamano, Nov 9, 2011
  16. 15/17 log: --show-signatureJunio C Hamano, Nov 9, 2011
  17. 16/17 test "commit -S" and "log --show-signature"Junio C Hamano, Nov 9, 2011
  18. 17/17 pretty: %G[?GS] placeholdersJunio C Hamano, Nov 9, 2011
  19. Robin H. JohnsonNov 9, 2011
  20. 18/17 request-pull: use the annotated tag contentsJunio C Hamano, Nov 9, 2011
  21. Junio C HamanoNov 9, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.