help: handle NULL value for alias.* config
[alt-git.git] / tree.c
blob990f9c9854e6a1a957ed01f74b20e694af07f278
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"
15 const char *tree_type = "tree";
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)
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;
29 if (depth > max_allowed_tree_depth)
30 return error("exceeded maximum allowed tree depth");
32 if (parse_tree(tree))
33 return -1;
35 init_tree_desc(&desc, tree->buffer, tree->size);
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;
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;
57 if (S_ISDIR(entry.mode))
58 oidcpy(&oid, &entry.oid);
59 else if (S_ISGITLINK(entry.mode)) {
60 struct commit *commit;
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);
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);
73 oidcpy(&oid, get_commit_tree_oid(commit));
75 else
76 continue;
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;
88 return 0;
91 int read_tree(struct repository *r,
92 struct tree *tree,
93 const struct pathspec *pathspec,
94 read_tree_fn_t fn, void *context)
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;
102 int base_name_compare(const char *name1, size_t len1, int mode1,
103 const char *name2, size_t len2, int mode2)
105 unsigned char c1, c2;
106 size_t len = len1 < len2 ? len1 : len2;
107 int cmp;
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;
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' < '.' < '/').
128 * This is used by routines that want to traverse the git namespace
129 * but then handle conflicting entries together when possible.
131 int df_name_compare(const char *name1, size_t len1, int mode1,
132 const char *name2, size_t len2, int mode2)
134 unsigned char c1, c2;
135 size_t len = len1 < len2 ? len1 : len2;
136 int cmp;
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;
157 int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
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;
170 struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
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);
178 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
180 if (item->object.parsed)
181 return 0;
182 item->object.parsed = 1;
183 item->buffer = buffer;
184 item->size = size;
186 return 0;
189 int parse_tree_gently(struct tree *item, int quiet_on_missing)
191 enum object_type type;
192 void *buffer;
193 unsigned long size;
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));
208 return parse_tree_buffer(item, buffer, size);
211 void free_tree_buffer(struct tree *tree)
213 FREE_AND_NULL(tree->buffer);
214 tree->size = 0;
215 tree->object.parsed = 0;
218 struct tree *parse_tree_indirect(const struct object_id *oid)
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);