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