new_commit ::= 'commit' sp ref_str lf
mark?
- ('author' sp name '<' email '>' ts tz lf)?
- 'committer' sp name '<' email '>' ts tz lf
+ ('author' sp name '<' email '>' when lf)?
+ 'committer' sp name '<' email '>' when lf
commit_msg
('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
lf;
commit_msg ::= data;
- file_change ::= file_del | file_obm | file_inm;
+ file_change ::= file_clr | file_del | file_obm | file_inm;
+ file_clr ::= 'deleteall' lf;
file_del ::= 'D' sp path_str lf;
file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
new_tag ::= 'tag' sp tag_str lf
'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
- 'tagger' sp name '<' email '>' ts tz lf
+ 'tagger' sp name '<' email '>' when lf
tag_msg;
tag_msg ::= data;
path_str ::= path | '"' quoted(path) '"' ;
mode ::= '100644' | '644'
| '100755' | '755'
- | '140000'
+ | '120000'
;
declen ::= # unsigned 32 bit value, ascii base10 notation;
bigint ::= # unsigned integer value, ascii base10 notation;
binary_data ::= # file content, not interpreted;
+ when ::= raw_when | rfc2822_when;
+ raw_when ::= ts sp tz;
+ rfc2822_when ::= # Valid RFC 2822 date and time;
+
sp ::= # ASCII space character;
lf ::= # ASCII newline (LF) character;
#include "object.h"
#include "blob.h"
#include "tree.h"
+#include "commit.h"
#include "delta.h"
#include "pack.h"
#include "refs.h"
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+#ifndef PRIuMAX
+#define PRIuMAX "llu"
+#endif
+
struct object_entry
{
struct object_entry *next;
unsigned char sha1[20];
};
+typedef enum {
+ WHENSPEC_RAW = 1,
+ WHENSPEC_RFC2822,
+ WHENSPEC_NOW,
+} whenspec_type;
+
/* Configured limits on output */
static unsigned long max_depth = 10;
static unsigned long max_packsize = (1LL << 32) - 1;
+static int force_update;
/* Stats and misc. counters */
static uintmax_t alloc_count;
static unsigned long object_count;
static unsigned long branch_count;
static unsigned long branch_load_count;
+static int failure;
+static FILE *pack_edges;
/* Memory pools */
static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
static struct tag *last_tag;
/* Input stream parsing */
+static whenspec_type whenspec = WHENSPEC_RAW;
static struct strbuf command_buf;
static uintmax_t next_mark;
static struct dbuf new_data;
alloc_count += cnt;
}
-static struct object_entry* new_object(unsigned char *sha1)
+static struct object_entry *new_object(unsigned char *sha1)
{
struct object_entry *e;
return e;
}
-static struct object_entry* find_object(unsigned char *sha1)
+static struct object_entry *find_object(unsigned char *sha1)
{
unsigned int h = sha1[0] << 8 | sha1[1];
struct object_entry *e;
return NULL;
}
-static struct object_entry* insert_object(unsigned char *sha1)
+static struct object_entry *insert_object(unsigned char *sha1)
{
unsigned int h = sha1[0] << 8 | sha1[1];
struct object_entry *e = object_table[h];
return r;
}
-static void* pool_alloc(size_t len)
+static void *pool_alloc(size_t len)
{
struct mem_pool *p;
void *r;
return r;
}
-static void* pool_calloc(size_t count, size_t size)
+static void *pool_calloc(size_t count, size_t size)
{
size_t len = count * size;
void *r = pool_alloc(len);
return r;
}
-static char* pool_strdup(const char *s)
+static char *pool_strdup(const char *s)
{
char *r = pool_alloc(strlen(s) + 1);
strcpy(r, s);
s->data.marked[idnum] = oe;
}
-static struct object_entry* find_mark(uintmax_t idnum)
+static struct object_entry *find_mark(uintmax_t idnum)
{
uintmax_t orig_idnum = idnum;
struct mark_set *s = marks;
oe = s->data.marked[idnum];
}
if (!oe)
- die("mark :%ju not declared", orig_idnum);
+ die("mark :%" PRIuMAX " not declared", orig_idnum);
return oe;
}
-static struct atom_str* to_atom(const char *s, unsigned short len)
+static struct atom_str *to_atom(const char *s, unsigned short len)
{
unsigned int hc = hc_str(s, len) % atom_table_sz;
struct atom_str *c;
return c;
}
-static struct branch* lookup_branch(const char *name)
+static struct branch *lookup_branch(const char *name)
{
unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
struct branch *b;
return NULL;
}
-static struct branch* new_branch(const char *name)
+static struct branch *new_branch(const char *name)
{
unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
struct branch* b = lookup_branch(name);
return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1;
}
-static struct tree_content* new_tree_content(unsigned int cnt)
+static struct tree_content *new_tree_content(unsigned int cnt)
{
struct avail_tree_content *f, *l = NULL;
struct tree_content *t;
release_tree_content(t);
}
-static struct tree_content* grow_tree_content(
+static struct tree_content *grow_tree_content(
struct tree_content *t,
int amt)
{
return r;
}
-static struct tree_entry* new_tree_entry(void)
+static struct tree_entry *new_tree_entry(void)
{
struct tree_entry *e;
return hashcmp(a->sha1, b->sha1);
}
-static char* create_index(void)
+static char *create_index(void)
{
static char tmpfile[PATH_MAX];
SHA_CTX ctx;
return tmpfile;
}
-static char* keep_pack(char *curr_index_name)
+static char *keep_pack(char *curr_index_name)
{
static char name[PATH_MAX];
static char *keep_msg = "fast-import";
install_packed_git(new_p);
/* Print the boundary */
- fprintf(stdout, "%s:", new_p->pack_name);
- for (i = 0; i < branch_table_sz; i++) {
- for (b = branch_table[i]; b; b = b->table_next_branch) {
- if (b->pack_id == pack_id)
- fprintf(stdout, " %s", sha1_to_hex(b->sha1));
+ if (pack_edges) {
+ fprintf(pack_edges, "%s:", new_p->pack_name);
+ for (i = 0; i < branch_table_sz; i++) {
+ for (b = branch_table[i]; b; b = b->table_next_branch) {
+ if (b->pack_id == pack_id)
+ fprintf(pack_edges, " %s", sha1_to_hex(b->sha1));
+ }
}
+ for (t = first_tag; t; t = t->next_tag) {
+ if (t->pack_id == pack_id)
+ fprintf(pack_edges, " %s", sha1_to_hex(t->sha1));
+ }
+ fputc('\n', pack_edges);
+ fflush(pack_edges);
}
- for (t = first_tag; t; t = t->next_tag) {
- if (t->pack_id == pack_id)
- fprintf(stdout, " %s", sha1_to_hex(t->sha1));
- }
- fputc('\n', stdout);
pack_id++;
}
last_blob.depth = 0;
}
-static void checkpoint(void)
+static void cycle_packfile(void)
{
end_packfile();
start_packfile();
SHA_CTX c;
z_stream s;
- hdrlen = sprintf((char*)hdr,"%s %lu",type_names[type],datlen) + 1;
+ hdrlen = sprintf((char*)hdr,"%s %lu", type_names[type],
+ (unsigned long)datlen) + 1;
SHA1_Init(&c);
SHA1_Update(&c, hdr, hdrlen);
SHA1_Update(&c, dat, datlen);
/* This new object needs to *not* have the current pack_id. */
e->pack_id = pack_id + 1;
- checkpoint();
+ cycle_packfile();
/* We cannot carry a delta into the new pack. */
if (delta) {
pack_size += s.total_out;
free(out);
- if (delta)
- free(delta);
+ free(delta);
if (last) {
- if (last->data && !last->no_free)
+ if (!last->no_free)
free(last->data);
last->data = dat;
last->offset = e->offset;
return 1;
}
-static void dump_branches(void)
+static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
+ struct ref_lock *lock;
+ unsigned char old_sha1[20];
+
+ if (read_ref(b->name, old_sha1))
+ hashclr(old_sha1);
+ lock = lock_any_ref_for_update(b->name, old_sha1);
+ if (!lock)
+ return error("Unable to lock %s", b->name);
+ if (!force_update && !is_null_sha1(old_sha1)) {
+ struct commit *old_cmit, *new_cmit;
+
+ old_cmit = lookup_commit_reference_gently(old_sha1, 0);
+ new_cmit = lookup_commit_reference_gently(b->sha1, 0);
+ if (!old_cmit || !new_cmit) {
+ unlock_ref(lock);
+ return error("Branch %s is missing commits.", b->name);
+ }
+
+ if (!in_merge_bases(old_cmit, new_cmit)) {
+ unlock_ref(lock);
+ warn("Not updating %s"
+ " (new tip %s does not contain %s)",
+ b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
+ return -1;
+ }
+ }
+ if (write_ref_sha1(lock, b->sha1, msg) < 0)
+ return error("Unable to update %s", b->name);
+ return 0;
+}
+
+static void dump_branches(void)
+{
unsigned int i;
struct branch *b;
- struct ref_lock *lock;
for (i = 0; i < branch_table_sz; i++) {
- for (b = branch_table[i]; b; b = b->table_next_branch) {
- lock = lock_any_ref_for_update(b->name, NULL);
- if (!lock || write_ref_sha1(lock, b->sha1, msg) < 0)
- die("Can't write %s", b->name);
- }
+ for (b = branch_table[i]; b; b = b->table_next_branch)
+ failure |= update_branch(b);
}
}
static const char *msg = "fast-import";
struct tag *t;
struct ref_lock *lock;
- char path[PATH_MAX];
+ char ref_name[PATH_MAX];
for (t = first_tag; t; t = t->next_tag) {
- sprintf(path, "refs/tags/%s", t->name);
- lock = lock_any_ref_for_update(path, NULL);
+ sprintf(ref_name, "tags/%s", t->name);
+ lock = lock_ref_sha1(ref_name, NULL);
if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
- die("Can't write %s", path);
+ failure |= error("Unable to update %s", ref_name);
}
}
} else {
for (k = 0; k < 1024; k++) {
if (m->data.marked[k])
- fprintf(f, ":%ju %s\n", base + k,
+ fprintf(f, ":%" PRIuMAX " %s\n", base + k,
sha1_to_hex(m->data.marked[k]->sha1));
}
}
if (mark_file)
{
FILE *f = fopen(mark_file, "w");
- dump_marks_helper(f, 0, marks);
- fclose(f);
+ if (f) {
+ dump_marks_helper(f, 0, marks);
+ fclose(f);
+ } else
+ failure |= error("Unable to write marks file %s: %s",
+ mark_file, strerror(errno));
}
}
next_mark = 0;
}
-static void* cmd_data (size_t *size)
+static void *cmd_data (size_t *size)
{
size_t length;
char *buffer;
while (n < length) {
size_t s = fread(buffer + n, 1, length - n, stdin);
if (!s && feof(stdin))
- die("EOF in data (%lu bytes remaining)", length - n);
+ die("EOF in data (%lu bytes remaining)",
+ (unsigned long)(length - n));
n += s;
}
}
return buffer;
}
+static int validate_raw_date(const char *src, char *result, int maxlen)
+{
+ const char *orig_src = src;
+ char *endp, sign;
+
+ strtoul(src, &endp, 10);
+ if (endp == src || *endp != ' ')
+ return -1;
+
+ src = endp + 1;
+ if (*src != '-' && *src != '+')
+ return -1;
+ sign = *src;
+
+ strtoul(src + 1, &endp, 10);
+ if (endp == src || *endp || (endp - orig_src) >= maxlen)
+ return -1;
+
+ strcpy(result, orig_src);
+ return 0;
+}
+
+static char *parse_ident(const char *buf)
+{
+ const char *gt;
+ size_t name_len;
+ char *ident;
+
+ gt = strrchr(buf, '>');
+ if (!gt)
+ die("Missing > in ident string: %s", buf);
+ gt++;
+ if (*gt != ' ')
+ die("Missing space after > in ident string: %s", buf);
+ gt++;
+ name_len = gt - buf;
+ ident = xmalloc(name_len + 24);
+ strncpy(ident, buf, name_len);
+
+ switch (whenspec) {
+ case WHENSPEC_RAW:
+ if (validate_raw_date(gt, ident + name_len, 24) < 0)
+ die("Invalid raw date \"%s\" in ident: %s", gt, buf);
+ break;
+ case WHENSPEC_RFC2822:
+ if (parse_date(gt, ident + name_len, 24) < 0)
+ die("Invalid rfc2822 date \"%s\" in ident: %s", gt, buf);
+ break;
+ case WHENSPEC_NOW:
+ if (strcmp("now", gt))
+ die("Date in ident must be 'now': %s", buf);
+ datestamp(ident + name_len, 24);
+ break;
+ }
+
+ return ident;
+}
+
static void cmd_new_blob(void)
{
size_t l;
const char *p = command_buf.buf + 2;
char *p_uq;
const char *endp;
- struct object_entry *oe;
+ struct object_entry *oe = oe;
unsigned char sha1[20];
uint16_t mode, inline_data = 0;
char type[20];
}
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode);
-
- if (p_uq)
- free(p_uq);
+ free(p_uq);
}
static void file_change_d(struct branch *b)
p = p_uq;
}
tree_content_remove(&b->branch_tree, p);
- if (p_uq)
- free(p_uq);
+ free(p_uq);
+}
+
+static void file_change_deleteall(struct branch *b)
+{
+ release_tree_content_recursive(b->branch_tree.tree);
+ hashclr(b->branch_tree.versions[0].sha1);
+ hashclr(b->branch_tree.versions[1].sha1);
+ load_tree(&b->branch_tree);
}
static void cmd_from(struct branch *b)
if (strncmp("from ", command_buf.buf, 5))
return;
- if (b->last_commit)
- die("Can't reinitailize branch %s", b->name);
+ if (b->branch_tree.tree) {
+ release_tree_content_recursive(b->branch_tree.tree);
+ b->branch_tree.tree = NULL;
+ }
from = strchr(command_buf.buf, ' ') + 1;
s = lookup_branch(from);
unsigned long size;
char *buf;
if (oe->type != OBJ_COMMIT)
- die("Mark :%ju not a commit", idnum);
+ die("Mark :%" PRIuMAX " not a commit", idnum);
hashcpy(b->sha1, oe->sha1);
buf = gfi_unpack_entry(oe, &size);
if (!buf || size < 46)
read_next_command();
}
-static struct hash_list* cmd_merge(unsigned int *count)
+static struct hash_list *cmd_merge(unsigned int *count)
{
- struct hash_list *list = NULL, *n, *e;
+ struct hash_list *list = NULL, *n, *e = e;
const char *from;
struct branch *s;
uintmax_t idnum = strtoumax(from + 1, NULL, 10);
struct object_entry *oe = find_mark(idnum);
if (oe->type != OBJ_COMMIT)
- die("Mark :%ju not a commit", idnum);
+ die("Mark :%" PRIuMAX " not a commit", idnum);
hashcpy(n->sha1, oe->sha1);
} else if (get_sha1(from, n->sha1))
die("Invalid ref name or SHA1 expression: %s", from);
else
list = n;
e = n;
- *count++;
+ (*count)++;
read_next_command();
}
return list;
read_next_command();
cmd_mark();
if (!strncmp("author ", command_buf.buf, 7)) {
- author = strdup(command_buf.buf);
+ author = parse_ident(command_buf.buf + 7);
read_next_command();
}
if (!strncmp("committer ", command_buf.buf, 10)) {
- committer = strdup(command_buf.buf);
+ committer = parse_ident(command_buf.buf + 10);
read_next_command();
}
if (!committer)
file_change_m(b);
else if (!strncmp("D ", command_buf.buf, 2))
file_change_d(b);
+ else if (!strcmp("deleteall", command_buf.buf))
+ file_change_deleteall(b);
else
die("Unsupported file_change: %s", command_buf.buf);
read_next_command();
store_tree(&b->branch_tree);
hashcpy(b->branch_tree.versions[0].sha1,
b->branch_tree.versions[1].sha1);
- size_dbuf(&new_data, 97 + msglen
+ size_dbuf(&new_data, 114 + msglen
+ merge_count * 49
+ (author
? strlen(author) + strlen(committer)
free(merge_list);
merge_list = next;
}
- if (author)
- sp += sprintf(sp, "%s\n", author);
- else
- sp += sprintf(sp, "author %s\n", committer + 10);
- sp += sprintf(sp, "%s\n\n", committer);
+ sp += sprintf(sp, "author %s\n", author ? author : committer);
+ sp += sprintf(sp, "committer %s\n", committer);
+ *sp++ = '\n';
memcpy(sp, msg, msglen);
sp += msglen;
- if (author)
- free(author);
+ free(author);
free(committer);
free(msg);
if (s) {
hashcpy(sha1, s->sha1);
} else if (*from == ':') {
+ struct object_entry *oe;
from_mark = strtoumax(from + 1, NULL, 10);
- struct object_entry *oe = find_mark(from_mark);
+ oe = find_mark(from_mark);
if (oe->type != OBJ_COMMIT)
- die("Mark :%ju not a commit", from_mark);
+ die("Mark :%" PRIuMAX " not a commit", from_mark);
hashcpy(sha1, oe->sha1);
} else if (!get_sha1(from, sha1)) {
unsigned long size;
/* tagger ... */
if (strncmp("tagger ", command_buf.buf, 7))
die("Expected tagger command, got %s", command_buf.buf);
- tagger = strdup(command_buf.buf);
+ tagger = parse_ident(command_buf.buf + 7);
/* tag payload/message */
read_next_command();
sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
sp += sprintf(sp, "type %s\n", type_names[OBJ_COMMIT]);
sp += sprintf(sp, "tag %s\n", t->name);
- sp += sprintf(sp, "%s\n\n", tagger);
+ sp += sprintf(sp, "tagger %s\n", tagger);
+ *sp++ = '\n';
memcpy(sp, msg, msglen);
sp += msglen;
free(tagger);
sp = strchr(command_buf.buf, ' ') + 1;
b = lookup_branch(sp);
if (b) {
- b->last_commit = 0;
+ hashclr(b->sha1);
+ hashclr(b->branch_tree.versions[0].sha1);
+ hashclr(b->branch_tree.versions[1].sha1);
if (b->branch_tree.tree) {
release_tree_content_recursive(b->branch_tree.tree);
b->branch_tree.tree = NULL;
static void cmd_checkpoint(void)
{
- if (object_count)
- checkpoint();
+ if (object_count) {
+ cycle_packfile();
+ dump_branches();
+ dump_tags();
+ dump_marks();
+ }
read_next_command();
}
static const char fast_import_usage[] =
-"git-fast-import [--depth=n] [--active-branches=n] [--export-marks=marks.file] [--branch-log=log]";
+"git-fast-import [--date-format=f] [--max-pack-size=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]";
int main(int argc, const char **argv)
{
- int i;
- uintmax_t total_count, duplicate_count;
+ int i, show_stats = 1;
git_config(git_default_config);
if (*a != '-' || !strcmp(a, "--"))
break;
+ else if (!strncmp(a, "--date-format=", 14)) {
+ const char *fmt = a + 14;
+ if (!strcmp(fmt, "raw"))
+ whenspec = WHENSPEC_RAW;
+ else if (!strcmp(fmt, "rfc2822"))
+ whenspec = WHENSPEC_RFC2822;
+ else if (!strcmp(fmt, "now"))
+ whenspec = WHENSPEC_NOW;
+ else
+ die("unknown --date-format argument %s", fmt);
+ }
else if (!strncmp(a, "--max-pack-size=", 16))
max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
else if (!strncmp(a, "--depth=", 8))
max_active_branches = strtoul(a + 18, NULL, 0);
else if (!strncmp(a, "--export-marks=", 15))
mark_file = a + 15;
+ else if (!strncmp(a, "--export-pack-edges=", 20)) {
+ if (pack_edges)
+ fclose(pack_edges);
+ pack_edges = fopen(a + 20, "a");
+ if (!pack_edges)
+ die("Cannot open %s: %s", a + 20, strerror(errno));
+ } else if (!strcmp(a, "--force"))
+ force_update = 1;
+ else if (!strcmp(a, "--quiet"))
+ show_stats = 0;
+ else if (!strcmp(a, "--stats"))
+ show_stats = 1;
else
die("unknown option %s", a);
}
unkeep_all_packs();
dump_marks();
- total_count = 0;
- for (i = 0; i < ARRAY_SIZE(object_count_by_type); i++)
- total_count += object_count_by_type[i];
- duplicate_count = 0;
- for (i = 0; i < ARRAY_SIZE(duplicate_count_by_type); i++)
- duplicate_count += duplicate_count_by_type[i];
-
- fprintf(stderr, "%s statistics:\n", argv[0]);
- fprintf(stderr, "---------------------------------------------------------------------\n");
- fprintf(stderr, "Alloc'd objects: %10ju\n", alloc_count);
- fprintf(stderr, "Total objects: %10ju (%10ju duplicates )\n", total_count, duplicate_count);
- fprintf(stderr, " blobs : %10ju (%10ju duplicates %10ju deltas)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB]);
- fprintf(stderr, " trees : %10ju (%10ju duplicates %10ju deltas)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE]);
- fprintf(stderr, " commits: %10ju (%10ju duplicates %10ju deltas)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT]);
- fprintf(stderr, " tags : %10ju (%10ju duplicates %10ju deltas)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG]);
- fprintf(stderr, "Total branches: %10lu (%10lu loads )\n", branch_count, branch_load_count);
- fprintf(stderr, " marks: %10ju (%10ju unique )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count);
- fprintf(stderr, " atoms: %10u\n", atom_cnt);
- fprintf(stderr, "Memory total: %10ju KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024);
- fprintf(stderr, " pools: %10lu KiB\n", total_allocd/1024);
- fprintf(stderr, " objects: %10ju KiB\n", (alloc_count*sizeof(struct object_entry))/1024);
- fprintf(stderr, "---------------------------------------------------------------------\n");
- pack_report();
- fprintf(stderr, "---------------------------------------------------------------------\n");
- fprintf(stderr, "\n");
+ if (pack_edges)
+ fclose(pack_edges);
+
+ if (show_stats) {
+ uintmax_t total_count = 0, duplicate_count = 0;
+ for (i = 0; i < ARRAY_SIZE(object_count_by_type); i++)
+ total_count += object_count_by_type[i];
+ for (i = 0; i < ARRAY_SIZE(duplicate_count_by_type); i++)
+ duplicate_count += duplicate_count_by_type[i];
+
+ fprintf(stderr, "%s statistics:\n", argv[0]);
+ fprintf(stderr, "---------------------------------------------------------------------\n");
+ fprintf(stderr, "Alloc'd objects: %10" PRIuMAX "\n", alloc_count);
+ fprintf(stderr, "Total objects: %10" PRIuMAX " (%10" PRIuMAX " duplicates )\n", total_count, duplicate_count);
+ fprintf(stderr, " blobs : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB]);
+ fprintf(stderr, " trees : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE]);
+ fprintf(stderr, " commits: %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT]);
+ fprintf(stderr, " tags : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG]);
+ fprintf(stderr, "Total branches: %10lu (%10lu loads )\n", branch_count, branch_load_count);
+ fprintf(stderr, " marks: %10" PRIuMAX " (%10" PRIuMAX " unique )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count);
+ fprintf(stderr, " atoms: %10u\n", atom_cnt);
+ fprintf(stderr, "Memory total: %10" PRIuMAX " KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024);
+ fprintf(stderr, " pools: %10lu KiB\n", (unsigned long)(total_allocd/1024));
+ fprintf(stderr, " objects: %10" PRIuMAX " KiB\n", (alloc_count*sizeof(struct object_entry))/1024);
+ fprintf(stderr, "---------------------------------------------------------------------\n");
+ pack_report();
+ fprintf(stderr, "---------------------------------------------------------------------\n");
+ fprintf(stderr, "\n");
+ }
- return 0;
+ return failure ? 1 : 0;
}