|
27 | 27 | #include "commit-reach.h"
|
28 | 28 | #include "commit-graph.h"
|
29 | 29 | #include "prio-queue.h"
|
| 30 | +#include "hashmap.h" |
30 | 31 |
|
31 | 32 | volatile show_early_output_fn_t show_early_output;
|
32 | 33 |
|
@@ -99,29 +100,146 @@ void mark_tree_uninteresting(struct repository *r, struct tree *tree)
|
99 | 100 | mark_tree_contents_uninteresting(r, tree);
|
100 | 101 | }
|
101 | 102 |
|
| 103 | +struct path_and_oids_entry { |
| 104 | + struct hashmap_entry ent; |
| 105 | + char *path; |
| 106 | + struct oidset trees; |
| 107 | +}; |
| 108 | + |
| 109 | +static int path_and_oids_cmp(const void *hashmap_cmp_fn_data, |
| 110 | + const struct path_and_oids_entry *e1, |
| 111 | + const struct path_and_oids_entry *e2, |
| 112 | + const void *keydata) |
| 113 | +{ |
| 114 | + return strcmp(e1->path, e2->path); |
| 115 | +} |
| 116 | + |
| 117 | +static void paths_and_oids_init(struct hashmap *map) |
| 118 | +{ |
| 119 | + hashmap_init(map, (hashmap_cmp_fn) path_and_oids_cmp, NULL, 0); |
| 120 | +} |
| 121 | + |
| 122 | +static void paths_and_oids_clear(struct hashmap *map) |
| 123 | +{ |
| 124 | + struct hashmap_iter iter; |
| 125 | + struct path_and_oids_entry *entry; |
| 126 | + hashmap_iter_init(map, &iter); |
| 127 | + |
| 128 | + while ((entry = (struct path_and_oids_entry *)hashmap_iter_next(&iter))) { |
| 129 | + oidset_clear(&entry->trees); |
| 130 | + free(entry->path); |
| 131 | + } |
| 132 | + |
| 133 | + hashmap_free(map, 1); |
| 134 | +} |
| 135 | + |
| 136 | +static void paths_and_oids_insert(struct hashmap *map, |
| 137 | + const char *path, |
| 138 | + const struct object_id *oid) |
| 139 | +{ |
| 140 | + int hash = strhash(path); |
| 141 | + struct path_and_oids_entry key; |
| 142 | + struct path_and_oids_entry *entry; |
| 143 | + |
| 144 | + hashmap_entry_init(&key, hash); |
| 145 | + |
| 146 | + /* use a shallow copy for the lookup */ |
| 147 | + key.path = (char *)path; |
| 148 | + oidset_init(&key.trees, 0); |
| 149 | + |
| 150 | + if (!(entry = (struct path_and_oids_entry *)hashmap_get(map, &key, NULL))) { |
| 151 | + entry = xcalloc(1, sizeof(struct path_and_oids_entry)); |
| 152 | + hashmap_entry_init(entry, hash); |
| 153 | + entry->path = xstrdup(key.path); |
| 154 | + oidset_init(&entry->trees, 16); |
| 155 | + hashmap_put(map, entry); |
| 156 | + } |
| 157 | + |
| 158 | + oidset_insert(&entry->trees, oid); |
| 159 | +} |
| 160 | + |
| 161 | +static void add_children_by_path(struct repository *r, |
| 162 | + struct tree *tree, |
| 163 | + struct hashmap *map) |
| 164 | +{ |
| 165 | + struct tree_desc desc; |
| 166 | + struct name_entry entry; |
| 167 | + |
| 168 | + if (!tree) |
| 169 | + return; |
| 170 | + |
| 171 | + if (parse_tree_gently(tree, 1) < 0) |
| 172 | + return; |
| 173 | + |
| 174 | + init_tree_desc(&desc, tree->buffer, tree->size); |
| 175 | + while (tree_entry(&desc, &entry)) { |
| 176 | + switch (object_type(entry.mode)) { |
| 177 | + case OBJ_TREE: |
| 178 | + paths_and_oids_insert(map, entry.path, entry.oid); |
| 179 | + |
| 180 | + if (tree->object.flags & UNINTERESTING) { |
| 181 | + struct tree *child = lookup_tree(r, entry.oid); |
| 182 | + if (child) |
| 183 | + child->object.flags |= UNINTERESTING; |
| 184 | + } |
| 185 | + break; |
| 186 | + case OBJ_BLOB: |
| 187 | + if (tree->object.flags & UNINTERESTING) { |
| 188 | + struct blob *child = lookup_blob(r, entry.oid); |
| 189 | + if (child) |
| 190 | + child->object.flags |= UNINTERESTING; |
| 191 | + } |
| 192 | + break; |
| 193 | + default: |
| 194 | + /* Subproject commit - not in this repository */ |
| 195 | + break; |
| 196 | + } |
| 197 | + } |
| 198 | + |
| 199 | + free_tree_buffer(tree); |
| 200 | +} |
| 201 | + |
102 | 202 | void mark_trees_uninteresting_sparse(struct repository *r,
|
103 | 203 | struct oidset *trees)
|
104 | 204 | {
|
| 205 | + unsigned has_interesting = 0, has_uninteresting = 0; |
| 206 | + struct hashmap map; |
| 207 | + struct hashmap_iter map_iter; |
| 208 | + struct path_and_oids_entry *entry; |
105 | 209 | struct object_id *oid;
|
106 | 210 | struct oidset_iter iter;
|
107 | 211 |
|
108 | 212 | oidset_iter_init(trees, &iter);
|
109 |
| - while ((oid = oidset_iter_next(&iter))) { |
| 213 | + while ((!has_interesting || !has_uninteresting) && |
| 214 | + (oid = oidset_iter_next(&iter))) { |
110 | 215 | struct tree *tree = lookup_tree(r, oid);
|
111 | 216 |
|
112 | 217 | if (!tree)
|
113 | 218 | continue;
|
114 | 219 |
|
115 |
| - if (tree->object.flags & UNINTERESTING) { |
116 |
| - /* |
117 |
| - * Remove the flag so the next call |
118 |
| - * is not a no-op. The flag is added |
119 |
| - * in mark_tree_unintersting(). |
120 |
| - */ |
121 |
| - tree->object.flags ^= UNINTERESTING; |
122 |
| - mark_tree_uninteresting(r, tree); |
123 |
| - } |
| 220 | + if (tree->object.flags & UNINTERESTING) |
| 221 | + has_uninteresting = 1; |
| 222 | + else |
| 223 | + has_interesting = 1; |
| 224 | + } |
| 225 | + |
| 226 | + /* Do not walk unless we have both types of trees. */ |
| 227 | + if (!has_uninteresting || !has_interesting) |
| 228 | + return; |
| 229 | + |
| 230 | + paths_and_oids_init(&map); |
| 231 | + |
| 232 | + oidset_iter_init(trees, &iter); |
| 233 | + while ((oid = oidset_iter_next(&iter))) { |
| 234 | + struct tree *tree = lookup_tree(r, oid); |
| 235 | + add_children_by_path(r, tree, &map); |
124 | 236 | }
|
| 237 | + |
| 238 | + hashmap_iter_init(&map, &map_iter); |
| 239 | + while ((entry = hashmap_iter_next(&map_iter))) |
| 240 | + mark_trees_uninteresting_sparse(r, &entry->trees); |
| 241 | + |
| 242 | + paths_and_oids_clear(&map); |
125 | 243 | }
|
126 | 244 |
|
127 | 245 | struct commit_stack {
|
|
0 commit comments