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