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