int main(int argc, char **argv)
{
unsigned char sha1[20];
if (argc != 2)
usage("read-tree <key>");
if (get_sha1_hex(argv[1], sha1) < 0)
usage("read-tree <key>");
unpack(sha1);
// get file type(blob/tree/commit), content size and content
buffer = read_sha1_file(sha1, type, &size);
map = map_sha1_file(sha1, &mapsize);
char *filename = sha1_file_name(sha1);
int fd = open(filename, O_RDONLY);
fstat(fd, &st);
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
*mapsize = st.st_size;
return map;
buf = unpack_sha1_file(map, mapsize, type, size);
char buffer[8192];
stream.next_in = map;
stream.avail_in = mapsize;
stream.next_out = buffer;
stream.avail_out = sizeof(buffer);
inflate(&stream, 0);
// buffer: "blob %lu\0xxx"
// buffer: "tree %lu\0%o %s\0sha1%o %s\0sha1...%o %s\0sha1"
// buffer: "commit %lu\0tree %s\nparent %s\n..parent %s\nauthor %s <%s> %s\ncommitter %s <%s> %s\n\n%s"
sscanf(buffer, "%10s %lu", type, size);
bytes = strlen(buffer) + 1;
buf = malloc(*size);
memcpy(buf, buffer + bytes, stream.total_out - bytes);
// buf: "xxx"
// buf: "%o %s\0sha1%o %s\0sha1...%o %s\0sha1"
// buf: "tree %s\nparent %s\n..parent %s\nauthor %s <%s> %s\ncommitter %s <%s> %s\n\n%s"
return buf;
if (strcmp(type, "tree"))
usage("expected a 'tree' node");
while (size)
int len = strlen(buffer)+1; // buffer: "%o %s\0sha1%o %s\0sha1...%o %s\0sha1"
unsigned char *sha1 = buffer + len;
char *path = strchr(buffer, ' ')+1;
sscanf(buffer, "%o", &mode);
buffer = sha1 + 20;
size -= len + 20;
// get file type(blob), content size and content
data = read_sha1_file(sha1, type, &filesize);
if (!data || strcmp(type, "blob"))
usage("tree file refers to bad file data");
fd = create_file(path); // save blob content to file
write(fd, data, filesize);
fchmod(fd, mode);
close(fd);
}