1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-06-11 13:57:04 +02:00
git/object-file-convert.c

174 lines
4.8 KiB
C
Raw Normal View History

#include "git-compat-util.h"
#include "gettext.h"
#include "strbuf.h"
#include "hex.h"
#include "repository.h"
#include "hash-ll.h"
#include "hash.h"
#include "object.h"
#include "loose.h"
#include "commit.h"
#include "gpg-interface.h"
#include "object-file-convert.h"
int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
const struct git_hash_algo *to, struct object_id *dest)
{
/*
* If the source algorithm is not set, then we're using the
* default hash algorithm for that object.
*/
const struct git_hash_algo *from =
src->algo ? &hash_algos[src->algo] : repo->hash_algo;
if (from == to) {
if (src != dest)
oidcpy(dest, src);
return 0;
}
if (repo_loose_object_map_oid(repo, src, to, dest)) {
/*
* We may have loaded the object map at repo initialization but
* another process (perhaps upstream of a pipe from us) may have
* written a new object into the map. If the object is missing,
* let's reload the map to see if the object has appeared.
*/
repo_read_loose_object_map(repo);
if (repo_loose_object_map_oid(repo, src, to, dest))
return -1;
}
return 0;
}
static int decode_tree_entry_raw(struct object_id *oid, const char **path,
size_t *len, const struct git_hash_algo *algo,
const char *buf, unsigned long size)
{
uint16_t mode;
const unsigned hashsz = algo->rawsz;
if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
return -1;
}
*path = parse_mode(buf, &mode);
if (!*path || !**path)
return -1;
*len = strlen(*path) + 1;
oidread_algop(oid, (const unsigned char *)*path + *len, algo);
return 0;
}
static int convert_tree_object(struct strbuf *out,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const char *buffer, size_t size)
{
const char *p = buffer, *end = buffer + size;
while (p < end) {
struct object_id entry_oid, mapped_oid;
const char *path = NULL;
size_t pathlen;
if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
end - p))
return error(_("failed to decode tree entry"));
if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
strbuf_add(out, p, path - p);
strbuf_add(out, path, pathlen);
strbuf_add(out, mapped_oid.hash, to->rawsz);
p = path + pathlen + from->rawsz;
}
return 0;
}
static int convert_tag_object(struct strbuf *out,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const char *buffer, size_t size)
{
struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
const int entry_len = from->hexsz + 7;
size_t payload_size;
struct object_id oid, mapped_oid;
const char *p;
/* Consume the object line */
if ((entry_len >= size) ||
memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
return error("bogus tag object");
if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
return error("bad tag object ID");
if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
return error("unable to map tree %s in tag object",
oid_to_hex(&oid));
size -= ((p + 1) - buffer);
buffer = p + 1;
/* Is there a signature for our algorithm? */
payload_size = parse_signed_buffer(buffer, size);
if (payload_size != size) {
/* Yes, there is. */
strbuf_add(&oursig, buffer + payload_size, size - payload_size);
}
/* Now, is there a signature for the other algorithm? */
parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
/*
* Our payload is now in payload and we may have up to two signatrures
* in oursig and othersig.
*/
/* Add some slop for longer signature header in the new algorithm. */
strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
strbuf_addbuf(out, &payload);
if (oursig.len)
add_header_signature(out, &oursig, from);
strbuf_addbuf(out, &othersig);
strbuf_release(&payload);
strbuf_release(&othersig);
strbuf_release(&oursig);
return 0;
}
int convert_object_file(struct strbuf *outbuf,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const void *buf, size_t len,
enum object_type type,
int gentle)
{
int ret;
/* Don't call this function when no conversion is necessary */
if ((from == to) || (type == OBJ_BLOB))
BUG("Refusing noop object file conversion");
switch (type) {
case OBJ_TREE:
ret = convert_tree_object(outbuf, from, to, buf, len);
break;
case OBJ_TAG:
ret = convert_tag_object(outbuf, from, to, buf, len);
break;
case OBJ_COMMIT:
default:
/* Not implemented yet, so fail. */
ret = -1;
break;
}
if (!ret)
return 0;
if (gentle) {
strbuf_release(outbuf);
return ret;
}
die(_("Failed to convert object from %s to %s"),
from->name, to->name);
}