Commit 47d8ec56e917adda2e336c29420f527fca733866

Sarath Lakshman 2011-04-03T17:18:56

New external API method: `git_tree_create` Creates a tree by scanning the index file. The method handles recursive creation of trees for subdirectories and adds them to the parent tree.

diff --git a/include/git2/tree.h b/include/git2/tree.h
index 164aec9..bd1f970 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 dea5046..299fc69 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 653b34d..f477b64 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 *, ...)