2018-07-20 16:33:15 +00:00
|
|
|
#include "test-tool.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "commit-reach.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2023-04-11 07:41:49 +00:00
|
|
|
#include "object-name.h"
|
2018-07-20 16:33:25 +00:00
|
|
|
#include "ref-filter.h"
|
2023-03-21 06:26:05 +00:00
|
|
|
#include "setup.h"
|
2018-07-20 16:33:20 +00:00
|
|
|
#include "string-list.h"
|
2018-07-20 16:33:15 +00:00
|
|
|
#include "tag.h"
|
|
|
|
|
2018-07-20 16:33:20 +00:00
|
|
|
static void print_sorted_commit_ids(struct commit_list *list)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct string_list s = STRING_LIST_INIT_DUP;
|
|
|
|
|
|
|
|
while (list) {
|
|
|
|
string_list_append(&s, oid_to_hex(&list->item->object.oid));
|
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
string_list_sort(&s);
|
|
|
|
|
|
|
|
for (i = 0; i < s.nr; i++)
|
|
|
|
printf("%s\n", s.items[i].string);
|
|
|
|
|
|
|
|
string_list_clear(&s, 0);
|
|
|
|
}
|
|
|
|
|
2018-07-20 16:33:15 +00:00
|
|
|
int cmd__reach(int ac, const char **av)
|
|
|
|
{
|
|
|
|
struct object_id oid_A, oid_B;
|
2018-07-20 16:33:17 +00:00
|
|
|
struct commit *A, *B;
|
2018-07-20 16:33:23 +00:00
|
|
|
struct commit_list *X, *Y;
|
2018-09-21 15:05:26 +00:00
|
|
|
struct object_array X_obj = OBJECT_ARRAY_INIT;
|
2018-11-02 13:14:47 +00:00
|
|
|
struct commit **X_array, **Y_array;
|
|
|
|
int X_nr, X_alloc, Y_nr, Y_alloc;
|
2018-07-20 16:33:15 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct repository *r = the_repository;
|
|
|
|
|
|
|
|
setup_git_directory();
|
|
|
|
|
|
|
|
if (ac < 2)
|
|
|
|
exit(1);
|
|
|
|
|
2018-07-20 16:33:17 +00:00
|
|
|
A = B = NULL;
|
2018-07-20 16:33:23 +00:00
|
|
|
X = Y = NULL;
|
2018-11-02 13:14:47 +00:00
|
|
|
X_nr = Y_nr = 0;
|
|
|
|
X_alloc = Y_alloc = 16;
|
2018-07-20 16:33:20 +00:00
|
|
|
ALLOC_ARRAY(X_array, X_alloc);
|
2018-11-02 13:14:47 +00:00
|
|
|
ALLOC_ARRAY(Y_array, Y_alloc);
|
2018-07-20 16:33:15 +00:00
|
|
|
|
|
|
|
while (strbuf_getline(&buf, stdin) != EOF) {
|
|
|
|
struct object_id oid;
|
2018-09-21 15:05:26 +00:00
|
|
|
struct object *orig;
|
|
|
|
struct object *peeled;
|
2018-07-20 16:33:15 +00:00
|
|
|
struct commit *c;
|
|
|
|
if (buf.len < 3)
|
|
|
|
continue;
|
|
|
|
|
2023-03-28 13:58:46 +00:00
|
|
|
if (repo_get_oid_committish(the_repository, buf.buf + 2, &oid))
|
2018-07-20 16:33:15 +00:00
|
|
|
die("failed to resolve %s", buf.buf + 2);
|
|
|
|
|
2018-09-21 15:05:26 +00:00
|
|
|
orig = parse_object(r, &oid);
|
|
|
|
peeled = deref_tag_noverify(orig);
|
2018-07-20 16:33:15 +00:00
|
|
|
|
2018-09-21 15:05:26 +00:00
|
|
|
if (!peeled)
|
2018-07-20 16:33:15 +00:00
|
|
|
die("failed to load commit for input %s resulting in oid %s\n",
|
|
|
|
buf.buf, oid_to_hex(&oid));
|
|
|
|
|
2020-06-17 09:14:08 +00:00
|
|
|
c = object_as_type(peeled, OBJ_COMMIT, 0);
|
2018-07-20 16:33:15 +00:00
|
|
|
|
|
|
|
if (!c)
|
|
|
|
die("failed to load commit for input %s resulting in oid %s\n",
|
|
|
|
buf.buf, oid_to_hex(&oid));
|
|
|
|
|
|
|
|
switch (buf.buf[0]) {
|
|
|
|
case 'A':
|
|
|
|
oidcpy(&oid_A, &oid);
|
2018-07-20 16:33:17 +00:00
|
|
|
A = c;
|
2018-07-20 16:33:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'B':
|
|
|
|
oidcpy(&oid_B, &oid);
|
2018-07-20 16:33:17 +00:00
|
|
|
B = c;
|
2018-07-20 16:33:15 +00:00
|
|
|
break;
|
|
|
|
|
2018-07-20 16:33:18 +00:00
|
|
|
case 'X':
|
|
|
|
commit_list_insert(c, &X);
|
2018-07-20 16:33:20 +00:00
|
|
|
ALLOC_GROW(X_array, X_nr + 1, X_alloc);
|
|
|
|
X_array[X_nr++] = c;
|
2018-09-21 15:05:26 +00:00
|
|
|
add_object_array(orig, NULL, &X_obj);
|
2018-07-20 16:33:18 +00:00
|
|
|
break;
|
|
|
|
|
2018-07-20 16:33:23 +00:00
|
|
|
case 'Y':
|
|
|
|
commit_list_insert(c, &Y);
|
2018-11-02 13:14:47 +00:00
|
|
|
ALLOC_GROW(Y_array, Y_nr + 1, Y_alloc);
|
|
|
|
Y_array[Y_nr++] = c;
|
2018-07-20 16:33:23 +00:00
|
|
|
break;
|
|
|
|
|
2018-07-20 16:33:15 +00:00
|
|
|
default:
|
|
|
|
die("unexpected start of line: %c", buf.buf[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
if (!strcmp(av[1], "ref_newer"))
|
|
|
|
printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
|
2018-07-20 16:33:17 +00:00
|
|
|
else if (!strcmp(av[1], "in_merge_bases"))
|
2023-03-28 13:58:47 +00:00
|
|
|
printf("%s(A,B):%d\n", av[1],
|
|
|
|
repo_in_merge_bases(the_repository, A, B));
|
commit-reach: fix in_merge_bases_many bug
Way back in f9b8908b (commit.c: use generation numbers for
in_merge_bases(), 2018-05-01), a heuristic was used to short-circuit
the in_merge_bases() walk. This works just fine as long as the
caller is checking only two commits, but when there are multiple,
there is a possibility that this heuristic is _very wrong_.
Some code moves since then has changed this method to
repo_in_merge_bases_many() inside commit-reach.c. The heuristic
computes the minimum generation number of the "reference" list, then
compares this number to the generation number of the "commit".
In a recent topic, a test was added that used in_merge_bases_many()
to test if a commit was reachable from a number of commits pulled
from a reflog. However, this highlighted the problem: if any of the
reference commits have a smaller generation number than the given
commit, then the walk is skipped _even if there exist some with
higher generation number_.
This heuristic is wrong! It must check the MAXIMUM generation number
of the reference commits, not the MINIMUM.
This highlights a testing gap. t6600-test-reach.sh covers many
methods in commit-reach.c, including in_merge_bases() and
get_merge_bases_many(), but since these methods either restrict to
two input commits or actually look for the full list of merge bases,
they don't check this heuristic!
Add a possible input to "test-tool reach" that tests
in_merge_bases_many() and add tests to t6600-test-reach.sh that
cover this heuristic. This includes cases for the reference commits
having generation above and below the generation of the input commit,
but also having maximum generation below the generation of the input
commit.
The fix itself is to swap min_generation with a max_generation in
repo_in_merge_bases_many().
Reported-by: Srinidhi Kaushik <shrinidhi.kaushik@gmail.com>
Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-10-02 14:58:56 +00:00
|
|
|
else if (!strcmp(av[1], "in_merge_bases_many"))
|
2023-03-28 13:58:47 +00:00
|
|
|
printf("%s(A,X):%d\n", av[1],
|
commit-reach(repo_in_merge_bases_many): optionally expect missing commits
Currently this function treats unrelated commit histories the same way
as commit histories with missing commit objects.
Typically, missing commit objects constitute a corrupt repository,
though, and should be reported as such. The next commits will make it
so, but there is one exception: In `git fetch --update-shallow` we
_expect_ commit objects to be missing, and we do want to treat the
now-incomplete commit histories as unrelated.
To allow for that, let's introduce an additional parameter that is
passed to `repo_in_merge_bases_many()` to trigger this behavior, and use
it in the two callers in `shallow.c`.
This commit changes behavior slightly: unless called from the
`shallow.c` functions that set the `ignore_missing_commits` bit, any
non-existing tip commit that is passed to `repo_in_merge_bases_many()`
will now result in an error.
Note: When encountering missing commits while traversing the commit
history in search for merge bases, with this commit there won't be a
change in behavior just yet, their children will still be interpreted as
root commits. This bug will get fixed by follow-up commits.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-28 09:44:08 +00:00
|
|
|
repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
|
2018-07-20 16:33:18 +00:00
|
|
|
else if (!strcmp(av[1], "is_descendant_of"))
|
2020-06-23 18:42:22 +00:00
|
|
|
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
|
2018-07-20 16:33:20 +00:00
|
|
|
else if (!strcmp(av[1], "get_merge_bases_many")) {
|
2024-02-28 09:44:16 +00:00
|
|
|
struct commit_list *list = NULL;
|
|
|
|
if (repo_get_merge_bases_many(the_repository,
|
|
|
|
A, X_nr,
|
|
|
|
X_array,
|
|
|
|
&list) < 0)
|
|
|
|
exit(128);
|
2018-07-20 16:33:20 +00:00
|
|
|
printf("%s(A,X):\n", av[1]);
|
|
|
|
print_sorted_commit_ids(list);
|
2018-07-20 16:33:22 +00:00
|
|
|
} else if (!strcmp(av[1], "reduce_heads")) {
|
|
|
|
struct commit_list *list = reduce_heads(X);
|
|
|
|
printf("%s(X):\n", av[1]);
|
|
|
|
print_sorted_commit_ids(list);
|
2018-07-20 16:33:23 +00:00
|
|
|
} else if (!strcmp(av[1], "can_all_from_reach")) {
|
|
|
|
printf("%s(X,Y):%d\n", av[1], can_all_from_reach(X, Y, 1));
|
2018-09-21 15:05:26 +00:00
|
|
|
} else if (!strcmp(av[1], "can_all_from_reach_with_flag")) {
|
|
|
|
struct commit_list *iter = Y;
|
|
|
|
|
|
|
|
while (iter) {
|
|
|
|
iter->item->object.flags |= 2;
|
|
|
|
iter = iter->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
|
2018-07-20 16:33:25 +00:00
|
|
|
} else if (!strcmp(av[1], "commit_contains")) {
|
2023-07-10 21:12:07 +00:00
|
|
|
struct ref_filter filter = REF_FILTER_INIT;
|
2018-07-20 16:33:25 +00:00
|
|
|
struct contains_cache cache;
|
|
|
|
init_contains_cache(&cache);
|
|
|
|
|
|
|
|
if (ac > 2 && !strcmp(av[2], "--tag"))
|
|
|
|
filter.with_commit_tag_algo = 1;
|
|
|
|
else
|
|
|
|
filter.with_commit_tag_algo = 0;
|
|
|
|
|
|
|
|
printf("%s(_,A,X,_):%d\n", av[1], commit_contains(&filter, A, X, &cache));
|
2018-11-02 13:14:47 +00:00
|
|
|
} else if (!strcmp(av[1], "get_reachable_subset")) {
|
|
|
|
const int reachable_flag = 1;
|
|
|
|
int i, count = 0;
|
|
|
|
struct commit_list *current;
|
|
|
|
struct commit_list *list = get_reachable_subset(X_array, X_nr,
|
|
|
|
Y_array, Y_nr,
|
|
|
|
reachable_flag);
|
|
|
|
printf("get_reachable_subset(X,Y)\n");
|
|
|
|
for (current = list; current; current = current->next) {
|
|
|
|
if (!(list->item->object.flags & reachable_flag))
|
|
|
|
die(_("commit %s is not marked reachable"),
|
|
|
|
oid_to_hex(&list->item->object.oid));
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
for (i = 0; i < Y_nr; i++) {
|
|
|
|
if (Y_array[i]->object.flags & reachable_flag)
|
|
|
|
count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
die(_("too many commits marked reachable"));
|
|
|
|
|
|
|
|
print_sorted_commit_ids(list);
|
2018-07-20 16:33:20 +00:00
|
|
|
}
|
2018-07-20 16:33:15 +00:00
|
|
|
|
2021-06-08 10:48:03 +00:00
|
|
|
return 0;
|
2018-07-20 16:33:15 +00:00
|
|
|
}
|