Fix the git_tree_write implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
diff --git a/include/git2/tree.h b/include/git2/tree.h
index bd1f970..60150b3 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -134,21 +134,27 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry);
* @param object pointer to the converted object
* @param repo repository where to lookup the pointed object
* @param entry a tree entry
- * @return a reference to the pointed object in the repository
+ * @return 0 on success; error code otherwise
*/
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
+ * Write a tree to the ODB from the index file
+ *
+ * This method will scan the index and write a representation
+ * of its current state back to disk; it recursively creates
+ * tree objects for each of the subtrees stored in the index,
+ * but only returns the OID of the root tree. This is the OID
+ * that can be used e.g. to create a commit.
+ *
+ * The index instance cannot be bare, and needs to be associated
+ * to an existing repository.
+ *
+ * @param oid Pointer where to store the written tree
+ * @param index Index to write
+ * @return 0 on success; error code otherwise
*/
-GIT_EXTERN(int) git_tree_create(git_oid *oid, git_repository *repo, const char *base, int baselen, int entry_no);
+GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index);
/** @} */
GIT_END_DECL
diff --git a/src/tree.c b/src/tree.c
index 299fc69..588b5e9 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -137,7 +137,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
while (buffer < buffer_end) {
git_tree_entry *entry;
- entry = git__malloc(sizeof(git_tree_entry));
+ entry = git__calloc(1, sizeof(git_tree_entry));
if (entry == NULL) {
error = GIT_ENOMEM;
break;
@@ -178,90 +178,98 @@ 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)
+static int write_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid)
{
- unsigned long size, offset;
+ int written;
+ written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0);
+ memcpy(buffer + written, &oid->id, GIT_OID_RAWSZ);
+ return written + GIT_OID_RAWSZ;
+}
+
+static int write_index(git_oid *oid, git_index *index, const char *base, int baselen, int entry_no, int maxentries)
+{
+ size_t 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;
- }
-
+ int nr, error;
+
/* Guess at some random initial size */
- size = 8192;
+ size = maxentries * 40;
buffer = git__malloc(size);
if (buffer == NULL)
return GIT_ENOMEM;
offset = 0;
- nr = entry_no;
- do {
+ for (nr = entry_no; nr < maxentries; ++nr) {
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;
+
+ unsigned int write_mode;
+ git_oid subtree_oid;
+ git_oid *write_oid;
/* Did we hit the end of the directory? Return how many we wrote */
- if (baselen >= pathlen || memcmp(base, pathname, baselen))
+ if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0)
break;
- entry_oid = &entry->oid;
- mode = entry->mode;
- sha1 = entry_oid->id;
-
/* Do we have _further_ subdirectories? */
filename = pathname + baselen;
dirname = strchr(filename, '/');
+
+ write_oid = &entry->oid;
+ write_mode = entry->mode;
+
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;
+
+#if 0
+ if (entry->mode != S_IFDIR) {
+ free(buffer);
+ return GIT_EOBJCORRUPTED;
+ }
+#endif
+ subdir_written = write_index(&subtree_oid, index, pathname, dirname - pathname + 1, nr, maxentries);
+
+ if (subdir_written < GIT_SUCCESS) {
+ free(buffer);
+ return subdir_written;
+ }
- nr += subdir_written;
+ nr = subdir_written - 1;
/* 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;
+ write_oid = &subtree_oid;
+ write_mode = S_IFDIR;
}
-
+
entrylen = pathlen - baselen;
- if (offset + entrylen + 100 > size) {
- size = alloc_nr(offset + entrylen + 100);
+ if (offset + entrylen + 32 > size) {
+ size = alloc_nr(offset + entrylen + 32);
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;
+
+ offset += write_entry(buffer + offset, write_mode, filename, entrylen, write_oid);
+ }
- stream->write(stream, buffer, offset);
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
+ error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE);
+ free(buffer);
+
+ return (error == GIT_SUCCESS) ? nr : error;
+}
+
+int git_tree_create_fromindex(git_oid *oid, git_index *index)
+{
+ int error;
+
+ if (index->repository == NULL)
+ return GIT_EBAREINDEX;
- return nr;
+ error = write_index(oid, index, "", 0, 0, git_index_entrycount(index));
+ return (error < GIT_SUCCESS) ? error : GIT_SUCCESS;
}