diff options
author | Vicent Martà <vicent@github.com> | 2013-06-03 20:28:58 +0400 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2013-06-03 20:28:58 +0400 |
commit | 947fad4f7f7fbbc6d624570e9186326d3beb8cce (patch) | |
tree | 6a52a0a06d219abcffa62046054d54c6114a0854 /src | |
parent | 9d9fff3c3dbc207a8a2ee2114eab19086afecb6e (diff) | |
parent | efd5a4e16a55ff32ce0b300d3792e44aed4157f8 (diff) |
Merge pull request #1624 from libgit2/vmg/full-ref-iterator
Breaking RefDB changes
Diffstat (limited to 'src')
-rw-r--r-- | src/branch.c | 30 | ||||
-rw-r--r-- | src/checkout.c | 31 | ||||
-rw-r--r-- | src/clone.c | 2 | ||||
-rw-r--r-- | src/diff.c | 34 | ||||
-rw-r--r-- | src/iterator.c | 214 | ||||
-rw-r--r-- | src/iterator.h | 38 | ||||
-rw-r--r-- | src/merge.c | 15 | ||||
-rw-r--r-- | src/notes.c | 14 | ||||
-rw-r--r-- | src/refdb.c | 105 | ||||
-rw-r--r-- | src/refdb.h | 20 | ||||
-rw-r--r-- | src/refdb_fs.c | 365 | ||||
-rw-r--r-- | src/refs.c | 298 | ||||
-rw-r--r-- | src/remote.c | 52 | ||||
-rw-r--r-- | src/repository.c | 4 | ||||
-rw-r--r-- | src/submodule.c | 18 | ||||
-rw-r--r-- | src/tag.c | 2 |
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); @@ -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 { |