]>
Commit | Line | Data |
---|---|---|
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" | |
225fe215 | 28 | #include "tree.h" |
44908fe7 VM |
29 | #include "git2/repository.h" |
30 | #include "git2/object.h" | |
225fe215 | 31 | |
c8f5ff8f | 32 | #define DEFAULT_TREE_SIZE 16 |
9de27ad0 SJ |
33 | #define MAX_FILEMODE 0777777 |
34 | #define MAX_FILEMODE_BYTES 6 | |
c8f5ff8f | 35 | |
c4034e63 | 36 | int entry_search_cmp(const void *key, const void *array_member) |
2a884588 VM |
37 | { |
38 | const char *filename = (const char *)key; | |
39 | const git_tree_entry *entry = *(const git_tree_entry **)(array_member); | |
40 | ||
41 | return strcmp(filename, entry->filename); | |
42 | } | |
43 | ||
72a3fe42 | 44 | #if 0 |
9de27ad0 SJ |
45 | static int valid_attributes(const int attributes) { |
46 | return attributes >= 0 && attributes <= MAX_FILEMODE; | |
47 | } | |
72a3fe42 | 48 | #endif |
9de27ad0 | 49 | |
2a884588 VM |
50 | int entry_sort_cmp(const void *a, const void *b) |
51 | { | |
52 | const git_tree_entry *entry_a = *(const git_tree_entry **)(a); | |
53 | const git_tree_entry *entry_b = *(const git_tree_entry **)(b); | |
54 | ||
ccef1c9d | 55 | return gitfo_cmp_path(entry_a->filename, strlen(entry_a->filename), |
35786cb7 JW |
56 | entry_a->attr & 040000, |
57 | entry_b->filename, strlen(entry_b->filename), | |
58 | entry_b->attr & 040000); | |
2a884588 VM |
59 | } |
60 | ||
72a3fe42 | 61 | void git_tree__free(git_tree *tree) |
225fe215 | 62 | { |
c4034e63 | 63 | unsigned int i; |
003c2690 | 64 | |
c4034e63 VM |
65 | for (i = 0; i < tree->entries.length; ++i) { |
66 | git_tree_entry *e; | |
67 | e = git_vector_get(&tree->entries, i); | |
68 | ||
69 | free(e->filename); | |
70 | free(e); | |
58519018 | 71 | } |
003c2690 | 72 | |
c8f5ff8f | 73 | git_vector_free(&tree->entries); |
225fe215 VM |
74 | free(tree); |
75 | } | |
76 | ||
d45b4a9a VM |
77 | const git_oid *git_tree_id(git_tree *c) |
78 | { | |
79 | return git_object_id((git_object *)c); | |
225fe215 VM |
80 | } |
81 | ||
2a884588 | 82 | unsigned int git_tree_entry_attributes(git_tree_entry *entry) |
003c2690 | 83 | { |
2a884588 | 84 | return entry->attr; |
003c2690 VM |
85 | } |
86 | ||
2a884588 | 87 | const char *git_tree_entry_name(git_tree_entry *entry) |
003c2690 | 88 | { |
c4b5bedc | 89 | assert(entry); |
2a884588 VM |
90 | return entry->filename; |
91 | } | |
003c2690 | 92 | |
2a884588 VM |
93 | const git_oid *git_tree_entry_id(git_tree_entry *entry) |
94 | { | |
c4b5bedc | 95 | assert(entry); |
2a884588 | 96 | return &entry->oid; |
003c2690 VM |
97 | } |
98 | ||
72a3fe42 | 99 | int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry) |
003c2690 | 100 | { |
1795f879 | 101 | assert(entry && object_out); |
72a3fe42 | 102 | return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); |
122c3405 VM |
103 | } |
104 | ||
2a884588 VM |
105 | git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) |
106 | { | |
c4034e63 | 107 | int idx; |
c4b5bedc VM |
108 | |
109 | assert(tree && filename); | |
110 | ||
86d7e1ca | 111 | idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); |
c4034e63 VM |
112 | if (idx == GIT_ENOTFOUND) |
113 | return NULL; | |
114 | ||
115 | return git_vector_get(&tree->entries, idx); | |
2a884588 VM |
116 | } |
117 | ||
118 | git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) | |
003c2690 | 119 | { |
c4b5bedc | 120 | assert(tree); |
c4034e63 | 121 | return git_vector_get(&tree->entries, (unsigned int)idx); |
003c2690 VM |
122 | } |
123 | ||
124 | size_t git_tree_entrycount(git_tree *tree) | |
125 | { | |
c4b5bedc | 126 | assert(tree); |
c4034e63 | 127 | return tree->entries.length; |
003c2690 VM |
128 | } |
129 | ||
720d5472 | 130 | static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end) |
d8603ed9 | 131 | { |
6f02c3ba | 132 | int error = GIT_SUCCESS; |
2a884588 | 133 | |
72a3fe42 VM |
134 | if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) |
135 | return GIT_ENOMEM; | |
e4def81a | 136 | |
003c2690 VM |
137 | while (buffer < buffer_end) { |
138 | git_tree_entry *entry; | |
d8603ed9 | 139 | |
29e1789b | 140 | entry = git__calloc(1, sizeof(git_tree_entry)); |
2a884588 VM |
141 | if (entry == NULL) { |
142 | error = GIT_ENOMEM; | |
143 | break; | |
003c2690 | 144 | } |
d8603ed9 | 145 | |
6f02c3ba | 146 | if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) |
c4034e63 | 147 | return GIT_ENOMEM; |
003c2690 | 148 | |
720d5472 | 149 | entry->attr = strtol(buffer, (char **)&buffer, 8); |
d8603ed9 VM |
150 | |
151 | if (*buffer++ != ' ') { | |
152 | error = GIT_EOBJCORRUPTED; | |
153 | break; | |
154 | } | |
155 | ||
58519018 VM |
156 | if (memchr(buffer, 0, buffer_end - buffer) == NULL) { |
157 | error = GIT_EOBJCORRUPTED; | |
158 | break; | |
159 | } | |
160 | ||
161 | entry->filename = git__strdup(buffer); | |
d8603ed9 | 162 | |
2a884588 VM |
163 | while (buffer < buffer_end && *buffer != 0) |
164 | buffer++; | |
003c2690 | 165 | |
2a884588 | 166 | buffer++; |
d8603ed9 VM |
167 | |
168 | git_oid_mkraw(&entry->oid, (const unsigned char *)buffer); | |
169 | buffer += GIT_OID_RAWSZ; | |
d8603ed9 VM |
170 | } |
171 | ||
172 | return error; | |
173 | } | |
58519018 | 174 | |
72a3fe42 | 175 | int git_tree__parse(git_tree *tree, git_odb_object *obj) |
58519018 | 176 | { |
72a3fe42 VM |
177 | assert(tree); |
178 | return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); | |
58519018 VM |
179 | } |
180 | ||
29e1789b | 181 | static int write_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid) |
47d8ec56 | 182 | { |
29e1789b VM |
183 | int written; |
184 | written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0); | |
185 | memcpy(buffer + written, &oid->id, GIT_OID_RAWSZ); | |
186 | return written + GIT_OID_RAWSZ; | |
187 | } | |
188 | ||
189 | static int write_index(git_oid *oid, git_index *index, const char *base, int baselen, int entry_no, int maxentries) | |
190 | { | |
191 | size_t size, offset; | |
47d8ec56 | 192 | char *buffer; |
29e1789b VM |
193 | int nr, error; |
194 | ||
47d8ec56 | 195 | /* Guess at some random initial size */ |
29e1789b | 196 | size = maxentries * 40; |
47d8ec56 SL |
197 | buffer = git__malloc(size); |
198 | if (buffer == NULL) | |
199 | return GIT_ENOMEM; | |
200 | ||
201 | offset = 0; | |
47d8ec56 | 202 | |
29e1789b | 203 | for (nr = entry_no; nr < maxentries; ++nr) { |
47d8ec56 | 204 | git_index_entry *entry = git_index_get(index, nr); |
29e1789b | 205 | |
47d8ec56 SL |
206 | const char *pathname = entry->path, *filename, *dirname; |
207 | int pathlen = strlen(pathname), entrylen; | |
29e1789b VM |
208 | |
209 | unsigned int write_mode; | |
210 | git_oid subtree_oid; | |
211 | git_oid *write_oid; | |
47d8ec56 SL |
212 | |
213 | /* Did we hit the end of the directory? Return how many we wrote */ | |
29e1789b | 214 | if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0) |
47d8ec56 SL |
215 | break; |
216 | ||
47d8ec56 SL |
217 | /* Do we have _further_ subdirectories? */ |
218 | filename = pathname + baselen; | |
219 | dirname = strchr(filename, '/'); | |
29e1789b VM |
220 | |
221 | write_oid = &entry->oid; | |
222 | write_mode = entry->mode; | |
223 | ||
47d8ec56 SL |
224 | if (dirname) { |
225 | int subdir_written; | |
29e1789b VM |
226 | |
227 | #if 0 | |
228 | if (entry->mode != S_IFDIR) { | |
229 | free(buffer); | |
230 | return GIT_EOBJCORRUPTED; | |
231 | } | |
232 | #endif | |
233 | subdir_written = write_index(&subtree_oid, index, pathname, dirname - pathname + 1, nr, maxentries); | |
234 | ||
235 | if (subdir_written < GIT_SUCCESS) { | |
236 | free(buffer); | |
237 | return subdir_written; | |
238 | } | |
47d8ec56 | 239 | |
29e1789b | 240 | nr = subdir_written - 1; |
47d8ec56 SL |
241 | |
242 | /* Now we need to write out the directory entry into this tree.. */ | |
47d8ec56 | 243 | pathlen = dirname - pathname; |
29e1789b VM |
244 | write_oid = &subtree_oid; |
245 | write_mode = S_IFDIR; | |
47d8ec56 | 246 | } |
29e1789b | 247 | |
47d8ec56 | 248 | entrylen = pathlen - baselen; |
29e1789b VM |
249 | if (offset + entrylen + 32 > size) { |
250 | size = alloc_nr(offset + entrylen + 32); | |
47d8ec56 SL |
251 | buffer = git__realloc(buffer, size); |
252 | ||
253 | if (buffer == NULL) | |
254 | return GIT_ENOMEM; | |
255 | } | |
29e1789b VM |
256 | |
257 | offset += write_entry(buffer + offset, write_mode, filename, entrylen, write_oid); | |
258 | } | |
47d8ec56 | 259 | |
29e1789b VM |
260 | error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE); |
261 | free(buffer); | |
262 | ||
263 | return (error == GIT_SUCCESS) ? nr : error; | |
264 | } | |
265 | ||
266 | int git_tree_create_fromindex(git_oid *oid, git_index *index) | |
267 | { | |
268 | int error; | |
269 | ||
270 | if (index->repository == NULL) | |
271 | return GIT_EBAREINDEX; | |
47d8ec56 | 272 | |
29e1789b VM |
273 | error = write_index(oid, index, "", 0, 0, git_index_entrycount(index)); |
274 | return (error < GIT_SUCCESS) ? error : GIT_SUCCESS; | |
47d8ec56 | 275 | } |