mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
Add revwalk filter API
This works similarly to the hide API. It allows the caller to specify a callback to be invoked at optimal times during the revwalk process in order to give the caller control over which commits to filter from iteration. This is different from the hide API in that only the commit in question will be potentially filtered.
This commit is contained in:
@@ -290,6 +290,32 @@ GIT_EXTERN(int) git_revwalk_add_hide_cb(
|
||||
git_revwalk_hide_cb hide_cb,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* This is a callback function that user can provide to filter a
|
||||
* commit from iteration. If the callback function returns non-zero value,
|
||||
* then this commit will be skipped during the revwalk and thus won't be
|
||||
* returned by git_revwalk_next.
|
||||
*
|
||||
* @param commit_id oid of the Commit in question
|
||||
* @param payload User-specified pointer to data to be passed as data payload
|
||||
* @return a boolean value of 1 or 0 if the commit should be filtered
|
||||
*/
|
||||
typedef int(*git_revwalk_filter_cb)(
|
||||
const git_oid *commit_id,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Adds a callback function to filter a commit from revwalk iteration.
|
||||
*
|
||||
* @param walk the revision walker
|
||||
* @param filter_cb callback function to filter a commit from the revwalk
|
||||
* @param payload data payload to be passed to callback function
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_add_filter_cb(
|
||||
git_revwalk *walk,
|
||||
git_revwalk_filter_cb filter_cb,
|
||||
void *payload);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
||||
@@ -232,6 +232,9 @@ static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk
|
||||
git_commit_list_node *next;
|
||||
|
||||
while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
|
||||
if (walk->filter_cb && walk->filter_cb(&next->oid, walk->filter_cb_payload))
|
||||
continue;
|
||||
|
||||
/* Some commits might become uninteresting after being added to the list */
|
||||
if (!next->uninteresting) {
|
||||
*object_out = next;
|
||||
@@ -248,6 +251,9 @@ static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk
|
||||
git_commit_list_node *next;
|
||||
|
||||
while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) {
|
||||
if (walk->filter_cb && walk->filter_cb(&next->oid, walk->filter_cb_payload))
|
||||
continue;
|
||||
|
||||
/* Some commits might become uninteresting after being added to the list */
|
||||
if (!next->uninteresting) {
|
||||
*object_out = next;
|
||||
@@ -524,6 +530,9 @@ static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, g
|
||||
/* All the children of 'item' have been emitted (since we got to it via the priority queue) */
|
||||
next->in_degree = 0;
|
||||
|
||||
if (walk->filter_cb && walk->filter_cb(&next->oid, walk->filter_cb_payload))
|
||||
continue;
|
||||
|
||||
pptr = &git_commit_list_insert(next, pptr)->next;
|
||||
}
|
||||
|
||||
@@ -744,3 +753,24 @@ int git_revwalk_add_hide_cb(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_revwalk_add_filter_cb(
|
||||
git_revwalk *walk,
|
||||
git_revwalk_filter_cb filter_cb,
|
||||
void *payload)
|
||||
{
|
||||
assert(walk);
|
||||
|
||||
if (walk->walking)
|
||||
git_revwalk_reset(walk);
|
||||
|
||||
if (walk->filter_cb) {
|
||||
/* There is already a callback added */
|
||||
giterr_set(GITERR_INVALID, "there is already a callback added to filter commits in revwalk");
|
||||
return -1;
|
||||
}
|
||||
|
||||
walk->filter_cb = filter_cb;
|
||||
walk->filter_cb_payload = payload;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ struct git_revwalk {
|
||||
/* hide callback */
|
||||
git_revwalk_hide_cb hide_cb;
|
||||
void *hide_cb_payload;
|
||||
|
||||
/* filter callback */
|
||||
git_revwalk_filter_cb filter_cb;
|
||||
void *filter_cb_payload;
|
||||
};
|
||||
|
||||
git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid);
|
||||
|
||||
304
tests/revwalk/filtercb.c
Normal file
304
tests/revwalk/filtercb.c
Normal file
@@ -0,0 +1,304 @@
|
||||
#include "clar_libgit2.h"
|
||||
/*
|
||||
* a4a7dce [0] Merge branch 'master' into br2
|
||||
|\
|
||||
| * 9fd738e [1] a fourth commit
|
||||
| * 4a202b3 [2] a third commit
|
||||
* | c47800c [3] branch commit one
|
||||
|/
|
||||
* 5b5b025 [5] another commit
|
||||
* 8496071 [4] testing
|
||||
*/
|
||||
static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
|
||||
|
||||
static const char *commit_strs[] = {
|
||||
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
|
||||
"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
|
||||
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
|
||||
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
|
||||
"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
|
||||
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
|
||||
};
|
||||
|
||||
#define commit_count 6
|
||||
|
||||
static git_oid commit_ids[commit_count];
|
||||
static git_oid _head_id;
|
||||
static git_repository *_repo;
|
||||
|
||||
|
||||
void test_revwalk_filtercb__initialize(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
|
||||
cl_git_pass(git_oid_fromstr(&_head_id, commit_head));
|
||||
|
||||
for (i = 0; i < commit_count; i++)
|
||||
cl_git_pass(git_oid_fromstr(&commit_ids[i], commit_strs[i]));
|
||||
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__cleanup(void)
|
||||
{
|
||||
git_repository_free(_repo);
|
||||
_repo = NULL;
|
||||
}
|
||||
|
||||
/* Filter all commits */
|
||||
static int filter_every_commit_cb(const git_oid *commit_id, void *data)
|
||||
{
|
||||
GIT_UNUSED(commit_id);
|
||||
GIT_UNUSED(data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Do not filter anything */
|
||||
static int filter_none_cb(const git_oid *commit_id, void *data)
|
||||
{
|
||||
GIT_UNUSED(commit_id);
|
||||
GIT_UNUSED(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Filter some commits */
|
||||
static int filter_commit_cb(const git_oid *commit_id, void *data)
|
||||
{
|
||||
GIT_UNUSED(commit_id);
|
||||
GIT_UNUSED(data);
|
||||
|
||||
return (git_oid_cmp(commit_id, &commit_ids[5]) == 0);
|
||||
}
|
||||
|
||||
/* In payload data, pointer to a commit id is passed */
|
||||
static int filter_commit_use_payload_cb(const git_oid *commit_id, void *data)
|
||||
{
|
||||
git_oid *filter_commit_id = data;
|
||||
|
||||
return (git_oid_cmp(commit_id, filter_commit_id) == 0);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__filter_all_cb(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_every_commit_cb, NULL));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
|
||||
/* First call to git_revwalk_next should return GIT_ITEROVER */
|
||||
cl_assert_equal_i(GIT_ITEROVER, git_revwalk_next(&id, walk));
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
|
||||
void test_revwalk_filtercb__filter_none_cb(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
int i, error;
|
||||
git_oid id;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_none_cb, NULL));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
|
||||
/* It should return all 6 commits */
|
||||
i = 0;
|
||||
while ((error = git_revwalk_next(&id, walk)) == 0)
|
||||
i++;
|
||||
|
||||
cl_assert_equal_i(i, 6);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__add_filter_cb_multiple_times(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_every_commit_cb, NULL));
|
||||
cl_git_fail(git_revwalk_add_filter_cb(walk, filter_every_commit_cb, NULL));
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__add_filter_cb_during_walking(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
|
||||
/* Start walking without adding filter callback */
|
||||
cl_git_pass(git_revwalk_next(&id, walk));
|
||||
|
||||
/* Now add filter callback */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_none_cb, NULL));
|
||||
|
||||
/* walk should be reset */
|
||||
error = git_revwalk_next(&id, walk);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__filter_some_commits(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int i, error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
|
||||
|
||||
/* Add filter callback */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_commit_cb, NULL));
|
||||
|
||||
i = 0;
|
||||
while ((error = git_revwalk_next(&id, walk)) == 0) {
|
||||
cl_assert_equal_oid(&commit_ids[i], &id);
|
||||
i++;
|
||||
}
|
||||
|
||||
cl_assert_equal_i(i, 5);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__test_payload(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int i, error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
|
||||
|
||||
/* Add filter callback, pass id of parent of initial commit as payload data */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_commit_use_payload_cb, &commit_ids[5]));
|
||||
|
||||
i = 0;
|
||||
while ((error = git_revwalk_next(&id, walk)) == 0) {
|
||||
cl_assert_equal_oid(&commit_ids[i], &id);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* walker should return five commits */
|
||||
cl_assert_equal_i(i, 5);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__test_reverse_sort_with_payload(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE);
|
||||
|
||||
/* Add filter callback, pass id of parent of initial commit as payload data */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_commit_use_payload_cb, &commit_ids[5]));
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[4], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[3], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[2], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[1], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[0], &id);
|
||||
|
||||
error = git_revwalk_next(&id, walk);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__test_time_sort_with_payload(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
git_revwalk_sorting(walk, GIT_SORT_TIME);
|
||||
|
||||
/* Add filter callback, pass id of parent of initial commit as payload data */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_commit_use_payload_cb, &commit_ids[5]));
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[0], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[3], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[1], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[2], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[4], &id);
|
||||
|
||||
error = git_revwalk_next(&id, walk);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
|
||||
void test_revwalk_filtercb__test_time_sort_reverse_with_payload(void)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_oid id;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_revwalk_new(&walk, _repo));
|
||||
cl_git_pass(git_revwalk_push(walk, &_head_id));
|
||||
git_revwalk_sorting(walk, GIT_SORT_TIME | GIT_SORT_REVERSE);
|
||||
|
||||
/* Add filter callback, pass id of parent of initial commit as payload data */
|
||||
cl_git_pass(git_revwalk_add_filter_cb(walk, filter_commit_use_payload_cb, &commit_ids[5]));
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[4], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[2], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[1], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[3], &id);
|
||||
|
||||
cl_assert_equal_i(0, git_revwalk_next(&id, walk));
|
||||
cl_assert_equal_oid(&commit_ids[0], &id);
|
||||
|
||||
error = git_revwalk_next(&id, walk);
|
||||
cl_assert_equal_i(error, GIT_ITEROVER);
|
||||
|
||||
git_revwalk_free(walk);
|
||||
}
|
||||
Reference in New Issue
Block a user