Commit c261c272 by Vicent Martí

Merge pull request #702 from arrbee/fix-status-file

Update git_status_file and add ranged iterators
parents b206d74c 2c833917
...@@ -36,15 +36,18 @@ GIT_BEGIN_DECL ...@@ -36,15 +36,18 @@ GIT_BEGIN_DECL
/** /**
* Gather file statuses and run a callback for each one. * Gather file statuses and run a callback for each one.
* *
* The callback is passed the path of the file, the status and the data pointer * The callback is passed the path of the file, the status and the data
* passed to this function. If the callback returns something other than * pointer passed to this function. If the callback returns something other
* GIT_SUCCESS, this function will return that value. * than 0, this function will return that value.
* *
* @param repo a repository object * @param repo a repository object
* @param callback the function to call on each file * @param callback the function to call on each file
* @return GIT_SUCCESS or the return value of the callback which did not return GIT_SUCCESS * @return 0 on success or the return value of the callback that was non-zero
*/ */
GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload); GIT_EXTERN(int) git_status_foreach(
git_repository *repo,
int (*callback)(const char *, unsigned int, void *),
void *payload);
/** /**
* Select the files on which to report status. * Select the files on which to report status.
...@@ -115,7 +118,7 @@ typedef struct { ...@@ -115,7 +118,7 @@ typedef struct {
*/ */
GIT_EXTERN(int) git_status_foreach_ext( GIT_EXTERN(int) git_status_foreach_ext(
git_repository *repo, git_repository *repo,
git_status_options *opts, const git_status_options *opts,
int (*callback)(const char *, unsigned int, void *), int (*callback)(const char *, unsigned int, void *),
void *payload); void *payload);
...@@ -129,7 +132,10 @@ GIT_EXTERN(int) git_status_foreach_ext( ...@@ -129,7 +132,10 @@ GIT_EXTERN(int) git_status_foreach_ext(
* the file doesn't exist in any of HEAD, the index or the worktree, * the file doesn't exist in any of HEAD, the index or the worktree,
* GIT_SUCCESS otherwise * GIT_SUCCESS otherwise
*/ */
GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo, const char *path); GIT_EXTERN(int) git_status_file(
unsigned int *status_flags,
git_repository *repo,
const char *path);
/** /**
* Test if the ignore rules apply to a given file. * Test if the ignore rules apply to a given file.
...@@ -141,11 +147,14 @@ GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo ...@@ -141,11 +147,14 @@ GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo
* *
* @param ignored boolean returning 0 if the file is not ignored, 1 if it is * @param ignored boolean returning 0 if the file is not ignored, 1 if it is
* @param repo a repository object * @param repo a repository object
* @param path the file to check ignores for, rooted at the repo's workdir * @param path the file to check ignores for, rooted at the repo's workdir.
* @return GIT_SUCCESS if the ignore rules could be processed for the file * @return 0 if ignore rules could be processed for the file (regardless
* (regardless of whether it exists or not), or an error < 0 if they could not. * of whether it exists or not), or an error < 0 if they could not.
*/ */
GIT_EXTERN(int) git_status_should_ignore(int *ignored, git_repository *repo, const char *path); GIT_EXTERN(int) git_status_should_ignore(
int *ignored,
git_repository *repo,
const char *path);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
......
...@@ -378,7 +378,7 @@ int git_attr_fnmatch__parse( ...@@ -378,7 +378,7 @@ int git_attr_fnmatch__parse(
pattern++; pattern++;
} }
/* remember if we see an unescaped wildcard in pattern */ /* remember if we see an unescaped wildcard in pattern */
else if ((*scan == '*' || *scan == '.' || *scan == '[') && else if (git__iswildcard(*scan) &&
(scan == pattern || (*(scan - 1) != '\\'))) (scan == pattern || (*(scan - 1) != '\\')))
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD; spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
} }
......
...@@ -415,3 +415,33 @@ int git_buf_cmp(const git_buf *a, const git_buf *b) ...@@ -415,3 +415,33 @@ int git_buf_cmp(const git_buf *a, const git_buf *b)
return (result != 0) ? result : return (result != 0) ? result :
(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
} }
int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
{
size_t i;
const char *str, *pfx;
git_buf_clear(buf);
if (!strings || !strings->count)
return 0;
/* initialize common prefix to first string */
if (git_buf_sets(buf, strings->strings[0]) < 0)
return -1;
/* go through the rest of the strings, truncating to shared prefix */
for (i = 1; i < strings->count; ++i) {
for (str = strings->strings[i], pfx = buf->ptr;
*str && *str == *pfx; str++, pfx++)
/* scanning */;
git_buf_truncate(buf, pfx - buf->ptr);
if (!buf->size)
break;
}
return 0;
}
...@@ -122,4 +122,7 @@ void git_buf_rtrim(git_buf *buf); ...@@ -122,4 +122,7 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b); int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Fill buf with the common prefix of a array of strings */
int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
#endif #endif
...@@ -11,6 +11,25 @@ ...@@ -11,6 +11,25 @@
#include "config.h" #include "config.h"
#include "attr_file.h" #include "attr_file.h"
static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
{
git_buf prefix = GIT_BUF_INIT;
const char *scan;
if (git_buf_common_prefix(&prefix, pathspec) < 0)
return NULL;
/* diff prefix will only be leading non-wildcards */
for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
git_buf_truncate(&prefix, scan - prefix.ptr);
if (prefix.size > 0)
return git_buf_detach(&prefix);
git_buf_free(&prefix);
return NULL;
}
static bool diff_pathspec_is_interesting(const git_strarray *pathspec) static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
{ {
const char *str; const char *str;
...@@ -613,13 +632,16 @@ int git_diff_tree_to_tree( ...@@ -613,13 +632,16 @@ int git_diff_tree_to_tree(
git_diff_list **diff) git_diff_list **diff)
{ {
git_iterator *a = NULL, *b = NULL; git_iterator *a = NULL, *b = NULL;
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && old_tree && new_tree && diff); assert(repo && old_tree && new_tree && diff);
if (git_iterator_for_tree(repo, old_tree, &a) < 0 || if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
git_iterator_for_tree(repo, new_tree, &b) < 0) git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
return -1; return -1;
git__free(prefix);
return diff_from_iterators(repo, opts, a, b, diff); return diff_from_iterators(repo, opts, a, b, diff);
} }
...@@ -630,13 +652,16 @@ int git_diff_index_to_tree( ...@@ -630,13 +652,16 @@ int git_diff_index_to_tree(
git_diff_list **diff) git_diff_list **diff)
{ {
git_iterator *a = NULL, *b = NULL; git_iterator *a = NULL, *b = NULL;
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && diff); assert(repo && diff);
if (git_iterator_for_tree(repo, old_tree, &a) < 0 || if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
git_iterator_for_index(repo, &b) < 0) git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
return -1; return -1;
git__free(prefix);
return diff_from_iterators(repo, opts, a, b, diff); return diff_from_iterators(repo, opts, a, b, diff);
} }
...@@ -646,13 +671,16 @@ int git_diff_workdir_to_index( ...@@ -646,13 +671,16 @@ int git_diff_workdir_to_index(
git_diff_list **diff) git_diff_list **diff)
{ {
git_iterator *a = NULL, *b = NULL; git_iterator *a = NULL, *b = NULL;
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && diff); assert(repo && diff);
if (git_iterator_for_index(repo, &a) < 0 || if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
git_iterator_for_workdir(repo, &b) < 0) git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
return -1; return -1;
git__free(prefix);
return diff_from_iterators(repo, opts, a, b, diff); return diff_from_iterators(repo, opts, a, b, diff);
} }
...@@ -664,13 +692,16 @@ int git_diff_workdir_to_tree( ...@@ -664,13 +692,16 @@ int git_diff_workdir_to_tree(
git_diff_list **diff) git_diff_list **diff)
{ {
git_iterator *a = NULL, *b = NULL; git_iterator *a = NULL, *b = NULL;
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && old_tree && diff); assert(repo && old_tree && diff);
if (git_iterator_for_tree(repo, old_tree, &a) < 0 || if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
git_iterator_for_workdir(repo, &b) < 0) git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
return -1; return -1;
git__free(prefix);
return diff_from_iterators(repo, opts, a, b, diff); return diff_from_iterators(repo, opts, a, b, diff);
} }
......
...@@ -502,6 +502,15 @@ int git_index_find(git_index *index, const char *path) ...@@ -502,6 +502,15 @@ 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_srch, path);
} }
unsigned int git_index__prefix_position(git_index *index, const char *path)
{
unsigned int pos;
git_vector_bsearch3(&pos, &index->entries, index_srch, path);
return pos;
}
void git_index_uniq(git_index *index) void git_index_uniq(git_index *index)
{ {
git_vector_uniq(&index->entries); git_vector_uniq(&index->entries);
......
...@@ -33,4 +33,6 @@ struct git_index { ...@@ -33,4 +33,6 @@ struct git_index {
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);
extern unsigned int git_index__prefix_position(git_index *index, const char *path);
#endif #endif
...@@ -21,23 +21,48 @@ typedef enum { ...@@ -21,23 +21,48 @@ typedef enum {
struct git_iterator { struct git_iterator {
git_iterator_type_t type; git_iterator_type_t type;
char *start;
char *end;
int (*current)(git_iterator *, const git_index_entry **); int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *); int (*at_end)(git_iterator *);
int (*advance)(git_iterator *, const git_index_entry **); int (*advance)(git_iterator *, const git_index_entry **);
int (*seek)(git_iterator *, const char *prefix);
int (*reset)(git_iterator *); int (*reset)(git_iterator *);
void (*free)(git_iterator *); void (*free)(git_iterator *);
}; };
int git_iterator_for_nothing(git_iterator **iter); extern int git_iterator_for_nothing(git_iterator **iter);
int git_iterator_for_tree( extern int git_iterator_for_tree_range(
git_repository *repo, git_tree *tree, git_iterator **iter); git_iterator **iter, git_repository *repo, git_tree *tree,
const char *start, const char *end);
int git_iterator_for_index( GIT_INLINE(int) git_iterator_for_tree(
git_repository *repo, git_iterator **iter); git_iterator **iter, git_repository *repo, git_tree *tree)
{
return git_iterator_for_tree_range(iter, repo, tree, NULL, NULL);
}
extern int git_iterator_for_index_range(
git_iterator **iter, git_repository *repo,
const char *start, const char *end);
GIT_INLINE(int) git_iterator_for_index(
git_iterator **iter, git_repository *repo)
{
return git_iterator_for_index_range(iter, repo, NULL, NULL);
}
extern int git_iterator_for_workdir_range(
git_iterator **iter, git_repository *repo,
const char *start, const char *end);
GIT_INLINE(int) git_iterator_for_workdir(
git_iterator **iter, git_repository *repo)
{
return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
}
int git_iterator_for_workdir(
git_repository *repo, git_iterator **iter);
/* 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
...@@ -64,6 +89,12 @@ GIT_INLINE(int) git_iterator_advance( ...@@ -64,6 +89,12 @@ GIT_INLINE(int) git_iterator_advance(
return iter->advance(iter, entry); return iter->advance(iter, entry);
} }
GIT_INLINE(int) git_iterator_seek(
git_iterator *iter, const char *prefix)
{
return iter->seek(iter, prefix);
}
GIT_INLINE(int) git_iterator_reset(git_iterator *iter) GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
{ {
return iter->reset(iter); return iter->reset(iter);
...@@ -75,6 +106,12 @@ GIT_INLINE(void) git_iterator_free(git_iterator *iter) ...@@ -75,6 +106,12 @@ GIT_INLINE(void) git_iterator_free(git_iterator *iter)
return; return;
iter->free(iter); iter->free(iter);
git__free(iter->start);
git__free(iter->end);
memset(iter, 0, sizeof(*iter));
git__free(iter); git__free(iter);
} }
...@@ -108,4 +145,7 @@ extern int git_iterator_current_is_ignored(git_iterator *iter); ...@@ -108,4 +145,7 @@ extern int git_iterator_current_is_ignored(git_iterator *iter);
extern int git_iterator_advance_into_directory( extern int git_iterator_advance_into_directory(
git_iterator *iter, const git_index_entry **entry); git_iterator *iter, const git_index_entry **entry);
extern int git_iterator_cmp(
git_iterator *iter, const char *path_prefix);
#endif #endif
...@@ -523,7 +523,7 @@ int git_note_foreach( ...@@ -523,7 +523,7 @@ int git_note_foreach(
if (git_tree_lookup(&tree, repo, &tree_oid) < 0) if (git_tree_lookup(&tree, repo, &tree_oid) < 0)
goto cleanup; goto cleanup;
if (git_iterator_for_tree(repo, tree, &iter) < 0) if (git_iterator_for_tree(&iter, repo, tree) < 0)
goto cleanup; goto cleanup;
if (git_iterator_current(iter, &item) < 0) if (git_iterator_current(iter, &item) < 0)
......
...@@ -70,7 +70,7 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -70,7 +70,7 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
int git_status_foreach_ext( int git_status_foreach_ext(
git_repository *repo, git_repository *repo,
git_status_options *opts, const git_status_options *opts,
int (*cb)(const char *, unsigned int, void *), int (*cb)(const char *, unsigned int, void *),
void *cbdata) void *cbdata)
{ {
...@@ -163,245 +163,71 @@ int git_status_foreach( ...@@ -163,245 +163,71 @@ int git_status_foreach(
return git_status_foreach_ext(repo, &opts, callback, payload); return git_status_foreach_ext(repo, &opts, callback, payload);
} }
struct status_file_info {
/* unsigned int count;
* the old stuff unsigned int status;
*/ char *expected;
struct status_entry {
git_index_time mtime;
git_oid head_oid;
git_oid index_oid;
git_oid wt_oid;
unsigned int status_flags;
char path[GIT_FLEX_ARRAY]; /* more */
}; };
static struct status_entry *status_entry_new(git_vector *entries, const char *path) static int get_one_status(const char *path, unsigned int status, void *data)
{
struct status_entry *e = git__calloc(sizeof(*e) + strlen(path) + 1, 1);
if (e == NULL)
return NULL;
if (entries != NULL)
git_vector_insert(entries, e);
strcpy(e->path, path);
return e;
}
GIT_INLINE(void) status_entry_update_from_tree_entry(struct status_entry *e, const git_tree_entry *tree_entry)
{
assert(e && tree_entry);
git_oid_cpy(&e->head_oid, &tree_entry->oid);
}
GIT_INLINE(void) status_entry_update_from_index_entry(struct status_entry *e, const git_index_entry *index_entry)
{
assert(e && index_entry);
git_oid_cpy(&e->index_oid, &index_entry->oid);
e->mtime = index_entry->mtime;
}
static void status_entry_update_from_index(struct status_entry *e, git_index *index)
{
int idx;
git_index_entry *index_entry;
assert(e && index);
idx = git_index_find(index, e->path);
if (idx < 0)
return;
index_entry = git_index_get(index, idx);
status_entry_update_from_index_entry(e, index_entry);
}
static int status_entry_update_from_workdir(struct status_entry *e, const char* full_path)
{
struct stat filest;
if (p_stat(full_path, &filest) < 0) {
giterr_set(GITERR_OS, "Cannot access file '%s'", full_path);
return GIT_ENOTFOUND;
}
if (e->mtime.seconds == (git_time_t)filest.st_mtime)
git_oid_cpy(&e->wt_oid, &e->index_oid);
else
git_odb_hashfile(&e->wt_oid, full_path, GIT_OBJ_BLOB);
return 0;
}
static int status_entry_update_flags(struct status_entry *e)
{
git_oid zero;
int head_zero, index_zero, wt_zero;
memset(&zero, 0x0, sizeof(git_oid));
head_zero = git_oid_cmp(&zero, &e->head_oid);
index_zero = git_oid_cmp(&zero, &e->index_oid);
wt_zero = git_oid_cmp(&zero, &e->wt_oid);
if (head_zero == 0 && index_zero == 0 && wt_zero == 0)
return GIT_ENOTFOUND;
if (head_zero == 0 && index_zero != 0)
e->status_flags |= GIT_STATUS_INDEX_NEW;
else if (index_zero == 0 && head_zero != 0)
e->status_flags |= GIT_STATUS_INDEX_DELETED;
else if (git_oid_cmp(&e->head_oid, &e->index_oid) != 0)
e->status_flags |= GIT_STATUS_INDEX_MODIFIED;
if (index_zero == 0 && wt_zero != 0)
e->status_flags |= GIT_STATUS_WT_NEW;
else if (wt_zero == 0 && index_zero != 0)
e->status_flags |= GIT_STATUS_WT_DELETED;
else if (git_oid_cmp(&e->index_oid, &e->wt_oid) != 0)
e->status_flags |= GIT_STATUS_WT_MODIFIED;
return 0;
}
static int status_entry_is_ignorable(struct status_entry *e)
{ {
/* don't ignore files that exist in head or index already */ struct status_file_info *sfi = data;
return (e->status_flags == GIT_STATUS_WT_NEW);
}
static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignores, const char *path) sfi->count++;
{ sfi->status = status;
int ignored;
if (git_ignore__lookup(ignores, path, &ignored) < 0) if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
giterr_set(GITERR_INVALID,
"Ambiguous path '%s' given to git_status_file", sfi->expected);
return -1; return -1;
if (ignored)
/* toggle off WT_NEW and on IGNORED */
e->status_flags =
(e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED;
return 0;
}
static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path)
{
char *dir_sep;
const git_tree_entry *tree_entry;
git_tree *subtree;
int error;
dir_sep = strchr(path, '/');
if (!dir_sep) {
if ((tree_entry = git_tree_entry_byname(tree, path)) != NULL)
/* The leaf exists in the tree*/
status_entry_update_from_tree_entry(e, tree_entry);
return 0;
}
/* Retrieve subtree name */
*dir_sep = '\0';
if ((tree_entry = git_tree_entry_byname(tree, path)) == NULL)
return 0; /* The subtree doesn't exist in the tree*/
*dir_sep = '/';
/* Retreive subtree */
error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid);
if (!error) {
error = recurse_tree_entry(subtree, e, dir_sep+1);
git_tree_free(subtree);
} }
return error; return 0;
} }
int git_status_file( int git_status_file(
unsigned int *status_flags, git_repository *repo, const char *path) unsigned int *status_flags,
git_repository *repo,
const char *path)
{ {
struct status_entry *e; int error;
git_index *index = NULL; git_status_options opts;
git_buf temp_path = GIT_BUF_INIT; struct status_file_info sfi;
int error = 0;
git_tree *tree = NULL;
const char *workdir;
assert(status_flags && repo && path); assert(status_flags && repo && path);
if ((workdir = git_repository_workdir(repo)) == NULL) { memset(&sfi, 0, sizeof(sfi));
giterr_set(GITERR_INVALID, "Cannot get file status from bare repo"); if ((sfi.expected = git__strdup(path)) == NULL)
return -1; return -1;
}
if (git_buf_joinpath(&temp_path, workdir, path) < 0)
return -1;
if (git_path_isdir(temp_path.ptr)) {
giterr_set(GITERR_INVALID, "Cannot get file status for directory '%s'", temp_path.ptr);
git_buf_free(&temp_path);
return -1;
}
e = status_entry_new(NULL, path);
GITERR_CHECK_ALLOC(e);
/* Find file in Workdir */
if (git_path_exists(temp_path.ptr) == true &&
(error = status_entry_update_from_workdir(e, temp_path.ptr)) < 0)
goto cleanup;
/* Find file in Index */
if ((error = git_repository_index__weakptr(&index, repo)) < 0)
goto cleanup;
status_entry_update_from_index(e, index);
/* Try to find file in HEAD */ memset(&opts, 0, sizeof(opts));
if ((error = git_repository_head_tree(&tree, repo)) < 0) opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
goto cleanup; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
if (tree != NULL) { GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
if ((error = git_buf_sets(&temp_path, path)) < 0 || GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
(error = recurse_tree_entry(tree, e, temp_path.ptr)) < 0) opts.pathspec.count = 1;
goto cleanup; opts.pathspec.strings = &sfi.expected;
}
/* Determine status */
if ((error = status_entry_update_flags(e)) < 0)
giterr_set(GITERR_OS, "Cannot find file '%s' to determine status", path);
if (!error && status_entry_is_ignorable(e)) {
git_ignores ignores;
if ((error = git_ignore__for_path(repo, path, &ignores)) == 0) error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
error = status_entry_update_ignore(e, &ignores, path);
git_ignore__free(&ignores); if (!error && !sfi.count) {
giterr_set(GITERR_INVALID,
"Attempt to get status of nonexistent file '%s'", path);
error = GIT_ENOTFOUND;
} }
if (!error) *status_flags = sfi.status;
*status_flags = e->status_flags;
cleanup: git__free(sfi.expected);
git_buf_free(&temp_path);
git_tree_free(tree);
git__free(e);
return error; return error;
} }
int git_status_should_ignore( int git_status_should_ignore(
int *ignored, git_repository *repo, const char *path) int *ignored,
git_repository *repo,
const char *path)
{ {
int error; int error;
git_ignores ignores; git_ignores ignores;
......
...@@ -195,6 +195,33 @@ const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx) ...@@ -195,6 +195,33 @@ const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
return git_vector_get(&tree->entries, idx); return git_vector_get(&tree->entries, idx);
} }
int git_tree_entry_prefix_position(git_tree *tree, const char *path)
{
git_vector *entries = &tree->entries;
struct tree_key_search ksearch;
unsigned int at_pos;
ksearch.filename = path;
ksearch.filename_len = strlen(path);
/* Find tree entry with appropriate prefix */
git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);
for (; at_pos < entries->length; ++at_pos) {
const git_tree_entry *entry = entries->contents[at_pos];
if (homing_search_cmp(&ksearch, entry) < 0)
break;
}
for (; at_pos > 0; --at_pos) {
const git_tree_entry *entry = entries->contents[at_pos - 1];
if (homing_search_cmp(&ksearch, entry) > 0)
break;
}
return at_pos;
}
unsigned int git_tree_entrycount(git_tree *tree) unsigned int git_tree_entrycount(git_tree *tree)
{ {
assert(tree); assert(tree);
......
...@@ -38,4 +38,14 @@ GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e) ...@@ -38,4 +38,14 @@ GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e)
void git_tree__free(git_tree *tree); void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree, git_odb_object *obj); int git_tree__parse(git_tree *tree, git_odb_object *obj);
/**
* Lookup the first position in the tree with a given prefix.
*
* @param tree a previously loaded tree.
* @param prefix the beginning of a path to find in the tree.
* @return index of the first item at or after the given prefix.
*/
int git_tree_entry_prefix_position(git_tree *tree, const char *prefix);
#endif #endif
...@@ -209,4 +209,9 @@ GIT_INLINE(bool) git__isspace(int c) ...@@ -209,4 +209,9 @@ GIT_INLINE(bool) git__isspace(int c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v'); return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
} }
GIT_INLINE(bool) git__iswildcard(int c)
{
return (c == '*' || c == '?' || c == '[');
}
#endif /* INCLUDE_util_h__ */ #endif /* INCLUDE_util_h__ */
...@@ -116,8 +116,13 @@ void git_vector_sort(git_vector *v) ...@@ -116,8 +116,13 @@ void git_vector_sort(git_vector *v)
v->sorted = 1; v->sorted = 1;
} }
int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *key) int git_vector_bsearch3(
unsigned int *at_pos,
git_vector *v,
git_vector_cmp key_lookup,
const void *key)
{ {
int rval;
size_t pos; size_t pos;
assert(v && key && key_lookup); assert(v && key && key_lookup);
...@@ -127,13 +132,16 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke ...@@ -127,13 +132,16 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke
git_vector_sort(v); git_vector_sort(v);
if (git__bsearch(v->contents, v->length, key, key_lookup, &pos) >= 0) rval = git__bsearch(v->contents, v->length, key, key_lookup, &pos);
return (int)pos;
return GIT_ENOTFOUND; if (at_pos != NULL)
*at_pos = (unsigned int)pos;
return (rval >= 0) ? (int)pos : GIT_ENOTFOUND;
} }
int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key) int git_vector_search2(
git_vector *v, git_vector_cmp key_lookup, const void *key)
{ {
unsigned int i; unsigned int i;
...@@ -157,11 +165,6 @@ int git_vector_search(git_vector *v, const void *entry) ...@@ -157,11 +165,6 @@ int git_vector_search(git_vector *v, const void *entry)
return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry); return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);
} }
int git_vector_bsearch(git_vector *v, const void *key)
{
return git_vector_bsearch2(v, v->_cmp, key);
}
int git_vector_remove(git_vector *v, unsigned int idx) int git_vector_remove(git_vector *v, unsigned int idx)
{ {
unsigned int i; unsigned int i;
......
...@@ -26,13 +26,24 @@ void git_vector_free(git_vector *v); ...@@ -26,13 +26,24 @@ void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v); void git_vector_clear(git_vector *v);
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);
int git_vector_search(git_vector *v, const void *entry); int git_vector_search(git_vector *v, const void *entry);
int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key); int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
int git_vector_bsearch(git_vector *v, const void *entry); int git_vector_bsearch3(
int git_vector_bsearch2(git_vector *v, git_vector_cmp cmp, const void *key); unsigned int *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
void git_vector_sort(git_vector *v); GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)
{
return git_vector_bsearch3(NULL, v, v->_cmp, key);
}
GIT_INLINE(int) git_vector_bsearch2(
git_vector *v, git_vector_cmp cmp, const void *key)
{
return git_vector_bsearch3(NULL, v, cmp, key);
}
GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position) GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
{ {
......
...@@ -561,3 +561,53 @@ void test_core_buffer__10(void) ...@@ -561,3 +561,53 @@ void test_core_buffer__10(void)
git_buf_free(&a); git_buf_free(&a);
} }
void test_core_buffer__11(void)
{
git_buf a = GIT_BUF_INIT;
git_strarray t;
char *t1[] = { "nothing", "in", "common" };
char *t2[] = { "something", "something else", "some other" };
char *t3[] = { "something", "some fun", "no fun" };
char *t4[] = { "happy", "happier", "happiest" };
char *t5[] = { "happiest", "happier", "happy" };
char *t6[] = { "no", "nope", "" };
char *t7[] = { "", "doesn't matter" };
t.strings = t1;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "");
t.strings = t2;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "some");
t.strings = t3;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "");
t.strings = t4;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "happ");
t.strings = t5;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "happ");
t.strings = t6;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "");
t.strings = t7;
t.count = 3;
cl_git_pass(git_buf_common_prefix(&a, &t));
cl_assert_equal_s(a.ptr, "");
git_buf_free(&a);
}
...@@ -22,6 +22,8 @@ void test_diff_iterator__cleanup(void) ...@@ -22,6 +22,8 @@ void test_diff_iterator__cleanup(void)
static void tree_iterator_test( static void tree_iterator_test(
const char *sandbox, const char *sandbox,
const char *treeish, const char *treeish,
const char *start,
const char *end,
int expected_count, int expected_count,
const char **expected_values) const char **expected_values)
{ {
...@@ -32,7 +34,7 @@ static void tree_iterator_test( ...@@ -32,7 +34,7 @@ static void tree_iterator_test(
git_repository *repo = cl_git_sandbox_init(sandbox); git_repository *repo = cl_git_sandbox_init(sandbox);
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish)); cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
cl_git_pass(git_iterator_for_tree(repo, t, &i)); cl_git_pass(git_iterator_for_tree_range(&i, repo, t, start, end));
cl_git_pass(git_iterator_current(i, &entry)); cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) { while (entry != NULL) {
...@@ -74,7 +76,7 @@ const char *expected_tree_0[] = { ...@@ -74,7 +76,7 @@ const char *expected_tree_0[] = {
void test_diff_iterator__tree_0(void) void test_diff_iterator__tree_0(void)
{ {
tree_iterator_test("attr", "605812a", 16, expected_tree_0); tree_iterator_test("attr", "605812a", NULL, NULL, 16, expected_tree_0);
} }
/* results of: git ls-tree -r --name-only 6bab5c79 */ /* results of: git ls-tree -r --name-only 6bab5c79 */
...@@ -97,7 +99,7 @@ const char *expected_tree_1[] = { ...@@ -97,7 +99,7 @@ const char *expected_tree_1[] = {
void test_diff_iterator__tree_1(void) void test_diff_iterator__tree_1(void)
{ {
tree_iterator_test("attr", "6bab5c79cd5", 13, expected_tree_1); tree_iterator_test("attr", "6bab5c79cd5", NULL, NULL, 13, expected_tree_1);
} }
/* results of: git ls-tree -r --name-only 26a125ee1 */ /* results of: git ls-tree -r --name-only 26a125ee1 */
...@@ -119,7 +121,7 @@ const char *expected_tree_2[] = { ...@@ -119,7 +121,7 @@ const char *expected_tree_2[] = {
void test_diff_iterator__tree_2(void) void test_diff_iterator__tree_2(void)
{ {
tree_iterator_test("status", "26a125ee1", 12, expected_tree_2); tree_iterator_test("status", "26a125ee1", NULL, NULL, 12, expected_tree_2);
} }
/* $ git ls-tree -r --name-only 0017bd4ab1e */ /* $ git ls-tree -r --name-only 0017bd4ab1e */
...@@ -136,7 +138,7 @@ const char *expected_tree_3[] = { ...@@ -136,7 +138,7 @@ const char *expected_tree_3[] = {
void test_diff_iterator__tree_3(void) void test_diff_iterator__tree_3(void)
{ {
tree_iterator_test("status", "0017bd4ab1e", 8, expected_tree_3); tree_iterator_test("status", "0017bd4ab1e", NULL, NULL, 8, expected_tree_3);
} }
/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */ /* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */
...@@ -170,14 +172,77 @@ const char *expected_tree_4[] = { ...@@ -170,14 +172,77 @@ const char *expected_tree_4[] = {
void test_diff_iterator__tree_4(void) void test_diff_iterator__tree_4(void)
{ {
tree_iterator_test( tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b", "attr", "24fa9a9fc4e202313e24b648087495441dab432b", NULL, NULL,
23, expected_tree_4); 23, expected_tree_4);
} }
void test_diff_iterator__tree_4_ranged(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
"sub", "sub",
11, &expected_tree_4[12]);
}
const char *expected_tree_ranged_0[] = {
"gitattributes",
"macro_bad",
"macro_test",
"root_test1",
"root_test2",
"root_test3",
"root_test4.txt",
NULL
};
void test_diff_iterator__tree_ranged_0(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
"git", "root",
7, expected_tree_ranged_0);
}
const char *expected_tree_ranged_1[] = {
"sub/subdir_test2.txt",
NULL
};
void test_diff_iterator__tree_ranged_1(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
"sub/subdir_test2.txt", "sub/subdir_test2.txt",
1, expected_tree_ranged_1);
}
void test_diff_iterator__tree_range_empty_0(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
"empty", "empty", 0, NULL);
}
void test_diff_iterator__tree_range_empty_1(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
"z_empty_after", NULL, 0, NULL);
}
void test_diff_iterator__tree_range_empty_2(void)
{
tree_iterator_test(
"attr", "24fa9a9fc4e202313e24b648087495441dab432b",
NULL, ".aaa_empty_before", 0, NULL);
}
/* -- INDEX ITERATOR TESTS -- */ /* -- INDEX ITERATOR TESTS -- */
static void index_iterator_test( static void index_iterator_test(
const char *sandbox, const char *sandbox,
const char *start,
const char *end,
int expected_count, int expected_count,
const char **expected_names, const char **expected_names,
const char **expected_oids) const char **expected_oids)
...@@ -187,7 +252,7 @@ static void index_iterator_test( ...@@ -187,7 +252,7 @@ static void index_iterator_test(
int count = 0; int count = 0;
git_repository *repo = cl_git_sandbox_init(sandbox); git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_iterator_for_index(repo, &i)); cl_git_pass(git_iterator_for_index_range(&i, repo, start, end));
cl_git_pass(git_iterator_current(i, &entry)); cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) { while (entry != NULL) {
...@@ -197,7 +262,7 @@ static void index_iterator_test( ...@@ -197,7 +262,7 @@ static void index_iterator_test(
if (expected_oids != NULL) { if (expected_oids != NULL) {
git_oid oid; git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oids[count])); cl_git_pass(git_oid_fromstr(&oid, expected_oids[count]));
cl_assert(git_oid_cmp(&oid, &entry->oid) == 0); cl_assert_equal_i(git_oid_cmp(&oid, &entry->oid), 0);
} }
count++; count++;
...@@ -206,7 +271,7 @@ static void index_iterator_test( ...@@ -206,7 +271,7 @@ static void index_iterator_test(
git_iterator_free(i); git_iterator_free(i);
cl_assert(count == expected_count); cl_assert_equal_i(expected_count, count);
} }
static const char *expected_index_0[] = { static const char *expected_index_0[] = {
...@@ -263,7 +328,46 @@ static const char *expected_index_oids_0[] = { ...@@ -263,7 +328,46 @@ static const char *expected_index_oids_0[] = {
void test_diff_iterator__index_0(void) void test_diff_iterator__index_0(void)
{ {
index_iterator_test("attr", 23, expected_index_0, expected_index_oids_0); index_iterator_test(
"attr", NULL, NULL, 23, expected_index_0, expected_index_oids_0);
}
static const char *expected_index_range[] = {
"root_test1",
"root_test2",
"root_test3",
"root_test4.txt",
};
static const char *expected_index_oids_range[] = {
"45141a79a77842c59a63229403220a4e4be74e3d",
"4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
"108bb4e7fd7b16490dc33ff7d972151e73d7166e",
"fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
};
void test_diff_iterator__index_range(void)
{
index_iterator_test(
"attr", "root", "root", 4, expected_index_range, expected_index_oids_range);
}
void test_diff_iterator__index_range_empty_0(void)
{
index_iterator_test(
"attr", "empty", "empty", 0, NULL, NULL);
}
void test_diff_iterator__index_range_empty_1(void)
{
index_iterator_test(
"attr", "z_empty_after", NULL, 0, NULL, NULL);
}
void test_diff_iterator__index_range_empty_2(void)
{
index_iterator_test(
"attr", NULL, ".aaa_empty_before", 0, NULL, NULL);
} }
static const char *expected_index_1[] = { static const char *expected_index_1[] = {
...@@ -300,7 +404,8 @@ static const char* expected_index_oids_1[] = { ...@@ -300,7 +404,8 @@ static const char* expected_index_oids_1[] = {
void test_diff_iterator__index_1(void) void test_diff_iterator__index_1(void)
{ {
index_iterator_test("status", 13, expected_index_1, expected_index_oids_1); index_iterator_test(
"status", NULL, NULL, 13, expected_index_1, expected_index_oids_1);
} }
...@@ -308,6 +413,8 @@ void test_diff_iterator__index_1(void) ...@@ -308,6 +413,8 @@ void test_diff_iterator__index_1(void)
static void workdir_iterator_test( static void workdir_iterator_test(
const char *sandbox, const char *sandbox,
const char *start,
const char *end,
int expected_count, int expected_count,
int expected_ignores, int expected_ignores,
const char **expected_names, const char **expected_names,
...@@ -318,7 +425,7 @@ static void workdir_iterator_test( ...@@ -318,7 +425,7 @@ static void workdir_iterator_test(
int count = 0, count_all = 0; int count = 0, count_all = 0;
git_repository *repo = cl_git_sandbox_init(sandbox); git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_iterator_for_workdir(repo, &i)); cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
cl_git_pass(git_iterator_current(i, &entry)); cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) { while (entry != NULL) {
...@@ -350,7 +457,7 @@ static void workdir_iterator_test( ...@@ -350,7 +457,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void) void test_diff_iterator__workdir_0(void)
{ {
workdir_iterator_test("attr", 25, 2, NULL, "ign"); workdir_iterator_test("attr", NULL, NULL, 25, 2, NULL, "ign");
} }
static const char *status_paths[] = { static const char *status_paths[] = {
...@@ -372,5 +479,92 @@ static const char *status_paths[] = { ...@@ -372,5 +479,92 @@ static const char *status_paths[] = {
void test_diff_iterator__workdir_1(void) void test_diff_iterator__workdir_1(void)
{ {
workdir_iterator_test("status", 12, 1, status_paths, "ignored_file"); workdir_iterator_test(
"status", NULL, NULL, 12, 1, status_paths, "ignored_file");
}
static const char *status_paths_range_0[] = {
"staged_changes",
"staged_changes_modified_file",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_modified_file",
NULL
};
void test_diff_iterator__workdir_1_ranged_0(void)
{
workdir_iterator_test(
"status", "staged", "staged", 5, 0, status_paths_range_0, NULL);
}
static const char *status_paths_range_1[] = {
"modified_file", NULL
};
void test_diff_iterator__workdir_1_ranged_1(void)
{
workdir_iterator_test(
"status", "modified_file", "modified_file",
1, 0, status_paths_range_1, NULL);
}
static const char *status_paths_range_3[] = {
"subdir.txt",
"subdir/current_file",
"subdir/modified_file",
NULL
};
void test_diff_iterator__workdir_1_ranged_3(void)
{
workdir_iterator_test(
"status", "subdir", "subdir/modified_file",
3, 0, status_paths_range_3, NULL);
}
static const char *status_paths_range_4[] = {
"subdir/current_file",
"subdir/modified_file",
"subdir/new_file",
NULL
};
void test_diff_iterator__workdir_1_ranged_4(void)
{
workdir_iterator_test(
"status", "subdir/", NULL, 3, 0, status_paths_range_4, NULL);
}
static const char *status_paths_range_5[] = {
"subdir/modified_file",
NULL
};
void test_diff_iterator__workdir_1_ranged_5(void)
{
workdir_iterator_test(
"status", "subdir/modified_file", "subdir/modified_file",
1, 0, status_paths_range_5, NULL);
}
void test_diff_iterator__workdir_1_ranged_empty_0(void)
{
workdir_iterator_test(
"status", "z_does_not_exist", NULL,
0, 0, NULL, NULL);
}
void test_diff_iterator__workdir_1_ranged_empty_1(void)
{
workdir_iterator_test(
"status", "empty", "empty",
0, 0, NULL, NULL);
}
void test_diff_iterator__workdir_1_ranged_empty_2(void)
{
workdir_iterator_test(
"status", NULL, "aaaa_empty_before",
0, 0, NULL, NULL);
} }
...@@ -103,3 +103,10 @@ void test_status_submodules__1(void) ...@@ -103,3 +103,10 @@ void test_status_submodules__1(void)
cl_assert(index == 6); cl_assert(index == 6);
} }
void test_status_submodules__single_file(void)
{
unsigned int status;
cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
cl_assert(status == 0);
}
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