]> git.proxmox.com Git - libgit2.git/blame - src/tree.c
Add unit tests for object write-back
[libgit2.git] / src / tree.c
CommitLineData
225fe215
VM
1/*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "common.h"
27#include "commit.h"
28#include "revwalk.h"
29#include "tree.h"
3315782c 30#include "git/repository.h"
225fe215 31
2a884588
VM
32static void resize_tree_array(git_tree *tree)
33{
34 git_tree_entry **new_entries;
35
36 tree->array_size = tree->array_size * 2;
37
38 new_entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
39 memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry *));
40
41 free(tree->entries);
42 tree->entries = new_entries;
43}
44
45int entry_cmp(const void *key, const void *array_member)
46{
47 const char *filename = (const char *)key;
48 const git_tree_entry *entry = *(const git_tree_entry **)(array_member);
49
50 return strcmp(filename, entry->filename);
51}
52
53int entry_sort_cmp(const void *a, const void *b)
54{
55 const git_tree_entry *entry_a = *(const git_tree_entry **)(a);
56 const git_tree_entry *entry_b = *(const git_tree_entry **)(b);
57
58 return strcmp(entry_a->filename, entry_b->filename);
59}
60
61static void entry_resort(git_tree *tree)
62{
63 qsort(tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_sort_cmp);
64}
65
66
67
68
225fe215
VM
69void git_tree__free(git_tree *tree)
70{
003c2690
VM
71 size_t i;
72
73 for (i = 0; i < tree->entry_count; ++i)
2a884588 74 free(tree->entries[i]);
003c2690
VM
75
76 free(tree->entries);
225fe215
VM
77 free(tree);
78}
79
d45b4a9a 80git_tree *git_tree_new(git_repository *repo)
225fe215 81{
d45b4a9a
VM
82 return (git_tree *)git_object_new(repo, GIT_OBJ_TREE);
83}
84
85const git_oid *git_tree_id(git_tree *c)
86{
87 return git_object_id((git_object *)c);
225fe215
VM
88}
89
3315782c 90git_tree *git_tree_lookup(git_repository *repo, const git_oid *id)
225fe215 91{
3315782c 92 return (git_tree *)git_repository_lookup(repo, id, GIT_OBJ_TREE);
d8603ed9
VM
93}
94
2a884588 95void git_tree_entry_set_attributes(git_tree_entry *entry, int attr)
003c2690 96{
2a884588
VM
97 assert(entry && entry->owner);
98
99 entry->attr = attr;
100 entry->owner->object.modified = 1;
003c2690
VM
101}
102
2a884588 103void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
003c2690 104{
2a884588
VM
105 assert(entry && entry->owner);
106
107 strncpy(entry->filename, name, GIT_TREE_MAX_FILENAME);
108 entry_resort(entry->owner);
109 entry->owner->object.modified = 1;
003c2690
VM
110}
111
2a884588 112void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid)
003c2690 113{
2a884588
VM
114 assert(entry && entry->owner);
115
116 git_oid_cpy(&entry->oid, oid);
117 entry->owner->object.modified = 1;
003c2690
VM
118}
119
2a884588 120unsigned int git_tree_entry_attributes(git_tree_entry *entry)
003c2690 121{
2a884588 122 return entry->attr;
003c2690
VM
123}
124
2a884588 125const char *git_tree_entry_name(git_tree_entry *entry)
003c2690 126{
c4b5bedc 127 assert(entry);
2a884588
VM
128 return entry->filename;
129}
003c2690 130
2a884588
VM
131const git_oid *git_tree_entry_id(git_tree_entry *entry)
132{
c4b5bedc 133 assert(entry);
2a884588 134 return &entry->oid;
003c2690
VM
135}
136
2a884588 137git_object *git_tree_entry_2object(git_tree_entry *entry)
003c2690 138{
c4b5bedc 139 assert(entry);
2a884588 140 return git_repository_lookup(entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
003c2690
VM
141}
142
2a884588
VM
143git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
144{
c4b5bedc
VM
145 git_tree_entry **found;
146
147 assert(tree && filename);
148
149 found = bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
150 return found ? *found : NULL;
2a884588
VM
151}
152
153git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
003c2690 154{
c4b5bedc
VM
155 assert(tree);
156
003c2690 157 if (tree->entries == NULL)
f2408cc2 158 return NULL;
003c2690 159
2a884588 160 return (idx >= 0 && idx < (int)tree->entry_count) ? tree->entries[idx] : NULL;
003c2690
VM
161}
162
163size_t git_tree_entrycount(git_tree *tree)
164{
c4b5bedc 165 assert(tree);
003c2690
VM
166 return tree->entry_count;
167}
168
2a884588
VM
169void git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
170{
171 git_tree_entry *entry;
172
c4b5bedc
VM
173 assert(tree && id && filename);
174
2a884588
VM
175 if (tree->entry_count >= tree->array_size)
176 resize_tree_array(tree);
177
178 if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
179 return;
180
181 memset(entry, 0x0, sizeof(git_tree_entry));
182
183 strncpy(entry->filename, filename, GIT_TREE_MAX_FILENAME);
184 git_oid_cpy(&entry->oid, id);
185 entry->attr = attributes;
186 entry->owner = tree;
187
188 tree->entries[tree->entry_count++] = entry;
189 entry_resort(tree);
190
191 tree->object.modified = 1;
192}
193
194int git_tree_remove_entry_byindex(git_tree *tree, int idx)
195{
196 git_tree_entry *remove_ptr;
197
c4b5bedc
VM
198 assert(tree);
199
2a884588
VM
200 if (idx < 0 || idx >= (int)tree->entry_count)
201 return GIT_ENOTFOUND;
202
203 remove_ptr = tree->entries[idx];
204 tree->entries[idx] = tree->entries[--tree->entry_count];
205
206 free(remove_ptr);
207 entry_resort(tree);
208
209 tree->object.modified = 1;
210 return GIT_SUCCESS;
211}
212
213int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
214{
215 git_tree_entry **entry_ptr;
216 int idx;
217
c4b5bedc
VM
218 assert(tree && filename);
219
2a884588
VM
220 entry_ptr = bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
221 if (entry_ptr == NULL)
222 return GIT_ENOTFOUND;
223
224 idx = (int)(entry_ptr - tree->entries);
225 return git_tree_remove_entry_byindex(tree, idx);
226}
227
228int git_tree__writeback(git_tree *tree, git_odb_source *src)
229{
230 size_t i;
231
c4b5bedc
VM
232 assert(tree && src);
233
2a884588
VM
234 if (tree->entries == NULL)
235 return GIT_ERROR;
236
237 entry_resort(tree);
238
239 for (i = 0; i < tree->entry_count; ++i) {
240 git_tree_entry *entry;
241 entry = tree->entries[i];
242
243 git__source_printf(src, "%06o %s\0", entry->attr, entry->filename);
244 git__source_write(src, entry->oid.id, GIT_OID_RAWSZ);
245 }
246
247 return GIT_SUCCESS;
248}
249
250
d8603ed9
VM
251int git_tree__parse(git_tree *tree)
252{
003c2690 253 static const size_t avg_entry_size = 40;
d8603ed9
VM
254
255 int error = 0;
d8603ed9 256 char *buffer, *buffer_end;
003c2690 257
2a884588
VM
258 assert(!tree->object.in_memory);
259
260 if (tree->entries != NULL) {
261 size_t i;
262
263 for (i = 0; i < tree->entry_count; ++i)
264 free(tree->entries[i]);
265
266 free(tree->entries);
267 }
d8603ed9 268
f49a2e49 269 error = git_object__source_open((git_object *)tree);
d8603ed9
VM
270 if (error < 0)
271 return error;
272
f49a2e49
VM
273 buffer = tree->object.source.raw.data;
274 buffer_end = buffer + tree->object.source.raw.len;
d8603ed9 275
003c2690 276 tree->entry_count = 0;
2a884588
VM
277 tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
278 tree->entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
d8603ed9 279
003c2690
VM
280 while (buffer < buffer_end) {
281 git_tree_entry *entry;
d8603ed9 282
2a884588
VM
283 if (tree->entry_count >= tree->array_size)
284 resize_tree_array(tree);
d8603ed9 285
2a884588
VM
286 entry = git__malloc(sizeof(git_tree_entry));
287 if (entry == NULL) {
288 error = GIT_ENOMEM;
289 break;
003c2690 290 }
d8603ed9 291
2a884588 292 tree->entries[tree->entry_count++] = entry;
003c2690 293
2a884588 294 entry->owner = tree;
003c2690 295 entry->attr = strtol(buffer, &buffer, 8);
d8603ed9
VM
296
297 if (*buffer++ != ' ') {
298 error = GIT_EOBJCORRUPTED;
299 break;
300 }
301
2a884588 302 strncpy(entry->filename, buffer, GIT_TREE_MAX_FILENAME);
d8603ed9 303
2a884588
VM
304 while (buffer < buffer_end && *buffer != 0)
305 buffer++;
003c2690 306
2a884588 307 buffer++;
d8603ed9
VM
308
309 git_oid_mkraw(&entry->oid, (const unsigned char *)buffer);
310 buffer += GIT_OID_RAWSZ;
d8603ed9
VM
311 }
312
f49a2e49 313 git_object__source_close((git_object *)tree);
d8603ed9
VM
314 return error;
315}