mirror of
https://github.com/git/git.git
synced 2025-12-05 18:47:16 -06:00
The ref and reflog iterators have their lifecycle attached to iteration:
once the iterator reaches its end, it is automatically released and the
caller doesn't have to care about that anymore. When the iterator should
be released before it has been exhausted, callers must explicitly abort
the iterator via `ref_iterator_abort()`.
This lifecycle is somewhat unusual in the Git codebase and creates two
problems:
- Callsites need to be very careful about when exactly they call
`ref_iterator_abort()`, as calling the function is only valid when
the iterator itself still is. This leads to somewhat awkward calling
patterns in some situations.
- It is impossible to reuse iterators and re-seek them to a different
prefix. This feature isn't supported by any iterator implementation
except for the reftable iterators anyway, but if it was implemented
it would allow us to optimize cases where we need to search for
specific references repeatedly by reusing internal state.
Detangle the lifecycle from iteration so that we don't deallocate the
iterator anymore once it is exhausted. Instead, callers are now expected
to always call a newly introduce `ref_iterator_free()` function that
deallocates the iterator and its internal state.
Note that the `dir_iterator` is somewhat special because it does not
implement the `ref_iterator` interface, but is only used to implement
other iterators. Consequently, we have to provide `dir_iterator_free()`
instead of `dir_iterator_release()` as the allocated structure itself is
managed by the `dir_iterator` interfaces, as well, and not freed by
`ref_iterator_free()` like in all the other cases.
While at it, drop the return value of `ref_iterator_abort()`, which
wasn't really required by any of the iterator implementations anyway.
Furthermore, stop calling `base_ref_iterator_free()` in any of the
backends, but instead call it in `ref_iterator_free()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
115 lines
3.6 KiB
C
115 lines
3.6 KiB
C
#ifndef DIR_ITERATOR_H
|
|
#define DIR_ITERATOR_H
|
|
|
|
#include "strbuf.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. Note that the original path
|
|
* is not included in the iteration.
|
|
*
|
|
* 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, directory paths are always given before
|
|
* their contents.
|
|
*
|
|
* A typical iteration looks like this:
|
|
*
|
|
* int ok;
|
|
* unsigned int flags = DIR_ITERATOR_PEDANTIC;
|
|
* struct dir_iterator *iter = dir_iterator_begin(path, flags);
|
|
*
|
|
* if (!iter)
|
|
* goto error_handler;
|
|
*
|
|
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
|
|
* if (want_to_stop_iteration()) {
|
|
* ok = ITER_DONE;
|
|
* 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();
|
|
* dir_iterator_free(iter);
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Flags for dir_iterator_begin:
|
|
*
|
|
* - DIR_ITERATOR_PEDANTIC: override dir-iterator's default behavior
|
|
* in case of an error at dir_iterator_advance(), which is to keep
|
|
* looking for a next valid entry. With this flag, resources are freed
|
|
* and ITER_ERROR is returned immediately. In both cases, a meaningful
|
|
* warning is emitted. Note: ENOENT errors are always ignored so that
|
|
* the API users may remove files during iteration.
|
|
*
|
|
* - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
|
|
*/
|
|
#define DIR_ITERATOR_PEDANTIC (1 << 0)
|
|
#define DIR_ITERATOR_SORTED (1 << 1)
|
|
|
|
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 with the combination of
|
|
* options specified by flags. On success, return a dir_iterator
|
|
* that holds the internal state of the iteration. In case of
|
|
* failure, return NULL and set errno accordingly.
|
|
*
|
|
* The iteration includes all paths under path, not including path
|
|
* itself and not including "." or ".." entries.
|
|
*
|
|
* Parameters are:
|
|
* - path is the starting directory. An internal copy will be made.
|
|
* - flags is a combination of the possible flags to initialize a
|
|
* dir-iterator or 0 for default behavior.
|
|
*/
|
|
struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags);
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* It is a bug to use iterator or call this function again after it
|
|
* has returned ITER_DONE or ITER_ERROR (which may be returned iff
|
|
* the DIR_ITERATOR_PEDANTIC flag was set).
|
|
*/
|
|
int dir_iterator_advance(struct dir_iterator *iterator);
|
|
|
|
/* Free the dir_iterator and any associated resources. */
|
|
void dir_iterator_free(struct dir_iterator *iterator);
|
|
|
|
#endif
|