|
| 1 | +#include "cache.h" |
| 2 | +#include "prio-queue.h" |
| 3 | +#include "commit.h" |
| 4 | +#include "commit-reach.h" |
| 5 | + |
| 6 | +/* Remember to update object flag allocation in object.h */ |
| 7 | +#define PARENT1 (1u<<16) |
| 8 | +#define PARENT2 (1u<<17) |
| 9 | +#define STALE (1u<<18) |
| 10 | +#define RESULT (1u<<19) |
| 11 | + |
| 12 | +static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); |
| 13 | + |
| 14 | +static int queue_has_nonstale(struct prio_queue *queue) |
| 15 | +{ |
| 16 | + int i; |
| 17 | + for (i = 0; i < queue->nr; i++) { |
| 18 | + struct commit *commit = queue->array[i].data; |
| 19 | + if (!(commit->object.flags & STALE)) |
| 20 | + return 1; |
| 21 | + } |
| 22 | + return 0; |
| 23 | +} |
| 24 | + |
| 25 | +/* all input commits in one and twos[] must have been parsed! */ |
| 26 | +static struct commit_list *paint_down_to_common(struct commit *one, int n, |
| 27 | + struct commit **twos, |
| 28 | + int min_generation) |
| 29 | +{ |
| 30 | + struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; |
| 31 | + struct commit_list *result = NULL; |
| 32 | + int i; |
| 33 | + uint32_t last_gen = GENERATION_NUMBER_INFINITY; |
| 34 | + |
| 35 | + one->object.flags |= PARENT1; |
| 36 | + if (!n) { |
| 37 | + commit_list_append(one, &result); |
| 38 | + return result; |
| 39 | + } |
| 40 | + prio_queue_put(&queue, one); |
| 41 | + |
| 42 | + for (i = 0; i < n; i++) { |
| 43 | + twos[i]->object.flags |= PARENT2; |
| 44 | + prio_queue_put(&queue, twos[i]); |
| 45 | + } |
| 46 | + |
| 47 | + while (queue_has_nonstale(&queue)) { |
| 48 | + struct commit *commit = prio_queue_get(&queue); |
| 49 | + struct commit_list *parents; |
| 50 | + int flags; |
| 51 | + |
| 52 | + if (commit->generation > last_gen) |
| 53 | + BUG("bad generation skip %8x > %8x at %s", |
| 54 | + commit->generation, last_gen, |
| 55 | + oid_to_hex(&commit->object.oid)); |
| 56 | + last_gen = commit->generation; |
| 57 | + |
| 58 | + if (commit->generation < min_generation) |
| 59 | + break; |
| 60 | + |
| 61 | + flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); |
| 62 | + if (flags == (PARENT1 | PARENT2)) { |
| 63 | + if (!(commit->object.flags & RESULT)) { |
| 64 | + commit->object.flags |= RESULT; |
| 65 | + commit_list_insert_by_date(commit, &result); |
| 66 | + } |
| 67 | + /* Mark parents of a found merge stale */ |
| 68 | + flags |= STALE; |
| 69 | + } |
| 70 | + parents = commit->parents; |
| 71 | + while (parents) { |
| 72 | + struct commit *p = parents->item; |
| 73 | + parents = parents->next; |
| 74 | + if ((p->object.flags & flags) == flags) |
| 75 | + continue; |
| 76 | + if (parse_commit(p)) |
| 77 | + return NULL; |
| 78 | + p->object.flags |= flags; |
| 79 | + prio_queue_put(&queue, p); |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + clear_prio_queue(&queue); |
| 84 | + return result; |
| 85 | +} |
| 86 | + |
| 87 | +static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos) |
| 88 | +{ |
| 89 | + struct commit_list *list = NULL; |
| 90 | + struct commit_list *result = NULL; |
| 91 | + int i; |
| 92 | + |
| 93 | + for (i = 0; i < n; i++) { |
| 94 | + if (one == twos[i]) |
| 95 | + /* |
| 96 | + * We do not mark this even with RESULT so we do not |
| 97 | + * have to clean it up. |
| 98 | + */ |
| 99 | + return commit_list_insert(one, &result); |
| 100 | + } |
| 101 | + |
| 102 | + if (parse_commit(one)) |
| 103 | + return NULL; |
| 104 | + for (i = 0; i < n; i++) { |
| 105 | + if (parse_commit(twos[i])) |
| 106 | + return NULL; |
| 107 | + } |
| 108 | + |
| 109 | + list = paint_down_to_common(one, n, twos, 0); |
| 110 | + |
| 111 | + while (list) { |
| 112 | + struct commit *commit = pop_commit(&list); |
| 113 | + if (!(commit->object.flags & STALE)) |
| 114 | + commit_list_insert_by_date(commit, &result); |
| 115 | + } |
| 116 | + return result; |
| 117 | +} |
| 118 | + |
| 119 | +struct commit_list *get_octopus_merge_bases(struct commit_list *in) |
| 120 | +{ |
| 121 | + struct commit_list *i, *j, *k, *ret = NULL; |
| 122 | + |
| 123 | + if (!in) |
| 124 | + return ret; |
| 125 | + |
| 126 | + commit_list_insert(in->item, &ret); |
| 127 | + |
| 128 | + for (i = in->next; i; i = i->next) { |
| 129 | + struct commit_list *new_commits = NULL, *end = NULL; |
| 130 | + |
| 131 | + for (j = ret; j; j = j->next) { |
| 132 | + struct commit_list *bases; |
| 133 | + bases = get_merge_bases(i->item, j->item); |
| 134 | + if (!new_commits) |
| 135 | + new_commits = bases; |
| 136 | + else |
| 137 | + end->next = bases; |
| 138 | + for (k = bases; k; k = k->next) |
| 139 | + end = k; |
| 140 | + } |
| 141 | + ret = new_commits; |
| 142 | + } |
| 143 | + return ret; |
| 144 | +} |
| 145 | + |
| 146 | +static int remove_redundant(struct commit **array, int cnt) |
| 147 | +{ |
| 148 | + /* |
| 149 | + * Some commit in the array may be an ancestor of |
| 150 | + * another commit. Move such commit to the end of |
| 151 | + * the array, and return the number of commits that |
| 152 | + * are independent from each other. |
| 153 | + */ |
| 154 | + struct commit **work; |
| 155 | + unsigned char *redundant; |
| 156 | + int *filled_index; |
| 157 | + int i, j, filled; |
| 158 | + |
| 159 | + work = xcalloc(cnt, sizeof(*work)); |
| 160 | + redundant = xcalloc(cnt, 1); |
| 161 | + ALLOC_ARRAY(filled_index, cnt - 1); |
| 162 | + |
| 163 | + for (i = 0; i < cnt; i++) |
| 164 | + parse_commit(array[i]); |
| 165 | + for (i = 0; i < cnt; i++) { |
| 166 | + struct commit_list *common; |
| 167 | + uint32_t min_generation = array[i]->generation; |
| 168 | + |
| 169 | + if (redundant[i]) |
| 170 | + continue; |
| 171 | + for (j = filled = 0; j < cnt; j++) { |
| 172 | + if (i == j || redundant[j]) |
| 173 | + continue; |
| 174 | + filled_index[filled] = j; |
| 175 | + work[filled++] = array[j]; |
| 176 | + |
| 177 | + if (array[j]->generation < min_generation) |
| 178 | + min_generation = array[j]->generation; |
| 179 | + } |
| 180 | + common = paint_down_to_common(array[i], filled, work, |
| 181 | + min_generation); |
| 182 | + if (array[i]->object.flags & PARENT2) |
| 183 | + redundant[i] = 1; |
| 184 | + for (j = 0; j < filled; j++) |
| 185 | + if (work[j]->object.flags & PARENT1) |
| 186 | + redundant[filled_index[j]] = 1; |
| 187 | + clear_commit_marks(array[i], all_flags); |
| 188 | + clear_commit_marks_many(filled, work, all_flags); |
| 189 | + free_commit_list(common); |
| 190 | + } |
| 191 | + |
| 192 | + /* Now collect the result */ |
| 193 | + COPY_ARRAY(work, array, cnt); |
| 194 | + for (i = filled = 0; i < cnt; i++) |
| 195 | + if (!redundant[i]) |
| 196 | + array[filled++] = work[i]; |
| 197 | + for (j = filled, i = 0; i < cnt; i++) |
| 198 | + if (redundant[i]) |
| 199 | + array[j++] = work[i]; |
| 200 | + free(work); |
| 201 | + free(redundant); |
| 202 | + free(filled_index); |
| 203 | + return filled; |
| 204 | +} |
| 205 | + |
| 206 | +static struct commit_list *get_merge_bases_many_0(struct commit *one, |
| 207 | + int n, |
| 208 | + struct commit **twos, |
| 209 | + int cleanup) |
| 210 | +{ |
| 211 | + struct commit_list *list; |
| 212 | + struct commit **rslt; |
| 213 | + struct commit_list *result; |
| 214 | + int cnt, i; |
| 215 | + |
| 216 | + result = merge_bases_many(one, n, twos); |
| 217 | + for (i = 0; i < n; i++) { |
| 218 | + if (one == twos[i]) |
| 219 | + return result; |
| 220 | + } |
| 221 | + if (!result || !result->next) { |
| 222 | + if (cleanup) { |
| 223 | + clear_commit_marks(one, all_flags); |
| 224 | + clear_commit_marks_many(n, twos, all_flags); |
| 225 | + } |
| 226 | + return result; |
| 227 | + } |
| 228 | + |
| 229 | + /* There are more than one */ |
| 230 | + cnt = commit_list_count(result); |
| 231 | + rslt = xcalloc(cnt, sizeof(*rslt)); |
| 232 | + for (list = result, i = 0; list; list = list->next) |
| 233 | + rslt[i++] = list->item; |
| 234 | + free_commit_list(result); |
| 235 | + |
| 236 | + clear_commit_marks(one, all_flags); |
| 237 | + clear_commit_marks_many(n, twos, all_flags); |
| 238 | + |
| 239 | + cnt = remove_redundant(rslt, cnt); |
| 240 | + result = NULL; |
| 241 | + for (i = 0; i < cnt; i++) |
| 242 | + commit_list_insert_by_date(rslt[i], &result); |
| 243 | + free(rslt); |
| 244 | + return result; |
| 245 | +} |
| 246 | + |
| 247 | +struct commit_list *get_merge_bases_many(struct commit *one, |
| 248 | + int n, |
| 249 | + struct commit **twos) |
| 250 | +{ |
| 251 | + return get_merge_bases_many_0(one, n, twos, 1); |
| 252 | +} |
| 253 | + |
| 254 | +struct commit_list *get_merge_bases_many_dirty(struct commit *one, |
| 255 | + int n, |
| 256 | + struct commit **twos) |
| 257 | +{ |
| 258 | + return get_merge_bases_many_0(one, n, twos, 0); |
| 259 | +} |
| 260 | + |
| 261 | +struct commit_list *get_merge_bases(struct commit *one, struct commit *two) |
| 262 | +{ |
| 263 | + return get_merge_bases_many_0(one, 1, &two, 1); |
| 264 | +} |
| 265 | + |
| 266 | +/* |
| 267 | + * Is "commit" a descendant of one of the elements on the "with_commit" list? |
| 268 | + */ |
| 269 | +int is_descendant_of(struct commit *commit, struct commit_list *with_commit) |
| 270 | +{ |
| 271 | + if (!with_commit) |
| 272 | + return 1; |
| 273 | + while (with_commit) { |
| 274 | + struct commit *other; |
| 275 | + |
| 276 | + other = with_commit->item; |
| 277 | + with_commit = with_commit->next; |
| 278 | + if (in_merge_bases(other, commit)) |
| 279 | + return 1; |
| 280 | + } |
| 281 | + return 0; |
| 282 | +} |
| 283 | + |
| 284 | +/* |
| 285 | + * Is "commit" an ancestor of one of the "references"? |
| 286 | + */ |
| 287 | +int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference) |
| 288 | +{ |
| 289 | + struct commit_list *bases; |
| 290 | + int ret = 0, i; |
| 291 | + uint32_t min_generation = GENERATION_NUMBER_INFINITY; |
| 292 | + |
| 293 | + if (parse_commit(commit)) |
| 294 | + return ret; |
| 295 | + for (i = 0; i < nr_reference; i++) { |
| 296 | + if (parse_commit(reference[i])) |
| 297 | + return ret; |
| 298 | + if (reference[i]->generation < min_generation) |
| 299 | + min_generation = reference[i]->generation; |
| 300 | + } |
| 301 | + |
| 302 | + if (commit->generation > min_generation) |
| 303 | + return ret; |
| 304 | + |
| 305 | + bases = paint_down_to_common(commit, nr_reference, reference, commit->generation); |
| 306 | + if (commit->object.flags & PARENT2) |
| 307 | + ret = 1; |
| 308 | + clear_commit_marks(commit, all_flags); |
| 309 | + clear_commit_marks_many(nr_reference, reference, all_flags); |
| 310 | + free_commit_list(bases); |
| 311 | + return ret; |
| 312 | +} |
| 313 | + |
| 314 | +/* |
| 315 | + * Is "commit" an ancestor of (i.e. reachable from) the "reference"? |
| 316 | + */ |
| 317 | +int in_merge_bases(struct commit *commit, struct commit *reference) |
| 318 | +{ |
| 319 | + return in_merge_bases_many(commit, 1, &reference); |
| 320 | +} |
| 321 | + |
| 322 | +struct commit_list *reduce_heads(struct commit_list *heads) |
| 323 | +{ |
| 324 | + struct commit_list *p; |
| 325 | + struct commit_list *result = NULL, **tail = &result; |
| 326 | + struct commit **array; |
| 327 | + int num_head, i; |
| 328 | + |
| 329 | + if (!heads) |
| 330 | + return NULL; |
| 331 | + |
| 332 | + /* Uniquify */ |
| 333 | + for (p = heads; p; p = p->next) |
| 334 | + p->item->object.flags &= ~STALE; |
| 335 | + for (p = heads, num_head = 0; p; p = p->next) { |
| 336 | + if (p->item->object.flags & STALE) |
| 337 | + continue; |
| 338 | + p->item->object.flags |= STALE; |
| 339 | + num_head++; |
| 340 | + } |
| 341 | + array = xcalloc(num_head, sizeof(*array)); |
| 342 | + for (p = heads, i = 0; p; p = p->next) { |
| 343 | + if (p->item->object.flags & STALE) { |
| 344 | + array[i++] = p->item; |
| 345 | + p->item->object.flags &= ~STALE; |
| 346 | + } |
| 347 | + } |
| 348 | + num_head = remove_redundant(array, num_head); |
| 349 | + for (i = 0; i < num_head; i++) |
| 350 | + tail = &commit_list_insert(array[i], tail)->next; |
| 351 | + free(array); |
| 352 | + return result; |
| 353 | +} |
| 354 | + |
| 355 | +void reduce_heads_replace(struct commit_list **heads) |
| 356 | +{ |
| 357 | + struct commit_list *result = reduce_heads(*heads); |
| 358 | + free_commit_list(*heads); |
| 359 | + *heads = result; |
| 360 | +} |
0 commit comments