You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
7.0 KiB
326 lines
7.0 KiB
#define _XOPEN_SOURCE /* glibc2 needs this */ |
|
#include <time.h> |
|
#include <ctype.h> |
|
#include "cache.h" |
|
|
|
struct entry { |
|
unsigned char old_sha1[20]; |
|
unsigned char new_sha1[20]; |
|
int converted; |
|
}; |
|
|
|
#define MAXOBJECTS (1000000) |
|
|
|
static struct entry *convert[MAXOBJECTS]; |
|
static int nr_convert; |
|
|
|
static struct entry * convert_entry(unsigned char *sha1); |
|
|
|
static struct entry *insert_new(unsigned char *sha1, int pos) |
|
{ |
|
struct entry *new = xmalloc(sizeof(struct entry)); |
|
memset(new, 0, sizeof(*new)); |
|
memcpy(new->old_sha1, sha1, 20); |
|
memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *)); |
|
convert[pos] = new; |
|
nr_convert++; |
|
if (nr_convert == MAXOBJECTS) |
|
die("you're kidding me - hit maximum object limit"); |
|
return new; |
|
} |
|
|
|
static struct entry *lookup_entry(unsigned char *sha1) |
|
{ |
|
int low = 0, high = nr_convert; |
|
|
|
while (low < high) { |
|
int next = (low + high) / 2; |
|
struct entry *n = convert[next]; |
|
int cmp = memcmp(sha1, n->old_sha1, 20); |
|
if (!cmp) |
|
return n; |
|
if (cmp < 0) { |
|
high = next; |
|
continue; |
|
} |
|
low = next+1; |
|
} |
|
return insert_new(sha1, low); |
|
} |
|
|
|
static void convert_binary_sha1(void *buffer) |
|
{ |
|
struct entry *entry = convert_entry(buffer); |
|
memcpy(buffer, entry->new_sha1, 20); |
|
} |
|
|
|
static void convert_ascii_sha1(void *buffer) |
|
{ |
|
unsigned char sha1[20]; |
|
struct entry *entry; |
|
|
|
if (get_sha1_hex(buffer, sha1)) |
|
die("expected sha1, got '%s'", buffer); |
|
entry = convert_entry(sha1); |
|
memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); |
|
} |
|
|
|
static unsigned int convert_mode(unsigned int mode) |
|
{ |
|
unsigned int newmode; |
|
|
|
newmode = mode & S_IFMT; |
|
if (S_ISREG(mode)) |
|
newmode |= (mode & 0100) ? 0755 : 0644; |
|
return newmode; |
|
} |
|
|
|
static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1) |
|
{ |
|
char *new = xmalloc(size); |
|
unsigned long newlen = 0; |
|
unsigned long used; |
|
|
|
used = 0; |
|
while (size) { |
|
int len = 21 + strlen(buffer); |
|
char *path = strchr(buffer, ' '); |
|
unsigned char *sha1; |
|
unsigned int mode; |
|
char *slash, *origpath; |
|
|
|
if (!path || sscanf(buffer, "%o", &mode) != 1) |
|
die("bad tree conversion"); |
|
mode = convert_mode(mode); |
|
path++; |
|
if (memcmp(path, base, baselen)) |
|
break; |
|
origpath = path; |
|
path += baselen; |
|
slash = strchr(path, '/'); |
|
if (!slash) { |
|
newlen += sprintf(new + newlen, "%o %s", mode, path); |
|
new[newlen++] = '\0'; |
|
memcpy(new + newlen, buffer + len - 20, 20); |
|
newlen += 20; |
|
|
|
used += len; |
|
size -= len; |
|
buffer += len; |
|
continue; |
|
} |
|
|
|
newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); |
|
new[newlen++] = 0; |
|
sha1 = (unsigned char *)(new + newlen); |
|
newlen += 20; |
|
|
|
len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1); |
|
|
|
used += len; |
|
size -= len; |
|
buffer += len; |
|
} |
|
|
|
write_sha1_file(new, newlen, "tree", result_sha1); |
|
free(new); |
|
return used; |
|
} |
|
|
|
static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1) |
|
{ |
|
void *orig_buffer = buffer; |
|
unsigned long orig_size = size; |
|
|
|
while (size) { |
|
int len = 1+strlen(buffer); |
|
|
|
convert_binary_sha1(buffer + len); |
|
|
|
len += 20; |
|
if (len > size) |
|
die("corrupt tree object"); |
|
size -= len; |
|
buffer += len; |
|
} |
|
|
|
write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1); |
|
} |
|
|
|
static unsigned long parse_oldstyle_date(const char *buf) |
|
{ |
|
char c, *p; |
|
char buffer[100]; |
|
struct tm tm; |
|
const char *formats[] = { |
|
"%c", |
|
"%a %b %d %T", |
|
"%Z", |
|
"%Y", |
|
" %Y", |
|
NULL |
|
}; |
|
/* We only ever did two timezones in the bad old format .. */ |
|
const char *timezones[] = { |
|
"PDT", "PST", "CEST", NULL |
|
}; |
|
const char **fmt = formats; |
|
|
|
p = buffer; |
|
while (isspace(c = *buf)) |
|
buf++; |
|
while ((c = *buf++) != '\n') |
|
*p++ = c; |
|
*p++ = 0; |
|
buf = buffer; |
|
memset(&tm, 0, sizeof(tm)); |
|
do { |
|
const char *next = strptime(buf, *fmt, &tm); |
|
if (next) { |
|
if (!*next) |
|
return mktime(&tm); |
|
buf = next; |
|
} else { |
|
const char **p = timezones; |
|
while (isspace(*buf)) |
|
buf++; |
|
while (*p) { |
|
if (!memcmp(buf, *p, strlen(*p))) { |
|
buf += strlen(*p); |
|
break; |
|
} |
|
p++; |
|
} |
|
} |
|
fmt++; |
|
} while (*buf && *fmt); |
|
printf("left: %s\n", buf); |
|
return mktime(&tm); |
|
} |
|
|
|
static int convert_date_line(char *dst, void **buf, unsigned long *sp) |
|
{ |
|
unsigned long size = *sp; |
|
char *line = *buf; |
|
char *next = strchr(line, '\n'); |
|
char *date = strchr(line, '>'); |
|
int len; |
|
|
|
if (!next || !date) |
|
die("missing or bad author/committer line %s", line); |
|
next++; date += 2; |
|
|
|
*buf = next; |
|
*sp = size - (next - line); |
|
|
|
len = date - line; |
|
memcpy(dst, line, len); |
|
dst += len; |
|
|
|
/* Is it already in new format? */ |
|
if (isdigit(*date)) { |
|
int datelen = next - date; |
|
memcpy(dst, date, datelen); |
|
return len + datelen; |
|
} |
|
|
|
/* |
|
* Hacky hacky: one of the sparse old-style commits does not have |
|
* any date at all, but we can fake it by using the committer date. |
|
*/ |
|
if (*date == '\n' && strchr(next, '>')) |
|
date = strchr(next, '>')+2; |
|
|
|
return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date)); |
|
} |
|
|
|
static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1) |
|
{ |
|
char *new = xmalloc(size + 100); |
|
unsigned long newlen = 0; |
|
|
|
// "tree <sha1>\n" |
|
memcpy(new + newlen, buffer, 46); |
|
newlen += 46; |
|
buffer += 46; |
|
size -= 46; |
|
|
|
// "parent <sha1>\n" |
|
while (!memcmp(buffer, "parent ", 7)) { |
|
memcpy(new + newlen, buffer, 48); |
|
newlen += 48; |
|
buffer += 48; |
|
size -= 48; |
|
} |
|
|
|
// "author xyz <xyz> date" |
|
newlen += convert_date_line(new + newlen, &buffer, &size); |
|
// "committer xyz <xyz> date" |
|
newlen += convert_date_line(new + newlen, &buffer, &size); |
|
|
|
// Rest |
|
memcpy(new + newlen, buffer, size); |
|
newlen += size; |
|
|
|
write_sha1_file(new, newlen, "commit", result_sha1); |
|
free(new); |
|
} |
|
|
|
static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1) |
|
{ |
|
void *orig_buffer = buffer; |
|
unsigned long orig_size = size; |
|
|
|
if (memcmp(buffer, "tree ", 5)) |
|
die("Bad commit '%s'", buffer); |
|
convert_ascii_sha1(buffer+5); |
|
buffer += 46; /* "tree " + "hex sha1" + "\n" */ |
|
while (!memcmp(buffer, "parent ", 7)) { |
|
convert_ascii_sha1(buffer+7); |
|
buffer += 48; |
|
} |
|
convert_date(orig_buffer, orig_size, result_sha1); |
|
} |
|
|
|
static struct entry * convert_entry(unsigned char *sha1) |
|
{ |
|
struct entry *entry = lookup_entry(sha1); |
|
char type[20]; |
|
void *buffer, *data; |
|
unsigned long size; |
|
|
|
if (entry->converted) |
|
return entry; |
|
data = read_sha1_file(sha1, type, &size); |
|
if (!data) |
|
die("unable to read object %s", sha1_to_hex(sha1)); |
|
|
|
buffer = xmalloc(size); |
|
memcpy(buffer, data, size); |
|
|
|
if (!strcmp(type, "blob")) { |
|
write_sha1_file(buffer, size, "blob", entry->new_sha1); |
|
} else if (!strcmp(type, "tree")) |
|
convert_tree(buffer, size, entry->new_sha1); |
|
else if (!strcmp(type, "commit")) |
|
convert_commit(buffer, size, entry->new_sha1); |
|
else |
|
die("unknown object type '%s' in %s", type, sha1_to_hex(sha1)); |
|
entry->converted = 1; |
|
free(buffer); |
|
free(data); |
|
return entry; |
|
} |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
unsigned char sha1[20]; |
|
struct entry *entry; |
|
|
|
if (argc != 2 || get_sha1(argv[1], sha1)) |
|
usage("git-convert-cache <sha1>"); |
|
|
|
entry = convert_entry(sha1); |
|
printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1)); |
|
return 0; |
|
}
|
|
|