diff options
author | Junio C Hamano <gitster@pobox.com> | 2016-07-25 14:13:33 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-07-25 14:13:33 -0700 |
commit | 87492cb24d9d8be8e18217b89ae5f090089ff31d (patch) | |
tree | b517e8efaa98fa713d8b6286ed6ee1c08c385a2e | |
parent | Merge branch 'mh/update-ref-errors' (diff) | |
parent | for_each_reflog(): reimplement using iterators (diff) | |
download | tgif-87492cb24d9d8be8e18217b89ae5f090089ff31d.tar.xz |
Merge branch 'mh/ref-iterators'
The API to iterate over all the refs (i.e. for_each_ref(), etc.)
has been revamped.
* mh/ref-iterators:
for_each_reflog(): reimplement using iterators
dir_iterator: new API for iterating over a directory tree
for_each_reflog(): don't abort for bad references
do_for_each_ref(): reimplement using reference iteration
refs: introduce an iterator interface
ref_resolves_to_object(): new function
entry_resolves_to_object(): rename function from ref_resolves_to_object()
get_ref_cache(): only create an instance if there is a submodule
remote rm: handle symbolic refs correctly
delete_refs(): add a flags argument
refs: use name "prefix" consistently
do_for_each_ref(): move docstring to the header file
refs: remove unnecessary "extern" keywords
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | builtin/fetch.c | 2 | ||||
-rw-r--r-- | builtin/remote.c | 8 | ||||
-rw-r--r-- | dir-iterator.c | 202 | ||||
-rw-r--r-- | dir-iterator.h | 87 | ||||
-rw-r--r-- | iterator.h | 81 | ||||
-rw-r--r-- | refs.c | 20 | ||||
-rw-r--r-- | refs.h | 141 | ||||
-rw-r--r-- | refs/files-backend.c | 631 | ||||
-rw-r--r-- | refs/iterator.c | 384 | ||||
-rw-r--r-- | refs/refs-internal.h | 226 |
11 files changed, 1460 insertions, 324 deletions
@@ -718,6 +718,7 @@ LIB_OBJS += diff-lib.o LIB_OBJS += diff-no-index.o LIB_OBJS += diff.o LIB_OBJS += dir.o +LIB_OBJS += dir-iterator.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o @@ -782,6 +783,7 @@ LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o +LIB_OBJS += refs/iterator.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace_object.o diff --git a/builtin/fetch.c b/builtin/fetch.c index d9ea6f392a..acd0cf1755 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -921,7 +921,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, for (ref = stale_refs; ref; ref = ref->next) string_list_append(&refnames, ref->name); - result = delete_refs(&refnames); + result = delete_refs(&refnames, 0); string_list_clear(&refnames, 0); } diff --git a/builtin/remote.c b/builtin/remote.c index a4d9c1a8e9..9f6a6b3a9c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -539,10 +539,6 @@ static int add_branch_for_removal(const char *refname, return 0; } - /* make sure that symrefs are deleted */ - if (flags & REF_ISSYMREF) - return unlink(git_path("%s", refname)); - string_list_append(branches->branches, refname); return 0; @@ -788,7 +784,7 @@ static int rm(int argc, const char **argv) strbuf_release(&buf); if (!result) - result = delete_refs(&branches); + result = delete_refs(&branches, REF_NODEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@ -1304,7 +1300,7 @@ static int prune_remote(const char *remote, int dry_run) string_list_sort(&refs_to_prune); if (!dry_run) - result |= delete_refs(&refs_to_prune); + result |= delete_refs(&refs_to_prune, 0); for_each_string_list_item(item, &states.stale) { const char *refname = item->util; diff --git a/dir-iterator.c b/dir-iterator.c new file mode 100644 index 0000000000..34182a9a1c --- /dev/null +++ b/dir-iterator.c @@ -0,0 +1,202 @@ +#include "cache.h" +#include "dir.h" +#include "iterator.h" +#include "dir-iterator.h" + +struct dir_iterator_level { + int initialized; + + DIR *dir; + + /* + * The length of the directory part of path at this level + * (including a trailing '/'): + */ + size_t prefix_len; + + /* + * The last action that has been taken with the current entry + * (needed for directories, which have to be included in the + * iteration and also iterated into): + */ + enum { + DIR_STATE_ITER, + DIR_STATE_RECURSE + } dir_state; +}; + +/* + * The full data structure used to manage the internal directory + * iteration state. It includes members that are not part of the + * public interface. + */ +struct dir_iterator_int { + struct dir_iterator base; + + /* + * The number of levels currently on the stack. This is always + * at least 1, because when it becomes zero the iteration is + * ended and this struct is freed. + */ + size_t levels_nr; + + /* The number of levels that have been allocated on the stack */ + size_t levels_alloc; + + /* + * A stack of levels. levels[0] is the uppermost directory + * that will be included in this iteration. + */ + struct dir_iterator_level *levels; +}; + +int dir_iterator_advance(struct dir_iterator *dir_iterator) +{ + struct dir_iterator_int *iter = + (struct dir_iterator_int *)dir_iterator; + + while (1) { + struct dir_iterator_level *level = + &iter->levels[iter->levels_nr - 1]; + struct dirent *de; + + if (!level->initialized) { + /* + * Note: dir_iterator_begin() ensures that + * path is not the empty string. + */ + if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1])) + strbuf_addch(&iter->base.path, '/'); + level->prefix_len = iter->base.path.len; + + level->dir = opendir(iter->base.path.buf); + if (!level->dir && errno != ENOENT) { + warning("error opening directory %s: %s", + iter->base.path.buf, strerror(errno)); + /* Popping the level is handled below */ + } + + level->initialized = 1; + } else if (S_ISDIR(iter->base.st.st_mode)) { + if (level->dir_state == DIR_STATE_ITER) { + /* + * The directory was just iterated + * over; now prepare to iterate into + * it. + */ + level->dir_state = DIR_STATE_RECURSE; + ALLOC_GROW(iter->levels, iter->levels_nr + 1, + iter->levels_alloc); + level = &iter->levels[iter->levels_nr++]; + level->initialized = 0; + continue; + } else { + /* + * The directory has already been + * iterated over and iterated into; + * we're done with it. + */ + } + } + + if (!level->dir) { + /* + * This level is exhausted (or wasn't opened + * successfully); pop up a level. + */ + if (--iter->levels_nr == 0) + return dir_iterator_abort(dir_iterator); + + continue; + } + + /* + * Loop until we find an entry that we can give back + * to the caller: + */ + while (1) { + strbuf_setlen(&iter->base.path, level->prefix_len); + errno = 0; + de = readdir(level->dir); + + if (!de) { + /* This level is exhausted; pop up a level. */ + if (errno) { + warning("error reading directory %s: %s", + iter->base.path.buf, strerror(errno)); + } else if (closedir(level->dir)) + warning("error closing directory %s: %s", + iter->base.path.buf, strerror(errno)); + + level->dir = NULL; + if (--iter->levels_nr == 0) + return dir_iterator_abort(dir_iterator); + break; + } + + if (is_dot_or_dotdot(de->d_name)) + continue; + + strbuf_addstr(&iter->base.path, de->d_name); + if (lstat(iter->base.path.buf, &iter->base.st) < 0) { + if (errno != ENOENT) + warning("error reading path '%s': %s", + iter->base.path.buf, + strerror(errno)); + continue; + } + + /* + * We have to set these each time because + * the path strbuf might have been realloc()ed. + */ + iter->base.relative_path = + iter->base.path.buf + iter->levels[0].prefix_len; + iter->base.basename = + iter->base.path.buf + level->prefix_len; + level->dir_state = DIR_STATE_ITER; + + return ITER_OK; + } + } +} + +int dir_iterator_abort(struct dir_iterator *dir_iterator) +{ + struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; + + for (; iter->levels_nr; iter->levels_nr--) { + struct dir_iterator_level *level = + &iter->levels[iter->levels_nr - 1]; + + if (level->dir && closedir(level->dir)) { + strbuf_setlen(&iter->base.path, level->prefix_len); + warning("error closing directory %s: %s", + iter->base.path.buf, strerror(errno)); + } + } + + free(iter->levels); + strbuf_release(&iter->base.path); + free(iter); + return ITER_DONE; +} + +struct dir_iterator *dir_iterator_begin(const char *path) +{ + struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter)); + struct dir_iterator *dir_iterator = &iter->base; + + if (!path || !*path) + die("BUG: empty path passed to dir_iterator_begin()"); + + strbuf_init(&iter->base.path, PATH_MAX); + strbuf_addstr(&iter->base.path, path); + + ALLOC_GROW(iter->levels, 10, iter->levels_alloc); + + iter->levels_nr = 1; + iter->levels[0].initialized = 0; + + return dir_iterator; +} diff --git a/dir-iterator.h b/dir-iterator.h new file mode 100644 index 0000000000..27739e6c29 --- /dev/null +++ b/dir-iterator.h @@ -0,0 +1,87 @@ +#ifndef DIR_ITERATOR_H +#define DIR_ITERATOR_H + +/* + * Iterate over a directory tree. + * + * Iterate over a directory tree, recursively, including paths of all + * types and hidden paths. Skip "." and ".." entries and don't follow + * symlinks except for the original path. + * + * Every time dir_iterator_advance() is called, update the members of + * the dir_iterator structure to reflect the next path in the + * iteration. The order that paths are iterated over within a + * directory is undefined, but directory paths are always iterated + * over before the subdirectory contents. + * + * A typical iteration looks like this: + * + * int ok; + * struct iterator *iter = dir_iterator_begin(path); + * + * while ((ok = dir_iterator_advance(iter)) == ITER_OK) { + * if (want_to_stop_iteration()) { + * ok = dir_iterator_abort(iter); + * break; + * } + * + * // Access information about the current path: + * if (S_ISDIR(iter->st.st_mode)) + * printf("%s is a directory\n", iter->relative_path); + * } + * + * if (ok != ITER_DONE) + * handle_error(); + * + * Callers are allowed to modify iter->path while they are working, + * but they must restore it to its original contents before calling + * dir_iterator_advance() again. + */ + +struct dir_iterator { + /* The current path: */ + struct strbuf path; + + /* + * The current path relative to the starting path. This part + * of the path always uses "/" characters to separate path + * components: + */ + const char *relative_path; + + /* The current basename: */ + const char *basename; + + /* The result of calling lstat() on path: */ + struct stat st; +}; + +/* + * Start a directory iteration over path. Return a dir_iterator that + * holds the internal state of the iteration. + * + * The iteration includes all paths under path, not including path + * itself and not including "." or ".." entries. + * + * path is the starting directory. An internal copy will be made. + */ +struct dir_iterator *dir_iterator_begin(const char *path); + +/* + * Advance the iterator to the first or next item and return ITER_OK. + * If the iteration is exhausted, free the dir_iterator and any + * resources associated with it and return ITER_DONE. On error, free + * dir_iterator and associated resources and return ITER_ERROR. It is + * a bug to use iterator or call this function again after it has + * returned ITER_DONE or ITER_ERROR. + */ +int dir_iterator_advance(struct dir_iterator *iterator); + +/* + * End the iteration before it has been exhausted. Free the + * dir_iterator and any associated resources and return ITER_DONE. On + * error, free the dir_iterator and return ITER_ERROR. + */ +int dir_iterator_abort(struct dir_iterator *iterator); + +#endif diff --git a/iterator.h b/iterator.h new file mode 100644 index 0000000000..0f6900e43a --- /dev/null +++ b/iterator.h @@ -0,0 +1,81 @@ +#ifndef ITERATOR_H +#define ITERATOR_H + +/* + * Generic constants related to iterators. + */ + +/* + * The attempt to advance the iterator was successful; the iterator + * reflects the new current entry. + */ +#define ITER_OK 0 + +/* + * The iterator is exhausted and has been freed. + */ +#define ITER_DONE -1 + +/* + * The iterator experienced an error. The iteration has been aborted + * and the iterator has been freed. + */ +#define ITER_ERROR -2 + +/* + * Return values for selector functions for merge iterators. The + * numerical values of these constants are important and must be + * compatible with ITER_DONE and ITER_ERROR. + */ +enum iterator_selection { + /* End the iteration without an error: */ + ITER_SELECT_DONE = ITER_DONE, + + /* Report an error and abort the iteration: */ + ITER_SELECT_ERROR = ITER_ERROR, + + /* + * The next group of constants are masks that are useful + * mainly internally. + */ + + /* The LSB selects whether iter0/iter1 is the "current" iterator: */ + ITER_CURRENT_SELECTION_MASK = 0x01, + + /* iter0 is the "current" iterator this round: */ + ITER_CURRENT_SELECTION_0 = 0x00, + + /* iter1 is the "current" iterator this round: */ + ITER_CURRENT_SELECTION_1 = 0x01, + + /* Yield the value from the current iterator? */ + ITER_YIELD_CURRENT = 0x02, + + /* Discard the value from the secondary iterator? */ + ITER_SKIP_SECONDARY = 0x04, + + /* + * The constants that a selector function should usually + * return. + */ + + /* Yield the value from iter0: */ + ITER_SELECT_0 = ITER_CURRENT_SELECTION_0 | ITER_YIELD_CURRENT, + + /* Yield the value from iter0 and discard the one from iter1: */ + ITER_SELECT_0_SKIP_1 = ITER_SELECT_0 | ITER_SKIP_SECONDARY, + + /* Discard the value from iter0 without yielding anything this round: */ + ITER_SKIP_0 = ITER_CURRENT_SELECTION_1 | ITER_SKIP_SECONDARY, + + /* Yield the value from iter1: */ + ITER_SELECT_1 = ITER_CURRENT_SELECTION_1 | ITER_YIELD_CURRENT, + + /* Yield the value from iter1 and discard the one from iter0: */ + ITER_SELECT_1_SKIP_0 = ITER_SELECT_1 | ITER_SKIP_SECONDARY, + + /* Discard the value from iter1 without yielding anything this round: */ + ITER_SKIP_1 = ITER_CURRENT_SELECTION_0 | ITER_SKIP_SECONDARY +}; + +#endif /* ITERATOR_H */ @@ -1120,6 +1120,26 @@ int head_ref(each_ref_fn fn, void *cb_data) return head_ref_submodule(NULL, fn, cb_data); } +/* + * Call fn for each reference in the specified submodule for which the + * refname begins with prefix. If trim is non-zero, then trim that + * many characters off the beginning of each refname before passing + * the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to + * include broken references in the iteration. If fn ever returns a + * non-zero value, stop the iteration and return that value; + * otherwise, return 0. + */ +static int do_for_each_ref(const char *submodule, const char *prefix, + each_ref_fn fn, int trim, int flags, void *cb_data) +{ + struct ref_iterator *iter; + + iter = files_ref_iterator_begin(submodule, prefix, flags); + iter = prefix_ref_iterator_begin(iter, prefix, trim); + + return do_for_each_ref_iterator(iter, fn, cb_data); +} + int for_each_ref(each_ref_fn fn, void *cb_data) { return do_for_each_ref(NULL, "", fn, 0, 0, cb_data); @@ -52,19 +52,19 @@ #define RESOLVE_REF_NO_RECURSE 0x02 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04 -extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); +const char *resolve_ref_unsafe(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); -extern char *resolve_refdup(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); +char *resolve_refdup(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); -extern int read_ref_full(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); -extern int read_ref(const char *refname, unsigned char *sha1); +int read_ref_full(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); +int read_ref(const char *refname, unsigned char *sha1); -extern int ref_exists(const char *refname); +int ref_exists(const char *refname); -extern int is_branch(const char *refname); +int is_branch(const char *refname); /* * If refname is a non-symbolic reference that refers to a tag object, @@ -74,24 +74,25 @@ extern int is_branch(const char *refname); * Symbolic references are considered unpeelable, even if they * ultimately resolve to a peelable tag. */ -extern int peel_ref(const char *refname, unsigned char *sha1); +int peel_ref(const char *refname, unsigned char *sha1); /** * Resolve refname in the nested "gitlink" repository that is located * at path. If the resolution is successful, return 0 and set sha1 to * the name of the object; otherwise, return a non-zero value. */ -extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1); +int resolve_gitlink_ref(const char *path, const char *refname, + unsigned char *sha1); /* * Return true iff abbrev_name is a possible abbreviation for * full_name according to the rules defined by ref_rev_parse_rules in * refs.c. */ -extern int refname_match(const char *abbrev_name, const char *full_name); +int refname_match(const char *abbrev_name, const char *full_name); -extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); -extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); +int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); /* * A ref_transaction represents a collection of ref updates @@ -140,7 +141,9 @@ extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); struct ref_transaction; /* - * Bit values set in the flags argument passed to each_ref_fn(): + * Bit values set in the flags argument passed to each_ref_fn() and + * stored in ref_iterator::flags. Other bits are for internal use + * only: */ /* Reference is a symbolic reference. */ @@ -182,38 +185,45 @@ typedef int each_ref_fn(const char *refname, * modifies the reference also returns a nonzero value to immediately * stop the iteration. */ -extern int head_ref(each_ref_fn fn, void *cb_data); -extern int for_each_ref(each_ref_fn fn, void *cb_data); -extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); -extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken); -extern int for_each_tag_ref(each_ref_fn fn, void *cb_data); -extern int for_each_branch_ref(each_ref_fn fn, void *cb_data); -extern int for_each_remote_ref(each_ref_fn fn, void *cb_data); -extern int for_each_replace_ref(each_ref_fn fn, void *cb_data); -extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); -extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data); - -extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_ref_in_submodule(const char *submodule, const char *prefix, +int head_ref(each_ref_fn fn, void *cb_data); +int for_each_ref(each_ref_fn fn, void *cb_data); +int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, + unsigned int broken); +int for_each_tag_ref(each_ref_fn fn, void *cb_data); +int for_each_branch_ref(each_ref_fn fn, void *cb_data); +int for_each_remote_ref(each_ref_fn fn, void *cb_data); +int for_each_replace_ref(each_ref_fn fn, void *cb_data); +int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); +int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, + const char *prefix, void *cb_data); + +int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); +int for_each_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_ref_in_submodule(const char *submodule, const char *prefix, each_ref_fn fn, void *cb_data); -extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); +int for_each_tag_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_branch_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_remote_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); -extern int head_ref_namespaced(each_ref_fn fn, void *cb_data); -extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); +int head_ref_namespaced(each_ref_fn fn, void *cb_data); +int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); /* can be used to learn about broken ref and symref */ -extern int for_each_rawref(each_ref_fn fn, void *cb_data); +int for_each_rawref(each_ref_fn fn, void *cb_data); static inline const char *has_glob_specials(const char *pattern) { return strpbrk(pattern, "?*["); } -extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); -extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames); +void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, + const struct string_list *refnames); /* * Flags for controlling behaviour of pack_refs() @@ -245,13 +255,13 @@ int pack_refs(unsigned int flags); int safe_create_reflog(const char *refname, int force_create, struct strbuf *err); /** Reads log for the value of ref during at_time. **/ -extern int read_ref_at(const char *refname, unsigned int flags, - unsigned long at_time, int cnt, - unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); +int read_ref_at(const char *refname, unsigned int flags, + unsigned long at_time, int cnt, + unsigned char *sha1, char **msg, + unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /** Check if a particular reflog exists */ -extern int reflog_exists(const char *refname); +int reflog_exists(const char *refname); /* * Delete the specified reference. If old_sha1 is non-NULL, then @@ -260,21 +270,26 @@ extern int reflog_exists(const char *refname); * exists, regardless of its old value. It is an error for old_sha1 to * be NULL_SHA1. flags is passed through to ref_transaction_delete(). */ -extern int delete_ref(const char *refname, const unsigned char *old_sha1, - unsigned int flags); +int delete_ref(const char *refname, const unsigned char *old_sha1, + unsigned int flags); /* * Delete the specified references. If there are any problems, emit * errors but attempt to keep going (i.e., the deletes are not done in - * an all-or-nothing transaction). + * an all-or-nothing transaction). flags is passed through to + * ref_transaction_delete(). */ -extern int delete_refs(struct string_list *refnames); +int delete_refs(struct string_list *refnames, unsigned int flags); /** Delete a reflog */ -extern int delete_reflog(const char *refname); +int delete_reflog(const char *refname); /* iterate over reflog entries */ -typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); +typedef int each_reflog_ent_fn( + unsigned char *old_sha1, unsigned char *new_sha1, + const char *committer, unsigned long timestamp, + int tz, const char *msg, void *cb_data); + int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data); int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); @@ -282,7 +297,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void * Calls the specified function for each reflog file until it returns nonzero, * and returns the value */ -extern int for_each_reflog(each_ref_fn, void *); +int for_each_reflog(each_ref_fn fn, void *cb_data); #define REFNAME_ALLOW_ONELEVEL 1 #define REFNAME_REFSPEC_PATTERN 2 @@ -295,16 +310,16 @@ extern int for_each_reflog(each_ref_fn, void *); * allow a single "*" wildcard character in the refspec. No leading or * repeated slashes are accepted. */ -extern int check_refname_format(const char *refname, int flags); +int check_refname_format(const char *refname, int flags); -extern const char *prettify_refname(const char *refname); +const char *prettify_refname(const char *refname); -extern char *shorten_unambiguous_ref(const char *refname, int strict); +char *shorten_unambiguous_ref(const char *refname, int strict); /** rename ref, return 0 on success **/ -extern int rename_ref(const char *oldref, const char *newref, const char *logmsg); +int rename_ref(const char *oldref, const char *newref, const char *logmsg); -extern int create_symref(const char *refname, const char *target, const char *logmsg); +int create_symref(const char *refname, const char *target, const char *logmsg); /* * Update HEAD of the specified gitdir. @@ -313,7 +328,7 @@ extern int create_symref(const char *refname, const char *target, const char *lo * $GIT_DIR points to. * Return 0 if successful, non-zero otherwise. * */ -extern int set_worktree_head_symref(const char *gitdir, const char *target); +int set_worktree_head_symref(const char *gitdir, const char *target); enum action_on_err { UPDATE_REFS_MSG_ON_ERR, @@ -463,7 +478,7 @@ int update_ref(const char *msg, const char *refname, const unsigned char *new_sha1, const unsigned char *old_sha1, unsigned int flags, enum action_on_err onerr); -extern int parse_hide_refs_config(const char *var, const char *value, const char *); +int parse_hide_refs_config(const char *var, const char *value, const char *); /* * Check whether a ref is hidden. If no namespace is set, both the first and @@ -473,7 +488,7 @@ extern int parse_hide_refs_config(const char *var, const char *value, const char * the ref is outside that namespace, the first parameter is NULL. The second * parameter always points to the full ref name. */ -extern int ref_is_hidden(const char *, const char *); +int ref_is_hidden(const char *, const char *); enum ref_type { REF_TYPE_PER_WORKTREE, @@ -522,11 +537,11 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data); * enum expire_reflog_flags. The three function pointers are described * above. On success, return zero. */ -extern int reflog_expire(const char *refname, const unsigned char *sha1, - unsigned int flags, - reflog_expiry_prepare_fn prepare_fn, - reflog_expiry_should_prune_fn should_prune_fn, - reflog_expiry_cleanup_fn cleanup_fn, - void *policy_cb_data); +int reflog_expire(const char *refname, const unsigned char *sha1, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data); #endif /* REFS_H */ diff --git a/refs/files-backend.c b/refs/files-backend.c index 1bf643025c..12290d2496 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,6 +1,8 @@ #include "../cache.h" #include "../refs.h" #include "refs-internal.h" +#include "../iterator.h" +#include "../dir-iterator.h" #include "../lockfile.h" #include "../object.h" #include "../dir.h" @@ -513,68 +515,36 @@ static void sort_ref_dir(struct ref_dir *dir) } /* - * Return true iff the reference described by entry can be resolved to - * an object in the database. Emit a warning if the referred-to - * object does not exist. + * Return true if refname, which has the specified oid and flags, can + * be resolved to an object in the database. If the referred-to object + * does not exist, emit a warning and return false. */ -static int ref_resolves_to_object(struct ref_entry *entry) +static int ref_resolves_to_object(const char *refname, + const struct object_id *oid, + unsigned int flags) { - if (entry->flag & REF_ISBROKEN) + if (flags & REF_ISBROKEN) return 0; - if (!has_sha1_file(entry->u.value.oid.hash)) { - error("%s does not point to a valid object!", entry->name); + if (!has_sha1_file(oid->hash)) { + error("%s does not point to a valid object!", refname); return 0; } return 1; } /* - * current_ref is a performance hack: when iterating over references - * using the for_each_ref*() functions, current_ref is set to the - * current reference's entry before calling the callback function. If - * the callback function calls peel_ref(), then peel_ref() first - * checks whether the reference to be peeled is the current reference - * (it usually is) and if so, returns that reference's peeled version - * if it is available. This avoids a refname lookup in a common case. + * Return true if the reference described by entry can be resolved to + * an object in the database; otherwise, emit a warning and return + * false. */ -static struct ref_entry *current_ref; - -typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); - -struct ref_entry_cb { - const char *base; - int trim; - int flags; - each_ref_fn *fn; - void *cb_data; -}; - -/* - * Handle one reference in a do_for_each_ref*()-style iteration, - * calling an each_ref_fn for each entry. - */ -static int do_one_ref(struct ref_entry *entry, void *cb_data) +static int entry_resolves_to_object(struct ref_entry *entry) { - struct ref_entry_cb *data = cb_data; - struct ref_entry *old_current_ref; - int retval; - - if (!starts_with(entry->name, data->base)) - return 0; - - if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) && - !ref_resolves_to_object(entry)) - return 0; - - /* Store the old value, in case this is a recursive call: */ - old_current_ref = current_ref; - current_ref = entry; - retval = data->fn(entry->name + data->trim, &entry->u.value.oid, - entry->flag, data->cb_data); - current_ref = old_current_ref; - return retval; + return ref_resolves_to_object(entry->name, + &entry->u.value.oid, entry->flag); } +typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); + /* * Call fn for each reference in dir that has index in the range * offset <= index < dir->nr. Recurse into subdirectories that are in @@ -604,78 +574,6 @@ static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset, } /* - * Call fn for each reference in the union of dir1 and dir2, in order - * by refname. Recurse into subdirectories. If a value entry appears - * in both dir1 and dir2, then only process the version that is in - * dir2. The input dirs must already be sorted, but subdirs will be - * sorted as needed. fn is called for all references, including - * broken ones. - */ -static int do_for_each_entry_in_dirs(struct ref_dir *dir1, - struct ref_dir *dir2, - each_ref_entry_fn fn, void *cb_data) -{ - int retval; - int i1 = 0, i2 = 0; - - assert(dir1->sorted == dir1->nr); - assert(dir2->sorted == dir2->nr); - while (1) { - struct ref_entry *e1, *e2; - int cmp; - if (i1 == dir1->nr) { - return do_for_each_entry_in_dir(dir2, i2, fn, cb_data); - } - if (i2 == dir2->nr) { - return do_for_each_entry_in_dir(dir1, i1, fn, cb_data); - } - e1 = dir1->entries[i1]; - e2 = dir2->entries[i2]; - cmp = strcmp(e1->name, e2->name); - if (cmp == 0) { - if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) { - /* Both are directories; descend them in parallel. */ - struct ref_dir *subdir1 = get_ref_dir(e1); - struct ref_dir *subdir2 = get_ref_dir(e2); - sort_ref_dir(subdir1); - sort_ref_dir(subdir2); - retval = do_for_each_entry_in_dirs( - subdir1, subdir2, fn, cb_data); - i1++; - i2++; - } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) { - /* Both are references; ignore the one from dir1. */ - retval = fn(e2, cb_data); - i1++; - i2++; - } else { - die("conflict between reference and directory: %s", - e1->name); - } - } else { - struct ref_entry *e; - if (cmp < 0) { - e = e1; - i1++; - } else { - e = e2; - i2++; - } - if (e->flag & REF_DIR) { - struct ref_dir *subdir = get_ref_dir(e); - sort_ref_dir(subdir); - retval = do_for_each_entry_in_dir( - subdir, 0, fn, cb_data); - } else { - retval = fn(e, cb_data); - } - } - if (retval) - return retval; - } -} - -/* * Load all of the refs from the dir into our in-memory cache. The hard work * of loading loose refs is done by get_ref_dir(), so we just need to recurse * through all of the sub-directories. We do not even need to care about @@ -691,6 +589,153 @@ static void prime_ref_dir(struct ref_dir *dir) } } +/* + * A level in the reference hierarchy that is currently being iterated + * through. + */ +struct cache_ref_iterator_level { + /* + * The ref_dir being iterated over at this level. The ref_dir + * is sorted before being stored here. + */ |