git
/
object-file-convert.c
279 строк · 8.1 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "gettext.h"5#include "strbuf.h"6#include "hex.h"7#include "repository.h"8#include "hash.h"9#include "hash.h"10#include "object.h"11#include "loose.h"12#include "commit.h"13#include "gpg-interface.h"14#include "object-file-convert.h"15
16int repo_oid_to_algop(struct repository *repo, const struct object_id *src,17const struct git_hash_algo *to, struct object_id *dest)18{
19/*20* If the source algorithm is not set, then we're using the
21* default hash algorithm for that object.
22*/
23const struct git_hash_algo *from =24src->algo ? &hash_algos[src->algo] : repo->hash_algo;25
26if (from == to) {27if (src != dest)28oidcpy(dest, src);29return 0;30}31if (repo_loose_object_map_oid(repo, src, to, dest)) {32/*33* We may have loaded the object map at repo initialization but
34* another process (perhaps upstream of a pipe from us) may have
35* written a new object into the map. If the object is missing,
36* let's reload the map to see if the object has appeared.
37*/
38repo_read_loose_object_map(repo);39if (repo_loose_object_map_oid(repo, src, to, dest))40return -1;41}42return 0;43}
44
45static int decode_tree_entry_raw(struct object_id *oid, const char **path,46size_t *len, const struct git_hash_algo *algo,47const char *buf, unsigned long size)48{
49uint16_t mode;50const unsigned hashsz = algo->rawsz;51
52if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {53return -1;54}55
56*path = parse_mode(buf, &mode);57if (!*path || !**path)58return -1;59*len = strlen(*path) + 1;60
61oidread(oid, (const unsigned char *)*path + *len, algo);62return 0;63}
64
65static int convert_tree_object(struct strbuf *out,66const struct git_hash_algo *from,67const struct git_hash_algo *to,68const char *buffer, size_t size)69{
70const char *p = buffer, *end = buffer + size;71
72while (p < end) {73struct object_id entry_oid, mapped_oid;74const char *path = NULL;75size_t pathlen;76
77if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,78end - p))79return error(_("failed to decode tree entry"));80if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))81return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));82strbuf_add(out, p, path - p);83strbuf_add(out, path, pathlen);84strbuf_add(out, mapped_oid.hash, to->rawsz);85p = path + pathlen + from->rawsz;86}87return 0;88}
89
90static int convert_tag_object(struct strbuf *out,91const struct git_hash_algo *from,92const struct git_hash_algo *to,93const char *buffer, size_t size)94{
95struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;96const int entry_len = from->hexsz + 7;97size_t payload_size;98struct object_id oid, mapped_oid;99const char *p;100
101/* Consume the object line */102if ((entry_len >= size) ||103memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')104return error("bogus tag object");105if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)106return error("bad tag object ID");107if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))108return error("unable to map tree %s in tag object",109oid_to_hex(&oid));110size -= ((p + 1) - buffer);111buffer = p + 1;112
113/* Is there a signature for our algorithm? */114payload_size = parse_signed_buffer(buffer, size);115if (payload_size != size) {116/* Yes, there is. */117strbuf_add(&oursig, buffer + payload_size, size - payload_size);118}119
120/* Now, is there a signature for the other algorithm? */121parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);122/*123* Our payload is now in payload and we may have up to two signatrures
124* in oursig and othersig.
125*/
126
127/* Add some slop for longer signature header in the new algorithm. */128strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);129strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));130strbuf_addbuf(out, &payload);131if (oursig.len)132add_header_signature(out, &oursig, from);133strbuf_addbuf(out, &othersig);134
135strbuf_release(&payload);136strbuf_release(&othersig);137strbuf_release(&oursig);138return 0;139}
140
141static int convert_commit_object(struct strbuf *out,142const struct git_hash_algo *from,143const struct git_hash_algo *to,144const char *buffer, size_t size)145{
146const char *tail = buffer;147const char *bufptr = buffer;148const int tree_entry_len = from->hexsz + 5;149const int parent_entry_len = from->hexsz + 7;150struct object_id oid, mapped_oid;151const char *p, *eol;152
153tail += size;154
155while ((bufptr < tail) && (*bufptr != '\n')) {156eol = memchr(bufptr, '\n', tail - bufptr);157if (!eol)158return error(_("bad %s in commit"), "line");159
160if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))161{162if (((bufptr + tree_entry_len) != eol) ||163parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||164(p != eol))165return error(_("bad %s in commit"), "tree");166
167if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))168return error(_("unable to map %s %s in commit object"),169"tree", oid_to_hex(&oid));170strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));171}172else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))173{174if (((bufptr + parent_entry_len) != eol) ||175parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||176(p != eol))177return error(_("bad %s in commit"), "parent");178
179if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))180return error(_("unable to map %s %s in commit object"),181"parent", oid_to_hex(&oid));182
183strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));184}185else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))186{187struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;188
189/* Recover the tag object from the mergetag */190strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);191
192bufptr = eol + 1;193while ((bufptr < tail) && (*bufptr == ' ')) {194eol = memchr(bufptr, '\n', tail - bufptr);195if (!eol) {196strbuf_release(&tag);197return error(_("bad %s in commit"), "mergetag continuation");198}199strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);200bufptr = eol + 1;201}202
203/* Compute the new tag object */204if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {205strbuf_release(&tag);206strbuf_release(&new_tag);207return -1;208}209
210/* Write the new mergetag */211strbuf_addstr(out, "mergetag");212strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);213strbuf_release(&tag);214strbuf_release(&new_tag);215}216else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))217strbuf_add(out, bufptr, (eol - bufptr) + 1);218else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))219strbuf_add(out, bufptr, (eol - bufptr) + 1);220else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))221strbuf_add(out, bufptr, (eol - bufptr) + 1);222else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))223strbuf_add(out, bufptr, (eol - bufptr) + 1);224else {225/* Unknown line fail it might embed an oid */226return -1;227}228/* Consume any trailing continuation lines */229bufptr = eol + 1;230while ((bufptr < tail) && (*bufptr == ' ')) {231eol = memchr(bufptr, '\n', tail - bufptr);232if (!eol)233return error(_("bad %s in commit"), "continuation");234strbuf_add(out, bufptr, (eol - bufptr) + 1);235bufptr = eol + 1;236}237}238if (bufptr < tail)239strbuf_add(out, bufptr, tail - bufptr);240return 0;241}
242
243int convert_object_file(struct strbuf *outbuf,244const struct git_hash_algo *from,245const struct git_hash_algo *to,246const void *buf, size_t len,247enum object_type type,248int gentle)249{
250int ret;251
252/* Don't call this function when no conversion is necessary */253if ((from == to) || (type == OBJ_BLOB))254BUG("Refusing noop object file conversion");255
256switch (type) {257case OBJ_COMMIT:258ret = convert_commit_object(outbuf, from, to, buf, len);259break;260case OBJ_TREE:261ret = convert_tree_object(outbuf, from, to, buf, len);262break;263case OBJ_TAG:264ret = convert_tag_object(outbuf, from, to, buf, len);265break;266default:267/* Not implemented yet, so fail. */268ret = -1;269break;270}271if (!ret)272return 0;273if (gentle) {274strbuf_release(outbuf);275return ret;276}277die(_("Failed to convert object from %s to %s"),278from->name, to->name);279}
280