diff --git a/include/git2/tree.h b/include/git2/tree.h index 164aec9e2a1..bd1f97073b0 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -138,6 +138,18 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); */ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry); +/** + * Create tree by scanning the index file. + * + * @param object identity for the tree + * @param repository where to lookup for object db + * @param base directory path + * @param path length for base + * @param index entry offset to start scan + * @return number of items added to the tree + */ +GIT_EXTERN(int) git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no); + /** @} */ GIT_END_DECL #endif diff --git a/src/tree.c b/src/tree.c index dea50463bea..299fc696c36 100644 --- a/src/tree.c +++ b/src/tree.c @@ -178,3 +178,90 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } +int git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no) +{ + unsigned long size, offset; + char *buffer; + int nr, maxentries; + git_index *index; + git_odb_stream *stream; + int error; + + git_index_open_inrepo(&index,repo); + maxentries = git_index_entrycount (index); + + /* FIXME: A matching error code could not be found. Hence used a close error code GIT_EBAREINDEX */ + if (maxentries <= 0) { + return GIT_EBAREINDEX; + } + + /* Guess at some random initial size */ + size = 8192; + buffer = git__malloc(size); + if (buffer == NULL) + return GIT_ENOMEM; + + offset = 0; + nr = entry_no; + + do { + git_index_entry *entry = git_index_get(index, nr); + const char *pathname = entry->path, *filename, *dirname; + int pathlen = strlen(pathname), entrylen; + git_oid *entry_oid; + unsigned int mode; + unsigned char *sha1; + + /* Did we hit the end of the directory? Return how many we wrote */ + if (baselen >= pathlen || memcmp(base, pathname, baselen)) + break; + + entry_oid = &entry->oid; + mode = entry->mode; + sha1 = entry_oid->id; + + /* Do we have _further_ subdirectories? */ + filename = pathname + baselen; + dirname = strchr(filename, '/'); + if (dirname) { + int subdir_written; + subdir_written = git_tree_create(oid, repo, pathname, dirname-pathname+1, nr); + + if (subdir_written == GIT_ENOMEM) + return GIT_ENOMEM; + + nr += subdir_written; + + /* Now we need to write out the directory entry into this tree.. */ + mode = S_IFDIR; + pathlen = dirname - pathname; + + /* ..but the directory entry doesn't count towards the total count */ + nr--; + sha1 = oid->id; + } + + entrylen = pathlen - baselen; + if (offset + entrylen + 100 > size) { + size = alloc_nr(offset + entrylen + 100); + buffer = git__realloc(buffer, size); + + if (buffer == NULL) + return GIT_ENOMEM; + } + offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename); + buffer[offset++] = 0; + memcpy(buffer + offset, sha1, GIT_OID_RAWSZ); + offset += GIT_OID_RAWSZ; + nr++; + } while (nr < maxentries); + + if ((error = git_odb_open_wstream(&stream, repo->db, offset, GIT_OBJ_TREE)) < GIT_SUCCESS) + return error; + + stream->write(stream, buffer, offset); + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return nr; +} diff --git a/src/util.h b/src/util.h index 653b34d02e0..f477b64fd67 100644 --- a/src/util.h +++ b/src/util.h @@ -14,6 +14,7 @@ */ #define git__malloc malloc #define git__calloc calloc +#define git__realloc realloc #define git__strdup strdup extern int git__fmt(char *, size_t, const char *, ...)