summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Martí <vicent@github.com>2013-06-03 09:28:58 -0700
committerVicent Martí <vicent@github.com>2013-06-03 09:28:58 -0700
commit947fad4f7f7fbbc6d624570e9186326d3beb8cce (patch)
tree6a52a0a06d219abcffa62046054d54c6114a0854 /src
parent9d9fff3c3dbc207a8a2ee2114eab19086afecb6e (diff)
parentefd5a4e16a55ff32ce0b300d3792e44aed4157f8 (diff)
downloadlibgit2-947fad4f7f7fbbc6d624570e9186326d3beb8cce.tar.gz
Merge pull request #1624 from libgit2/vmg/full-ref-iterator
Breaking RefDB changes
Diffstat (limited to 'src')
-rw-r--r--src/branch.c30
-rw-r--r--src/checkout.c31
-rw-r--r--src/clone.c2
-rw-r--r--src/diff.c34
-rw-r--r--src/iterator.c214
-rw-r--r--src/iterator.h38
-rw-r--r--src/merge.c15
-rw-r--r--src/notes.c14
-rw-r--r--src/refdb.c105
-rw-r--r--src/refdb.h20
-rw-r--r--src/refdb_fs.c365
-rw-r--r--src/refs.c298
-rw-r--r--src/remote.c52
-rw-r--r--src/repository.c4
-rw-r--r--src/submodule.c18
-rw-r--r--src/tag.c2
16 files changed, 700 insertions, 542 deletions
diff --git a/src/branch.c b/src/branch.c
index dd6dc68f4..de38e3355 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -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);
diff --git a/src/checkout.c b/src/checkout.c
index c28fcdee0..7a2e68300 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -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)
- goto fail;
- }
-
+ 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;
diff --git a/src/clone.c b/src/clone.c
index 191040da7..af3298fd0 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -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)
diff --git a/src/diff.c b/src/diff.c
index b96ff4705..05ef4f16b 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -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
diff --git a/src/iterator.c b/src/iterator.c
index ff08c1ce0..4360b99ad 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -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,22 +432,18 @@ 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;
- const git_tree_entry *te;
+ tree_iterator_frame *tf;
+ const git_tree_entry *te;
- iterator__clear_entry(entry);
+ if (ti->entry_is_current)
+ return 0;
- if (tf->current >= tf->n_entries)
- return 0;
- te = tf->entries[tf->current]->te;
+ tf = ti->head;
+ te = tf->entries[tf->current]->te;
ti->entry.mode = te->attr;
git_oid_cpy(&ti->entry.oid, &te->oid);
@@ -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,13 +1206,14 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
- if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- if (error) {
- git_iterator_free((git_iterator *)fi);
- fi = NULL;
+ if ((error = fs_iterator__expand_dir(fi)) < 0) {
+ if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
+ giterr_clear();
+ error = 0;
+ } else {
+ git_iterator_free((git_iterator *)fi);
+ fi = NULL;
+ }
}
*out = (git_iterator *)fi;
diff --git a/src/iterator.h b/src/iterator.h
index 7998f7c6b..493ff4b2a 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -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);
diff --git a/src/merge.c b/src/merge.c
index 11345587c..047d96013 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -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;
}
diff --git a/src/notes.c b/src/notes.c
index 3e3db58db..beace1b50 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -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);
+ git_oid_cpy(note_id, &item->oid);
- 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;
}
diff --git a/src/refdb.c b/src/refdb.c
index 9f9037ce7..359842e44 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -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))) {
- ref->db = db;
- *out = ref;
- } else {
- *out = NULL;
- }
+ error = db->backend->lookup(&ref, db->backend, ref_name);
+ if (error < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ ref->db = db;
- 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;
- }
+ int error;
- if (db->backend->iterator_glob)
- return db->backend->iterator_glob(out, db->backend, glob);
+ if ((error = iter->next(out, iter)) < 0)
+ return 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;
-
- (*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;
- return db->backend->delete(db->backend, ref);
+ if (out) {
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+ }
+
+ 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);
}
diff --git a/src/refdb.h b/src/refdb.h
index 2edd05d18..3aea37b62 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -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
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 7de56a1a0..4083ba9e5 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -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))
- return GIT_ITEROVER;
+ if (loose_lookup(out, backend, path) == 0)
+ return 0;
+
+ giterr_clear();
}
- *out = kh_key(iter->h, iter->k);
- iter->k++;
+ while (iter->packed_pos < kh_end(packfile)) {
+ struct packref *ref = NULL;
- return 0;
+ while (!kh_exist(packfile, iter->packed_pos)) {
+ iter->packed_pos++;
+ if (iter->packed_pos == kh_end(packfile))
+ return GIT_ITEROVER;
+ }
+
+ 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;
- git_buf_clear(&iter->buf);
- if (!entry)
- return GIT_ITEROVER;
+ *out = path;
+ return 0;
+ }
- if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0)
- return -1;
+ 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;
+ }
- git_iterator_advance(NULL, iter->fsiter);
+ *out = kh_key(packfile, iter->packed_pos);
+ iter->packed_pos++;
- /* 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;
+ if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0)
+ continue;
- *out = git_buf_cstr(&iter->buf);
- } while (retry);
+ return 0;
+ }
- return 0;
+ return GIT_ITEROVER;
}
-static int iter_loose_setup(refdb_fs_iter *iter)
+static int refdb_fs_backend__iterator(
+ git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
{
- refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend;
+ refdb_fs_iter *iter;
+ refdb_fs_backend *backend;
+
+ assert(_backend);
+ backend = (refdb_fs_backend *)_backend;
- git_buf_clear(&iter->buf);
- if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0)
+ if (packed_load(backend) < 0)
return -1;
- return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL);
+ iter = git__calloc(1, sizeof(refdb_fs_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ if (glob != NULL)
+ iter->glob = git__strdup(glob);
+
+ 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;
+
+ 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 refdb_fs_backend__next(const char **out, git_reference_iterator *_iter)
+static bool ref_is_available(
+ const char *old_ref, const char *new_ref, const char *this_ref)
{
- refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
+ 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;
+ }
+ }
- if (iter->loose)
- return iter_loose(out, iter);
+ return true;
+}
- if (iter->k != kh_end(iter->h)) {
- int error = iter_packed(out, iter);
- if (error != GIT_ITEROVER)
- return error;
- }
+static int reference_path_available(
+ refdb_fs_backend *backend,
+ const char *new_ref,
+ const char* old_ref,
+ int force)
+{
+ struct packref *this_ref;
- if (iter_loose_setup(iter) < 0)
+ if (packed_load(backend) < 0)
return -1;
- iter->loose = 1;
- return iter_loose(out, iter);
+ if (!force) {
+ int exists;
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+ });
+
+ 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,22 +1100,66 @@ 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 (error == GIT_ENOTFOUND)
+ return loose_deleted ? 0 : GIT_ENOTFOUND;
- if ((pack_error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref->name)) == 0) {
+ 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)
{
refdb_fs_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;
diff --git a/src/refs.c b/src/refs.c
index 166669ef2..2b545954d 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -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 (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
- */
+ if (reference->db)
+ GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
- 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;
+ if (reference_has_log < 0)
+ return reference_has_log;
- *out = result;
-
- return error;
-
-rollback:
- git_refdb_write(ref->db, ref);
-
-on_error:
- git_reference_free(result);
+ if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+ return error;
- 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(&name, iter)) == 0) {
- if (callback(name, payload)) {
+ 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(&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)
{
diff --git a/src/remote.c b/src/remote.c
index 4c1b39818..943b72bb7 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -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;
}
diff --git a/src/repository.c b/src/repository.c
index 28505e822..2e7a334c9 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -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;
}
diff --git a/src/submodule.c b/src/submodule.c
index f4fbcd35a..16114d8ac 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -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);
diff --git a/src/tag.c b/src/tag.c
index ecf3876cb..71f4c1eb1 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -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 {