Skip to content

Commit 5231c63

Browse files
committed
commit: copy merged signed tags to headers of merge commit
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 <[email protected]>
1 parent 274a5c0 commit 5231c63

File tree

3 files changed

+116
-11
lines changed

3 files changed

+116
-11
lines changed

builtin/commit.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
14251425
pptr = &commit_list_insert(c->item, pptr)->next;
14261426
} else if (whence == FROM_MERGE) {
14271427
struct strbuf m = STRBUF_INIT;
1428-
struct commit *commit;
14291428
FILE *fp;
14301429

14311430
if (!reflog_msg)
@@ -1436,11 +1435,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
14361435
die_errno(_("could not open '%s' for reading"),
14371436
git_path("MERGE_HEAD"));
14381437
while (strbuf_getline(&m, fp, '\n') != EOF) {
1439-
unsigned char sha1[20];
1440-
if (get_sha1_hex(m.buf, sha1) < 0)
1438+
struct commit *parent;
1439+
1440+
parent = get_merge_parent(m.buf);
1441+
if (!parent)
14411442
die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
1442-
commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
1443-
pptr = &commit_list_insert(commit, pptr)->next;
1443+
pptr = &commit_list_insert(parent, pptr)->next;
14441444
}
14451445
fclose(fp);
14461446
strbuf_release(&m);

commit.c

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -840,14 +840,95 @@ struct commit_list *reduce_heads(struct commit_list *heads)
840840
return result;
841841
}
842842

843+
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
844+
{
845+
struct merge_remote_desc *desc;
846+
struct commit_extra_header *mergetag;
847+
char *buf;
848+
unsigned long size, len;
849+
enum object_type type;
850+
851+
desc = merge_remote_util(parent);
852+
if (!desc || !desc->obj)
853+
return;
854+
buf = read_sha1_file(desc->obj->sha1, &type, &size);
855+
if (!buf || type != OBJ_TAG)
856+
goto free_return;
857+
len = parse_signature(buf, size);
858+
if (size == len)
859+
goto free_return;
860+
/*
861+
* We could verify this signature and either omit the tag when
862+
* it does not validate, but the integrator may not have the
863+
* public key of the signer of the tag he is merging, while a
864+
* later auditor may have it while auditing, so let's not run
865+
* verify-signed-buffer here for now...
866+
*
867+
* if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
868+
* warn("warning: signed tag unverified.");
869+
*/
870+
mergetag = xcalloc(1, sizeof(*mergetag));
871+
mergetag->key = xstrdup("mergetag");
872+
mergetag->value = buf;
873+
mergetag->len = size;
874+
875+
**tail = mergetag;
876+
*tail = &mergetag->next;
877+
return;
878+
879+
free_return:
880+
free(buf);
881+
}
882+
883+
void append_merge_tag_headers(struct commit_list *parents,
884+
struct commit_extra_header ***tail)
885+
{
886+
while (parents) {
887+
struct commit *parent = parents->item;
888+
handle_signed_tag(parent, tail);
889+
parents = parents->next;
890+
}
891+
}
892+
893+
static void add_extra_header(struct strbuf *buffer,
894+
struct commit_extra_header *extra)
895+
{
896+
strbuf_addstr(buffer, extra->key);
897+
strbuf_add_lines(buffer, " ", extra->value, extra->len);
898+
}
899+
900+
void free_commit_extra_headers(struct commit_extra_header *extra)
901+
{
902+
while (extra) {
903+
struct commit_extra_header *next = extra->next;
904+
free(extra->key);
905+
free(extra->value);
906+
free(extra);
907+
extra = next;
908+
}
909+
}
910+
911+
int commit_tree(const char *msg, unsigned char *tree,
912+
struct commit_list *parents, unsigned char *ret,
913+
const char *author)
914+
{
915+
struct commit_extra_header *extra = NULL, **tail = &extra;
916+
int result;
917+
918+
append_merge_tag_headers(parents, &tail);
919+
result = commit_tree_extended(msg, tree, parents, ret, author, extra);
920+
free_commit_extra_headers(extra);
921+
return result;
922+
}
923+
843924
static const char commit_utf8_warn[] =
844925
"Warning: commit message does not conform to UTF-8.\n"
845926
"You may want to amend it after fixing the message, or set the config\n"
846927
"variable i18n.commitencoding to the encoding your project uses.\n";
847928

848-
int commit_tree(const char *msg, unsigned char *tree,
849-
struct commit_list *parents, unsigned char *ret,
850-
const char *author)
929+
int commit_tree_extended(const char *msg, unsigned char *tree,
930+
struct commit_list *parents, unsigned char *ret,
931+
const char *author, struct commit_extra_header *extra)
851932
{
852933
int result;
853934
int encoding_is_utf8;
@@ -868,8 +949,10 @@ int commit_tree(const char *msg, unsigned char *tree,
868949
*/
869950
while (parents) {
870951
struct commit_list *next = parents->next;
952+
struct commit *parent = parents->item;
953+
871954
strbuf_addf(&buffer, "parent %s\n",
872-
sha1_to_hex(parents->item->object.sha1));
955+
sha1_to_hex(parent->object.sha1));
873956
free(parents);
874957
parents = next;
875958
}
@@ -881,6 +964,11 @@ int commit_tree(const char *msg, unsigned char *tree,
881964
strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
882965
if (!encoding_is_utf8)
883966
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
967+
968+
while (extra) {
969+
add_extra_header(&buffer, extra);
970+
extra = extra->next;
971+
}
884972
strbuf_addch(&buffer, '\n');
885973

886974
/* And add the comment */

commit.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,26 @@ static inline int single_parent(struct commit *commit)
181181

182182
struct commit_list *reduce_heads(struct commit_list *heads);
183183

184+
struct commit_extra_header {
185+
struct commit_extra_header *next;
186+
char *key;
187+
char *value;
188+
size_t len;
189+
};
190+
191+
extern void append_merge_tag_headers(struct commit_list *parents,
192+
struct commit_extra_header ***tail);
193+
184194
extern int commit_tree(const char *msg, unsigned char *tree,
185-
struct commit_list *parents, unsigned char *ret,
186-
const char *author);
195+
struct commit_list *parents, unsigned char *ret,
196+
const char *author);
197+
198+
extern int commit_tree_extended(const char *msg, unsigned char *tree,
199+
struct commit_list *parents, unsigned char *ret,
200+
const char *author,
201+
struct commit_extra_header *);
202+
203+
extern void free_commit_extra_headers(struct commit_extra_header *extra);
187204

188205
struct merge_remote_desc {
189206
struct object *obj; /* the named object, could be a tag */

0 commit comments

Comments
 (0)