Update iterators for consistency across library

This updates all the `foreach()` type functions across the library
that take callbacks from the user to have a consistent behavior.
The rules are:

* A callback terminates the loop by returning any non-zero value
* Once the callback returns non-zero, it will not be called again
  (i.e. the loop stops all iteration regardless of state)
* If the callback returns non-zero, the parent fn returns GIT_EUSER
* Although the parent returns GIT_EUSER, no error will be set in
  the library and `giterr_last()` will return NULL if called.

This commit makes those changes across the library and adds tests
for most of the iteration APIs to make sure that they follow the
above rules.
This commit is contained in:
Russell Belfer 2012-08-03 17:08:01 -07:00
parent 2031760c62
commit 5dca201072
32 changed files with 401 additions and 147 deletions

View File

@ -172,18 +172,17 @@ GIT_EXTERN(int) git_attr_get_many(
* *
* @param repo The repository containing the path. * @param repo The repository containing the path.
* @param flags A combination of GIT_ATTR_CHECK... flags. * @param flags A combination of GIT_ATTR_CHECK... flags.
* @param path The path inside the repo to check attributes. This * @param path Path inside the repo to check attributes. This does not have
* does not have to exist, but if it does not, then * to exist, but if it does not, then it will be treated as a
* it will be treated as a plain file (i.e. not a directory). * plain file (i.e. not a directory).
* @param callback The function that will be invoked on each attribute * @param callback Function to invoke on each attribute name and value. The
* and attribute value. The name parameter will be the name * value may be NULL is the attribute is explicitly set to
* of the attribute and the value will be the value it is * UNSPECIFIED using the '!' sign. Callback will be invoked
* set to, including possibly NULL if the attribute is * only once per attribute name, even if there are multiple
* explicitly set to UNSPECIFIED using the ! sign. This * rules for a given file. The highest priority rule will be
* will be invoked only once per attribute name, even if * used. Return a non-zero value from this to stop looping.
* there are multiple rules for a given file. The highest
* priority rule will be used.
* @param payload Passed on as extra parameter to callback function. * @param payload Passed on as extra parameter to callback function.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_attr_foreach( GIT_EXTERN(int) git_attr_foreach(
git_repository *repo, git_repository *repo,

View File

@ -74,6 +74,8 @@ GIT_EXTERN(int) git_branch_delete(
/** /**
* Loop over all the branches and issue a callback for each one. * Loop over all the branches and issue a callback for each one.
* *
* If the callback returns a non-zero value, this will stop looping.
*
* @param repo Repository where to find the branches. * @param repo Repository where to find the branches.
* *
* @param list_flags Filtering flags for the branch * @param list_flags Filtering flags for the branch
@ -84,7 +86,7 @@ GIT_EXTERN(int) git_branch_delete(
* *
* @param payload Extra parameter to callback function. * @param payload Extra parameter to callback function.
* *
* @return 0 or an error code. * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_branch_foreach( GIT_EXTERN(int) git_branch_foreach(
git_repository *repo, git_repository *repo,

View File

@ -302,12 +302,12 @@ GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name);
* The callback receives the normalized name and value of each variable * The callback receives the normalized name and value of each variable
* in the config backend, and the data pointer passed to this function. * in the config backend, and the data pointer passed to this function.
* As soon as one of the callback functions returns something other than 0, * As soon as one of the callback functions returns something other than 0,
* this function returns that value. * this function stops iterating and returns `GIT_EUSER`.
* *
* @param cfg where to get the variables from * @param cfg where to get the variables from
* @param callback the function to call on each variable * @param callback the function to call on each variable
* @param payload the data to pass to the callback * @param payload the data to pass to the callback
* @return 0 or the return value of the callback which didn't return 0 * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_config_foreach( GIT_EXTERN(int) git_config_foreach(
git_config *cfg, git_config *cfg,

View File

@ -332,6 +332,9 @@ GIT_EXTERN(int) git_diff_merge(
* callbacks will not be invoked for binary files on the diff list or for * callbacks will not be invoked for binary files on the diff list or for
* files whose only changed is a file mode change. * files whose only changed is a file mode change.
* *
* Returning a non-zero value from any of the callbacks will terminate
* the iteration and cause this return `GIT_EUSER`.
*
* @param diff A git_diff_list generated by one of the above functions. * @param diff A git_diff_list generated by one of the above functions.
* @param cb_data Reference pointer that will be passed to your callbacks. * @param cb_data Reference pointer that will be passed to your callbacks.
* @param file_cb Callback function to make per file in the diff. * @param file_cb Callback function to make per file in the diff.
@ -341,6 +344,7 @@ GIT_EXTERN(int) git_diff_merge(
* @param line_cb Optional callback to make per line of diff text. This * @param line_cb Optional callback to make per line of diff text. This
* same callback will be made for context lines, added, and * same callback will be made for context lines, added, and
* removed lines, and even for a deleted trailing newline. * removed lines, and even for a deleted trailing newline.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_diff_foreach( GIT_EXTERN(int) git_diff_foreach(
git_diff_list *diff, git_diff_list *diff,
@ -351,6 +355,14 @@ GIT_EXTERN(int) git_diff_foreach(
/** /**
* Iterate over a diff generating text output like "git diff --name-status". * Iterate over a diff generating text output like "git diff --name-status".
*
* Returning a non-zero value from the callbacks will terminate the
* iteration and cause this return `GIT_EUSER`.
*
* @param diff A git_diff_list generated by one of the above functions.
* @param cb_data Reference pointer that will be passed to your callback.
* @param print_cb Callback to make per line of diff text.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_diff_print_compact( GIT_EXTERN(int) git_diff_print_compact(
git_diff_list *diff, git_diff_list *diff,
@ -362,6 +374,9 @@ GIT_EXTERN(int) git_diff_print_compact(
* *
* This is a super easy way to generate a patch from a diff. * This is a super easy way to generate a patch from a diff.
* *
* Returning a non-zero value from the callbacks will terminate the
* iteration and cause this return `GIT_EUSER`.
*
* @param diff A git_diff_list generated by one of the above functions. * @param diff A git_diff_list generated by one of the above functions.
* @param cb_data Reference pointer that will be passed to your callbacks. * @param cb_data Reference pointer that will be passed to your callbacks.
* @param print_cb Callback function to output lines of the diff. This * @param print_cb Callback function to output lines of the diff. This
@ -369,6 +384,7 @@ GIT_EXTERN(int) git_diff_print_compact(
* headers, and diff lines. Fortunately, you can probably * headers, and diff lines. Fortunately, you can probably
* use various GIT_DIFF_LINE constants to determine what * use various GIT_DIFF_LINE constants to determine what
* text you are given. * text you are given.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_diff_print_patch( GIT_EXTERN(int) git_diff_print_patch(
git_diff_list *diff, git_diff_list *diff,
@ -393,6 +409,8 @@ GIT_EXTERN(int) git_diff_print_patch(
* When at least one of the blobs being dealt with is binary, the * When at least one of the blobs being dealt with is binary, the
* `git_diff_delta` binary attribute will be set to 1 and no call to the * `git_diff_delta` binary attribute will be set to 1 and no call to the
* hunk_cb nor line_cb will be made. * hunk_cb nor line_cb will be made.
*
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_diff_blobs( GIT_EXTERN(int) git_diff_blobs(
git_blob *old_blob, git_blob *old_blob,

View File

@ -25,6 +25,7 @@ enum {
GIT_EEXISTS = -4, GIT_EEXISTS = -4,
GIT_EAMBIGUOUS = -5, GIT_EAMBIGUOUS = -5,
GIT_EBUFS = -6, GIT_EBUFS = -6,
GIT_EUSER = -7,
GIT_PASSTHROUGH = -30, GIT_PASSTHROUGH = -30,
GIT_REVWALKOVER = -31, GIT_REVWALKOVER = -31,

View File

@ -119,19 +119,21 @@ typedef struct {
* *
* @param repo Repository where to find the notes. * @param repo Repository where to find the notes.
* *
* @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits". * @param notes_ref OID reference to read from (optional); defaults to
* "refs/notes/commits".
* *
* @param note_cb Callback to invoke per found annotation. * @param note_cb Callback to invoke per found annotation. Return non-zero
* to stop looping.
* *
* @param payload Extra parameter to callback function. * @param payload Extra parameter to callback function.
* *
* @return 0 or an error code. * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_note_foreach( GIT_EXTERN(int) git_note_foreach(
git_repository *repo, git_repository *repo,
const char *notes_ref, const char *notes_ref,
int (*note_cb)(git_note_data *note_data, void *payload), int (*note_cb)(git_note_data *note_data, void *payload),
void *payload void *payload
); );
/** @} */ /** @} */

View File

@ -176,13 +176,14 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
* List all objects available in the database * List all objects available in the database
* *
* The callback will be called for each object available in the * The callback will be called for each object available in the
* database. Note that the objects are likely to be returned in the * database. Note that the objects are likely to be returned in the index
* index order, which would make accessing the objects in that order * order, which would make accessing the objects in that order inefficient.
* inefficient. * Return a non-zero value from the callback to stop looping.
* *
* @param db database to use * @param db database to use
* @param cb the callback to call for each object * @param cb the callback to call for each object
* @param data data to pass to the callback * @param data data to pass to the callback
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data); GIT_EXTERN(int) git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data);

View File

@ -268,14 +268,15 @@ GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, un
* *
* The `callback` function will be called for each of the references * The `callback` function will be called for each of the references
* in the repository, and will receive the name of the reference and * in the repository, and will receive the name of the reference and
* the `payload` value passed to this method. * the `payload` value passed to this method. Returning a non-zero
* value from the callback will terminate the iteration.
* *
* @param repo Repository where to find the refs * @param repo Repository where to find the refs
* @param list_flags Filtering flags for the reference * @param list_flags Filtering flags for the reference
* listing. * listing.
* @param callback Function which will be called for every listed ref * @param callback Function which will be called for every listed ref
* @param payload Additional data to pass to the callback * @param payload Additional data to pass to the callback
* @return 0 or an error code * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload); GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);

View File

@ -133,9 +133,12 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
* The remote (or more exactly its transport) must be connected. The * The remote (or more exactly its transport) must be connected. The
* memory belongs to the remote. * memory belongs to the remote.
* *
* If you a return a non-zero value from the callback, this will stop
* looping over the refs.
*
* @param refs where to store the refs * @param refs where to store the refs
* @param remote the remote * @param remote the remote
* @return 0 or an error code * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload); GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);

View File

@ -38,11 +38,11 @@ enum {
* *
* The callback is passed the path of the file, the status and the data * The callback is passed the path of the file, the status and the data
* pointer passed to this function. If the callback returns something other * pointer passed to this function. If the callback returns something other
* than 0, this function will return that value. * than 0, this function will stop looping and return GIT_EUSER.
* *
* @param repo a repository object * @param repo a repository object
* @param callback the function to call on each file * @param callback the function to call on each file
* @return 0 on success or the return value of the callback that was non-zero * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
GIT_EXTERN(int) git_status_foreach( GIT_EXTERN(int) git_status_foreach(
git_repository *repo, git_repository *repo,

View File

@ -163,11 +163,14 @@ int git_attr_foreach(
continue; continue;
git_strmap_insert(seen, assign->name, assign, error); git_strmap_insert(seen, assign->name, assign, error);
if (error >= 0) if (error < 0)
error = callback(assign->name, assign->value, payload);
if (error != 0)
goto cleanup; goto cleanup;
error = callback(assign->name, assign->value, payload);
if (error) {
error = GIT_EUSER;
goto cleanup;
}
} }
} }
} }

View File

@ -218,8 +218,10 @@ static int file_foreach(
continue; continue;
/* abort iterator on non-zero return value */ /* abort iterator on non-zero return value */
if ((result = fn(key, var->value, data)) != 0) if (fn(key, var->value, data)) {
result = GIT_EUSER;
goto cleanup; goto cleanup;
}
} }
); );

View File

@ -23,6 +23,7 @@ typedef struct {
unsigned int index; unsigned int index;
git_diff_delta *delta; git_diff_delta *delta;
git_diff_range range; git_diff_range range;
int error;
} diff_output_info; } diff_output_info;
static int read_next_int(const char **str, int *value) static int read_next_int(const char **str, int *value)
@ -49,25 +50,24 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
if (*scan != '@') if (*scan != '@')
return -1; info->error = -1;
else if (read_next_int(&scan, &range.old_start) < 0)
info->error = -1;
else if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
info->error = -1;
else if (read_next_int(&scan, &range.new_start) < 0)
info->error = -1;
else if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
info->error = -1;
else if (range.old_start < 0 || range.new_start < 0)
info->error = -1;
else {
memcpy(&info->range, &range, sizeof(git_diff_range));
if (read_next_int(&scan, &range.old_start) < 0) if (info->hunk_cb(
return -1; info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size))
if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0) info->error = GIT_EUSER;
return -1; }
if (read_next_int(&scan, &range.new_start) < 0)
return -1;
if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
return -1;
if (range.old_start < 0 || range.new_start < 0)
return -1;
memcpy(&info->range, &range, sizeof(git_diff_range));
return info->hunk_cb(
info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
} }
if ((len == 2 || len == 3) && info->line_cb) { if ((len == 2 || len == 3) && info->line_cb) {
@ -80,23 +80,24 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
GIT_DIFF_LINE_CONTEXT; GIT_DIFF_LINE_CONTEXT;
if (info->line_cb( if (info->line_cb(
info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0) info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size))
return -1; info->error = GIT_EUSER;
/* This should only happen if we are adding a line that does not /* This should only happen if we are adding a line that does not
* have a newline at the end and the old code did. In that case, * have a newline at the end and the old code did. In that case,
* we have a ADD with a DEL_EOFNL as a pair. * we have a ADD with a DEL_EOFNL as a pair.
*/ */
if (len == 3) { else if (len == 3) {
origin = (origin == GIT_DIFF_LINE_ADDITION) ? origin = (origin == GIT_DIFF_LINE_ADDITION) ?
GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL; GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
return info->line_cb( if (info->line_cb(
info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size); info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size))
info->error = GIT_EUSER;
} }
} }
return 0; return info->error;
} }
#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY) #define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
@ -318,6 +319,7 @@ int git_diff_foreach(
xdemitconf_t xdiff_config; xdemitconf_t xdiff_config;
xdemitcb_t xdiff_callback; xdemitcb_t xdiff_callback;
memset(&info, 0, sizeof(info));
info.diff = diff; info.diff = diff;
info.cb_data = data; info.cb_data = data;
info.hunk_cb = hunk_cb; info.hunk_cb = hunk_cb;
@ -422,11 +424,11 @@ int git_diff_foreach(
* diffs to tell if a file has really been changed. * diffs to tell if a file has really been changed.
*/ */
if (file_cb != NULL) { if (file_cb != NULL &&
error = file_cb( file_cb(data, delta, (float)info.index / diff->deltas.length))
data, delta, (float)info.index / diff->deltas.length); {
if (error < 0) error = GIT_EUSER;
goto cleanup; goto cleanup;
} }
/* don't do hunk and line diffs if file is binary */ /* don't do hunk and line diffs if file is binary */
@ -451,6 +453,7 @@ int git_diff_foreach(
xdl_diff(&old_xdiff_data, &new_xdiff_data, xdl_diff(&old_xdiff_data, &new_xdiff_data,
&xdiff_params, &xdiff_config, &xdiff_callback); &xdiff_params, &xdiff_config, &xdiff_callback);
error = info.error;
cleanup: cleanup:
release_content(&delta->old_file, &old_data, old_blob); release_content(&delta->old_file, &old_data, old_blob);
@ -524,7 +527,11 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
if (git_buf_oom(pi->buf)) if (git_buf_oom(pi->buf))
return -1; return -1;
return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
return GIT_EUSER;
return 0;
} }
int git_diff_print_compact( int git_diff_print_compact(
@ -586,7 +593,6 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
const char *oldpath = delta->old_file.path; const char *oldpath = delta->old_file.path;
const char *newpfx = pi->diff->opts.new_prefix; const char *newpfx = pi->diff->opts.new_prefix;
const char *newpath = delta->new_file.path; const char *newpath = delta->new_file.path;
int result;
GIT_UNUSED(progress); GIT_UNUSED(progress);
@ -619,9 +625,8 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
if (git_buf_oom(pi->buf)) if (git_buf_oom(pi->buf))
return -1; return -1;
result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
if (result < 0) return GIT_EUSER;
return result;
if (delta->binary != 1) if (delta->binary != 1)
return 0; return 0;
@ -633,7 +638,11 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
if (git_buf_oom(pi->buf)) if (git_buf_oom(pi->buf))
return -1; return -1;
return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
return GIT_EUSER;
return 0;
} }
static int print_patch_hunk( static int print_patch_hunk(
@ -649,7 +658,11 @@ static int print_patch_hunk(
if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0) if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
return -1; return -1;
return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); if (pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
return GIT_EUSER;
return 0;
} }
static int print_patch_line( static int print_patch_line(
@ -674,7 +687,11 @@ static int print_patch_line(
if (git_buf_oom(pi->buf)) if (git_buf_oom(pi->buf))
return -1; return -1;
return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); if (pi->print_cb(pi->cb_data, delta, range, line_origin,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
return GIT_EUSER;
return 0;
} }
int git_diff_print_patch( int git_diff_print_patch(
@ -763,11 +780,8 @@ int git_diff_blobs(
if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0) if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
return -1; return -1;
if (file_cb != NULL) { if (file_cb != NULL && file_cb(cb_data, &delta, 1))
int error = file_cb(cb_data, &delta, 1); return GIT_EUSER;
if (error < 0)
return error;
}
/* don't do hunk and line diffs if the two blobs are identical */ /* don't do hunk and line diffs if the two blobs are identical */
if (delta.status == GIT_DELTA_UNMODIFIED) if (delta.status == GIT_DELTA_UNMODIFIED)
@ -777,6 +791,7 @@ int git_diff_blobs(
if (delta.binary == 1) if (delta.binary == 1)
return 0; return 0;
memset(&info, 0, sizeof(info));
info.diff = NULL; info.diff = NULL;
info.delta = &delta; info.delta = &delta;
info.cb_data = cb_data; info.cb_data = cb_data;
@ -790,5 +805,5 @@ int git_diff_blobs(
xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback); xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
return 0; return info.error;
} }

View File

@ -522,11 +522,11 @@ static int process_entry_path(
int (*note_cb)(git_note_data *note_data, void *payload), int (*note_cb)(git_note_data *note_data, void *payload),
void *payload) void *payload)
{ {
int i = 0, j = 0, error = -1, len; int i = 0, j = 0, error, len;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
git_note_data note_data; git_note_data note_data;
if (git_buf_puts(&buf, entry_path) < 0) if ((error = git_buf_puts(&buf, entry_path)) < 0)
goto cleanup; goto cleanup;
len = git_buf_len(&buf); len = git_buf_len(&buf);
@ -539,7 +539,6 @@ static int process_entry_path(
if (git__fromhex(buf.ptr[i]) < 0) { if (git__fromhex(buf.ptr[i]) < 0) {
/* This is not a note entry */ /* This is not a note entry */
error = 0;
goto cleanup; goto cleanup;
} }
@ -555,16 +554,17 @@ static int process_entry_path(
if (j != GIT_OID_HEXSZ) { if (j != GIT_OID_HEXSZ) {
/* This is not a note entry */ /* This is not a note entry */
error = 0;
goto cleanup; goto cleanup;
} }
if (git_oid_fromstr(&note_data.annotated_object_oid, buf.ptr) < 0) if ((error = git_oid_fromstr(
return -1; &note_data.annotated_object_oid, buf.ptr)) < 0)
goto cleanup;
git_oid_cpy(&note_data.blob_oid, note_oid); git_oid_cpy(&note_data.blob_oid, note_oid);
error = note_cb(&note_data, payload); if (note_cb(&note_data, payload))
error = GIT_EUSER;
cleanup: cleanup:
git_buf_free(&buf); git_buf_free(&buf);
@ -577,34 +577,27 @@ int git_note_foreach(
int (*note_cb)(git_note_data *note_data, void *payload), int (*note_cb)(git_note_data *note_data, void *payload),
void *payload) void *payload)
{ {
int error = -1; int error;
git_iterator *iter = NULL; git_iterator *iter = NULL;
git_tree *tree = NULL; git_tree *tree = NULL;
git_commit *commit = NULL; git_commit *commit = NULL;
const git_index_entry *item; const git_index_entry *item;
if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, &notes_ref)) < 0) if (!(error = retrieve_note_tree_and_commit(
goto cleanup; &tree, &commit, repo, &notes_ref)) &&
!(error = git_iterator_for_tree(&iter, repo, tree)))
error = git_iterator_current(iter, &item);
if (git_iterator_for_tree(&iter, repo, tree) < 0) while (!error && item) {
goto cleanup; error = process_entry_path(item->path, &item->oid, note_cb, payload);
if (git_iterator_current(iter, &item) < 0) if (!error)
goto cleanup; error = git_iterator_advance(iter, &item);
while (item) {
if (process_entry_path(item->path, &item->oid, note_cb, payload) < 0)
goto cleanup;
if (git_iterator_advance(iter, &item) < 0)
goto cleanup;
} }
error = 0;
cleanup:
git_iterator_free(iter); git_iterator_free(iter);
git_tree_free(tree); git_tree_free(tree);
git_commit_free(commit); git_commit_free(commit);
return error; return error;
} }

View File

@ -609,9 +609,12 @@ int git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data
{ {
unsigned int i; unsigned int i;
backend_internal *internal; backend_internal *internal;
git_vector_foreach(&db->backends, i, internal) { git_vector_foreach(&db->backends, i, internal) {
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
b->foreach(b, cb, data); int error = b->foreach(b, cb, data);
if (error < 0)
return error;
} }
return 0; return 0;

View File

@ -680,6 +680,7 @@ struct foreach_state {
size_t dir_len; size_t dir_len;
int (*cb)(git_oid *oid, void *data); int (*cb)(git_oid *oid, void *data);
void *data; void *data;
int cb_error;
}; };
GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
@ -718,8 +719,10 @@ static int foreach_object_dir_cb(void *_state, git_buf *path)
if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
return 0; return 0;
if (state->cb(&oid, state->data) < 0) if (state->cb(&oid, state->data)) {
state->cb_error = GIT_EUSER;
return -1; return -1;
}
return 0; return 0;
} }
@ -728,10 +731,7 @@ static int foreach_cb(void *_state, git_buf *path)
{ {
struct foreach_state *state = (struct foreach_state *) _state; struct foreach_state *state = (struct foreach_state *) _state;
if (git_path_direach(path, foreach_object_dir_cb, state) < 0) return git_path_direach(path, foreach_object_dir_cb, state);
return -1;
return 0;
} }
static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data)
@ -749,14 +749,16 @@ static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *
git_buf_sets(&buf, objects_dir); git_buf_sets(&buf, objects_dir);
git_path_to_dir(&buf); git_path_to_dir(&buf);
memset(&state, 0, sizeof(state));
state.cb = cb; state.cb = cb;
state.data = data; state.data = data;
state.dir_len = git_buf_len(&buf); state.dir_len = git_buf_len(&buf);
error = git_path_direach(&buf, foreach_cb, &state); error = git_path_direach(&buf, foreach_cb, &state);
git_buf_free(&buf); git_buf_free(&buf);
return error; return state.cb_error ? state.cb_error : error;
} }
static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)

View File

@ -422,6 +422,7 @@ static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data)
{ {
int error;
struct git_pack_file *p; struct git_pack_file *p;
struct pack_backend *backend; struct pack_backend *backend;
unsigned int i; unsigned int i;
@ -430,12 +431,14 @@ static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *o
backend = (struct pack_backend *)_backend; backend = (struct pack_backend *)_backend;
/* Make sure we know about the packfiles */ /* Make sure we know about the packfiles */
if (packfile_refresh_all(backend) < 0) if ((error = packfile_refresh_all(backend)) < 0)
return -1; return error;
git_vector_foreach(&backend->packs, i, p) { git_vector_foreach(&backend->packs, i, p) {
git_pack_foreach_entry(p, cb, &data); if ((error = git_pack_foreach_entry(p, cb, &data)) < 0)
return error;
} }
return 0; return 0;
} }

View File

@ -687,10 +687,9 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_
} }
int git_pack_foreach_entry( int git_pack_foreach_entry(
struct git_pack_file *p, struct git_pack_file *p,
int (*cb)(git_oid *oid, void *data), int (*cb)(git_oid *oid, void *data),
void *data) void *data)
{ {
const unsigned char *index = p->index_map.data, *current; const unsigned char *index = p->index_map.data, *current;
unsigned stride; unsigned stride;
@ -722,7 +721,9 @@ int git_pack_foreach_entry(
current = index; current = index;
for (i = 0; i < p->num_objects; i++) { for (i = 0; i < p->num_objects; i++) {
cb((git_oid *)current, data); if (cb((git_oid *)current, data))
return GIT_EUSER;
current += stride; current += stride;
} }

View File

@ -217,6 +217,7 @@ extern int git_path_apply_relative(git_buf *target, const char *relpath);
* the input state and the second arg is pathbuf. The function * the input state and the second arg is pathbuf. The function
* may modify the pathbuf, but only by appending new text. * may modify the pathbuf, but only by appending new text.
* @param state to pass to fn as the first arg. * @param state to pass to fn as the first arg.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/ */
extern int git_path_direach( extern int git_path_direach(
git_buf *pathbuf, git_buf *pathbuf,

View File

@ -501,6 +501,7 @@ struct dirent_list_data {
int (*callback)(const char *, void *); int (*callback)(const char *, void *);
void *callback_payload; void *callback_payload;
int callback_error;
}; };
static int _dirent_loose_listall(void *_data, git_buf *full_path) static int _dirent_loose_listall(void *_data, git_buf *full_path)
@ -521,7 +522,10 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
return 0; /* we are filtering out this reference */ return 0; /* we are filtering out this reference */
} }
return data->callback(file_path, data->callback_payload); if (data->callback(file_path, data->callback_payload))
data->callback_error = GIT_EUSER;
return data->callback_error;
} }
static int _dirent_loose_load(void *data, git_buf *full_path) static int _dirent_loose_load(void *data, git_buf *full_path)
@ -844,15 +848,17 @@ static int reference_path_available(
const char *ref, const char *ref,
const char* old_ref) const char* old_ref)
{ {
int error;
struct reference_available_t data; struct reference_available_t data;
data.new_ref = ref; data.new_ref = ref;
data.old_ref = old_ref; data.old_ref = old_ref;
data.available = 1; data.available = 1;
if (git_reference_foreach(repo, GIT_REF_LISTALL, error = git_reference_foreach(
_reference_available_cb, (void *)&data) < 0) repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data);
return -1; if (error < 0)
return error;
if (!data.available) { if (!data.available) {
giterr_set(GITERR_REFERENCE, giterr_set(GITERR_REFERENCE,
@ -1487,8 +1493,8 @@ int git_reference_foreach(
return -1; return -1;
git_strmap_foreach(repo->references.packfile, ref_name, ref, { git_strmap_foreach(repo->references.packfile, ref_name, ref, {
if (callback(ref_name, payload) < 0) if (callback(ref_name, payload))
return 0; return GIT_EUSER;
}); });
} }
@ -1500,14 +1506,16 @@ int git_reference_foreach(
data.repo = repo; data.repo = repo;
data.callback = callback; data.callback = callback;
data.callback_payload = payload; data.callback_payload = payload;
data.callback_error = 0;
if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0) if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)
return -1; return -1;
result = git_path_direach(&refs_path, _dirent_loose_listall, &data); result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
git_buf_free(&refs_path); git_buf_free(&refs_path);
return result; return data.callback_error ? GIT_EUSER : result;
} }
static int cb__reflist_add(const char *ref, void *data) static int cb__reflist_add(const char *ref, void *data)

View File

@ -114,7 +114,8 @@ int git_status_foreach_ext(
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
for (i = 0; !err && i < idx2head->deltas.length; i++) { for (i = 0; !err && i < idx2head->deltas.length; i++) {
i2h = GIT_VECTOR_GET(&idx2head->deltas, i); i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata); if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
err = GIT_EUSER;
} }
git_diff_list_free(idx2head); git_diff_list_free(idx2head);
idx2head = NULL; idx2head = NULL;
@ -130,14 +131,17 @@ int git_status_foreach_ext(
cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path); cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) { if (cmp < 0) {
err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata); if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
err = GIT_EUSER;
i++; i++;
} else if (cmp > 0) { } else if (cmp > 0) {
err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata); if (cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
j++; j++;
} else { } else {
err = cb(i2h->old_file.path, index_delta2status(i2h->status) | if (cb(i2h->old_file.path, index_delta2status(i2h->status) |
workdir_delta2status(w2i->status), cbdata); workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
i++; j++; i++; j++;
} }
} }
@ -146,6 +150,7 @@ cleanup:
git_tree_free(head); git_tree_free(head);
git_diff_list_free(idx2head); git_diff_list_free(idx2head);
git_diff_list_free(wd2idx); git_diff_list_free(wd2idx);
return err; return err;
} }
@ -166,9 +171,10 @@ int git_status_foreach(
} }
struct status_file_info { struct status_file_info {
char *expected;
unsigned int count; unsigned int count;
unsigned int status; unsigned int status;
char *expected; int ambiguous;
}; };
static int get_one_status(const char *path, unsigned int status, void *data) static int get_one_status(const char *path, unsigned int status, void *data)
@ -183,6 +189,7 @@ static int get_one_status(const char *path, unsigned int status, void *data)
p_fnmatch(sfi->expected, path, 0) != 0)) { p_fnmatch(sfi->expected, path, 0) != 0)) {
giterr_set(GITERR_INVALID, giterr_set(GITERR_INVALID,
"Ambiguous path '%s' given to git_status_file", sfi->expected); "Ambiguous path '%s' given to git_status_file", sfi->expected);
sfi->ambiguous = true;
return GIT_EAMBIGUOUS; return GIT_EAMBIGUOUS;
} }
@ -215,6 +222,9 @@ int git_status_file(
error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi); error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
if (error < 0 && sfi.ambiguous)
error = GIT_EAMBIGUOUS;
if (!error && !sfi.count) { if (!error && !sfi.count) {
giterr_set(GITERR_INVALID, giterr_set(GITERR_INVALID,
"Attempt to get status of nonexistent file '%s'", path); "Attempt to get status of nonexistent file '%s'", path);

View File

@ -239,10 +239,8 @@ static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaqu
pkt = (git_pkt_ref *)p; pkt = (git_pkt_ref *)p;
if (list_cb(&pkt->head, opaque) < 0) { if (list_cb(&pkt->head, opaque))
giterr_set(GITERR_NET, "User callback returned error"); return GIT_EUSER;
return -1;
}
} }
return 0; return 0;

View File

@ -324,10 +324,8 @@ static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaq
if (p->type != GIT_PKT_REF) if (p->type != GIT_PKT_REF)
continue; continue;
if (list_cb(&p->head, opaque) < 0) { if (list_cb(&p->head, opaque))
giterr_set(GITERR_NET, "The user callback returned error"); return GIT_EUSER;
return -1;
}
} }
return 0; return 0;

View File

@ -126,8 +126,8 @@ static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *pay
assert(transport && transport->connected); assert(transport && transport->connected);
git_vector_foreach(refs, i, h) { git_vector_foreach(refs, i, h) {
if (list_cb(h, payload) < 0) if (list_cb(h, payload))
return -1; return GIT_EUSER;
} }
return 0; return 0;

View File

@ -113,6 +113,22 @@ static int count_attrs(
return 0; return 0;
} }
static int cancel_iteration(
const char *name,
const char *value,
void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(value);
*((int *)payload) -= 1;
if (*((int *)payload) < 0)
return -1;
return 0;
}
void test_attr_repo__foreach(void) void test_attr_repo__foreach(void)
{ {
int count; int count;
@ -131,6 +147,12 @@ void test_attr_repo__foreach(void)
cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt", cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
&count_attrs, &count)); &count_attrs, &count));
cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */ cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
count = 2;
cl_assert_equal_i(
GIT_EUSER, git_attr_foreach(
g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
);
} }
void test_attr_repo__manpage_example(void) void test_attr_repo__manpage_example(void)

View File

@ -226,7 +226,7 @@ void test_config_read__foreach(void)
count = 3; count = 3;
cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count)); cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
cl_assert_equal_i(-100, ret); cl_assert_equal_i(GIT_EUSER, ret);
git_config_free(cfg); git_config_free(cfg);
} }

View File

@ -90,3 +90,53 @@ void test_diff_index__0(void)
git_tree_free(a); git_tree_free(a);
git_tree_free(b); git_tree_free(b);
} }
static int diff_stop_after_2_files(
void *cb_data,
git_diff_delta *delta,
float progress)
{
diff_expects *e = cb_data;
GIT_UNUSED(progress);
GIT_UNUSED(delta);
e->files++;
return (e->files == 2);
}
void test_diff_index__1(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
const char *b_commit = "0017bd4ab1ec3"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
cl_assert(a);
cl_assert(b);
opts.context_lines = 1;
opts.interhunk_lines = 1;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
cl_assert_equal_i(
GIT_EUSER,
git_diff_foreach(diff, &exp, diff_stop_after_2_files, NULL, NULL)
);
cl_assert(exp.files == 2);
git_diff_list_free(diff);
diff = NULL;
git_tree_free(a);
git_tree_free(b);
}

View File

@ -95,11 +95,39 @@ void test_notes_notes__can_retrieve_a_list_of_notes_for_a_given_namespace(void)
create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n"); create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes)); cl_git_pass(git_note_foreach
(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes));
cl_assert_equal_i(4, retrieved_notes); cl_assert_equal_i(4, retrieved_notes);
} }
static int note_cancel_cb(git_note_data *note_data, void *payload)
{
unsigned int *count = (unsigned int *)payload;
GIT_UNUSED(note_data);
(*count)++;
return (*count > 2);
}
void test_notes_notes__can_cancel_foreach(void)
{
git_oid note_oid1, note_oid2, note_oid3, note_oid4;
unsigned int retrieved_notes = 0;
create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
cl_assert_equal_i(
GIT_EUSER,
git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes",
note_cancel_cb, &retrieved_notes));
}
void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void) void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void)
{ {
int error; int error;

View File

@ -31,6 +31,24 @@ static int foreach_cb(git_oid *oid, void *data)
void test_odb_foreach__foreach(void) void test_odb_foreach__foreach(void)
{ {
nobj = 0;
cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
cl_assert(nobj == 1683); cl_assert(nobj == 1683);
} }
static int foreach_stop_cb(git_oid *oid, void *data)
{
GIT_UNUSED(data);
GIT_UNUSED(oid);
nobj++;
return (nobj == 1000);
}
void test_odb_foreach__interrupt_foreach(void)
{
nobj = 0;
cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL));
cl_assert(nobj == 1000);
}

View File

@ -126,3 +126,28 @@ void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void
assert_branch_has_been_found(exp, "nulltoken/HEAD"); assert_branch_has_been_found(exp, "nulltoken/HEAD");
assert_branch_has_been_found(exp, "nulltoken/HEAD"); assert_branch_has_been_found(exp, "nulltoken/HEAD");
} }
static int branch_list_interrupt_cb(
const char *branch_name, git_branch_t branch_type, void *payload)
{
int *count;
GIT_UNUSED(branch_type);
GIT_UNUSED(branch_name);
count = (int *)payload;
(*count)++;
return (*count == 5);
}
void test_refs_branches_foreach__can_cancel(void)
{
int count = 0;
cl_assert_equal_i(GIT_EUSER,
git_branch_foreach(repo, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE,
branch_list_interrupt_cb, &count));
cl_assert_equal_i(5, count);
}

View File

@ -68,3 +68,25 @@ void test_refs_foreachglob__retrieve_partially_named_references(void)
assert_retrieval("*test*", GIT_REF_LISTALL, 4); assert_retrieval("*test*", GIT_REF_LISTALL, 4);
} }
static int interrupt_cb(const char *reference_name, void *payload)
{
int *count = (int *)payload;
GIT_UNUSED(reference_name);
(*count)++;
return (*count == 11);
}
void test_refs_foreachglob__can_cancel(void)
{
int count = 0;
cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob(
repo, "*", GIT_REF_LISTALL, interrupt_cb, &count) );
cl_assert_equal_i(11, count);
}

View File

@ -591,7 +591,7 @@ void test_status_worktree__bracket_in_filename(void)
error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET); error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET);
cl_git_fail(error); cl_git_fail(error);
cl_assert(error == GIT_EAMBIGUOUS); cl_assert_equal_i(GIT_EAMBIGUOUS, error);
git_index_free(index); git_index_free(index);
git_repository_free(repo); git_repository_free(repo);
@ -772,3 +772,28 @@ void test_status_worktree__disable_pathspec_match(void)
git_repository_free(repo); git_repository_free(repo);
} }
static int cb_status__interrupt(const char *p, unsigned int s, void *payload)
{
volatile int *count = (int *)payload;
GIT_UNUSED(p);
GIT_UNUSED(s);
(*count)++;
return (*count == 8);
}
void test_status_worktree__interruptable_foreach(void)
{
int count = 0;
git_repository *repo = cl_git_sandbox_init("status");
cl_assert_equal_i(
GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count)
);
cl_assert_equal_i(8, count);
}