]>
Commit | Line | Data |
---|---|---|
1 | #include "git-compat-util.h" | |
2 | #include "cache-tree.h" | |
3 | #include "hex.h" | |
4 | #include "tree.h" | |
5 | #include "object-name.h" | |
6 | #include "object-store-ll.h" | |
7 | #include "blob.h" | |
8 | #include "commit.h" | |
9 | #include "tag.h" | |
10 | #include "alloc.h" | |
11 | #include "tree-walk.h" | |
12 | #include "repository.h" | |
13 | #include "environment.h" | |
14 | ||
15 | const char *tree_type = "tree"; | |
16 | ||
17 | int read_tree_at(struct repository *r, | |
18 | struct tree *tree, struct strbuf *base, | |
19 | int depth, | |
20 | const struct pathspec *pathspec, | |
21 | read_tree_fn_t fn, void *context) | |
22 | { | |
23 | struct tree_desc desc; | |
24 | struct name_entry entry; | |
25 | struct object_id oid; | |
26 | int len, oldlen = base->len; | |
27 | enum interesting retval = entry_not_interesting; | |
28 | ||
29 | if (depth > max_allowed_tree_depth) | |
30 | return error("exceeded maximum allowed tree depth"); | |
31 | ||
32 | if (parse_tree(tree)) | |
33 | return -1; | |
34 | ||
35 | init_tree_desc(&desc, tree->buffer, tree->size); | |
36 | ||
37 | while (tree_entry(&desc, &entry)) { | |
38 | if (retval != all_entries_interesting) { | |
39 | retval = tree_entry_interesting(r->index, &entry, | |
40 | base, pathspec); | |
41 | if (retval == all_entries_not_interesting) | |
42 | break; | |
43 | if (retval == entry_not_interesting) | |
44 | continue; | |
45 | } | |
46 | ||
47 | switch (fn(&entry.oid, base, | |
48 | entry.path, entry.mode, context)) { | |
49 | case 0: | |
50 | continue; | |
51 | case READ_TREE_RECURSIVE: | |
52 | break; | |
53 | default: | |
54 | return -1; | |
55 | } | |
56 | ||
57 | if (S_ISDIR(entry.mode)) | |
58 | oidcpy(&oid, &entry.oid); | |
59 | else if (S_ISGITLINK(entry.mode)) { | |
60 | struct commit *commit; | |
61 | ||
62 | commit = lookup_commit(r, &entry.oid); | |
63 | if (!commit) | |
64 | die("Commit %s in submodule path %s%s not found", | |
65 | oid_to_hex(&entry.oid), | |
66 | base->buf, entry.path); | |
67 | ||
68 | if (repo_parse_commit(r, commit)) | |
69 | die("Invalid commit %s in submodule path %s%s", | |
70 | oid_to_hex(&entry.oid), | |
71 | base->buf, entry.path); | |
72 | ||
73 | oidcpy(&oid, get_commit_tree_oid(commit)); | |
74 | } | |
75 | else | |
76 | continue; | |
77 | ||
78 | len = tree_entry_len(&entry); | |
79 | strbuf_add(base, entry.path, len); | |
80 | strbuf_addch(base, '/'); | |
81 | retval = read_tree_at(r, lookup_tree(r, &oid), | |
82 | base, depth + 1, pathspec, | |
83 | fn, context); | |
84 | strbuf_setlen(base, oldlen); | |
85 | if (retval) | |
86 | return -1; | |
87 | } | |
88 | return 0; | |
89 | } | |
90 | ||
91 | int read_tree(struct repository *r, | |
92 | struct tree *tree, | |
93 | const struct pathspec *pathspec, | |
94 | read_tree_fn_t fn, void *context) | |
95 | { | |
96 | struct strbuf sb = STRBUF_INIT; | |
97 | int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context); | |
98 | strbuf_release(&sb); | |
99 | return ret; | |
100 | } | |
101 | ||
102 | int base_name_compare(const char *name1, size_t len1, int mode1, | |
103 | const char *name2, size_t len2, int mode2) | |
104 | { | |
105 | unsigned char c1, c2; | |
106 | size_t len = len1 < len2 ? len1 : len2; | |
107 | int cmp; | |
108 | ||
109 | cmp = memcmp(name1, name2, len); | |
110 | if (cmp) | |
111 | return cmp; | |
112 | c1 = name1[len]; | |
113 | c2 = name2[len]; | |
114 | if (!c1 && S_ISDIR(mode1)) | |
115 | c1 = '/'; | |
116 | if (!c2 && S_ISDIR(mode2)) | |
117 | c2 = '/'; | |
118 | return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; | |
119 | } | |
120 | ||
121 | /* | |
122 | * df_name_compare() is identical to base_name_compare(), except it | |
123 | * compares conflicting directory/file entries as equal. Note that | |
124 | * while a directory name compares as equal to a regular file, they | |
125 | * then individually compare _differently_ to a filename that has | |
126 | * a dot after the basename (because '\0' < '.' < '/'). | |
127 | * | |
128 | * This is used by routines that want to traverse the git namespace | |
129 | * but then handle conflicting entries together when possible. | |
130 | */ | |
131 | int df_name_compare(const char *name1, size_t len1, int mode1, | |
132 | const char *name2, size_t len2, int mode2) | |
133 | { | |
134 | unsigned char c1, c2; | |
135 | size_t len = len1 < len2 ? len1 : len2; | |
136 | int cmp; | |
137 | ||
138 | cmp = memcmp(name1, name2, len); | |
139 | if (cmp) | |
140 | return cmp; | |
141 | /* Directories and files compare equal (same length, same name) */ | |
142 | if (len1 == len2) | |
143 | return 0; | |
144 | c1 = name1[len]; | |
145 | if (!c1 && S_ISDIR(mode1)) | |
146 | c1 = '/'; | |
147 | c2 = name2[len]; | |
148 | if (!c2 && S_ISDIR(mode2)) | |
149 | c2 = '/'; | |
150 | if (c1 == '/' && !c2) | |
151 | return 0; | |
152 | if (c2 == '/' && !c1) | |
153 | return 0; | |
154 | return c1 - c2; | |
155 | } | |
156 | ||
157 | int name_compare(const char *name1, size_t len1, const char *name2, size_t len2) | |
158 | { | |
159 | size_t min_len = (len1 < len2) ? len1 : len2; | |
160 | int cmp = memcmp(name1, name2, min_len); | |
161 | if (cmp) | |
162 | return cmp; | |
163 | if (len1 < len2) | |
164 | return -1; | |
165 | if (len1 > len2) | |
166 | return 1; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | struct tree *lookup_tree(struct repository *r, const struct object_id *oid) | |
171 | { | |
172 | struct object *obj = lookup_object(r, oid); | |
173 | if (!obj) | |
174 | return create_object(r, oid, alloc_tree_node(r)); | |
175 | return object_as_type(obj, OBJ_TREE, 0); | |
176 | } | |
177 | ||
178 | int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size) | |
179 | { | |
180 | if (item->object.parsed) | |
181 | return 0; | |
182 | item->object.parsed = 1; | |
183 | item->buffer = buffer; | |
184 | item->size = size; | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | int parse_tree_gently(struct tree *item, int quiet_on_missing) | |
190 | { | |
191 | enum object_type type; | |
192 | void *buffer; | |
193 | unsigned long size; | |
194 | ||
195 | if (item->object.parsed) | |
196 | return 0; | |
197 | buffer = repo_read_object_file(the_repository, &item->object.oid, | |
198 | &type, &size); | |
199 | if (!buffer) | |
200 | return quiet_on_missing ? -1 : | |
201 | error("Could not read %s", | |
202 | oid_to_hex(&item->object.oid)); | |
203 | if (type != OBJ_TREE) { | |
204 | free(buffer); | |
205 | return error("Object %s not a tree", | |
206 | oid_to_hex(&item->object.oid)); | |
207 | } | |
208 | return parse_tree_buffer(item, buffer, size); | |
209 | } | |
210 | ||
211 | void free_tree_buffer(struct tree *tree) | |
212 | { | |
213 | FREE_AND_NULL(tree->buffer); | |
214 | tree->size = 0; | |
215 | tree->object.parsed = 0; | |
216 | } | |
217 | ||
218 | struct tree *parse_tree_indirect(const struct object_id *oid) | |
219 | { | |
220 | struct repository *r = the_repository; | |
221 | struct object *obj = parse_object(r, oid); | |
222 | return (struct tree *)repo_peel_to_type(r, NULL, 0, obj, OBJ_TREE); | |
223 | } |