Git 2.46-rc2
[git/debian.git] / object-file-convert.c
blob3887d6d57b7965074702829403b36518ab7022e6
1 #define USE_THE_REPOSITORY_VARIABLE
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"
16 int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
17 const struct git_hash_algo *to, struct object_id *dest)
20 * If the source algorithm is not set, then we're using the
21 * default hash algorithm for that object.
23 const struct git_hash_algo *from =
24 src->algo ? &hash_algos[src->algo] : repo->hash_algo;
26 if (from == to) {
27 if (src != dest)
28 oidcpy(dest, src);
29 return 0;
31 if (repo_loose_object_map_oid(repo, src, to, dest)) {
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.
38 repo_read_loose_object_map(repo);
39 if (repo_loose_object_map_oid(repo, src, to, dest))
40 return -1;
42 return 0;
45 static int decode_tree_entry_raw(struct object_id *oid, const char **path,
46 size_t *len, const struct git_hash_algo *algo,
47 const char *buf, unsigned long size)
49 uint16_t mode;
50 const unsigned hashsz = algo->rawsz;
52 if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
53 return -1;
56 *path = parse_mode(buf, &mode);
57 if (!*path || !**path)
58 return -1;
59 *len = strlen(*path) + 1;
61 oidread(oid, (const unsigned char *)*path + *len, algo);
62 return 0;
65 static int convert_tree_object(struct strbuf *out,
66 const struct git_hash_algo *from,
67 const struct git_hash_algo *to,
68 const char *buffer, size_t size)
70 const char *p = buffer, *end = buffer + size;
72 while (p < end) {
73 struct object_id entry_oid, mapped_oid;
74 const char *path = NULL;
75 size_t pathlen;
77 if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
78 end - p))
79 return error(_("failed to decode tree entry"));
80 if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
81 return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
82 strbuf_add(out, p, path - p);
83 strbuf_add(out, path, pathlen);
84 strbuf_add(out, mapped_oid.hash, to->rawsz);
85 p = path + pathlen + from->rawsz;
87 return 0;
90 static int convert_tag_object(struct strbuf *out,
91 const struct git_hash_algo *from,
92 const struct git_hash_algo *to,
93 const char *buffer, size_t size)
95 struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
96 const int entry_len = from->hexsz + 7;
97 size_t payload_size;
98 struct object_id oid, mapped_oid;
99 const char *p;
101 /* Consume the object line */
102 if ((entry_len >= size) ||
103 memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
104 return error("bogus tag object");
105 if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
106 return error("bad tag object ID");
107 if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
108 return error("unable to map tree %s in tag object",
109 oid_to_hex(&oid));
110 size -= ((p + 1) - buffer);
111 buffer = p + 1;
113 /* Is there a signature for our algorithm? */
114 payload_size = parse_signed_buffer(buffer, size);
115 if (payload_size != size) {
116 /* Yes, there is. */
117 strbuf_add(&oursig, buffer + payload_size, size - payload_size);
120 /* Now, is there a signature for the other algorithm? */
121 parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
123 * Our payload is now in payload and we may have up to two signatrures
124 * in oursig and othersig.
127 /* Add some slop for longer signature header in the new algorithm. */
128 strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
129 strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
130 strbuf_addbuf(out, &payload);
131 if (oursig.len)
132 add_header_signature(out, &oursig, from);
133 strbuf_addbuf(out, &othersig);
135 strbuf_release(&payload);
136 strbuf_release(&othersig);
137 strbuf_release(&oursig);
138 return 0;
141 static int convert_commit_object(struct strbuf *out,
142 const struct git_hash_algo *from,
143 const struct git_hash_algo *to,
144 const char *buffer, size_t size)
146 const char *tail = buffer;
147 const char *bufptr = buffer;
148 const int tree_entry_len = from->hexsz + 5;
149 const int parent_entry_len = from->hexsz + 7;
150 struct object_id oid, mapped_oid;
151 const char *p, *eol;
153 tail += size;
155 while ((bufptr < tail) && (*bufptr != '\n')) {
156 eol = memchr(bufptr, '\n', tail - bufptr);
157 if (!eol)
158 return error(_("bad %s in commit"), "line");
160 if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
162 if (((bufptr + tree_entry_len) != eol) ||
163 parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
164 (p != eol))
165 return error(_("bad %s in commit"), "tree");
167 if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
168 return error(_("unable to map %s %s in commit object"),
169 "tree", oid_to_hex(&oid));
170 strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
172 else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
174 if (((bufptr + parent_entry_len) != eol) ||
175 parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
176 (p != eol))
177 return error(_("bad %s in commit"), "parent");
179 if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
180 return error(_("unable to map %s %s in commit object"),
181 "parent", oid_to_hex(&oid));
183 strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
185 else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
187 struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
189 /* Recover the tag object from the mergetag */
190 strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
192 bufptr = eol + 1;
193 while ((bufptr < tail) && (*bufptr == ' ')) {
194 eol = memchr(bufptr, '\n', tail - bufptr);
195 if (!eol) {
196 strbuf_release(&tag);
197 return error(_("bad %s in commit"), "mergetag continuation");
199 strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
200 bufptr = eol + 1;
203 /* Compute the new tag object */
204 if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
205 strbuf_release(&tag);
206 strbuf_release(&new_tag);
207 return -1;
210 /* Write the new mergetag */
211 strbuf_addstr(out, "mergetag");
212 strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
213 strbuf_release(&tag);
214 strbuf_release(&new_tag);
216 else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
217 strbuf_add(out, bufptr, (eol - bufptr) + 1);
218 else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
219 strbuf_add(out, bufptr, (eol - bufptr) + 1);
220 else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
221 strbuf_add(out, bufptr, (eol - bufptr) + 1);
222 else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
223 strbuf_add(out, bufptr, (eol - bufptr) + 1);
224 else {
225 /* Unknown line fail it might embed an oid */
226 return -1;
228 /* Consume any trailing continuation lines */
229 bufptr = eol + 1;
230 while ((bufptr < tail) && (*bufptr == ' ')) {
231 eol = memchr(bufptr, '\n', tail - bufptr);
232 if (!eol)
233 return error(_("bad %s in commit"), "continuation");
234 strbuf_add(out, bufptr, (eol - bufptr) + 1);
235 bufptr = eol + 1;
238 if (bufptr < tail)
239 strbuf_add(out, bufptr, tail - bufptr);
240 return 0;
243 int convert_object_file(struct strbuf *outbuf,
244 const struct git_hash_algo *from,
245 const struct git_hash_algo *to,
246 const void *buf, size_t len,
247 enum object_type type,
248 int gentle)
250 int ret;
252 /* Don't call this function when no conversion is necessary */
253 if ((from == to) || (type == OBJ_BLOB))
254 BUG("Refusing noop object file conversion");
256 switch (type) {
257 case OBJ_COMMIT:
258 ret = convert_commit_object(outbuf, from, to, buf, len);
259 break;
260 case OBJ_TREE:
261 ret = convert_tree_object(outbuf, from, to, buf, len);
262 break;
263 case OBJ_TAG:
264 ret = convert_tag_object(outbuf, from, to, buf, len);
265 break;
266 default:
267 /* Not implemented yet, so fail. */
268 ret = -1;
269 break;
271 if (!ret)
272 return 0;
273 if (gentle) {
274 strbuf_release(outbuf);
275 return ret;
277 die(_("Failed to convert object from %s to %s"),
278 from->name, to->name);