diff options
author | Vicent Marti <vicent@github.com> | 2013-12-13 06:20:19 -0800 |
---|---|---|
committer | Vicent Marti <vicent@github.com> | 2013-12-13 06:20:19 -0800 |
commit | 79194bcdc956406979cd27ac99198826860d3f20 (patch) | |
tree | 407594b97b6e39ef3ac0723dc33aa3162ce933cc | |
parent | 25a1fab0a96fd87e4ebc4ec195ac59a4213e92ad (diff) | |
parent | 7a16d54b5457aa9f60c25a204277ae0ce609ad2e (diff) | |
download | libgit2-79194bcdc956406979cd27ac99198826860d3f20.tar.gz |
Merge pull request #1986 from libgit2/rb/error-handling-cleanups
Clean up some error handling and change callback error behavior
109 files changed, 2325 insertions, 1637 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c19a5a79..48cbccb4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ OPTION( ANDROID "Build for android NDK" OFF ) OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) +OPTION( VALGRIND "Configure build for valgrind" OFF ) IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET( USE_ICONV ON ) @@ -340,9 +341,11 @@ IF (WIN32 AND NOT CYGWIN) ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501) FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h) ELSEIF (AMIGA) - ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R) - FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h) + ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) ELSE() + IF (VALGRIND) + ADD_DEFINITIONS(-DNO_MMAP) + ENDIF() FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h) ENDIF() FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h) diff --git a/include/git2/attr.h b/include/git2/attr.h index f256ff861..3adbb2c24 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -199,8 +199,9 @@ typedef int (*git_attr_foreach_cb)(const char *name, const char *value, void *pa * only once per attribute name, even if there are multiple * rules for a given file. The highest priority rule will be * used. Return a non-zero value from this to stop looping. + * The value will be returned from `git_attr_foreach`. * @param payload Passed on as extra parameter to callback function. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_attr_foreach( git_repository *repo, diff --git a/include/git2/blob.h b/include/git2/blob.h index dcab4fbe0..19ad4d949 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -159,37 +159,32 @@ typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload * Write a loose blob to the Object Database from a * provider of chunks of data. * - * Provided the `hintpath` parameter is filled, its value - * will help to determine what git filters should be applied - * to the object before it can be placed to the object database. + * If the `hintpath` parameter is filled, it will be used to determine + * what git filters should be applied to the object before it is written + * to the object database. * + * The implementation of the callback MUST respect the following rules: * - * The implementation of the callback has to respect the - * following rules: + * - `content` must be filled by the callback. The maximum number of + * bytes that the buffer can accept per call is defined by the + * `max_length` parameter. Allocation and freeing of the buffer will + * be taken care of by libgit2. * - * - `content` will have to be filled by the consumer. The maximum number - * of bytes that the buffer can accept per call is defined by the - * `max_length` parameter. Allocation and freeing of the buffer will be taken - * care of by the function. + * - The `callback` must return the number of bytes that have been + * written to the `content` buffer. * - * - The callback is expected to return the number of bytes - * that `content` have been filled with. - * - * - When there is no more data to stream, the callback should - * return 0. This will prevent it from being invoked anymore. - * - * - When an error occurs, the callback should return -1. + * - When there is no more data to stream, `callback` should return + * 0. This will prevent it from being invoked anymore. * + * - If an error occurs, the callback should return a negative value. + * This value will be returned to the caller. * * @param id Return the id of the written blob - * - * @param repo repository where the blob will be written. - * This repository can be bare or not. - * - * @param hintpath if not NULL, will help selecting the filters - * to apply onto the content of the blob to be created. - * - * @return 0 or an error code + * @param repo Repository where the blob will be written. + * This repository can be bare or not. + * @param hintpath If not NULL, will be used to select data filters + * to apply onto the content of the blob to be created. + * @return 0 or error code (from either libgit2 or callback function) */ GIT_EXTERN(int) git_blob_create_fromchunks( git_oid *id, diff --git a/include/git2/checkout.h b/include/git2/checkout.h index efafdc3e4..0e9d338c6 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -174,7 +174,12 @@ typedef enum { * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files. * * Returning a non-zero value from this callback will cancel the checkout. - * Notification callbacks are made prior to modifying any files on disk. + * The non-zero return value will be propagated back and returned by the + * git_checkout_... call. + * + * Notification callbacks are made prior to modifying any files on disk, + * so canceling on any notification will still happen prior to any files + * being modified. */ typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, @@ -252,9 +257,9 @@ typedef struct git_checkout_opts { * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing - * branch, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non + * existing branch, non-zero value returned by `notify_cb`, or + * other error code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_head( git_repository *repo, @@ -266,8 +271,8 @@ GIT_EXTERN(int) git_checkout_head( * @param repo repository into which to check out (must be non-bare) * @param index index to be checked out (or NULL to use repository index) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_index( git_repository *repo, @@ -282,8 +287,8 @@ GIT_EXTERN(int) git_checkout_index( * @param treeish a commit, tag or tree which content will be used to update * the working directory (or NULL to use HEAD) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_tree( git_repository *repo, diff --git a/include/git2/clone.h b/include/git2/clone.h index 331cf92e7..59a73aa15 100644 --- a/include/git2/clone.h +++ b/include/git2/clone.h @@ -26,23 +26,25 @@ GIT_BEGIN_DECL /** * Clone options structure * - * Use zeros to indicate default settings. It's easiest to use the - * `GIT_CLONE_OPTIONS_INIT` macro: + * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this: * * git_clone_options opts = GIT_CLONE_OPTIONS_INIT; * - * - `checkout_opts` is options for the checkout step. To disable checkout, - * set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT. - * - `bare` should be set to zero to create a standard repo, non-zero for - * a bare repo - * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's - * certificate should be ignored. + * - `checkout_opts` are option passed to the checkout step. To disable + * checkout, set the `checkout_strategy` to GIT_CHECKOUT_NONE. + * Generally you will want the use GIT_CHECKOUT_SAFE_CREATE to create + * all files in the working directory for the newly cloned repository. + * - `bare` should be set to zero (false) to create a standard repo, + * or non-zero for a bare repo + * - `ignore_cert_errors` should be set to 1 if errors validating the + * remote host's certificate should be ignored. * * ** "origin" remote options: ** - * - `remote_name` is the name given to the "origin" remote. The default is - * "origin". - * - `checkout_branch` gives the name of the branch to checkout. NULL means - * use the remote's HEAD. + * + * - `remote_name` is the name to be given to the "origin" remote. The + * default is "origin". + * - `checkout_branch` gives the name of the branch to checkout. NULL + * means use the remote's HEAD. */ typedef struct git_clone_options { @@ -70,16 +72,17 @@ typedef struct git_clone_options { * @param out pointer that will receive the resulting repository object * @param url the remote repository to clone * @param local_path local directory to clone to - * @param options configuration options for the clone. If NULL, the function - * works as though GIT_OPTIONS_INIT were passed. - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @param options configuration options for the clone. If NULL, the + * function works as though GIT_OPTIONS_INIT were passed. + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message) */ GIT_EXTERN(int) git_clone( - git_repository **out, - const char *url, - const char *local_path, - const git_clone_options *options); + git_repository **out, + const char *url, + const char *local_path, + const git_clone_options *options); /** * Clone into a repository @@ -91,11 +94,17 @@ GIT_EXTERN(int) git_clone( * @param repo the repository to use * @param remote the remote repository to clone from * @param co_opts options to use during checkout - * @param branch the branch to checkout after the clone, pass NULL for the remote's - * default branch - * @return 0 on success or an error code + * @param branch the branch to checkout after the clone, pass NULL for the + * remote's default branch + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message) */ -GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch); +GIT_EXTERN(int) git_clone_into( + git_repository *repo, + git_remote *remote, + const git_checkout_opts *co_opts, + const char *branch); /** @} */ GIT_END_DECL diff --git a/include/git2/config.h b/include/git2/config.h index 95da4bc03..f650f1b41 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -450,13 +450,13 @@ GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, co * * The callback receives the normalized name and value of each variable * 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, - * this function stops iterating and returns `GIT_EUSER`. + * If the callback returns a non-zero value, the function stops iterating + * and returns that value to the caller. * * @param cfg where to get the variables from * @param callback the function to call on each variable * @param payload the data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_config_foreach( const git_config *cfg, @@ -612,8 +612,8 @@ GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value); GIT_EXTERN(int) git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data); + git_config_foreach_cb callback, + void *payload); /** @} */ diff --git a/include/git2/diff.h b/include/git2/diff.h index 315cc1215..76fb23654 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -468,7 +468,7 @@ typedef int (*git_diff_line_cb)( * Flags to control the behavior of diff rename/copy detection. */ typedef enum { - /** Obey `diff.renames`. This is overridden by any other GIT_DIFF_FIND_ALL flag. */ + /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */ GIT_DIFF_FIND_BY_CONFIG = 0, /** Look for renames? (`--find-renames`) */ @@ -577,9 +577,9 @@ typedef struct { unsigned int version; /** - * Combination of git_diff_find_t values (default FIND_BY_CONFIG). - * Note that if you don't explicitly set this, `diff.renames` could be set - * to false, resulting in `git_diff_find_similar` doing nothing. + * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG). + * NOTE: if you don't explicitly set this, `diff.renames` could be set + * to false, resulting in `git_diff_find_similar` doing nothing. */ uint32_t flags; @@ -870,7 +870,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); * 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`. + * the iteration and return the value to the user. * * @param diff A git_diff generated by one of the above functions. * @param file_cb Callback function to make per file in the diff. @@ -881,7 +881,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); * same callback will be made for context lines, added, and * removed lines, and even for a deleted trailing newline. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_foreach( git_diff *diff, @@ -918,13 +918,13 @@ typedef enum { * Iterate over a diff generating formatted text output. * * Returning a non-zero value from the callbacks will terminate the - * iteration and cause this return `GIT_EUSER`. + * iteration and return the non-zero value to the caller. * * @param diff A git_diff generated by one of the above functions. * @param format A git_diff_format_t value to pick the text format. * @param print_cb Callback to make per line of diff text. * @param payload Reference pointer that will be passed to your callback. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_print( git_diff *diff, @@ -964,7 +964,7 @@ GIT_EXTERN(int) git_diff_print( * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, @@ -999,7 +999,7 @@ GIT_EXTERN(int) git_diff_blobs( * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, diff --git a/include/git2/errors.h b/include/git2/errors.h index f1a8ea1ae..973d56003 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -8,7 +8,6 @@ #define INCLUDE_git_errors_h__ #include "common.h" -#include "buffer.h" /** * @file git2/errors.h @@ -20,25 +19,38 @@ GIT_BEGIN_DECL /** Generic return codes */ typedef enum { - GIT_OK = 0, - GIT_ERROR = -1, - GIT_ENOTFOUND = -3, - GIT_EEXISTS = -4, - GIT_EAMBIGUOUS = -5, - GIT_EBUFS = -6, - GIT_EUSER = -7, - GIT_EBAREREPO = -8, - GIT_EUNBORNBRANCH = -9, - GIT_EUNMERGED = -10, - GIT_ENONFASTFORWARD = -11, - GIT_EINVALIDSPEC = -12, - GIT_EMERGECONFLICT = -13, - GIT_ELOCKED = -14, + GIT_OK = 0, /*< No error */ - GIT_PASSTHROUGH = -30, - GIT_ITEROVER = -31, + GIT_ERROR = -1, /*< Generic error */ + GIT_ENOTFOUND = -3, /*< Requested object could not be found */ + GIT_EEXISTS = -4, /*< Object exists preventing operation */ + GIT_EAMBIGUOUS = -5, /*< More than one object matches */ + GIT_EBUFS = -6, /*< Output buffer too short to hold data */ + + /* GIT_EUSER is a special error that is never generated by libgit2 + * code. You can return it from a callback (e.g to stop an iteration) + * to know that it was generated by the callback and not by libgit2. + */ + GIT_EUSER = -7, + + GIT_EBAREREPO = -8, /*< Operation not allowed on bare repository */ + GIT_EUNBORNBRANCH = -9, /*< HEAD refers to branch with no commits */ + GIT_EUNMERGED = -10, /*< Merge in progress prevented operation */ + GIT_ENONFASTFORWARD = -11, /*< Reference was not fast-forwardable */ + GIT_EINVALIDSPEC = -12, /*< Name/ref spec was not in a valid format */ + GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ + GIT_ELOCKED = -14, /*< Lock file prevented operation */ + + GIT_PASSTHROUGH = -30, /*< Internal only */ + GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ } git_error_code; +/** + * Structure to store extra details of the last error that occurred. + * + * This is kept on a per-thread basis if GIT_THREADS was defined when the + * library was build, otherwise one is kept globally for the library + */ typedef struct { char *message; int klass; @@ -72,6 +84,7 @@ typedef enum { GITERR_SSH, GITERR_FILTER, GITERR_REVERT, + GITERR_CALLBACK, } git_error_t; /** @@ -91,7 +104,7 @@ GIT_EXTERN(void) giterr_clear(void); * Get the last error data and clear it. * * This copies the last error into the given `git_error` struct - * and returns 0 if the copy was successful, leaving the error + * and returns 0 if the copy was successful, leaving the error * cleared as if `giterr_clear` had been called. * * If there was no existing error in the library, -1 will be returned diff --git a/include/git2/index.h b/include/git2/index.h index a60db370a..ffefad15d 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -493,7 +493,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); * item in the working directory immediately *before* it is added to / * updated in the index. Returning zero will add the item to the index, * greater than zero will skip the item, and less than zero will abort the - * scan and cause GIT_EUSER to be returned. + * scan and return that value to the caller. * * @param index an existing index object * @param pathspec array of path patterns @@ -502,7 +502,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_add_all( git_index *index, @@ -524,7 +524,7 @@ GIT_EXTERN(int) git_index_add_all( * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_remove_all( git_index *index, @@ -553,7 +553,7 @@ GIT_EXTERN(int) git_index_remove_all( * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_update_all( git_index *index, diff --git a/include/git2/notes.h b/include/git2/notes.h index 76361633b..0cb6ce5f1 100644 --- a/include/git2/notes.h +++ b/include/git2/notes.h @@ -189,7 +189,7 @@ GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo); * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_note_foreach( git_repository *repo, diff --git a/include/git2/odb.h b/include/git2/odb.h index ad56384f0..82df4d300 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -189,7 +189,7 @@ GIT_EXTERN(int) git_odb_refresh(struct git_odb *db); * @param db database to use * @param cb the callback to call for each object * @param payload data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload); diff --git a/include/git2/pack.h b/include/git2/pack.h index 88a2716bb..11bb559d8 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -52,7 +52,7 @@ typedef enum { GIT_PACKBUILDER_ADDING_OBJECTS = 0, GIT_PACKBUILDER_DELTAFICATION = 1, } git_packbuilder_stage_t; - + /** * Initialize a new packbuilder * @@ -143,6 +143,7 @@ GIT_EXTERN(int) git_packbuilder_write( GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb); typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); + /** * Create the new pack and pass each object to the callback * diff --git a/include/git2/patch.h b/include/git2/patch.h index 6a6ad92d7..e09f625c0 100644 --- a/include/git2/patch.h +++ b/include/git2/patch.h @@ -218,13 +218,13 @@ GIT_EXTERN(size_t) git_patch_size( * Serialize the patch to text via callback. * * Returning a non-zero value from the callback will terminate the iteration - * and cause this return `GIT_EUSER`. + * and return that value to the caller. * * @param patch A git_patch representing changes to one file * @param print_cb Callback function to output lines of the patch. Will be * called for file headers, hunk headers, and diff lines. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_patch_print( git_patch *patch, diff --git a/include/git2/push.h b/include/git2/push.h index 77ef74039..12f0e7f2c 100644 --- a/include/git2/push.h +++ b/include/git2/push.h @@ -132,17 +132,19 @@ GIT_EXTERN(int) git_push_finish(git_push *push); GIT_EXTERN(int) git_push_unpack_ok(git_push *push); /** - * Call callback `cb' on each status + * Invoke callback `cb' on each status entry * * For each of the updated references, we receive a status report in the * form of `ok refs/heads/master` or `ng refs/heads/master <msg>`. * `msg != NULL` means the reference has not been updated for the given * reason. * + * Return a non-zero value from the callback to stop the loop. + * * @param push The push object * @param cb The callback to call on each object * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_push_status_foreach(git_push *push, int (*cb)(const char *ref, const char *msg, void *data), diff --git a/include/git2/refs.h b/include/git2/refs.h index 4041947f6..e2bfa9615 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -310,20 +310,33 @@ typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload); * Perform a callback on each reference in the repository. * * The `callback` function will be called for each reference in the - * repository, receiving the name of the reference and the `payload` value + * repository, receiving the reference object and 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 callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, git_reference_foreach_cb callback, void *payload); +/** + * Perform a callback on the fully-qualified name of each reference. + * + * The `callback` function will be called for each reference in the + * repository, receiving the name of the reference and 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 callback Function which will be called for every listed ref name + * @param payload Additional data to pass to the callback + * @return 0 on success, non-zero callback return value, or error code + */ GIT_EXTERN(int) git_reference_foreach_name( git_repository *repo, git_reference_foreach_name_cb callback, diff --git a/include/git2/repository.h b/include/git2/repository.h index c6bd4dca4..9f71d2959 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -503,14 +503,18 @@ typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name, void *payload); /** - * Call callback 'callback' for each entry in the given FETCH_HEAD file. + * Invoke 'callback' for each entry in the given FETCH_HEAD file. + * + * Return a non-zero value from the callback to stop the loop. * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no FETCH_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_fetchhead_foreach( + git_repository *repo, git_repository_fetchhead_foreach_cb callback, void *payload); @@ -518,15 +522,19 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid, void *payload); /** - * If a merge is in progress, call callback 'cb' for each commit ID in the + * If a merge is in progress, invoke 'callback' for each commit ID in the * MERGE_HEAD file. * + * Return a non-zero value from the callback to stop the loop. + * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no MERGE_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb callback, void *payload); diff --git a/include/git2/stash.h b/include/git2/stash.h index b48d33f5d..e2fe2cf0b 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -62,19 +62,15 @@ GIT_EXTERN(int) git_stash_save( unsigned int flags); /** - * When iterating over all the stashed states, callback that will be - * issued per entry. + * This is a callback function you can provide to iterate over all the + * stashed states that will be invoked per entry. * * @param index The position within the stash list. 0 points to the - * most recent stashed state. - * + * most recent stashed state. * @param message The stash message. - * * @param stash_id The commit oid of the stashed state. - * * @param payload Extra parameter to callback function. - * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 to continue iterating or non-zero to stop */ typedef int (*git_stash_cb)( size_t index, @@ -89,12 +85,12 @@ typedef int (*git_stash_cb)( * * @param repo Repository where to find the stash. * - * @param callback Callback to invoke per found stashed state. The most recent - * stash state will be enumerated first. + * @param callback Callback to invoke per found stashed state. The most + * recent stash state will be enumerated first. * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_stash_foreach( git_repository *repo, diff --git a/include/git2/status.h b/include/git2/status.h index 4ec3432df..aa68c0da0 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -203,12 +203,12 @@ typedef struct { * into this function. * * If the callback returns a non-zero value, this function will stop looping - * and return GIT_EUSER. + * and return that value to caller. * * @param repo A repository object * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach( git_repository *repo, @@ -227,7 +227,7 @@ GIT_EXTERN(int) git_status_foreach( * @param opts Status options structure * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach_ext( git_repository *repo, diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index 94ad3aed4..8fe21c9c0 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -149,6 +149,7 @@ typedef int (*git_filter_init_fn)(git_filter *self); * Specified as `filter.shutdown`, this is an optional callback invoked * when the filter is unregistered or when libgit2 is shutting down. It * will be called once at most and should release resources as needed. + * This may be called even if the `initialize` callback was not made. * * Typically this function will free the `git_filter` object itself. */ diff --git a/include/git2/tree.h b/include/git2/tree.h index d94b446c2..422365674 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -332,11 +332,18 @@ GIT_EXTERN(int) git_treebuilder_insert( GIT_EXTERN(int) git_treebuilder_remove( git_treebuilder *bld, const char *filename); +/** + * Callback for git_treebuilder_filter + * + * The return value is treated as a boolean, with zero indicating that the + * entry should be left alone and any non-zero value meaning that the + * entry should be removed from the treebuilder list (i.e. filtered out). + */ typedef int (*git_treebuilder_filter_cb)( const git_tree_entry *entry, void *payload); /** - * Filter the entries in the tree + * Selectively remove entries in the tree * * The `filter` callback will be called for each entry in the tree with a * pointer to the entry and the provided `payload`; if the callback returns @@ -344,7 +351,7 @@ typedef int (*git_treebuilder_filter_cb)( * * @param bld Tree builder * @param filter Callback to filter entries - * @param payload Extra data to pass to filter + * @param payload Extra data to pass to filter callback */ GIT_EXTERN(void) git_treebuilder_filter( git_treebuilder *bld, diff --git a/src/amiga/map.c b/src/amiga/map.c deleted file mode 100644 index 0ba7995c6..000000000 --- a/src/amiga/map.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#include <git2/common.h> - -#ifndef GIT_WIN32 - -#include "posix.h" -#include "map.h" -#include <errno.h> - -int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) -{ - GIT_MMAP_VALIDATE(out, len, prot, flags); - - out->data = NULL; - out->len = 0; - - if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { - giterr_set(GITERR_OS, "Trying to map shared-writeable"); - return -1; - } - - out->data = malloc(len); - GITERR_CHECK_ALLOC(out->data); - - if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { - giterr_set(GITERR_OS, "mmap emulation failed"); - return -1; - } - - out->len = len; - return 0; -} - -int p_munmap(git_map *map) -{ - assert(map != NULL); - free(map->data); - - return 0; -} - -#endif - diff --git a/src/attr.c b/src/attr.c index 98a328a55..e6e274e42 100644 --- a/src/attr.c +++ b/src/attr.c @@ -193,8 +193,7 @@ int git_attr_foreach( error = callback(assign->name, assign->value, payload); if (error) { - giterr_clear(); - error = GIT_EUSER; + giterr_set_after_callback(error); goto cleanup; } } @@ -536,7 +535,7 @@ static int collect_attr_files( int error; git_buf dir = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); - attr_walk_up_info info; + attr_walk_up_info info = { NULL }; if (git_attr_cache__init(repo) < 0 || git_vector_init(files, 4, NULL) < 0) @@ -603,11 +602,15 @@ static int attr_cache__lookup_path( { git_buf buf = GIT_BUF_INIT; int error; - const char *cfgval = NULL; + const git_config_entry *entry = NULL; *out = NULL; - if (!(error = git_config_get_string(&cfgval, cfg, key))) { + if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0) + return error; + + if (entry) { + const char *cfgval = entry->value; /* expand leading ~/ as needed */ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && @@ -616,13 +619,9 @@ static int attr_cache__lookup_path( else if (cfgval) *out = git__strdup(cfgval); - } else if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - - if (!git_futils_find_xdg_file(&buf, fallback)) - *out = git_buf_detach(&buf); } + else if (!git_futils_find_xdg_file(&buf, fallback)) + *out = git_buf_detach(&buf); git_buf_free(&buf); diff --git a/src/blame.c b/src/blame.c index ea9f77af5..a1357415a 100644 --- a/src/blame.c +++ b/src/blame.c @@ -108,17 +108,23 @@ git_blame* git_blame__alloc( git_blame_options opts, const char *path) { - git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame)); - if (!gbr) { - giterr_set_oom(); + git_blame *gbr = git__calloc(1, sizeof(git_blame)); + if (!gbr) return NULL; - } - git_vector_init(&gbr->hunks, 8, hunk_cmp); - git_vector_init(&gbr->paths, 8, paths_cmp); + gbr->repository = repo; gbr->options = opts; - gbr->path = git__strdup(path); - git_vector_insert(&gbr->paths, git__strdup(path)); + + if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 || + git_vector_init(&gbr->paths, 8, paths_cmp) < 0 || + (gbr->path = git__strdup(path)) == NULL || + git_vector_insert(&gbr->paths, git__strdup(path)) < 0) + { + git_blame_free(gbr); + git__free(gbr); + return NULL; + } + return gbr; } @@ -126,7 +132,6 @@ void git_blame_free(git_blame *blame) { size_t i; git_blame_hunk *hunk; - char *path; if (!blame) return; @@ -134,13 +139,11 @@ void git_blame_free(git_blame *blame) free_hunk(hunk); git_vector_free(&blame->hunks); - git_vector_foreach(&blame->paths, i, path) - git__free(path); - git_vector_free(&blame->paths); + git_vector_free_deep(&blame->paths); git_array_clear(blame->line_index); - git__free((void*)blame->path); + git__free(blame->path); git_blob_free(blame->final_blob); git__free(blame); } diff --git a/src/blame.h b/src/blame.h index 637e43985..7e23de808 100644 --- a/src/blame.h +++ b/src/blame.h @@ -64,7 +64,7 @@ typedef struct git_blame__entry { } git_blame__entry; struct git_blame { - const char *path; + char *path; git_repository *repository; git_blame_options options; diff --git a/src/blob.c b/src/blob.c index 2c6d52800..ab344ae98 100644 --- a/src/blob.c +++ b/src/blob.c @@ -272,37 +272,44 @@ int git_blob_create_fromchunks( int (*source_cb)(char *content, size_t max_length, void *payload), void *payload) { - int error = -1, read_bytes; + int error; char *content = NULL; git_filebuf file = GIT_FILEBUF_INIT; git_buf path = GIT_BUF_INIT; - if (git_buf_joinpath( - &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0) + assert(oid && repo && source_cb); + + if ((error = git_buf_joinpath( + &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0) goto cleanup; content = git__malloc(BUFFER_SIZE); GITERR_CHECK_ALLOC(content); - if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0) + if ((error = git_filebuf_open( + &file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666)) < 0) goto cleanup; while (1) { - read_bytes = source_cb(content, BUFFER_SIZE, payload); - - assert(read_bytes <= BUFFER_SIZE); + int read_bytes = source_cb(content, BUFFER_SIZE, payload); - if (read_bytes <= 0) + if (!read_bytes) break; - if (git_filebuf_write(&file, content, read_bytes) < 0) + if (read_bytes > BUFFER_SIZE) { + giterr_set(GITERR_OBJECT, "Invalid chunk size while creating blob"); + error = GIT_EBUFS; + } else if (read_bytes < 0) { + error = giterr_set_after_callback(read_bytes); + } else { + error = git_filebuf_write(&file, content, read_bytes); + } + + if (error < 0) goto cleanup; } - if (read_bytes < 0) - goto cleanup; - - if (git_filebuf_flush(&file) < 0) + if ((error = git_filebuf_flush(&file)) < 0) goto cleanup; error = git_blob__create_from_paths( @@ -312,6 +319,7 @@ cleanup: git_buf_free(&path); git_filebuf_cleanup(&file); git__free(content); + return error; } diff --git a/src/branch.c b/src/branch.c index 95b3fd980..ef71c2cd1 100644 --- a/src/branch.c +++ b/src/branch.c @@ -90,29 +90,28 @@ int git_branch_delete(git_reference *branch) assert(branch); - if (!git_reference_is_branch(branch) && - !git_reference_is_remote(branch)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch)); - return -1; + if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { + giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", + git_reference_name(branch)); + return GIT_ENOTFOUND; } if ((is_head = git_branch_is_head(branch)) < 0) return is_head; if (is_head) { - giterr_set(GITERR_REFERENCE, - "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch)); + giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + "the current HEAD of the repository.", git_reference_name(branch)); return -1; } - if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + if (git_buf_join(&config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; if (git_config_rename_section( - git_reference_owner(branch), - git_buf_cstr(&config_section), - NULL) < 0) - goto on_error; + git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) + goto on_error; if (git_reference_delete(branch) < 0) goto on_error; @@ -206,17 +205,21 @@ int git_branch_move( 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); + /* first update ref then config so failure won't trash config */ - if ((error = git_config_rename_section(git_reference_owner(branch), - git_buf_cstr(&old_config_section), - git_buf_cstr(&new_config_section))) < 0) + error = git_reference_rename( + out, branch, git_buf_cstr(&new_reference_name), force); + if (error < 0) goto done; - error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force); + git_buf_join(&old_config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); + git_buf_join(&new_config_section, '.', "branch", new_branch_name); + + error = git_config_rename_section( + git_reference_owner(branch), + git_buf_cstr(&old_config_section), + git_buf_cstr(&new_config_section)); done: git_buf_free(&new_reference_name); diff --git a/src/checkout.c b/src/checkout.c index 6d7e3cfd4..0f30d16f3 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -83,10 +83,8 @@ static int checkout_notify( const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL; const char *path = NULL; - if (!data->opts.notify_cb) - return 0; - - if ((why & data->opts.notify_flags) == 0) + if (!data->opts.notify_cb || + (why & data->opts.notify_flags) == 0) return 0; if (wditem) { @@ -125,8 +123,13 @@ static int checkout_notify( path = delta->old_file.path; } - return data->opts.notify_cb( - why, path, baseline, target, workdir, data->opts.notify_payload); + { + int error = data->opts.notify_cb( + why, path, baseline, target, workdir, data->opts.notify_payload); + + return giterr_set_after_callback_function( + error, "git_checkout notification"); + } } static bool checkout_is_workdir_modified( @@ -186,69 +189,66 @@ static bool checkout_is_workdir_modified( ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO) static int checkout_action_common( + int *action, checkout_data *data, - int action, const git_diff_delta *delta, const git_index_entry *wd) { git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; - if (action <= 0) - return action; - if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) - action = (action & ~CHECKOUT_ACTION__REMOVE); + *action = (*action & ~CHECKOUT_ACTION__REMOVE); - if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { + if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { if (S_ISGITLINK(delta->new_file.mode)) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) | + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) | CHECKOUT_ACTION__UPDATE_SUBMODULE; /* to "update" a symlink, we must remove the old one first */ if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) - action |= CHECKOUT_ACTION__REMOVE; + *action |= CHECKOUT_ACTION__REMOVE; notify = GIT_CHECKOUT_NOTIFY_UPDATED; } - if ((action & CHECKOUT_ACTION__CONFLICT) != 0) + if ((*action & CHECKOUT_ACTION__CONFLICT) != 0) notify = GIT_CHECKOUT_NOTIFY_CONFLICT; - if (notify != GIT_CHECKOUT_NOTIFY_NONE && - checkout_notify(data, notify, delta, wd) != 0) - return GIT_EUSER; - - return action; + return checkout_notify(data, notify, delta, wd); } static int checkout_action_no_wd( + int *action, checkout_data *data, const git_diff_delta *delta) { - int action = CHECKOUT_ACTION__NONE; + int error = 0; + + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 12 */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); + error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL); + if (error) + return error; + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_DELETED: /* case 8 or 25 */ default: /* impossible */ break; } - return checkout_action_common(data, action, delta, NULL); + return checkout_action_common(action, data, delta, NULL); } static int checkout_action_wd_only( @@ -257,6 +257,7 @@ static int checkout_action_wd_only( const git_index_entry *wd, git_vector *pathspec) { + int error = 0; bool remove = false; git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; @@ -269,13 +270,13 @@ static int checkout_action_wd_only( /* check if item is tracked in the index but not in the checkout diff */ if (data->index != NULL) { if (wd->mode != GIT_FILEMODE_TREE) { - int error; - - if ((error = git_index_find(NULL, data->index, wd->path)) == 0) { + if (!(error = git_index_find(NULL, data->index, wd->path))) { notify = GIT_CHECKOUT_NOTIFY_DIRTY; remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); } else if (error != GIT_ENOTFOUND) return error; + else + giterr_clear(); } else { /* for tree entries, we have to see if there are any index * entries that are contained inside that tree @@ -301,18 +302,16 @@ static int checkout_action_wd_only( remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0); } - if (checkout_notify(data, notify, NULL, wd)) - return GIT_EUSER; + error = checkout_notify(data, notify, NULL, wd); - if (remove) { + if (!error && remove) { char *path = git_pool_strdup(&data->pool, wd->path); GITERR_CHECK_ALLOC(path); - if (git_vector_insert(&data->removes, path) < 0) - return -1; + error = git_vector_insert(&data->removes, path); } - return 0; + return error; } static bool submodule_is_config_only( @@ -331,35 +330,35 @@ static bool submodule_is_config_only( } static int checkout_action_with_wd( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) { - if (checkout_notify( - data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); } break; case GIT_DELTA_ADDED: /* case 3, 4 or 6 */ - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */ if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -367,92 +366,93 @@ static int checkout_action_with_wd( /* either deleting items in old tree will delete the wd dir, * or we'll get a conflict when we attempt blob update... */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else if (wd->mode == GIT_FILEMODE_COMMIT) { /* workdir is possibly a "phantom" submodule - treat as a * tree if the only submodule info came from the config */ if (submodule_is_config_only(data, wd->path)) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); } else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); } else if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); /* don't update if the typechange is to a tree */ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB); + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_blocker( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* should show delta as dirty / deleted */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED: case GIT_DELTA_MODIFIED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* not 100% certain about this... */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_dir( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) || - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */ if (delta->old_file.mode == GIT_FILEMODE_COMMIT) /* expected submodule (and maybe found one) */; else if (delta->new_file.mode != GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */ - if (delta->old_file.mode != GIT_FILEMODE_TREE && - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + if (delta->old_file.mode != GIT_FILEMODE_TREE) + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -462,39 +462,41 @@ static int checkout_action_with_wd_dir( * directory if is it left empty, so we can defer removing the * dir and it will succeed if no children are left. */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); - if (action != CHECKOUT_ACTION__NONE) - action |= CHECKOUT_ACTION__DEFER_REMOVE; + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + if (*action != CHECKOUT_ACTION__NONE) + *action |= CHECKOUT_ACTION__DEFER_REMOVE; } else if (delta->new_file.mode != GIT_FILEMODE_TREE) /* For typechange to dir, dir is already created so no action */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action( + int *action, checkout_data *data, git_diff_delta *delta, git_iterator *workdir, - const git_index_entry **wditem_ptr, + const git_index_entry **wditem, git_vector *pathspec) { - const git_index_entry *wd = *wditem_ptr; - int cmp = -1, act; + int cmp = -1, error; int (*strcomp)(const char *, const char *) = data->diff->strcomp; int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp; - int error; + int (*advance)(const git_index_entry **, git_iterator *) = NULL; /* move workdir iterator to follow along with deltas */ while (1) { + const git_index_entry *wd = *wditem; + if (!wd) - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); cmp = strcomp(wd->path, delta->old_file.path); @@ -512,79 +514,77 @@ static int checkout_action( if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ - error = git_iterator_advance_into_or_over(&wd, workdir); - if (error && error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; + error = git_iterator_advance_into_or_over(wditem, workdir); + if (error < 0 && error != GIT_ITEROVER) + goto done; continue; } /* case 3 maybe - wd contains non-dir where dir expected */ if (delta->old_file.path[strlen(wd->path)] == '/') { - act = checkout_action_with_wd_blocker(data, delta, wd); - *wditem_ptr = - git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd_blocker( + action, data, delta, wd); + advance = git_iterator_advance; + goto done; } } /* case 1 - handle wd item (if it matches pathspec) */ - if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0) - goto fail; - if ((error = git_iterator_advance(&wd, workdir)) < 0 && + error = checkout_action_wd_only(data, workdir, wd, pathspec); + if (error) + goto done; + if ((error = git_iterator_advance(wditem, workdir)) < 0 && error != GIT_ITEROVER) - goto fail; - - *wditem_ptr = wd; + goto done; continue; } if (cmp == 0) { /* case 4 */ - act = checkout_action_with_wd(data, delta, wd); - *wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance; + goto done; } cmp = pfxcomp(wd->path, delta->old_file.path); if (cmp == 0) { /* case 5 */ if (wd->path[strlen(delta->old_file.path)] != '/') - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); if (delta->status == GIT_DELTA_TYPECHANGE) { if (delta->old_file.mode == GIT_FILEMODE_TREE) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance_into(&wd, workdir)) < 0 && - error != GIT_ENOTFOUND) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance_into; + goto done; } if (delta->new_file.mode == GIT_FILEMODE_TREE || delta->new_file.mode == GIT_FILEMODE_COMMIT || delta->old_file.mode == GIT_FILEMODE_COMMIT) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance(&wd, workdir)) < 0 && - error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, wd); + advance = git_iterator_advance; + goto done; } } - return checkout_action_with_wd_dir(data, delta, wd); + return checkout_action_with_wd_dir(action, data, delta, wd); } /* case 6 - wd is after delta */ - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); } -fail: - *wditem_ptr = NULL; - return -1; +done: + if (!error && advance != NULL && + (error = advance(wditem, workdir)) < 0) { + *wditem = NULL; + if (error == GIT_ITEROVER) + error = 0; + } + + return error; } static int checkout_remaining_wd_items( @@ -846,7 +846,7 @@ static int checkout_conflicts_coalesce_renames( /* Juggle entries based on renames */ names = git_index_name_entrycount(data->index); - + for (i = 0; i < names; i++) { name_entry = git_index_name_get_byindex(data->index, i); @@ -965,7 +965,7 @@ static int checkout_get_actions( checkout_data *data, git_iterator *workdir) { - int error = 0; + int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; git_pool pathpool = GIT_POOL_INIT_STRINGPOOL; @@ -992,12 +992,9 @@ static int checkout_get_actions( } git_vector_foreach(deltas, i, delta) { - int act = checkout_action(data, delta, workdir, &wditem, &pathspec); - - if (act < 0) { - error = act; + error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec); + if (error != 0) goto fail; - } actions[i] = act; @@ -1012,7 +1009,7 @@ static int checkout_get_actions( } error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec); - if (error < 0) + if (error) goto fail; counts[CHECKOUT_ACTION__REMOVE] += data->removes.length; @@ -1760,9 +1757,6 @@ static int checkout_create_conflicts(checkout_data *data) static void checkout_data_clear(checkout_data *data) { - checkout_conflictdata *conflict; - size_t i; - if (data->opts_free_baseline) { git_tree_free(data->opts.baseline); data->opts.baseline = NULL; @@ -1771,10 +1765,7 @@ static void checkout_data_clear(checkout_data *data) git_vector_free(&data->removes); git_pool_clear(&data->pool); - git_vector_foreach(&data->conflicts, i, conflict) - git__free(conflict); - - git_vector_free(&data->conflicts); + git_vector_free_deep(&data->conflicts); git__free(data->pfx); data->pfx = NULL; @@ -1966,7 +1957,7 @@ int git_checkout_iterator( * actions to be taken, plus look for conflicts and send notifications, * then loop through conflicts. */ - if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0) + if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0) goto cleanup; data.total_steps = counts[CHECKOUT_ACTION__REMOVE] + @@ -1998,9 +1989,6 @@ int git_checkout_iterator( assert(data.completed_steps == data.total_steps); cleanup: - if (error == GIT_EUSER) - giterr_clear(); - if (!error && data.index != NULL && (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) error = git_index_write(data.index); diff --git a/src/clone.c b/src/clone.c index 23aacd478..828c47ffb 100644 --- a/src/clone.c +++ b/src/clone.c @@ -115,43 +115,38 @@ static int reference_matches_remote_head( { struct head_info *head_info = (struct head_info *)payload; git_oid oid; + int error; /* TODO: Should we guard against references * which name doesn't start with refs/heads/ ? */ - /* Stop looking if we've already found a match */ - if (head_info->found) + error = git_reference_name_to_id(&oid, head_info->repo, reference_name); + if (error == GIT_ENOTFOUND) { + /* If the reference doesn't exists, it obviously cannot match the + * expected oid. */ + giterr_clear(); return 0; - - if (git_reference_name_to_id( - &oid, - head_info->repo, - reference_name) < 0) { - /* If the reference doesn't exists, it obviously cannot match the expected oid. */ - giterr_clear(); - return 0; } - if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) { + if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) { /* Determine the local reference name from the remote tracking one */ - if (git_refspec_transform_l( - &head_info->branchname, - head_info->refspec, - reference_name) < 0) - return -1; - - if (git_buf_len(&head_info->branchname) > 0) { - if (git_buf_sets( - &head_info->branchname, - git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0) - return -1; + error = git_refspec_transform_l( + &head_info->branchname, head_info->refspec, reference_name); - head_info->found = 1; + if (!error && + git_buf_len(&head_info->branchname) > 0 && + !(error = git_buf_sets( + &head_info->branchname, + git_buf_cstr(&head_info->branchname) + + strlen(GIT_REFS_HEADS_DIR)))) + { + head_info->found = true; + error = GIT_ITEROVER; } } - return 0; + return error; } static int update_head_to_new_branch( @@ -160,16 +155,11 @@ static int update_head_to_new_branch( const char *name) { git_reference *tracking_branch = NULL; - int error; + int error = create_tracking_branch(&tracking_branch, repo, target, name); - if ((error = create_tracking_branch( - &tracking_branch, - repo, - target, - name)) < 0) - return error; - - error = git_repository_set_head(repo, git_reference_name(tracking_branch)); + if (!error) + error = git_repository_set_head( + repo, git_reference_name(tracking_branch)); git_reference_free(tracking_branch); @@ -178,34 +168,30 @@ static int update_head_to_new_branch( static int update_head_to_remote(git_repository *repo, git_remote *remote) { - int retcode = -1; + int error = 0; size_t refs_len; git_refspec dummy_spec; const git_remote_head *remote_head, **refs; struct head_info head_info; git_buf remote_master_name = GIT_BUF_INIT; - if (git_remote_ls(&refs, &refs_len, remote) < 0) - return -1; + if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) + return error; /* Did we just clone an empty repository? */ - if (refs_len == 0) { + if (refs_len == 0) return setup_tracking_config( - repo, - "master", - GIT_REMOTE_ORIGIN, - GIT_REFS_HEADS_MASTER_FILE); - } + repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); /* Get the remote's HEAD. This is always the first ref in the list. */ remote_head = refs[0]; assert(remote_head); + memset(&head_info, 0, sizeof(head_info)); git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); - git_buf_init(&head_info.branchname, 16); head_info.repo = repo; - head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); - head_info.found = 0; + head_info.refspec = + git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); if (head_info.refspec == NULL) { memset(&dummy_spec, 0, sizeof(git_refspec)); @@ -213,50 +199,46 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) } /* Determine the remote tracking reference name from the local master */ - if (git_refspec_transform_r( + if ((error = git_refspec_transform_r( &remote_master_name, head_info.refspec, - GIT_REFS_HEADS_MASTER_FILE) < 0) - return -1; + GIT_REFS_HEADS_MASTER_FILE)) < 0) + return error; /* Check to see if the remote HEAD points to the remote master */ - if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0) + error = reference_matches_remote_head( + git_buf_cstr(&remote_master_name), &head_info); + if (error < 0 && error != GIT_ITEROVER) goto cleanup; if (head_info.found) { - retcode = update_head_to_new_branch( + error = update_head_to_new_branch( repo, &head_info.remote_head_oid, git_buf_cstr(&head_info.branchname)); - goto cleanup; } /* Not master. Check all the other refs. */ - if (git_reference_foreach_name( - repo, - reference_matches_remote_head, - &head_info) < 0) - goto cleanup; + error = git_reference_foreach_name( + repo, reference_matches_remote_head, &head_info); + if (error < 0 && error != GIT_ITEROVER) + goto cleanup; if (head_info.found) { - retcode = update_head_to_new_branch( + error = update_head_to_new_branch( repo, &head_info.remote_head_oid, git_buf_cstr(&head_info.branchname)); - - goto cleanup; } else { - retcode = git_repository_set_head_detached( - repo, - &head_info.remote_head_oid); - goto cleanup; + error = git_repository_set_head_detached( + repo, &head_info.remote_head_oid); } cleanup: git_buf_free(&remote_master_name); git_buf_free(&head_info.branchname); - return retcode; + return error; } static int update_head_to_branch( @@ -359,7 +341,7 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ old_fetchhead = git_remote_update_fetchhead(remote); git_remote_set_update_fetchhead(remote, 0); - if ((error = git_remote_fetch(remote)) < 0) + if ((error = git_remote_fetch(remote)) != 0) goto cleanup; if (branch) @@ -373,10 +355,12 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ cleanup: git_remote_set_update_fetchhead(remote, old_fetchhead); + /* Go back to the original refspecs */ - if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) { - git_strarray_free(&refspecs); - return -1; + { + int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs); + if (!error) + error = error_alt; } git_strarray_free(&refspecs); @@ -424,9 +408,10 @@ int git_clone( git_remote_free(origin); } - if (error < 0) { + if (error != 0) { git_repository_free(repo); repo = NULL; + (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); } diff --git a/src/common.h b/src/common.h index a1888785e..e315b5979 100644 --- a/src/common.h +++ b/src/common.h @@ -62,7 +62,7 @@ * Check a return value and propogate result if non-zero. */ #define GITERR_CHECK_ERROR(code) \ - do { int _err = (code); if (_err < 0) return _err; } while (0) + do { int _err = (code); if (_err) return _err; } while (0) /** * Set the error message for this thread, formatting as needed. @@ -76,28 +76,61 @@ void giterr_set(int error_class, const char *string, ...); int giterr_set_regex(const regex_t *regex, int error_code); /** - * Gets the system error code for this thread. + * Set error message for user callback if needed. + * + * If the error code in non-zero and no error message is set, this + * sets a generic error message. + * + * @return This always returns the `error_code` parameter. */ -GIT_INLINE(int) giterr_system_last(void) +GIT_INLINE(int) giterr_set_after_callback_function( + int error_code, const char *action) { + if (error_code) { + const git_error *e = giterr_last(); + if (!e || !e->message) + giterr_set(e ? e->klass : GITERR_CALLBACK, + "%s callback returned %d", action, error_code); + } + return error_code; +} + #ifdef GIT_WIN32 - return GetLastError(); +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __FUNCTION__) #else - return errno; +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __func__) #endif -} + +/** + * Gets the system error code for this thread. + */ +int giterr_system_last(void); /** * Sets the system error code for this thread. */ -GIT_INLINE(void) giterr_system_set(int code) -{ -#ifdef GIT_WIN32 - SetLastError(code); -#else - errno = code; -#endif -} +void giterr_system_set(int code); + +/** + * Structure to preserve libgit2 error state + */ +typedef struct { + int error_code; + git_error error_msg; +} git_error_state; + +/** + * Capture current error state to restore later, returning error code. + * If `error_code` is zero, this does nothing and returns zero. + */ +int giterr_capture(git_error_state *state, int error_code); + +/** + * Restore error state to a previous value, returning saved error code. + */ +int giterr_restore(git_error_state *state); /** * Check a versioned structure for validity diff --git a/src/config.c b/src/config.c index 0d9471383..056a6ae13 100644 --- a/src/config.c +++ b/src/config.c @@ -480,47 +480,45 @@ int git_config_foreach( int git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data) + git_config_foreach_cb cb, + void *payload) { git_config_entry *entry; git_config_iterator* iter; regex_t regex; - int result = 0; + int error = 0; if (regexp != NULL) { - if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { - giterr_set_regex(®ex, result); + if ((error = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, error); regfree(®ex); return -1; } } - if ((result = backend->iterator(&iter, backend)) < 0) { + if ((error = backend->iterator(&iter, backend)) < 0) { iter = NULL; return -1; } - while(!(iter->next(&entry, iter) < 0)) { + while (!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0) continue; /* abort iterator on non-zero return value */ - if (fn(entry, data)) { - giterr_clear(); - result = GIT_EUSER; - goto cleanup; + if ((error = cb(entry, payload)) != 0) { + giterr_set_after_callback(error); + break; } } -cleanup: if (regexp != NULL) regfree(®ex); iter->free(iter); - return result; + return error; } int git_config_foreach_match( @@ -536,10 +534,9 @@ int git_config_foreach_match( if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0) return error; - while ((error = git_config_next(&entry, iter)) == 0) { - if(cb(entry, payload)) { - giterr_clear(); - error = GIT_EUSER; + while (!(error = git_config_next(&entry, iter))) { + if ((error = cb(entry, payload)) != 0) { + giterr_set_after_callback(error); break; } } @@ -620,6 +617,78 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) /*********** * Getters ***********/ + +static int config_error_notfound(const char *name) +{ + giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + return GIT_ENOTFOUND; +} + +enum { + GET_ALL_ERRORS = 0, + GET_NO_MISSING = 1, + GET_NO_ERRORS = 2 +}; + +static int get_entry( + const git_config_entry **out, + const git_config *cfg, + const char *name, + bool normalize_name, + int want_errors) +{ + int res = GIT_ENOTFOUND; + const char *key = name; + char *normalized = NULL; + size_t i; + file_internal *internal; + + *out = NULL; + + if (normalize_name) { + if ((res = git_config__normalize_name(name, &normalized)) < 0) + goto cleanup; + key = normalized; + } + + git_vector_foreach(&cfg->files, i, internal) { + if (!internal || !internal->file) + continue; + + res = internal->file->get(internal->file, key, out); + if (res != GIT_ENOTFOUND) + break; + } + + git__free(normalized); + +cleanup: + if (res == GIT_ENOTFOUND) + res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name); + else if (res && (want_errors == GET_NO_ERRORS)) { + giterr_clear(); + res = 0; + } + + return res; +} + +int git_config_get_entry( + const git_config_entry **out, const git_config *cfg, const char *name) +{ + return get_entry(out, cfg, name, true, GET_ALL_ERRORS); +} + +int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors) +{ + return get_entry( + out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING); +} + int git_config_get_mapped( int *out, const git_config *cfg, @@ -627,116 +696,91 @@ int git_config_get_mapped( const git_cvar_map *maps, size_t map_n) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_lookup_map_value(out, maps, map_n, value); + return git_config_lookup_map_value(out, maps, map_n, entry->value); } int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int64(out, value); + return git_config_parse_int64(out, entry->value); } int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int32(out, value); + return git_config_parse_int32(out, entry->value); } -static int get_string_at_file(const char **out, const git_config_backend *file, const char *name) +int git_config_get_bool(int *out, const git_config *cfg, const char *name) { const git_config_entry *entry; - int res; + int ret; - res = file->get(file, name, &entry); - if (!res) - *out = entry->value; + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) + return ret; - return res; + return git_config_parse_bool(out, entry->value); } -static int config_error_notfound(const char *name) +int git_config_get_string( + const char **out, const git_config *cfg, const char *name) { - giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); - return GIT_ENOTFOUND; + const git_config_entry *entry; + int ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); + *out = !ret ? (entry->value ? entry->value : "") : NULL; + return ret; } -static int get_string(const char **out, const git_config *cfg, const char *name) +const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value) { - file_internal *internal; - unsigned int i; - int res; - - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - - res = get_string_at_file(out, internal->file, name); - if (res != GIT_ENOTFOUND) - return res; - } - - return config_error_notfound(name); + const git_config_entry *entry; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); + return (entry && entry->value) ? entry->value : fallback_value; } -int git_config_get_bool(int *out, const git_config *cfg, const char *name) +int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value) { - const char *value = NULL; - int ret; - - if ((ret = get_string(&value, cfg, name)) < 0) - return ret; - - return git_config_parse_bool(out, value); -} + int val = fallback_value; + const git_config_entry *entry; -int git_config_get_string(const char **out, const git_config *cfg, const char *name) -{ - int ret; - const char *str = NULL; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - if ((ret = get_string(&str, cfg, name)) < 0) - return ret; + if (entry && git_config_parse_bool(&val, entry->value) < 0) + giterr_clear(); - *out = str == NULL ? "" : str; - return 0; + return val; } -int git_config_get_entry(const git_config_entry **out, const git_config *cfg, const char *name) +int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value) { - file_internal *internal; - unsigned int i; - git_config_backend *file; - int ret; - - *out = NULL; + int32_t val = (int32_t)fallback_value; + const git_config_entry *entry; - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - file = internal->file; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - ret = file->get(file, name, out); - if (ret != GIT_ENOTFOUND) - return ret; - } + if (entry && git_config_parse_int32(&val, entry->value) < 0) + giterr_clear(); - return config_error_notfound(name); + return (int)val; } int git_config_get_multivar_foreach( @@ -753,9 +797,10 @@ int git_config_get_multivar_foreach( found = 0; while ((err = iter->next(&entry, iter)) == 0) { found = 1; - if(cb(entry, payload)) { - iter->free(iter); - return GIT_EUSER; + + if ((err = cb(entry, payload)) != 0) { + giterr_set_after_callback(err); + break; } } @@ -1070,7 +1115,7 @@ int git_config_parse_int64(int64_t *out, const char *value) const char *num_end; int64_t num; - if (git__strtol64(&num, value, &num_end, 0) < 0) + if (!value || git__strtol64(&num, value, &num_end, 0) < 0) goto fail_parse; switch (*num_end) { @@ -1167,7 +1212,6 @@ struct rename_data { git_config *config; git_buf *name; size_t old_len; - int actual_error; }; static int rename_config_entries_cb( @@ -1190,8 +1234,6 @@ static int rename_config_entries_cb( if (!error) error = git_config_delete_entry(data->config, entry->name); - data->actual_error = error; /* preserve actual error code */ - return error; } @@ -1216,7 +1258,6 @@ int git_config_rename_section( data.config = config; data.name = &replace; data.old_len = strlen(old_section_name) + 1; - data.actual_error = 0; if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0) goto cleanup; @@ -1233,9 +1274,6 @@ int git_config_rename_section( error = git_config_foreach_match( config, git_buf_cstr(&pattern), rename_config_entries_cb, &data); - if (error == GIT_EUSER) - error = data.actual_error; - cleanup: git_buf_free(&pattern); git_buf_free(&replace); diff --git a/src/config.h b/src/config.h index 01e8465cc..3cd888c88 100644 --- a/src/config.h +++ b/src/config.h @@ -51,5 +51,26 @@ extern int git_config_file__ondisk(git_config_backend **out, const char *path); extern int git_config__normalize_name(const char *in, char **out); +/* internal only: does not normalize key and sets out to NULL if not found */ +extern int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors); + +/* + * Lookup functions that cannot fail. These functions look up a config + * value and return a fallback value if the value is missing or if any + * failures occur while trying to access the value. + */ + +extern const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value); + +extern int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value); + +extern int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value); #endif diff --git a/src/config_cache.c b/src/config_cache.c index 6808521a3..ec75d1501 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -78,22 +78,22 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) struct map_data *data = &_cvar_maps[(int)cvar]; git_config *config; int error; + const git_config_entry *entry; - error = git_repository_config__weakptr(&config, repo); - if (error < 0) + if ((error = git_repository_config__weakptr(&config, repo)) < 0) return error; - if (data->maps) - error = git_config_get_mapped( - out, config, data->cvar_name, data->maps, data->map_count); - else - error = git_config_get_bool(out, config, data->cvar_name); + git_config__lookup_entry(&entry, config, data->cvar_name, false); - if (error == GIT_ENOTFOUND) { - giterr_clear(); + if (!entry) *out = data->default_value; - } - else if (error < 0) + else if (data->maps) + error = git_config_lookup_map_value( + out, data->maps, data->map_count, entry->value); + else + error = git_config_parse_bool(out, entry->value); + + if (error < 0) return error; repo->cvar_cache[(int)cvar] = *out; diff --git a/src/config_file.c b/src/config_file.c index 15c8de49c..0971aa7b0 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -404,20 +404,12 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val /* * Internal function that actually gets the value in string form */ -static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out) +static int config_get(const git_config_backend *cfg, const char *key, const git_config_entry **out) { diskfile_backend *b = (diskfile_backend *)cfg; - char *key; - khiter_t pos; - int error; + khiter_t pos = git_strmap_lookup_index(b->values, key); cvar_t *var; - if ((error = git_config__normalize_name(name, &key)) < 0) - return error; - - pos = git_strmap_lookup_index(b->values, key); - git__free(key); - /* no error message; the config system will write one */ if (!git_strmap_valid_index(b->values, pos)) return GIT_ENOTFOUND; @@ -427,7 +419,6 @@ static int config_get(const git_config_backend *cfg, const char *name, const git var = var->next; *out = var->entry; - return 0; } diff --git a/src/diff.c b/src/diff.c index 4c33a0213..7f2e58c0c 100644 --- a/src/diff.c +++ b/src/diff.c @@ -49,16 +49,29 @@ static git_diff_delta *diff_delta__alloc( return delta; } -static int diff_notify( - const git_diff *diff, - const git_diff_delta *delta, - const char *matched_pathspec) +static int diff_insert_delta( + git_diff *diff, git_diff_delta *delta, const char *matched_pathspec) { - if (!diff->opts.notify_cb) - return 0; + int error = 0; + + if (diff->opts.notify_cb) { + error = diff->opts.notify_cb( + diff, delta, matched_pathspec, diff->opts.notify_payload); + + if (error) { + git__free(delta); + + if (error > 0) /* positive value means to skip this delta */ + return 0; + else /* negative value means to cancel diff */ + return giterr_set_after_callback_function(error, "git_diff"); + } + } + + if ((error = git_vector_insert(&diff->deltas, delta)) < 0) + git__free(delta); - return diff->opts.notify_cb( - diff, delta, matched_pathspec, diff->opts.notify_payload); + return error; } static int diff_delta__from_one( @@ -68,7 +81,6 @@ static int diff_delta__from_one( { git_diff_delta *delta; const char *matched_pathspec; - int notify_res; if ((entry->flags & GIT_IDXENTRY_VALID) != 0) return 0; @@ -111,21 +123,12 @@ static int diff_delta__from_one( !git_oid_iszero(&delta->new_file.oid)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? GIT_EUSER : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static int diff_delta__from_two( git_diff *diff, - git_delta_t status, + git_delta_t status, const git_index_entry *old_entry, uint32_t old_mode, const git_index_entry *new_entry, @@ -134,7 +137,6 @@ static int diff_delta__from_two( const char *matched_pathspec) { git_diff_delta *delta; - int notify_res; const char *canonical_path = old_entry->path; if (status == GIT_DELTA_UNMODIFIED && @@ -173,16 +175,7 @@ static int diff_delta__from_two( if (new_oid || !git_oid_iszero(&new_entry->oid)) delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? GIT_EUSER : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static git_diff_delta *diff_delta__last_for_item( @@ -304,26 +297,6 @@ bool git_diff_delta__should_skip( } -static int config_bool(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_bool(&val, cfg, name) < 0) - giterr_clear(); - - return val; -} - -static int config_int(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_int32(&val, cfg, name) < 0) - giterr_clear(); - - return val; -} - static const char *diff_mnemonic_prefix( git_iterator_type_t type, bool left_side) { @@ -422,8 +395,8 @@ static int diff_list_apply_options( diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* load config values that affect diff behavior */ - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((val = git_repository_config__weakptr(&cfg, repo)) < 0) + return val; if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; @@ -445,7 +418,7 @@ static int diff_list_apply_options( /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { - int context = config_int(cfg, "diff.context", 3); + int context = git_config__get_int_force(cfg, "diff.context", 3); diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3; /* add other defaults here */ @@ -460,12 +433,11 @@ static int diff_list_apply_options( /* if ignore_submodules not explicitly set, check diff config */ if (diff->opts.ignore_submodules <= 0) { - const char *str; + const git_config_entry *entry; + git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true); - if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0) - giterr_clear(); - else if (str != NULL && - git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0) + if (entry && git_submodule_parse_ignore( + &diff->opts.ignore_submodules, entry->value) < 0) giterr_clear(); } @@ -474,9 +446,9 @@ static int diff_list_apply_options( const char *use_old = DIFF_OLD_PREFIX_DEFAULT; const char *use_new = DIFF_NEW_PREFIX_DEFAULT; - if (config_bool(cfg, "diff.noprefix", 0)) { + if (git_config__get_bool_force(cfg, "diff.noprefix", 0)) use_old = use_new = ""; - } else if (config_bool(cfg, "diff.mnemonicprefix", 0)) { + else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) { use_old = diff_mnemonic_prefix(diff->old_src, true); use_new = diff_mnemonic_prefix(diff->new_src, false); } @@ -504,14 +476,7 @@ static int diff_list_apply_options( static void diff_list_free(git_diff *diff) { - git_diff_delta *delta; - unsigned int i; - - git_vector_foreach(&diff->deltas, i, delta) { - git__free(delta); - diff->deltas.contents[i] = NULL; - } - git_vector_free(&diff->deltas); + git_vector_free_deep(&diff->deltas); git_pathspec__vfree(&diff->pathspec); git_pool_clear(&diff->pool); @@ -682,6 +647,7 @@ static int maybe_modified( unsigned int nmode = nitem->mode; bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); const char *matched_pathspec; + int error = 0; if (!git_pathspec__match( &diff->pathspec, oitem->path, @@ -716,10 +682,9 @@ static int maybe_modified( if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) status = GIT_DELTA_TYPECHANGE; else { - if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || - diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) - return -1; - return 0; + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) + error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); + return error; } } @@ -741,8 +706,8 @@ static int maybe_modified( /* TODO: add check against index file st_mtime to avoid racy-git */ if (S_ISGITLINK(nmode)) { - if (maybe_modified_submodule(&status, &noid, diff, info) < 0) - return -1; + if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0) + return error; } /* if the stat data looks different, then mark modified - this just @@ -769,9 +734,9 @@ static int maybe_modified( */ if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { if (git_oid_iszero(&noid)) { - if (git_diff__oid_for_file(diff->repo, - nitem->path, nitem->mode, nitem->file_size, &noid) < 0) - return -1; + if ((error = git_diff__oid_for_file(diff->repo, + nitem->path, nitem->mode, nitem->file_size, &noid)) < 0) + return error; } /* if oid matches, then mark unmodified (except submodules, where @@ -926,7 +891,7 @@ static int handle_unmatched_new_item( git_diff_delta *last; /* attempt to insert record for this directory */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* if delta wasn't created (because of rules), just skip ahead */ @@ -1005,7 +970,7 @@ static int handle_unmatched_new_item( } /* Actually create the record for this item if necessary */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of @@ -1030,7 +995,7 @@ static int handle_unmatched_old_item( git_diff *diff, diff_in_progress *info) { int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); - if (error < 0) + if (error != 0) return error; /* if we are generating TYPECHANGE records then check for that @@ -1364,7 +1329,7 @@ int git_diff__paired_foreach( int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload), void *payload) { - int cmp; + int cmp, error = 0; git_diff_delta *h2i, *i2w; size_t i, j, i_max, j_max; int (*strcomp)(const char *, const char *) = git__strcmp; @@ -1420,18 +1385,17 @@ int git_diff__paired_foreach( strcomp(h2i->new_file.path, i2w->old_file.path); if (cmp < 0) { - if (cb(h2i, NULL, payload)) - return GIT_EUSER; - i++; + i++; i2w = NULL; } else if (cmp > 0) { - if (cb(NULL, i2w, payload)) - return GIT_EUSER; - j++; + j++; h2i = NULL; } else { - if (cb(h2i, i2w, payload)) - return GIT_EUSER; i++; j++; } + + if ((error = cb(h2i, i2w, payload)) != 0) { + giterr_set_after_callback(error); + break; + } } /* restore case-insensitive delta sort */ @@ -1447,5 +1411,5 @@ int git_diff__paired_foreach( git_vector_sort(&idx2wd->deltas); } - return 0; + return error; } diff --git a/src/diff_driver.c b/src/diff_driver.c index bd5a8fbd9..167c0cc5a 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -14,6 +14,7 @@ #include "strmap.h" #include "map.h" #include "buf_text.h" +#include "config.h" #include "repository.h" GIT__USE_STRMAP; @@ -130,14 +131,14 @@ static git_diff_driver_registry *git_repository_driver_registry( static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { - int error = 0, bval; + int error = 0; git_diff_driver_registry *reg; git_diff_driver *drv; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; - const char *val; + const git_config_entry *ce; bool found_driver = false; reg = git_repository_driver_registry(repo); @@ -164,23 +165,21 @@ static int git_diff_driver_load( if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - /* diff.<driver>.binary unspecified, so just continue */ - giterr_clear(); - } else if (git_config_parse_bool(&bval, val) < 0) { - /* TODO: warn that diff.<driver>.binary has invalid value */ - giterr_clear(); - } else if (bval) { + + switch (git_config__get_bool_force(cfg, name.ptr, -1)) { + case true: /* if diff.<driver>.binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; - } else { + case false: /* if diff.<driver>.binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; + break; + default: + /* diff.<driver>.binary unspecified, so just continue */ + break; } /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */ @@ -211,16 +210,16 @@ static int git_diff_driver_load( git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "wordregex", strlen("wordregex")); - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - giterr_clear(); /* no diff.<driver>.wordregex, so just continue */ - } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&drv->word_pattern, error); + if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; - } else { + if (!ce || !ce->value) + /* no diff.<driver>.wordregex, so just continue */; + else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; + else { + /* TODO: warn about bad regex instead of failure */ + error = giterr_set_regex(&drv->word_pattern, error); + goto done; } /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience diff --git a/src/diff_patch.c b/src/diff_patch.c index cc49d68eb..9c2eb885f 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -193,21 +193,18 @@ cleanup: return error; } -static int diff_patch_file_callback( +static int diff_patch_invoke_file_callback( git_patch *patch, git_diff_output *output) { - float progress; + float progress = patch->diff ? + ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; if (!output->file_cb) return 0; - progress = patch->diff ? - ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; - - if (output->file_cb(patch->delta, progress, output->payload) != 0) - output->error = GIT_EUSER; - - return output->error; + return giterr_set_after_callback_function( + output->file_cb(patch->delta, progress, output->payload), + "git_patch"); } static int diff_patch_generate(git_patch *patch, git_diff_output *output) @@ -229,7 +226,7 @@ static int diff_patch_generate(git_patch *patch, git_diff_output *output) return 0; if (output->diff_cb != NULL && - !(error = output->diff_cb(output, patch))) + (error = output->diff_cb(output, patch)) < 0) patch->flags |= GIT_DIFF_PATCH_DIFFED; return error; @@ -272,9 +269,10 @@ int git_diff_foreach( size_t idx; git_patch patch; - if (diff_required(diff, "git_diff_foreach") < 0) - return -1; + if ((error = diff_required(diff, "git_diff_foreach")) < 0) + return error; + memset(&xo, 0, sizeof(xo)); diff_output_init( &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, &diff->opts); @@ -285,22 +283,18 @@ int git_diff_foreach( if (git_diff_delta__should_skip(&diff->opts, patch.delta)) continue; - if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) { + if ((error = diff_patch_init_from_diff(&patch, diff, idx)) < 0) + break; - error = diff_patch_file_callback(&patch, &xo.output); + if (!(error = diff_patch_invoke_file_callback(&patch, &xo.output))) + error = diff_patch_generate(&patch, &xo.output); - if (!error) - error = diff_patch_generate(&patch, &xo.output); + git_patch_free(&patch); - git_patch_free(&patch); - } - - if (error < 0) + if (error) break; } - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -332,14 +326,11 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) return error; - error = diff_patch_file_callback(patch, (git_diff_output *)xo); + error = diff_patch_invoke_file_callback(patch, (git_diff_output *)xo); if (!error) error = diff_patch_generate(patch, (git_diff_output *)xo); - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ - return error; } @@ -424,9 +415,7 @@ int git_diff_blobs( diff_patch_with_delta pd; git_xdiff_output xo; - memset(&pd, 0, sizeof(pd)); memset(&xo, 0, sizeof(xo)); - diff_output_init( &xo.output, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); @@ -436,6 +425,7 @@ int git_diff_blobs( else if (!new_path && old_path) new_path = old_path; + memset(&pd, 0, sizeof(pd)); error = diff_patch_from_blobs( &pd, &xo, old_blob, old_path, new_blob, new_path, opts); @@ -463,7 +453,6 @@ int git_patch_from_blobs( return -1; memset(&xo, 0, sizeof(xo)); - diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); @@ -536,9 +525,7 @@ int git_diff_blob_to_buffer( diff_patch_with_delta pd; git_xdiff_output xo; - memset(&pd, 0, sizeof(pd)); memset(&xo, 0, sizeof(xo)); - diff_output_init( &xo.output, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); @@ -548,6 +535,7 @@ int git_diff_blob_to_buffer( else if (!buf_path && old_path) buf_path = old_path; + memset(&pd, 0, sizeof(pd)); error = diff_patch_from_blob_and_buffer( &pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); @@ -576,7 +564,6 @@ int git_patch_from_blob_and_buffer( return -1; memset(&xo, 0, sizeof(xo)); - diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); @@ -622,17 +609,18 @@ int git_patch_from_diff( if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0) return error; + memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, patch); git_xdiff_init(&xo, &diff->opts); - error = diff_patch_file_callback(patch, &xo.output); + error = diff_patch_invoke_file_callback(patch, &xo.output); if (!error) error = diff_patch_generate(patch, &xo.output); if (!error) { - /* if cumulative diff size is < 0.5 total size, flatten the patch */ - /* unload the file content */ + /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */ + /* TODO: and unload the file content */ } if (error || !patch_ptr) @@ -640,8 +628,6 @@ int git_patch_from_diff( else *patch_ptr = patch; - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -905,7 +891,7 @@ static int diff_patch_line_cb( GIT_UNUSED(hunk_); hunk = git_array_last(patch->hunks); - GITERR_CHECK_ALLOC(hunk); + assert(hunk); /* programmer error if no hunk is available */ line = git_array_alloc(patch->lines); GITERR_CHECK_ALLOC(line); diff --git a/src/diff_print.c b/src/diff_print.c index b04b11515..7a70e2b18 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -89,12 +89,6 @@ char git_diff_status_char(git_delta_t status) return code; } -static int callback_error(void) -{ - giterr_clear(); - return GIT_EUSER; -} - static int diff_print_one_name_only( const git_diff_delta *delta, float progress, void *data) { @@ -108,19 +102,16 @@ static int diff_print_one_name_only( return 0; git_buf_clear(out); - - if (git_buf_puts(out, delta->new_file.path) < 0 || - git_buf_putc(out, '\n')) + git_buf_puts(out, delta->new_file.path); + git_buf_putc(out, '\n'); + if (git_buf_oom(out)) return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_name_status( @@ -154,7 +145,6 @@ static int diff_print_one_name_status( git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); else git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); - if (git_buf_oom(out)) return -1; @@ -162,10 +152,7 @@ static int diff_print_one_name_status( pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_raw( @@ -208,10 +195,7 @@ static int diff_print_one_raw( pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_oid_range( @@ -238,10 +222,7 @@ static int diff_print_oid_range( git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); } - if (git_buf_oom(out)) - return -1; - - return 0; + return git_buf_oom(out) ? -1 : 0; } static int diff_delta_format_with_paths( @@ -285,8 +266,7 @@ int git_diff_delta__format_file_header( git_buf_printf(out, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); - if (diff_print_oid_range(out, delta, oid_strlen) < 0) - return -1; + GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) diff_delta_format_with_paths( @@ -298,6 +278,7 @@ int git_diff_delta__format_file_header( static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { + int error; diff_print_info *pi = data; const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; @@ -313,36 +294,33 @@ static int diff_print_patch_file( (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) return 0; - if (git_diff_delta__format_file_header( - pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0) - return -1; + if ((error = git_diff_delta__format_file_header( + pi->buf, delta, oldpfx, newpfx, pi->oid_strlen)) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0) + return error; if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) return 0; git_buf_clear(pi->buf); - if (diff_delta_format_with_paths( + if ((error = diff_delta_format_with_paths( pi->buf, delta, oldpfx, newpfx, - "Binary files %s%s and %s%s differ\n") < 0) - return -1; + "Binary files %s%s and %s%s differ\n")) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_BINARY; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); pi->line.num_lines = 1; - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_patch_hunk( @@ -359,10 +337,7 @@ static int diff_print_patch_hunk( pi->line.content = h->header; pi->line.content_len = h->header_len; - if (pi->print_cb(d, h, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(d, h, &pi->line, pi->payload); } static int diff_print_patch_line( @@ -376,10 +351,7 @@ static int diff_print_patch_line( if (S_ISDIR(delta->new_file.mode)) return 0; - if (pi->print_cb(delta, hunk, line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, hunk, line, pi->payload); } /* print a git_diff to an output callback */ @@ -421,9 +393,14 @@ int git_diff_print( if (!(error = diff_print_info_init( &pi, &buf, diff, format, print_cb, payload))) + { error = git_diff_foreach( diff, print_file, print_hunk, print_line, &pi); + if (error) /* make sure error message is set */ + giterr_set_after_callback_function(error, "git_diff_print"); + } + git_buf_free(&buf); return error; @@ -444,10 +421,15 @@ int git_patch_print( if (!(error = diff_print_info_init( &pi, &temp, git_patch__diff(patch), GIT_DIFF_FORMAT_PATCH, print_cb, payload))) + { error = git_patch__invoke_callbacks( patch, diff_print_patch_file, diff_print_patch_hunk, diff_print_patch_line, &pi); + if (error) /* make sure error message is set */ + giterr_set_after_callback_function(error, "git_patch_print"); + } + git_buf_free(&temp); return error; @@ -478,15 +460,12 @@ int git_patch_to_str( int error; git_buf output = GIT_BUF_INIT; - error = git_patch_print(patch, diff_print_to_buffer_cb, &output); - - /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, - * meaning a memory allocation failure, so just map to -1... - */ - if (error == GIT_EUSER) - error = -1; - - *string = git_buf_detach(&output); + if (!(error = git_patch_print(patch, diff_print_to_buffer_cb, &output))) + *string = git_buf_detach(&output); + else { + git_buf_free(&output); + *string = NULL; + } return error; } diff --git a/src/diff_tform.c b/src/diff_tform.c index 702e43bd3..263a64d12 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -13,6 +13,7 @@ #include "hashsig.h" #include "path.h" #include "fileops.h" +#include "config.h" static git_diff_delta *diff_delta__dup( const git_diff_delta *d, git_pool *pool) @@ -208,9 +209,7 @@ int git_diff_merge(git_diff *onto, const git_diff *from) git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); } - git_vector_foreach(&onto_new, i, delta) - git__free(delta); - git_vector_free(&onto_new); + git_vector_free_deep(&onto_new); git_pool_clear(&onto_pool); return error; @@ -281,28 +280,22 @@ static int normalize_find_opts( git_repository_config__weakptr(&cfg, diff->repo) < 0) return -1; - if (given) { + if (given) memcpy(opts, given, sizeof(*opts)); - } else { - GIT_INIT_STRUCTURE(opts, GIT_DIFF_FIND_OPTIONS_VERSION); - } if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) { - const char *val = NULL; - - if (git_config_get_string(&val, cfg, "diff.renames") < 0) - giterr_clear(); - else if (val) { - int boolval; - if (!git__parse_bool(&boolval, val) && !boolval) { - /* do nothing */ - } else if (!strcasecmp(val, "copies") || !strcasecmp(val, "copy")) - opts->flags |= (GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES); - else - opts->flags |= GIT_DIFF_FIND_RENAMES; - } + const char *rule = + git_config__get_string_force(cfg, "diff.renames", "true"); + int boolval; + + if (!git__parse_bool(&boolval, rule) && !boolval) + /* don't set FIND_RENAMES if bool value is false */; + else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) + opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + else + opts->flags |= GIT_DIFF_FIND_RENAMES; } /* some flags imply others */ @@ -343,14 +336,11 @@ static int normalize_find_opts( #undef USE_DEFAULT if (!opts->rename_limit) { - int32_t limit = 0; - - opts->rename_limit = DEFAULT_RENAME_LIMIT; + opts->rename_limit = git_config__get_int_force( + cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT); - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - else if (limit > 0) - opts->rename_limit = limit; + if (opts->rename_limit <= 0) + opts->rename_limit = DEFAULT_RENAME_LIMIT; } /* assign the internal metric with whitespace flag as payload */ @@ -450,9 +440,7 @@ static int apply_splits_and_deletes( return 0; on_error: - git_vector_foreach(&onto, i, delta) - git__free(delta); - git_vector_free(&onto); + git_vector_free_deep(&onto); return -1; } @@ -824,11 +812,11 @@ int git_diff_find_similar( int error = 0, result; uint16_t similarity; git_diff_delta *src, *tgt; - git_diff_find_options opts; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; size_t num_deltas, num_srcs = 0, num_tgts = 0; size_t tried_srcs = 0, tried_tgts = 0; size_t num_rewrites = 0, num_updates = 0, num_bumped = 0; - void **sigcache; /* cache of similarity metric file signatures */ + void **sigcache = NULL; /* cache of similarity metric file signatures */ diff_find_match *tgt2src = NULL; diff_find_match *src2tgt = NULL; diff_find_match *tgt2src_copy = NULL; @@ -838,15 +826,15 @@ int git_diff_find_similar( if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; - /* No flags set; nothing to do */ - if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) - return 0; - num_deltas = diff->deltas.length; /* TODO: maybe abort if deltas.length > rename_limit ??? */ if (!git__is_uint32(num_deltas)) - return 0; + goto cleanup; + + /* No flags set; nothing to do */ + if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) + goto cleanup; sigcache = git__calloc(num_deltas * 2, sizeof(void *)); GITERR_CHECK_ALLOC(sigcache); @@ -1121,11 +1109,13 @@ cleanup: git__free(src2tgt); git__free(tgt2src_copy); - for (t = 0; t < num_deltas * 2; ++t) { - if (sigcache[t] != NULL) - opts.metric->free_signature(sigcache[t], opts.metric->payload); + if (sigcache) { + for (t = 0; t < num_deltas * 2; ++t) { + if (sigcache[t] != NULL) + opts.metric->free_signature(sigcache[t], opts.metric->payload); + } + git__free(sigcache); } - git__free(sigcache); if (!given_opts || !given_opts->metric) git__free(opts.metric); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index e0bc11f7f..e5984f1c9 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -28,25 +28,29 @@ static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header) { /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ if (*header != '@') - return -1; + goto fail; if (git_xdiff_scan_int(&header, &hunk->old_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0) - return -1; + goto fail; } else hunk->old_lines = 1; if (git_xdiff_scan_int(&header, &hunk->new_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0) - return -1; + goto fail; } else hunk->new_lines = 1; if (hunk->old_start < 0 || hunk->new_start < 0) - return -1; + goto fail; return 0; + +fail: + giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff"); + return -1; } typedef struct { @@ -122,8 +126,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) info->hunk.header[info->hunk.header_len] = '\0'; if (output->hunk_cb != NULL && - output->hunk_cb(delta, &info->hunk, output->payload)) - output->error = GIT_EUSER; + (output->error = output->hunk_cb( + delta, &info->hunk, output->payload))) + return output->error; info->old_lineno = info->hunk.old_start; info->new_lineno = info->hunk.new_start; @@ -146,10 +151,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) output->error = diff_update_lines( info, &line, bufs[1].ptr, bufs[1].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } if (len == 3 && !output->error) { @@ -168,10 +172,9 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) output->error = diff_update_lines( info, &line, bufs[2].ptr, bufs[2].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } return output->error; @@ -219,11 +222,9 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) xo->output.diff_cb = git_xdiff; - memset(&xo->config, 0, sizeof(xo->config)); xo->config.ctxlen = opts ? opts->context_lines : 3; xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; - memset(&xo->params, 0, sizeof(xo->params)); if (flags & GIT_DIFF_IGNORE_WHITESPACE) xo->params.flags |= XDF_WHITESPACE_FLAGS; if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) @@ -236,6 +237,5 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) if (flags & GIT_DIFF_MINIMAL) xo->params.flags |= XDF_NEED_MINIMAL; - memset(&xo->callback, 0, sizeof(xo->callback)); xo->callback.outf = git_xdiff_cb; } diff --git a/src/errors.c b/src/errors.c index d04da4ca9..a0b085923 100644 --- a/src/errors.c +++ b/src/errors.c @@ -23,7 +23,8 @@ static void set_error(int error_class, char *string) { git_error *error = &GIT_GLOBAL->error_t; - git__free(error->message); + if (error->message != string) + git__free(error->message); error->message = string; error->klass = error_class; @@ -103,8 +104,10 @@ int giterr_set_regex(const regex_t *regex, int error_code) void giterr_clear(void) { - set_error(0, NULL); - GIT_GLOBAL->last_error = NULL; + if (GIT_GLOBAL->last_error != NULL) { + set_error(0, NULL); + GIT_GLOBAL->last_error = NULL; + } errno = 0; #ifdef GIT_WIN32 @@ -134,3 +137,39 @@ const git_error *giterr_last(void) { return GIT_GLOBAL->last_error; } + +int giterr_capture(git_error_state *state, int error_code) +{ + state->error_code = error_code; + if (error_code) + giterr_detach(&state->error_msg); + return error_code; +} + +int giterr_restore(git_error_state *state) +{ + if (state && state->error_code && state->error_msg.message) + set_error(state->error_msg.klass, state->error_msg.message); + else + giterr_clear(); + + return state ? state->error_code : 0; +} + +int giterr_system_last(void) +{ +#ifdef GIT_WIN32 + return GetLastError(); +#else + return errno; +#endif +} + +void giterr_system_set(int code) +{ +#ifdef GIT_WIN32 + SetLastError(code); +#else + errno = code; +#endif +} diff --git a/src/fetch.c b/src/fetch.c index 276591821..5bf2b93c1 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -104,7 +104,7 @@ cleanup: int git_fetch_negotiate(git_remote *remote) { git_transport *t = remote->transport; - + if (filter_wants(remote) < 0) { giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); return -1; @@ -128,9 +128,9 @@ int git_fetch_download_pack(git_remote *remote) { git_transport *t = remote->transport; - if(!remote->need_pack) + if (!remote->need_pack) return 0; return t->download_pack(t, remote->repo, &remote->stats, - remote->callbacks.transfer_progress, remote->callbacks.payload); + remote->callbacks.transfer_progress, remote->callbacks.payload); } diff --git a/src/fetchhead.c b/src/fetchhead.c index 67089d13d..4435454ef 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -260,8 +260,8 @@ int git_repository_fetchhead_foreach(git_repository *repo, while ((line = git__strsep(&buffer, "\n")) != NULL) { ++line_num; - if ((error = fetchhead_ref_parse(&oid, &is_merge, &name, &remote_url, - line, line_num)) < 0) + if ((error = fetchhead_ref_parse( + &oid, &is_merge, &name, &remote_url, line, line_num)) < 0) goto done; if (git_buf_len(&name) > 0) @@ -269,8 +269,9 @@ int git_repository_fetchhead_foreach(git_repository *repo, else ref_name = NULL; - if ((cb(ref_name, remote_url, &oid, is_merge, payload)) != 0) { - error = GIT_EUSER; + error = cb(ref_name, remote_url, &oid, is_merge, payload); + if (error) { + giterr_set_after_callback(error); goto done; } } diff --git a/src/fileops.c b/src/fileops.c index 5763b370b..a60689f3f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -403,7 +403,6 @@ typedef struct { const char *base; size_t baselen; uint32_t flags; - int error; int depth; } futils__rmdir_data; @@ -447,8 +446,8 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling) static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { + int error = 0; futils__rmdir_data *data = opaque; - int error = data->error; struct stat st; if (data->depth > FUTILS_MAX_DEPTH) @@ -474,13 +473,14 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); - if (error < 0) - return (error == GIT_EUSER) ? data->error : error; data->depth--; + if (error < 0) + return error; + if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) - return data->error; + return error; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && @@ -499,28 +499,23 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); - data->error = error; return error; } static int futils__rmdir_empty_parent(void *opaque, git_buf *path) { futils__rmdir_data *data = opaque; - int error; + int error = 0; if (git_buf_len(path) <= data->baselen) - return GIT_ITEROVER; - - error = p_rmdir(git_buf_cstr(path)); + error = GIT_ITEROVER; - if (error) { + else if (p_rmdir(git_buf_cstr(path)) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { - giterr_clear(); - error = 0; + /* do nothing */ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { - giterr_clear(); error = GIT_ITEROVER; } else { error = git_path_set_error(errno, git_buf_cstr(path), "rmdir"); @@ -535,12 +530,13 @@ int git_futils_rmdir_r( { int error; git_buf fullpath = GIT_BUF_INIT; - futils__rmdir_data data = { 0 }; + futils__rmdir_data data; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; + memset(&data, 0, sizeof(data)); data.base = base ? base : ""; data.baselen = base ? strlen(base) : 0; data.flags = flags; @@ -548,12 +544,13 @@ int git_futils_rmdir_r( error = futils__rmdir_recurs_foreach(&data, &fullpath); /* remove now-empty parents if requested */ - if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) { + if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) { + giterr_clear(); + error = 0; } git_buf_free(&fullpath); @@ -618,6 +615,8 @@ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { git_futils_guess_template_dirs, }; +static int git_futils__dirs_shutdown_set = 0; + void git_futils_dirs_global_shutdown(void) { int i; @@ -634,8 +633,6 @@ int git_futils_dirs_global_init(void) for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++) error = git_futils_dirs_get(&path, i); - git__on_shutdown(git_futils_dirs_global_shutdown); - return error; } @@ -655,9 +652,16 @@ int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which) GITERR_CHECK_ERROR(git_futils_check_selector(which)); - if (!git_buf_len(&git_futils__dirs[which])) + if (!git_buf_len(&git_futils__dirs[which])) { + /* prepare shutdown if we're going to need it */ + if (!git_futils__dirs_shutdown_set) { + git__on_shutdown(git_futils_dirs_global_shutdown); + git_futils__dirs_shutdown_set = 1; + } + GITERR_CHECK_ERROR( git_futils__dir_guess[which](&git_futils__dirs[which])); + } *out = &git_futils__dirs[which]; return 0; @@ -858,7 +862,6 @@ typedef struct { uint32_t flags; uint32_t mkdir_flags; mode_t dirmode; - int error; } cp_r_info; #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) @@ -896,23 +899,21 @@ static int _cp_r_callback(void *ref, git_buf *from) from->ptr[git_path_basename_offset(from)] == '.') return 0; - if (git_buf_joinpath( - &info->to, info->to_root, from->ptr + info->from_prefix) < 0) { - error = -1; - goto exit; - } + if ((error = git_buf_joinpath( + &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) + return error; if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) - goto exit; + return error; else { giterr_clear(); error = 0; } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) - goto exit; + return error; if (S_ISDIR(from_st.st_mode)) { mode_t oldmode = info->dirmode; @@ -926,17 +927,13 @@ static int _cp_r_callback(void *ref, git_buf *from) error = _cp_r_mkdir(info, from); /* recurse onto target directory */ - if (!error && (!exists || S_ISDIR(to_st.st_mode))) { + if (!error && (!exists || S_ISDIR(to_st.st_mode))) error = git_path_direach(from, 0, _cp_r_callback, info); - if (error == GIT_EUSER) - error = info->error; - } - if (oldmode != 0) info->dirmode = oldmode; - goto exit; + return error; } if (exists) { @@ -946,8 +943,7 @@ static int _cp_r_callback(void *ref, git_buf *from) if (p_unlink(info->to.ptr) < 0) { giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", info->to.ptr); - error = -1; - goto exit; + return GIT_EEXISTS; } } @@ -960,7 +956,7 @@ static int _cp_r_callback(void *ref, git_buf *from) /* Make container directory on demand if needed */ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) - goto exit; + return error; /* make symlink or regular file */ if (S_ISLNK(from_st.st_mode)) @@ -974,8 +970,6 @@ static int _cp_r_callback(void *ref, git_buf *from) error = git_futils_cp(from->ptr, info->to.ptr, usemode); } -exit: - info->error = error; return error; } @@ -992,11 +986,11 @@ int git_futils_cp_r( if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ return -1; + memset(&info, 0, sizeof(info)); info.to_root = to; info.flags = flags; info.dirmode = dirmode; info.from_prefix = path.size; - info.error = 0; git_buf_init(&info.to, 0); /* precalculate mkdir flags */ @@ -1018,9 +1012,6 @@ int git_futils_cp_r( git_buf_free(&path); git_buf_free(&info.to); - if (error == GIT_EUSER) - error = info.error; - return error; } diff --git a/src/filter.c b/src/filter.c index 9f866fe88..ff81eb14e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -69,7 +69,7 @@ static void filter_registry_shutdown(void) return; git_vector_foreach(®->filters, pos, fdef) { - if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { + if (fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); fdef->initialized = false; } diff --git a/src/ignore.c b/src/ignore.c index 27d7c7ec4..c79fe4871 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -74,10 +74,12 @@ static int parse_ignore_file( #define push_ignore_file(R,IGN,S,B,F) \ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) -static int push_one_ignore(void *ref, git_buf *path) +static int push_one_ignore(void *payload, git_buf *path) { - git_ignores *ign = (git_ignores *)ref; - return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); + git_ignores *ign = payload; + + return push_ignore_file( + ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); } static int get_internal_ignores(git_attr_file **ign, git_repository *repo) diff --git a/src/index.c b/src/index.c index 09e7b2346..bb81f666e 100644 --- a/src/index.c +++ b/src/index.c @@ -2036,11 +2036,12 @@ int git_index_read_tree(git_index *index, const git_tree *tree) error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); - git_vector_sort(&entries); - - git_index_clear(index); + if (!error) { + git_vector_sort(&entries); + git_index_clear(index); + git_vector_swap(&entries, &index->entries); + } - git_vector_swap(&entries, &index->entries); git_vector_free(&entries); return error; @@ -2116,8 +2117,7 @@ int git_index_add_all( if (error > 0) /* return > 0 means skip this one */ continue; if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + giterr_set_after_callback(error); break; } } @@ -2204,11 +2204,8 @@ static int index_apply_to_all( error = 0; continue; } - if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + if (error < 0) /* return < 0 means abort */ break; - } } /* index manipulation may alter entry, so don't depend on it */ @@ -2253,8 +2250,13 @@ int git_index_remove_all( git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_REMOVE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; } int git_index_update_all( @@ -2263,6 +2265,11 @@ int git_index_update_all( git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_UPDATE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; } diff --git a/src/indexer.c b/src/indexer.c index 852a04120..6132571cc 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -386,12 +386,10 @@ on_error: static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats) { - if (idx->progress_cb && - idx->progress_cb(stats, idx->progress_payload)) { - giterr_clear(); - return GIT_EUSER; - } - + if (idx->progress_cb) + return giterr_set_after_callback_function( + idx->progress_cb(stats, idx->progress_payload), + "indexer progress"); return 0; } @@ -443,8 +441,8 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran processed = stats->indexed_objects; - if (git_filebuf_write(&idx->pack_file, data, size) < 0) - return -1; + if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0) + return error; hash_partially(idx, data, (int)size); @@ -452,12 +450,12 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran if (idx->opened_pack) { idx->pack->mwf.size += size; } else { - if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0) - return -1; + if ((error = open_pack(&idx->pack, idx->pack_file.path_lock)) < 0) + return error; idx->opened_pack = 1; mwf = &idx->pack->mwf; - if (git_mwindow_file_register(&idx->pack->mwf) < 0) - return -1; + if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0) + return error; } if (!idx->parsed_header) { @@ -466,8 +464,8 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header)) return 0; - if (parse_header(&idx->hdr, idx->pack) < 0) - return -1; + if ((error = parse_header(&idx->hdr, idx->pack)) < 0) + return error; idx->parsed_header = 1; idx->nr_objects = ntohl(hdr->hdr_entries); @@ -497,7 +495,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran processed = stats->indexed_objects = 0; stats->total_objects = total_objects; - if ((error = do_progress_callback(idx, stats)) < 0) + if ((error = do_progress_callback(idx, stats)) != 0) return error; } @@ -505,6 +503,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran /* As the file grows any windows we try to use will be out of date */ git_mwindow_free_all(mwf); + while (processed < idx->nr_objects) { git_packfile_stream *stream = &idx->stream; git_off_t entry_start = idx->off; @@ -522,7 +521,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return -1; + goto on_error; git_mwindow_close(&w); idx->entry_start = entry_start; @@ -535,7 +534,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran return 0; } if (error < 0) - return -1; + goto on_error; idx->have_delta = 1; } else { @@ -544,9 +543,10 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran } idx->have_stream = 1; - if (git_packfile_stream_open(stream, idx->pack, idx->off) < 0) - goto on_error; + error = git_packfile_stream_open(stream, idx->pack, idx->off); + if (error < 0) + goto on_error; } if (idx->have_delta) { @@ -580,7 +580,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran } stats->received_objects++; - if ((error = do_progress_callback(idx, stats)) < 0) + if ((error = do_progress_callback(idx, stats)) != 0) goto on_error; } @@ -860,7 +860,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off < idx->pack->mwf.size - 20) { - giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); + giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack"); return -1; } @@ -1007,30 +1007,20 @@ on_error: void git_indexer_free(git_indexer *idx) { - khiter_t k; - unsigned int i; - struct entry *e; - struct delta_info *delta; - if (idx == NULL) return; - git_vector_foreach(&idx->objects, i, e) - git__free(e); - git_vector_free(&idx->objects); + git_vector_free_deep(&idx->objects); if (idx->pack) { - for (k = kh_begin(idx->pack->idx_cache); k != kh_end(idx->pack->idx_cache); k++) { - if (kh_exist(idx->pack->idx_cache, k)) - git__free(kh_value(idx->pack->idx_cache, k)); - } + struct git_pack_entry *pentry; + kh_foreach_value( + idx->pack->idx_cache, pentry, { git__free(pentry); }); git_oidmap_free(idx->pack->idx_cache); } - git_vector_foreach(&idx->deltas, i, delta) - git__free(delta); - git_vector_free(&idx->deltas); + git_vector_free_deep(&idx->deltas); git_packfile_free(idx->pack); git_filebuf_cleanup(&idx->pack_file); git__free(idx); diff --git a/src/iterator.c b/src/iterator.c index 8646399ab..0e7d0db85 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -920,12 +920,7 @@ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) static void fs_iterator__free_frame(fs_iterator_frame *ff) { - size_t i; - git_path_with_stat *path; - - git_vector_foreach(&ff->entries, i, path) - git__free(path); - git_vector_free(&ff->entries); + git_vector_free_deep(&ff->entries); git__free(ff); } @@ -991,9 +986,8 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->base.start, fi->base.end, &ff->entries); if (error < 0) { - git_error last_error = {0}; - - giterr_detach(&last_error); + git_error_state last_error = { 0 }; + giterr_capture(&last_error, error); /* these callbacks may clear the error message */ fs_iterator__free_frame(ff); @@ -1001,12 +995,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi) /* next time return value we skipped to */ fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; - if (last_error.message) { - giterr_set_str(last_error.klass, last_error.message); - free(last_error.message); - } - - return error; + return giterr_restore(&last_error); } if (ff->entries.length == 0) { diff --git a/src/merge.c b/src/merge.c index 00415cbc0..c0be37dd8 100644 --- a/src/merge.c +++ b/src/merge.c @@ -26,6 +26,7 @@ #include "oid.h" #include "index.h" #include "filebuf.h" +#include "config.h" #include "git2/types.h" #include "git2/repository.h" @@ -253,7 +254,8 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l return 0; } -int git_repository_mergehead_foreach(git_repository *repo, +int git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb cb, void *payload) { @@ -285,8 +287,8 @@ int git_repository_mergehead_foreach(git_repository *repo, if ((error = git_oid_fromstr(&oid, line)) < 0) goto cleanup; - if (cb(&oid, payload) != 0) { - error = GIT_EUSER; + if ((error = cb(&oid, payload)) != 0) { + giterr_set_after_callback(error); goto cleanup; } @@ -1396,19 +1398,13 @@ static int merge_tree_normalize_opts( } if (!opts->target_limit) { - int32_t limit = 0; - - opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT; + int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0); - if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) { - giterr_clear(); - - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - } + if (!limit) + limit = git_config__get_int_force(cfg, "diff.renamelimit", 0); - if (limit > 0) - opts->target_limit = limit; + opts->target_limit = (limit <= 0) ? + GIT_MERGE_TREE_TARGET_LIMIT : (unsigned int)limit; } /* assign the internal metric with whitespace flag as payload */ @@ -2389,11 +2385,7 @@ done: git_index_set_caps(index_repo, index_repo_caps); git_index_free(index_repo); - - git_vector_foreach(&paths, i, path) - git__free(path); - - git_vector_free(&paths); + git_vector_free_deep(&paths); return error; } diff --git a/src/notes.c b/src/notes.c index beace1b50..795904917 100644 --- a/src/notes.c +++ b/src/notes.c @@ -378,20 +378,11 @@ cleanup: static int note_get_default_ref(const char **out, git_repository *repo) { - int ret; git_config *cfg; + int ret = git_repository_config__weakptr(&cfg, repo); - *out = NULL; - - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; - - ret = git_config_get_string(out, cfg, "core.notesRef"); - if (ret == GIT_ENOTFOUND) { - giterr_clear(); - *out = GIT_NOTES_DEFAULT_REF; - return 0; - } + *out = (ret != 0) ? NULL : git_config__get_string_force( + cfg, "core.notesref", GIT_NOTES_DEFAULT_REF); return ret; } @@ -592,8 +583,8 @@ int git_note_foreach( return error; while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { - if (note_cb(¬e_id, &annotated_id, payload)) { - error = GIT_EUSER; + if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { + giterr_set_after_callback(error); break; } } diff --git a/src/odb_loose.c b/src/odb_loose.c index ced272b33..fd4ffff1e 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -547,8 +547,7 @@ static int locate_object_short_oid( /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); - - if (error && error != GIT_EUSER) + if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) @@ -696,7 +695,6 @@ struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; void *data; - int cb_error; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) @@ -735,12 +733,8 @@ static int foreach_object_dir_cb(void *_state, git_buf *path) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - if (state->cb(&oid, state->data)) { - state->cb_error = GIT_EUSER; - return -1; - } - - return 0; + return giterr_set_after_callback_function( + state->cb(&oid, state->data), "git_odb_foreach"); } static int foreach_cb(void *_state, git_buf *path) @@ -764,6 +758,8 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb git_buf_sets(&buf, objects_dir); git_path_to_dir(&buf); + if (git_buf_oom(&buf)) + return -1; memset(&state, 0, sizeof(state)); state.cb = cb; @@ -774,7 +770,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb git_buf_free(&buf); - return state.cb_error ? state.cb_error : error; + return error; } static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid) diff --git a/src/odb_pack.c b/src/odb_pack.c index fd2ca0fd8..903b00d26 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -190,31 +190,39 @@ static int packfile_sort__cb(const void *a_, const void *b_) } - -static int packfile_load__cb(void *_data, git_buf *path) +static int packfile_load__cb(void *data, git_buf *path) { - struct pack_backend *backend = (struct pack_backend *)_data; + struct pack_backend *backend = data; struct git_pack_file *pack; + const char *path_str = git_buf_cstr(path); + size_t i, cmp_len = git_buf_len(path); int error; - size_t i; - if (git__suffixcmp(path->ptr, ".idx") != 0) + if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0) return 0; /* not an index */ + cmp_len -= strlen(".idx"); + for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p = git_vector_get(&backend->packs, i); - if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0) + + if (memcmp(p->pack_name, path_str, cmp_len) == 0) return 0; } error = git_packfile_alloc(&pack, path->ptr); - if (error == GIT_ENOTFOUND) - /* ignore missing .pack file as git does */ + + /* ignore missing .pack file as git does */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; - else if (error < 0) - return error; + } + + if (!error) + error = git_vector_insert(&backend->packs, pack); + + return error; - return git_vector_insert(&backend->packs, pack); } static int pack_entry_find_inner( @@ -314,13 +322,12 @@ static int pack_entry_find_prefix( * Implement the git_odb_backend API calls * ***********************************************************/ -static int pack_backend__refresh(git_odb_backend *_backend) +static int pack_backend__refresh(git_odb_backend *backend_) { - struct pack_backend *backend = (struct pack_backend *)_backend; - int error; struct stat st; git_buf path = GIT_BUF_INIT; + struct pack_backend *backend = (struct pack_backend *)backend_; if (backend->pack_folder == NULL) return 0; @@ -334,12 +341,9 @@ static int pack_backend__refresh(git_odb_backend *_backend) error = git_path_direach(&path, 0, packfile_load__cb, backend); git_buf_free(&path); - - if (error < 0) - return -1; - git_vector_sort(&backend->packs); - return 0; + + return error; } static int pack_backend__read_header_internal( diff --git a/src/pack-objects.c b/src/pack-objects.c index 2d62507f2..335944c0c 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -205,14 +205,18 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, po = pb->object_list + pb->nr_objects; memset(po, 0x0, sizeof(*po)); - if (git_odb_read_header(&po->size, &po->type, pb->odb, oid) < 0) - return -1; + if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0) + return ret; pb->nr_objects++; git_oid_cpy(&po->id, oid); po->hash = name_hash(name); pos = kh_put(oid, pb->object_ix, &po->id, &ret); + if (ret < 0) { + giterr_set_oom(); + return ret; + } assert(ret != 0); kh_value(pb->object_ix, pos) = po; @@ -220,12 +224,17 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, if (pb->progress_cb) { double current_time = git__timer(); - if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) { + double elapsed = current_time - pb->last_progress_report_time; + + if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; - if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) { - giterr_clear(); - return GIT_EUSER; - } + + ret = pb->progress_cb( + GIT_PACKBUILDER_ADDING_OBJECTS, + pb->nr_objects, 0, pb->progress_cb_payload); + + if (ret) + return giterr_set_after_callback(ret); } } @@ -323,8 +332,10 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) git_hash_update(&pb->ctx, data, size) < 0) goto on_error; - if (po->delta_data) + if (po->delta_data) { git__free(po->delta_data); + po->delta_data = NULL; + } git_odb_object_free(obj); git_buf_free(&zbuf); @@ -603,6 +614,15 @@ static int write_pack(git_packbuilder *pb, error = cb(entry_oid.id, GIT_OID_RAWSZ, data); done: + /* if callback cancelled writing, we must still free delta_data */ + for ( ; i < pb->nr_objects; ++i) { + po = write_order[i]; + if (po->delta_data) { + git__free(po->delta_data); + po->delta_data = NULL; + } + } + git__free(write_order); git_buf_free(&buf); return error; @@ -1284,21 +1304,22 @@ const git_oid *git_packbuilder_hash(git_packbuilder *pb) return &pb->pack_oid; } -static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload) +static int cb_tree_walk( + const char *root, const git_tree_entry *entry, void *payload) { + int error; struct tree_walk_context *ctx = payload; /* A commit inside a tree represents a submodule commit and should be skipped. */ if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) return 0; - if (git_buf_sets(&ctx->buf, root) < 0 || - git_buf_puts(&ctx->buf, git_tree_entry_name(entry)) < 0) - return -1; + if (!(error = git_buf_sets(&ctx->buf, root)) && + !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry)))) + error = git_packbuilder_insert( + ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf)); - return git_packbuilder_insert(ctx->pb, - git_tree_entry_id(entry), - git_buf_cstr(&ctx->buf)); + return error; } int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) @@ -1318,22 +1339,17 @@ int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) { - git_tree *tree; + int error; + git_tree *tree = NULL; struct tree_walk_context context = { pb, GIT_BUF_INIT }; - if (git_tree_lookup(&tree, pb->repo, oid) < 0 || - git_packbuilder_insert(pb, oid, NULL) < 0) - return -1; - - if (git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context) < 0) { - git_tree_free(tree); - git_buf_free(&context.buf); - return -1; - } + if (!(error = git_tree_lookup(&tree, pb->repo, oid)) && + !(error = git_packbuilder_insert(pb, oid, NULL))) + error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context); git_tree_free(tree); git_buf_free(&context.buf); - return 0; + return error; } uint32_t git_packbuilder_object_count(git_packbuilder *pb) diff --git a/src/pack.c b/src/pack.c index 644b2d465..23fcf3530 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1042,10 +1042,9 @@ int git_pack_foreach_entry( { const unsigned char *index = p->index_map.data, *current; uint32_t i; + int error = 0; if (index == NULL) { - int error; - if ((error = pack_index_open(p)) < 0) return error; @@ -1062,7 +1061,6 @@ int git_pack_foreach_entry( if (p->oids == NULL) { git_vector offsets, oids; - int error; if ((error = git_vector_init(&oids, p->num_objects, NULL))) return error; @@ -1084,15 +1082,16 @@ int git_pack_foreach_entry( git_vector_foreach(&offsets, i, current) git_vector_insert(&oids, (void*)¤t[4]); } + git_vector_free(&offsets); - p->oids = (git_oid **)oids.contents; + p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); } for (i = 0; i < p->num_objects; i++) - if (cb(p->oids[i], data)) - return GIT_EUSER; + if ((error = cb(p->oids[i], data)) != 0) + return giterr_set_after_callback(error); - return 0; + return error; } static int pack_entry_find_offset( diff --git a/src/path.c b/src/path.c index 750dd3ef7..feb273915 100644 --- a/src/path.c +++ b/src/path.c @@ -436,8 +436,12 @@ int git_path_walk_up( while (scan >= stop) { error = cb(data, &iter); iter.ptr[scan] = oldc; - if (error < 0) + + if (error) { + giterr_set_after_callback(error); break; + } + scan = git_buf_rfind_next(&iter, '/'); if (scan >= 0) { scan++; @@ -528,7 +532,9 @@ bool git_path_is_empty_dir(const char *path) if (!git_path_isdir(path)) return false; - if (!(error = git_buf_sets(&dir, path))) + if ((error = git_buf_sets(&dir, path)) != 0) + giterr_clear(); + else error = git_path_direach(&dir, 0, path_found_entry, NULL); git_buf_free(&dir); @@ -778,7 +784,7 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen) return 0; while (1) { - if (git_buf_grow(&ic->buf, wantlen) < 0) + if (git_buf_grow(&ic->buf, wantlen + 1) < 0) return -1; nfc = ic->buf.ptr + ic->buf.size; @@ -867,7 +873,7 @@ int git_path_direach( if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) break; #endif - + if ((error = git_buf_put(path, de_path, de_len)) < 0) break; @@ -875,8 +881,8 @@ int git_path_direach( git_buf_truncate(path, wd_len); /* restore path */ - if (error) { - error = GIT_EUSER; + if (error != 0) { + giterr_set_after_callback(error); break; } } diff --git a/src/path.h b/src/path.h index 3daafd265..f26175d15 100644 --- a/src/path.h +++ b/src/path.h @@ -255,9 +255,10 @@ enum { * @param flags Combination of GIT_PATH_DIR flags. * @param callback Callback for each entry. Passed the `payload` and each * successive path inside the directory as a full path. This may - * safely append text to the pathbuf if needed. + * safely append text to the pathbuf if needed. Return non-zero to + * cancel iteration (and return value will be propagated back). * @param payload Passed to callback as first argument. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success or error code from OS error or from callback */ extern int git_path_direach( git_buf *pathbuf, @@ -288,7 +289,7 @@ extern int git_path_cmp( * original input path. * @param callback Function to invoke on each path. Passed the `payload` * and the buffer containing the current path. The path should not - * be modified in any way. + * be modified in any way. Return non-zero to stop iteration. * @param state Passed to fn as the first ath. */ extern int git_path_walk_up( diff --git a/src/pathspec.c b/src/pathspec.c index 1e7e65e90..bad8dacdb 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -102,15 +102,7 @@ int git_pathspec__vinit( /* free data from the pathspec vector */ void git_pathspec__vfree(git_vector *vspec) { - git_attr_fnmatch *match; - unsigned int i; - - git_vector_foreach(vspec, i, match) { - git__free(match); - vspec->contents[i] = NULL; - } - - git_vector_free(vspec); + git_vector_free_deep(vspec); } struct pathspec_match_context { diff --git a/src/pool.c b/src/pool.c index d484769e9..146f118b4 100644 --- a/src/pool.c +++ b/src/pool.c @@ -190,19 +190,18 @@ void *git_pool_malloc(git_pool *pool, uint32_t items) char *git_pool_strndup(git_pool *pool, const char *str, size_t n) { - void *ptr = NULL; + char *ptr = NULL; assert(pool && str && pool->item_size == sizeof(char)); - if (n + 1 == 0) { - giterr_set_oom(); + if ((uint32_t)(n + 1) < n) return NULL; - } if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) { memcpy(ptr, str, n); - *(((char *)ptr) + n) = '\0'; + ptr[n] = '\0'; } + pool->has_string_alloc = 1; return ptr; diff --git a/src/posix.c b/src/posix.c index b75109b83..525785f35 100644 --- a/src/posix.c +++ b/src/posix.c @@ -203,4 +203,40 @@ int p_write(git_file fd, const void *buf, size_t cnt) return 0; } +#ifdef NO_MMAP +#include "map.h" + +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +{ + GIT_MMAP_VALIDATE(out, len, prot, flags); + + out->data = NULL; + out->len = 0; + + if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { + giterr_set(GITERR_OS, "Trying to map shared-writeable"); + return -1; + } + + out->data = malloc(len); + GITERR_CHECK_ALLOC(out->data); + + if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { + giterr_set(GITERR_OS, "mmap emulation failed"); + return -1; + } + + out->len = len; + return 0; +} + +int p_munmap(git_map *map) +{ + assert(map != NULL); + free(map->data); + + return 0; +} + +#endif diff --git a/src/push.c b/src/push.c index 3c9d5bb35..dd77864a5 100644 --- a/src/push.c +++ b/src/push.c @@ -541,10 +541,7 @@ static int queue_objects(git_push *push) error = 0; on_error: - git_vector_foreach(&commits, i, oid) - git__free(oid); - - git_vector_free(&commits); + git_vector_free_deep(&commits); return error; } @@ -662,8 +659,9 @@ int git_push_status_foreach(git_push *push, unsigned int i; git_vector_foreach(&push->status, i, status) { - if (cb(status->ref, status->msg, data) < 0) - return GIT_EUSER; + int error = cb(status->ref, status->msg, data); + if (error) + return giterr_set_after_callback(error); } return 0; diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 62d5c1047..df7cb9d4d 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -264,9 +264,9 @@ done: return error; } -static int _dirent_loose_load(void *data, git_buf *full_path) +static int _dirent_loose_load(void *payload, git_buf *full_path) { - refdb_fs_backend *backend = (refdb_fs_backend *)data; + refdb_fs_backend *backend = payload; const char *file_path; if (git__suffixcmp(full_path->ptr, ".lock") == 0) @@ -305,7 +305,7 @@ static int packed_loadloose(refdb_fs_backend *backend) git_buf_free(&refs_path); - return (error == GIT_EUSER) ? -1 : error; + return error; } static int refdb_fs_backend__exists( diff --git a/src/refs.c b/src/refs.c index 472a79890..4f3a557c6 100644 --- a/src/refs.c +++ b/src/refs.c @@ -471,7 +471,7 @@ int git_reference_rename( 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. */ + /* Update HEAD it was pointing to the reference being renamed */ 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"); @@ -513,20 +513,19 @@ int git_reference_foreach( git_reference *ref; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; - while ((error = git_reference_next(&ref, iter)) == 0) { - if (callback(ref, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next(&ref, iter))) { + if ((error = callback(ref, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -540,20 +539,19 @@ int git_reference_foreach_name( const char *refname; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -568,20 +566,19 @@ int git_reference_foreach_glob( const char *refname; int error; - if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) - return -1; + if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) + return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -624,7 +621,9 @@ void git_reference_iterator_free(git_reference_iterator *iter) static int cb__reflist_add(const char *ref, void *data) { - return git_vector_insert((git_vector *)data, git__strdup(ref)); + char *name = git__strdup(ref); + GITERR_CHECK_ALLOC(name); + return git_vector_insert((git_vector *)data, name); } int git_reference_list( @@ -647,8 +646,8 @@ int git_reference_list( return -1; } - array->strings = (char **)ref_list.contents; - array->count = ref_list.length; + array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list); + return 0; } diff --git a/src/remote.c b/src/remote.c index 3d890a5f1..294a8709d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -45,7 +45,7 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch) static int download_tags_value(git_remote *remote, git_config *cfg) { - const char *val; + const git_config_entry *ce; git_buf buf = GIT_BUF_INIT; int error; @@ -53,16 +53,14 @@ static int download_tags_value(git_remote *remote, git_config *cfg) if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) return -1; - error = git_config_get_string(&val, cfg, git_buf_cstr(&buf)); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); git_buf_free(&buf); - if (!error && !strcmp(val, "--no-tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; - else if (!error && !strcmp(val, "--tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; + if (!error && ce && ce->value) { + if (!strcmp(ce->value, "--no-tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; + else if (!strcmp(ce->value, "--tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; } return error; @@ -104,12 +102,7 @@ static int get_check_cert(int *out, git_repository *repo) if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - if ((error = git_config_get_bool(out, cfg, "http.sslVerify")) == 0) - return 0; - else if (error != GIT_ENOTFOUND) - return error; - - giterr_clear(); + *out = git_config__get_bool_force(cfg, "http.sslverify", 1); return 0; } @@ -262,8 +255,7 @@ struct refspec_cb_data { static int refspec_cb(const git_config_entry *entry, void *payload) { - const struct refspec_cb_data *data = (struct refspec_cb_data *)payload; - + struct refspec_cb_data *data = (struct refspec_cb_data *)payload; return add_refspec(data->remote, entry->value, data->fetch); } @@ -290,9 +282,6 @@ static int get_optional_config( error = 0; } - if (error < 0) - error = -1; - return error; } @@ -303,7 +292,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) const char *val; int error = 0; git_config *config; - struct refspec_cb_data data; + struct refspec_cb_data data = { NULL }; bool optional_setting_found = false, found; assert(out && repo && name); @@ -325,17 +314,15 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) if ((error = get_check_cert(&remote->check_cert, repo)) < 0) goto cleanup; - if ((git_vector_init(&remote->refs, 32, NULL) < 0) || - (git_vector_init(&remote->refspecs, 2, NULL) < 0) || - (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) { + if (git_vector_init(&remote->refs, 32, NULL) < 0 || + git_vector_init(&remote->refspecs, 2, NULL) < 0 || + git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; } - if (git_buf_printf(&buf, "remote.%s.url", name) < 0) { - error = -1; + if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) goto cleanup; - } if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) goto cleanup; @@ -370,6 +357,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) data.remote = remote; data.fetch = true; + git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.fetch", name); @@ -493,7 +481,7 @@ int git_remote_save(const git_remote *remote) } if (error < 0) { git_buf_free(&buf); - return -1; + return error; } } @@ -641,7 +629,7 @@ int git_remote_connect(git_remote *remote, git_direction direction) if (!remote->check_cert) flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT; - if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) < 0) + if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0) goto on_error; remote->transport = t; @@ -667,7 +655,8 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) { git_config *cfg; - const char *val; + const git_config_entry *ce; + const char *val = NULL; int error; assert(remote); @@ -684,44 +673,39 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur * to least specific. */ /* remote.<name>.proxy config setting */ - if (remote->name && 0 != *(remote->name)) { + if (remote->name && remote->name[0]) { git_buf buf = GIT_BUF_INIT; if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) return error; - if ((error = git_config_get_string(&val, cfg, git_buf_cstr(&buf))) == 0 && - val && ('\0' != *val)) { - git_buf_free(&buf); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); + git_buf_free(&buf); - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if (error < 0) return error; - giterr_clear(); - git_buf_free(&buf); + if (ce && ce->value) { + val = ce->value; + goto found; + } } /* http.proxy config setting */ - if ((error = git_config_get_string(&val, cfg, "http.proxy")) == 0 && - val && ('\0' != *val)) { - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) return error; - - giterr_clear(); + if (ce && ce->value) { + val = ce->value; + goto found; + } /* HTTP_PROXY / HTTPS_PROXY environment variables */ val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); - if (val && ('\0' != *val)) { +found: + if (val && val[0]) { *proxy_url = git__strdup(val); GITERR_CHECK_ALLOC(*proxy_url); - return 0; } return 0; @@ -797,7 +781,7 @@ int git_remote_download(git_remote *remote) git_vector_free(&refs); if (error < 0) - return -1; + return error; if ((error = git_fetch_negotiate(remote)) < 0) return error; @@ -810,10 +794,10 @@ int git_remote_fetch(git_remote *remote) int error; /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) return error; - if ((error = git_remote_download(remote)) < 0) + if ((error = git_remote_download(remote)) != 0) return error; /* We don't need to be connected anymore */ @@ -1041,7 +1025,6 @@ int git_remote_update_tips(git_remote *remote) int error; size_t i; - if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; @@ -1141,40 +1124,28 @@ static int remote_list_cb(const git_config_entry *entry, void *payload) int git_remote_list(git_strarray *remotes_list, git_repository *repo) { - git_config *cfg; - git_vector list; int error; + git_config *cfg; + git_vector list = GIT_VECTOR_INIT; - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) + return error; - if (git_vector_init(&list, 4, git__strcmp_cb) < 0) - return -1; + if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) + return error; error = git_config_foreach_match( cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); if (error < 0) { - size_t i; - char *elem; - - git_vector_foreach(&list, i, elem) { - git__free(elem); - } - - git_vector_free(&list); - - /* cb error is converted to GIT_EUSER by git_config_foreach */ - if (error == GIT_EUSER) - error = -1; - + git_vector_free_deep(&list); return error; } git_vector_uniq(&list, git__free); - remotes_list->strings = (char **)list.contents; - remotes_list->count = list.length; + remotes_list->strings = + (char **)git_vector_detach(&remotes_list->count, NULL, &list); return 0; } @@ -1261,8 +1232,7 @@ cleanup: return error; } -struct update_data -{ +struct update_data { git_config *config; const char *old_remote_name; const char *new_remote_name; @@ -1278,9 +1248,7 @@ static int update_config_entries_cb( return 0; return git_config_set_string( - data->config, - entry->name, - data->new_remote_name); + data->config, entry->name, data->new_remote_name); } static int update_branch_remote_config_entry( @@ -1288,20 +1256,17 @@ static int update_branch_remote_config_entry( const char *old_name, const char *new_name) { - git_config *config; - struct update_data data; + int error; + struct update_data data = { NULL }; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) + return error; - data.config = config; data.old_remote_name = old_name; data.new_remote_name = new_name; return git_config_foreach_match( - config, - "branch\\..+\\.remote", - update_config_entries_cb, &data); + data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); } static int rename_one_remote_reference( @@ -1309,18 +1274,20 @@ static int rename_one_remote_reference( const char *old_remote_name, const char *new_remote_name) { - int error = -1; + int error; git_buf new_name = GIT_BUF_INIT; - if (git_buf_printf( + error = 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) - return -1; + reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)); - error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0); - git_reference_free(reference); + if (!error) { + error = git_reference_rename( + NULL, reference, git_buf_cstr(&new_name), 0); + git_reference_free(reference); + } git_buf_free(&new_name); return error; @@ -1331,12 +1298,12 @@ static int rename_remote_references( const char *old_name, const char *new_name) { - int error = -1; + int error; git_reference *ref; git_reference_iterator *iter; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; while ((error = git_reference_next(&ref, iter)) == 0) { if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { @@ -1344,18 +1311,13 @@ static int rename_remote_references( continue; } - if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) { - git_reference_iterator_free(iter); - return error; - } + if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) + break; } git_reference_iterator_free(iter); - if (error == GIT_ITEROVER) - return 0; - - return error; + return (error == GIT_ITEROVER) ? 0 : error; } static int rename_fetch_refspecs( @@ -1368,52 +1330,50 @@ static int rename_fetch_refspecs( git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; const git_refspec *spec; size_t i; - int error = -1; + int error = 0; - if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) - goto cleanup; + if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) + return error; + + if ((error = git_buf_printf( + &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0) + return error; git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; - /* Every refspec is a problem refspec for an in-memory remote */ - if (!remote->name) { - if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; - goto cleanup; - } - - continue; - } + /* Every refspec is a problem refspec for an in-memory remote, OR */ + /* Does the dst part of the refspec follow the expected format? */ + if (!remote->name || + strcmp(git_buf_cstr(&base), spec->string)) { - /* Does the dst part of the refspec follow the extected standard format? */ - if (strcmp(git_buf_cstr(&base), spec->string)) { - if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; - goto cleanup; + if ((error = callback(spec->string, payload)) != 0) { + giterr_set_after_callback(error); + break; } continue; } /* If we do want to move it to the new section */ - if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0) - goto cleanup; - if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) - goto cleanup; + git_buf_clear(&val); + git_buf_clear(&var); - if (git_repository_config__weakptr(&config, remote->repo) < 0) - goto cleanup; + if (git_buf_printf( + &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 || + git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) + { + error = -1; + break; + } - if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0) - goto cleanup; + if ((error = git_config_set_string( + config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) + break; } - error = 0; - -cleanup: git_buf_free(&base); git_buf_free(&var); git_buf_free(&val); @@ -1448,11 +1408,11 @@ int git_remote_rename( new_name, callback, payload)) < 0) - return error; + return error; remote->name = git__strdup(new_name); + GITERR_CHECK_ALLOC(remote->name); - if (!remote->name) return 0; return git_remote_save(remote); } @@ -1479,11 +1439,13 @@ int git_remote_rename( new_name, callback, payload)) < 0) - return error; + return error; } git__free(remote->name); + remote->name = git__strdup(new_name); + GITERR_CHECK_ALLOC(remote->name); return 0; } @@ -1655,9 +1617,7 @@ static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int p return 0; on_error: - git_vector_foreach(&refspecs, i, dup) - git__free(dup); - git_vector_free(&refspecs); + git_vector_free_deep(&refspecs); return -1; } diff --git a/src/repository.c b/src/repository.c index 278c0384e..94f6603aa 100644 --- a/src/repository.c +++ b/src/repository.c @@ -186,39 +186,37 @@ static int load_workdir(git_repository *repo, git_buf *parent_path) { int error; git_config *config; - const char *worktree; - git_buf worktree_buf = GIT_BUF_INIT; + const git_config_entry *ce; + git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&config, repo)) < 0) + return error; - error = git_config_get_string(&worktree, config, "core.worktree"); - if (!error && worktree != NULL) { - error = git_path_prettify_dir( - &worktree_buf, worktree, repo->path_repository); - if (error < 0) + if ((error = git_config__lookup_entry( + &ce, config, "core.worktree", false)) < 0) + return error; + + if (ce && ce->value) { + if ((error = git_path_prettify_dir( + &worktree, ce->value, repo->path_repository)) < 0) return error; - repo->workdir = git_buf_detach(&worktree_buf); + + repo->workdir = git_buf_detach(&worktree); } - else if (error != GIT_ENOTFOUND) - return error; + else if (parent_path && git_path_isdir(parent_path->ptr)) + repo->workdir = git_buf_detach(parent_path); else { - giterr_clear(); + if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + git_path_to_dir(&worktree) < 0) + return -1; - if (parent_path && git_path_isdir(parent_path->ptr)) - repo->workdir = git_buf_detach(parent_path); - else { - git_path_dirname_r(&worktree_buf, repo->path_repository); - git_path_to_dir(&worktree_buf); - repo->workdir = git_buf_detach(&worktree_buf); - } + repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); - return 0; } @@ -1610,15 +1608,14 @@ static int at_least_one_cb(const char *refname, void *payload) { GIT_UNUSED(refname); GIT_UNUSED(payload); - - return GIT_EUSER; + return GIT_PASSTHROUGH; } static int repo_contains_no_reference(git_repository *repo) { int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) diff --git a/src/revwalk.c b/src/revwalk.c index 3dd14b419..c0a053211 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -112,12 +112,13 @@ static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commi static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) { + int error; git_object *obj; git_otype type; git_commit_list_node *commit; - if (git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY)) < 0) + return error; type = git_object_type(obj); git_object_free(obj); @@ -173,7 +174,6 @@ struct push_cb_data { static int push_glob_cb(const char *refname, void *data_) { struct push_cb_data *data = (struct push_cb_data *)data_; - return push_ref(data->walk, refname, data->hide); } @@ -191,6 +191,8 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); + if (git_buf_oom(&buf)) + return -1; /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); @@ -200,11 +202,8 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) data.walk = walk; data.hide = hide; - if (git_buf_oom(&buf)) - error = -1; - else - error = git_reference_foreach_glob( - walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); + error = git_reference_foreach_glob( + walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); git_buf_free(&buf); return error; diff --git a/src/stash.c b/src/stash.c index 083c2a4cd..eae56966c 100644 --- a/src/stash.c +++ b/src/stash.c @@ -440,7 +440,7 @@ static int is_dirty_cb(const char *path, unsigned int status, void *payload) GIT_UNUSED(status); GIT_UNUSED(payload); - return 1; + return GIT_PASSTHROUGH; } static int ensure_there_are_changes_to_stash( @@ -463,7 +463,7 @@ static int ensure_there_are_changes_to_stash( error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) @@ -582,12 +582,14 @@ int git_stash_foreach( for (i = 0; i < max; i++) { entry = git_reflog_entry_byindex(reflog, i); - if (callback(i, + error = callback(i, git_reflog_entry_message(entry), git_reflog_entry_id_new(entry), - payload)) { - error = GIT_EUSER; - break; + payload); + + if (error) { + giterr_set_after_callback(error); + break; } } diff --git a/src/status.c b/src/status.c index 07fdcb5c3..7a1472d8d 100644 --- a/src/status.c +++ b/src/status.c @@ -314,8 +314,9 @@ int git_status_list_new( goto done; } - if ((error = git_diff__paired_foreach( - status->head2idx, status->idx2wd, status_collect, status)) < 0) + error = git_diff__paired_foreach( + status->head2idx, status->idx2wd, status_collect, status); + if (error < 0) goto done; if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) @@ -360,19 +361,13 @@ const git_status_entry *git_status_byindex(git_status_list *status, size_t i) void git_status_list_free(git_status_list *status) { - git_status_entry *status_entry; - size_t i; - if (status == NULL) return; git_diff_free(status->head2idx); git_diff_free(status->idx2wd); - git_vector_foreach(&status->paired, i, status_entry) - git__free(status_entry); - - git_vector_free(&status->paired); + git_vector_free_deep(&status->paired); git__memzero(status, sizeof(*status)); git__free(status); @@ -397,9 +392,8 @@ int git_status_foreach_ext( status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; - if (cb(path, status_entry->status, payload) != 0) { - error = GIT_EUSER; - giterr_clear(); + if ((error = cb(path, status_entry->status, payload)) != 0) { + giterr_set_after_callback(error); break; } } diff --git a/src/submodule.c b/src/submodule.c index 586494fed..f6660a87e 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -168,9 +168,8 @@ int git_submodule_foreach( break; } - if (callback(sm, sm->name, payload)) { - giterr_clear(); - error = GIT_EUSER; + if ((error = callback(sm, sm->name, payload)) != 0) { + giterr_set_after_callback(error); break; } }); @@ -825,17 +824,14 @@ int git_submodule_reload(git_submodule *submodule) assert(submodule); /* refresh index data */ - - if (submodule_update_index(submodule) < 0) - return -1; + if ((error = submodule_update_index(submodule)) < 0) + return error; /* refresh HEAD tree data */ - - if (submodule_update_head(submodule) < 0) - return -1; + if ((error = submodule_update_head(submodule)) < 0) + return error; /* refresh config data */ - mods = open_gitmodules(submodule->repo, false, NULL); if (mods != NULL) { git_buf path = GIT_BUF_INIT; @@ -852,19 +848,17 @@ int git_submodule_reload(git_submodule *submodule) git_buf_free(&path); git_config_file_free(mods); - } - if (error < 0) - return error; + if (error < 0) + return error; + } /* refresh wd data */ - submodule->flags = submodule->flags & ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); - error = submodule_load_from_wd_lite(submodule, submodule->path, NULL); - - return error; + return submodule_load_from_wd_lite( + submodule, submodule->path, submodule->repo); } static void submodule_copy_oid_maybe( @@ -1087,15 +1081,14 @@ int git_submodule_parse_update(git_submodule_update_t *out, const char *value) } static int submodule_load_from_config( - const git_config_entry *entry, void *data) + const git_config_entry *entry, void *payload) { - git_repository *repo = data; + git_repository *repo = payload; git_strmap *smcfg = repo->submodules; const char *namestart, *property, *alternate = NULL; - const char *key = entry->name, *value = entry->value; + const char *key = entry->name, *value = entry->value, *path; git_buf name = GIT_BUF_INIT; git_submodule *sm; - bool is_path; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1108,15 +1101,11 @@ static int submodule_load_from_config( return 0; property++; - is_path = (strcasecmp(property, "path") == 0); + path = !strcasecmp(property, "path") ? value : NULL; - if (git_buf_set(&name, namestart, property - namestart - 1) < 0) - return -1; - - if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) { - git_buf_free(&name); - return -1; - } + if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || + (error = submodule_get(&sm, repo, name.ptr, path)) < 0) + goto done; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; @@ -1130,15 +1119,20 @@ static int submodule_load_from_config( if (strcmp(sm->name, name.ptr) != 0) { alternate = sm->name = git_buf_detach(&name); - } else if (is_path && value && strcmp(sm->path, value) != 0) { + } else if (path && strcmp(path, sm->path) != 0) { alternate = sm->path = git__strdup(value); - if (!sm->path) + if (!sm->path) { error = -1; + goto done; + } } + if (alternate) { void *old_sm = NULL; git_strmap_insert2(smcfg, alternate, sm, old_sm, error); + if (error < 0) + goto done; if (error >= 0) GIT_REFCOUNT_INC(sm); /* inserted under a new key */ @@ -1149,42 +1143,44 @@ static int submodule_load_from_config( } } - git_buf_free(&name); - if (error < 0) - return error; - /* TODO: Look up path in index and if it is present but not a GITLINK * then this should be deleted (at least to match git's behavior) */ - if (is_path) - return 0; + if (path) + goto done; /* copy other properties into submodule entry */ if (strcasecmp(property, "url") == 0) { git__free(sm->url); sm->url = NULL; - if (value != NULL && (sm->url = git__strdup(value)) == NULL) - return -1; + if (value != NULL && (sm->url = git__strdup(value)) == NULL) { + error = -1; + goto done; + } } else if (strcasecmp(property, "update") == 0) { - if (git_submodule_parse_update(&sm->update, value) < 0) - return -1; + if ((error = git_submodule_parse_update(&sm->update, value)) < 0) + goto done; sm->update_default = sm->update; } else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { - if (git__parse_bool(&sm->fetch_recurse, value) < 0) - return submodule_config_error("fetchRecurseSubmodules", value); + if (git__parse_bool(&sm->fetch_recurse, value) < 0) { + error = submodule_config_error("fetchRecurseSubmodules", value); + goto done; + } } else if (strcasecmp(property, "ignore") == 0) { - if (git_submodule_parse_ignore(&sm->ignore, value) < 0) - return -1; + if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) + goto done; sm->ignore_default = sm->ignore; } /* ignore other unknown submodule properties */ - return 0; +done: + git_buf_free(&name); + return error; } static int submodule_load_from_wd_lite( @@ -1192,8 +1188,7 @@ static int submodule_load_from_wd_lite( { git_buf path = GIT_BUF_INIT; - GIT_UNUSED(name); - GIT_UNUSED(payload); + GIT_UNUSED(name); GIT_UNUSED(payload); if (git_repository_is_bare(sm->repo)) return 0; @@ -1208,7 +1203,6 @@ static int submodule_load_from_wd_lite( sm->flags |= GIT_SUBMODULE_STATUS_IN_WD; git_buf_free(&path); - return 0; } @@ -1342,7 +1336,6 @@ static int load_submodule_config(git_repository *repo) { int error; git_oid gitmodules_oid; - git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; if (repo->submodules) @@ -1370,10 +1363,9 @@ static int load_submodule_config(git_repository *repo) /* add submodule information from .gitmodules */ - if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL) - error = git_config_file_foreach(mods, submodule_load_from_config, repo); - - if (error != 0) + if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL && + (error = git_config_file_foreach( + mods, submodule_load_from_config, repo)) < 0) goto cleanup; /* shallow scan submodules in work tree */ @@ -1382,8 +1374,6 @@ static int load_submodule_config(git_repository *repo) error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); cleanup: - git_buf_free(&path); - if (mods != NULL) git_config_file_free(mods); @@ -1468,7 +1458,7 @@ static int submodule_update_config( int error; git_config *config; git_buf key = GIT_BUF_INIT; - const char *old = NULL; + const git_config_entry *ce = NULL; assert(submodule); @@ -1480,14 +1470,16 @@ static int submodule_update_config( if (error < 0) goto cleanup; - if (git_config_get_string(&old, config, key.ptr) < 0) - giterr_clear(); + if ((error = git_config__lookup_entry(&ce, config, key.ptr, false)) < 0) + goto cleanup; - if (!old && only_existing) + if (!ce && only_existing) + goto cleanup; + if (ce && !overwrite) goto cleanup; - if (old && !overwrite) + if (value && ce && ce->value && !strcmp(ce->value, value)) goto cleanup; - if ((!old && !value) || (old && value && strcmp(old, value) == 0)) + if (!value && (!ce || !ce->value)) goto cleanup; if (!value) @@ -418,16 +418,19 @@ typedef struct { static int tags_cb(const char *ref, void *data) { + int error; git_oid oid; tag_cb_data *d = (tag_cb_data *)data; if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0) return 0; /* no tag */ - if (git_reference_name_to_id(&oid, d->repo, ref) < 0) - return -1; + if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { + if ((error = d->cb(ref, &oid, d->cb_data)) != 0) + giterr_set_after_callback_function(error, "git_tag_foreach"); + } - return d->cb(ref, &oid, d->cb_data); + return error; } int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) @@ -455,8 +458,14 @@ static int tag_list_cb(const char *tag_name, git_oid *oid, void *data) tag_filter_data *filter = (tag_filter_data *)data; GIT_UNUSED(oid); - if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) - return git_vector_insert(filter->taglist, git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN)); + if (!*filter->pattern || + p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) + { + char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); + GITERR_CHECK_ALLOC(matched); + + return git_vector_insert(filter->taglist, matched); + } return 0; } @@ -469,20 +478,20 @@ int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_reposit assert(tag_names && repo && pattern); - if (git_vector_init(&taglist, 8, NULL) < 0) - return -1; + if ((error = git_vector_init(&taglist, 8, NULL)) < 0) + return error; filter.taglist = &taglist; filter.pattern = pattern; error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); - if (error < 0) { + + if (error < 0) git_vector_free(&taglist); - return -1; - } - tag_names->strings = (char **)taglist.contents; - tag_names->count = taglist.length; + tag_names->strings = + (char **)git_vector_detach(&tag_names->count, NULL, &taglist); + return 0; } diff --git a/src/transports/git.c b/src/transports/git.c index 5dcd4eff7..21507c1c7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -93,18 +93,19 @@ static int git_stream_read( size_t buf_size, size_t *bytes_read) { + int error; git_stream *s = (git_stream *)stream; gitno_buffer buf; *bytes_read = 0; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); - if (gitno_recv(&buf) < 0) - return -1; + if ((error = gitno_recv(&buf)) < 0) + return error; *bytes_read = buf.offset; @@ -116,10 +117,11 @@ static int git_stream_write( const char *buffer, size_t len) { + int error; git_stream *s = (git_stream *)stream; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; return gitno_send(&s->socket, buffer, len, 0); } @@ -140,7 +142,7 @@ static void git_stream_free(git_smart_subtransport_stream *stream) } git__free(s->url); - git__free(s); + git__free(s); } static int git_stream_alloc( @@ -182,18 +184,21 @@ static int _git_uploadpack_ls( char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; git_stream *s; - int error = -1; + int error; *stream = NULL; + if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0) - return -1; + if ((error = git_stream_alloc(t, stream_url, cmd_uploadpack, stream)) < 0) + return error; s = (git_stream *)*stream; - if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_extract_url_parts( + &host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) t->current_stream = s; diff --git a/src/transports/http.c b/src/transports/http.c index ace0d97d0..c6aaeb9cf 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -382,9 +382,6 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) static void clear_parser_state(http_subtransport *t) { - unsigned i; - char *entry; - http_parser_init(&t->parser, HTTP_RESPONSE); gitno_buffer_setup(&t->socket, &t->parse_buffer, @@ -407,10 +404,7 @@ static void clear_parser_state(http_subtransport *t) git__free(t->location); t->location = NULL; - git_vector_foreach(&t->www_authenticate, i, entry) - git__free(entry); - - git_vector_free(&t->www_authenticate); + git_vector_free_deep(&t->www_authenticate); } static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) diff --git a/src/transports/local.c b/src/transports/local.c index 4502f0202..4635d5dd3 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -43,43 +43,43 @@ typedef struct { static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; + git_oid head_oid; git_remote_head *head; git_object *obj = NULL, *target = NULL; git_buf buf = GIT_BUF_INIT; int error; - head = git__calloc(1, sizeof(git_remote_head)); - GITERR_CHECK_ALLOC(head); - - head->name = git__strdup(name); - GITERR_CHECK_ALLOC(head->name); - - error = git_reference_name_to_id(&head->oid, t->repo, name); + error = git_reference_name_to_id(&head_oid, t->repo, name); if (error < 0) { - git__free(head->name); - git__free(head); if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { - /* This is actually okay. Empty repos often have a HEAD that points to - * a nonexistent "refs/heads/master". */ + /* This is actually okay. Empty repos often have a HEAD that + * points to a nonexistent "refs/heads/master". */ giterr_clear(); return 0; } return error; } - if (git_vector_insert(&t->refs, head) < 0) - { + head = git__calloc(1, sizeof(git_remote_head)); + GITERR_CHECK_ALLOC(head); + + head->name = git__strdup(name); + GITERR_CHECK_ALLOC(head->name); + + git_oid_cpy(&head->oid, &head_oid); + + if ((error = git_vector_insert(&t->refs, head)) < 0) { git__free(head->name); git__free(head); - return -1; + return error; } /* If it's not a tag, we don't need to try to peel it */ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return 0; - if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0) + return error; head = NULL; @@ -94,27 +94,24 @@ static int add_ref(transport_local *t, const char *name) /* And if it's a tag, peel it, and add it to the list */ head = git__calloc(1, sizeof(git_remote_head)); GITERR_CHECK_ALLOC(head); + if (git_buf_join(&buf, 0, name, peeled) < 0) return -1; - head->name = git_buf_detach(&buf); - if (git_tag_peel(&target, (git_tag *) obj) < 0) - goto on_error; - - git_oid_cpy(&head->oid, git_object_id(target)); - git_object_free(obj); - git_object_free(target); - - if (git_vector_insert(&t->refs, head) < 0) - return -1; + if (!(error = git_tag_peel(&target, (git_tag *)obj))) { + git_oid_cpy(&head->oid, git_object_id(target)); - return 0; + if ((error = git_vector_insert(&t->refs, head)) < 0) { + git__free(head->name); + git__free(head); + } + } -on_error: git_object_free(obj); git_object_free(target); - return -1; + + return error; } static int store_refs(transport_local *t) @@ -222,7 +219,7 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t return -1; } - *out = (const git_remote_head **) t->refs.contents; + *out = (const git_remote_head **)t->refs.contents; *size = t->refs.length; return 0; @@ -250,8 +247,9 @@ static int local_negotiate_fetch( git_oid_cpy(&rhead->loid, git_object_id(obj)); else if (error != GIT_ENOTFOUND) return error; + else + giterr_clear(); git_object_free(obj); - giterr_clear(); } return 0; @@ -528,7 +526,7 @@ static int local_download_pack( } } - if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0) + if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0) goto cleanup; /* Write the data to the ODB */ @@ -539,7 +537,7 @@ static int local_download_pack( data.progress_payload = progress_payload; data.writepack = writepack; - if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) < 0) + if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0) goto cleanup; } error = writepack->commit(writepack, stats); diff --git a/src/transports/smart.c b/src/transports/smart.c index 5242beb65..69eaf9b78 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -23,13 +23,13 @@ static int git_smart__recv_cb(gitno_buffer *buf) buf->offset += bytes_read; - if (t->packetsize_cb && !t->cancelled.val) - if (t->packetsize_cb(bytes_read, t->packetsize_payload)) { + if (t->packetsize_cb && !t->cancelled.val) { + error = t->packetsize_cb(bytes_read, t->packetsize_payload); + if (error) { git_atomic_set(&t->cancelled, 1); - - giterr_clear(); return GIT_EUSER; } + } return (int)(buf->offset - old_len); } @@ -342,7 +342,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) t->parent.is_connected = git_smart__is_connected; t->parent.read_flags = git_smart__read_flags; t->parent.cancel = git_smart__cancel; - + t->owner = owner; t->rpc = definition->rpc; @@ -359,7 +359,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) if (definition->callback(&t->wrapped, &t->parent) < 0) { git__free(t); return -1; - } + } *out = (git_transport *) t; return 0; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index a4046ee43..dd9b5e0ed 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -45,7 +45,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) error = GIT_EBUFS; if (error < 0 && error != GIT_EBUFS) - return -1; + return error; if (error == GIT_EBUFS) { if ((recvd = gitno_recv(buf)) < 0) @@ -209,12 +209,13 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) git_strarray refs; unsigned int i; git_reference *ref; + int error; - if (git_reference_list(&refs, repo) < 0) - return -1; + if ((error = git_reference_list(&refs, repo)) < 0) + return error; - if (git_revwalk_new(&walk, repo) < 0) - return -1; + if ((error = git_revwalk_new(&walk, repo)) < 0) + return error; git_revwalk_sorting(walk, GIT_SORT_TIME); @@ -223,13 +224,13 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) continue; - if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0) + if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0) goto on_error; if (git_reference_type(ref) == GIT_REF_SYMBOLIC) continue; - if (git_revwalk_push(walk, git_reference_target(ref)) < 0) + if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0) goto on_error; git_reference_free(ref); @@ -242,7 +243,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) on_error: git_reference_free(ref); git_strarray_free(&refs); - return -1; + return error; } static int wait_while_ack(gitno_buffer *buf) @@ -503,7 +504,7 @@ int git_smart__download_pack( } if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || - ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0)) + ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)) goto done; /* @@ -539,11 +540,9 @@ int git_smart__download_pack( if (pkt->type == GIT_PKT_PROGRESS) { if (t->progress_cb) { git_pkt_progress *p = (git_pkt_progress *) pkt; - if (t->progress_cb(p->data, p->len, t->message_cb_payload)) { - giterr_clear(); - error = GIT_EUSER; + error = t->progress_cb(p->data, p->len, t->message_cb_payload); + if (error) goto done; - } } git__free(pkt); } else if (pkt->type == GIT_PKT_DATA) { @@ -551,7 +550,7 @@ int git_smart__download_pack( error = writepack->append(writepack, p->data, p->len, stats); git__free(pkt); - if (error < 0) + if (error != 0) goto done; } else if (pkt->type == GIT_PKT_FLUSH) { /* A flush indicates the end of the packfile */ @@ -564,17 +563,15 @@ int git_smart__download_pack( * Trailing execution of progress_cb, if necessary... * Only the callback through the npp datastructure currently * updates the last_fired_bytes value. It is possible that - * progress has already been reported with the correct + * progress has already been reported with the correct * "received_bytes" value, but until (if?) this is unified * then we will report progress again to be sure that the * correct last received_bytes value is reported. */ if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) { - if (npp.callback(npp.stats, npp.payload) < 0) { - giterr_clear(); - error = GIT_EUSER; + error = npp.callback(npp.stats, npp.payload); + if (error != 0) goto done; - } } error = writepack->commit(writepack, stats); diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 673cd0faf..e47e19cca 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -667,9 +667,11 @@ replay: if (allowed_types && (!t->cred || 0 == (t->cred->credtype & allowed_types))) { - if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types, - t->owner->cred_acquire_payload) < 0) - return GIT_EUSER; + int error = t->owner->cred_acquire_cb( + &t->cred, t->owner->url, t->connection_data.user, + allowed_types, t->owner->cred_acquire_payload); + if (error < 0) + return error; assert(t->cred); diff --git a/src/tree.c b/src/tree.c index bb59ff82b..4d77ff778 100644 --- a/src/tree.c +++ b/src/tree.c @@ -884,22 +884,22 @@ static int tree_walk( git_vector_foreach(&tree->entries, i, entry) { if (preorder) { error = callback(path->ptr, entry, payload); - if (error > 0) { + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } + if (error > 0) { /* positive value skips this entry */ error = 0; continue; } - if (error < 0) { - giterr_clear(); - return GIT_EUSER; - } } if (git_tree_entry__is_tree(entry)) { git_tree *subtree; size_t path_len = git_buf_len(path); - if ((error = git_tree_lookup( - &subtree, tree->object.repo, &entry->oid)) < 0) + error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid); + if (error < 0) break; /* append the next entry to the path */ @@ -907,21 +907,24 @@ static int tree_walk( git_buf_putc(path, '/'); if (git_buf_oom(path)) - return -1; + error = -1; + else + error = tree_walk(subtree, callback, path, payload, preorder); - error = tree_walk(subtree, callback, path, payload, preorder); git_tree_free(subtree); - if (error != 0) break; git_buf_truncate(path, path_len); } - if (!preorder && callback(path->ptr, entry, payload) < 0) { - giterr_clear(); - error = GIT_EUSER; - break; + if (!preorder) { + error = callback(path->ptr, entry, payload); + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } + error = 0; } } diff --git a/src/unix/map.c b/src/unix/map.c index 7de99c99d..e62ab3e76 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -6,7 +6,7 @@ */ #include <git2/common.h> -#ifndef GIT_WIN32 +#if !defined(GIT_WIN32) && !defined(NO_MMAP) #include "map.h" #include <sys/mman.h> diff --git a/src/vector.c b/src/vector.c index 362e7b0c0..050e032a0 100644 --- a/src/vector.c +++ b/src/vector.c @@ -77,6 +77,20 @@ void git_vector_free(git_vector *v) v->_alloc_size = 0; } +void git_vector_free_deep(git_vector *v) +{ + size_t i; + + assert(v); + + for (i = 0; i < v->length; ++i) { + git__free(v->contents[i]); + v->contents[i] = NULL; + } + + git_vector_free(v); +} + int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) { assert(v); @@ -90,6 +104,22 @@ int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) return resize_vector(v, max(initial_size, MIN_ALLOCSIZE)); } +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v) +{ + void **data = v->contents; + + if (size) + *size = v->length; + if (asize) + *asize = v->_alloc_size; + + v->_alloc_size = 0; + v->length = 0; + v->contents = NULL; + + return data; +} + int git_vector_insert(git_vector *v, void *element) { assert(v); diff --git a/src/vector.h b/src/vector.h index 279f5c6ee..d318463c6 100644 --- a/src/vector.h +++ b/src/vector.h @@ -23,10 +23,13 @@ typedef struct git_vector { int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp); void git_vector_free(git_vector *v); +void git_vector_free_deep(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v); + void git_vector_sort(git_vector *v); /** Linear search for matching entry using internal comparison function */ diff --git a/src/win32/map.c b/src/win32/map.c index 44c6c4e2e..902ea3994 100644 --- a/src/win32/map.c +++ b/src/win32/map.c @@ -8,6 +8,7 @@ #include "map.h" #include <errno.h> +#ifndef NO_MMAP static DWORD get_page_size(void) { @@ -112,4 +113,4 @@ int p_munmap(git_map *map) return error; } - +#endif diff --git a/tests/attr/repo.c b/tests/attr/repo.c index ef2ad5ce9..f9ba585fb 100644 --- a/tests/attr/repo.c +++ b/tests/attr/repo.c @@ -129,6 +129,8 @@ static int count_attrs( return 0; } +#define CANCEL_VALUE 12345 + static int cancel_iteration( const char *name, const char *value, @@ -140,7 +142,7 @@ static int cancel_iteration( *((int *)payload) -= 1; if (*((int *)payload) < 0) - return -1; + return CANCEL_VALUE; return 0; } @@ -166,7 +168,7 @@ void test_attr_repo__foreach(void) count = 2; cl_assert_equal_i( - GIT_EUSER, git_attr_foreach( + CANCEL_VALUE, git_attr_foreach( g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count) ); } diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 66b01bc7f..d2e92f8e8 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -486,6 +486,84 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void) git_index_free(index); } +struct checkout_cancel_at { + const char *filename; + int error; + int count; +}; + +static int checkout_cancel_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + struct checkout_cancel_at *ca = payload; + + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + + ca->count++; + + if (!strcmp(path, ca->filename)) + return ca->error; + + return 0; +} + +void test_checkout_tree__can_cancel_checkout_from_notify(void) +{ + struct checkout_cancel_at ca; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + git_object *obj = NULL; + + assert_on_branch(g_repo, "master"); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + ca.filename = "new.txt"; + ca.error = -5555; + ca.count = 0; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + opts.notify_cb = checkout_cancel_cb; + opts.notify_payload = &ca; + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_assert(!git_path_exists("testrepo/new.txt")); + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555); + + cl_assert(!git_path_exists("testrepo/new.txt")); + + /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */ + /* on case-sensitive FS = README, then above */ + + if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */ + cl_assert_equal_i(3, ca.count); + else + cl_assert_equal_i(4, ca.count); + + /* and again with a different stopping point and return code */ + ca.filename = "README"; + ca.error = 123; + ca.count = 0; + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123); + + cl_assert(!git_path_exists("testrepo/new.txt")); + + if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */ + cl_assert_equal_i(4, ca.count); + else + cl_assert_equal_i(1, ca.count); + + git_object_free(obj); +} + void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) { git_index *index = NULL; diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c index a286e2a8f..3cd5fb7f6 100644 --- a/tests/clone/nonetwork.c +++ b/tests/clone/nonetwork.c @@ -22,7 +22,7 @@ void test_clone_nonetwork__initialize(void) memset(&g_options, 0, sizeof(git_clone_options)); g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.checkout_opts = dummy_opts; - g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; g_options.remote_callbacks = dummy_callbacks; } @@ -151,6 +151,61 @@ void test_clone_nonetwork__can_checkout_given_branch(void) cl_git_pass(git_repository_head(&g_ref, g_repo)); cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test"); + + cl_assert(git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_fetch_transfer_progress_cb( + const git_transfer_progress *stats, void *data) +{ + GIT_UNUSED(stats); GIT_UNUSED(data); + return -54321; +} + +void test_clone_nonetwork__can_cancel_clone_in_fetch(void) +{ + g_options.checkout_branch = "test"; + + g_options.remote_callbacks.transfer_progress = + clone_cancel_fetch_transfer_progress_cb; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -54321); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_checkout_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + const char *at_file = payload; + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + if (!strcmp(path, at_file)) + return -12345; + return 0; +} + +void test_clone_nonetwork__can_cancel_clone_in_checkout(void) +{ + g_options.checkout_branch = "test"; + + g_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + g_options.checkout_opts.notify_cb = clone_cancel_checkout_cb; + g_options.checkout_opts.notify_payload = "readme.txt"; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -12345); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); } void test_clone_nonetwork__can_detached_head(void) diff --git a/tests/config/read.c b/tests/config/read.c index abc088d59..25672729f 100644 --- a/tests/config/read.c +++ b/tests/config/read.c @@ -247,7 +247,7 @@ void test_config_read__foreach(void) count = 3; cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count)); - cl_assert_equal_i(GIT_EUSER, ret); + cl_assert_equal_i(-100, ret); git_config_free(cfg); } diff --git a/tests/config/rename.c b/tests/config/rename.c new file mode 100644 index 000000000..db07c798f --- /dev/null +++ b/tests/config/rename.c @@ -0,0 +1,85 @@ +#include "clar_libgit2.h" +#include "config.h" + +static git_repository *g_repo = NULL; +static git_config *g_config = NULL; + +void test_config_rename__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&g_config, g_repo)); +} + +void test_config_rename__cleanup(void) +{ + git_config_free(g_config); + g_config = NULL; + + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_config_rename__can_rename(void) +{ + const git_config_entry *ce; + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); +} + +void test_config_rename__prevent_overwrite(void) +{ + const git_config_entry *ce; + + cl_git_pass(git_config_set_string( + g_config, "branch.local-track.remote", "yellow")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s("yellow", ce->value); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + + /* so, we don't currently prevent overwrite... */ + /* { + const git_error *err; + cl_assert((err = giterr_last()) != NULL); + cl_assert(err->message != NULL); + } */ +} + +static void assert_invalid_config_section_name( + git_repository *repo, const char *name) +{ + cl_git_fail_with( + git_config_rename_section(repo, "branch.remoteless", name), + GIT_EINVALIDSPEC); +} + +void test_config_rename__require_a_valid_new_name(void) +{ + assert_invalid_config_section_name(g_repo, ""); + assert_invalid_config_section_name(g_repo, "bra\nch"); + assert_invalid_config_section_name(g_repo, "branc#"); + assert_invalid_config_section_name(g_repo, "bra\nch.duh"); + assert_invalid_config_section_name(g_repo, "branc#.duh"); +} diff --git a/tests/config/validkeyname.c b/tests/config/validkeyname.c index 33699737b..0ef4a9ae3 100644 --- a/tests/config/validkeyname.c +++ b/tests/config/validkeyname.c @@ -46,23 +46,3 @@ void test_config_validkeyname__accessing_requires_a_valid_name(void) assert_invalid_config_key_name("dif.dir\nstat.lines"); assert_invalid_config_key_name("dif.dirstat.li\nes"); } - -static void assert_invalid_config_section_name(git_repository *repo, const char *name) -{ - cl_git_fail_with(git_config_rename_section(repo, "branch.remoteless", name), GIT_EINVALIDSPEC); -} - -void test_config_validkeyname__renaming_a_section_requires_a_valid_name(void) -{ - git_repository *repo; - - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - - assert_invalid_config_section_name(repo, ""); - assert_invalid_config_section_name(repo, "bra\nch"); - assert_invalid_config_section_name(repo, "branc#"); - assert_invalid_config_section_name(repo, "bra\nch.duh"); - assert_invalid_config_section_name(repo, "branc#.duh"); - - git_repository_free(repo); -} diff --git a/tests/config/write.c b/tests/config/write.c index 15f750dc0..922d75557 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -303,3 +303,4 @@ void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void) git_config_free(cfg); } + diff --git a/tests/core/path.c b/tests/core/path.c index cf2d5e944..471491b87 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -350,15 +350,26 @@ void test_core_path__10_fromurl(void) typedef struct { int expect_idx; + int cancel_after; char **expect; } check_walkup_info; +#define CANCEL_VALUE 1234 + static int check_one_walkup_step(void *ref, git_buf *path) { check_walkup_info *info = (check_walkup_info *)ref; + + if (!info->cancel_after) { + cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); + return CANCEL_VALUE; + } + info->cancel_after--; + cl_assert(info->expect[info->expect_idx] != NULL); cl_assert_equal_s(info->expect[info->expect_idx], path->ptr); info->expect_idx++; + return 0; } @@ -381,6 +392,7 @@ void test_core_path__11_walkup(void) check_walkup_info info; info.expect = expect; + info.cancel_after = -1; for (i = 0, j = 0; expect[i] != NULL; i++, j++) { @@ -400,6 +412,42 @@ void test_core_path__11_walkup(void) git_buf_free(&p); } +void test_core_path__11a_walkup_cancel(void) +{ + git_buf p = GIT_BUF_INIT; + int cancel[] = { 3, 2, 1, 0 }; + char *expect[] = { + "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, + "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, + "/a/b/c/d/e", "[CANCEL]", NULL, + "[CANCEL]", NULL, + NULL + }; + char *root[] = { NULL, NULL, "/", "", NULL }; + int i, j; + check_walkup_info info; + + info.expect = expect; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_buf_sets(&p, expect[i]); + + info.cancel_after = cancel[j]; + info.expect_idx = i; + + cl_assert_equal_i( + CANCEL_VALUE, + git_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + /* skip to next run of expectations */ + while (expect[i] != NULL) i++; + } + + git_buf_free(&p); +} + void test_core_path__12_offset_to_path_root(void) { cl_assert(git_path_root("non/rooted/path") == -1); diff --git a/tests/core/pool.c b/tests/core/pool.c index 3073c4a45..351d0c20f 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -139,7 +139,8 @@ void test_core_pool__strndup_limit(void) git_pool p; cl_git_pass(git_pool_init(&p, 1, 100)); - cl_assert(git_pool_strndup(&p, "foo", -1) == NULL); + /* ensure 64 bit doesn't overflow */ + cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); git_pool_clear(&p); } diff --git a/tests/diff/index.c b/tests/diff/index.c index 8f4567137..21afe8da2 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -128,9 +128,7 @@ void test_diff_index__1(void) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts)); cl_assert_equal_i( - GIT_EUSER, - git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) - ); + 1, git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) ); cl_assert_equal_i(2, exp.files); diff --git a/tests/diff/notify.c b/tests/diff/notify.c index cc33cb71c..da7390d3f 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -20,7 +20,7 @@ static int assert_called_notifications( { bool found = false; notify_expected *exp = (notify_expected*)payload; - notify_expected *e;; + notify_expected *e; GIT_UNUSED(diff_so_far); @@ -182,10 +182,12 @@ void test_diff_notify__notify_cb_can_abort_diff(void) opts.pathspec.count = 1; pathspec = "file_deleted"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); pathspec = "staged_changes_modified_file"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); } static int filter_all( diff --git a/tests/diff/patch.c b/tests/diff/patch.c index bd1598b21..0cef3bd3a 100644 --- a/tests/diff/patch.c +++ b/tests/diff/patch.c @@ -30,8 +30,6 @@ static int check_removal_cb( const git_diff_line *line, void *payload) { - GIT_UNUSED(payload); - switch (line->origin) { case GIT_DIFF_LINE_FILE_HDR: cl_assert_equal_s(EXPECTED_HEADER, line->content); @@ -40,10 +38,12 @@ static int check_removal_cb( case GIT_DIFF_LINE_HUNK_HDR: cl_assert_equal_s(EXPECTED_HUNK, line->content); - /* Fall through */ + goto check_hunk; case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_DELETION: + if (payload != NULL) + return *(int *)payload; goto check_hunk; default: @@ -101,6 +101,39 @@ void test_diff_patch__can_properly_display_the_removal_of_a_file(void) git_tree_free(one); } +void test_diff_patch__can_cancel_diff_print(void) +{ + const char *one_sha = "26a125e"; + const char *another_sha = "735b6a2"; + git_tree *one, *another; + git_diff *diff; + int fail_with; + + g_repo = cl_git_sandbox_init("status"); + + one = resolve_commit_oid_to_tree(g_repo, one_sha); + another = resolve_commit_oid_to_tree(g_repo, another_sha); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL)); + + fail_with = -2323; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + fail_with = 45; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + git_diff_free(diff); + + git_tree_free(another); + git_tree_free(one); +} + void test_diff_patch__to_string(void) { const char *one_sha = "26a125e"; diff --git a/tests/diff/rename.c b/tests/diff/rename.c index ca6d076d6..93e69f479 100644 --- a/tests/diff/rename.c +++ b/tests/diff/rename.c @@ -111,6 +111,28 @@ void test_diff_rename__match_oid(void) git_diff_free(diff); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, old_tree, new_tree, &diffopts)); + + /* git diff --find-copies-harder -M100 -B100 \ + * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \ + * 2bc7f351d20b53f1c72c16c4b036e491c478c49a + */ + opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | + GIT_DIFF_FIND_EXACT_MATCH_ONLY; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_free(diff); + git_tree_free(old_tree); git_tree_free(new_tree); } diff --git a/tests/index/addall.c b/tests/index/addall.c index 44c51279d..452733710 100644 --- a/tests/index/addall.c +++ b/tests/index/addall.c @@ -4,6 +4,7 @@ #include "fileops.h" git_repository *g_repo = NULL; +#define TEST_DIR "addall" void test_index_addall__initialize(void) { @@ -13,6 +14,8 @@ void test_index_addall__cleanup(void) { git_repository_free(g_repo); g_repo = NULL; + + cl_fixture_cleanup(TEST_DIR); } #define STATUS_INDEX_FLAGS \ @@ -132,6 +135,25 @@ static void check_stat_data(git_index *index, const char *path, bool match) } } +static void addall_create_test_repo(bool check_every_step) +{ + cl_git_pass(git_repository_init(&g_repo, TEST_DIR, false)); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/file.foo", "a file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.bar", "another file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); +} + void test_index_addall__repo_lifecycle(void) { int error; @@ -139,43 +161,33 @@ void test_index_addall__repo_lifecycle(void) git_strarray paths = { NULL, 0 }; char *strs[1]; - cl_git_pass(git_repository_init(&g_repo, "addall", false)); - check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + addall_create_test_repo(true); cl_git_pass(git_repository_index(&index, g_repo)); - cl_git_mkfile("addall/file.foo", "a file"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); - - cl_git_mkfile("addall/.gitignore", "*.foo\n"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); - - cl_git_mkfile("addall/file.bar", "another file"); - check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); - strs[0] = "file.*"; paths.strings = strs; paths.count = 1; cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); - cl_git_rewritefile("addall/file.bar", "new content for file"); - check_stat_data(index, "addall/file.bar", false); + cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file"); + check_stat_data(index, TEST_DIR "/file.bar", false); check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); - cl_git_mkfile("addall/file.zzz", "yet another one"); - cl_git_mkfile("addall/other.zzz", "yet another one"); - cl_git_mkfile("addall/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit"); @@ -195,27 +207,27 @@ void test_index_addall__repo_lifecycle(void) /* add with force - should allow */ cl_git_pass(git_index_add_all( index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); /* now it's in the index, so regular add should work */ - cl_git_rewritefile("addall/file.foo", "new content for file"); - check_stat_data(index, "addall/file.foo", false); + cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file"); + check_stat_data(index, TEST_DIR "/file.foo", false); check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); cl_git_pass(git_index_add_bypath(index, "more.zzz")); - check_stat_data(index, "addall/more.zzz", true); + check_stat_data(index, TEST_DIR "/more.zzz", true); check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); - cl_git_rewritefile("addall/file.zzz", "new content for file"); + cl_git_rewritefile(TEST_DIR "/file.zzz", "new content for file"); check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); cl_git_pass(git_index_add_bypath(index, "file.zzz")); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); strs[0] = "*.zzz"; @@ -228,7 +240,7 @@ void test_index_addall__repo_lifecycle(void) cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit"); check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); - cl_must_pass(p_unlink("addall/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); /* update_all should be able to remove entries */ @@ -240,9 +252,9 @@ void test_index_addall__repo_lifecycle(void) check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); /* must be able to remove at any position while still updating other files */ - cl_must_pass(p_unlink("addall/.gitignore")); - cl_git_rewritefile("addall/file.zzz", "reconstructed file"); - cl_git_rewritefile("addall/more.zzz", "altered file reality"); + cl_must_pass(p_unlink(TEST_DIR "/.gitignore")); + cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file"); + cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality"); check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); @@ -256,3 +268,89 @@ void test_index_addall__repo_lifecycle(void) git_index_free(index); } + +static int addall_match_prefix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__prefixcmp(path, payload) ? 0 : 1; +} + +static int addall_match_suffix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__suffixcmp(path, payload) ? 0 : 1; +} + +static int addall_cancel_at( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !strcmp(path, payload) ? -123 : 0; +} + +void test_index_addall__callback_filtering(void) +{ + git_index *index; + + addall_create_test_repo(false); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "file.")); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "other")); + check_stat_data(index, TEST_DIR "/other.zzz", true); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_remove_all(index, NULL, addall_match_suffix, ".zzz")); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123); + check_status(g_repo, 3, 0, 0, 2, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 5, 0, 0, 0, 0, 0, 1); + + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/more.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/other.zzz")); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123); + /* file.zzz removed from index (so Index Adds 5 -> 4) and + * more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */ + check_status(g_repo, 4, 0, 0, 0, 2, 0, 1); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123); + /* more.zzz removed from index (so Index Adds 4 -> 3) and + * Just other.zzz removed (so Worktree Dels 2 -> 1) */ + check_status(g_repo, 3, 0, 0, 0, 1, 0, 1); + + git_index_free(index); +} diff --git a/tests/notes/notes.c b/tests/notes/notes.c index 82dcaf8ca..c2579a2c4 100644 --- a/tests/notes/notes.c +++ b/tests/notes/notes.c @@ -129,7 +129,7 @@ void test_notes_notes__can_cancel_foreach(void) create_note(¬e_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); cl_assert_equal_i( - GIT_EUSER, + 1, git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_cancel_cb, &retrieved_notes)); } diff --git a/tests/object/blob/fromchunks.c b/tests/object/blob/fromchunks.c index 03ed4efb4..b61cabfe1 100644 --- a/tests/object/blob/fromchunks.c +++ b/tests/object/blob/fromchunks.c @@ -59,7 +59,7 @@ void test_object_blob_fromchunks__doesnot_overwrite_an_already_existing_object(v git_buf content = GIT_BUF_INIT; git_oid expected_oid, oid; int howmany = 7; - + cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); @@ -117,3 +117,40 @@ void test_object_blob_fromchunks__creating_a_blob_from_chunks_honors_the_attribu assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt"); assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno"); } + +static int failing_chunked_source_cb( + char *content, size_t max_length, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(max_length); + + (*count)--; + if (*count == 0) + return -1234; + + strcpy(content, textual_content); + return (int)strlen(textual_content); +} + +void test_object_blob_fromchunks__can_stop_with_error(void) +{ + git_oid expected_oid, oid; + git_object *blob; + int howmany = 7; + + cl_git_pass(git_oid_fromstr( + &expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); + + cl_git_fail_with(git_blob_create_fromchunks( + &oid, repo, NULL, failing_chunked_source_cb, &howmany), -1234); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); +} + diff --git a/tests/object/tree/walk.c b/tests/object/tree/walk.c index 1207e864c..f8005e579 100644 --- a/tests/object/tree/walk.c +++ b/tests/object/tree/walk.c @@ -59,7 +59,7 @@ static int treewalk_stop_cb( (*count) += 1; - return (*count == 2) ? -1 : 0; + return (*count == 2) ? -123 : 0; } static int treewalk_stop_immediately_cb( @@ -83,20 +83,20 @@ void test_object_tree_walk__1(void) ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL)); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL)); git_tree_free(tree); @@ -152,7 +152,7 @@ void test_object_tree_walk__2(void) memset(&data, 0, sizeof(data)); data.stop = "3.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(3, data.files); cl_assert_equal_i(2, data.dirs); @@ -168,7 +168,7 @@ void test_object_tree_walk__2(void) memset(&data, 0, sizeof(data)); data.stop = "new.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(7, data.files); cl_assert_equal_i(4, data.dirs); diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index 468c0ccd1..3bea0ed4d 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -164,24 +164,25 @@ void test_object_tree_write__sorted_subtrees(void) git_treebuilder_free(builder); } +static struct { + unsigned int attr; + const char *filename; +} _entries[] = { + { GIT_FILEMODE_BLOB, "aardvark" }, + { GIT_FILEMODE_BLOB, ".first" }, + { GIT_FILEMODE_BLOB, "apple" }, + { GIT_FILEMODE_BLOB, "last"}, + { GIT_FILEMODE_BLOB, "apple_after"}, + { GIT_FILEMODE_BLOB, "after_aardvark"}, + { 0, NULL }, +}; + void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) { git_treebuilder *builder; - int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; + int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; git_oid blank_oid, tree_oid; git_tree *tree; - struct { - unsigned int attr; - const char *filename; - } entries[] = { - { GIT_FILEMODE_BLOB, "aardvark" }, - { GIT_FILEMODE_BLOB, ".first" }, - { GIT_FILEMODE_BLOB, "apple" }, - { GIT_FILEMODE_BLOB, "last"}, - { GIT_FILEMODE_BLOB, "apple_after"}, - { GIT_FILEMODE_BLOB, "after_aardvark"}, - { 0, NULL }, - }; memset(&blank_oid, 0x0, sizeof(blank_oid)); @@ -189,9 +190,9 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder)); - for (i = 0; entries[i].filename; ++i) + for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, - builder, entries[i].filename, &blank_oid, entries[i].attr)); + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); @@ -260,3 +261,56 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) git_tree_free(tree); } + +static int treebuilder_filter_prefixed( + const git_tree_entry *entry, void *payload) +{ + return !git__prefixcmp(git_tree_entry_name(entry), payload); +} + +void test_object_tree_write__filtering(void) +{ + git_treebuilder *builder; + int i; + git_oid blank_oid, tree_oid; + git_tree *tree; + + memset(&blank_oid, 0x0, sizeof(blank_oid)); + + cl_git_pass(git_treebuilder_create(&builder, NULL)); + + for (i = 0; _entries[i].filename; ++i) + cl_git_pass(git_treebuilder_insert(NULL, + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); + + cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") != NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple"); + + cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") == NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a"); + + cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "aardvark") == NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); + + git_treebuilder_free(builder); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); + + cl_assert_equal_i(2, (int)git_tree_entrycount(tree)); + + git_tree_free(tree); +} diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c index ebb8866f7..256ae9cd7 100644 --- a/tests/odb/foreach.c +++ b/tests/odb/foreach.c @@ -5,7 +5,6 @@ static git_odb *_odb; static git_repository *_repo; -static int nobj; void test_odb_foreach__cleanup(void) { @@ -18,10 +17,10 @@ void test_odb_foreach__cleanup(void) static int foreach_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); return 0; } @@ -38,43 +37,46 @@ static int foreach_cb(const git_oid *oid, void *data) */ void test_odb_foreach__foreach(void) { + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */ } void test_odb_foreach__one_pack(void) { git_odb_backend *backend = NULL; + int nobj = 0; cl_git_pass(git_odb_new(&_odb)); cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"))); cl_git_pass(git_odb_add_backend(_odb, backend, 1)); _repo = NULL; - nobj = 0; - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert(nobj == 1628); } static int foreach_stop_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); - return (nobj == 1000); + return (*nobj == 1000) ? -321 : 0; } void test_odb_foreach__interrupt_foreach(void) { - nobj = 0; + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL)); + cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj)); cl_assert(nobj == 1000); } diff --git a/tests/online/clone.c b/tests/online/clone.c index efc76d958..be4421ae5 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -273,7 +273,7 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload) GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return 1; + return 4321; return 0; } @@ -281,7 +281,8 @@ void test_online_clone__can_cancel(void) { g_options.remote_callbacks.transfer_progress = cancel_at_half; - cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER); + cl_git_fail_with( + git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321); } diff --git a/tests/online/fetch.c b/tests/online/fetch.c index 5153a7ae0..7e9dfdbbe 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -129,7 +129,7 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload) GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return -1; + return -4321; return 0; } @@ -147,7 +147,7 @@ void test_online_fetch__can_cancel(void) git_remote_set_callbacks(remote, &callbacks); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_fail_with(git_remote_download(remote), GIT_EUSER); + cl_git_fail_with(git_remote_download(remote), -4321); git_remote_disconnect(remote); git_remote_free(remote); } diff --git a/tests/online/push.c b/tests/online/push.c index be505c3a1..33f174654 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -207,6 +207,7 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r } cl_assert_equal_i(error, GIT_ITEROVER); + git_branch_iterator_free(iter); /* Loop through expected refs, make sure they exist */ for (i = 0; i < expected_refs_len; i++) { @@ -371,19 +372,25 @@ void test_online_push__cleanup(void) cl_git_sandbox_cleanup(); } -static int push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload) +static int push_pack_progress_cb( + int stage, unsigned int current, unsigned int total, void* payload) { - int *was_called = (int *) payload; + int *calls = (int *)payload; GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total); - *was_called = 1; + if (*calls < 0) + return *calls; + (*calls)++; return 0; } -static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload) +static int push_transfer_progress_cb( + unsigned int current, unsigned int total, size_t bytes, void* payload) { - int *was_called = (int *) payload; + int *calls = (int *)payload; GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes); - *was_called = 1; + if (*calls < 0) + return *calls; + (*calls)++; return 0; } @@ -397,15 +404,16 @@ static int push_transfer_progress_cb(unsigned int current, unsigned int total, s * @param expected_ret expected return value from git_push_finish() * @param check_progress_cb Check that the push progress callbacks are called */ -static void do_push(const char *refspecs[], size_t refspecs_len, +static void do_push( + const char *refspecs[], size_t refspecs_len, push_status expected_statuses[], size_t expected_statuses_len, - expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb) + expected_ref expected_refs[], size_t expected_refs_len, + int expected_ret, int check_progress_cb) { git_push *push; git_push_options opts = GIT_PUSH_OPTIONS_INIT; size_t i; - int ret; - int pack_progress_called = 0, transfer_progress_called = 0; + int pack_progress_calls = 0, transfer_progress_calls = 0; if (_remote) { /* Auto-detect the number of threads to use */ @@ -416,30 +424,35 @@ static void do_push(const char *refspecs[], size_t refspecs_len, cl_git_pass(git_push_new(&push, _remote)); cl_git_pass(git_push_set_options(push, &opts)); - if (check_progress_cb) - cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called)); + if (check_progress_cb) { + /* if EUSER, then abort in transfer */ + if (expected_ret == GIT_EUSER) + transfer_progress_calls = GIT_EUSER; + + cl_git_pass( + git_push_set_callbacks( + push, push_pack_progress_cb, &pack_progress_calls, + push_transfer_progress_cb, &transfer_progress_calls)); + } for (i = 0; i < refspecs_len; i++) cl_git_pass(git_push_add_refspec(push, refspecs[i])); if (expected_ret < 0) { - cl_git_fail(ret = git_push_finish(push)); + cl_git_fail_with(git_push_finish(push), expected_ret); cl_assert_equal_i(0, git_push_unpack_ok(push)); - } - else { - cl_git_pass(ret = git_push_finish(push)); + } else { + cl_git_pass(git_push_finish(push)); cl_assert_equal_i(1, git_push_unpack_ok(push)); } - if (check_progress_cb) { - cl_assert_equal_i(1, pack_progress_called); - cl_assert_equal_i(1, transfer_progress_called); + if (check_progress_cb && !expected_ret) { + cl_assert(pack_progress_calls > 0); + cl_assert(transfer_progress_calls > 0); } do_verify_push_status(push, expected_statuses, expected_statuses_len); - cl_assert_equal_i(expected_ret, ret); - verify_refs(_remote, expected_refs, expected_refs_len); cl_git_pass(git_push_update_tips(push)); @@ -507,6 +520,12 @@ void test_online_push__b5(void) exp_refs, ARRAY_SIZE(exp_refs), 0, 1); } +void test_online_push__b5_cancel(void) +{ + const char *specs[] = { "refs/heads/b5:refs/heads/b5" }; + do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1); +} + void test_online_push__multi(void) { const char *specs[] = { @@ -731,7 +750,7 @@ void test_online_push__bad_refspecs(void) git_push *push; if (_remote) { -// cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); +/* cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); */ cl_git_pass(git_push_new(&push, _remote)); /* Unexpanded branch names not supported */ diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c index 07963a9e7..084f8e666 100644 --- a/tests/pack/indexer.c +++ b/tests/pack/indexer.c @@ -11,7 +11,7 @@ * This is a packfile with three objects. The second is a delta which * depends on the third, which is also a delta. */ -unsigned char out_of_order_pack[] = { +static const unsigned char out_of_order_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -23,13 +23,13 @@ unsigned char out_of_order_pack[] = { 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92, 0x6f, 0xae, 0x66, 0x75 }; -unsigned int out_of_order_pack_len = 112; +static const unsigned int out_of_order_pack_len = 112; /* * Packfile with two objects. The second is a delta against an object * which is not in the packfile */ -unsigned char thin_pack[] = { +static const unsigned char thin_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -38,18 +38,19 @@ unsigned char thin_pack[] = { 0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97, 0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04 }; -unsigned int thin_pack_len = 78; +static const unsigned int thin_pack_len = 78; -unsigned char base_obj[] = { 07, 076 }; -unsigned int base_obj_len = 2; +static const unsigned char base_obj[] = { 07, 076 }; +static const unsigned int base_obj_len = 2; void test_pack_indexer__out_of_order(void) { - git_indexer *idx; - git_transfer_progress stats; + git_indexer *idx = 0; + git_transfer_progress stats = { 0 }; cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); - cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats)); + cl_git_pass(git_indexer_append( + idx, out_of_order_pack, out_of_order_pack_len, &stats)); cl_git_pass(git_indexer_commit(idx, &stats)); cl_assert_equal_i(stats.total_objects, 3); @@ -61,8 +62,8 @@ void test_pack_indexer__out_of_order(void) void test_pack_indexer__fix_thin(void) { - git_indexer *idx; - git_transfer_progress stats; + git_indexer *idx = NULL; + git_transfer_progress stats = { 0 }; git_repository *repo; git_odb *odb; git_oid id, should_id; diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c index 1ae2322a5..53db81828 100644 --- a/tests/pack/packbuilder.c +++ b/tests/pack/packbuilder.c @@ -12,6 +12,7 @@ static git_packbuilder *_packbuilder; static git_indexer *_indexer; static git_vector _commits; static int _commits_is_initialized; +static git_transfer_progress _stats; void test_pack_packbuilder__initialize(void) { @@ -20,6 +21,7 @@ void test_pack_packbuilder__initialize(void) cl_git_pass(git_packbuilder_new(&_packbuilder, _repo)); cl_git_pass(git_vector_init(&_commits, 0, NULL)); _commits_is_initialized = 1; + memset(&_stats, 0, sizeof(_stats)); } void test_pack_packbuilder__cleanup(void) @@ -184,11 +186,10 @@ void test_pack_packbuilder__permissions_readwrite(void) test_write_pack_permission(0666, 0666); } -static git_transfer_progress stats; static int foreach_cb(void *buf, size_t len, void *payload) { git_indexer *idx = (git_indexer *) payload; - cl_git_pass(git_indexer_append(idx, buf, len, &stats)); + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); return 0; } @@ -199,6 +200,24 @@ void test_pack_packbuilder__foreach(void) seed_packbuilder(); cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx)); - cl_git_pass(git_indexer_commit(idx, &stats)); + cl_git_pass(git_indexer_commit(idx, &_stats)); + git_indexer_free(idx); +} + +static int foreach_cancel_cb(void *buf, size_t len, void *payload) +{ + git_indexer *idx = (git_indexer *)payload; + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); + return (_stats.total_objects > 2) ? -1111 : 0; +} + +void test_pack_packbuilder__foreach_with_cancel(void) +{ + git_indexer *idx; + + seed_packbuilder(); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); + cl_git_fail_with( + git_packbuilder_foreach(_packbuilder, foreach_cancel_cb, idx), -1111); git_indexer_free(idx); } diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c index ecf14e006..9d233de1a 100644 --- a/tests/refs/branches/move.c +++ b/tests/refs/branches/move.c @@ -66,12 +66,54 @@ void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_n void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void) { git_reference *original_ref, *new_ref; + git_config *config; + const git_config_entry *ce; + char *original_remote, *original_merge; + + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + original_remote = strdup(ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + original_merge = strdup(ce->value); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0)); + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "cannot-fetch", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + git_reference_free(original_ref); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/track-local")); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + free(original_remote); free(original_merge); git_reference_free(original_ref); + git_config_free(config); } void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) diff --git a/tests/refs/foreachglob.c b/tests/refs/foreachglob.c index 2c458082f..c0f6ce763 100644 --- a/tests/refs/foreachglob.c +++ b/tests/refs/foreachglob.c @@ -81,14 +81,14 @@ static int interrupt_cb(const char *reference_name, void *payload) (*count)++; - return (*count == 11); + return (*count == 11) ? -1000 : 0; } void test_refs_foreachglob__can_cancel(void) { int count = 0; - cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( + cl_assert_equal_i(-1000, git_reference_foreach_glob( repo, "*", interrupt_cb, &count) ); cl_assert_equal_i(11, count); diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c index 266410fdf..a29b0cf8b 100644 --- a/tests/refs/iterator.c +++ b/tests/refs/iterator.c @@ -46,36 +46,43 @@ static int refcmp_cb(const void *a, const void *b) return strcmp(refa->name, refb->name); } +static void assert_all_refnames_match(git_vector *output) +{ + size_t i; + git_reference *ref; + + cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames)); + + git_vector_sort(output); + + git_vector_foreach(output, i, ref) { + cl_assert_equal_s(ref->name, refnames[i]); + git_reference_free(ref); + } + + git_vector_free(output); +} + void test_refs_iterator__list(void) { git_reference_iterator *iter; git_vector output; git_reference *ref; - int error; - size_t i; cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); cl_git_pass(git_reference_iterator_new(&iter, repo)); - do { - error = git_reference_next(&ref, iter); - cl_assert(error == 0 || error == GIT_ITEROVER); - if (error != GIT_ITEROVER) { - cl_git_pass(git_vector_insert(&output, ref)); - } - } while (!error); + while (1) { + int error = git_reference_next(&ref, iter); + if (error == GIT_ITEROVER) + break; + cl_git_pass(error); + cl_git_pass(git_vector_insert(&output, ref)); + } git_reference_iterator_free(iter); - cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); - git_vector_sort(&output); - - git_vector_foreach(&output, i, ref) { - cl_assert_equal_s(ref->name, refnames[i]); - git_reference_free(ref); - } - - git_vector_free(&output); + assert_all_refnames_match(&output); } void test_refs_iterator__empty(void) @@ -95,3 +102,87 @@ void test_refs_iterator__empty(void) git_odb_free(odb); git_repository_free(empty); } + +static int refs_foreach_cb(git_reference *reference, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, reference)); + return 0; +} + +void test_refs_iterator__foreach(void) +{ + git_vector output; + cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); + cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); + assert_all_refnames_match(&output); +} + +static int refs_foreach_cancel_cb(git_reference *reference, void *payload) +{ + int *cancel_after = payload; + + git_reference_free(reference); + + if (!*cancel_after) + return -333; + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_can_cancel(void) +{ + int cancel_after = 3; + cl_git_fail_with( + git_reference_foreach(repo, refs_foreach_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +static int refs_foreach_name_cb(const char *name, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, git__strdup(name))); + return 0; +} + +void test_refs_iterator__foreach_name(void) +{ + git_vector output; + size_t i; + char *name; + + cl_git_pass(git_vector_init(&output, 32, &git__strcmp_cb)); + cl_git_pass( + git_reference_foreach_name(repo, refs_foreach_name_cb, &output)); + + cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); + git_vector_sort(&output); + + git_vector_foreach(&output, i, name) { + cl_assert_equal_s(name, refnames[i]); + git__free(name); + } + + git_vector_free(&output); +} + +static int refs_foreach_name_cancel_cb(const char *name, void *payload) +{ + int *cancel_after = payload; + if (!*cancel_after) + return -333; + GIT_UNUSED(name); + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_name_can_cancel(void) +{ + int cancel_after = 5; + cl_git_fail_with( + git_reference_foreach_name( + repo, refs_foreach_name_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 34be6d34c..fd57fcc1e 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -552,7 +552,7 @@ static int cb_status__interrupt(const char *p, unsigned int s, void *payload) (*count)++; - return (*count == 8); + return (*count == 8) ? -111 : 0; } void test_status_worktree__interruptable_foreach(void) @@ -561,7 +561,7 @@ void test_status_worktree__interruptable_foreach(void) git_repository *repo = cl_git_sandbox_init("status"); cl_assert_equal_i( - GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count) + -111, git_status_foreach(repo, cb_status__interrupt, &count) ); cl_assert_equal_i(8, count); diff --git a/tests/valgrind-supp-mac.txt b/tests/valgrind-supp-mac.txt index 99833d091..0cdc975fa 100644 --- a/tests/valgrind-supp-mac.txt +++ b/tests/valgrind-supp-mac.txt @@ -103,14 +103,6 @@ fun:ssl23_connect } { - mac-ssl-uninitialized-4 - Memcheck:Param - ... - obj:/usr/lib/libcrypto.0.9.8.dylib - ... - fun:ssl23_connect -} -{ mac-ssl-leak-1 Memcheck:Leak ... |