Commit 947fad4f by Vicent Martí

Merge pull request #1624 from libgit2/vmg/full-ref-iterator

Breaking RefDB changes
parents 9d9fff3c efd5a4e1
......@@ -257,11 +257,6 @@ GIT_EXTERN(int) git_reference_set_target(
* The new name will be checked for validity.
* See `git_reference_create_symbolic()` for rules about valid names.
*
* On success, the given git_reference will be deleted from disk and a
* new `git_reference` will be returned.
*
* The reference will be immediately renamed in-memory and on disk.
*
* If the `force` flag is not enabled, and there's already
* a reference with the given name, the renaming will fail.
*
......@@ -277,7 +272,7 @@ GIT_EXTERN(int) git_reference_set_target(
*
*/
GIT_EXTERN(int) git_reference_rename(
git_reference **out,
git_reference **new_ref,
git_reference *ref,
const char *new_name,
int force);
......@@ -308,7 +303,8 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
*/
GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo);
typedef int (*git_reference_foreach_cb)(const char *refname, void *payload);
typedef int (*git_reference_foreach_cb)(git_reference *reference, void *payload);
typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload);
/**
* Perform a callback on each reference in the repository.
......@@ -328,6 +324,11 @@ GIT_EXTERN(int) git_reference_foreach(
git_reference_foreach_cb callback,
void *payload);
GIT_EXTERN(int) git_reference_foreach_name(
git_repository *repo,
git_reference_foreach_name_cb callback,
void *payload);
/**
* Free the given reference.
*
......@@ -351,7 +352,9 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
* @param repo the repository
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo);
GIT_EXTERN(int) git_reference_iterator_new(
git_reference_iterator **out,
git_repository *repo);
/**
* Create an iterator for the repo's references that match the
......@@ -362,16 +365,21 @@ GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_rep
* @param glob the glob to match against the reference names
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob);
GIT_EXTERN(int) git_reference_iterator_glob_new(
git_reference_iterator **out,
git_repository *repo,
const char *glob);
/**
* Get the next reference name
* Get the next reference
*
* @param out pointer in which to store the string
* @param out pointer in which to store the reference
* @param iter the iterator
* @param 0, GIT_ITEROVER if there are no more; or an error code
*/
GIT_EXTERN(int) git_reference_next(const char **out, git_reference_iterator *iter);
GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
/**
* Free the iterator and its associated resources
......@@ -401,7 +409,7 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter);
GIT_EXTERN(int) git_reference_foreach_glob(
git_repository *repo,
const char *glob,
git_reference_foreach_cb callback,
git_reference_foreach_name_cb callback,
void *payload);
/**
......
......@@ -33,8 +33,27 @@ GIT_BEGIN_DECL
* and assign `iter->parent.backend` to your `git_refdb_backend`.
*/
struct git_reference_iterator {
git_refdb_backend *backend;
char *glob;
git_refdb *db;
/**
* Return the current reference and advance the iterator.
*/
int (*next)(
git_reference **ref,
git_reference_iterator *iter);
/**
* Return the name of the current reference and advance the iterator
*/
int (*next_name)(
const char **ref_name,
git_reference_iterator *iter);
/**
* Free the iterator
*/
void (*free)(
git_reference_iterator *iter);
};
/** An instance for a custom backend */
......@@ -66,46 +85,25 @@ struct git_refdb_backend {
*/
int (*iterator)(
git_reference_iterator **iter,
struct git_refdb_backend *backend);
/**
* Allocate a glob-filtering iterator object for the backend.
*
* A refdb implementation may provide this function. If it's
* not available, the glob matching will be done by the frontend.
*/
int (*iterator_glob)(
git_reference_iterator **iter,
struct git_refdb_backend *backend,
const char *glob);
/**
* Return the current value and advance the iterator.
*
* A refdb implementation must provide this function.
*/
int (*next)(
const char **name,
git_reference_iterator *iter);
/**
* Free the iterator
*
* A refdb implementation must provide this function.
*/
void (*iterator_free)(
git_reference_iterator *iter);
/*
* Writes the given reference to the refdb. A refdb implementation
* must provide this function.
*/
int (*write)(git_refdb_backend *backend, const git_reference *ref);
int (*write)(git_refdb_backend *backend,
const git_reference *ref, int force);
int (*rename)(
git_reference **out, git_refdb_backend *backend,
const char *old_name, const char *new_name, int force);
/**
* Deletes the given reference from the refdb. A refdb implementation
* must provide this function.
*/
int (*delete)(git_refdb_backend *backend, const git_reference *ref);
int (*delete)(git_refdb_backend *backend, const char *ref_name);
/**
* Suggests that the given refdb compress or optimize its references.
......
......@@ -131,28 +131,32 @@ int git_branch_foreach(
void *payload)
{
git_reference_iterator *iter;
const char *name;
git_reference *ref;
int error;
if (git_reference_iterator_new(&iter, repo) < 0)
return -1;
while ((error = git_reference_next(&name, iter)) == 0) {
while ((error = git_reference_next(&ref, iter)) == 0) {
if (list_flags & GIT_BRANCH_LOCAL &&
git__prefixcmp(name, GIT_REFS_HEADS_DIR) == 0) {
if (callback(name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, payload)) {
git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) {
if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR),
GIT_BRANCH_LOCAL, payload)) {
error = GIT_EUSER;
break;
}
}
if (list_flags & GIT_BRANCH_REMOTE &&
git__prefixcmp(name, GIT_REFS_REMOTES_DIR) == 0) {
if (callback(name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, payload)) {
git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0) {
if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR),
GIT_BRANCH_REMOTE, payload)) {
error = GIT_EUSER;
break;
}
}
git_reference_free(ref);
}
if (error == GIT_ITEROVER)
......@@ -160,7 +164,6 @@ int git_branch_foreach(
git_reference_iterator_free(iter);
return error;
}
int git_branch_move(
......@@ -179,18 +182,21 @@ int git_branch_move(
if (!git_reference_is_branch(branch))
return not_a_local_branch(git_reference_name(branch));
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0 ||
(error = git_buf_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 ||
(error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0)
error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name);
if (error < 0)
goto done;
git_buf_printf(&old_config_section,
"branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
git_buf_printf(&new_config_section, "branch.%s", new_branch_name);
if ((error = git_config_rename_section(git_reference_owner(branch),
git_buf_cstr(&old_config_section),
git_buf_cstr(&new_config_section))) < 0)
goto done;
if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0)
goto done;
error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force);
done:
git_buf_free(&new_reference_name);
......
......@@ -495,12 +495,9 @@ static int checkout_action(
if (cmp == 0) {
if (wd->mode == GIT_FILEMODE_TREE) {
/* case 2 - entry prefixed by workdir tree */
if ((error = git_iterator_advance_into(&wd, workdir)) < 0) {
if (error != GIT_ENOTFOUND ||
git_iterator_advance(&wd, workdir) < 0)
error = git_iterator_advance_into_or_over(&wd, workdir);
if (error && error != GIT_ITEROVER)
goto fail;
}
*wditem_ptr = wd;
continue;
}
......@@ -515,8 +512,10 @@ static int checkout_action(
}
/* case 1 - handle wd item (if it matches pathspec) */
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
git_iterator_advance(&wd, workdir) < 0)
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0)
goto fail;
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
error != GIT_ITEROVER)
goto fail;
*wditem_ptr = wd;
......@@ -539,8 +538,9 @@ static int checkout_action(
if (delta->status == GIT_DELTA_TYPECHANGE) {
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
act = checkout_action_with_wd(data, delta, wd);
if (git_iterator_advance_into(&wd, workdir) < 0)
wd = NULL;
if ((error = git_iterator_advance_into(&wd, workdir)) < 0 &&
error != GIT_ENOTFOUND)
goto fail;
*wditem_ptr = wd;
return act;
}
......@@ -550,8 +550,9 @@ static int checkout_action(
delta->old_file.mode == GIT_FILEMODE_COMMIT)
{
act = checkout_action_with_wd(data, delta, wd);
if (git_iterator_advance(&wd, workdir) < 0)
wd = NULL;
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
error != GIT_ITEROVER)
goto fail;
*wditem_ptr = wd;
return act;
}
......@@ -582,6 +583,9 @@ static int checkout_remaining_wd_items(
error = git_iterator_advance(&wd, workdir);
}
if (error == GIT_ITEROVER)
error = 0;
return error;
}
......@@ -603,7 +607,8 @@ static int checkout_get_actions(
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
return -1;
if ((error = git_iterator_current(&wditem, workdir)) < 0)
if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
error != GIT_ITEROVER)
goto fail;
deltas = &data->diff->deltas;
......
......@@ -241,7 +241,7 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
}
/* Not master. Check all the other refs. */
if (git_reference_foreach(
if (git_reference_foreach_name(
repo,
reference_matches_remote_head,
&head_info) < 0)
......
......@@ -796,6 +796,9 @@ static int diff_scan_inside_untracked_dir(
done:
git_buf_free(&base);
if (error == GIT_ITEROVER)
error = 0;
return error;
}
......@@ -981,8 +984,11 @@ static int handle_matched_item(
{
int error = 0;
if (!(error = maybe_modified(diff, info)) &&
!(error = git_iterator_advance(&info->oitem, info->old_iter)))
if ((error = maybe_modified(diff, info)) < 0)
return error;
if (!(error = git_iterator_advance(&info->oitem, info->old_iter)) ||
error == GIT_ITEROVER)
error = git_iterator_advance(&info->nitem, info->new_iter);
return error;
......@@ -1011,15 +1017,22 @@ int git_diff__from_iterators(
/* make iterators have matching icase behavior */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
if (!(error = git_iterator_set_ignore_case(old_iter, true)))
error = git_iterator_set_ignore_case(new_iter, true);
if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
(error = git_iterator_set_ignore_case(new_iter, true)) < 0)
goto cleanup;
}
/* finish initialization */
if (!error &&
!(error = diff_list_apply_options(diff, opts)) &&
!(error = git_iterator_current(&info.oitem, old_iter)))
error = git_iterator_current(&info.nitem, new_iter);
if ((error = diff_list_apply_options(diff, opts)) < 0)
goto cleanup;
if ((error = git_iterator_current(&info.oitem, old_iter)) < 0 &&
error != GIT_ITEROVER)
goto cleanup;
if ((error = git_iterator_current(&info.nitem, new_iter)) < 0 &&
error != GIT_ITEROVER)
goto cleanup;
error = 0;
/* run iterators building diffs */
while (!error && (info.oitem || info.nitem)) {
......@@ -1041,8 +1054,13 @@ int git_diff__from_iterators(
*/
else
error = handle_matched_item(diff, &info);
/* because we are iterating over two lists, ignore ITEROVER */
if (error == GIT_ITEROVER)
error = 0;
}
cleanup:
if (!error)
*diff_ptr = diff;
else
......
......@@ -46,6 +46,9 @@
#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
#define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
#define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
#define iterator__end(I) ((git_iterator *)(I))->end
#define iterator__past_end(I,PATH) \
(iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
......@@ -68,6 +71,8 @@ static int iterator__reset_range(
GITERR_CHECK_ALLOC(iter->end);
}
iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
return 0;
}
......@@ -109,7 +114,7 @@ static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
{
GIT_UNUSED(i);
iterator__clear_entry(e);
return 0;
return GIT_ITEROVER;
}
static int empty_iterator__seek(git_iterator *i, const char *p)
......@@ -193,6 +198,7 @@ typedef struct {
git_buf path;
int path_ambiguities;
bool path_has_filename;
bool entry_is_current;
int (*strncomp)(const char *a, const char *b, size_t sz);
} tree_iterator;
......@@ -266,9 +272,28 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
((tree_iterator *)p)->strncomp);
}
static bool tree_iterator__move_to_next(
tree_iterator *ti, tree_iterator_frame *tf)
{
if (tf->next > tf->current + 1)
ti->path_ambiguities--;
if (!tf->up) { /* at root */
tf->current = tf->next;
return false;
}
for (; tf->current < tf->next; tf->current++) {
git_tree_free(tf->entries[tf->current]->tree);
tf->entries[tf->current]->tree = NULL;
}
return (tf->current < tf->n_entries);
}
static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
{
int error;
int error = 0;
const git_tree_entry *te, *last = NULL;
tf->next = tf->current;
......@@ -279,18 +304,23 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
break;
/* load trees for items in [current,next) range */
if (git_tree_entry__is_tree(te) &&
(error = git_tree_lookup(
&tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0)
return error;
/* try to load trees for items in [current,next) range */
if (!error && git_tree_entry__is_tree(te))
error = git_tree_lookup(
&tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
}
if (tf->next > tf->current + 1)
ti->path_ambiguities++;
/* if a tree lookup failed, advance over this span and return failure */
if (error < 0) {
tree_iterator__move_to_next(ti, tf);
return error;
}
if (last && !tree_iterator__current_filename(ti, last))
return -1;
return -1; /* must have been allocation failure */
return 0;
}
......@@ -308,7 +338,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
size_t i, n_entries = 0;
if (head->current >= head->n_entries || !head->entries[head->current]->tree)
return 0;
return GIT_ITEROVER;
for (i = head->current; i < head->next; ++i)
n_entries += git_tree_entrycount(head->entries[i]->tree);
......@@ -359,7 +389,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
}
}
ti->path_has_filename = false;
ti->path_has_filename = ti->entry_is_current = false;
if ((error = tree_iterator__set_next(ti, tf)) < 0)
return error;
......@@ -371,25 +401,6 @@ static int tree_iterator__push_frame(tree_iterator *ti)
return 0;
}
static bool tree_iterator__move_to_next(
tree_iterator *ti, tree_iterator_frame *tf)
{
if (tf->next > tf->current + 1)
ti->path_ambiguities--;
if (!tf->up) { /* at root */
tf->current = tf->next;
return false;
}
for (; tf->current < tf->next; tf->current++) {
git_tree_free(tf->entries[tf->current]->tree);
tf->entries[tf->current]->tree = NULL;
}
return (tf->current < tf->n_entries);
}
static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
{
tree_iterator_frame *tf = ti->head;
......@@ -412,7 +423,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
return true;
}
static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
{
while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
......@@ -421,21 +432,17 @@ static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
ti->path_ambiguities = 0;
git_buf_clear(&ti->path);
}
return 0;
}
static int tree_iterator__current(
const git_index_entry **entry, git_iterator *self)
static int tree_iterator__update_entry(tree_iterator *ti)
{
tree_iterator *ti = (tree_iterator *)self;
tree_iterator_frame *tf = ti->head;
tree_iterator_frame *tf;
const git_tree_entry *te;
iterator__clear_entry(entry);
if (tf->current >= tf->n_entries)
if (ti->entry_is_current)
return 0;
tf = ti->head;
te = tf->entries[tf->current]->te;
ti->entry.mode = te->attr;
......@@ -447,12 +454,36 @@ static int tree_iterator__current(
if (ti->path_ambiguities > 0)
tree_iterator__rewrite_filename(ti);
if (iterator__past_end(ti, ti->entry.path))
return tree_iterator__pop_all(ti, true, false);
if (iterator__past_end(ti, ti->entry.path)) {
tree_iterator__pop_all(ti, true, false);
return GIT_ITEROVER;
}
ti->entry_is_current = true;
return 0;
}
static int tree_iterator__current(
const git_index_entry **entry, git_iterator *self)
{
int error;
tree_iterator *ti = (tree_iterator *)self;
tree_iterator_frame *tf = ti->head;
iterator__clear_entry(entry);
if (tf->current >= tf->n_entries)
return GIT_ITEROVER;
if ((error = tree_iterator__update_entry(ti)) < 0)
return error;
if (entry)
*entry = &ti->entry;
ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
return 0;
}
......@@ -464,8 +495,10 @@ static int tree_iterator__advance_into(
iterator__clear_entry(entry);
if (tree_iterator__at_tree(ti) &&
!(error = tree_iterator__push_frame(ti)))
if (tree_iterator__at_tree(ti))
error = tree_iterator__push_frame(ti);
if (!error && entry)
error = tree_iterator__current(entry, self);
return error;
......@@ -480,8 +513,11 @@ static int tree_iterator__advance(
iterator__clear_entry(entry);
if (tf->current > tf->n_entries)
return 0;
if (tf->current >= tf->n_entries)
return GIT_ITEROVER;
if (!iterator__has_been_accessed(ti))
return tree_iterator__current(entry, self);
if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
tree_iterator__at_tree(ti))
......@@ -489,7 +525,7 @@ static int tree_iterator__advance(
if (ti->path_has_filename) {
git_buf_rtruncate_at_char(&ti->path, '/');
ti->path_has_filename = false;
ti->path_has_filename = ti->entry_is_current = false;
}
/* scan forward and up, advancing in frame or popping frame when done */
......@@ -699,7 +735,9 @@ static int index_iterator__current(
if (entry)
*entry = ie;
return 0;
ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
return (ie != NULL) ? 0 : GIT_ITEROVER;
}
static int index_iterator__at_end(git_iterator *self)
......@@ -715,6 +753,9 @@ static int index_iterator__advance(
size_t entrycount = git_index_entrycount(ii->index);
const git_index_entry *ie;
if (!iterator__has_been_accessed(ii))
return index_iterator__current(entry, self);
if (index_iterator__at_tree(ii)) {
if (iterator__do_autoexpand(ii)) {
ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
......@@ -906,6 +947,8 @@ static void fs_iterator__pop_frame(
}
static int fs_iterator__update_entry(fs_iterator *fi);
static int fs_iterator__advance_over(
const git_index_entry **entry, git_iterator *self);
static int fs_iterator__entry_cmp(const void *i, const void *item)
{
......@@ -945,7 +988,13 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
fi->base.start, fi->base.end, &ff->entries);
if (error < 0 || ff->entries.length == 0) {
if (error < 0) {
fs_iterator__free_frame(ff);
fs_iterator__advance_over(NULL, (git_iterator *)fi);
return error;
}
if (ff->entries.length == 0) {
fs_iterator__free_frame(ff);
return GIT_ENOTFOUND;
}
......@@ -966,9 +1015,14 @@ static int fs_iterator__current(
const git_index_entry **entry, git_iterator *self)
{
fs_iterator *fi = (fs_iterator *)self;
const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
if (entry)
*entry = (fi->entry.path == NULL) ? NULL : &fi->entry;
return 0;
*entry = fe;
fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
return (fe != NULL) ? 0 : GIT_ITEROVER;
}
static int fs_iterator__at_end(git_iterator *self)
......@@ -998,6 +1052,9 @@ static int fs_iterator__advance_into(
if (!error && entry)
error = fs_iterator__current(entry, iter);
if (!error && !fi->entry.path)
error = GIT_ITEROVER;
return error;
}
......@@ -1035,6 +1092,9 @@ static int fs_iterator__advance(
{
fs_iterator *fi = (fs_iterator *)self;
if (!iterator__has_been_accessed(fi))
return fs_iterator__current(entry, self);
/* given include_trees & autoexpand, we might have to go into a tree */
if (iterator__do_autoexpand(fi) &&
fi->entry.path != NULL &&
......@@ -1063,18 +1123,23 @@ static int fs_iterator__seek(git_iterator *self, const char *prefix)
static int fs_iterator__reset(
git_iterator *self, const char *start, const char *end)
{
int error;
fs_iterator *fi = (fs_iterator *)self;
while (fi->stack != NULL && fi->stack->next != NULL)
fs_iterator__pop_frame(fi, fi->stack, false);
fi->depth = 0;
if (iterator__reset_range(self, start, end) < 0)
return -1;
if ((error = iterator__reset_range(self, start, end)) < 0)
return error;
fs_iterator__seek_frame_start(fi, fi->stack);
return fs_iterator__update_entry(fi);
error = fs_iterator__update_entry(fi);
if (error == GIT_ITEROVER)
error = 0;
return error;
}
static void fs_iterator__free(git_iterator *self)
......@@ -1089,18 +1154,23 @@ static void fs_iterator__free(git_iterator *self)
static int fs_iterator__update_entry(fs_iterator *fi)
{
git_path_with_stat *ps =
git_vector_get(&fi->stack->entries, fi->stack->index);
git_path_with_stat *ps;
git_buf_truncate(&fi->path, fi->root_len);
memset(&fi->entry, 0, sizeof(fi->entry));
if (!fi->stack)
return GIT_ITEROVER;
ps = git_vector_get(&fi->stack->entries, fi->stack->index);
if (!ps)
return 0;
return GIT_ITEROVER;
git_buf_truncate(&fi->path, fi->root_len);
if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
return -1;
if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
return 0;
return GIT_ITEROVER;
fi->entry.path = ps->path;
git_index_entry__init_from_stat(&fi->entry, &ps->st);
......@@ -1114,8 +1184,13 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
/* if this is a tree and trees aren't included, then skip */
if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi))
return git_iterator_advance(NULL, (git_iterator *)fi);
if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
if (error != GIT_ENOTFOUND)
return error;
giterr_clear();
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
}
return 0;
}
......@@ -1131,14 +1206,15 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) {
if ((error = fs_iterator__expand_dir(fi)) < 0) {
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
giterr_clear();
error = 0;
}
if (error) {
} else {
git_iterator_free((git_iterator *)fi);
fi = NULL;
}
}
*out = (git_iterator *)fi;
return error;
......
......@@ -142,9 +142,9 @@ GIT_INLINE(int) git_iterator_advance(
*
* If the current item is not a tree, this is a no-op.
*
* For working directory iterators only, a tree (i.e. directory) can be
* empty. In that case, this function returns GIT_ENOTFOUND and does not
* advance. That can't happen for tree and index iterators.
* For filesystem and working directory iterators, a tree (i.e. directory)
* can be empty. In that case, this function returns GIT_ENOTFOUND and
* does not advance. That can't happen for tree and index iterators.
*/
GIT_INLINE(int) git_iterator_advance_into(
const git_index_entry **entry, git_iterator *iter)
......@@ -152,18 +152,50 @@ GIT_INLINE(int) git_iterator_advance_into(
return iter->cb->advance_into(entry, iter);
}
/**
* Advance into a tree or skip over it if it is empty.
*
* Because `git_iterator_advance_into` may return GIT_ENOTFOUND if the
* directory is empty (only with filesystem and working directory
* iterators) and a common response is to just call `git_iterator_advance`
* when that happens, this bundles the two into a single simple call.
*/
GIT_INLINE(int) git_iterator_advance_into_or_over(
const git_index_entry **entry, git_iterator *iter)
{
int error = iter->cb->advance_into(entry, iter);
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = iter->cb->advance(entry, iter);
}
return error;
}
/* Seek is currently unimplemented */
GIT_INLINE(int) git_iterator_seek(
git_iterator *iter, const char *prefix)
{
return iter->cb->seek(iter, prefix);
}
/**
* Go back to the start of the iteration.
*
* This resets the iterator to the start of the iteration. It also allows
* you to reset the `start` and `end` pathname boundaries of the iteration
* when doing so.
*/
GIT_INLINE(int) git_iterator_reset(
git_iterator *iter, const char *start, const char *end)
{
return iter->cb->reset(iter, start, end);
}
/**
* Check if the iterator is at the end
*
* @return 0 if not at end, >0 if at end
*/
GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
{
return iter->cb->at_end(iter);
......
......@@ -1259,7 +1259,8 @@ int git_merge_diff_list__find_differences(
/* Set up the iterators */
for (i = 0; i < 3; i++) {
if ((error = git_iterator_current(&items[i], iterators[i])) < 0)
error = git_iterator_current(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
......@@ -1313,11 +1314,16 @@ int git_merge_diff_list__find_differences(
error = merge_index_insert_conflict(diff_list, &df_data, cur_items);
else
error = merge_index_insert_unmodified(diff_list, cur_items);
if (error < 0)
goto done;
/* Advance each iterator that participated */
for (i = 0; i < 3; i++) {
if (cur_items[i] != NULL &&
(error = git_iterator_advance(&items[i], iterators[i])) < 0)
if (cur_items[i] == NULL)
continue;
error = git_iterator_advance(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
}
......@@ -1326,6 +1332,9 @@ done:
for (i = 0; i < 3; i++)
git_iterator_free(iterators[i]);
if (error == GIT_ITEROVER)
error = 0;
return error;
}
......
......@@ -647,18 +647,12 @@ int git_note_next(
const git_index_entry *item;
if ((error = git_iterator_current(&item, it)) < 0)
goto exit;
return error;
if (item != NULL) {
git_oid_cpy(note_id, &item->oid);
error = process_entry_path(item->path, annotated_id);
if (error >= 0)
error = git_iterator_advance(NULL, it);
} else {
error = GIT_ITEROVER;
}
if (!(error = process_entry_path(item->path, annotated_id)))
git_iterator_advance(NULL, it);
exit:
return error;
}
......@@ -86,7 +86,7 @@ int git_refdb_compress(git_refdb *db)
return 0;
}
static void refdb_free(git_refdb *db)
void git_refdb__free(git_refdb *db)
{
refdb_free_backend(db);
git__free(db);
......@@ -97,7 +97,7 @@ void git_refdb_free(git_refdb *db)
if (db == NULL)
return;
GIT_REFCOUNT_DEC(db, refdb_free);
GIT_REFCOUNT_DEC(db, git_refdb__free);
}
int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
......@@ -114,90 +114,91 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
assert(db && db->backend && out && ref_name);
if (!(error = db->backend->lookup(&ref, db->backend, ref_name))) {
error = db->backend->lookup(&ref, db->backend, ref_name);
if (error < 0)
return error;
GIT_REFCOUNT_INC(db);
ref->db = db;
*out = ref;
} else {
*out = NULL;
}
return error;
*out = ref;
return 0;
}
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db)
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
{
if (!db->backend || !db->backend->iterator) {
giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators");
return -1;
}
if (db->backend->iterator(out, db->backend) < 0)
if (db->backend->iterator(out, db->backend, glob) < 0)
return -1;
GIT_REFCOUNT_INC(db);
(*out)->db = db;
return 0;
}
int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob)
int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter)
{
if (!db->backend) {
giterr_set(GITERR_REFERENCE, "There are no backends loaded");
return -1;
}
if (db->backend->iterator_glob)
return db->backend->iterator_glob(out, db->backend, glob);
int error;
/* If the backend doesn't support glob-filtering themselves, we have to do it */
if (db->backend->iterator(out, db->backend) < 0)
return -1;
if ((error = iter->next(out, iter)) < 0)
return error;
(*out)->glob = git__strdup(glob);
if (!(*out)->glob) {
db->backend->iterator_free(*out);
return -1;
}
GIT_REFCOUNT_INC(iter->db);
(*out)->db = iter->db;
return 0;
}
int git_refdb_next(const char **out, git_reference_iterator *iter)
int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
{
int error;
if (!iter->glob)
return iter->backend->next(out, iter);
/* If the iterator has a glob, we need to filter */
while ((error = iter->backend->next(out, iter)) == 0) {
if (!p_fnmatch(iter->glob, *out, 0))
break;
}
return error;
return iter->next_name(out, iter);
}
void git_refdb_iterator_free(git_reference_iterator *iter)
{
git__free(iter->glob);
iter->backend->iterator_free(iter);
GIT_REFCOUNT_DEC(iter->db, git_refdb__free);
iter->free(iter);
}
struct glob_cb_data {
const char *glob;
git_reference_foreach_cb callback;
void *payload;
};
int git_refdb_write(git_refdb *db, const git_reference *ref)
int git_refdb_write(git_refdb *db, git_reference *ref, int force)
{
assert(db && db->backend);
return db->backend->write(db->backend, ref);
GIT_REFCOUNT_INC(db);
ref->db = db;
return db->backend->write(db->backend, ref, force);
}
int git_refdb_delete(struct git_refdb *db, const git_reference *ref)
int git_refdb_rename(
git_reference **out,
git_refdb *db,
const char *old_name,
const char *new_name,
int force)
{
int error;
assert(db && db->backend);
error = db->backend->rename(out, db->backend, old_name, new_name, force);
if (error < 0)
return error;
if (out) {
GIT_REFCOUNT_INC(db);
(*out)->db = db;
}
return db->backend->delete(db->backend, ref);
return 0;
}
int git_refdb_delete(struct git_refdb *db, const char *ref_name)
{
assert(db && db->backend);
return db->backend->delete(db->backend, ref_name);
}
......@@ -16,6 +16,8 @@ struct git_refdb {
git_refdb_backend *backend;
};
void git_refdb__free(git_refdb *db);
int git_refdb_exists(
int *exists,
git_refdb *refdb,
......@@ -26,13 +28,19 @@ int git_refdb_lookup(
git_refdb *refdb,
const char *ref_name);
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db);
int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob);
int git_refdb_next(const char **out, git_reference_iterator *iter);
int git_refdb_rename(
git_reference **out,
git_refdb *db,
const char *old_name,
const char *new_name,
int force);
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob);
int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter);
int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter);
void git_refdb_iterator_free(git_reference_iterator *iter);
int git_refdb_write(git_refdb *refdb, const git_reference *ref);
int git_refdb_delete(git_refdb *refdb, const git_reference *ref);
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
int git_refdb_delete(git_refdb *refdb, const char *ref_name);
#endif
......@@ -29,7 +29,8 @@ GIT__USE_STRMAP;
enum {
PACKREF_HAS_PEEL = 1,
PACKREF_WAS_LOOSE = 2,
PACKREF_CANNOT_PEEL = 4
PACKREF_CANNOT_PEEL = 4,
PACKREF_SHADOWED = 8,
};
enum {
......@@ -115,11 +116,8 @@ static int packed_parse_oid(
git_oid_cpy(&ref->oid, &id);
ref->flags = 0;
*ref_out = ref;
*buffer_out = refname_end + 1;
return 0;
corrupt:
......@@ -552,139 +550,239 @@ static int refdb_fs_backend__lookup(
return result;
}
struct dirent_list_data {
refdb_fs_backend *backend;
size_t repo_path_len;
unsigned int list_type:2;
git_reference_foreach_cb callback;
void *callback_payload;
int callback_error;
};
typedef struct {
git_reference_iterator parent;
unsigned int loose;
/* packed */
git_strmap *h;
khiter_t k;
/* loose */
git_iterator *fsiter;
git_buf buf;
char *glob;
git_vector loose;
unsigned int loose_pos;
khiter_t packed_pos;
} refdb_fs_iter;
static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend)
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
{
refdb_fs_iter *iter;
refdb_fs_backend *backend;
refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
char *loose_path;
size_t i;
assert(_backend);
backend = (refdb_fs_backend *)_backend;
git_vector_foreach(&iter->loose, i, loose_path) {
git__free(loose_path);
}
if (packed_load(backend) < 0)
git_vector_free(&iter->loose);
git__free(iter->glob);
git__free(iter);
}
static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
{
git_strmap *packfile = backend->refcache.packfile;
git_buf path = GIT_BUF_INIT;
git_iterator *fsit;
const git_index_entry *entry = NULL;
if (!backend->path) /* do nothing if no path for loose refs */
return 0;
if (git_buf_printf(&path, "%s/refs", backend->path) < 0)
return -1;
iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter);
if (git_iterator_for_filesystem(&fsit, git_buf_cstr(&path), 0, NULL, NULL) < 0)
return -1;
iter->parent.backend = _backend;
iter->h = backend->refcache.packfile;
iter->k = kh_begin(backend->refcache.packfile);
git_vector_init(&iter->loose, 8, NULL);
git_buf_sets(&path, GIT_REFS_DIR);
*out = (git_reference_iterator *)iter;
while (!git_iterator_advance(&entry, fsit)) {
const char *ref_name;
khiter_t pos;
git_buf_truncate(&path, strlen(GIT_REFS_DIR));
git_buf_puts(&path, entry->path);
ref_name = git_buf_cstr(&path);
if (git__suffixcmp(ref_name, ".lock") == 0 ||
(iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
continue;
pos = git_strmap_lookup_index(packfile, ref_name);
if (git_strmap_valid_index(packfile, pos)) {
struct packref *ref = git_strmap_value_at(packfile, pos);
ref->flags |= PACKREF_SHADOWED;
}
git_vector_insert(&iter->loose, git__strdup(ref_name));
}
git_iterator_free(fsit);
git_buf_free(&path);
return 0;
}
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
static int refdb_fs_backend__iterator_next(
git_reference **out, git_reference_iterator *_iter)
{
refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
git_strmap *packfile = backend->refcache.packfile;
git_buf_free(&iter->buf);
git_iterator_free(iter->fsiter);
git__free(iter);
}
while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
static int iter_packed(const char **out, refdb_fs_iter *iter)
{
/* Move forward to the next entry */
while (!kh_exist(iter->h, iter->k)) {
iter->k++;
if (iter->k == kh_end(iter->h))
if (loose_lookup(out, backend, path) == 0)
return 0;
giterr_clear();
}
while (iter->packed_pos < kh_end(packfile)) {
struct packref *ref = NULL;
while (!kh_exist(packfile, iter->packed_pos)) {
iter->packed_pos++;
if (iter->packed_pos == kh_end(packfile))
return GIT_ITEROVER;
}
*out = kh_key(iter->h, iter->k);
iter->k++;
ref = kh_val(packfile, iter->packed_pos);
iter->packed_pos++;
if (ref->flags & PACKREF_SHADOWED)
continue;
if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
continue;
*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
if (*out == NULL)
return -1;
return 0;
}
return GIT_ITEROVER;
}
static int iter_loose(const char **out, refdb_fs_iter *iter)
static int refdb_fs_backend__iterator_next_name(
const char **out, git_reference_iterator *_iter)
{
const git_index_entry *entry;
int retry;
git_strmap *packfile_refs;
refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend;
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
git_strmap *packfile = backend->refcache.packfile;
packfile_refs = backend->refcache.packfile;
while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
do {
khiter_t pos;
if (git_iterator_current(&entry, iter->fsiter) < 0)
return -1;
if (git_strmap_exists(packfile, path))
continue;
*out = path;
return 0;
}
git_buf_clear(&iter->buf);
if (!entry)
while (iter->packed_pos < kh_end(packfile)) {
while (!kh_exist(packfile, iter->packed_pos)) {
iter->packed_pos++;
if (iter->packed_pos == kh_end(packfile))
return GIT_ITEROVER;
}
*out = kh_key(packfile, iter->packed_pos);
iter->packed_pos++;
if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0)
continue;
if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0)
return 0;
}
return GIT_ITEROVER;
}
static int refdb_fs_backend__iterator(
git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
{
refdb_fs_iter *iter;
refdb_fs_backend *backend;
assert(_backend);
backend = (refdb_fs_backend *)_backend;
if (packed_load(backend) < 0)
return -1;
git_iterator_advance(NULL, iter->fsiter);
iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter);
if (glob != NULL)
iter->glob = git__strdup(glob);
/* Skip this one if we already listed it in packed */
pos = git_strmap_lookup_index(packfile_refs, git_buf_cstr(&iter->buf));
retry = 0;
if (git_strmap_valid_index(packfile_refs, pos) ||
!git_reference_is_valid_name(git_buf_cstr(&iter->buf)))
retry = 1;
iter->parent.next = refdb_fs_backend__iterator_next;
iter->parent.next_name = refdb_fs_backend__iterator_next_name;
iter->parent.free = refdb_fs_backend__iterator_free;
*out = git_buf_cstr(&iter->buf);
} while (retry);
if (iter_load_loose_paths(backend, iter) < 0) {
refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
return -1;
}
*out = (git_reference_iterator *)iter;
return 0;
}
static int iter_loose_setup(refdb_fs_iter *iter)
static bool ref_is_available(
const char *old_ref, const char *new_ref, const char *this_ref)
{
refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend;
git_buf_clear(&iter->buf);
if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0)
return -1;
if (old_ref == NULL || strcmp(old_ref, this_ref)) {
size_t reflen = strlen(this_ref);
size_t newlen = strlen(new_ref);
size_t cmplen = reflen < newlen ? reflen : newlen;
const char *lead = reflen < newlen ? new_ref : this_ref;
if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
return false;
}
}
return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL);
return true;
}
static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter)
static int reference_path_available(
refdb_fs_backend *backend,
const char *new_ref,
const char* old_ref,
int force)
{
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
struct packref *this_ref;
if (packed_load(backend) < 0)
return -1;
if (iter->loose)
return iter_loose(out, iter);
if (!force) {
int exists;
if (iter->k != kh_end(iter->h)) {
int error = iter_packed(out, iter);
if (error != GIT_ITEROVER)
return error;
if (refdb_fs_backend__exists(&exists, (git_refdb_backend *)backend, new_ref) < 0)
return -1;
if (exists) {
giterr_set(GITERR_REFERENCE,
"Failed to write reference '%s': a reference with "
" that name already exists.", new_ref);
return GIT_EEXISTS;
}
}
if (iter_loose_setup(iter) < 0)
git_strmap_foreach_value(backend->refcache.packfile, this_ref, {
if (!ref_is_available(old_ref, new_ref, this_ref->name)) {
giterr_set(GITERR_REFERENCE,
"The path to reference '%s' collides with an existing one", new_ref);
return -1;
iter->loose = 1;
}
});
return iter_loose(out, iter);
return 0;
}
static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
......@@ -695,8 +793,7 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
/* Remove a possibly existing empty directory hierarchy
* which name would collide with the reference name
*/
if (git_futils_rmdir_r(ref->name, backend->path,
GIT_RMDIR_SKIP_NONEMPTY) < 0)
if (git_futils_rmdir_r(ref->name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0)
return -1;
if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
......@@ -956,37 +1053,40 @@ cleanup_memory:
static int refdb_fs_backend__write(
git_refdb_backend *_backend,
const git_reference *ref)
const git_reference *ref,
int force)
{
refdb_fs_backend *backend;
int error;
assert(_backend);
backend = (refdb_fs_backend *)_backend;
error = reference_path_available(backend, ref->name, NULL, force);
if (error < 0)
return error;
return loose_write(backend, ref);
}
static int refdb_fs_backend__delete(
git_refdb_backend *_backend,
const git_reference *ref)
const char *ref_name)
{
refdb_fs_backend *backend;
git_repository *repo;
git_buf loose_path = GIT_BUF_INIT;
struct packref *pack_ref;
khiter_t pack_ref_pos;
int error = 0, pack_error;
int error = 0;
bool loose_deleted = 0;
assert(_backend);
assert(ref);
assert(ref_name);
backend = (refdb_fs_backend *)_backend;
repo = backend->repo;
/* If a loose reference exists, remove it from the filesystem */
if (git_buf_joinpath(&loose_path, repo->path_repository, ref->name) < 0)
if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
return -1;
if (git_path_isfile(loose_path.ptr)) {
......@@ -1000,20 +1100,64 @@ static int refdb_fs_backend__delete(
return error;
/* If a packed reference exists, remove it from the packfile and repack */
error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref_name);
if ((pack_error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref->name)) == 0) {
if (error == GIT_ENOTFOUND)
return loose_deleted ? 0 : GIT_ENOTFOUND;
if (error == 0) {
git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos);
git__free(pack_ref);
error = packed_write(backend);
}
if (pack_error == GIT_ENOTFOUND)
error = loose_deleted ? 0 : GIT_ENOTFOUND;
else
error = pack_error;
return error;
}
static int refdb_fs_backend__rename(
git_reference **out,
git_refdb_backend *_backend,
const char *old_name,
const char *new_name,
int force)
{
refdb_fs_backend *backend;
git_reference *old, *new;
int error;
assert(_backend);
backend = (refdb_fs_backend *)_backend;
error = reference_path_available(backend, new_name, old_name, force);
if (error < 0)
return error;
error = refdb_fs_backend__lookup(&old, _backend, old_name);
if (error < 0)
return error;
error = refdb_fs_backend__delete(_backend, old_name);
if (error < 0) {
git_reference_free(old);
return error;
}
new = realloc(old, sizeof(git_reference) + strlen(new_name) + 1);
memcpy(new->name, new_name, strlen(new_name) + 1);
error = loose_write(backend, new);
if (error < 0) {
git_reference_free(new);
return error;
}
if (out) {
*out = new;
} else {
git_reference_free(new);
}
return 0;
}
static int refdb_fs_backend__compress(git_refdb_backend *_backend)
......@@ -1088,7 +1232,7 @@ static int setup_namespace(git_buf *path, git_repository *repo)
}
git_buf_printf(path, "refs/namespaces/%s/refs", end);
free(parts);
git__free(parts);
/* Make sure that the folder with the namespace exists */
if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
......@@ -1121,10 +1265,9 @@ int git_refdb_backend_fs(
backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator;
backend->parent.next = &refdb_fs_backend__next;
backend->parent.iterator_free = &refdb_fs_backend__iterator_free;
backend->parent.write = &refdb_fs_backend__write;
backend->parent.delete = &refdb_fs_backend__delete;
backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress;
backend->parent.free = &refdb_fs_backend__free;
......
......@@ -95,123 +95,15 @@ void git_reference_free(git_reference *reference)
if (reference->type == GIT_REF_SYMBOLIC)
git__free(reference->target.symbolic);
git__free(reference);
}
struct reference_available_t {
const char *new_ref;
const char *old_ref;
int available;
};
static int _reference_available_cb(const char *ref, void *data)
{
struct reference_available_t *d;
assert(ref && data);
d = (struct reference_available_t *)data;
if (!d->old_ref || strcmp(d->old_ref, ref)) {
size_t reflen = strlen(ref);
size_t newlen = strlen(d->new_ref);
size_t cmplen = reflen < newlen ? reflen : newlen;
const char *lead = reflen < newlen ? d->new_ref : ref;
if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
d->available = 0;
return -1;
}
}
return 0;
}
static int reference_path_available(
git_repository *repo,
const char *ref,
const char* old_ref)
{
int error;
struct reference_available_t data;
data.new_ref = ref;
data.old_ref = old_ref;
data.available = 1;
error = git_reference_foreach(
repo, _reference_available_cb, (void *)&data);
if (error < 0)
return error;
if (!data.available) {
giterr_set(GITERR_REFERENCE,
"The path to reference '%s' collides with an existing one", ref);
return -1;
}
return 0;
}
/*
* Check if a reference could be written to disk, based on:
*
* - Whether a reference with the same name already exists,
* and we are allowing or disallowing overwrites
*
* - Whether the name of the reference would collide with
* an existing path
*/
static int reference_can_write(
git_repository *repo,
const char *refname,
const char *previous_name,
int force)
{
git_refdb *refdb;
if (reference->db)
GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
if (git_repository_refdb__weakptr(&refdb, repo) < 0)
return -1;
/* see if the reference shares a path with an existing reference;
* if a path is shared, we cannot create the reference, even when forcing */
if (reference_path_available(repo, refname, previous_name) < 0)
return -1;
/* check if the reference actually exists, but only if we are not forcing
* the rename. If we are forcing, it's OK to overwrite */
if (!force) {
int exists;
if (git_refdb_exists(&exists, refdb, refname) < 0)
return -1;
/* We cannot proceed if the reference already exists and we're not forcing
* the rename; the existing one would be overwritten */
if (exists) {
giterr_set(GITERR_REFERENCE,
"A reference with that name (%s) already exists", refname);
return GIT_EEXISTS;
}
}
/* FIXME: if the reference exists and we are forcing, do we really need to
* remove the reference first?
*
* Two cases:
*
* - the reference already exists and is loose: not a problem, the file
* gets overwritten on disk
*
* - the reference already exists and is packed: we write a new one as
* loose, which by all means renders the packed one useless
*/
return 0;
git__free(reference);
}
int git_reference_delete(git_reference *ref)
{
return git_refdb_delete(ref->db, ref);
return git_refdb_delete(ref->db, ref->name);
}
int git_reference_lookup(git_reference **ref_out,
......@@ -418,22 +310,24 @@ static int reference__create(
if (ref_out)
*ref_out = NULL;
if ((error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name)) < 0 ||
(error = reference_can_write(repo, normalized, NULL, force)) < 0 ||
(error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name);
if (error < 0)
return error;
error = git_repository_refdb__weakptr(&refdb, repo);
if (error < 0)
return error;
if (oid != NULL) {
assert(symbolic == NULL);
ref = git_reference__alloc(name, oid, NULL);
ref = git_reference__alloc(normalized, oid, NULL);
} else {
ref = git_reference__alloc_symbolic(name, symbolic);
ref = git_reference__alloc_symbolic(normalized, symbolic);
}
GITERR_CHECK_ALLOC(ref);
ref->db = refdb;
if ((error = git_refdb_write(refdb, ref)) < 0) {
if ((error = git_refdb_write(refdb, ref, force)) < 0) {
git_reference_free(ref);
return error;
}
......@@ -530,76 +424,41 @@ int git_reference_rename(
unsigned int normalization_flags;
char normalized[GIT_REFNAME_MAX];
bool should_head_be_updated = false;
git_reference *result = NULL;
int error = 0;
int reference_has_log;
*out = NULL;
normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
if ((error = git_reference_normalize_name(
normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
(error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0)
normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
return error;
/*
* Create the new reference.
*/
if (ref->type == GIT_REF_OID) {
result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel);
} else if (ref->type == GIT_REF_SYMBOLIC) {
result = git_reference__alloc_symbolic(new_name, ref->target.symbolic);
} else {
assert(0);
}
if (result == NULL)
return -1;
result->db = ref->db;
/* Check if we have to update HEAD. */
if ((error = git_branch_is_head(ref)) < 0)
goto on_error;
return error;
should_head_be_updated = (error > 0);
/* Now delete the old ref and save the new one. */
if ((error = git_refdb_delete(ref->db, ref)) < 0)
goto on_error;
/* Save the new reference. */
if ((error = git_refdb_write(ref->db, result)) < 0)
goto rollback;
if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0)
return error;
/* Update HEAD it was poiting to the reference being renamed. */
if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
if (should_head_be_updated &&
(error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
goto on_error;
return error;
}
/* Rename the reflog file, if it exists. */
reference_has_log = git_reference_has_log(ref);
if (reference_has_log < 0) {
error = reference_has_log;
goto on_error;
}
if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
goto on_error;
*out = result;
if (reference_has_log < 0)
return reference_has_log;
if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
return error;
rollback:
git_refdb_write(ref->db, ref);
on_error:
git_reference_free(result);
return error;
return 0;
}
int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
......@@ -623,14 +482,69 @@ int git_reference_foreach(
void *payload)
{
git_reference_iterator *iter;
const char *name;
git_reference *ref;
int error;
if (git_reference_iterator_new(&iter, repo) < 0)
return -1;
while ((error = git_reference_next(&ref, iter)) == 0) {
if (callback(ref, payload)) {
error = GIT_EUSER;
goto out;
}
}
if (error == GIT_ITEROVER)
error = 0;
out:
git_reference_iterator_free(iter);
return error;
}
int git_reference_foreach_name(
git_repository *repo,
git_reference_foreach_name_cb callback,
void *payload)
{
git_reference_iterator *iter;
const char *refname;
int error;
if (git_reference_iterator_new(&iter, repo) < 0)
return -1;
while ((error = git_reference_next(&name, iter)) == 0) {
if (callback(name, payload)) {
while ((error = git_reference_next_name(&refname, iter)) == 0) {
if (callback(refname, payload)) {
error = GIT_EUSER;
goto out;
}
}
if (error == GIT_ITEROVER)
error = 0;
out:
git_reference_iterator_free(iter);
return error;
}
int git_reference_foreach_glob(
git_repository *repo,
const char *glob,
git_reference_foreach_name_cb callback,
void *payload)
{
git_reference_iterator *iter;
const char *refname;
int error;
if (git_reference_iterator_glob_new(&iter, repo, glob) < 0)
return -1;
while ((error = git_reference_next_name(&refname, iter)) == 0) {
if (callback(refname, payload)) {
error = GIT_EUSER;
goto out;
}
......@@ -651,22 +565,28 @@ int git_reference_iterator_new(git_reference_iterator **out, git_repository *rep
if (git_repository_refdb__weakptr(&refdb, repo) < 0)
return -1;
return git_refdb_iterator(out, refdb);
return git_refdb_iterator(out, refdb, NULL);
}
int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob)
int git_reference_iterator_glob_new(
git_reference_iterator **out, git_repository *repo, const char *glob)
{
git_refdb *refdb;
if (git_repository_refdb__weakptr(&refdb, repo) < 0)
return -1;
return git_refdb_iterator_glob(out, refdb, glob);
return git_refdb_iterator(out, refdb, glob);
}
int git_reference_next(const char **out, git_reference_iterator *iter)
int git_reference_next(git_reference **out, git_reference_iterator *iter)
{
return git_refdb_next(out, iter);
return git_refdb_iterator_next(out, iter);
}
int git_reference_next_name(const char **out, git_reference_iterator *iter)
{
return git_refdb_iterator_next_name(out, iter);
}
void git_reference_iterator_free(git_reference_iterator *iter)
......@@ -693,7 +613,7 @@ int git_reference_list(
if (git_vector_init(&ref_list, 8, NULL) < 0)
return -1;
if (git_reference_foreach(
if (git_reference_foreach_name(
repo, &cb__reflist_add, (void *)&ref_list) < 0) {
git_vector_free(&ref_list);
return -1;
......@@ -991,34 +911,6 @@ int git_reference__update_terminal(
return reference__update_terminal(repo, ref_name, oid, 0);
}
int git_reference_foreach_glob(
git_repository *repo,
const char *glob,
git_reference_foreach_cb callback,
void *payload)
{
git_reference_iterator *iter;
const char *name;
int error;
if (git_reference_iterator_glob_new(&iter, repo, glob) < 0)
return -1;
while ((error = git_reference_next(&name, iter)) == 0) {
if (callback(name, payload)) {
error = GIT_EUSER;
goto out;
}
}
if (error == GIT_ITEROVER)
error = 0;
out:
git_reference_iterator_free(iter);
return error;
}
int git_reference_has_log(
git_reference *ref)
{
......
......@@ -1233,31 +1233,23 @@ static int update_branch_remote_config_entry(
}
static int rename_one_remote_reference(
git_repository *repo,
const char *reference_name,
git_reference *reference,
const char *old_remote_name,
const char *new_remote_name)
{
int error = -1;
git_buf new_name = GIT_BUF_INIT;
git_reference *reference = NULL;
git_reference *newref = NULL;
if (git_buf_printf(
&new_name,
GIT_REFS_REMOTES_DIR "%s%s",
new_remote_name,
reference_name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0)
reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0)
return -1;
if (git_reference_lookup(&reference, repo, reference_name) < 0)
goto cleanup;
error = git_reference_rename(&newref, reference, git_buf_cstr(&new_name), 0);
error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0);
git_reference_free(reference);
cleanup:
git_reference_free(newref);
git_buf_free(&new_name);
return error;
}
......@@ -1267,46 +1259,28 @@ static int rename_remote_references(
const char *old_name,
const char *new_name)
{
git_vector refnames;
int error = -1;
unsigned int i;
char *name;
const char *refname;
git_reference *ref;
git_reference_iterator *iter;
if (git_vector_init(&refnames, 8, NULL) < 0)
return -1;
if (git_reference_iterator_new(&iter, repo) < 0)
goto cleanup;
return -1;
while ((error = git_reference_next(&refname, iter)) == 0) {
if (git__prefixcmp(refname, GIT_REFS_REMOTES_DIR))
while ((error = git_reference_next(&ref, iter)) == 0) {
if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR))
continue;
if ((error = git_vector_insert(&refnames, git__strdup(refname))) < 0)
break;
if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) {
git_reference_iterator_free(iter);
return error;
}
}
git_reference_iterator_free(iter);
if (error == GIT_ITEROVER)
error = 0;
else
goto cleanup;
git_vector_foreach(&refnames, i, name) {
if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0)
goto cleanup;
}
error = 0;
cleanup:
git_vector_foreach(&refnames, i, name) {
git__free(name);
}
if (error == GIT_ITEROVER)
return 0;
git_vector_free(&refnames);
return error;
}
......
......@@ -1473,12 +1473,14 @@ static int at_least_one_cb(const char *refname, void *payload)
static int repo_contains_no_reference(git_repository *repo)
{
int error = git_reference_foreach(repo, at_least_one_cb, NULL);
int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
if (error == GIT_EUSER)
return 0;
if (!error)
return 1;
return error;
}
......
......@@ -1143,9 +1143,7 @@ static int load_submodule_config_from_index(
(error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0)
return error;
error = git_iterator_current(&entry, i);
while (!error && entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
if (S_ISGITLINK(entry->mode)) {
error = submodule_load_from_index(repo, entry);
......@@ -1158,10 +1156,11 @@ static int load_submodule_config_from_index(
if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
git_oid_cpy(gitmodules_oid, &entry->oid);
}
error = git_iterator_advance(&entry, i);
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
return error;
......@@ -1183,9 +1182,7 @@ static int load_submodule_config_from_head(
return error;
}
error = git_iterator_current(&entry, i);
while (!error && entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
if (S_ISGITLINK(entry->mode)) {
error = submodule_load_from_head(repo, entry->path, &entry->oid);
......@@ -1199,10 +1196,11 @@ static int load_submodule_config_from_head(
git_oid_iszero(gitmodules_oid))
git_oid_cpy(gitmodules_oid, &entry->oid);
}
error = git_iterator_advance(&entry, i);
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
git_tree_free(head);
......
......@@ -440,7 +440,7 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
data.cb_data = cb_data;
data.repo = repo;
return git_reference_foreach(repo, &tags_cb, &data);
return git_reference_foreach_name(repo, &tags_cb, &data);
}
typedef struct {
......
......@@ -31,7 +31,7 @@ static void tree_iterator_test(
git_tree *t;
git_iterator *i;
const git_index_entry *entry;
int count = 0, count_post_reset = 0;
int error, count = 0, count_post_reset = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
......@@ -39,29 +39,30 @@ static void tree_iterator_test(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end));
/* test loop */
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
cl_assert(entry);
if (expected_values != NULL)
cl_assert_equal_s(expected_values[count], entry->path);
count++;
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(!entry);
cl_assert_equal_i(expected_count, count);
/* test reset */
cl_git_pass(git_iterator_reset(i, NULL, NULL));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
cl_assert(entry);
if (expected_values != NULL)
cl_assert_equal_s(expected_values[count_post_reset], entry->path);
count_post_reset++;
cl_git_pass(git_iterator_advance(&entry, i));
}
git_iterator_free(i);
cl_assert_equal_i(expected_count, count);
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(!entry);
cl_assert_equal_i(count, count_post_reset);
git_iterator_free(i);
git_tree_free(t);
}
......@@ -298,7 +299,7 @@ void test_diff_iterator__tree_special_functions(void)
git_iterator *i;
const git_index_entry *entry;
git_repository *repo = cl_git_sandbox_init("attr");
int cases = 0;
int error, cases = 0;
const char *rootoid = "ce39a97a7fb1fa90bcf5e711249c1e507476ae0e";
t = resolve_commit_oid_to_tree(
......@@ -307,9 +308,10 @@ void test_diff_iterator__tree_special_functions(void)
cl_git_pass(git_iterator_for_tree(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
cl_assert(entry);
if (strcmp(entry->path, "sub/file") == 0) {
cases++;
check_tree_entry(
......@@ -338,11 +340,11 @@ void test_diff_iterator__tree_special_functions(void)
"2929de282ce999e95183aedac6451d3384559c4b",
rootoid, NULL);
}
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(!entry);
cl_assert_equal_i(4, cases);
git_iterator_free(i);
git_tree_free(t);
}
......@@ -360,14 +362,15 @@ static void index_iterator_test(
git_index *index;
git_iterator *i;
const git_index_entry *entry;
int count = 0;
int error, count = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_index(&i, index, 0, start, end));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
while (!(error = git_iterator_advance(&entry, i))) {
cl_assert(entry);
if (expected_names != NULL)
cl_assert_equal_s(expected_names[count], entry->path);
......@@ -378,13 +381,14 @@ static void index_iterator_test(
}
count++;
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(!entry);
cl_assert_equal_i(expected_count, count);
git_iterator_free(i);
git_index_free(index);
cl_assert_equal_i(expected_count, count);
}
static const char *expected_index_0[] = {
......@@ -535,12 +539,15 @@ static void workdir_iterator_test(
{
git_iterator *i;
const git_index_entry *entry;
int count = 0, count_all = 0, count_all_post_reset = 0;
int error, 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(
&i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, start, end));
cl_git_pass(git_iterator_current(&entry, i));
error = git_iterator_current(&entry, i);
cl_assert((error == 0 && entry != NULL) ||
(error == GIT_ITEROVER && entry == NULL));
while (entry != NULL) {
int ignored = git_iterator_current_is_ignored(i);
......@@ -560,29 +567,39 @@ static void workdir_iterator_test(
count++;
count_all++;
cl_git_pass(git_iterator_advance(&entry, i));
error = git_iterator_advance(&entry, i);
cl_assert((error == 0 && entry != NULL) ||
(error == GIT_ITEROVER && entry == NULL));
}
cl_assert_equal_i(expected_count, count);
cl_assert_equal_i(expected_count + expected_ignores, count_all);
cl_git_pass(git_iterator_reset(i, NULL, NULL));
cl_git_pass(git_iterator_current(&entry, i));
error = git_iterator_current(&entry, i);
cl_assert((error == 0 && entry != NULL) ||
(error == GIT_ITEROVER && entry == NULL));
while (entry != NULL) {
if (S_ISDIR(entry->mode)) {
cl_git_pass(git_iterator_advance_into(&entry, i));
continue;
}
if (expected_names != NULL)
cl_assert_equal_s(
expected_names[count_all_post_reset], entry->path);
count_all_post_reset++;
cl_git_pass(git_iterator_advance(&entry, i));
}
git_iterator_free(i);
error = git_iterator_advance(&entry, i);
cl_assert(error == 0 || error == GIT_ITEROVER);
}
cl_assert_equal_i(expected_count, count);
cl_assert_equal_i(expected_count + expected_ignores, count_all);
cl_assert_equal_i(count_all, count_all_post_reset);
git_iterator_free(i);
}
void test_diff_iterator__workdir_0(void)
......@@ -752,8 +769,10 @@ void test_diff_iterator__workdir_builtin_ignores(void)
{
/* it is possible to advance "into" a submodule */
cl_git_pass(git_iterator_advance_into(&entry, i));
} else
cl_git_pass(git_iterator_advance(&entry, i));
} else {
int error = git_iterator_advance(&entry, i);
cl_assert(!error || error == GIT_ITEROVER);
}
}
cl_assert(expected[idx].path == NULL);
......@@ -766,7 +785,7 @@ static void check_wd_first_through_third_range(
{
git_iterator *i;
const git_index_entry *entry;
int idx;
int error, idx;
static const char *expected[] = { "FIRST", "second", "THIRD", NULL };
cl_git_pass(git_iterator_for_workdir(
......@@ -776,7 +795,8 @@ static void check_wd_first_through_third_range(
for (idx = 0; entry != NULL; ++idx) {
cl_assert_equal_s(expected[idx], entry->path);
cl_git_pass(git_iterator_advance(&entry, i));
error = git_iterator_advance(&entry, i);
cl_assert(!error || error == GIT_ITEROVER);
}
cl_assert(expected[idx] == NULL);
......@@ -814,8 +834,7 @@ static void check_tree_range(
{
git_tree *head;
git_iterator *i;
const git_index_entry *entry;
int count;
int error, count;
cl_git_pass(git_repository_head_tree(&head, repo));
......@@ -824,13 +843,10 @@ static void check_tree_range(
ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE,
start, end));
cl_git_pass(git_iterator_current(&entry, i));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(&entry, i));
}
for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
/* count em up */;
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_i(expected_count, count);
git_iterator_free(i);
......@@ -872,8 +888,7 @@ static void check_index_range(
{
git_index *index;
git_iterator *i;
const git_index_entry *entry;
int count, caps;
int error, count, caps;
bool is_ignoring_case;
cl_git_pass(git_repository_index(&index, repo));
......@@ -888,13 +903,10 @@ static void check_index_range(
cl_assert(git_iterator_ignore_case(i) == ignore_case);
cl_git_pass(git_iterator_current(&entry, i));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(&entry, i));
}
for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
/* count em up */;
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_i(expected_count, count);
git_iterator_free(i);
......
#include "clar_libgit2.h"
#include "buffer.h"
#include "posix.h"
#include "path.h"
#include "refs.h"
#include "testdb.h"
#define TEST_REPO_PATH "testrepo"
static git_repository *repo;
int unlink_ref(void *payload, git_buf *file)
{
GIT_UNUSED(payload);
return p_unlink(git_buf_cstr(file));
}
int empty(void *payload, git_buf *file)
{
GIT_UNUSED(payload);
GIT_UNUSED(file);
return -1;
}
int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *filename))
{
const char *repo_path;
git_buf repo_refs_dir = GIT_BUF_INIT;
int error = 0;
repo_path = git_repository_path(repo);
git_buf_joinpath(&repo_refs_dir, repo_path, "HEAD");
if (git_path_exists(git_buf_cstr(&repo_refs_dir)) &&
cb(NULL, &repo_refs_dir) < 0)
return -1;
git_buf_joinpath(&repo_refs_dir, repo_path, "refs");
git_buf_joinpath(&repo_refs_dir, git_buf_cstr(&repo_refs_dir), "heads");
if (git_path_direach(&repo_refs_dir, cb, NULL) != 0)
return -1;
git_buf_joinpath(&repo_refs_dir, repo_path, "packed-refs");
if (git_path_exists(git_buf_cstr(&repo_refs_dir)) &&
cb(NULL, &repo_refs_dir) < 0)
return -1;
git_buf_free(&repo_refs_dir);
return error;
}
void test_refdb_inmemory__initialize(void)
{
git_buf repo_refs_dir = GIT_BUF_INIT;
git_refdb *refdb;
git_refdb_backend *refdb_backend;
repo = cl_git_sandbox_init(TEST_REPO_PATH);
cl_git_pass(git_repository_refdb(&refdb, repo));
cl_git_pass(refdb_backend_test(&refdb_backend, repo));
cl_git_pass(git_refdb_set_backend(refdb, refdb_backend));
ref_file_foreach(repo, unlink_ref);
git_buf_free(&repo_refs_dir);
git_refdb_free(refdb);
}
void test_refdb_inmemory__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_refdb_inmemory__doesnt_write_ref_file(void)
{
git_reference *ref;
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_reference_create(&ref, repo, GIT_REFS_HEADS_DIR "test1", &oid, 0));
ref_file_foreach(repo, empty);
git_reference_free(ref);
}
void test_refdb_inmemory__read(void)
{
git_reference *write1, *write2, *write3, *read1, *read2, *read3;
git_oid oid1, oid2, oid3;
cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0));
cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d"));
cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0));
cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af"));
cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0));
cl_git_pass(git_reference_lookup(&read1, repo, GIT_REFS_HEADS_DIR "test1"));
cl_assert(strcmp(git_reference_name(read1), git_reference_name(write1)) == 0);
cl_assert(git_oid_cmp(git_reference_target(read1), git_reference_target(write1)) == 0);
cl_git_pass(git_reference_lookup(&read2, repo, GIT_REFS_HEADS_DIR "test2"));
cl_assert(strcmp(git_reference_name(read2), git_reference_name(write2)) == 0);
cl_assert(git_oid_cmp(git_reference_target(read2), git_reference_target(write2)) == 0);
cl_git_pass(git_reference_lookup(&read3, repo, GIT_REFS_HEADS_DIR "test3"));
cl_assert(strcmp(git_reference_name(read3), git_reference_name(write3)) == 0);
cl_assert(git_oid_cmp(git_reference_target(read3), git_reference_target(write3)) == 0);
git_reference_free(write1);
git_reference_free(write2);
git_reference_free(write3);
git_reference_free(read1);
git_reference_free(read2);
git_reference_free(read3);
}
int foreach_test(const char *ref_name, void *payload)
{
git_reference *ref;
git_oid expected;
size_t *i = payload;
cl_git_pass(git_reference_lookup(&ref, repo, ref_name));
if (*i == 0)
cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
else if (*i == 1)
cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d"));
else if (*i == 2)
cl_git_pass(git_oid_fromstr(&expected, "763d71aadf09a7951596c9746c024e7eece7c7af"));
cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0);
++(*i);
git_reference_free(ref);
return 0;
}
void test_refdb_inmemory__foreach(void)
{
git_reference *write1, *write2, *write3;
git_oid oid1, oid2, oid3;
size_t i = 0;
cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0));
cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d"));
cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0));
cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af"));
cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0));
cl_git_pass(git_reference_foreach(repo,foreach_test, &i));
cl_assert_equal_i(3, (int)i);
git_reference_free(write1);
git_reference_free(write2);
git_reference_free(write3);
}
int delete_test(const char *ref_name, void *payload)
{
git_reference *ref;
git_oid expected;
size_t *i = payload;
cl_git_pass(git_reference_lookup(&ref, repo, ref_name));
cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d"));
cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0);
++(*i);
git_reference_free(ref);
return 0;
}
void test_refdb_inmemory__delete(void)
{
git_reference *write1, *write2, *write3;
git_oid oid1, oid2, oid3;
size_t i = 0;
cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0));
cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d"));
cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0));
cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af"));
cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0));
git_reference_delete(write1);
git_reference_free(write1);
git_reference_delete(write3);
git_reference_free(write3);
cl_git_pass(git_reference_foreach(repo, delete_test, &i));
cl_assert_equal_i(1, (int)i);
git_reference_free(write2);
}
#include "vector.h"
#include "util.h"
#include "testdb.h"
typedef struct refdb_test_backend {
git_refdb_backend parent;
git_repository *repo;
git_vector refs;
} refdb_test_backend;
typedef struct refdb_test_entry {
char *name;
git_ref_t type;
union {
git_oid oid;
char *symbolic;
} target;
} refdb_test_entry;
static int ref_name_cmp(const void *a, const void *b)
{
return strcmp(git_reference_name((git_reference *)a),
git_reference_name((git_reference *)b));
}
static int refdb_test_backend__exists(
int *exists,
git_refdb_backend *_backend,
const char *ref_name)
{
refdb_test_backend *backend;
refdb_test_entry *entry;
size_t i;
assert(_backend);
backend = (refdb_test_backend *)_backend;
*exists = 0;
git_vector_foreach(&backend->refs, i, entry) {
if (strcmp(entry->name, ref_name) == 0) {
*exists = 1;
break;
}
}
return 0;
}
static int refdb_test_backend__write(
git_refdb_backend *_backend,
const git_reference *ref)
{
refdb_test_backend *backend;
refdb_test_entry *entry;
assert(_backend);
backend = (refdb_test_backend *)_backend;
entry = git__calloc(1, sizeof(refdb_test_entry));
GITERR_CHECK_ALLOC(entry);
entry->name = git__strdup(git_reference_name(ref));
GITERR_CHECK_ALLOC(entry->name);
entry->type = git_reference_type(ref);
if (entry->type == GIT_REF_OID)
git_oid_cpy(&entry->target.oid, git_reference_target(ref));
else {
entry->target.symbolic = git__strdup(git_reference_symbolic_target(ref));
GITERR_CHECK_ALLOC(entry->target.symbolic);
}
git_vector_insert(&backend->refs, entry);
return 0;
}
static int refdb_test_backend__lookup(
git_reference **out,
git_refdb_backend *_backend,
const char *ref_name)
{
refdb_test_backend *backend;
refdb_test_entry *entry;
size_t i;
assert(_backend);
backend = (refdb_test_backend *)_backend;
git_vector_foreach(&backend->refs, i, entry) {
if (strcmp(entry->name, ref_name) == 0) {
if (entry->type == GIT_REF_OID) {
*out = git_reference__alloc(ref_name,
&entry->target.oid, NULL);
} else if (entry->type == GIT_REF_SYMBOLIC) {
*out = git_reference__alloc_symbolic(ref_name,
entry->target.symbolic);
}
if (*out == NULL)
return -1;
return 0;
}
}
return GIT_ENOTFOUND;
}
typedef struct {
git_reference_iterator parent;
size_t i;
} refdb_test_iter;
static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend)
{
refdb_test_iter *iter;
GIT_UNUSED(_backend);
iter = git__calloc(1, sizeof(refdb_test_iter));
GITERR_CHECK_ALLOC(iter);
iter->parent.backend = _backend;
iter->i = 0;
*out = (git_reference_iterator *) iter;
return 0;
}
static int refdb_test_backend__next(const char **name, git_reference_iterator *_iter)
{
refdb_test_entry *entry;
refdb_test_backend *backend = (refdb_test_backend *) _iter->backend;
refdb_test_iter *iter = (refdb_test_iter *) _iter;
entry = git_vector_get(&backend->refs, iter->i);
if (!entry)
return GIT_ITEROVER;
*name = entry->name;
iter->i++;
return 0;
}
static void refdb_test_backend__iterator_free(git_reference_iterator *iter)
{
git__free(iter);
}
static void refdb_test_entry_free(refdb_test_entry *entry)
{
if (entry->type == GIT_REF_SYMBOLIC)
git__free(entry->target.symbolic);
git__free(entry->name);
git__free(entry);
}
static int refdb_test_backend__delete(
git_refdb_backend *_backend,
const git_reference *ref)
{
refdb_test_backend *backend;
refdb_test_entry *entry;
size_t i;
assert(_backend);
backend = (refdb_test_backend *)_backend;
git_vector_foreach(&backend->refs, i, entry) {
if (strcmp(entry->name, git_reference_name(ref)) == 0) {
git_vector_remove(&backend->refs, i);
refdb_test_entry_free(entry);
}
}
return GIT_ENOTFOUND;
}
static void refdb_test_backend__free(git_refdb_backend *_backend)
{
refdb_test_backend *backend;
refdb_test_entry *entry;
size_t i;
assert(_backend);
backend = (refdb_test_backend *)_backend;
git_vector_foreach(&backend->refs, i, entry)
refdb_test_entry_free(entry);
git_vector_free(&backend->refs);
git__free(backend);
}
int refdb_backend_test(
git_refdb_backend **backend_out,
git_repository *repo)
{
refdb_test_backend *backend;
backend = git__calloc(1, sizeof(refdb_test_backend));
GITERR_CHECK_ALLOC(backend);
git_vector_init(&backend->refs, 0, ref_name_cmp);
backend->repo = repo;
backend->parent.exists = &refdb_test_backend__exists;
backend->parent.lookup = &refdb_test_backend__lookup;
backend->parent.iterator = &refdb_test_backend__iterator;
backend->parent.next = &refdb_test_backend__next;
backend->parent.iterator_free = &refdb_test_backend__iterator_free;
backend->parent.write = &refdb_test_backend__write;
backend->parent.delete = &refdb_test_backend__delete;
backend->parent.free = &refdb_test_backend__free;
*backend_out = (git_refdb_backend *)backend;
return 0;
}
#include <git2/errors.h>
#include <git2/repository.h>
#include <git2/refdb.h>
#include <git2/sys/refs.h>
#include <git2/sys/refdb_backend.h>
int refdb_backend_test(
git_refdb_backend **backend_out,
git_repository *repo);
......@@ -38,40 +38,43 @@ static const char *refnames[] = {
"refs/tags/wrapped_tag",
};
static int refcmp_cb(const void *a, const void *b)
{
const git_reference *refa = (const git_reference *)a;
const git_reference *refb = (const git_reference *)b;
return strcmp(refa->name, refb->name);
}
void test_refs_iterator__list(void)
{
git_reference_iterator *iter;
git_vector output;
char *refname;
git_reference *ref;
int error;
size_t i;
cl_git_pass(git_vector_init(&output, 32, git__strcmp_cb));
cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
cl_git_pass(git_reference_iterator_new(&iter, repo));
do {
const char *name;
error = git_reference_next(&name, iter);
error = git_reference_next(&ref, iter);
cl_assert(error == 0 || error == GIT_ITEROVER);
if (error != GIT_ITEROVER) {
char *dup = git__strdup(name);
cl_assert(dup != NULL);
cl_git_pass(git_vector_insert(&output, dup));
cl_git_pass(git_vector_insert(&output, ref));
}
} while (!error);
git_reference_iterator_free(iter);
cl_assert_equal_i(output.length, ARRAY_SIZE(refnames));
git_vector_sort(&output);
git_vector_foreach(&output, i, refname) {
cl_assert_equal_s(refname, refnames[i]);
}
git_reference_iterator_free(iter);
git_vector_foreach(&output, i, refname) {
git__free(refname);
git_vector_foreach(&output, i, ref) {
cl_assert_equal_s(ref->name, refnames[i]);
git_reference_free(ref);
}
git_vector_free(&output);
}
......@@ -79,14 +82,14 @@ void test_refs_iterator__empty(void)
{
git_reference_iterator *iter;
git_odb *odb;
const char *name;
git_reference *ref;
git_repository *empty;
cl_git_pass(git_odb_new(&odb));
cl_git_pass(git_repository_wrap_odb(&empty, odb));
cl_git_pass(git_reference_iterator_new(&iter, empty));
cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&name, iter));
cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter));
git_reference_iterator_free(iter);
git_odb_free(odb);
......
......@@ -31,12 +31,11 @@ static void expect_iterator_items(
if (expected_flat < 0) { v = true; expected_flat = -expected_flat; }
if (expected_total < 0) { v = true; expected_total = -expected_total; }
count = 0;
cl_git_pass(git_iterator_current(&entry, i));
if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees");
while (entry != NULL) {
count = 0;
while (!git_iterator_advance(&entry, i)) {
if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
if (no_trees)
......@@ -54,8 +53,6 @@ static void expect_iterator_items(
cl_assert(entry->mode != GIT_FILEMODE_TREE);
}
cl_git_pass(git_iterator_advance(&entry, i));
if (++count > expected_flat)
break;
}
......@@ -93,10 +90,14 @@ static void expect_iterator_items(
/* could return NOTFOUND if directory is empty */
cl_assert(!error || error == GIT_ENOTFOUND);
if (error == GIT_ENOTFOUND)
cl_git_pass(git_iterator_advance(&entry, i));
} else
cl_git_pass(git_iterator_advance(&entry, i));
if (error == GIT_ENOTFOUND) {
error = git_iterator_advance(&entry, i);
cl_assert(!error || error == GIT_ITEROVER);
}
} else {
error = git_iterator_advance(&entry, i);
cl_assert(!error || error == GIT_ITEROVER);
}
if (++count > expected_total)
break;
......
......@@ -370,12 +370,9 @@ void test_submodule_status__iterator(void)
cl_git_pass(git_iterator_for_workdir(&iter, g_repo,
GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
cl_git_pass(git_iterator_current(&entry, iter));
for (i = 0; entry; ++i) {
for (i = 0; !git_iterator_advance(&entry, iter); ++i)
cl_assert_equal_s(expected[i], entry->path);
cl_git_pass(git_iterator_advance(&entry, iter));
}
git_iterator_free(iter);
......
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