Skip to content

Commit 920f93c

Browse files
derrickstoleegitster
authored andcommitted
commit-reach: move commit_contains from ref-filter
There are several commit walks in the codebase. Group them together into a new commit-reach.c file and corresponding header. After we group these walks into one place, we can reduce duplicate logic by calling equivalent methods. All methods are direct moves, except we also make the commit_contains() method public so its consumers in ref-filter.c can still call it. We can also test this method in a test-tool in a later commit. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1d614d4 commit 920f93c

File tree

3 files changed

+147
-139
lines changed

3 files changed

+147
-139
lines changed

commit-reach.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "cache.h"
22
#include "commit.h"
3+
#include "commit-graph.h"
34
#include "decorate.h"
45
#include "prio-queue.h"
56
#include "tree.h"
7+
#include "ref-filter.c"
68
#include "revision.h"
79
#include "tag.h"
810
#include "commit-reach.h"
@@ -411,3 +413,122 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
411413
unmark_and_free(used, TMP_MARK);
412414
return found;
413415
}
416+
417+
/*
418+
* Mimicking the real stack, this stack lives on the heap, avoiding stack
419+
* overflows.
420+
*
421+
* At each recursion step, the stack items points to the commits whose
422+
* ancestors are to be inspected.
423+
*/
424+
struct contains_stack {
425+
int nr, alloc;
426+
struct contains_stack_entry {
427+
struct commit *commit;
428+
struct commit_list *parents;
429+
} *contains_stack;
430+
};
431+
432+
static int in_commit_list(const struct commit_list *want, struct commit *c)
433+
{
434+
for (; want; want = want->next)
435+
if (!oidcmp(&want->item->object.oid, &c->object.oid))
436+
return 1;
437+
return 0;
438+
}
439+
440+
/*
441+
* Test whether the candidate is contained in the list.
442+
* Do not recurse to find out, though, but return -1 if inconclusive.
443+
*/
444+
static enum contains_result contains_test(struct commit *candidate,
445+
const struct commit_list *want,
446+
struct contains_cache *cache,
447+
uint32_t cutoff)
448+
{
449+
enum contains_result *cached = contains_cache_at(cache, candidate);
450+
451+
/* If we already have the answer cached, return that. */
452+
if (*cached)
453+
return *cached;
454+
455+
/* or are we it? */
456+
if (in_commit_list(want, candidate)) {
457+
*cached = CONTAINS_YES;
458+
return CONTAINS_YES;
459+
}
460+
461+
/* Otherwise, we don't know; prepare to recurse */
462+
parse_commit_or_die(candidate);
463+
464+
if (candidate->generation < cutoff)
465+
return CONTAINS_NO;
466+
467+
return CONTAINS_UNKNOWN;
468+
}
469+
470+
static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
471+
{
472+
ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);
473+
contains_stack->contains_stack[contains_stack->nr].commit = candidate;
474+
contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;
475+
}
476+
477+
static enum contains_result contains_tag_algo(struct commit *candidate,
478+
const struct commit_list *want,
479+
struct contains_cache *cache)
480+
{
481+
struct contains_stack contains_stack = { 0, 0, NULL };
482+
enum contains_result result;
483+
uint32_t cutoff = GENERATION_NUMBER_INFINITY;
484+
const struct commit_list *p;
485+
486+
for (p = want; p; p = p->next) {
487+
struct commit *c = p->item;
488+
load_commit_graph_info(the_repository, c);
489+
if (c->generation < cutoff)
490+
cutoff = c->generation;
491+
}
492+
493+
result = contains_test(candidate, want, cache, cutoff);
494+
if (result != CONTAINS_UNKNOWN)
495+
return result;
496+
497+
push_to_contains_stack(candidate, &contains_stack);
498+
while (contains_stack.nr) {
499+
struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];
500+
struct commit *commit = entry->commit;
501+
struct commit_list *parents = entry->parents;
502+
503+
if (!parents) {
504+
*contains_cache_at(cache, commit) = CONTAINS_NO;
505+
contains_stack.nr--;
506+
}
507+
/*
508+
* If we just popped the stack, parents->item has been marked,
509+
* therefore contains_test will return a meaningful yes/no.
510+
*/
511+
else switch (contains_test(parents->item, want, cache, cutoff)) {
512+
case CONTAINS_YES:
513+
*contains_cache_at(cache, commit) = CONTAINS_YES;
514+
contains_stack.nr--;
515+
break;
516+
case CONTAINS_NO:
517+
entry->parents = parents->next;
518+
break;
519+
case CONTAINS_UNKNOWN:
520+
push_to_contains_stack(parents->item, &contains_stack);
521+
break;
522+
}
523+
}
524+
free(contains_stack.contains_stack);
525+
return contains_test(candidate, want, cache, cutoff);
526+
}
527+
528+
int commit_contains(struct ref_filter *filter, struct commit *commit,
529+
struct commit_list *list, struct contains_cache *cache)
530+
{
531+
if (filter->with_commit_tag_algo)
532+
return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
533+
return is_descendant_of(commit, list);
534+
}

commit-reach.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#ifndef __COMMIT_REACH_H__
22
#define __COMMIT_REACH_H__
33

4+
#include "commit-slab.h"
5+
46
struct commit;
57
struct commit_list;
8+
struct contains_cache;
9+
struct ref_filter;
610

711
struct commit_list *get_merge_bases_many(struct commit *one,
812
int n,
@@ -20,7 +24,6 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit);
2024
int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference);
2125
int in_merge_bases(struct commit *commit, struct commit *reference);
2226

23-
2427
/*
2528
* Takes a list of commits and returns a new list where those
2629
* have been removed that can be reached from other commits in
@@ -41,4 +44,19 @@ void reduce_heads_replace(struct commit_list **heads);
4144

4245
int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
4346

47+
/*
48+
* Unknown has to be "0" here, because that's the default value for
49+
* contains_cache slab entries that have not yet been assigned.
50+
*/
51+
enum contains_result {
52+
CONTAINS_UNKNOWN = 0,
53+
CONTAINS_NO,
54+
CONTAINS_YES
55+
};
56+
57+
define_commit_slab(contains_cache, enum contains_result);
58+
59+
int commit_contains(struct ref_filter *filter, struct commit *commit,
60+
struct commit_list *list, struct contains_cache *cache);
61+
4462
#endif

ref-filter.c

Lines changed: 7 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,144 +1624,6 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
16241624
return 0;
16251625
}
16261626

1627-
/*
1628-
* Unknown has to be "0" here, because that's the default value for
1629-
* contains_cache slab entries that have not yet been assigned.
1630-
*/
1631-
enum contains_result {
1632-
CONTAINS_UNKNOWN = 0,
1633-
CONTAINS_NO,
1634-
CONTAINS_YES
1635-
};
1636-
1637-
define_commit_slab(contains_cache, enum contains_result);
1638-
1639-
struct ref_filter_cbdata {
1640-
struct ref_array *array;
1641-
struct ref_filter *filter;
1642-
struct contains_cache contains_cache;
1643-
struct contains_cache no_contains_cache;
1644-
};
1645-
1646-
/*
1647-
* Mimicking the real stack, this stack lives on the heap, avoiding stack
1648-
* overflows.
1649-
*
1650-
* At each recursion step, the stack items points to the commits whose
1651-
* ancestors are to be inspected.
1652-
*/
1653-
struct contains_stack {
1654-
int nr, alloc;
1655-
struct contains_stack_entry {
1656-
struct commit *commit;
1657-
struct commit_list *parents;
1658-
} *contains_stack;
1659-
};
1660-
1661-
static int in_commit_list(const struct commit_list *want, struct commit *c)
1662-
{
1663-
for (; want; want = want->next)
1664-
if (!oidcmp(&want->item->object.oid, &c->object.oid))
1665-
return 1;
1666-
return 0;
1667-
}
1668-
1669-
/*
1670-
* Test whether the candidate is contained in the list.
1671-
* Do not recurse to find out, though, but return -1 if inconclusive.
1672-
*/
1673-
static enum contains_result contains_test(struct commit *candidate,
1674-
const struct commit_list *want,
1675-
struct contains_cache *cache,
1676-
uint32_t cutoff)
1677-
{
1678-
enum contains_result *cached = contains_cache_at(cache, candidate);
1679-
1680-
/* If we already have the answer cached, return that. */
1681-
if (*cached)
1682-
return *cached;
1683-
1684-
/* or are we it? */
1685-
if (in_commit_list(want, candidate)) {
1686-
*cached = CONTAINS_YES;
1687-
return CONTAINS_YES;
1688-
}
1689-
1690-
/* Otherwise, we don't know; prepare to recurse */
1691-
parse_commit_or_die(candidate);
1692-
1693-
if (candidate->generation < cutoff)
1694-
return CONTAINS_NO;
1695-
1696-
return CONTAINS_UNKNOWN;
1697-
}
1698-
1699-
static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
1700-
{
1701-
ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);
1702-
contains_stack->contains_stack[contains_stack->nr].commit = candidate;
1703-
contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;
1704-
}
1705-
1706-
static enum contains_result contains_tag_algo(struct commit *candidate,
1707-
const struct commit_list *want,
1708-
struct contains_cache *cache)
1709-
{
1710-
struct contains_stack contains_stack = { 0, 0, NULL };
1711-
enum contains_result result;
1712-
uint32_t cutoff = GENERATION_NUMBER_INFINITY;
1713-
const struct commit_list *p;
1714-
1715-
for (p = want; p; p = p->next) {
1716-
struct commit *c = p->item;
1717-
load_commit_graph_info(the_repository, c);
1718-
if (c->generation < cutoff)
1719-
cutoff = c->generation;
1720-
}
1721-
1722-
result = contains_test(candidate, want, cache, cutoff);
1723-
if (result != CONTAINS_UNKNOWN)
1724-
return result;
1725-
1726-
push_to_contains_stack(candidate, &contains_stack);
1727-
while (contains_stack.nr) {
1728-
struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];
1729-
struct commit *commit = entry->commit;
1730-
struct commit_list *parents = entry->parents;
1731-
1732-
if (!parents) {
1733-
*contains_cache_at(cache, commit) = CONTAINS_NO;
1734-
contains_stack.nr--;
1735-
}
1736-
/*
1737-
* If we just popped the stack, parents->item has been marked,
1738-
* therefore contains_test will return a meaningful yes/no.
1739-
*/
1740-
else switch (contains_test(parents->item, want, cache, cutoff)) {
1741-
case CONTAINS_YES:
1742-
*contains_cache_at(cache, commit) = CONTAINS_YES;
1743-
contains_stack.nr--;
1744-
break;
1745-
case CONTAINS_NO:
1746-
entry->parents = parents->next;
1747-
break;
1748-
case CONTAINS_UNKNOWN:
1749-
push_to_contains_stack(parents->item, &contains_stack);
1750-
break;
1751-
}
1752-
}
1753-
free(contains_stack.contains_stack);
1754-
return contains_test(candidate, want, cache, cutoff);
1755-
}
1756-
1757-
static int commit_contains(struct ref_filter *filter, struct commit *commit,
1758-
struct commit_list *list, struct contains_cache *cache)
1759-
{
1760-
if (filter->with_commit_tag_algo)
1761-
return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
1762-
return is_descendant_of(commit, list);
1763-
}
1764-
17651627
/*
17661628
* Return 1 if the refname matches one of the patterns, otherwise 0.
17671629
* A pattern can be a literal prefix (e.g. a refname "refs/heads/master"
@@ -1988,6 +1850,13 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
19881850
return ref_kind_from_refname(refname);
19891851
}
19901852

1853+
struct ref_filter_cbdata {
1854+
struct ref_array *array;
1855+
struct ref_filter *filter;
1856+
struct contains_cache contains_cache;
1857+
struct contains_cache no_contains_cache;
1858+
};
1859+
19911860
/*
19921861
* A call-back given to for_each_ref(). Filter refs and keep them for
19931862
* later object processing.

0 commit comments

Comments
 (0)