Commit 34a4ad46 by Vicent Martí

Merge pull request #1211 from arrbee/fix-icase-status-file

Fix case insensitivity issues in git_status_file
parents 4b45675d fffe429a
......@@ -209,6 +209,15 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
/**
* Compare two tree entries
*
* @param e1 first tree entry
* @param e2 second tree entry
* @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2
*/
GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
/**
* Convert a tree entry to the git_object it points too.
*
* You must call `git_object_free()` on the object when you are done with it.
......
......@@ -224,7 +224,7 @@ static int checkout_action_wd_only(
if (!git_pathspec_match_path(
pathspec, wd->path,
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
workdir->ignore_case))
git_iterator_ignore_case(workdir)))
return 0;
/* check if item is tracked in the index but not in the checkout diff */
......@@ -1130,7 +1130,7 @@ static int checkout_data_init(
if ((error = git_config_refresh(cfg)) < 0)
goto cleanup;
if (git_iterator_inner_type(target) == GIT_ITERATOR_INDEX) {
if (git_iterator_inner_type(target) == GIT_ITERATOR_TYPE_INDEX) {
/* if we are iterating over the index, don't reload */
data->index = git_iterator_index_get_index(target);
GIT_REFCOUNT_INC(data->index);
......@@ -1208,6 +1208,7 @@ int git_checkout_iterator(
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
uint32_t *actions = NULL;
size_t *counts = NULL;
git_iterator_flag_t iterflags = 0;
/* initialize structures and options */
error = checkout_data_init(&data, target, opts);
......@@ -1228,18 +1229,21 @@ int git_checkout_iterator(
diff_opts.pathspec = data.opts.paths;
/* set up iterators */
iterflags = git_iterator_ignore_case(target) ?
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_workdir_range(
&workdir, data.repo, data.pfx, data.pfx)) < 0 ||
&workdir, data.repo, iterflags, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_tree_range(
&baseline, data.opts.baseline, data.pfx, data.pfx)) < 0)
&baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
goto cleanup;
/* Handle case insensitivity for baseline if necessary */
if (workdir->ignore_case && !baseline->ignore_case) {
if (git_iterator_ignore_case(workdir) != git_iterator_ignore_case(baseline))
if ((error = git_iterator_spoolandsort_push(baseline, true)) < 0)
goto cleanup;
}
/* Generate baseline-to-target diff which will include an entry for
* every possible update that might need to be made.
......
......@@ -418,7 +418,7 @@ static int maybe_modified(
git_delta_t status = GIT_DELTA_MODIFIED;
unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode;
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR);
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
GIT_UNUSED(old_iter);
......@@ -556,7 +556,9 @@ static int diff_list_init_from_iterators(
/* Use case-insensitive compare if either iterator has
* the ignore_case bit set */
if (!old_iter->ignore_case && !new_iter->ignore_case) {
if (!git_iterator_ignore_case(old_iter) &&
!git_iterator_ignore_case(new_iter))
{
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
diff->strcomp = git__strcmp;
......@@ -714,7 +716,7 @@ int git_diff__from_iterators(
else if (git_iterator_current_is_ignored(new_iter))
delta_type = GIT_DELTA_IGNORED;
else if (new_iter->type != GIT_ITERATOR_WORKDIR)
else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED;
if (diff_delta__from_one(diff, delta_type, nitem) < 0)
......@@ -786,8 +788,8 @@ int git_diff_tree_to_tree(
assert(diff && repo);
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, pfx, pfx),
git_iterator_for_tree_range(&b, new_tree, pfx, pfx)
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_tree_range(&b, new_tree, 0, pfx, pfx)
);
return error;
......@@ -808,8 +810,8 @@ int git_diff_tree_to_index(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, pfx, pfx),
git_iterator_for_index_range(&b, index, pfx, pfx)
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_index_range(&b, index, 0, pfx, pfx)
);
return error;
......@@ -829,8 +831,8 @@ int git_diff_index_to_workdir(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_index_range(&a, index, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, pfx, pfx)
git_iterator_for_index_range(&a, index, 0, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx)
);
return error;
......@@ -848,8 +850,8 @@ int git_diff_tree_to_workdir(
assert(diff && repo);
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, pfx, pfx)
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx)
);
return error;
......
......@@ -495,7 +495,7 @@ static void diff_patch_init(
patch->old_src = patch->diff->old_src;
patch->new_src = patch->diff->new_src;
} else {
patch->old_src = patch->new_src = GIT_ITERATOR_TREE;
patch->old_src = patch->new_src = GIT_ITERATOR_TYPE_TREE;
}
}
......@@ -578,7 +578,7 @@ static int diff_patch_load(
*/
if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
patch->old_src == GIT_ITERATOR_WORKDIR) {
patch->old_src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = get_workdir_content(
ctxt, delta, &delta->old_file, &patch->old_data)) < 0)
goto cleanup;
......@@ -587,7 +587,7 @@ static int diff_patch_load(
}
if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
patch->new_src == GIT_ITERATOR_WORKDIR) {
patch->new_src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = get_workdir_content(
ctxt, delta, &delta->new_file, &patch->new_data)) < 0)
goto cleanup;
......@@ -596,7 +596,7 @@ static int diff_patch_load(
}
if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
patch->old_src != GIT_ITERATOR_WORKDIR) {
patch->old_src != GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = get_blob_content(
ctxt, delta, &delta->old_file,
&patch->old_data, &patch->old_blob)) < 0)
......@@ -606,7 +606,7 @@ static int diff_patch_load(
}
if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
patch->new_src != GIT_ITERATOR_WORKDIR) {
patch->new_src != GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = get_blob_content(
ctxt, delta, &delta->new_file,
&patch->new_data, &patch->new_blob)) < 0)
......@@ -1666,32 +1666,28 @@ int git_diff__paired_foreach(
int cmp;
git_diff_delta *i2h, *w2i;
size_t i, j, i_max, j_max;
bool icase = false;
int (*strcomp)(const char *, const char *);
i_max = idx2head ? idx2head->deltas.length : 0;
j_max = wd2idx ? wd2idx->deltas.length : 0;
if (idx2head && wd2idx &&
(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
{
/* Then use the ignore-case sorter... */
icase = true;
/* and assert that both are ignore-case sorted. If this function
* ever needs to support merge joining result sets that are not sorted
* by the same function, then it will need to be extended to do a spool
* and sort on one of the results before merge joining */
assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
}
/* Get appropriate strcmp function */
strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
/* Assert both iterators use matching ignore-case. If this function ever
* supports merging diffs that are not sorted by the same function, then
* it will need to spool and sort on one of the results before merging
*/
if (idx2head && wd2idx) {
assert(idx2head->strcomp == wd2idx->strcomp);
}
for (i = 0, j = 0; i < i_max || j < j_max; ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 :
STRCMP_CASESELECT(icase, i2h->old_file.path, w2i->old_file.path);
strcomp(i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) {
if (cb(i2h, NULL, payload))
......
......@@ -12,20 +12,26 @@
#include "vector.h"
#include "buffer.h"
#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) (((ITER).ignore_case) ? \
#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) \
(((ITER).flags & GIT_ITERATOR_IGNORE_CASE) != 0 ? \
git__prefixcmp_icase((STR), (PREFIX)) : \
git__prefixcmp((STR), (PREFIX)))
typedef struct git_iterator git_iterator;
typedef enum {
GIT_ITERATOR_EMPTY = 0,
GIT_ITERATOR_TREE = 1,
GIT_ITERATOR_INDEX = 2,
GIT_ITERATOR_WORKDIR = 3,
GIT_ITERATOR_SPOOLANDSORT = 4
GIT_ITERATOR_TYPE_EMPTY = 0,
GIT_ITERATOR_TYPE_TREE = 1,
GIT_ITERATOR_TYPE_INDEX = 2,
GIT_ITERATOR_TYPE_WORKDIR = 3,
GIT_ITERATOR_TYPE_SPOOLANDSORT = 4
} git_iterator_type_t;
typedef enum {
GIT_ITERATOR_IGNORE_CASE = (1 << 0), /* ignore_case */
GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1), /* force ignore_case off */
} git_iterator_flag_t;
typedef struct {
int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *);
......@@ -41,50 +47,60 @@ struct git_iterator {
git_repository *repo;
char *start;
char *end;
bool ignore_case;
int (*prefixcomp)(const char *str, const char *prefix);
unsigned int flags;
};
extern int git_iterator_for_nothing(git_iterator **iter);
extern int git_iterator_for_nothing(
git_iterator **out, git_iterator_flag_t flags);
/* tree iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
*/
extern int git_iterator_for_tree_range(
git_iterator **iter, git_tree *tree,
const char *start, const char *end);
git_iterator **out,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_tree(
git_iterator **iter, git_tree *tree)
GIT_INLINE(int) git_iterator_for_tree(git_iterator **out, git_tree *tree)
{
return git_iterator_for_tree_range(iter, tree, NULL, NULL);
return git_iterator_for_tree_range(out, tree, 0, NULL, NULL);
}
/* index iterators will take the ignore_case value from the index; the
* ignore_case flags are not used
*/
extern int git_iterator_for_index_range(
git_iterator **iter, git_index *index, const char *start, const char *end);
GIT_INLINE(int) git_iterator_for_index(
git_iterator **iter, git_index *index)
{
return git_iterator_for_index_range(iter, index, NULL, NULL);
}
extern int git_iterator_for_repo_index_range(
git_iterator **iter, git_repository *repo,
const char *start, const char *end);
git_iterator **out,
git_index *index,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_repo_index(
git_iterator **iter, git_repository *repo)
GIT_INLINE(int) git_iterator_for_index(git_iterator **out, git_index *index)
{
return git_iterator_for_repo_index_range(iter, repo, NULL, NULL);
return git_iterator_for_index_range(out, index, 0, NULL, NULL);
}
/* workdir iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
*/
extern int git_iterator_for_workdir_range(
git_iterator **iter, git_repository *repo,
const char *start, const char *end);
git_iterator **out,
git_repository *repo,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_workdir(
git_iterator **iter, git_repository *repo)
GIT_INLINE(int) git_iterator_for_workdir(git_iterator **out, git_repository *repo)
{
return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
return git_iterator_for_workdir_range(out, repo, 0, NULL, NULL);
}
extern void git_iterator_free(git_iterator *iter);
/* Spool all iterator values, resort with alternative ignore_case value
* and replace callbacks with spoolandsort alternates.
*/
......@@ -130,21 +146,6 @@ GIT_INLINE(int) git_iterator_reset(
return iter->cb->reset(iter, start, end);
}
GIT_INLINE(void) git_iterator_free(git_iterator *iter)
{
if (iter == NULL)
return;
iter->cb->free(iter);
git__free(iter->start);
git__free(iter->end);
memset(iter, 0, sizeof(*iter));
git__free(iter);
}
GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
{
return iter->type;
......@@ -155,6 +156,16 @@ GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter)
return iter->repo;
}
GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter)
{
return iter->flags;
}
GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter)
{
return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
}
extern int git_iterator_current_tree_entry(
git_iterator *iter, const git_tree_entry **tree_entry);
......
......@@ -701,6 +701,30 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
int git_path_icmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2)
{
unsigned char c1, c2;
size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = strncasecmp(name1, name2, len);
if (cmp)
return cmp;
c1 = name1[len];
c2 = name2[len];
if (c1 == '\0' && isdir1)
c1 = '/';
if (c2 == '\0' && isdir2)
c2 = '/';
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
int git_path_direach(
git_buf *path,
int (*fn)(void *, git_buf *),
......
......@@ -261,12 +261,17 @@ extern int git_path_direach(
void *state);
/**
* Sort function to order two paths.
* Sort function to order two paths
*/
extern int git_path_cmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2);
/** Path sort function that is case insensitive */
extern int git_path_icmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2);
/**
* Invoke callback up path directory by directory until the ceiling is
* reached (inclusive of a final call at the root_path).
......
......@@ -196,21 +196,24 @@ struct status_file_info {
char *expected;
unsigned int count;
unsigned int status;
int fnm_flags;
int ambiguous;
};
static int get_one_status(const char *path, unsigned int status, void *data)
{
struct status_file_info *sfi = data;
int (*strcomp)(const char *a, const char *b);
sfi->count++;
sfi->status = status;
strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp;
if (sfi->count > 1 ||
(strcmp(sfi->expected, path) != 0 &&
p_fnmatch(sfi->expected, path, 0) != 0)) {
giterr_set(GITERR_INVALID,
"Ambiguous path '%s' given to git_status_file", sfi->expected);
(strcomp(sfi->expected, path) != 0 &&
p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0))
{
sfi->ambiguous = true;
return GIT_EAMBIGUOUS;
}
......@@ -226,11 +229,17 @@ int git_status_file(
int error;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_file_info sfi = {0};
git_index *index;
assert(status_flags && repo && path);
if ((error = git_repository_index__weakptr(&index, repo)) < 0)
return error;
if ((sfi.expected = git__strdup(path)) == NULL)
return -1;
if (index->ignore_case)
sfi.fnm_flags = FNM_CASEFOLD;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
......@@ -242,8 +251,11 @@ int git_status_file(
error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
if (error < 0 && sfi.ambiguous)
if (error < 0 && sfi.ambiguous) {
giterr_set(GITERR_INVALID,
"Ambiguous path '%s' given to git_status_file", sfi.expected);
error = GIT_EAMBIGUOUS;
}
if (!error && !sfi.count) {
git_buf full = GIT_BUF_INIT;
......
......@@ -1130,10 +1130,12 @@ static int load_submodule_config_from_index(
git_repository *repo, git_oid *gitmodules_oid)
{
int error;
git_index *index;
git_iterator *i;
const git_index_entry *entry;
if ((error = git_iterator_for_repo_index(&i, repo)) < 0)
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
(error = git_iterator_for_index(&i, index)) < 0)
return error;
error = git_iterator_current(i, &entry);
......
......@@ -55,14 +55,23 @@ static int valid_entry_name(const char *filename)
strcmp(filename, DOT_GIT) != 0));
}
static int entry_sort_cmp(const void *a, const void *b)
int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
{
const git_tree_entry *entry_a = (const git_tree_entry *)(a);
const git_tree_entry *entry_b = (const git_tree_entry *)(b);
return git_path_cmp(
entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a),
entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b));
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2));
}
int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
{
return git_path_icmp(
e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
e2->filename, e2->filename_len, git_tree_entry__is_tree(e2));
}
static int entry_sort_cmp(const void *a, const void *b)
{
return git_tree_entry_cmp((const git_tree_entry *)a, (const git_tree_entry *)b);
}
static git_tree_entry *alloc_entry(const char *filename)
......
......@@ -39,6 +39,8 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
}
extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2);
void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree, git_odb_object *obj);
......
......@@ -23,9 +23,8 @@
# define MIN(x,y) (((x) < (y) ? (x) : (y)))
#endif
typedef int (*cmp_ptr_t)(const void *, const void *);
static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
static int binsearch(
void **dst, const void *x, size_t size, git__tsort_r_cmp cmp, void *payload)
{
int l, c, r;
void *lx, *cx;
......@@ -38,12 +37,12 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
lx = dst[l];
/* check for beginning conditions */
if (cmp(x, lx) < 0)
if (cmp(x, lx, payload) < 0)
return 0;
else if (cmp(x, lx) == 0) {
else if (cmp(x, lx, payload) == 0) {
int i = 1;
while (cmp(x, dst[i]) == 0)
while (cmp(x, dst[i], payload) == 0)
i++;
return i;
}
......@@ -51,7 +50,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
/* guaranteed not to be >= rx */
cx = dst[c];
while (1) {
const int val = cmp(x, cx);
const int val = cmp(x, cx, payload);
if (val < 0) {
if (c - l <= 1) return c;
r = c;
......@@ -62,7 +61,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
} else {
do {
cx = dst[++c];
} while (cmp(x, cx) == 0);
} while (cmp(x, cx, payload) == 0);
return c;
}
c = l + ((r - l) >> 1);
......@@ -71,7 +70,8 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
}
/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
static void bisort(
void **dst, size_t start, size_t size, git__tsort_r_cmp cmp, void *payload)
{
size_t i;
void *x;
......@@ -80,12 +80,12 @@ static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
for (i = start; i < size; i++) {
int j;
/* If this entry is already correct, just move along */
if (cmp(dst[i - 1], dst[i]) <= 0)
if (cmp(dst[i - 1], dst[i], payload) <= 0)
continue;
/* Else we need to find the right place, shift everything over, and squeeze in */
x = dst[i];
location = binsearch(dst, x, i, cmp);
location = binsearch(dst, x, i, cmp, payload);
for (j = (int)i - 1; j >= location; j--) {
dst[j + 1] = dst[j];
}
......@@ -102,7 +102,8 @@ struct tsort_run {
struct tsort_store {
size_t alloc;
cmp_ptr_t cmp;
git__tsort_r_cmp cmp;
void *payload;
void **storage;
};
......@@ -118,7 +119,8 @@ static void reverse_elements(void **dst, ssize_t start, ssize_t end)
}
}
static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
static ssize_t count_run(
void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
{
ssize_t curr = start + 2;
......@@ -126,7 +128,7 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s
return 1;
if (start >= size - 2) {
if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {
if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) {
void *tmp = dst[size - 1];
dst[size - 1] = dst[size - 2];
dst[size - 2] = tmp;
......@@ -135,13 +137,15 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s
return 2;
}
if (store->cmp(dst[start], dst[start + 1]) <= 0) {
while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)
if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) {
while (curr < size - 1 &&
store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0)
curr++;
return curr - start;
} else {
while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)
while (curr < size - 1 &&
store->cmp(dst[curr - 1], dst[curr], store->payload) > 0)
curr++;
/* reverse in-place */
......@@ -219,7 +223,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr,
for (k = curr; k < curr + A + B; k++) {
if ((i < A) && (j < curr + A + B)) {
if (store->cmp(storage[i], dst[j]) <= 0)
if (store->cmp(storage[i], dst[j], store->payload) <= 0)
dst[k] = storage[i++];
else
dst[k] = dst[j++];
......@@ -235,7 +239,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr,
for (k = curr + A + B - 1; k >= curr; k--) {
if ((i >= 0) && (j >= curr)) {
if (store->cmp(dst[j], storage[i]) > 0)
if (store->cmp(dst[j], storage[i], store->payload) > 0)
dst[k] = dst[j--];
else
dst[k] = storage[i--];
......@@ -307,7 +311,7 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
if (run < minrun) run = minrun;\
if (run > (ssize_t)size - curr) run = size - curr;\
if (run > len) {\
bisort(&dst[curr], len, run, cmp);\
bisort(&dst[curr], len, run, cmp, payload);\
len = run;\
}\
run_stack[stack_curr].start = curr;\
......@@ -329,7 +333,8 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
}\
while (0)
void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
void git__tsort_r(
void **dst, size_t size, git__tsort_r_cmp cmp, void *payload)
{
struct tsort_store _store, *store = &_store;
struct tsort_run run_stack[128];
......@@ -340,7 +345,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
ssize_t minrun;
if (size < 64) {
bisort(dst, 1, size, cmp);
bisort(dst, 1, size, cmp, payload);
return;
}
......@@ -351,6 +356,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
store->alloc = 0;
store->storage = NULL;
store->cmp = cmp;
store->payload = payload;
PUSH_NEXT();
PUSH_NEXT();
......@@ -365,3 +371,13 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
PUSH_NEXT();
}
}
static int tsort_r_cmp(const void *a, const void *b, void *payload)
{
return ((git__tsort_cmp)payload)(a, b);
}
void git__tsort(void **dst, size_t size, git__tsort_cmp cmp)
{
git__tsort_r(dst, size, tsort_r_cmp, cmp);
}
......@@ -462,7 +462,7 @@ uint32_t git__hash(const void *key, int len, uint32_t seed)
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
*/
int git__bsearch(
void **array,
size_t array_len,
......@@ -493,6 +493,37 @@ int git__bsearch(
return (cmp == 0) ? 0 : -1;
}
int git__bsearch_r(
void **array,
size_t array_len,
const void *key,
int (*compare_r)(const void *, const void *, void *),
void *payload,
size_t *position)
{
unsigned int lim;
int cmp = -1;
void **part, **base = array;
for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
part = base + (lim >> 1);
cmp = (*compare_r)(key, *part, payload);
if (cmp == 0) {
base = part;
break;
}
if (cmp > 0) { /* key > p; take right partition */
base = part + 1;
lim--;
} /* else take left partition */
}
if (position)
*position = (base - array);
return (cmp == 0) ? 0 : -1;
}
/**
* A strcmp wrapper
*
......
......@@ -119,7 +119,15 @@ GIT_INLINE(const char *) git__next_line(const char *s)
return s;
}
extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
typedef int (*git__tsort_cmp)(const void *a, const void *b);
extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
typedef int (*git__tsort_r_cmp)(const void *a, const void *b, void *payload);
extern void git__tsort_r(
void **dst, size_t size, git__tsort_r_cmp cmp, void *payload);
/**
* @param position If non-NULL, this will be set to the position where the
......@@ -130,7 +138,15 @@ extern int git__bsearch(
void **array,
size_t array_len,
const void *key,
int (*compare)(const void *, const void *),
int (*compare)(const void *key, const void *element),
size_t *position);
extern int git__bsearch_r(
void **array,
size_t array_len,
const void *key,
int (*compare_r)(const void *key, const void *element, void *payload),
void *payload,
size_t *position);
extern int git__strcmp_cb(const void *a, const void *b);
......
......@@ -35,7 +35,8 @@ static void tree_iterator_test(
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
cl_git_pass(git_iterator_for_tree_range(&i, t, start, end));
cl_git_pass(git_iterator_for_tree_range(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end));
/* test loop */
cl_git_pass(git_iterator_current(i, &entry));
......@@ -304,7 +305,8 @@ void test_diff_iterator__tree_special_functions(void)
repo, "24fa9a9fc4e202313e24b648087495441dab432b");
cl_assert(t != NULL);
cl_git_pass(git_iterator_for_tree_range(&i, t, NULL, NULL));
cl_git_pass(git_iterator_for_tree_range(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
......@@ -355,12 +357,14 @@ static void index_iterator_test(
const char **expected_names,
const char **expected_oids)
{
git_index *index;
git_iterator *i;
const git_index_entry *entry;
int count = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_iterator_for_repo_index_range(&i, repo, start, end));
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_index_range(&i, index, 0, start, end));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
......@@ -378,6 +382,7 @@ static void index_iterator_test(
}
git_iterator_free(i);
git_index_free(index);
cl_assert_equal_i(expected_count, count);
}
......@@ -533,7 +538,7 @@ static void workdir_iterator_test(
int count = 0, count_all = 0, count_all_post_reset = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
cl_git_pass(git_iterator_for_workdir_range(&i, repo, 0, start, end));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
......@@ -731,7 +736,7 @@ void test_diff_iterator__workdir_builtin_ignores(void)
cl_git_mkfile("attr/sub/.git", "whatever");
cl_git_pass(
git_iterator_for_workdir_range(&i, repo, "dir", "sub/sub/file"));
git_iterator_for_workdir_range(&i, repo, 0, "dir", "sub/sub/file"));
cl_git_pass(git_iterator_current(i, &entry));
for (idx = 0; entry != NULL; ++idx) {
......@@ -750,3 +755,155 @@ void test_diff_iterator__workdir_builtin_ignores(void)
git_iterator_free(i);
}
static void check_wd_first_through_third_range(
git_repository *repo, const char *start, const char *end)
{
git_iterator *i;
const git_index_entry *entry;
int idx;
static const char *expected[] = { "FIRST", "second", "THIRD", NULL };
cl_git_pass(git_iterator_for_workdir_range(
&i, repo, GIT_ITERATOR_IGNORE_CASE, start, end));
cl_git_pass(git_iterator_current(i, &entry));
for (idx = 0; entry != NULL; ++idx) {
cl_assert_equal_s(expected[idx], entry->path);
if (S_ISDIR(entry->mode))
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
else
cl_git_pass(git_iterator_advance(i, &entry));
}
cl_assert(expected[idx] == NULL);
git_iterator_free(i);
}
void test_diff_iterator__workdir_handles_icase_range(void)
{
git_repository *repo;
repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
cl_git_mkfile("empty_standard_repo/before", "whatever\n");
cl_git_mkfile("empty_standard_repo/FIRST", "whatever\n");
cl_git_mkfile("empty_standard_repo/second", "whatever\n");
cl_git_mkfile("empty_standard_repo/THIRD", "whatever\n");
cl_git_mkfile("empty_standard_repo/zafter", "whatever\n");
cl_git_mkfile("empty_standard_repo/Zlast", "whatever\n");
check_wd_first_through_third_range(repo, "first", "third");
check_wd_first_through_third_range(repo, "FIRST", "THIRD");
check_wd_first_through_third_range(repo, "first", "THIRD");
check_wd_first_through_third_range(repo, "FIRST", "third");
check_wd_first_through_third_range(repo, "FirSt", "tHiRd");
}
static void check_tree_range(
git_repository *repo,
const char *start,
const char *end,
bool ignore_case,
int expected_count)
{
git_tree *head;
git_iterator *i;
const git_index_entry *entry;
int count;
cl_git_pass(git_repository_head_tree(&head, repo));
cl_git_pass(git_iterator_for_tree_range(
&i, head,
ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE,
start, end));
cl_git_pass(git_iterator_current(i, &entry));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(i, &entry));
}
cl_assert_equal_i(expected_count, count);
git_iterator_free(i);
git_tree_free(head);
}
void test_diff_iterator__tree_handles_icase_range(void)
{
git_repository *repo;
repo = cl_git_sandbox_init("testrepo");
check_tree_range(repo, "B", "C", false, 0);
check_tree_range(repo, "B", "C", true, 1);
check_tree_range(repo, "a", "z", false, 3);
check_tree_range(repo, "a", "z", true, 4);
}
static void check_index_range(
git_repository *repo,
const char *start,
const char *end,
bool ignore_case,
int expected_count)
{
git_index *index;
git_iterator *i;
const git_index_entry *entry;
int count, caps;
bool is_ignoring_case;
cl_git_pass(git_repository_index(&index, repo));
caps = git_index_caps(index);
is_ignoring_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0);
if (ignore_case != is_ignoring_case)
cl_git_pass(git_index_set_caps(index, caps ^ GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_iterator_for_index_range(&i, index, 0, start, end));
cl_assert(git_iterator_ignore_case(i) == ignore_case);
cl_git_pass(git_iterator_current(i, &entry));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(i, &entry));
}
cl_assert_equal_i(expected_count, count);
git_iterator_free(i);
git_index_free(index);
}
void test_diff_iterator__index_handles_icase_range(void)
{
git_repository *repo;
git_index *index;
git_tree *head;
repo = cl_git_sandbox_init("testrepo");
/* reset index to match HEAD */
cl_git_pass(git_repository_head_tree(&head, repo));
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_read_tree(index, head));
cl_git_pass(git_index_write(index));
git_tree_free(head);
git_index_free(index);
/* do some ranged iterator checks toggling case sensitivity */
check_index_range(repo, "B", "C", false, 0);
check_index_range(repo, "B", "C", true, 1);
check_index_range(repo, "a", "z", false, 3);
check_index_range(repo, "a", "z", true, 4);
}
......@@ -580,3 +580,90 @@ void test_status_worktree__conflicted_item(void)
git_index_free(index);
}
static void stage_and_commit(git_repository *repo, const char *path)
{
git_oid tree_oid, commit_oid;
git_tree *tree;
git_signature *signature;
git_index *index;
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_from_workdir(index, path));
cl_git_pass(git_index_write(index));
cl_git_pass(git_index_write_tree(&tree_oid, index));
git_index_free(index);
cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
cl_git_pass(git_commit_create_v(
&commit_oid,
repo,
"HEAD",
signature,
signature,
NULL,
"Initial commit\n\0",
tree,
0));
git_tree_free(tree);
git_signature_free(signature);
}
static void assert_ignore_case(
bool should_ignore_case,
int expected_lower_cased_file_status,
int expected_camel_cased_file_status)
{
git_config *config;
unsigned int status;
git_buf lower_case_path = GIT_BUF_INIT, camel_case_path = GIT_BUF_INIT;
git_repository *repo, *repo2;
repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_bool(config, "core.ignorecase", should_ignore_case));
git_config_free(config);
cl_git_pass(git_buf_joinpath(&lower_case_path,
git_repository_workdir(repo), "plop"));
cl_git_mkfile(git_buf_cstr(&lower_case_path), "");
stage_and_commit(repo, "plop");
cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo"));
cl_git_pass(git_status_file(&status, repo2, "plop"));
cl_assert_equal_i(GIT_STATUS_CURRENT, status);
cl_git_pass(git_buf_joinpath(&camel_case_path,
git_repository_workdir(repo), "Plop"));
cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path)));
cl_git_pass(git_status_file(&status, repo2, "plop"));
cl_assert_equal_i(expected_lower_cased_file_status, status);
cl_git_pass(git_status_file(&status, repo2, "Plop"));
cl_assert_equal_i(expected_camel_cased_file_status, status);
git_repository_free(repo2);
git_buf_free(&lower_case_path);
git_buf_free(&camel_case_path);
}
void test_status_worktree__file_status_honors_core_ignorecase_true(void)
{
assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT);
}
void test_status_worktree__file_status_honors_core_ignorecase_false(void)
{
assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment