summaryrefslogtreecommitdiff
path: root/delta.c
diff options
context:
space:
mode:
Diffstat (limited to 'delta.c')
-rw-r--r--delta.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/delta.c b/delta.c
new file mode 100644
index 0000000..05ef7fd
--- /dev/null
+++ b/delta.c
@@ -0,0 +1,115 @@
+#include "object.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "tag.h"
+#include "delta.h"
+#include "cache.h"
+#include <string.h>
+
+/* the delta object definition (it can alias any other object) */
+struct delta {
+ union {
+ struct object object;
+ struct blob blob;
+ struct tree tree;
+ struct commit commit;
+ struct tag tag;
+ } u;
+};
+
+struct delta *lookup_delta(unsigned char *sha1)
+{
+ struct object *obj = lookup_object(sha1);
+ if (!obj) {
+ struct delta *ret = xmalloc(sizeof(struct delta));
+ memset(ret, 0, sizeof(struct delta));
+ created_object(sha1, &ret->u.object);
+ return ret;
+ }
+ return (struct delta *) obj;
+}
+
+int parse_delta_buffer(struct delta *item, void *buffer, unsigned long size)
+{
+ struct object *reference;
+ struct object_list *p;
+
+ if (item->u.object.delta)
+ return 0;
+ item->u.object.delta = 1;
+ if (size <= 20)
+ return -1;
+ reference = lookup_object(buffer);
+ if (!reference) {
+ struct delta *ref = xmalloc(sizeof(struct delta));
+ memset(ref, 0, sizeof(struct delta));
+ created_object(buffer, &ref->u.object);
+ reference = &ref->u.object;
+ }
+
+ p = xmalloc(sizeof(*p));
+ p->item = &item->u.object;
+ p->next = reference->attached_deltas;
+ reference->attached_deltas = p;
+ return 0;
+}
+
+int process_deltas(void *src, unsigned long src_size, const char *src_type,
+ struct object_list *delta_list)
+{
+ int deepest = 0;
+ do {
+ struct object *obj = delta_list->item;
+ static char type[10];
+ void *map, *delta, *buf;
+ unsigned long map_size, delta_size, buf_size;
+ map = map_sha1_file(obj->sha1, &map_size);
+ if (!map)
+ continue;
+ delta = unpack_sha1_file(map, map_size, type, &delta_size);
+ munmap(map, map_size);
+ if (!delta)
+ continue;
+ if (strcmp(type, "delta") || delta_size <= 20) {
+ free(delta);
+ continue;
+ }
+ buf = patch_delta(src, src_size,
+ delta+20, delta_size-20,
+ &buf_size);
+ free(delta);
+ if (!buf)
+ continue;
+ if (check_sha1_signature(obj->sha1, buf, buf_size, src_type) < 0)
+ printf("sha1 mismatch for delta %s\n", sha1_to_hex(obj->sha1));
+ if (obj->type && obj->type != src_type) {
+ error("got %s when expecting %s for delta %s",
+ src_type, obj->type, sha1_to_hex(obj->sha1));
+ free(buf);
+ continue;
+ }
+ obj->type = src_type;
+ if (src_type == blob_type) {
+ parse_blob_buffer((struct blob *)obj, buf, buf_size);
+ } else if (src_type == tree_type) {
+ parse_tree_buffer((struct tree *)obj, buf, buf_size);
+ } else if (src_type == commit_type) {
+ parse_commit_buffer((struct commit *)obj, buf, buf_size);
+ } else if (src_type == tag_type) {
+ parse_tag_buffer((struct tag *)obj, buf, buf_size);
+ } else {
+ error("unknown object type %s", src_type);
+ free(buf);
+ continue;
+ }
+ if (obj->attached_deltas) {
+ int depth = process_deltas(buf, buf_size, src_type,
+ obj->attached_deltas);
+ if (deepest < depth)
+ deepest = depth;
+ }
+ free(buf);
+ } while ((delta_list = delta_list->next));
+ return deepest + 1;
+}