Coverage Report

Created: 2026-11-12 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/pseudo-merge.c
Line
Count
Source
1
#define USE_THE_REPOSITORY_VARIABLE
2
#define DISABLE_SIGN_COMPARE_WARNINGS
3
4
#include "git-compat-util.h"
5
#include "pseudo-merge.h"
6
#include "date.h"
7
#include "oid-array.h"
8
#include "strbuf.h"
9
#include "config.h"
10
#include "string-list.h"
11
#include "refs.h"
12
#include "pack-bitmap.h"
13
#include "commit.h"
14
#include "alloc.h"
15
#include "progress.h"
16
#include "hex.h"
17
18
0
#define DEFAULT_PSEUDO_MERGE_DECAY 1.0
19
0
#define DEFAULT_PSEUDO_MERGE_MAX_MERGES 64
20
0
#define DEFAULT_PSEUDO_MERGE_SAMPLE_RATE 1
21
0
#define DEFAULT_PSEUDO_MERGE_THRESHOLD approxidate("1.week.ago")
22
0
#define DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD approxidate("1.month.ago")
23
0
#define DEFAULT_PSEUDO_MERGE_STABLE_SIZE 512
24
25
static double gitexp(double base, int exp)
26
0
{
27
0
  double result = 1;
28
0
  while (1) {
29
0
    if (exp % 2)
30
0
      result *= base;
31
0
    exp >>= 1;
32
0
    if (!exp)
33
0
      break;
34
0
    base *= base;
35
0
  }
36
0
  return result;
37
0
}
38
39
static uint32_t pseudo_merge_group_size(const struct pseudo_merge_group *group,
40
          const struct pseudo_merge_matches *matches,
41
          uint32_t i)
42
0
{
43
0
  double C = 0.0f;
44
0
  uint32_t n;
45
46
  /*
47
   * The size of pseudo-merge groups decays according to a power series,
48
   * which looks like:
49
   *
50
   *   f(n) = C * n^-k
51
   *
52
   * , where 'n' is the n-th pseudo-merge group, 'f(n)' is its size, 'k'
53
   * is the decay rate, and 'C' is a scaling value.
54
   *
55
   * The value of C depends on the number of groups, decay rate, and total
56
   * number of commits. It is computed such that if there are M and N
57
   * total groups and commits, respectively, that:
58
   *
59
   *   N = f(0) + f(1) + ... f(M-1)
60
   *
61
   * Rearranging to isolate C, we get:
62
   *
63
   *   N = \sum_{n=1}^M C / n^k
64
   *
65
   *   N / C = \sum_{n=1}^M n^-k
66
   *
67
   *   C = N / \sum_{n=1}^M n^-k
68
   *
69
   * For example, if we have a decay rate of 'k' being equal to 1.5, 'N'
70
   * total commits equal to 10,000, and 'M' being equal to 6 groups, then
71
   * the (rounded) group sizes are:
72
   *
73
   *   { 5469, 1934, 1053, 684, 489, 372 }
74
   *
75
   * increasing the number of total groups, say to 10, scales the group
76
   * sizes appropriately:
77
   *
78
   *   { 5012, 1772, 964, 626, 448, 341, 271, 221, 186, 158 }
79
   */
80
0
  for (n = 0; n < group->max_merges; n++)
81
0
    C += 1.0 / gitexp(n + 1, group->decay);
82
0
  C = matches->unstable_nr / C;
83
84
0
  return (uint32_t)((C / gitexp(i + 1, group->decay)) + 0.5);
85
0
}
86
87
static void pseudo_merge_group_init(struct pseudo_merge_group *group)
88
0
{
89
0
  memset(group, 0, sizeof(struct pseudo_merge_group));
90
91
0
  strmap_init_with_options(&group->matches, NULL, 1);
92
93
0
  group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
94
0
  group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
95
0
  group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
96
0
  group->threshold = DEFAULT_PSEUDO_MERGE_THRESHOLD;
97
0
  group->stable_threshold = DEFAULT_PSEUDO_MERGE_STABLE_THRESHOLD;
98
0
  group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
99
0
}
100
101
void pseudo_merge_group_release(struct pseudo_merge_group *group)
102
0
{
103
0
  struct hashmap_iter iter;
104
0
  struct strmap_entry *e;
105
106
0
  regfree(group->pattern);
107
0
  free(group->pattern);
108
109
0
  strmap_for_each_entry(&group->matches, &iter, e) {
110
0
    struct pseudo_merge_matches *matches = e->value;
111
0
    free(matches->stable);
112
0
    free(matches->unstable);
113
0
    free(matches);
114
0
  }
115
0
  strmap_clear(&group->matches, 0);
116
117
0
  free(group->merges);
118
0
}
119
120
static int pseudo_merge_config(const char *var, const char *value,
121
             const struct config_context *ctx,
122
             void *cb_data)
123
0
{
124
0
  struct string_list *list = cb_data;
125
0
  struct string_list_item *item;
126
0
  struct pseudo_merge_group *group;
127
0
  struct strbuf buf = STRBUF_INIT;
128
0
  const char *sub, *key;
129
0
  size_t sub_len;
130
0
  int ret = 0;
131
132
0
  if (parse_config_key(var, "bitmappseudomerge", &sub, &sub_len, &key))
133
0
    goto done;
134
135
0
  if (!sub_len)
136
0
    goto done;
137
138
0
  strbuf_add(&buf, sub, sub_len);
139
140
0
  item = string_list_lookup(list, buf.buf);
141
0
  if (!item) {
142
0
    item = string_list_insert(list, buf.buf);
143
144
0
    item->util = xmalloc(sizeof(struct pseudo_merge_group));
145
0
    pseudo_merge_group_init(item->util);
146
0
  }
147
148
0
  group = item->util;
149
150
0
  if (!strcmp(key, "pattern")) {
151
0
    struct strbuf re = STRBUF_INIT;
152
153
0
    if (group->pattern) {
154
0
      regfree(group->pattern);
155
0
      free(group->pattern);
156
0
    }
157
0
    if (*value != '^')
158
0
      strbuf_addch(&re, '^');
159
0
    strbuf_addstr(&re, value);
160
161
0
    group->pattern = xcalloc(1, sizeof(regex_t));
162
0
    if (regcomp(group->pattern, re.buf, REG_EXTENDED))
163
0
      die(_("failed to load pseudo-merge regex for %s: '%s'"),
164
0
          sub, re.buf);
165
166
0
    strbuf_release(&re);
167
0
  } else if (!strcmp(key, "decay")) {
168
0
    group->decay = git_config_double(var, value, ctx->kvi);
169
0
    if (group->decay < 0) {
170
0
      warning(_("%s must be non-negative, using default"), var);
171
0
      group->decay = DEFAULT_PSEUDO_MERGE_DECAY;
172
0
    }
173
0
  } else if (!strcmp(key, "samplerate")) {
174
0
    group->sample_rate = git_config_double(var, value, ctx->kvi);
175
0
    if (!(0 < group->sample_rate && group->sample_rate <= 1)) {
176
0
      warning(_("%s must be between 0 (exclusive) and 1, using default"), var);
177
0
      group->sample_rate = DEFAULT_PSEUDO_MERGE_SAMPLE_RATE;
178
0
    }
179
0
  } else if (!strcmp(key, "threshold")) {
180
0
    if (git_config_expiry_date(&group->threshold, var, value)) {
181
0
      ret = -1;
182
0
      goto done;
183
0
    }
184
0
  } else if (!strcmp(key, "maxmerges")) {
185
0
    group->max_merges = git_config_int(var, value, ctx->kvi);
186
0
    if (group->max_merges < 0) {
187
0
      warning(_("%s must be non-negative, using default"), var);
188
0
      group->max_merges = DEFAULT_PSEUDO_MERGE_MAX_MERGES;
189
0
    }
190
0
  } else if (!strcmp(key, "stablethreshold")) {
191
0
    if (git_config_expiry_date(&group->stable_threshold, var, value)) {
192
0
      ret = -1;
193
0
      goto done;
194
0
    }
195
0
  } else if (!strcmp(key, "stablesize")) {
196
0
    group->stable_size = git_config_int(var, value, ctx->kvi);
197
0
    if (group->stable_size <= 0) {
198
0
      warning(_("%s must be positive, using default"), var);
199
0
      group->stable_size = DEFAULT_PSEUDO_MERGE_STABLE_SIZE;
200
0
    }
201
0
  }
202
203
0
done:
204
0
  strbuf_release(&buf);
205
206
0
  return ret;
207
0
}
208
209
void load_pseudo_merges_from_config(struct repository *r,
210
            struct string_list *list)
211
0
{
212
0
  struct string_list_item *item;
213
214
0
  repo_config(r, pseudo_merge_config, list);
215
216
0
  for_each_string_list_item(item, list) {
217
0
    struct pseudo_merge_group *group = item->util;
218
0
    if (!group->pattern)
219
0
      die(_("pseudo-merge group '%s' missing required pattern"),
220
0
          item->string);
221
0
    if (group->threshold < group->stable_threshold)
222
0
      die(_("pseudo-merge group '%s' has unstable threshold "
223
0
            "before stable one"), item->string);
224
0
  }
225
0
}
226
227
static int find_pseudo_merge_group_for_ref(const struct reference *ref, void *_data)
228
0
{
229
0
  struct bitmap_writer *writer = _data;
230
0
  const struct object_id *maybe_peeled = ref->oid;
231
0
  struct object_id peeled;
232
0
  struct commit *c;
233
0
  uint32_t i;
234
0
  int has_bitmap;
235
236
0
  if (!reference_get_peeled_oid(the_repository, ref, &peeled))
237
0
    maybe_peeled = &peeled;
238
239
0
  c = lookup_commit(the_repository, maybe_peeled);
240
0
  if (!c)
241
0
    return 0;
242
0
  if (repo_parse_commit(the_repository, c))
243
0
    return 0;
244
0
  if (!packlist_find(writer->to_pack, maybe_peeled))
245
0
    return 0;
246
247
0
  has_bitmap = bitmap_writer_has_bitmapped_object_id(writer, maybe_peeled);
248
249
0
  for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
250
0
    struct pseudo_merge_group *group;
251
0
    struct pseudo_merge_matches *matches;
252
0
    struct strbuf group_name = STRBUF_INIT;
253
0
    regmatch_t captures[16];
254
0
    size_t j;
255
256
0
    group = writer->pseudo_merge_groups.items[i].util;
257
0
    if (regexec(group->pattern, ref->name, ARRAY_SIZE(captures),
258
0
          captures, 0))
259
0
      continue;
260
261
0
    if (captures[ARRAY_SIZE(captures) - 1].rm_so != -1)
262
0
      warning(_("pseudo-merge regex from config has too many capture "
263
0
          "groups (max=%"PRIuMAX")"),
264
0
        (uintmax_t)ARRAY_SIZE(captures) - 2);
265
266
0
    for (j = !!group->pattern->re_nsub; j < ARRAY_SIZE(captures); j++) {
267
0
      regmatch_t *match = &captures[j];
268
0
      if (match->rm_so == -1)
269
0
        continue;
270
271
0
      if (group_name.len)
272
0
        strbuf_addch(&group_name, '-');
273
274
0
      strbuf_add(&group_name, ref->name + match->rm_so,
275
0
           match->rm_eo - match->rm_so);
276
0
    }
277
278
0
    matches = strmap_get(&group->matches, group_name.buf);
279
0
    if (!matches) {
280
0
      matches = xcalloc(1, sizeof(*matches));
281
0
      strmap_put(&group->matches, group_name.buf,
282
0
           matches);
283
0
    }
284
285
0
    if (c->date <= group->stable_threshold) {
286
0
      ALLOC_GROW(matches->stable, matches->stable_nr + 1,
287
0
           matches->stable_alloc);
288
0
      matches->stable[matches->stable_nr++] = c;
289
0
    } else if (c->date <= group->threshold && !has_bitmap) {
290
0
      ALLOC_GROW(matches->unstable, matches->unstable_nr + 1,
291
0
           matches->unstable_alloc);
292
0
      matches->unstable[matches->unstable_nr++] = c;
293
0
    }
294
295
0
    strbuf_release(&group_name);
296
0
  }
297
298
0
  return 0;
299
0
}
300
301
static struct commit *push_pseudo_merge(struct pseudo_merge_group *group)
302
0
{
303
0
  struct commit *merge;
304
305
0
  ALLOC_GROW(group->merges, group->merges_nr + 1, group->merges_alloc);
306
307
0
  merge = alloc_commit_node(the_repository);
308
0
  merge->object.parsed = 1;
309
0
  merge->object.flags |= BITMAP_PSEUDO_MERGE;
310
311
0
  group->merges[group->merges_nr++] = merge;
312
313
0
  return merge;
314
0
}
315
316
static struct pseudo_merge_commit_idx *pseudo_merge_idx(kh_oid_map_t *pseudo_merge_commits,
317
              const struct object_id *oid)
318
319
0
{
320
0
  struct pseudo_merge_commit_idx *pmc;
321
0
  int hash_ret;
322
0
  khiter_t hash_pos = kh_put_oid_map(pseudo_merge_commits, *oid,
323
0
             &hash_ret);
324
325
0
  if (hash_ret) {
326
0
    CALLOC_ARRAY(pmc, 1);
327
0
    kh_value(pseudo_merge_commits, hash_pos) = pmc;
328
0
  } else {
329
0
    pmc = kh_value(pseudo_merge_commits, hash_pos);
330
0
  }
331
332
0
  return pmc;
333
0
}
334
335
0
#define MIN_PSEUDO_MERGE_SIZE 8
336
337
static void select_pseudo_merges_1(struct bitmap_writer *writer,
338
           struct pseudo_merge_group *group,
339
           struct pseudo_merge_matches *matches)
340
0
{
341
0
  uint32_t i, j;
342
0
  uint32_t stable_merges_nr;
343
344
0
  if (!matches->stable_nr && !matches->unstable_nr)
345
0
    return; /* all tips in this group already have bitmaps */
346
347
0
  stable_merges_nr = matches->stable_nr / group->stable_size;
348
0
  if (matches->stable_nr % group->stable_size)
349
0
    stable_merges_nr++;
350
351
  /* make stable_merges_nr pseudo merges for stable commits */
352
0
  for (i = 0, j = 0; i < stable_merges_nr; i++) {
353
0
    struct commit *merge;
354
0
    struct commit_list **p;
355
356
0
    merge = push_pseudo_merge(group);
357
0
    p = &merge->parents;
358
359
    /*
360
     * For each pseudo-merge created above, add parents to the
361
     * allocated commit node from the stable set of commits
362
     * (un-bitmapped, newer than the stable threshold).
363
     */
364
0
    do {
365
0
      struct commit *c;
366
0
      struct pseudo_merge_commit_idx *pmc;
367
368
0
      if (j >= matches->stable_nr)
369
0
        break;
370
371
0
      c = matches->stable[j++];
372
      /*
373
       * Here and below, make sure that we keep our mapping of
374
       * commits -> pseudo-merge(s) which include the key'd
375
       * commit up-to-date.
376
       */
377
0
      pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
378
0
                 &c->object.oid);
379
380
0
      ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
381
382
0
      pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
383
0
      p = commit_list_append(c, p);
384
0
    } while (j % group->stable_size);
385
386
0
    if (merge->parents) {
387
0
      bitmap_writer_push_commit(writer, merge, 1);
388
0
      writer->pseudo_merges_nr++;
389
0
    }
390
0
  }
391
392
  /* make up to group->max_merges pseudo merges for unstable commits */
393
0
  for (i = 0, j = 0; i < group->max_merges; i++) {
394
0
    struct commit *merge;
395
0
    struct commit_list **p;
396
0
    uint32_t size, end;
397
398
0
    merge = push_pseudo_merge(group);
399
0
    p = &merge->parents;
400
401
0
    size = pseudo_merge_group_size(group, matches, i);
402
0
    end = size < MIN_PSEUDO_MERGE_SIZE ? matches->unstable_nr : j + size;
403
404
    /*
405
     * For each pseudo-merge commit created above, add parents to
406
     * the allocated commit node from the unstable set of commits
407
     * (newer than the stable threshold).
408
     *
409
     * Account for the sample rate, since not every candidate from
410
     * the set of stable commits will be included as a pseudo-merge
411
     * parent.
412
     */
413
0
    for (; j < end && j < matches->unstable_nr; j++) {
414
0
      struct commit *c = matches->unstable[j];
415
0
      struct pseudo_merge_commit_idx *pmc;
416
417
0
      if (j % (uint32_t)(1.0 / group->sample_rate))
418
0
        continue;
419
420
0
      pmc = pseudo_merge_idx(writer->pseudo_merge_commits,
421
0
                 &c->object.oid);
422
423
0
      ALLOC_GROW(pmc->pseudo_merge, pmc->nr + 1, pmc->alloc);
424
425
0
      pmc->pseudo_merge[pmc->nr++] = writer->pseudo_merges_nr;
426
0
      p = commit_list_append(c, p);
427
0
    }
428
429
0
    if (merge->parents) {
430
0
      bitmap_writer_push_commit(writer, merge, 1);
431
0
      writer->pseudo_merges_nr++; }
432
0
    if (end >= matches->unstable_nr)
433
0
      break;
434
0
  }
435
0
}
436
437
static int commit_date_cmp(const void *va, const void *vb)
438
0
{
439
0
  timestamp_t a = (*(const struct commit **)va)->date;
440
0
  timestamp_t b = (*(const struct commit **)vb)->date;
441
442
0
  if (a < b)
443
0
    return -1;
444
0
  else if (a > b)
445
0
    return 1;
446
0
  return 0;
447
0
}
448
449
static void sort_pseudo_merge_matches(struct pseudo_merge_matches *matches)
450
0
{
451
0
  QSORT(matches->stable, matches->stable_nr, commit_date_cmp);
452
0
  QSORT(matches->unstable, matches->unstable_nr, commit_date_cmp);
453
0
}
454
455
void select_pseudo_merges(struct bitmap_writer *writer)
456
0
{
457
0
  struct progress *progress = NULL;
458
0
  uint32_t i;
459
460
0
  if (!writer->pseudo_merge_groups.nr)
461
0
    return;
462
463
0
  if (writer->show_progress)
464
0
    progress = start_progress(the_repository,
465
0
            "Selecting pseudo-merge commits",
466
0
            writer->pseudo_merge_groups.nr);
467
468
0
  refs_for_each_ref(get_main_ref_store(the_repository),
469
0
        find_pseudo_merge_group_for_ref, writer);
470
471
0
  for (i = 0; i < writer->pseudo_merge_groups.nr; i++) {
472
0
    struct pseudo_merge_group *group;
473
0
    struct hashmap_iter iter;
474
0
    struct strmap_entry *e;
475
476
0
    group = writer->pseudo_merge_groups.items[i].util;
477
0
    strmap_for_each_entry(&group->matches, &iter, e) {
478
0
      struct pseudo_merge_matches *matches = e->value;
479
480
0
      sort_pseudo_merge_matches(matches);
481
482
0
      select_pseudo_merges_1(writer, group, matches);
483
0
    }
484
485
0
    display_progress(progress, i + 1);
486
0
  }
487
488
0
  stop_progress(&progress);
489
0
}
490
491
void free_pseudo_merge_map(struct pseudo_merge_map *pm)
492
0
{
493
0
  uint32_t i;
494
0
  for (i = 0; i < pm->nr; i++) {
495
0
    ewah_pool_free(pm->v[i].commits);
496
0
    ewah_pool_free(pm->v[i].bitmap);
497
0
  }
498
0
  free(pm->v);
499
0
}
500
501
struct pseudo_merge_commit_ext {
502
  uint32_t nr;
503
  const unsigned char *ptr;
504
};
505
506
static int pseudo_merge_ext_at(const struct pseudo_merge_map *pm,
507
             struct pseudo_merge_commit_ext *ext, size_t at)
508
0
{
509
0
  if (at >= pm->map_size)
510
0
    return error(_("extended pseudo-merge read out-of-bounds "
511
0
             "(%"PRIuMAX" >= %"PRIuMAX")"),
512
0
           (uintmax_t)at, (uintmax_t)pm->map_size);
513
0
  if (at + 4 >= pm->map_size)
514
0
    return error(_("extended pseudo-merge entry is too short "
515
0
             "(%"PRIuMAX" >= %"PRIuMAX")"),
516
0
           (uintmax_t)(at + 4), (uintmax_t)pm->map_size);
517
518
0
  ext->nr = get_be32(pm->map + at);
519
0
  ext->ptr = pm->map + at + sizeof(uint32_t);
520
521
0
  return 0;
522
0
}
523
524
struct ewah_bitmap *pseudo_merge_bitmap(const struct pseudo_merge_map *pm,
525
          struct pseudo_merge *merge)
526
0
{
527
0
  if (!merge->loaded_commits)
528
0
    BUG("cannot use unloaded pseudo-merge bitmap");
529
530
0
  if (!merge->loaded_bitmap) {
531
0
    size_t at = merge->bitmap_at;
532
533
0
    merge->bitmap = read_bitmap(pm->map, pm->map_size, &at);
534
0
    merge->loaded_bitmap = 1;
535
0
  }
536
537
0
  return merge->bitmap;
538
0
}
539
540
struct pseudo_merge *use_pseudo_merge(const struct pseudo_merge_map *pm,
541
              struct pseudo_merge *merge)
542
0
{
543
0
  if (!merge->loaded_commits) {
544
0
    size_t pos = merge->at;
545
546
0
    merge->commits = read_bitmap(pm->map, pm->map_size, &pos);
547
0
    merge->bitmap_at = pos;
548
0
    merge->loaded_commits = 1;
549
0
  }
550
0
  return merge;
551
0
}
552
553
static struct pseudo_merge *pseudo_merge_at(const struct pseudo_merge_map *pm,
554
              struct object_id *oid,
555
              size_t want)
556
0
{
557
0
  size_t lo = 0;
558
0
  size_t hi = pm->nr;
559
560
0
  while (lo < hi) {
561
0
    size_t mi = lo + (hi - lo) / 2;
562
0
    size_t got = pm->v[mi].at;
563
564
0
    if (got == want)
565
0
      return use_pseudo_merge(pm, &pm->v[mi]);
566
0
    else if (got < want)
567
0
      lo = mi + 1;
568
0
    else
569
0
      hi = mi;
570
0
  }
571
572
0
  warning(_("could not find pseudo-merge for commit %s at offset %"PRIuMAX),
573
0
    oid_to_hex(oid), (uintmax_t)want);
574
575
0
  return NULL;
576
0
}
577
578
struct pseudo_merge_commit {
579
  uint32_t commit_pos;
580
  uint64_t pseudo_merge_ofs;
581
};
582
583
0
#define PSEUDO_MERGE_COMMIT_RAWSZ (sizeof(uint32_t)+sizeof(uint64_t))
584
585
static void read_pseudo_merge_commit_at(struct pseudo_merge_commit *merge,
586
          const unsigned char *at)
587
0
{
588
0
  merge->commit_pos = get_be32(at);
589
0
  merge->pseudo_merge_ofs = get_be64(at + sizeof(uint32_t));
590
0
}
591
592
static int nth_pseudo_merge_ext(const struct pseudo_merge_map *pm,
593
        struct pseudo_merge_commit_ext *ext,
594
        struct pseudo_merge_commit *merge,
595
        uint32_t n)
596
0
{
597
0
  size_t ofs;
598
599
0
  if (n >= ext->nr)
600
0
    return error(_("extended pseudo-merge lookup out-of-bounds "
601
0
             "(%"PRIu32" >= %"PRIu32")"), n, ext->nr);
602
603
0
  ofs = get_be64(ext->ptr + st_mult(n, sizeof(uint64_t)));
604
0
  if (ofs >= pm->map_size)
605
0
    return error(_("out-of-bounds read: (%"PRIuMAX" >= %"PRIuMAX")"),
606
0
           (uintmax_t)ofs, (uintmax_t)pm->map_size);
607
608
0
  merge->pseudo_merge_ofs = ofs;
609
610
0
  return 0;
611
0
}
612
613
static unsigned apply_pseudo_merge(const struct pseudo_merge_map *pm,
614
           struct pseudo_merge *merge,
615
           struct bitmap *result,
616
           struct bitmap *roots)
617
0
{
618
0
  if (merge->satisfied)
619
0
    return 0;
620
621
0
  if (!ewah_bitmap_is_subset(merge->commits, roots ? roots : result))
622
0
    return 0;
623
624
0
  bitmap_or_ewah(result, pseudo_merge_bitmap(pm, merge));
625
0
  if (roots)
626
0
    bitmap_or_ewah(roots, pseudo_merge_bitmap(pm, merge));
627
0
  merge->satisfied = 1;
628
629
0
  return 1;
630
0
}
631
632
static int pseudo_merge_commit_cmp(const void *va, const void *vb)
633
0
{
634
0
  struct pseudo_merge_commit merge;
635
0
  uint32_t key = *(uint32_t*)va;
636
637
0
  read_pseudo_merge_commit_at(&merge, vb);
638
639
0
  if (key < merge.commit_pos)
640
0
    return -1;
641
0
  if (key > merge.commit_pos)
642
0
    return 1;
643
0
  return 0;
644
0
}
645
646
static int find_pseudo_merge(const struct pseudo_merge_map *pm, uint32_t pos,
647
           struct pseudo_merge_commit *out)
648
0
{
649
0
  const unsigned char *at;
650
651
0
  if (!pm->commits_nr)
652
0
    return 0;
653
654
0
  at = bsearch(&pos, pm->commits, pm->commits_nr,
655
0
         PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp);
656
0
  if (!at)
657
0
    return 0;
658
659
0
  read_pseudo_merge_commit_at(out, at);
660
0
  return 1;
661
0
}
662
663
int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm,
664
           struct bitmap *result,
665
           struct commit *commit, uint32_t commit_pos)
666
0
{
667
0
  struct pseudo_merge *merge;
668
0
  struct pseudo_merge_commit merge_commit;
669
0
  int ret = 0;
670
671
0
  if (!find_pseudo_merge(pm, commit_pos, &merge_commit))
672
0
    return 0;
673
674
0
  if (merge_commit.pseudo_merge_ofs & ((uint64_t)1<<63)) {
675
0
    struct pseudo_merge_commit_ext ext = { 0 };
676
0
    off_t ofs = merge_commit.pseudo_merge_ofs & ~((uint64_t)1<<63);
677
0
    uint32_t i;
678
679
0
    if (pseudo_merge_ext_at(pm, &ext, ofs) < 0) {
680
0
      warning(_("could not read extended pseudo-merge table "
681
0
          "for commit %s"),
682
0
        oid_to_hex(&commit->object.oid));
683
0
      return ret;
684
0
    }
685
686
0
    for (i = 0; i < ext.nr; i++) {
687
0
      if (nth_pseudo_merge_ext(pm, &ext, &merge_commit, i) < 0)
688
0
        return ret;
689
690
0
      merge = pseudo_merge_at(pm, &commit->object.oid,
691
0
            merge_commit.pseudo_merge_ofs);
692
693
0
      if (!merge)
694
0
        return ret;
695
696
0
      if (apply_pseudo_merge(pm, merge, result, NULL))
697
0
        ret++;
698
0
    }
699
0
  } else {
700
0
    merge = pseudo_merge_at(pm, &commit->object.oid,
701
0
          merge_commit.pseudo_merge_ofs);
702
703
0
    if (!merge)
704
0
      return ret;
705
706
0
    if (apply_pseudo_merge(pm, merge, result, NULL))
707
0
      ret++;
708
0
  }
709
710
0
  if (ret)
711
0
    cascade_pseudo_merges(pm, result, NULL);
712
713
0
  return ret;
714
0
}
715
716
int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
717
        struct bitmap *result,
718
        struct bitmap *roots)
719
0
{
720
0
  unsigned any_satisfied;
721
0
  int ret = 0;
722
723
0
  do {
724
0
    struct pseudo_merge *merge;
725
0
    uint32_t i;
726
727
0
    any_satisfied = 0;
728
729
0
    for (i = 0; i < pm->nr; i++) {
730
0
      merge = use_pseudo_merge(pm, &pm->v[i]);
731
0
      if (apply_pseudo_merge(pm, merge, result, roots)) {
732
0
        any_satisfied |= 1;
733
0
        ret++;
734
0
      }
735
0
    }
736
0
  } while (any_satisfied);
737
738
0
  return ret;
739
0
}
740
741
struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
742
                struct bitmap *parents)
743
0
{
744
0
  struct pseudo_merge *match = NULL;
745
0
  size_t i;
746
747
0
  if (!pm->nr)
748
0
    return NULL;
749
750
  /*
751
   * NOTE: this loop is quadratic in the worst-case (where no
752
   * matching pseudo-merge bitmaps are found), but in practice
753
   * this is OK for a few reasons:
754
   *
755
   *   - Rejecting pseudo-merge bitmaps that do not match the
756
   *     given commit is done quickly (i.e. `bitmap_equals_ewah()`
757
   *     returns early when we know the two bitmaps aren't equal.
758
   *
759
   *   - Already matched pseudo-merge bitmaps (which we track with
760
   *     the `->satisfied` bit here) are skipped as potential
761
   *     candidates.
762
   *
763
   *   - The number of pseudo-merges should be small (in the
764
   *     hundreds for most repositories).
765
   *
766
   * If in the future this semi-quadratic behavior does become a
767
   * problem, another approach would be to keep track of which
768
   * pseudo-merges are still "viable" after enumerating the
769
   * pseudo-merge commit's parents:
770
   *
771
   *   - A pseudo-merge bitmap becomes non-viable when the bit(s)
772
   *     corresponding to one or more parent(s) of the given
773
   *     commit are not set in a candidate pseudo-merge's commits
774
   *     bitmap.
775
   *
776
   *   - After processing all bits, enumerate the remaining set of
777
   *     viable pseudo-merge bitmaps, and check that their
778
   *     popcount() matches the number of parents in the given
779
   *     commit.
780
   */
781
0
  for (i = 0; i < pm->nr; i++) {
782
0
    struct pseudo_merge *candidate = use_pseudo_merge(pm, &pm->v[i]);
783
0
    if (!candidate || candidate->satisfied)
784
0
      continue;
785
0
    if (!bitmap_equals_ewah(parents, candidate->commits))
786
0
      continue;
787
788
0
    match = candidate;
789
0
    match->satisfied = 1;
790
0
    break;
791
0
  }
792
793
0
  return match;
794
0
}