]>
Commit | Line | Data |
---|---|---|
5e9d802a EB |
1 | #include "git-compat-util.h" |
2 | #include "gettext.h" | |
3 | #include "strbuf.h" | |
33a14e81 | 4 | #include "hex.h" |
5e9d802a EB |
5 | #include "repository.h" |
6 | #include "hash-ll.h" | |
33a14e81 | 7 | #include "hash.h" |
5e9d802a | 8 | #include "object.h" |
23b2c7e9 | 9 | #include "loose.h" |
c8762c30 | 10 | #include "commit.h" |
11 | #include "gpg-interface.h" | |
5e9d802a EB |
12 | #include "object-file-convert.h" |
13 | ||
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) | |
16 | { | |
17 | /* | |
18 | * If the source algorithm is not set, then we're using the | |
19 | * default hash algorithm for that object. | |
20 | */ | |
21 | const struct git_hash_algo *from = | |
22 | src->algo ? &hash_algos[src->algo] : repo->hash_algo; | |
23 | ||
24 | if (from == to) { | |
25 | if (src != dest) | |
26 | oidcpy(dest, src); | |
27 | return 0; | |
28 | } | |
23b2c7e9 | 29 | if (repo_loose_object_map_oid(repo, src, to, dest)) { |
30 | /* | |
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. | |
35 | */ | |
36 | repo_read_loose_object_map(repo); | |
37 | if (repo_loose_object_map_oid(repo, src, to, dest)) | |
38 | return -1; | |
39 | } | |
40 | return 0; | |
5e9d802a EB |
41 | } |
42 | ||
33a14e81 | 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) | |
46 | { | |
47 | uint16_t mode; | |
48 | const unsigned hashsz = algo->rawsz; | |
49 | ||
50 | if (size < hashsz + 3 || buf[size - (hashsz + 1)]) { | |
51 | return -1; | |
52 | } | |
53 | ||
54 | *path = parse_mode(buf, &mode); | |
55 | if (!*path || !**path) | |
56 | return -1; | |
57 | *len = strlen(*path) + 1; | |
58 | ||
59 | oidread_algop(oid, (const unsigned char *)*path + *len, algo); | |
60 | return 0; | |
61 | } | |
62 | ||
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) | |
67 | { | |
68 | const char *p = buffer, *end = buffer + size; | |
69 | ||
70 | while (p < end) { | |
71 | struct object_id entry_oid, mapped_oid; | |
72 | const char *path = NULL; | |
73 | size_t pathlen; | |
74 | ||
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; | |
84 | } | |
85 | return 0; | |
86 | } | |
87 | ||
c8762c30 | 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) | |
92 | { | |
ac45d995 EB |
93 | struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT; |
94 | const int entry_len = from->hexsz + 7; | |
c8762c30 | 95 | size_t payload_size; |
96 | struct object_id oid, mapped_oid; | |
97 | const char *p; | |
98 | ||
ac45d995 EB |
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; | |
c8762c30 | 110 | |
111 | /* Is there a signature for our algorithm? */ | |
112 | payload_size = parse_signed_buffer(buffer, size); | |
c8762c30 | 113 | if (payload_size != size) { |
114 | /* Yes, there is. */ | |
115 | strbuf_add(&oursig, buffer + payload_size, size - payload_size); | |
116 | } | |
c8762c30 | 117 | |
ac45d995 EB |
118 | /* Now, is there a signature for the other algorithm? */ |
119 | parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to); | |
c8762c30 | 120 | /* |
121 | * Our payload is now in payload and we may have up to two signatrures | |
122 | * in oursig and othersig. | |
123 | */ | |
ac45d995 EB |
124 | |
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); | |
c8762c30 | 129 | if (oursig.len) |
130 | add_header_signature(out, &oursig, from); | |
ac45d995 EB |
131 | strbuf_addbuf(out, &othersig); |
132 | ||
133 | strbuf_release(&payload); | |
134 | strbuf_release(&othersig); | |
135 | strbuf_release(&oursig); | |
c8762c30 | 136 | return 0; |
137 | } | |
138 | ||
318b023e | 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) | |
143 | { | |
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; | |
08a45903 | 149 | const char *p, *eol; |
318b023e | 150 | |
151 | tail += size; | |
318b023e | 152 | |
08a45903 EB |
153 | while ((bufptr < tail) && (*bufptr != '\n')) { |
154 | eol = memchr(bufptr, '\n', tail - bufptr); | |
155 | if (!eol) | |
156 | return error(_("bad %s in commit"), "line"); | |
157 | ||
158 | if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5)) | |
159 | { | |
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"); | |
164 | ||
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)); | |
169 | } | |
170 | else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7)) | |
171 | { | |
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"); | |
318b023e | 176 | |
08a45903 EB |
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)); | |
318b023e | 180 | |
08a45903 EB |
181 | strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid)); |
182 | } | |
183 | else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9)) | |
184 | { | |
185 | struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT; | |
318b023e | 186 | |
08a45903 EB |
187 | /* Recover the tag object from the mergetag */ |
188 | strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1); | |
189 | ||
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"); | |
196 | } | |
197 | strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1); | |
198 | bufptr = eol + 1; | |
199 | } | |
200 | ||
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; | |
206 | } | |
207 | ||
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); | |
213 | } | |
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; | |
225 | } | |
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; | |
234 | } | |
318b023e | 235 | } |
08a45903 EB |
236 | if (bufptr < tail) |
237 | strbuf_add(out, bufptr, tail - bufptr); | |
318b023e | 238 | return 0; |
239 | } | |
240 | ||
5e9d802a EB |
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) | |
247 | { | |
248 | int ret; | |
249 | ||
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"); | |
253 | ||
254 | switch (type) { | |
318b023e | 255 | case OBJ_COMMIT: |
256 | ret = convert_commit_object(outbuf, from, to, buf, len); | |
257 | break; | |
5e9d802a | 258 | case OBJ_TREE: |
33a14e81 | 259 | ret = convert_tree_object(outbuf, from, to, buf, len); |
260 | break; | |
5e9d802a | 261 | case OBJ_TAG: |
c8762c30 | 262 | ret = convert_tag_object(outbuf, from, to, buf, len); |
263 | break; | |
5e9d802a EB |
264 | default: |
265 | /* Not implemented yet, so fail. */ | |
266 | ret = -1; | |
267 | break; | |
268 | } | |
269 | if (!ret) | |
270 | return 0; | |
271 | if (gentle) { | |
272 | strbuf_release(outbuf); | |
273 | return ret; | |
274 | } | |
275 | die(_("Failed to convert object from %s to %s"), | |
276 | from->name, to->name); | |
277 | } |