Skip to content

Commit 6206089

Browse files
bk2204gitster
authored andcommitted
commit: write commits for both hashes
When we write a commit, we include data that is specific to the hash algorithm, such as parents and the root tree. In order to write both a SHA-1 commit and a SHA-256 version, we need to convert between them. However, a straightforward conversion isn't necessarily what we want. When we sign a commit, we sign its data, so if we create a commit for SHA-256 and then write a SHA-1 version, we'll still have only signed the SHA-256 data. While this is valid, it would be better to sign both forms of data so people using SHA-1 can verify the signatures as well. Consequently, we don't want to use the standard mapping that occurs when we write an object. Instead, let's move most of the writing of the commit into a separate function which is agnostic of the hash algorithm and which simply writes into a buffer and specify both versions of the object ourselves. We can then call this function twice: once with the SHA-256 contents, and if SHA-1 is enabled, once with the SHA-1 contents. If we're signing the commit, we then sign both versions and append both signatures to both buffers. To produce a consistent hash, we always append the signatures in the order in which Git implemented them: first SHA-1, then SHA-256. In order to make this signing code work, we split the commit signing code into two functions, one which signs the buffer, and one which appends the signature. Signed-off-by: brian m. carlson <[email protected]> Signed-off-by: Eric W. Biederman <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c253849 commit 6206089

File tree

1 file changed

+136
-45
lines changed

1 file changed

+136
-45
lines changed

commit.c

Lines changed: 136 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "shallow.h"
2929
#include "tree.h"
3030
#include "hook.h"
31+
#include "object-file-convert.h"
3132

3233
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
3334

@@ -1100,12 +1101,11 @@ static const char *gpg_sig_headers[] = {
11001101
"gpgsig-sha256",
11011102
};
11021103

1103-
int sign_with_header(struct strbuf *buf, const char *keyid)
1104+
static int add_commit_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
11041105
{
1105-
struct strbuf sig = STRBUF_INIT;
11061106
int inspos, copypos;
11071107
const char *eoh;
1108-
const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
1108+
const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
11091109
int gpg_sig_header_len = strlen(gpg_sig_header);
11101110

11111111
/* find the end of the header */
@@ -1115,15 +1115,8 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
11151115
else
11161116
inspos = eoh - buf->buf + 1;
11171117

1118-
if (!keyid || !*keyid)
1119-
keyid = get_signing_key();
1120-
if (sign_buffer(buf, &sig, keyid)) {
1121-
strbuf_release(&sig);
1122-
return -1;
1123-
}
1124-
1125-
for (copypos = 0; sig.buf[copypos]; ) {
1126-
const char *bol = sig.buf + copypos;
1118+
for (copypos = 0; sig->buf[copypos]; ) {
1119+
const char *bol = sig->buf + copypos;
11271120
const char *eol = strchrnul(bol, '\n');
11281121
int len = (eol - bol) + !!*eol;
11291122

@@ -1136,11 +1129,17 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
11361129
inspos += len;
11371130
copypos += len;
11381131
}
1139-
strbuf_release(&sig);
11401132
return 0;
11411133
}
11421134

1143-
1135+
static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
1136+
{
1137+
if (!keyid || !*keyid)
1138+
keyid = get_signing_key();
1139+
if (sign_buffer(buf, sig, keyid))
1140+
return -1;
1141+
return 0;
1142+
}
11441143

11451144
int parse_signed_commit(const struct commit *commit,
11461145
struct strbuf *payload, struct strbuf *signature,
@@ -1599,70 +1598,162 @@ N_("Warning: commit message did not conform to UTF-8.\n"
15991598
"You may want to amend it after fixing the message, or set the config\n"
16001599
"variable i18n.commitEncoding to the encoding your project uses.\n");
16011600

1602-
int commit_tree_extended(const char *msg, size_t msg_len,
1603-
const struct object_id *tree,
1604-
struct commit_list *parents, struct object_id *ret,
1605-
const char *author, const char *committer,
1606-
const char *sign_commit,
1607-
struct commit_extra_header *extra)
1601+
static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
1602+
const struct object_id *tree,
1603+
const struct object_id *parents, size_t parents_len,
1604+
const char *author, const char *committer,
1605+
struct commit_extra_header *extra)
16081606
{
1609-
int result;
16101607
int encoding_is_utf8;
1611-
struct strbuf buffer;
1612-
1613-
assert_oid_type(tree, OBJ_TREE);
1614-
1615-
if (memchr(msg, '\0', msg_len))
1616-
return error("a NUL byte in commit log message not allowed.");
1608+
size_t i;
16171609

16181610
/* Not having i18n.commitencoding is the same as having utf-8 */
16191611
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
16201612

1621-
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
1622-
strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
1613+
strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
1614+
strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
16231615

16241616
/*
16251617
* NOTE! This ordering means that the same exact tree merged with a
16261618
* different order of parents will be a _different_ changeset even
16271619
* if everything else stays the same.
16281620
*/
1629-
while (parents) {
1630-
struct commit *parent = pop_commit(&parents);
1631-
strbuf_addf(&buffer, "parent %s\n",
1632-
oid_to_hex(&parent->object.oid));
1633-
}
1621+
for (i = 0; i < parents_len; i++)
1622+
strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
16341623

16351624
/* Person/date information */
16361625
if (!author)
16371626
author = git_author_info(IDENT_STRICT);
1638-
strbuf_addf(&buffer, "author %s\n", author);
1627+
strbuf_addf(buffer, "author %s\n", author);
16391628
if (!committer)
16401629
committer = git_committer_info(IDENT_STRICT);
1641-
strbuf_addf(&buffer, "committer %s\n", committer);
1630+
strbuf_addf(buffer, "committer %s\n", committer);
16421631
if (!encoding_is_utf8)
1643-
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
1632+
strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
16441633

16451634
while (extra) {
1646-
add_extra_header(&buffer, extra);
1635+
add_extra_header(buffer, extra);
16471636
extra = extra->next;
16481637
}
1649-
strbuf_addch(&buffer, '\n');
1638+
strbuf_addch(buffer, '\n');
16501639

16511640
/* And add the comment */
1652-
strbuf_add(&buffer, msg, msg_len);
1641+
strbuf_add(buffer, msg, msg_len);
1642+
}
16531643

1654-
/* And check the encoding */
1655-
if (encoding_is_utf8 && !verify_utf8(&buffer))
1656-
fprintf(stderr, _(commit_utf8_warn));
1644+
int commit_tree_extended(const char *msg, size_t msg_len,
1645+
const struct object_id *tree,
1646+
struct commit_list *parents, struct object_id *ret,
1647+
const char *author, const char *committer,
1648+
const char *sign_commit,
1649+
struct commit_extra_header *extra)
1650+
{
1651+
struct repository *r = the_repository;
1652+
int result = 0;
1653+
int encoding_is_utf8;
1654+
struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
1655+
struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
1656+
struct object_id *parent_buf = NULL, *compat_oid = NULL;
1657+
struct object_id compat_oid_buf;
1658+
size_t i, nparents;
1659+
1660+
/* Not having i18n.commitencoding is the same as having utf-8 */
1661+
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
1662+
1663+
assert_oid_type(tree, OBJ_TREE);
1664+
1665+
if (memchr(msg, '\0', msg_len))
1666+
return error("a NUL byte in commit log message not allowed.");
16571667

1658-
if (sign_commit && sign_with_header(&buffer, sign_commit)) {
1668+
nparents = commit_list_count(parents);
1669+
CALLOC_ARRAY(parent_buf, nparents);
1670+
i = 0;
1671+
while (parents) {
1672+
struct commit *parent = pop_commit(&parents);
1673+
oidcpy(&parent_buf[i++], &parent->object.oid);
1674+
}
1675+
1676+
write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
1677+
if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
16591678
result = -1;
16601679
goto out;
16611680
}
1681+
if (r->compat_hash_algo) {
1682+
struct object_id mapped_tree;
1683+
struct object_id *mapped_parents;
1684+
1685+
CALLOC_ARRAY(mapped_parents, nparents);
1686+
1687+
if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
1688+
result = -1;
1689+
free(mapped_parents);
1690+
goto out;
1691+
}
1692+
for (i = 0; i < nparents; i++)
1693+
if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
1694+
result = -1;
1695+
free(mapped_parents);
1696+
goto out;
1697+
}
1698+
write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
1699+
mapped_parents, nparents, author, committer, extra);
1700+
free(mapped_parents);
1701+
1702+
if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
1703+
result = -1;
1704+
goto out;
1705+
}
1706+
}
1707+
1708+
if (sign_commit) {
1709+
struct sig_pairs {
1710+
struct strbuf *sig;
1711+
const struct git_hash_algo *algo;
1712+
} bufs [2] = {
1713+
{ &compat_sig, r->compat_hash_algo },
1714+
{ &sig, r->hash_algo },
1715+
};
1716+
int i;
1717+
1718+
/*
1719+
* We write algorithms in the order they were implemented in
1720+
* Git to produce a stable hash when multiple algorithms are
1721+
* used.
1722+
*/
1723+
if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
1724+
SWAP(bufs[0], bufs[1]);
1725+
1726+
/*
1727+
* We traverse each algorithm in order, and apply the signature
1728+
* to each buffer.
1729+
*/
1730+
for (i = 0; i < ARRAY_SIZE(bufs); i++) {
1731+
if (!bufs[i].algo)
1732+
continue;
1733+
add_commit_signature(&buffer, bufs[i].sig, bufs[i].algo);
1734+
if (r->compat_hash_algo)
1735+
add_commit_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
1736+
}
1737+
}
16621738

1663-
result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
1739+
/* And check the encoding. */
1740+
if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
1741+
fprintf(stderr, _(commit_utf8_warn));
1742+
1743+
if (r->compat_hash_algo) {
1744+
hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
1745+
OBJ_COMMIT, &compat_oid_buf);
1746+
compat_oid = &compat_oid_buf;
1747+
}
1748+
1749+
result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
1750+
ret, compat_oid, 0);
16641751
out:
1752+
free(parent_buf);
16651753
strbuf_release(&buffer);
1754+
strbuf_release(&compat_buffer);
1755+
strbuf_release(&sig);
1756+
strbuf_release(&compat_sig);
16661757
return result;
16671758
}
16681759

0 commit comments

Comments
 (0)