Commit eada0762 by Russell Belfer

Merge pull request #939 from pwkelley/ignorecase

Support for the core.ignorecase flag
parents 8bc5cacc f08c60a5
...@@ -47,6 +47,7 @@ enum { ...@@ -47,6 +47,7 @@ enum {
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12)
}; };
/** /**
......
...@@ -376,6 +376,7 @@ int git_attr_cache__push_file( ...@@ -376,6 +376,7 @@ int git_attr_cache__push_file(
const char *filename, const char *filename,
git_attr_file_source source, git_attr_file_source source,
git_attr_file_parser parse, git_attr_file_parser parse,
void* parsedata,
git_vector *stack) git_vector *stack)
{ {
int error = 0; int error = 0;
...@@ -436,7 +437,7 @@ int git_attr_cache__push_file( ...@@ -436,7 +437,7 @@ int git_attr_cache__push_file(
goto finish; goto finish;
} }
if (parse && (error = parse(repo, content, file)) < 0) if (parse && (error = parse(repo, parsedata, content, file)) < 0)
goto finish; goto finish;
git_strmap_insert(cache->files, file->key, file, error); //-V595 git_strmap_insert(cache->files, file->key, file, error); //-V595
...@@ -468,7 +469,7 @@ finish: ...@@ -468,7 +469,7 @@ finish:
} }
#define push_attr_file(R,S,B,F) \ #define push_attr_file(R,S,B,F) \
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S)) git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S))
typedef struct { typedef struct {
git_repository *repo; git_repository *repo;
...@@ -517,7 +518,7 @@ static int push_one_attr(void *ref, git_buf *path) ...@@ -517,7 +518,7 @@ static int push_one_attr(void *ref, git_buf *path)
for (i = 0; !error && i < n_src; ++i) for (i = 0; !error && i < n_src; ++i)
error = git_attr_cache__push_file( error = git_attr_cache__push_file(
info->repo, path->ptr, GIT_ATTR_FILE, src[i], info->repo, path->ptr, GIT_ATTR_FILE, src[i],
git_attr_file__parse_buffer, info->files); git_attr_file__parse_buffer, NULL, info->files);
return error; return error;
} }
......
...@@ -25,7 +25,7 @@ typedef struct { ...@@ -25,7 +25,7 @@ typedef struct {
} git_attr_cache; } git_attr_cache;
typedef int (*git_attr_file_parser)( typedef int (*git_attr_file_parser)(
git_repository *, const char *, git_attr_file *); git_repository *, void *, const char *, git_attr_file *);
extern int git_attr_cache__init(git_repository *repo); extern int git_attr_cache__init(git_repository *repo);
...@@ -41,6 +41,7 @@ extern int git_attr_cache__push_file( ...@@ -41,6 +41,7 @@ extern int git_attr_cache__push_file(
const char *filename, const char *filename,
git_attr_file_source source, git_attr_file_source source,
git_attr_file_parser parse, git_attr_file_parser parse,
void *parsedata, /* passed through to parse function */
git_vector *stack); git_vector *stack);
extern int git_attr_cache__internal_file( extern int git_attr_cache__internal_file(
......
...@@ -53,13 +53,15 @@ fail: ...@@ -53,13 +53,15 @@ fail:
} }
int git_attr_file__parse_buffer( int git_attr_file__parse_buffer(
git_repository *repo, const char *buffer, git_attr_file *attrs) git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
{ {
int error = 0; int error = 0;
const char *scan = NULL; const char *scan = NULL;
char *context = NULL; char *context = NULL;
git_attr_rule *rule = NULL; git_attr_rule *rule = NULL;
GIT_UNUSED(parsedata);
assert(buffer && attrs); assert(buffer && attrs);
scan = buffer; scan = buffer;
...@@ -123,7 +125,7 @@ int git_attr_file__new_and_load( ...@@ -123,7 +125,7 @@ int git_attr_file__new_and_load(
if (!(error = git_futils_readbuffer(&content, path))) if (!(error = git_futils_readbuffer(&content, path)))
error = git_attr_file__parse_buffer( error = git_attr_file__parse_buffer(
NULL, git_buf_cstr(&content), *attrs_ptr); NULL, NULL, git_buf_cstr(&content), *attrs_ptr);
git_buf_free(&content); git_buf_free(&content);
...@@ -207,16 +209,17 @@ bool git_attr_fnmatch__match( ...@@ -207,16 +209,17 @@ bool git_attr_fnmatch__match(
const git_attr_path *path) const git_attr_path *path)
{ {
int fnm; int fnm;
int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0;
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir) if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
return false; return false;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME); fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags);
else if (path->is_dir) else if (path->is_dir)
fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR); fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags);
else else
fnm = p_fnmatch(match->pattern, path->basename, 0); fnm = p_fnmatch(match->pattern, path->basename, icase_flags);
return (fnm == FNM_NOMATCH) ? false : true; return (fnm == FNM_NOMATCH) ? false : true;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define GIT_ATTR_FNMATCH_IGNORE (1U << 4) #define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
#define GIT_ATTR_FNMATCH_HASWILD (1U << 5) #define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6) #define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
extern const char *git_attr__true; extern const char *git_attr__true;
extern const char *git_attr__false; extern const char *git_attr__false;
...@@ -96,7 +97,7 @@ extern void git_attr_file__free(git_attr_file *file); ...@@ -96,7 +97,7 @@ extern void git_attr_file__free(git_attr_file *file);
extern void git_attr_file__clear_rules(git_attr_file *file); extern void git_attr_file__clear_rules(git_attr_file *file);
extern int git_attr_file__parse_buffer( extern int git_attr_file__parse_buffer(
git_repository *repo, const char *buf, git_attr_file *file); git_repository *repo, void *parsedata, const char *buf, git_attr_file *file);
extern int git_attr_file__lookup_one( extern int git_attr_file__lookup_one(
git_attr_file *file, git_attr_file *file,
......
...@@ -584,6 +584,22 @@ static int maybe_modified( ...@@ -584,6 +584,22 @@ static int maybe_modified(
diff, status, oitem, omode, nitem, nmode, use_noid); diff, status, oitem, omode, nitem, nmode, use_noid);
} }
static int git_index_entry_cmp_case(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcmp(entry_a->path, entry_b->path);
}
static int git_index_entry_cmp_icase(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path);
}
static int diff_from_iterators( static int diff_from_iterators(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, /**< can be NULL for defaults */ const git_diff_options *opts, /**< can be NULL for defaults */
...@@ -594,12 +610,36 @@ static int diff_from_iterators( ...@@ -594,12 +610,36 @@ static int diff_from_iterators(
const git_index_entry *oitem, *nitem; const git_index_entry *oitem, *nitem;
git_buf ignore_prefix = GIT_BUF_INIT; git_buf ignore_prefix = GIT_BUF_INIT;
git_diff_list *diff = git_diff_list_alloc(repo, opts); git_diff_list *diff = git_diff_list_alloc(repo, opts);
git_vector_cmp entry_compare;
if (!diff) if (!diff)
goto fail; goto fail;
diff->old_src = old_iter->type; diff->old_src = old_iter->type;
diff->new_src = new_iter->type; diff->new_src = new_iter->type;
/* Use case-insensitive compare if either iterator has
* the ignore_case bit set */
if (!old_iter->ignore_case && !new_iter->ignore_case) {
entry_compare = git_index_entry_cmp_case;
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
} else {
entry_compare = git_index_entry_cmp_icase;
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
/* If one of the iterators doesn't have ignore_case set,
* then that's unfortunate because we'll have to spool
* its data, sort it icase, and then use that for our
* merge join to the other iterator that is icase sorted */
if (!old_iter->ignore_case) {
if (git_iterator_spoolandsort(&old_iter, old_iter, git_index_entry_cmp_icase, true) < 0)
goto fail;
} else if (!new_iter->ignore_case) {
if (git_iterator_spoolandsort(&new_iter, new_iter, git_index_entry_cmp_icase, true) < 0)
goto fail;
}
}
if (git_iterator_current(old_iter, &oitem) < 0 || if (git_iterator_current(old_iter, &oitem) < 0 ||
git_iterator_current(new_iter, &nitem) < 0) git_iterator_current(new_iter, &nitem) < 0)
goto fail; goto fail;
...@@ -608,7 +648,7 @@ static int diff_from_iterators( ...@@ -608,7 +648,7 @@ static int diff_from_iterators(
while (oitem || nitem) { while (oitem || nitem) {
/* create DELETED records for old items not matched in new */ /* create DELETED records for old items not matched in new */
if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) { if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
git_iterator_advance(old_iter, &oitem) < 0) git_iterator_advance(old_iter, &oitem) < 0)
goto fail; goto fail;
...@@ -617,12 +657,12 @@ static int diff_from_iterators( ...@@ -617,12 +657,12 @@ static int diff_from_iterators(
/* create ADDED, TRACKED, or IGNORED records for new items not /* create ADDED, TRACKED, or IGNORED records for new items not
* matched in old (and/or descend into directories as needed) * matched in old (and/or descend into directories as needed)
*/ */
else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) { else if (nitem && (!oitem || entry_compare(oitem, nitem) > 0)) {
git_delta_t delta_type = GIT_DELTA_UNTRACKED; git_delta_t delta_type = GIT_DELTA_UNTRACKED;
/* check if contained in ignored parent directory */ /* check if contained in ignored parent directory */
if (git_buf_len(&ignore_prefix) && if (git_buf_len(&ignore_prefix) &&
git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0) ITERATOR_PREFIXCMP(*old_iter, nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
delta_type = GIT_DELTA_IGNORED; delta_type = GIT_DELTA_IGNORED;
if (S_ISDIR(nitem->mode)) { if (S_ISDIR(nitem->mode)) {
...@@ -630,7 +670,7 @@ static int diff_from_iterators( ...@@ -630,7 +670,7 @@ static int diff_from_iterators(
* it or if the user requested the contents of untracked * it or if the user requested the contents of untracked
* directories and it is not under an ignored directory. * directories and it is not under an ignored directory.
*/ */
if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) || if ((oitem && ITERATOR_PREFIXCMP(*old_iter, oitem->path, nitem->path) == 0) ||
(delta_type == GIT_DELTA_UNTRACKED && (delta_type == GIT_DELTA_UNTRACKED &&
(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0)) (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
{ {
...@@ -684,7 +724,7 @@ static int diff_from_iterators( ...@@ -684,7 +724,7 @@ static int diff_from_iterators(
* (or ADDED and DELETED pair if type changed) * (or ADDED and DELETED pair if type changed)
*/ */
else { else {
assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0); assert(oitem && nitem && entry_compare(oitem, nitem) == 0);
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 || if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
git_iterator_advance(old_iter, &oitem) < 0 || git_iterator_advance(old_iter, &oitem) < 0 ||
...@@ -837,6 +877,7 @@ int git_diff_merge( ...@@ -837,6 +877,7 @@ int git_diff_merge(
git_pool onto_pool; git_pool onto_pool;
git_vector onto_new; git_vector onto_new;
git_diff_delta *delta; git_diff_delta *delta;
bool ignore_case = false;
unsigned int i, j; unsigned int i, j;
assert(onto && from); assert(onto && from);
...@@ -848,10 +889,21 @@ int git_diff_merge( ...@@ -848,10 +889,21 @@ int git_diff_merge(
git_pool_init(&onto_pool, 1, 0) < 0) git_pool_init(&onto_pool, 1, 0) < 0)
return -1; return -1;
if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
{
ignore_case = true;
/* This function currently only supports merging diff lists that
* are sorted identically. */
assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
}
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path); int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
if (cmp < 0) { if (cmp < 0) {
delta = diff_delta__dup(o, &onto_pool); delta = diff_delta__dup(o, &onto_pool);
......
#include "git2/ignore.h" #include "git2/ignore.h"
#include "ignore.h" #include "ignore.h"
#include "path.h" #include "path.h"
#include "config.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude" #define GIT_IGNORE_INTERNAL "[internal]exclude"
#define GIT_IGNORE_FILE_INREPO "info/exclude" #define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore" #define GIT_IGNORE_FILE ".gitignore"
static int parse_ignore_file( static int parse_ignore_file(
git_repository *repo, const char *buffer, git_attr_file *ignores) git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores)
{ {
int error = 0; int error = 0;
git_attr_fnmatch *match = NULL; git_attr_fnmatch *match = NULL;
const char *scan = NULL; const char *scan = NULL;
char *context = NULL; char *context = NULL;
bool ignore_case = false;
GIT_UNUSED(repo); git_config *cfg = NULL;
int val;
/* Prefer to have the caller pass in a git_ignores as the parsedata object.
* If they did not, then we can (much more slowly) find the value of
* ignore_case by using the repository object. */
if (parsedata != NULL) {
ignore_case = ((git_ignores *)parsedata)->ignore_case;
} else {
if ((error = git_repository_config(&cfg, repo)) < 0)
return error;
if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
ignore_case = (val != 0);
git_config_free(cfg);
}
if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) { if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
context = ignores->key + 2; context = ignores->key + 2;
...@@ -31,6 +48,9 @@ static int parse_ignore_file( ...@@ -31,6 +48,9 @@ static int parse_ignore_file(
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE; match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
if (ignore_case)
match->flags |= GIT_ATTR_FNMATCH_ICASE;
if (!(error = git_attr_fnmatch__parse( if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan))) match, ignores->pool, context, &scan)))
{ {
...@@ -58,13 +78,13 @@ static int parse_ignore_file( ...@@ -58,13 +78,13 @@ static int parse_ignore_file(
return error; return error;
} }
#define push_ignore_file(R,S,B,F) \ #define push_ignore_file(R,IGN,S,B,F) \
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S)) git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S))
static int push_one_ignore(void *ref, git_buf *path) static int push_one_ignore(void *ref, git_buf *path)
{ {
git_ignores *ign = (git_ignores *)ref; git_ignores *ign = (git_ignores *)ref;
return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
} }
int git_ignore__for_path( int git_ignore__for_path(
...@@ -74,6 +94,8 @@ int git_ignore__for_path( ...@@ -74,6 +94,8 @@ int git_ignore__for_path(
{ {
int error = 0; int error = 0;
const char *workdir = git_repository_workdir(repo); const char *workdir = git_repository_workdir(repo);
git_config *cfg = NULL;
int val;
assert(ignores); assert(ignores);
...@@ -81,6 +103,17 @@ int git_ignore__for_path( ...@@ -81,6 +103,17 @@ int git_ignore__for_path(
git_buf_init(&ignores->dir, 0); git_buf_init(&ignores->dir, 0);
ignores->ign_internal = NULL; ignores->ign_internal = NULL;
/* Set the ignore_case flag appropriately */
if ((error = git_repository_config(&cfg, repo)) < 0)
goto cleanup;
if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
ignores->ignore_case = (val != 0);
else
ignores->ignore_case = 0;
git_config_free(cfg);
if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 || if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 || (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
(error = git_attr_cache__init(repo)) < 0) (error = git_attr_cache__init(repo)) < 0)
...@@ -109,14 +142,14 @@ int git_ignore__for_path( ...@@ -109,14 +142,14 @@ int git_ignore__for_path(
} }
/* load .git/info/exclude */ /* load .git/info/exclude */
error = push_ignore_file(repo, &ignores->ign_global, error = push_ignore_file(repo, ignores, &ignores->ign_global,
git_repository_path(repo), GIT_IGNORE_FILE_INREPO); git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
/* load core.excludesfile */ /* load core.excludesfile */
if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
error = push_ignore_file(repo, &ignores->ign_global, NULL, error = push_ignore_file(repo, ignores, &ignores->ign_global, NULL,
git_repository_attr_cache(repo)->cfg_excl_file); git_repository_attr_cache(repo)->cfg_excl_file);
cleanup: cleanup:
...@@ -132,7 +165,7 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir) ...@@ -132,7 +165,7 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
return -1; return -1;
else else
return push_ignore_file( return push_ignore_file(
ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
} }
int git_ignore__pop_dir(git_ignores *ign) int git_ignore__pop_dir(git_ignores *ign)
...@@ -223,7 +256,7 @@ int git_ignore_add_rule( ...@@ -223,7 +256,7 @@ int git_ignore_add_rule(
git_attr_file *ign_internal; git_attr_file *ign_internal;
if (!(error = get_internal_ignores(&ign_internal, repo))) if (!(error = get_internal_ignores(&ign_internal, repo)))
error = parse_ignore_file(repo, rules, ign_internal); error = parse_ignore_file(repo, NULL, rules, ign_internal);
return error; return error;
} }
......
...@@ -23,6 +23,7 @@ typedef struct { ...@@ -23,6 +23,7 @@ typedef struct {
git_attr_file *ign_internal; git_attr_file *ign_internal;
git_vector ign_path; git_vector ign_path;
git_vector ign_global; git_vector ign_global;
unsigned int ignore_case:1;
} git_ignores; } git_ignores;
extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign); extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
......
...@@ -99,6 +99,13 @@ static int index_srch(const void *key, const void *array_member) ...@@ -99,6 +99,13 @@ static int index_srch(const void *key, const void *array_member)
return strcmp(key, entry->path); return strcmp(key, entry->path);
} }
static int index_isrch(const void *key, const void *array_member)
{
const git_index_entry *entry = array_member;
return strcasecmp(key, entry->path);
}
static int index_cmp(const void *a, const void *b) static int index_cmp(const void *a, const void *b)
{ {
const git_index_entry *entry_a = a; const git_index_entry *entry_a = a;
...@@ -107,6 +114,14 @@ static int index_cmp(const void *a, const void *b) ...@@ -107,6 +114,14 @@ static int index_cmp(const void *a, const void *b)
return strcmp(entry_a->path, entry_b->path); return strcmp(entry_a->path, entry_b->path);
} }
static int index_icmp(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path);
}
static int unmerged_srch(const void *key, const void *array_member) static int unmerged_srch(const void *key, const void *array_member)
{ {
const git_index_entry_unmerged *entry = array_member; const git_index_entry_unmerged *entry = array_member;
...@@ -147,6 +162,14 @@ static unsigned int index_merge_mode( ...@@ -147,6 +162,14 @@ static unsigned int index_merge_mode(
return index_create_mode(mode); return index_create_mode(mode);
} }
static void index_set_ignore_case(git_index *index, bool ignore_case)
{
index->entries._cmp = ignore_case ? index_icmp : index_cmp;
index->entries_search = ignore_case ? index_isrch : index_srch;
index->entries.sorted = 0;
git_vector_sort(&index->entries);
}
int git_index_open(git_index **index_out, const char *index_path) int git_index_open(git_index **index_out, const char *index_path)
{ {
git_index *index; git_index *index;
...@@ -162,6 +185,8 @@ int git_index_open(git_index **index_out, const char *index_path) ...@@ -162,6 +185,8 @@ int git_index_open(git_index **index_out, const char *index_path)
if (git_vector_init(&index->entries, 32, index_cmp) < 0) if (git_vector_init(&index->entries, 32, index_cmp) < 0)
return -1; return -1;
index->entries_search = index_srch;
/* Check if index file is stored on disk already */ /* Check if index file is stored on disk already */
if (git_path_exists(index->index_file_path) == true) if (git_path_exists(index->index_file_path) == true)
index->on_disk = 1; index->on_disk = 1;
...@@ -228,8 +253,12 @@ void git_index_clear(git_index *index) ...@@ -228,8 +253,12 @@ void git_index_clear(git_index *index)
int git_index_set_caps(git_index *index, unsigned int caps) int git_index_set_caps(git_index *index, unsigned int caps)
{ {
int old_ignore_case;
assert(index); assert(index);
old_ignore_case = index->ignore_case;
if (caps == GIT_INDEXCAP_FROM_OWNER) { if (caps == GIT_INDEXCAP_FROM_OWNER) {
git_config *cfg; git_config *cfg;
int val; int val;
...@@ -255,6 +284,11 @@ int git_index_set_caps(git_index *index, unsigned int caps) ...@@ -255,6 +284,11 @@ int git_index_set_caps(git_index *index, unsigned int caps)
index->no_symlinks = ((caps & GIT_INDEXCAP_NO_SYMLINKS) != 0); index->no_symlinks = ((caps & GIT_INDEXCAP_NO_SYMLINKS) != 0);
} }
if (old_ignore_case != index->ignore_case)
{
index_set_ignore_case(index, index->ignore_case);
}
return 0; return 0;
} }
...@@ -552,14 +586,14 @@ int git_index_remove(git_index *index, int position) ...@@ -552,14 +586,14 @@ int git_index_remove(git_index *index, int position)
int git_index_find(git_index *index, const char *path) int git_index_find(git_index *index, const char *path)
{ {
return git_vector_bsearch2(&index->entries, index_srch, path); return git_vector_bsearch2(&index->entries, index->entries_search, path);
} }
unsigned int git_index__prefix_position(git_index *index, const char *path) unsigned int git_index__prefix_position(git_index *index, const char *path)
{ {
unsigned int pos; unsigned int pos;
git_vector_bsearch3(&pos, &index->entries, index_srch, path); git_vector_bsearch3(&pos, &index->entries, index->entries_search, path);
return pos; return pos;
} }
...@@ -938,16 +972,28 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) ...@@ -938,16 +972,28 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
static int write_entries(git_index *index, git_filebuf *file) static int write_entries(git_index *index, git_filebuf *file)
{ {
int error = 0;
unsigned int i; unsigned int i;
git_vector case_sorted;
for (i = 0; i < index->entries.length; ++i) { git_index_entry *entry;
git_index_entry *entry; git_vector *out = &index->entries;
entry = git_vector_get(&index->entries, i);
if (write_disk_entry(file, entry) < 0) /* If index->entries is sorted case-insensitively, then we need
return -1; * to re-sort it case-sensitively before writing */
if (index->ignore_case) {
git_vector_dup(&case_sorted, &index->entries, index_cmp);
git_vector_sort(&case_sorted);
out = &case_sorted;
} }
return 0; git_vector_foreach(out, i, entry)
if ((error = write_disk_entry(file, entry)) < 0)
break;
if (index->ignore_case)
git_vector_free(&case_sorted);
return error;
} }
static int write_index(git_index *index, git_filebuf *file) static int write_index(git_index *index, git_filebuf *file)
......
...@@ -34,6 +34,8 @@ struct git_index { ...@@ -34,6 +34,8 @@ struct git_index {
git_tree_cache *tree; git_tree_cache *tree;
git_vector unmerged; git_vector unmerged;
git_vector_cmp entries_search;
}; };
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry); extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
(P)->base.type = GIT_ITERATOR_ ## NAME_UC; \ (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \
(P)->base.start = start ? git__strdup(start) : NULL; \ (P)->base.start = start ? git__strdup(start) : NULL; \
(P)->base.end = end ? git__strdup(end) : NULL; \ (P)->base.end = end ? git__strdup(end) : NULL; \
(P)->base.ignore_case = 0; \
(P)->base.current = NAME_LC ## _iterator__current; \ (P)->base.current = NAME_LC ## _iterator__current; \
(P)->base.at_end = NAME_LC ## _iterator__at_end; \ (P)->base.at_end = NAME_LC ## _iterator__at_end; \
(P)->base.advance = NAME_LC ## _iterator__advance; \ (P)->base.advance = NAME_LC ## _iterator__advance; \
...@@ -336,7 +337,7 @@ static int index_iterator__current( ...@@ -336,7 +337,7 @@ static int index_iterator__current(
if (ie != NULL && if (ie != NULL &&
ii->base.end != NULL && ii->base.end != NULL &&
git__prefixcmp(ie->path, ii->base.end) > 0) ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)
{ {
ii->current = git_index_entrycount(ii->index); ii->current = git_index_entrycount(ii->index);
ie = NULL; ie = NULL;
...@@ -401,6 +402,7 @@ int git_iterator_for_index_range( ...@@ -401,6 +402,7 @@ int git_iterator_for_index_range(
if ((error = git_repository_index(&ii->index, repo)) < 0) if ((error = git_repository_index(&ii->index, repo)) < 0)
git__free(ii); git__free(ii);
else { else {
ii->base.ignore_case = ii->index->ignore_case;
ii->current = start ? git_index__prefix_position(ii->index, start) : 0; ii->current = start ? git_index__prefix_position(ii->index, start) : 0;
*iter = (git_iterator *)ii; *iter = (git_iterator *)ii;
} }
...@@ -428,12 +430,30 @@ typedef struct { ...@@ -428,12 +430,30 @@ typedef struct {
int is_ignored; int is_ignored;
} workdir_iterator; } workdir_iterator;
static workdir_iterator_frame *workdir_iterator__alloc_frame(void) static int git_path_with_stat_cmp_case(const void *a, const void *b)
{
const git_path_with_stat *path_with_stat_a = a;
const git_path_with_stat *path_with_stat_b = b;
return strcmp(path_with_stat_a->path, path_with_stat_b->path);
}
static int git_path_with_stat_cmp_icase(const void *a, const void *b)
{
const git_path_with_stat *path_with_stat_a = a;
const git_path_with_stat *path_with_stat_b = b;
return strcasecmp(path_with_stat_a->path, path_with_stat_b->path);
}
static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi)
{ {
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, git_path_with_stat_cmp_icase, git_path_with_stat_cmp_case);
if (wf == NULL) if (wf == NULL)
return NULL; return NULL;
if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != 0) { if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
git__free(wf); git__free(wf);
return NULL; return NULL;
} }
...@@ -453,16 +473,22 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf) ...@@ -453,16 +473,22 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
static int workdir_iterator__update_entry(workdir_iterator *wi); static int workdir_iterator__update_entry(workdir_iterator *wi);
static int workdir_iterator__entry_cmp(const void *prefix, const void *item) static int workdir_iterator__entry_cmp_case(const void *prefix, const void *item)
{ {
const git_path_with_stat *ps = item; const git_path_with_stat *ps = item;
return git__prefixcmp((const char *)prefix, ps->path); return git__prefixcmp((const char *)prefix, ps->path);
} }
static int workdir_iterator__entry_cmp_icase(const void *prefix, const void *item)
{
const git_path_with_stat *ps = item;
return git__prefixcmp_icase((const char *)prefix, ps->path);
}
static int workdir_iterator__expand_dir(workdir_iterator *wi) static int workdir_iterator__expand_dir(workdir_iterator *wi)
{ {
int error; int error;
workdir_iterator_frame *wf = workdir_iterator__alloc_frame(); workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi);
GITERR_CHECK_ALLOC(wf); GITERR_CHECK_ALLOC(wf);
error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries); error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
...@@ -476,12 +502,15 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi) ...@@ -476,12 +502,15 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
if (!wi->stack) if (!wi->stack)
wf->start = wi->base.start; wf->start = wi->base.start;
else if (wi->stack->start && else if (wi->stack->start &&
git__prefixcmp(wi->stack->start, wi->path.ptr + wi->root_len) == 0) ITERATOR_PREFIXCMP(wi->base, wi->stack->start, wi->path.ptr + wi->root_len) == 0)
wf->start = wi->stack->start; wf->start = wi->stack->start;
if (wf->start) if (wf->start)
git_vector_bsearch3( git_vector_bsearch3(
&wf->index, &wf->entries, workdir_iterator__entry_cmp, wf->start); &wf->index,
&wf->entries,
CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case),
wf->start);
wf->next = wi->stack; wf->next = wi->stack;
wi->stack = wf; wi->stack = wf;
...@@ -526,8 +555,8 @@ static int workdir_iterator__advance( ...@@ -526,8 +555,8 @@ static int workdir_iterator__advance(
next = git_vector_get(&wf->entries, ++wf->index); next = git_vector_get(&wf->entries, ++wf->index);
if (next != NULL) { if (next != NULL) {
/* match git's behavior of ignoring anything named ".git" */ /* match git's behavior of ignoring anything named ".git" */
if (strcmp(next->path, DOT_GIT "/") == 0 || if (STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT "/") == 0 ||
strcmp(next->path, DOT_GIT) == 0) STRCMP_CASESELECT(wi->base.ignore_case, next->path, DOT_GIT) == 0)
continue; continue;
/* else found a good entry */ /* else found a good entry */
break; break;
...@@ -604,13 +633,14 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) ...@@ -604,13 +633,14 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
return -1; return -1;
if (wi->base.end && if (wi->base.end &&
git__prefixcmp(wi->path.ptr + wi->root_len, wi->base.end) > 0) ITERATOR_PREFIXCMP(wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
return 0; return 0;
wi->entry.path = ps->path; wi->entry.path = ps->path;
/* skip over .git entry */ /* skip over .git entry */
if (strcmp(ps->path, DOT_GIT "/") == 0 || strcmp(ps->path, DOT_GIT) == 0) if (STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT "/") == 0 ||
STRCMP_CASESELECT(wi->base.ignore_case, ps->path, DOT_GIT) == 0)
return workdir_iterator__advance((git_iterator *)wi, NULL); return workdir_iterator__advance((git_iterator *)wi, NULL);
/* if there is an error processing the entry, treat as ignored */ /* if there is an error processing the entry, treat as ignored */
...@@ -656,6 +686,7 @@ int git_iterator_for_workdir_range( ...@@ -656,6 +686,7 @@ int git_iterator_for_workdir_range(
{ {
int error; int error;
workdir_iterator *wi; workdir_iterator *wi;
git_index *index;
assert(iter && repo); assert(iter && repo);
...@@ -666,6 +697,17 @@ int git_iterator_for_workdir_range( ...@@ -666,6 +697,17 @@ int git_iterator_for_workdir_range(
wi->repo = repo; wi->repo = repo;
if ((error = git_repository_index(&index, repo)) < 0) {
git__free(wi);
return error;
}
/* Set the ignore_case flag for the workdir iterator to match
* that of the index. */
wi->base.ignore_case = index->ignore_case;
git_index_free(index);
if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 || if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
git_path_to_dir(&wi->path) < 0 || git_path_to_dir(&wi->path) < 0 ||
git_ignore__for_path(repo, "", &wi->ignores) < 0) git_ignore__for_path(repo, "", &wi->ignores) < 0)
...@@ -690,6 +732,129 @@ int git_iterator_for_workdir_range( ...@@ -690,6 +732,129 @@ int git_iterator_for_workdir_range(
return error; return error;
} }
typedef struct {
git_iterator base;
git_iterator *wrapped;
git_vector entries;
git_vector_cmp comparer;
git_pool entry_pool;
git_pool string_pool;
unsigned int position;
} spoolandsort_iterator;
static int spoolandsort_iterator__current(
git_iterator *self, const git_index_entry **entry)
{
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
if (si->position < si->entries.length)
*entry = (const git_index_entry *)git_vector_get_const(&si->entries, si->position);
else
*entry = NULL;
return 0;
}
static int spoolandsort_iterator__at_end(git_iterator *self)
{
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
return 0 == si->entries.length || si->entries.length - 1 <= si->position;
}
static int spoolandsort_iterator__advance(
git_iterator *self, const git_index_entry **entry)
{
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
if (si->position < si->entries.length)
*entry = (const git_index_entry *)git_vector_get_const(&si->entries, ++si->position);
else
*entry = NULL;
return 0;
}
static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix)
{
GIT_UNUSED(self);
GIT_UNUSED(prefix);
return -1;
}
static int spoolandsort_iterator__reset(git_iterator *self)
{
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
si->position = 0;
return 0;
}
static void spoolandsort_iterator__free(git_iterator *self)
{
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
git_pool_clear(&si->string_pool);
git_pool_clear(&si->entry_pool);
git_vector_free(&si->entries);
git_iterator_free(si->wrapped);
}
int git_iterator_spoolandsort_range(
git_iterator **iter,
git_iterator *towrap,
git_vector_cmp comparer,
bool ignore_case,
const char *start,
const char *end)
{
spoolandsort_iterator *si;
const git_index_entry *item;
assert(iter && towrap && comparer);
ITERATOR_BASE_INIT(si, spoolandsort, SPOOLANDSORT);
si->base.ignore_case = ignore_case;
si->wrapped = towrap;
si->comparer = comparer;
si->position = 0;
if (git_vector_init(&si->entries, 16, si->comparer) < 0 ||
git_iterator_current(towrap, &item) < 0 ||
git_pool_init(&si->entry_pool, sizeof(git_index_entry), 0) ||
git_pool_init(&si->string_pool, 1, 0))
{
git__free(si);
return -1;
}
while (item)
{
git_index_entry *clone = git_pool_malloc(&si->entry_pool, 1);
memcpy(clone, item, sizeof(git_index_entry));
if (item->path)
{
clone->path = git_pool_strdup(&si->string_pool, item->path);
}
git_vector_insert(&si->entries, clone);
if (git_iterator_advance(towrap, &item) < 0)
{
git__free(si);
return -1;
}
}
git_vector_sort(&si->entries);
*iter = (git_iterator *)si;
return 0;
}
int git_iterator_current_tree_entry( int git_iterator_current_tree_entry(
git_iterator *iter, const git_tree_entry **tree_entry) git_iterator *iter, const git_tree_entry **tree_entry)
...@@ -737,6 +902,6 @@ int git_iterator_cmp( ...@@ -737,6 +902,6 @@ int git_iterator_cmp(
if (!path_prefix) if (!path_prefix)
return -1; return -1;
return git__prefixcmp(entry->path, path_prefix); return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix);
} }
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
#include "common.h" #include "common.h"
#include "git2/index.h" #include "git2/index.h"
#include "vector.h"
#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) (((ITER).ignore_case) ? \
git__prefixcmp_icase((STR), (PREFIX)) : \
git__prefixcmp((STR), (PREFIX)))
typedef struct git_iterator git_iterator; typedef struct git_iterator git_iterator;
...@@ -16,7 +21,8 @@ typedef enum { ...@@ -16,7 +21,8 @@ typedef enum {
GIT_ITERATOR_EMPTY = 0, GIT_ITERATOR_EMPTY = 0,
GIT_ITERATOR_TREE = 1, GIT_ITERATOR_TREE = 1,
GIT_ITERATOR_INDEX = 2, GIT_ITERATOR_INDEX = 2,
GIT_ITERATOR_WORKDIR = 3 GIT_ITERATOR_WORKDIR = 3,
GIT_ITERATOR_SPOOLANDSORT = 4
} git_iterator_type_t; } git_iterator_type_t;
struct git_iterator { struct git_iterator {
...@@ -29,6 +35,7 @@ struct git_iterator { ...@@ -29,6 +35,7 @@ struct git_iterator {
int (*seek)(git_iterator *, const char *prefix); int (*seek)(git_iterator *, const char *prefix);
int (*reset)(git_iterator *); int (*reset)(git_iterator *);
void (*free)(git_iterator *); void (*free)(git_iterator *);
unsigned int ignore_case:1;
}; };
extern int git_iterator_for_nothing(git_iterator **iter); extern int git_iterator_for_nothing(git_iterator **iter);
...@@ -63,6 +70,17 @@ GIT_INLINE(int) git_iterator_for_workdir( ...@@ -63,6 +70,17 @@ GIT_INLINE(int) git_iterator_for_workdir(
return git_iterator_for_workdir_range(iter, repo, NULL, NULL); return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
} }
extern int git_iterator_spoolandsort_range(
git_iterator **iter, git_iterator *towrap,
git_vector_cmp comparer, bool ignore_case,
const char *start, const char *end);
GIT_INLINE(int) git_iterator_spoolandsort(
git_iterator **iter, git_iterator *towrap,
git_vector_cmp comparer, bool ignore_case)
{
return git_iterator_spoolandsort_range(iter, towrap, comparer, ignore_case, NULL, NULL);
}
/* Entry is not guaranteed to be fully populated. For a tree iterator, /* Entry is not guaranteed to be fully populated. For a tree iterator,
* we will only populate the mode, oid and path, for example. For a workdir * we will only populate the mode, oid and path, for example. For a workdir
......
...@@ -82,6 +82,7 @@ int git_status_foreach_ext( ...@@ -82,6 +82,7 @@ int git_status_foreach_ext(
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
git_diff_delta *i2h, *w2i; git_diff_delta *i2h, *w2i;
size_t i, j, i_max, j_max; size_t i, j, i_max, j_max;
bool ignore_case = false;
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
...@@ -124,11 +125,26 @@ int git_status_foreach_ext( ...@@ -124,11 +125,26 @@ int git_status_foreach_ext(
i_max = idx2head ? idx2head->deltas.length : 0; i_max = idx2head ? idx2head->deltas.length : 0;
j_max = wd2idx ? wd2idx->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... */
ignore_case = 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));
}
for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) { for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path); cmp = !w2i ? -1 : !i2h ? 1 : STRCMP_CASESELECT(ignore_case, i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) { if (cmp < 0) {
if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata)) if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
......
...@@ -199,6 +199,11 @@ int git__prefixcmp(const char *str, const char *prefix) ...@@ -199,6 +199,11 @@ int git__prefixcmp(const char *str, const char *prefix)
} }
} }
int git__prefixcmp_icase(const char *str, const char *prefix)
{
return strncasecmp(str, prefix, strlen(prefix));
}
int git__suffixcmp(const char *str, const char *suffix) int git__suffixcmp(const char *str, const char *suffix)
{ {
size_t a = strlen(str); size_t a = strlen(str);
......
...@@ -70,7 +70,14 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size) ...@@ -70,7 +70,14 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
#define git__free(ptr) free(ptr) #define git__free(ptr) free(ptr)
#define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
#define CASESELECT(IGNORE_CASE, ICASE, CASE) \
((IGNORE_CASE) ? (ICASE) : (CASE))
extern int git__prefixcmp(const char *str, const char *prefix); extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix); extern int git__suffixcmp(const char *str, const char *suffix);
extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base); extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
......
...@@ -24,6 +24,22 @@ static int resize_vector(git_vector *v) ...@@ -24,6 +24,22 @@ static int resize_vector(git_vector *v)
return 0; return 0;
} }
int git_vector_dup(git_vector *v, git_vector *src, git_vector_cmp cmp)
{
assert(v && src);
v->_alloc_size = src->length;
v->_cmp = cmp;
v->length = src->length;
v->sorted = src->sorted && cmp == src->_cmp;
v->contents = git__malloc(src->length * sizeof(void *));
GITERR_CHECK_ALLOC(v->contents);
memcpy(v->contents, src->contents, src->length * sizeof(void *));
return 0;
}
void git_vector_free(git_vector *v) void git_vector_free(git_vector *v)
{ {
assert(v); assert(v);
......
...@@ -24,6 +24,7 @@ typedef struct git_vector { ...@@ -24,6 +24,7 @@ typedef struct git_vector {
int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp); int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v); void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v); void git_vector_clear(git_vector *v);
int git_vector_dup(git_vector *v, git_vector *src, git_vector_cmp cmp);
void git_vector_swap(git_vector *a, git_vector *b); void git_vector_swap(git_vector *a, git_vector *b);
void git_vector_sort(git_vector *v); void git_vector_sort(git_vector *v);
......
...@@ -252,7 +252,7 @@ void test_attr_lookup__from_buffer(void) ...@@ -252,7 +252,7 @@ void test_attr_lookup__from_buffer(void)
cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL)); cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL));
cl_git_pass(git_attr_file__parse_buffer(NULL, "a* foo\nabc bar\n* baz", file)); cl_git_pass(git_attr_file__parse_buffer(NULL, NULL, "a* foo\nabc bar\n* baz", file));
cl_assert(file->rules.length == 3); cl_assert(file->rules.length == 3);
......
...@@ -90,6 +90,56 @@ static const int entry_count2 = 15; ...@@ -90,6 +90,56 @@ static const int entry_count2 = 15;
/* entries for a copy of tests/resources/status with some mods */ /* entries for a copy of tests/resources/status with some mods */
static const char *entry_paths3_icase[] = {
".HEADER",
"42-is-not-prime.sigh",
"current_file",
"current_file/",
"file_deleted",
"ignored_file",
"modified_file",
"new_file",
"README.md",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir",
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses3_icase[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
};
static const char *entry_paths3[] = { static const char *entry_paths3[] = {
".HEADER", ".HEADER",
"42-is-not-prime.sigh", "42-is-not-prime.sigh",
......
...@@ -110,7 +110,13 @@ void test_status_worktree__swap_subdir_and_file(void) ...@@ -110,7 +110,13 @@ void test_status_worktree__swap_subdir_and_file(void)
{ {
status_entry_counts counts; status_entry_counts counts;
git_repository *repo = cl_git_sandbox_init("status"); git_repository *repo = cl_git_sandbox_init("status");
git_index *index;
git_status_options opts; git_status_options opts;
bool ignore_case;
cl_git_pass(git_repository_index(&index, repo));
ignore_case = index->ignore_case;
git_index_free(index);
/* first alter the contents of the worktree */ /* first alter the contents of the worktree */
cl_git_pass(p_rename("status/current_file", "status/swap")); cl_git_pass(p_rename("status/current_file", "status/swap"));
...@@ -124,8 +130,8 @@ void test_status_worktree__swap_subdir_and_file(void) ...@@ -124,8 +130,8 @@ void test_status_worktree__swap_subdir_and_file(void)
/* now get status */ /* now get status */
memset(&counts, 0x0, sizeof(status_entry_counts)); memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = entry_count3; counts.expected_entry_count = entry_count3;
counts.expected_paths = entry_paths3; counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3;
counts.expected_statuses = entry_statuses3; counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
......
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