diff options
97 files changed, 1804 insertions, 1142 deletions
diff --git a/.travis.yml b/.travis.yml index caead67b1..29ef9d40d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,16 +2,22 @@ # see travis-ci.org for details # As CMake is not officially supported we use erlang VMs -language: erlang +language: c + +compiler: + - gcc + - clang # Settings to try env: - - CC=gcc OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release" - - CC=clang OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release" - - CC=gcc OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON" - - CC=clang OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON" - - CC=i586-mingw32msvc-gcc OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON" - + - OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release" + - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON" + +matrix: + include: + - compiler: i586-mingw32msvc-gcc + env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON" + # Make sure CMake is installed install: - sudo apt-get install cmake valgrind @@ -35,6 +41,11 @@ branches: # Notify development list when needed notifications: + irc: + channels: + - irc.freenode.net#libgit2 + on_success: change + on_failure: always recipients: - vicent@github.com email: diff --git a/examples/network/fetch.c b/examples/network/fetch.c index 73bfbddd0..52e0412f4 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -40,9 +40,8 @@ exit: pthread_exit(&data->ret); } -int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) +static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) { - const char *action; char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1]; git_oid_fmt(b_str, b); @@ -68,6 +67,7 @@ int fetch(git_repository *repo, int argc, char **argv) struct dl_data data; git_remote_callbacks callbacks; + argc = argc; // Figure out whether it's a named remote or a URL printf("Fetching %s\n", argv[1]); if (git_remote_load(&remote, repo, argv[1]) < 0) { @@ -96,10 +96,14 @@ int fetch(git_repository *repo, int argc, char **argv) // the download rate. do { usleep(10000); - printf("\rReceived %d/%d objects in %d bytes", stats.processed, stats.total, bytes); + printf("\rReceived %d/%d objects in %zu bytes", stats.processed, stats.total, bytes); } while (!data.finished); - printf("\rReceived %d/%d objects in %d bytes\n", stats.processed, stats.total, bytes); + if (data.ret < 0) + goto on_error; + + pthread_join(worker, NULL); + printf("\rReceived %d/%d objects in %zu bytes\n", stats.processed, stats.total, bytes); // Disconnect the underlying connection to prevent from idling. git_remote_disconnect(remote); diff --git a/examples/network/git2.c b/examples/network/git2.c index 9f0f43e2c..ecb16630b 100644 --- a/examples/network/git2.c +++ b/examples/network/git2.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include "common.h" @@ -17,7 +18,7 @@ struct { { NULL, NULL} }; -int run_command(git_cb fn, int argc, char **argv) +static int run_command(git_cb fn, int argc, char **argv) { int error; git_repository *repo; @@ -46,7 +47,7 @@ int run_command(git_cb fn, int argc, char **argv) int main(int argc, char **argv) { - int i, error; + int i; if (argc < 2) { fprintf(stderr, "usage: %s <cmd> [repo]\n", argv[0]); diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c index ef5a35957..85aac4aff 100644 --- a/examples/network/index-pack.c +++ b/examples/network/index-pack.c @@ -2,13 +2,20 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include "common.h" // This could be run in the main loop whilst the application waits for // the indexing to finish in a worker thread -int index_cb(const git_indexer_stats *stats, void *data) +static int index_cb(const git_indexer_stats *stats, void *data) { + data = data; printf("\rProcessing %d of %d", stats->processed, stats->total); + + return 0; } int index_pack(git_repository *repo, int argc, char **argv) @@ -20,6 +27,7 @@ int index_pack(git_repository *repo, int argc, char **argv) ssize_t read_bytes; char buf[512]; + repo = repo; if (argc < 2) { fprintf(stderr, "I need a packfile\n"); return EXIT_FAILURE; @@ -43,7 +51,7 @@ int index_pack(git_repository *repo, int argc, char **argv) if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0) goto cleanup; - printf("\rIndexing %d of %d", stats.processed, stats.total); + index_cb(&stats, NULL); } while (read_bytes > 0); if (read_bytes < 0) { @@ -65,38 +73,3 @@ int index_pack(git_repository *repo, int argc, char **argv) git_indexer_stream_free(idx); return error; } - -int index_pack_old(git_repository *repo, int argc, char **argv) -{ - git_indexer *indexer; - git_indexer_stats stats; - int error; - char hash[GIT_OID_HEXSZ + 1] = {0}; - - if (argc < 2) { - fprintf(stderr, "I need a packfile\n"); - return EXIT_FAILURE; - } - - // Create a new indexer - error = git_indexer_new(&indexer, argv[1]); - if (error < 0) - return error; - - // Index the packfile. This function can take a very long time and - // should be run in a worker thread. - error = git_indexer_run(indexer, &stats); - if (error < 0) - return error; - - // Write the information out to an index file - error = git_indexer_write(indexer); - - // Get the packfile's hash (which should become it's filename) - git_oid_fmt(hash, git_indexer_hash(indexer)); - puts(hash); - - git_indexer_free(indexer); - - return 0; -} diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c index 39cc64725..822d6f668 100644 --- a/examples/network/ls-remote.c +++ b/examples/network/ls-remote.c @@ -7,12 +7,14 @@ static int show_ref__cb(git_remote_head *head, void *payload) { char oid[GIT_OID_HEXSZ + 1] = {0}; + + payload = payload; git_oid_fmt(oid, &head->oid); printf("%s\t%s\n", oid, head->name); return 0; } -int use_unnamed(git_repository *repo, const char *url) +static int use_unnamed(git_repository *repo, const char *url) { git_remote *remote = NULL; int error; @@ -37,7 +39,7 @@ cleanup: return error; } -int use_remote(git_repository *repo, char *name) +static int use_remote(git_repository *repo, char *name) { git_remote *remote = NULL; int error; @@ -63,8 +65,9 @@ cleanup: int ls_remote(git_repository *repo, int argc, char **argv) { - int error, i; + int error; + argc = argc; /* If there's a ':' in the name, assume it's an URL */ if (strchr(argv[1], ':') != NULL) { error = use_unnamed(repo, argv[1]); diff --git a/include/git2/attr.h b/include/git2/attr.h index fad7183da..2de9f4b0e 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -30,7 +30,7 @@ GIT_BEGIN_DECL * Then for file `xyz.c` looking up attribute "foo" gives a value for * which `GIT_ATTR_TRUE(value)` is true. */ -#define GIT_ATTR_TRUE(attr) ((attr) == git_l_attr__true) +#define GIT_ATTR_TRUE(attr) (git_attr_value(attr) == GIT_ATTR_TRUE_T) /** * GIT_ATTR_FALSE checks if an attribute is set off. In core git @@ -44,7 +44,7 @@ GIT_BEGIN_DECL * Then for file `zyx.h` looking up attribute "foo" gives a value for * which `GIT_ATTR_FALSE(value)` is true. */ -#define GIT_ATTR_FALSE(attr) ((attr) == git_l_attr__false) +#define GIT_ATTR_FALSE(attr) (git_attr_value(attr) == GIT_ATTR_FALSE_T) /** * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This @@ -62,7 +62,7 @@ GIT_BEGIN_DECL * file `onefile.rb` or looking up "bar" on any file will all give * `GIT_ATTR_UNSPECIFIED(value)` of true. */ -#define GIT_ATTR_UNSPECIFIED(attr) (!(attr) || (attr) == git_l_attr__unset) +#define GIT_ATTR_UNSPECIFIED(attr) (git_attr_value(attr) == GIT_ATTR_UNSPECIFIED_T) /** * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as @@ -74,13 +74,29 @@ GIT_BEGIN_DECL * Given this, looking up "eol" for `onefile.txt` will give back the * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true. */ -#define GIT_ATTR_HAS_VALUE(attr) \ - ((attr) && (attr) != git_l_attr__unset && \ - (attr) != git_l_attr__true && (attr) != git_attr__false) +#define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_T) -GIT_EXTERN(const char *) git_l_attr__true; -GIT_EXTERN(const char *) git_l_attr__false; -GIT_EXTERN(const char *) git_l_attr__unset; +typedef enum { + GIT_ATTR_UNSPECIFIED_T = 0, + GIT_ATTR_TRUE_T, + GIT_ATTR_FALSE_T, + GIT_ATTR_VALUE_T, +} git_attr_t; + +/* + * Return the value type for a given attribute. + * + * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute + * was not set at all), or `VALUE`, if the attribute was set to + * an actual string. + * + * If the attribute has a `VALUE` string, it can be accessed normally + * as a NULL-terminated C string. + * + * @param attr The attribute + * @return the value type for the attribute + */ +GIT_EXTERN(git_attr_t) git_attr_value(const char *attr); /** * Check attribute flags: Reading values from index and working directory. @@ -172,18 +188,17 @@ GIT_EXTERN(int) git_attr_get_many( * * @param repo The repository containing the path. * @param flags A combination of GIT_ATTR_CHECK... flags. - * @param path The path inside the repo to check attributes. This - * does not have to exist, but if it does not, then - * it will be treated as a plain file (i.e. not a directory). - * @param callback The function that will be invoked on each attribute - * and attribute value. The name parameter will be the name - * of the attribute and the value will be the value it is - * set to, including possibly NULL if the attribute is - * explicitly set to UNSPECIFIED using the ! sign. This - * will be invoked only once per attribute name, even if - * there are multiple rules for a given file. The highest - * priority rule will be used. + * @param path Path inside the repo to check attributes. This does not have + * to exist, but if it does not, then it will be treated as a + * plain file (i.e. not a directory). + * @param callback Function to invoke on each attribute name and value. The + * value may be NULL is the attribute is explicitly set to + * UNSPECIFIED using the '!' sign. Callback will be invoked + * 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. * @param payload Passed on as extra parameter to callback function. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_attr_foreach( git_repository *repo, diff --git a/include/git2/blob.h b/include/git2/blob.h index 544dc7c41..f0719f15d 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -46,7 +46,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len) +GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB); } diff --git a/include/git2/branch.h b/include/git2/branch.h index 2f46720af..8bf7eb9d4 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -74,6 +74,8 @@ GIT_EXTERN(int) git_branch_delete( /** * Loop over all the branches and issue a callback for each one. * + * If the callback returns a non-zero value, this will stop looping. + * * @param repo Repository where to find the branches. * * @param list_flags Filtering flags for the branch @@ -84,7 +86,7 @@ GIT_EXTERN(int) git_branch_delete( * * @param payload Extra parameter to callback function. * - * @return 0 or an error code. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_branch_foreach( git_repository *repo, diff --git a/include/git2/commit.h b/include/git2/commit.h index e8ecc808b..a159b79e1 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -48,7 +48,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len) +GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT); } diff --git a/include/git2/common.h b/include/git2/common.h index 1af045cff..0af37e81f 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -103,6 +103,29 @@ GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src); */ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); +/** + * Combinations of these values describe the capabilities of libgit2. + */ +enum { + GIT_CAP_THREADS = ( 1 << 0 ), + GIT_CAP_HTTPS = ( 1 << 1 ) +}; + +/** + * Query compile time options for libgit2. + * + * @return A combination of GIT_CAP_* values. + * + * - GIT_CAP_THREADS + * Libgit2 was compiled with thread support. Note that thread support is still to be seen as a + * 'work in progress'. + * + * - GIT_CAP_HTTPS + * Libgit2 supports the https:// protocol. This requires the open ssl library to be + * found when compiling libgit2. + */ +GIT_EXTERN(int) git_libgit2_capabilities(void); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/config.h b/include/git2/config.h index c46e7fc9d..f415fbd9d 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -302,12 +302,12 @@ GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name); * The callback receives the normalized name and value of each variable * 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 returns that value. + * this function stops iterating and returns `GIT_EUSER`. * * @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 or the return value of the callback which didn't return 0 + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_config_foreach( git_config *cfg, @@ -342,7 +342,7 @@ GIT_EXTERN(int) git_config_foreach_match( * * A mapping array looks as follows: * - * git_cvar_map autocrlf_mapping[3] = { + * git_cvar_map autocrlf_mapping[] = { * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE}, * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE}, * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}, diff --git a/include/git2/diff.h b/include/git2/diff.h index 85727d969..79ef7a49b 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -332,6 +332,9 @@ GIT_EXTERN(int) git_diff_merge( * callbacks will not be invoked for binary files on the diff list or for * files whose only changed is a file mode change. * + * Returning a non-zero value from any of the callbacks will terminate + * the iteration and cause this return `GIT_EUSER`. + * * @param diff A git_diff_list generated by one of the above functions. * @param cb_data Reference pointer that will be passed to your callbacks. * @param file_cb Callback function to make per file in the diff. @@ -341,6 +344,7 @@ GIT_EXTERN(int) git_diff_merge( * @param line_cb Optional callback to make per line of diff text. This * same callback will be made for context lines, added, and * removed lines, and even for a deleted trailing newline. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_diff_foreach( git_diff_list *diff, @@ -351,6 +355,14 @@ GIT_EXTERN(int) git_diff_foreach( /** * Iterate over a diff generating text output like "git diff --name-status". + * + * Returning a non-zero value from the callbacks will terminate the + * iteration and cause this return `GIT_EUSER`. + * + * @param diff A git_diff_list generated by one of the above functions. + * @param cb_data Reference pointer that will be passed to your callback. + * @param print_cb Callback to make per line of diff text. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_diff_print_compact( git_diff_list *diff, @@ -362,6 +374,9 @@ GIT_EXTERN(int) git_diff_print_compact( * * This is a super easy way to generate a patch from a diff. * + * Returning a non-zero value from the callbacks will terminate the + * iteration and cause this return `GIT_EUSER`. + * * @param diff A git_diff_list generated by one of the above functions. * @param cb_data Reference pointer that will be passed to your callbacks. * @param print_cb Callback function to output lines of the diff. This @@ -369,6 +384,7 @@ GIT_EXTERN(int) git_diff_print_compact( * headers, and diff lines. Fortunately, you can probably * use various GIT_DIFF_LINE constants to determine what * text you are given. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_diff_print_patch( git_diff_list *diff, @@ -393,6 +409,8 @@ GIT_EXTERN(int) git_diff_print_patch( * When at least one of the blobs being dealt with is binary, the * `git_diff_delta` binary attribute will be set to 1 and no call to the * hunk_cb nor line_cb will be made. + * + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_diff_blobs( git_blob *old_blob, diff --git a/include/git2/errors.h b/include/git2/errors.h index ca7f0de6e..2ab1da403 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -25,6 +25,7 @@ enum { GIT_EEXISTS = -4, GIT_EAMBIGUOUS = -5, GIT_EBUFS = -6, + GIT_EUSER = -7, GIT_PASSTHROUGH = -30, GIT_REVWALKOVER = -31, diff --git a/include/git2/index.h b/include/git2/index.h index c88a1701c..062932e1a 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -280,7 +280,7 @@ GIT_EXTERN(int) git_index_remove(git_index *index, int position); * @param n the position of the entry * @return a pointer to the entry; NULL if out of bounds */ -GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, unsigned int n); +GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, size_t n); /** * Get the count of entries currently in the index @@ -320,7 +320,7 @@ GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_bypath(git_i * @param n the position of the entry * @return a pointer to the unmerged entry; NULL if out of bounds */ -GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, unsigned int n); +GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, size_t n); /** * Return the stage number from a git index entry diff --git a/include/git2/message.h b/include/git2/message.h index 7f2558583..b42cb7677 100644 --- a/include/git2/message.h +++ b/include/git2/message.h @@ -23,8 +23,9 @@ GIT_BEGIN_DECL * * Optionally, can remove lines starting with a "#". * - * @param message_out The user allocated buffer which will be filled with - * the cleaned up message. + * @param message_out The user allocated buffer which will be filled with + * the cleaned up message. Pass NULL if you just want to get the size of the + * prettified message as the output value. * * @param size The size of the allocated buffer message_out. * @@ -32,7 +33,8 @@ GIT_BEGIN_DECL * * @param strip_comments 1 to remove lines starting with a "#", 0 otherwise. * - * @return GIT_SUCCESS or an error code + * @return -1 on error, else number of characters in prettified message + * including the trailing NUL byte */ GIT_EXTERN(int) git_message_prettify(char *message_out, size_t buffer_size, const char *message, int strip_comments); diff --git a/include/git2/notes.h b/include/git2/notes.h index 19073abd1..af480a408 100644 --- a/include/git2/notes.h +++ b/include/git2/notes.h @@ -23,10 +23,11 @@ GIT_BEGIN_DECL * * The note must be freed manually by the user. * - * @param note the note; NULL in case of error - * @param repo the Git repository - * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits" - * @param oid OID of the object + * @param note pointer to the read note; NULL in case of error + * @param repo repository where to look up the note + * @param notes_ref canonical name of the reference to use (optional); + * defaults to "refs/notes/commits" + * @param oid OID of the git object to read the note from * * @return 0 or an error code */ @@ -50,17 +51,17 @@ GIT_EXTERN(const char *) git_note_message(git_note *note); */ GIT_EXTERN(const git_oid *) git_note_oid(git_note *note); - /** * Add a note for an object * - * @param oid pointer to store the OID (optional); NULL in case of error - * @param repo the Git repository + * @param out pointer to store the OID (optional); NULL in case of error + * @param repo repository where to store the note * @param author signature of the notes commit author * @param committer signature of the notes commit committer - * @param notes_ref OID reference to update (optional); defaults to "refs/notes/commits" - * @param oid The OID of the object - * @param oid The note to add for object oid + * @param notes_ref canonical name of the reference to use (optional); + * defaults to "refs/notes/commits" + * @param oid OID of the git object to decorate + * @param note Content of the note to add for object oid * * @return 0 or an error code */ @@ -73,11 +74,12 @@ GIT_EXTERN(int) git_note_create(git_oid *out, git_repository *repo, /** * Remove the note for an object * - * @param repo the Git repository - * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits" + * @param repo repository where the note lives + * @param notes_ref canonical name of the reference to use (optional); + * defaults to "refs/notes/commits" * @param author signature of the notes commit author * @param committer signature of the notes commit committer - * @param oid the oid which note's to be removed + * @param oid OID of the git object to remove the note from * * @return 0 or an error code */ @@ -119,19 +121,21 @@ typedef struct { * * @param repo Repository where to find the notes. * - * @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits". + * @param notes_ref Reference to read from (optional); defaults to + * "refs/notes/commits". * - * @param note_cb Callback to invoke per found annotation. + * @param note_cb Callback to invoke per found annotation. Return non-zero + * to stop looping. * * @param payload Extra parameter to callback function. * - * @return 0 or an error code. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_note_foreach( - git_repository *repo, - const char *notes_ref, - int (*note_cb)(git_note_data *note_data, void *payload), - void *payload + git_repository *repo, + const char *notes_ref, + int (*note_cb)(git_note_data *note_data, void *payload), + void *payload ); /** @} */ diff --git a/include/git2/object.h b/include/git2/object.h index d9e653fd4..722434dec 100644 --- a/include/git2/object.h +++ b/include/git2/object.h @@ -75,7 +75,7 @@ GIT_EXTERN(int) git_object_lookup_prefix( git_object **object_out, git_repository *repo, const git_oid *id, - unsigned int len, + size_t len, git_otype type); /** diff --git a/include/git2/odb.h b/include/git2/odb.h index dac9e06a9..1919f61a0 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -139,7 +139,7 @@ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *i * GIT_ENOTFOUND if the object is not in the database. * GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix) */ -GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len); +GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len); /** * Read the header of an object from the database, without @@ -176,13 +176,14 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); * List all objects available in the database * * The callback will be called for each object available in the - * database. Note that the objects are likely to be returned in the - * index order, which would make accessing the objects in that order - * inefficient. + * database. Note that the objects are likely to be returned in the index + * order, which would make accessing the objects in that order inefficient. + * Return a non-zero value from the callback to stop looping. * * @param db database to use * @param cb the callback to call for each object * @param data data to pass to the callback + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data); diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index 3f67202d1..b812fef1e 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -42,7 +42,7 @@ struct git_odb_backend { void **, size_t *, git_otype *, struct git_odb_backend *, const git_oid *, - unsigned int); + size_t); int (* read_header)( size_t *, git_otype *, @@ -100,6 +100,7 @@ struct git_odb_stream { GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir); GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir, int compression_level, int do_fsync); +GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **backend_out, const char *index_file); GIT_END_DECL diff --git a/include/git2/oid.h b/include/git2/oid.h index a05b40a37..887b33e50 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -136,7 +136,31 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src); * @param b second oid structure. * @return <0, 0, >0 if a < b, a == b, a > b. */ -GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); +GIT_INLINE(int) git_oid_cmp(const git_oid *a, const git_oid *b) +{ + const unsigned char *sha1 = a->id; + const unsigned char *sha2 = b->id; + int i; + + for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) { + if (*sha1 != *sha2) + return *sha1 - *sha2; + } + + return 0; +} + +/** + * Compare two oid structures for equality + * + * @param a first oid structure. + * @param b second oid structure. + * @return true if equal, false otherwise + */ +GIT_INLINE(int) git_oid_equal(const git_oid *a, const git_oid *b) +{ + return !git_oid_cmp(a, b); +} /** * Compare the first 'len' hexadecimal characters (packets of 4 bits) @@ -147,7 +171,7 @@ GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); * @param len the number of hex chars to compare * @return 0 in case of a match */ -GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int len); +GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len); /** * Check if an oid equals an hex formatted object id. diff --git a/include/git2/reflog.h b/include/git2/reflog.h index a73d1f7fd..447915ef8 100644 --- a/include/git2/reflog.h +++ b/include/git2/reflog.h @@ -92,7 +92,7 @@ GIT_EXTERN(unsigned int) git_reflog_entrycount(git_reflog *reflog); * @param idx the position to lookup * @return the entry; NULL if not found */ -GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx); +GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, size_t idx); /** * Remove an entry from the reflog by its index diff --git a/include/git2/refs.h b/include/git2/refs.h index 8dd8e3116..9e7060075 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -268,14 +268,15 @@ GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, un * * The `callback` function will be called for each of the references * in the repository, and will receive the name of the reference and - * the `payload` value passed to this method. + * the `payload` value passed to this method. Returning a non-zero + * value from the callback will terminate the iteration. * * @param repo Repository where to find the refs * @param list_flags Filtering flags for the reference * listing. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback - * @return 0 or an error code + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload); @@ -334,6 +335,8 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); * * @param repo Repository where to find the references. * + * @param glob Glob pattern references should match. + * * @param list_flags Filtering flags for the reference * listing. * diff --git a/include/git2/remote.h b/include/git2/remote.h index 7e563f96f..96f460e98 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -163,9 +163,12 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction); * The remote (or more exactly its transport) must be connected. The * memory belongs to the remote. * + * If you a return a non-zero value from the callback, this will stop + * looping over the refs. + * * @param refs where to store the refs * @param remote the remote - * @return 0 or an error code + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload); diff --git a/include/git2/repository.h b/include/git2/repository.h index ff81b75ec..e727ff317 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -36,6 +36,19 @@ GIT_BEGIN_DECL GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *path); /** + * Create a "fake" repository to wrap an object database + * + * Create a repository object to wrap an object database to be used + * with the API when all you have is an object database. This doesn't + * have any paths associated with it, so use with care. + * + * @param repository pointer to the repo + * @param odb the object database to wrap + * @return 0 or an error code + */ +GIT_EXTERN(int) git_repository_wrap_odb(git_repository **repository, git_odb *odb); + +/** * Look for a git repository and copy its path in the given buffer. * The lookup start from base_path and walk across parent directories * if nothing has been found. The lookup ends when the first repository @@ -302,6 +315,28 @@ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo); */ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index); +/** + * Retrive git's prepared message + * + * Operations such as git revert/cherry-pick/merge with the -n option + * stop just short of creating a commit with the changes and save + * their prepared message in .git/MERGE_MSG so the next git-commit + * execution can present it to the user for them to amend if they + * wish. + * + * Use this function to get the contents of this file. Don't forget to + * remove the file after you create the commit. + */ +GIT_EXTERN(int) git_repository_message(char *buffer, size_t len, git_repository *repo); + +/** + * Remove git's prepared message. + * + * Remove the message that `git_repository_message` retrieves. + */ +GIT_EXTERN(int) git_repository_message_remove(git_repository *repo); + + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/status.h b/include/git2/status.h index 9e7b5de4a..cc94d7680 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -38,11 +38,11 @@ enum { * * The callback is passed the path of the file, the status and the data * pointer passed to this function. If the callback returns something other - * than 0, this function will return that value. + * than 0, this function will stop looping and return GIT_EUSER. * * @param repo a repository object * @param callback the function to call on each file - * @return 0 on success or the return value of the callback that was non-zero + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_status_foreach( git_repository *repo, diff --git a/include/git2/tag.h b/include/git2/tag.h index b522451a1..aab4b77a8 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -46,7 +46,7 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len) +GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, size_t len) { return git_object_lookup_prefix((git_object **)tag, repo, id, len, (git_otype)GIT_OBJ_TAG); } diff --git a/include/git2/tree.h b/include/git2/tree.h index f12b15e2e..85407d7ac 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -50,7 +50,7 @@ GIT_INLINE(int) git_tree_lookup_prefix( git_tree **tree, git_repository *repo, const git_oid *id, - unsigned int len) + size_t len) { return git_object_lookup_prefix((git_object **)tree, repo, id, len, GIT_OBJ_TREE); } @@ -126,7 +126,18 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const c * @param idx the position in the entry list * @return the tree entry; NULL if not found */ -GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, unsigned int idx); +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, size_t idx); + +/** + * Lookup a tree entry by SHA value. + * + * Warning: this must examine every entry in the tree, so it is not fast. + * + * @param tree a previously loaded tree. + * @param oid the sha being looked for + * @return the tree entry; NULL if not found + */ +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid(git_tree *tree, const git_oid *oid); /** * Get the UNIX file attributes of a tree entry @@ -340,8 +351,9 @@ enum git_treewalk_mode { * the current (relative) root for the entry and the entry * data itself. * - * If the callback returns a negative value, the passed entry - * will be skipped on the traversal. + * If the callback returns a positive value, the passed entry will be + * skipped on the traversal (in pre mode). A negative value stops the + * walk. * * @param tree The tree to walk * @param callback Function to call on each tree entry diff --git a/src/attr.c b/src/attr.c index 6fbd005d5..de714a697 100644 --- a/src/attr.c +++ b/src/attr.c @@ -1,10 +1,30 @@ #include "repository.h" #include "fileops.h" #include "config.h" +#include "git2/oid.h" #include <ctype.h> GIT__USE_STRMAP; +const char *git_attr__true = "[internal]__TRUE__"; +const char *git_attr__false = "[internal]__FALSE__"; +const char *git_attr__unset = "[internal]__UNSET__"; + +git_attr_t git_attr_value(const char *attr) +{ + if (attr == NULL || attr == git_attr__unset) + return GIT_ATTR_UNSPECIFIED_T; + + if (attr == git_attr__true) + return GIT_ATTR_TRUE_T; + + if (attr == git_attr__false) + return GIT_ATTR_FALSE_T; + + return GIT_ATTR_VALUE_T; +} + + static int collect_attr_files( git_repository *repo, uint32_t flags, @@ -22,7 +42,7 @@ int git_attr_get( int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; - unsigned int i, j; + size_t i, j; git_attr_file *file; git_attr_name attr; git_attr_rule *rule; @@ -74,7 +94,7 @@ int git_attr_get_many( int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; - unsigned int i, j, k; + size_t i, j, k; git_attr_file *file; git_attr_rule *rule; attr_get_many_info *info = NULL; @@ -138,7 +158,7 @@ int git_attr_foreach( int error; git_attr_path path; git_vector files = GIT_VECTOR_INIT; - unsigned int i, j, k; + size_t i, j, k; git_attr_file *file; git_attr_rule *rule; git_attr_assignment *assign; @@ -163,11 +183,14 @@ int git_attr_foreach( continue; git_strmap_insert(seen, assign->name, assign, error); - if (error >= 0) - error = callback(assign->name, assign->value, payload); + if (error < 0) + goto cleanup; - if (error != 0) + error = callback(assign->name, assign->value, payload); + if (error) { + error = GIT_EUSER; goto cleanup; + } } } } diff --git a/src/attr_file.c b/src/attr_file.c index 837c42d8e..20b3cf631 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -5,10 +5,6 @@ #include "git2/tree.h" #include <ctype.h> -const char *git_l_attr__true = "[internal]__TRUE__"; -const char *git_l_attr__false = "[internal]__FALSE__"; -const char *git_l_attr__unset = "[internal]__UNSET__"; - static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); static void git_attr_rule__clear(git_attr_rule *rule); @@ -183,7 +179,7 @@ int git_attr_file__lookup_one( const char *attr, const char **value) { - unsigned int i; + size_t i; git_attr_name name; git_attr_rule *rule; @@ -493,14 +489,14 @@ int git_attr_assignment__parse( } assign->name_hash = 5381; - assign->value = git_l_attr__true; + assign->value = git_attr__true; /* look for magic name prefixes */ if (*scan == '-') { - assign->value = git_l_attr__false; + assign->value = git_attr__false; scan++; } else if (*scan == '!') { - assign->value = git_l_attr__unset; /* explicit unspecified state */ + assign->value = git_attr__unset; /* explicit unspecified state */ scan++; } else if (*scan == '#') /* comment rest of line */ break; @@ -536,7 +532,7 @@ int git_attr_assignment__parse( } /* expand macros (if given a repo with a macro cache) */ - if (repo != NULL && assign->value == git_l_attr__true) { + if (repo != NULL && assign->value == git_attr__true) { git_attr_rule *macro = git_attr_cache__lookup_macro(repo, assign->name); diff --git a/src/attr_file.h b/src/attr_file.h index 7939f838a..9d6730d90 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -24,6 +24,10 @@ #define GIT_ATTR_FNMATCH_HASWILD (1U << 5) #define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6) +extern const char *git_attr__true; +extern const char *git_attr__false; +extern const char *git_attr__unset; + typedef struct { char *pattern; size_t length; diff --git a/src/cache.c b/src/cache.c index f8d89403b..3aa14f012 100644 --- a/src/cache.c +++ b/src/cache.c @@ -11,6 +11,7 @@ #include "thread-utils.h" #include "util.h" #include "cache.h" +#include "git2/oid.h" int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) { diff --git a/src/commit.c b/src/commit.c index 32c47944b..b66978aff 100644 --- a/src/commit.c +++ b/src/commit.c @@ -226,7 +226,7 @@ GIT_COMMIT_GETTER(const char *, message, commit->message) GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding) GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time) GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) -GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length) +GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_oids.length) GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid); int git_commit_tree(git_tree **tree_out, git_commit *commit) diff --git a/src/common.h b/src/common.h index 1db308fc7..1d85428b3 100644 --- a/src/common.h +++ b/src/common.h @@ -59,4 +59,7 @@ void giterr_set_regex(const regex_t *regex, int error_code); #include "util.h" +typedef struct git_transport git_transport; +typedef struct gitno_buffer gitno_buffer; + #endif /* INCLUDE_common_h__ */ diff --git a/src/config.c b/src/config.c index 98fb3b20d..44cfe760c 100644 --- a/src/config.c +++ b/src/config.c @@ -410,7 +410,7 @@ int git_config_get_multivar(git_config *cfg, const char *name, const char *regex file_internal *internal; git_config_file *file; int ret = GIT_ENOTFOUND; - unsigned int i; + size_t i; assert(cfg->files.length); @@ -434,7 +434,7 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex file_internal *internal; git_config_file *file; int ret = GIT_ENOTFOUND; - unsigned int i; + size_t i; for (i = cfg->files.length; i > 0; --i) { internal = git_vector_get(&cfg->files, i - 1); diff --git a/src/config_file.c b/src/config_file.c index 7ced1e5ba..547509b9f 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -218,8 +218,10 @@ static int file_foreach( continue; /* abort iterator on non-zero return value */ - if ((result = fn(key, var->value, data)) != 0) + if (fn(key, var->value, data)) { + result = GIT_EUSER; goto cleanup; + } } ); @@ -1341,10 +1343,8 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val else value_start = var_end + 1; - if (git__isspace(var_end[-1])) { - do var_end--; - while (git__isspace(var_end[0])); - } + do var_end--; + while (git__isspace(*var_end)); *var_name = git__strndup(line, var_end - line + 1); GITERR_CHECK_ALLOC(*var_name); diff --git a/src/date.c b/src/date.c index f0e637a45..965e6caab 100644 --- a/src/date.c +++ b/src/date.c @@ -121,9 +121,9 @@ static const struct { { "IDLE", +12, 0, }, /* International Date Line East */ }; -static int match_string(const char *date, const char *str) +static size_t match_string(const char *date, const char *str) { - int i = 0; + size_t i = 0; for (i = 0; *date; date++, str++, i++) { if (*date == *str) @@ -149,12 +149,12 @@ static int skip_alpha(const char *date) /* * Parse month, weekday, or timezone name */ -static int match_alpha(const char *date, struct tm *tm, int *offset) +static size_t match_alpha(const char *date, struct tm *tm, int *offset) { unsigned int i; for (i = 0; i < 12; i++) { - int match = match_string(date, month_names[i]); + size_t match = match_string(date, month_names[i]); if (match >= 3) { tm->tm_mon = i; return match; @@ -162,7 +162,7 @@ static int match_alpha(const char *date, struct tm *tm, int *offset) } for (i = 0; i < 7; i++) { - int match = match_string(date, weekday_names[i]); + size_t match = match_string(date, weekday_names[i]); if (match >= 3) { tm->tm_wday = i; return match; @@ -170,8 +170,8 @@ static int match_alpha(const char *date, struct tm *tm, int *offset) } for (i = 0; i < ARRAY_SIZE(timezone_names); i++) { - int match = match_string(date, timezone_names[i].name); - if (match >= 3 || match == (int)strlen(timezone_names[i].name)) { + size_t match = match_string(date, timezone_names[i].name); + if (match >= 3 || match == strlen(timezone_names[i].name)) { int off = timezone_names[i].offset; /* This is bogus, but we like summer */ @@ -241,7 +241,7 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, return 0; } -static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm) +static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm) { time_t now; struct tm now_tm; @@ -319,9 +319,9 @@ static int nodate(struct tm *tm) /* * We've seen a digit. Time? Year? Date? */ -static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt) +static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt) { - int n; + size_t n; char *end; unsigned long num; @@ -349,7 +349,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt case '/': case '-': if (isdigit(end[1])) { - int match = match_multi_number(num, *end, date, end, tm); + size_t match = match_multi_number(num, *end, date, end, tm); if (match) return match; } @@ -413,11 +413,11 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt return n; } -static int match_tz(const char *date, int *offp) +static size_t match_tz(const char *date, int *offp) { char *end; int hour = strtoul(date + 1, &end, 10); - int n = end - (date + 1); + size_t n = end - (date + 1); int min = 0; if (n == 4) { @@ -506,7 +506,7 @@ static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset !match_object_header_date(date + 1, timestamp, offset)) return 0; /* success */ for (;;) { - int match = 0; + size_t match = 0; unsigned char c = *date; /* Stop at end of string or newline */ @@ -685,7 +685,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm ; for (i = 0; i < 12; i++) { - int match = match_string(date, month_names[i]); + size_t match = match_string(date, month_names[i]); if (match >= 3) { tm->tm_mon = i; *touched = 1; @@ -694,7 +694,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm } for (s = special; s->name; s++) { - int len = strlen(s->name); + size_t len = strlen(s->name); if (match_string(date, s->name) == len) { s->fn(tm, now, num); *touched = 1; @@ -704,7 +704,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm if (!*num) { for (i = 1; i < 11; i++) { - int len = strlen(number_name[i]); + size_t len = strlen(number_name[i]); if (match_string(date, number_name[i]) == len) { *num = i; *touched = 1; @@ -720,7 +720,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm tl = typelen; while (tl->type) { - int len = strlen(tl->type); + size_t len = strlen(tl->type); if (match_string(date, tl->type) >= len-1) { update_tm(tm, now, tl->length * *num); *num = 0; @@ -731,7 +731,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm } for (i = 0; i < 7; i++) { - int match = match_string(date, weekday_names[i]); + size_t match = match_string(date, weekday_names[i]); if (match >= 3) { int diff, n = *num -1; *num = 0; @@ -783,7 +783,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num) case '/': case '-': if (isdigit(end[1])) { - int match = match_multi_number(number, *end, date, end, tm); + size_t match = match_multi_number(number, *end, date, end, tm); if (match) return date + match; } diff --git a/src/diff.c b/src/diff.c index 2b1529d63..a5bf07a65 100644 --- a/src/diff.c +++ b/src/diff.c @@ -6,6 +6,7 @@ */ #include "common.h" #include "git2/diff.h" +#include "git2/oid.h" #include "diff.h" #include "fileops.h" #include "config.h" diff --git a/src/diff_output.c b/src/diff_output.c index f6650b345..bd8e8edda 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -8,6 +8,7 @@ #include "git2/diff.h" #include "git2/attr.h" #include "git2/blob.h" +#include "git2/oid.h" #include "xdiff/xdiff.h" #include <ctype.h> #include "diff.h" @@ -23,6 +24,7 @@ typedef struct { unsigned int index; git_diff_delta *delta; git_diff_range range; + int error; } diff_output_info; static int read_next_int(const char **str, int *value) @@ -49,25 +51,24 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len) /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ if (*scan != '@') - return -1; - - if (read_next_int(&scan, &range.old_start) < 0) - return -1; - if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0) - return -1; - - if (read_next_int(&scan, &range.new_start) < 0) - return -1; - if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0) - return -1; - - if (range.old_start < 0 || range.new_start < 0) - return -1; - - memcpy(&info->range, &range, sizeof(git_diff_range)); - - return info->hunk_cb( - info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size); + info->error = -1; + else if (read_next_int(&scan, &range.old_start) < 0) + info->error = -1; + else if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0) + info->error = -1; + else if (read_next_int(&scan, &range.new_start) < 0) + info->error = -1; + else if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0) + info->error = -1; + else if (range.old_start < 0 || range.new_start < 0) + info->error = -1; + else { + memcpy(&info->range, &range, sizeof(git_diff_range)); + + if (info->hunk_cb( + info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size)) + info->error = GIT_EUSER; + } } if ((len == 2 || len == 3) && info->line_cb) { @@ -80,23 +81,24 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len) GIT_DIFF_LINE_CONTEXT; if (info->line_cb( - info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0) - return -1; + info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size)) + info->error = GIT_EUSER; /* This should only happen if we are adding a line that does not * have a newline at the end and the old code did. In that case, * we have a ADD with a DEL_EOFNL as a pair. */ - if (len == 3) { + else if (len == 3) { origin = (origin == GIT_DIFF_LINE_ADDITION) ? GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL; - return info->line_cb( - info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size); + if (info->line_cb( + info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size)) + info->error = GIT_EUSER; } } - return 0; + return info->error; } #define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY) @@ -318,6 +320,7 @@ int git_diff_foreach( xdemitconf_t xdiff_config; xdemitcb_t xdiff_callback; + memset(&info, 0, sizeof(info)); info.diff = diff; info.cb_data = data; info.hunk_cb = hunk_cb; @@ -422,11 +425,11 @@ int git_diff_foreach( * diffs to tell if a file has really been changed. */ - if (file_cb != NULL) { - error = file_cb( - data, delta, (float)info.index / diff->deltas.length); - if (error < 0) - goto cleanup; + if (file_cb != NULL && + file_cb(data, delta, (float)info.index / diff->deltas.length)) + { + error = GIT_EUSER; + goto cleanup; } /* don't do hunk and line diffs if file is binary */ @@ -451,6 +454,7 @@ int git_diff_foreach( xdl_diff(&old_xdiff_data, &new_xdiff_data, &xdiff_params, &xdiff_config, &xdiff_callback); + error = info.error; cleanup: release_content(&delta->old_file, &old_data, old_blob); @@ -524,7 +528,11 @@ static int print_compact(void *data, git_diff_delta *delta, float progress) if (git_buf_oom(pi->buf)) return -1; - return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); + if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + return GIT_EUSER; + + return 0; } int git_diff_print_compact( @@ -586,7 +594,6 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress) const char *oldpath = delta->old_file.path; const char *newpfx = pi->diff->opts.new_prefix; const char *newpath = delta->new_file.path; - int result; GIT_UNUSED(progress); @@ -619,9 +626,8 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress) if (git_buf_oom(pi->buf)) return -1; - result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); - if (result < 0) - return result; + if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + return GIT_EUSER; if (delta->binary != 1) return 0; @@ -633,7 +639,11 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress) if (git_buf_oom(pi->buf)) return -1; - return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); + if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, + git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + return GIT_EUSER; + + return 0; } static int print_patch_hunk( @@ -649,7 +659,11 @@ static int print_patch_hunk( if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0) return -1; - return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); + if (pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + return GIT_EUSER; + + return 0; } static int print_patch_line( @@ -674,7 +688,11 @@ static int print_patch_line( if (git_buf_oom(pi->buf)) return -1; - return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf)); + if (pi->print_cb(pi->cb_data, delta, range, line_origin, + git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + return GIT_EUSER; + + return 0; } int git_diff_print_patch( @@ -763,11 +781,8 @@ int git_diff_blobs( if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0) return -1; - if (file_cb != NULL) { - int error = file_cb(cb_data, &delta, 1); - if (error < 0) - return error; - } + if (file_cb != NULL && file_cb(cb_data, &delta, 1)) + return GIT_EUSER; /* don't do hunk and line diffs if the two blobs are identical */ if (delta.status == GIT_DELTA_UNMODIFIED) @@ -777,6 +792,7 @@ int git_diff_blobs( if (delta.binary == 1) return 0; + memset(&info, 0, sizeof(info)); info.diff = NULL; info.delta = δ info.cb_data = cb_data; @@ -790,5 +806,5 @@ int git_diff_blobs( xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback); - return 0; + return info.error; } diff --git a/src/fetch.c b/src/fetch.c index 603284842..d96ac7781 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -5,7 +5,6 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "git2/remote.h" #include "git2/oid.h" #include "git2/refs.h" #include "git2/revwalk.h" @@ -18,6 +17,7 @@ #include "pack.h" #include "fetch.h" #include "netops.h" +#include "pkt.h" struct filter_payload { git_remote *remote; @@ -70,7 +70,62 @@ static int filter_wants(git_remote *remote) if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0) return -1; - return remote->transport->ls(remote->transport, &filter_ref__cb, &p); + return git_remote_ls(remote, filter_ref__cb, &p); +} + +/* Wait until we get an ack from the */ +static int recv_pkt(git_pkt **out, gitno_buffer *buf) +{ + const char *ptr = buf->data, *line_end = ptr; + git_pkt *pkt; + int pkt_type, error = 0, ret; + + do { + if (buf->offset > 0) + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); + else + error = GIT_EBUFS; + + if (error == 0) + break; /* return the pkt */ + + if (error < 0 && error != GIT_EBUFS) + return -1; + + if ((ret = gitno_recv(buf)) < 0) + return -1; + } while (error); + + gitno_consume(buf, line_end); + pkt_type = pkt->type; + if (out != NULL) + *out = pkt; + else + git__free(pkt); + + return pkt_type; +} + +static int store_common(git_transport *t) +{ + git_pkt *pkt = NULL; + gitno_buffer *buf = &t->buffer; + + do { + if (recv_pkt(&pkt, buf) < 0) + return -1; + + if (pkt->type == GIT_PKT_ACK) { + if (git_vector_insert(&t->common, pkt) < 0) + return -1; + } else { + git__free(pkt); + return 0; + } + + } while (1); + + return 0; } /* @@ -81,6 +136,12 @@ static int filter_wants(git_remote *remote) int git_fetch_negotiate(git_remote *remote) { git_transport *t = remote->transport; + gitno_buffer *buf = &t->buffer; + git_buf data = GIT_BUF_INIT; + git_revwalk *walk = NULL; + int error, pkt_type; + unsigned int i; + git_oid oid; if (filter_wants(remote) < 0) { giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); @@ -92,60 +153,174 @@ int git_fetch_negotiate(git_remote *remote) return 0; /* - * Now we have everything set up so we can start tell the server - * what we want and what we have. + * Now we have everything set up so we can start tell the + * server what we want and what we have. Call the function if + * the transport has its own logic. This is transitional and + * will be removed once this function can support git and http. */ - return t->negotiate_fetch(t, remote->repo, &remote->refs); + if (t->own_logic) + return t->negotiate_fetch(t, remote->repo, &remote->refs); + + /* No own logic, do our thing */ + if (git_pkt_buffer_wants(&remote->refs, &t->caps, &data) < 0) + return -1; + + if (git_fetch_setup_walk(&walk, remote->repo) < 0) + goto on_error; + /* + * We don't support any kind of ACK extensions, so the negotiation + * boils down to sending what we have and listening for an ACK + * every once in a while. + */ + i = 0; + while ((error = git_revwalk_next(&oid, walk)) == 0) { + git_pkt_buffer_have(&oid, &data); + i++; + if (i % 20 == 0) { + git_pkt_buffer_flush(&data); + if (git_buf_oom(&data)) + goto on_error; + + if (t->negotiation_step(t, data.ptr, data.size) < 0) + goto on_error; + + git_buf_clear(&data); + if (t->caps.multi_ack) { + if (store_common(t) < 0) + goto on_error; + } else { + pkt_type = recv_pkt(NULL, buf); + + if (pkt_type == GIT_PKT_ACK) { + break; + } else if (pkt_type == GIT_PKT_NAK) { + continue; + } else { + giterr_set(GITERR_NET, "Unexpected pkt type"); + goto on_error; + } + } + } + + if (t->common.length > 0) + break; + + if (i % 20 == 0 && t->rpc) { + git_pkt_ack *pkt; + unsigned int i; + + if (git_pkt_buffer_wants(&remote->refs, &t->caps, &data) < 0) + goto on_error; + + git_vector_foreach(&t->common, i, pkt) { + git_pkt_buffer_have(&pkt->oid, &data); + } + + if (git_buf_oom(&data)) + goto on_error; + } + } + + if (error < 0 && error != GIT_REVWALKOVER) + goto on_error; + + /* Tell the other end that we're done negotiating */ + if (t->rpc && t->common.length > 0) { + git_pkt_ack *pkt; + unsigned int i; + + if (git_pkt_buffer_wants(&remote->refs, &t->caps, &data) < 0) + goto on_error; + + git_vector_foreach(&t->common, i, pkt) { + git_pkt_buffer_have(&pkt->oid, &data); + } + + if (git_buf_oom(&data)) + goto on_error; + } + + git_pkt_buffer_done(&data); + if (t->negotiation_step(t, data.ptr, data.size) < 0) + goto on_error; + + git_buf_free(&data); + git_revwalk_free(walk); + + /* Now let's eat up whatever the server gives us */ + if (!t->caps.multi_ack) { + pkt_type = recv_pkt(NULL, buf); + if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { + giterr_set(GITERR_NET, "Unexpected pkt type"); + return -1; + } + } else { + git_pkt_ack *pkt; + do { + if (recv_pkt((git_pkt **)&pkt, buf) < 0) + return -1; + + if (pkt->type == GIT_PKT_NAK || + (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) { + git__free(pkt); + break; + } + + git__free(pkt); + } while (1); + } + + return 0; + +on_error: + git_revwalk_free(walk); + git_buf_free(&data); + return -1; } int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats) { + git_transport *t = remote->transport; + if(!remote->need_pack) return 0; - return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats); + if (t->own_logic) + return t->download_pack(t, remote->repo, bytes, stats); + + return git_fetch__download_pack(t, remote->repo, bytes, stats); + } /* Receiving data from a socket and storing it is pretty much the same for git and HTTP */ int git_fetch__download_pack( - const char *buffered, - size_t buffered_size, git_transport *t, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats) { int recvd; - char buff[1024]; - gitno_buffer buf; git_buf path = GIT_BUF_INIT; + gitno_buffer *buf = &t->buffer; git_indexer_stream *idx = NULL; - gitno_buffer_setup(t, &buf, buff, sizeof(buff)); - - if (memcmp(buffered, "PACK", strlen("PACK"))) { - giterr_set(GITERR_NET, "The pack doesn't start with the signature"); - return -1; - } - if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0) return -1; if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0) goto on_error; + git_buf_free(&path); memset(stats, 0, sizeof(git_indexer_stats)); - if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0) - goto on_error; - - *bytes = buffered_size; + *bytes = 0; do { - if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0) + if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0) goto on_error; - gitno_consume_n(&buf, buf.offset); - if ((recvd = gitno_recv(&buf)) < 0) + gitno_consume_n(buf, buf->offset); + + if ((recvd = gitno_recv(buf)) < 0) goto on_error; *bytes += recvd; diff --git a/src/fetch.h b/src/fetch.h index a7f126520..87bb43b07 100644 --- a/src/fetch.h +++ b/src/fetch.h @@ -12,8 +12,7 @@ int git_fetch_negotiate(git_remote *remote); int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats); -int git_fetch__download_pack(const char *buffered, size_t buffered_size, git_transport *t, - git_repository *repo, git_off_t *bytes, git_indexer_stats *stats); +int git_fetch__download_pack(git_transport *t, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats); int git_fetch_setup_walk(git_revwalk **out, git_repository *repo); #endif diff --git a/src/filebuf.c b/src/filebuf.c index 876f8e3e7..8b3ebb3e2 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -319,10 +319,15 @@ int git_filebuf_commit(git_filebuf *file, mode_t mode) if (verify_last_error(file) < 0) goto on_error; - p_close(file->fd); - file->fd = -1; file->fd_is_open = false; + if (p_close(file->fd) < 0) { + giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock); + goto on_error; + } + + file->fd = -1; + if (p_chmod(file->path_lock, mode)) { giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock); goto on_error; diff --git a/src/fileops.c b/src/fileops.c index bc58a0572..4de58b0cc 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -424,6 +424,7 @@ int git_futils_find_system_file(git_buf *path, const char *filename) } if (win32_find_file(path, &root, filename) < 0) { + giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename); git_buf_clear(path); return GIT_ENOTFOUND; } @@ -438,6 +439,7 @@ int git_futils_find_system_file(git_buf *path, const char *filename) return 0; git_buf_clear(path); + giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename); return GIT_ENOTFOUND; #endif } @@ -455,6 +457,7 @@ int git_futils_find_global_file(git_buf *path, const char *filename) } if (win32_find_file(path, &root, filename) < 0) { + giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename); git_buf_clear(path); return GIT_ENOTFOUND; } @@ -473,6 +476,7 @@ int git_futils_find_global_file(git_buf *path, const char *filename) return -1; if (git_path_exists(path->ptr) == false) { + giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename); git_buf_clear(path); return GIT_ENOTFOUND; } diff --git a/src/ignore.c b/src/ignore.c index f2d08f59e..93d979f1a 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -156,7 +156,7 @@ void git_ignore__free(git_ignores *ignores) static bool ignore_lookup_in_rules( git_vector *rules, git_attr_path *path, int *ignored) { - unsigned int j; + size_t j; git_attr_fnmatch *match; git_vector_rforeach(rules, j, match) { diff --git a/src/index.c b/src/index.c index 5f6206554..a1042b723 100644 --- a/src/index.c +++ b/src/index.c @@ -14,6 +14,7 @@ #include "tree-cache.h" #include "hash.h" #include "git2/odb.h" +#include "git2/oid.h" #include "git2/blob.h" #include "git2/config.h" @@ -329,16 +330,16 @@ int git_index_write(git_index *index) unsigned int git_index_entrycount(git_index *index) { assert(index); - return index->entries.length; + return (unsigned int)index->entries.length; } unsigned int git_index_entrycount_unmerged(git_index *index) { assert(index); - return index->unmerged.length; + return (unsigned int)index->unmerged.length; } -git_index_entry *git_index_get(git_index *index, unsigned int n) +git_index_entry *git_index_get(git_index *index, size_t n) { git_vector_sort(&index->entries); return git_vector_get(&index->entries, n); @@ -584,7 +585,7 @@ const git_index_entry_unmerged *git_index_get_unmerged_bypath( } const git_index_entry_unmerged *git_index_get_unmerged_byindex( - git_index *index, unsigned int n) + git_index *index, size_t n) { assert(index); return git_vector_get(&index->unmerged, n); @@ -963,7 +964,7 @@ static int write_index(git_index *index, git_filebuf *file) header.signature = htonl(INDEX_HEADER_SIG); header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER); - header.entry_count = htonl(index->entries.length); + header.entry_count = htonl((uint32_t)index->entries.length); if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0) return -1; diff --git a/src/message.c b/src/message.c index a4aadb28f..e6dedc9fb 100644 --- a/src/message.c +++ b/src/message.c @@ -62,21 +62,25 @@ int git_message__prettify(git_buf *message_out, const char *message, int strip_c int git_message_prettify(char *message_out, size_t buffer_size, const char *message, int strip_comments) { git_buf buf = GIT_BUF_INIT; + ssize_t out_size = -1; - if (strlen(message) + 1 > buffer_size) { /* We have to account for a potentially missing \n */ + if (message_out && buffer_size) + *message_out = '\0'; + + if (git_message__prettify(&buf, message, strip_comments) < 0) + goto done; + + if (message_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */ giterr_set(GITERR_INVALID, "Buffer too short to hold the cleaned message"); - return -1; + goto done; } - *message_out = '\0'; + if (message_out) + git_buf_copy_cstr(message_out, buffer_size, &buf); - if (git_message__prettify(&buf, message, strip_comments) < 0) { - git_buf_free(&buf); - return -1; - } + out_size = buf.size + 1; - git_buf_copy_cstr(message_out, buffer_size, &buf); +done: git_buf_free(&buf); - - return 0; + return out_size; } diff --git a/src/netops.c b/src/netops.c index b369e5106..49a0308bb 100644 --- a/src/netops.c +++ b/src/netops.c @@ -61,63 +61,72 @@ static int ssl_set_error(gitno_ssl *ssl, int error) } #endif -void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len) +int gitno_recv(gitno_buffer *buf) { - memset(buf, 0x0, sizeof(gitno_buffer)); - memset(data, 0x0, len); - buf->data = data; - buf->len = len; - buf->offset = 0; - buf->fd = t->socket; -#ifdef GIT_SSL - if (t->encrypt) - buf->ssl = &t->ssl; -#endif + return buf->recv(buf); } #ifdef GIT_SSL -static int ssl_recv(gitno_ssl *ssl, void *data, size_t len) +static int gitno__recv_ssl(gitno_buffer *buf) { int ret; do { - ret = SSL_read(ssl->ssl, data, len); - } while (SSL_get_error(ssl->ssl, ret) == SSL_ERROR_WANT_READ); + ret = SSL_read(buf->ssl->ssl, buf->data + buf->offset, buf->len - buf->offset); + } while (SSL_get_error(buf->ssl->ssl, ret) == SSL_ERROR_WANT_READ); - if (ret < 0) - return ssl_set_error(ssl, ret); + if (ret < 0) { + net_set_error("Error receiving socket data"); + return -1; + } + buf->offset += ret; return ret; } #endif -int gitno_recv(gitno_buffer *buf) +int gitno__recv(gitno_buffer *buf) { int ret; -#ifdef GIT_SSL - if (buf->ssl != NULL) { - if ((ret = ssl_recv(buf->ssl, buf->data + buf->offset, buf->len - buf->offset)) < 0) - return -1; - } else { - ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0); - if (ret < 0) { - net_set_error("Error receiving socket data"); - return -1; - } - } -#else ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0); if (ret < 0) { net_set_error("Error receiving socket data"); return -1; } -#endif buf->offset += ret; return ret; } +void gitno_buffer_setup_callback( + git_transport *t, + gitno_buffer *buf, + char *data, + size_t len, + int (*recv)(gitno_buffer *buf), void *cb_data) +{ + memset(buf, 0x0, sizeof(gitno_buffer)); + memset(data, 0x0, len); + buf->data = data; + buf->len = len; + buf->offset = 0; + buf->fd = t->socket; + buf->recv = recv; + buf->cb_data = cb_data; +} + +void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, size_t len) +{ +#ifdef GIT_SSL + if (t->use_ssl) { + gitno_buffer_setup_callback(t, buf, data, len, gitno__recv_ssl, NULL); + buf->ssl = &t->ssl; + } else +#endif + gitno_buffer_setup_callback(t, buf, data, len, gitno__recv, NULL); +} + /* Consume up to ptr and move the rest of the buffer to the beginning */ void gitno_consume(gitno_buffer *buf, const char *ptr) { @@ -147,7 +156,7 @@ int gitno_ssl_teardown(git_transport *t) int ret; #endif - if (!t->encrypt) + if (!t->use_ssl) return 0; #ifdef GIT_SSL @@ -415,7 +424,7 @@ int gitno_connect(git_transport *t, const char *host, const char *port) t->socket = s; p_freeaddrinfo(info); - if (t->encrypt && ssl_setup(t, host) < 0) + if (t->use_ssl && ssl_setup(t, host) < 0) return -1; return 0; @@ -445,7 +454,7 @@ int gitno_send(git_transport *t, const char *msg, size_t len, int flags) size_t off = 0; #ifdef GIT_SSL - if (t->encrypt) + if (t->use_ssl) return send_ssl(&t->ssl, msg, len); #endif diff --git a/src/netops.h b/src/netops.h index 4976f87f8..7c53fd0dc 100644 --- a/src/netops.h +++ b/src/netops.h @@ -8,10 +8,9 @@ #define INCLUDE_netops_h__ #include "posix.h" -#include "transport.h" #include "common.h" -typedef struct gitno_buffer { +struct gitno_buffer { char *data; size_t len; size_t offset; @@ -19,10 +18,14 @@ typedef struct gitno_buffer { #ifdef GIT_SSL struct gitno_ssl *ssl; #endif -} gitno_buffer; + int (*recv)(gitno_buffer *buffer); + void *cb_data; +}; -void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, unsigned int len); +void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, size_t len); +void gitno_buffer_setup_callback(git_transport *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); int gitno_recv(gitno_buffer *buf); +int gitno__recv(gitno_buffer *buf); void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume_n(gitno_buffer *buf, size_t cons); diff --git a/src/notes.c b/src/notes.c index 7813e9985..6f9e7779d 100644 --- a/src/notes.c +++ b/src/notes.c @@ -522,13 +522,14 @@ static int process_entry_path( int (*note_cb)(git_note_data *note_data, void *payload), void *payload) { - int i = 0, j = 0, error = -1, len; + int error = -1; + size_t i = 0, j = 0, len; git_buf buf = GIT_BUF_INIT; git_note_data note_data; - if (git_buf_puts(&buf, entry_path) < 0) + if ((error = git_buf_puts(&buf, entry_path)) < 0) goto cleanup; - + len = git_buf_len(&buf); while (i < len) { @@ -536,10 +537,9 @@ static int process_entry_path( i++; continue; } - + if (git__fromhex(buf.ptr[i]) < 0) { /* This is not a note entry */ - error = 0; goto cleanup; } @@ -555,16 +555,17 @@ static int process_entry_path( if (j != GIT_OID_HEXSZ) { /* This is not a note entry */ - error = 0; goto cleanup; } - if (git_oid_fromstr(¬e_data.annotated_object_oid, buf.ptr) < 0) - return -1; + if ((error = git_oid_fromstr( + ¬e_data.annotated_object_oid, buf.ptr)) < 0) + goto cleanup; git_oid_cpy(¬e_data.blob_oid, note_oid); - error = note_cb(¬e_data, payload); + if (note_cb(¬e_data, payload)) + error = GIT_EUSER; cleanup: git_buf_free(&buf); @@ -577,34 +578,27 @@ int git_note_foreach( int (*note_cb)(git_note_data *note_data, void *payload), void *payload) { - int error = -1; + int error; git_iterator *iter = NULL; git_tree *tree = NULL; git_commit *commit = NULL; const git_index_entry *item; - if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, ¬es_ref)) < 0) - goto cleanup; - - if (git_iterator_for_tree(&iter, repo, tree) < 0) - goto cleanup; - - if (git_iterator_current(iter, &item) < 0) - goto cleanup; + if (!(error = retrieve_note_tree_and_commit( + &tree, &commit, repo, ¬es_ref)) && + !(error = git_iterator_for_tree(&iter, repo, tree))) + error = git_iterator_current(iter, &item); - while (item) { - if (process_entry_path(item->path, &item->oid, note_cb, payload) < 0) - goto cleanup; + while (!error && item) { + error = process_entry_path(item->path, &item->oid, note_cb, payload); - if (git_iterator_advance(iter, &item) < 0) - goto cleanup; + if (!error) + error = git_iterator_advance(iter, &item); } - error = 0; - -cleanup: git_iterator_free(iter); git_tree_free(tree); git_commit_free(commit); + return error; } diff --git a/src/object.c b/src/object.c index 3ff894212..227774047 100644 --- a/src/object.c +++ b/src/object.c @@ -81,7 +81,7 @@ int git_object_lookup_prefix( git_object **object_out, git_repository *repo, const git_oid *id, - unsigned int len, + size_t len, git_otype type) { git_object *object = NULL; @@ -14,6 +14,7 @@ #include "delta-apply.h" #include "git2/odb_backend.h" +#include "git2/oid.h" #define GIT_ALTERNATES_FILE "info/alternates" @@ -553,7 +554,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) } int git_odb_read_prefix( - git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len) + git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) { unsigned int i; int error = GIT_ENOTFOUND; @@ -609,9 +610,12 @@ int git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data { unsigned int i; backend_internal *internal; + git_vector_foreach(&db->backends, i, internal) { git_odb_backend *b = internal->backend; - b->foreach(b, cb, data); + int error = b->foreach(b, cb, data); + if (error < 0) + return error; } return 0; diff --git a/src/odb_loose.c b/src/odb_loose.c index 2197a4264..41121ae10 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -42,7 +42,7 @@ typedef struct loose_backend { typedef struct { size_t dir_len; unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */ - unsigned int short_oid_len; + size_t short_oid_len; int found; /* number of matching * objects already found */ unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of @@ -502,7 +502,7 @@ static int locate_object_short_oid( git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, - unsigned int len) + size_t len) { char *objects_dir = backend->objects_dir; size_t dir_len = strlen(objects_dir); @@ -629,7 +629,7 @@ static int loose_backend__read_prefix( git_otype *type_p, git_odb_backend *backend, const git_oid *short_oid, - unsigned int len) + size_t len) { int error = 0; @@ -680,6 +680,7 @@ struct foreach_state { size_t dir_len; int (*cb)(git_oid *oid, void *data); void *data; + int cb_error; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) @@ -718,8 +719,10 @@ static int foreach_object_dir_cb(void *_state, git_buf *path) if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - if (state->cb(&oid, state->data) < 0) + if (state->cb(&oid, state->data)) { + state->cb_error = GIT_EUSER; return -1; + } return 0; } @@ -728,10 +731,7 @@ static int foreach_cb(void *_state, git_buf *path) { struct foreach_state *state = (struct foreach_state *) _state; - if (git_path_direach(path, foreach_object_dir_cb, state) < 0) - return -1; - - return 0; + return git_path_direach(path, foreach_object_dir_cb, state); } static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) @@ -749,14 +749,16 @@ static int loose_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid * git_buf_sets(&buf, objects_dir); git_path_to_dir(&buf); + memset(&state, 0, sizeof(state)); state.cb = cb; state.data = data; state.dir_len = git_buf_len(&buf); error = git_path_direach(&buf, foreach_cb, &state); + git_buf_free(&buf); - return error; + return state.cb_error ? state.cb_error : error; } static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) diff --git a/src/odb_pack.c b/src/odb_pack.c index 4b860e864..8fc6e68e8 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -149,7 +149,7 @@ static int pack_entry_find_prefix( struct git_pack_entry *e, struct pack_backend *backend, const git_oid *short_oid, - unsigned int len); + size_t len); @@ -295,7 +295,7 @@ static int pack_entry_find_prefix( struct git_pack_entry *e, struct pack_backend *backend, const git_oid *short_oid, - unsigned int len) + size_t len) { int error; unsigned int i; @@ -384,7 +384,7 @@ static int pack_backend__read_prefix( git_otype *type_p, git_odb_backend *backend, const git_oid *short_oid, - unsigned int len) + size_t len) { int error = 0; @@ -422,6 +422,7 @@ static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *oid, void *data), void *data) { + int error; struct git_pack_file *p; struct pack_backend *backend; unsigned int i; @@ -430,12 +431,14 @@ static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *o backend = (struct pack_backend *)_backend; /* Make sure we know about the packfiles */ - if (packfile_refresh_all(backend) < 0) - return -1; + if ((error = packfile_refresh_all(backend)) < 0) + return error; git_vector_foreach(&backend->packs, i, p) { - git_pack_foreach_entry(p, cb, &data); + if ((error = git_pack_foreach_entry(p, cb, &data)) < 0) + return error; } + return 0; } @@ -458,6 +461,41 @@ static void pack_backend__free(git_odb_backend *_backend) git__free(backend); } +int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) +{ + struct pack_backend *backend = NULL; + struct git_pack_file *packfile = NULL; + + if (git_packfile_check(&packfile, idx) < 0) + return -1; + + backend = git__calloc(1, sizeof(struct pack_backend)); + GITERR_CHECK_ALLOC(backend); + + if (git_vector_init(&backend->packs, 1, NULL) < 0) + goto on_error; + + if (git_vector_insert(&backend->packs, packfile) < 0) + goto on_error; + + backend->parent.read = &pack_backend__read; + backend->parent.read_prefix = &pack_backend__read_prefix; + backend->parent.read_header = NULL; + backend->parent.exists = &pack_backend__exists; + backend->parent.foreach = &pack_backend__foreach; + backend->parent.free = &pack_backend__free; + + *backend_out = (git_odb_backend *)backend; + + return 0; + +on_error: + git_vector_free(&backend->packs); + git__free(backend); + git__free(packfile); + return -1; +} + int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) { struct pack_backend *backend = NULL; @@ -161,12 +161,7 @@ void git_oid_cpy(git_oid *out, const git_oid *src) memcpy(out->id, src->id, sizeof(out->id)); } -int git_oid_cmp(const git_oid *a, const git_oid *b) -{ - return memcmp(a->id, b->id, sizeof(a->id)); -} - -int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len) +int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) { const unsigned char *a = oid_a->id; const unsigned char *b = oid_b->id; diff --git a/src/oidmap.h b/src/oidmap.h index 5a0bab6ec..1791adb16 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -28,13 +28,8 @@ GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid) return h; } -GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b) -{ - return (memcmp(a->id, b->id, sizeof(a->id)) == 0); -} - #define GIT__USE_OIDMAP \ - __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal) + __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, git_oid_equal) #define git_oidmap_alloc() kh_init(oid) #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL diff --git a/src/pack.c b/src/pack.c index 1d88eaa7d..e1fa085fd 100644 --- a/src/pack.c +++ b/src/pack.c @@ -38,7 +38,7 @@ static int pack_entry_find_offset( git_oid *found_oid, struct git_pack_file *p, const git_oid *short_oid, - unsigned int len); + size_t len); static int packfile_error(const char *message) { @@ -687,10 +687,9 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_ } int git_pack_foreach_entry( - struct git_pack_file *p, - int (*cb)(git_oid *oid, void *data), - void *data) - + struct git_pack_file *p, + int (*cb)(git_oid *oid, void *data), + void *data) { const unsigned char *index = p->index_map.data, *current; unsigned stride; @@ -722,7 +721,9 @@ int git_pack_foreach_entry( current = index; for (i = 0; i < p->num_objects; i++) { - cb((git_oid *)current, data); + if (cb((git_oid *)current, data)) + return GIT_EUSER; + current += stride; } @@ -734,7 +735,7 @@ static int pack_entry_find_offset( git_oid *found_oid, struct git_pack_file *p, const git_oid *short_oid, - unsigned int len) + size_t len) { const uint32_t *level1_ofs = p->index_map.data; const unsigned char *index = p->index_map.data; @@ -827,7 +828,7 @@ int git_pack_entry_find( struct git_pack_entry *e, struct git_pack_file *p, const git_oid *short_oid, - unsigned int len) + size_t len) { git_off_t offset; git_oid found_oid; diff --git a/src/pack.h b/src/pack.h index 7e1f978b0..178545675 100644 --- a/src/pack.h +++ b/src/pack.h @@ -101,7 +101,7 @@ int git_pack_entry_find( struct git_pack_entry *e, struct git_pack_file *p, const git_oid *short_oid, - unsigned int len); + size_t len); int git_pack_foreach_entry( struct git_pack_file *p, int (*cb)(git_oid *oid, void *data), diff --git a/src/path.h b/src/path.h index 5eecb7261..14618b2fc 100644 --- a/src/path.h +++ b/src/path.h @@ -239,6 +239,7 @@ extern int git_path_apply_relative(git_buf *target, const char *relpath); * the input state and the second arg is pathbuf. The function * may modify the pathbuf, but only by appending new text. * @param state to pass to fn as the first arg. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ extern int git_path_direach( git_buf *pathbuf, @@ -42,15 +42,29 @@ static int flush_pkt(git_pkt **out) /* the rest of the line will be useful for multi_ack */ static int ack_pkt(git_pkt **out, const char *line, size_t len) { - git_pkt *pkt; + git_pkt_ack *pkt; GIT_UNUSED(line); GIT_UNUSED(len); - pkt = git__malloc(sizeof(git_pkt)); + pkt = git__calloc(1, sizeof(git_pkt_ack)); GITERR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_ACK; - *out = pkt; + line += 3; + len -= 3; + + if (len >= GIT_OID_HEXSZ) { + git_oid_fromstr(&pkt->oid, line + 1); + line += GIT_OID_HEXSZ + 1; + len -= GIT_OID_HEXSZ + 1; + } + + if (len >= 7) { + if (!git__prefixcmp(line + 1, "continue")) + pkt->status = GIT_ACK_CONTINUE; + } + + *out = (git_pkt *) pkt; return 0; } @@ -283,20 +297,28 @@ int git_pkt_buffer_flush(git_buf *buf) static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf) { - char capstr[20]; + git_buf str = GIT_BUF_INIT; char oid[GIT_OID_HEXSZ +1] = {0}; unsigned int len; if (caps->ofs_delta) - strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr)); + git_buf_puts(&str, GIT_CAP_OFS_DELTA " "); + + if (caps->multi_ack) + git_buf_puts(&str, GIT_CAP_MULTI_ACK " "); + + if (git_buf_oom(&str)) + return -1; len = (unsigned int) (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + - strlen(capstr) + 1 /* LF */); + git_buf_len(&str) + 1 /* LF */); git_buf_grow(buf, git_buf_len(buf) + len); - git_oid_fmt(oid, &head->oid); - return git_buf_printf(buf, "%04xwant %s %s\n", len, oid, capstr); + git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str)); + git_buf_free(&str); + + return git_buf_oom(buf); } /* diff --git a/src/protocol.c b/src/protocol.c index 6b3861796..20d6e230f 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -9,38 +9,36 @@ #include "pkt.h" #include "buffer.h" -int git_protocol_store_refs(git_protocol *p, const char *data, size_t len) +int git_protocol_store_refs(git_transport *t, int flushes) { - git_buf *buf = &p->buf; - git_vector *refs = p->refs; - int error; - const char *line_end, *ptr; - - if (len == 0) { /* EOF */ - if (git_buf_len(buf) != 0) { - giterr_set(GITERR_NET, "Unexpected EOF"); - return p->error = -1; - } else { - return 0; - } - } + gitno_buffer *buf = &t->buffer; + git_vector *refs = &t->refs; + int error, flush = 0, recvd; + const char *line_end; + git_pkt *pkt; - git_buf_put(buf, data, len); - ptr = buf->ptr; - while (1) { - git_pkt *pkt; + do { + if (buf->offset > 0) + error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); + else + error = GIT_EBUFS; - if (git_buf_len(buf) == 0) - return 0; + if (error < 0 && error != GIT_EBUFS) + return -1; - error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf)); - if (error == GIT_EBUFS) - return 0; /* Ask for more */ - if (error < 0) - return p->error = -1; + if (error == GIT_EBUFS) { + if ((recvd = gitno_recv(buf)) < 0) + return -1; - git_buf_consume(buf, line_end); + if (recvd == 0 && !flush) { + giterr_set(GITERR_NET, "Early EOF"); + return -1; + } + + continue; + } + gitno_consume(buf, line_end); if (pkt->type == GIT_PKT_ERR) { giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error); git__free(pkt); @@ -48,10 +46,42 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len) } if (git_vector_insert(refs, pkt) < 0) - return p->error = -1; + return -1; if (pkt->type == GIT_PKT_FLUSH) - p->flush = 1; + flush++; + } while (flush < flushes); + + return flush; +} + +int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps) +{ + const char *ptr; + + /* No refs or capabilites, odd but not a problem */ + if (pkt == NULL || pkt->capabilities == NULL) + return 0; + + ptr = pkt->capabilities; + while (ptr != NULL && *ptr != '\0') { + if (*ptr == ' ') + ptr++; + + if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { + caps->common = caps->ofs_delta = 1; + ptr += strlen(GIT_CAP_OFS_DELTA); + continue; + } + + if(!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) { + caps->common = caps->multi_ack = 1; + ptr += strlen(GIT_CAP_MULTI_ACK); + continue; + } + + /* We don't know this capability, so skip it */ + ptr = strchr(ptr, ' '); } return 0; diff --git a/src/protocol.h b/src/protocol.h index a6c3e0735..615be8d63 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -9,15 +9,9 @@ #include "transport.h" #include "buffer.h" +#include "pkt.h" -typedef struct { - git_transport *transport; - git_vector *refs; - git_buf buf; - int error; - unsigned int flush :1; -} git_protocol; - -int git_protocol_store_refs(git_protocol *p, const char *data, size_t len); +int git_protocol_store_refs(git_transport *t, int flushes); +int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps); #endif diff --git a/src/reflog.c b/src/reflog.c index 53cc25fa1..80e40b960 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -197,7 +197,7 @@ static int create_new_reflog_file(const char *filepath) int git_reflog_read(git_reflog **reflog, git_reference *ref) { - int error; + int error = -1; git_buf log_path = GIT_BUF_INIT; git_buf log_file = GIT_BUF_INIT; git_reflog *log = NULL; @@ -406,10 +406,10 @@ int git_reflog_delete(git_reference *ref) unsigned int git_reflog_entrycount(git_reflog *reflog) { assert(reflog); - return reflog->entries.length; + return (unsigned int)reflog->entries.length; } -const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx) +const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx) { assert(reflog); return git_vector_get(&reflog->entries, idx); diff --git a/src/refs.c b/src/refs.c index 32f54fc31..cf55a6fd5 100644 --- a/src/refs.c +++ b/src/refs.c @@ -14,6 +14,7 @@ #include <git2/tag.h> #include <git2/object.h> +#include <git2/oid.h> GIT__USE_STRMAP; @@ -128,6 +129,7 @@ static int reference_read( result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated); git_buf_free(&path); + return result; } @@ -135,12 +137,13 @@ static int loose_parse_symbolic(git_reference *ref, git_buf *file_content) { const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF); const char *refname_start; - char *eol; refname_start = (const char *)file_content->ptr; - if (git_buf_len(file_content) < header_len + 1) - goto corrupt; + if (git_buf_len(file_content) < header_len + 1) { + giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); + return -1; + } /* * Assume we have already checked for the header @@ -151,45 +154,16 @@ static int loose_parse_symbolic(git_reference *ref, git_buf *file_content) ref->target.symbolic = git__strdup(refname_start); GITERR_CHECK_ALLOC(ref->target.symbolic); - /* remove newline at the end of file */ - eol = strchr(ref->target.symbolic, '\n'); - if (eol == NULL) - goto corrupt; - - *eol = '\0'; - if (eol[-1] == '\r') - eol[-1] = '\0'; - return 0; - -corrupt: - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); - return -1; } static int loose_parse_oid(git_oid *oid, git_buf *file_content) { - char *buffer; - - buffer = (char *)file_content->ptr; - - /* File format: 40 chars (OID) + newline */ - if (git_buf_len(file_content) < GIT_OID_HEXSZ + 1) - goto corrupt; - - if (git_oid_fromstr(oid, buffer) < 0) - goto corrupt; - - buffer = buffer + GIT_OID_HEXSZ; - if (*buffer == '\r') - buffer++; - - if (*buffer != '\n') - goto corrupt; - - return 0; + /* File format: 40 chars (OID) */ + if (git_buf_len(file_content) == GIT_OID_HEXSZ && + git_oid_fromstr(oid, git_buf_cstr(file_content)) == 0) + return 0; -corrupt: giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); return -1; } @@ -226,6 +200,8 @@ static int loose_lookup(git_reference *ref) if (!updated) return 0; + git_buf_rtrim(&ref_file); + if (ref->flags & GIT_REF_SYMBOLIC) { git__free(ref->target.symbolic); ref->target.symbolic = NULL; @@ -259,6 +235,8 @@ static int loose_lookup_to_packfile( if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0) return -1; + git_buf_rtrim(&ref_file); + name_len = strlen(name); ref = git__malloc(sizeof(struct packref) + name_len + 1); GITERR_CHECK_ALLOC(ref); @@ -500,6 +478,7 @@ struct dirent_list_data { int (*callback)(const char *, void *); void *callback_payload; + int callback_error; }; static int _dirent_loose_listall(void *_data, git_buf *full_path) @@ -520,7 +499,10 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path) return 0; /* we are filtering out this reference */ } - return data->callback(file_path, data->callback_payload); + if (data->callback(file_path, data->callback_payload)) + data->callback_error = GIT_EUSER; + + return data->callback_error; } static int _dirent_loose_load(void *data, git_buf *full_path) @@ -843,15 +825,17 @@ static int reference_path_available( const char *ref, const char* old_ref) { + int error; struct reference_available_t data; data.new_ref = ref; data.old_ref = old_ref; data.available = 1; - if (git_reference_foreach(repo, GIT_REF_LISTALL, - _reference_available_cb, (void *)&data) < 0) - return -1; + error = git_reference_foreach( + repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data); + if (error < 0) + return error; if (!data.available) { giterr_set(GITERR_REFERENCE, @@ -1486,8 +1470,8 @@ int git_reference_foreach( return -1; git_strmap_foreach(repo->references.packfile, ref_name, ref, { - if (callback(ref_name, payload) < 0) - return 0; + if (callback(ref_name, payload)) + return GIT_EUSER; }); } @@ -1499,14 +1483,16 @@ int git_reference_foreach( data.repo = repo; data.callback = callback; data.callback_payload = payload; + data.callback_error = 0; if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0) return -1; result = git_path_direach(&refs_path, _dirent_loose_listall, &data); + git_buf_free(&refs_path); - return result; + return data.callback_error ? GIT_EUSER : result; } static int cb__reflist_add(const char *ref, void *data) diff --git a/src/remote.c b/src/remote.c index c2bfb09bb..fe026b175 100644 --- a/src/remote.c +++ b/src/remote.c @@ -5,15 +5,16 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "git2/remote.h" #include "git2/config.h" #include "git2/types.h" +#include "git2/oid.h" #include "config.h" #include "repository.h" #include "remote.h" #include "fetch.h" #include "refs.h" +#include "pkt.h" #include <regex.h> @@ -401,6 +402,10 @@ on_error: int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) { + git_vector *refs = &remote->transport->refs; + unsigned int i; + git_pkt *p = NULL; + assert(remote); if (!remote->transport || !remote->transport->connected) { @@ -408,7 +413,19 @@ int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) return -1; } - return remote->transport->ls(remote->transport, list_cb, payload); + git_vector_foreach(refs, i, p) { + git_pkt_ref *pkt = NULL; + + if (p->type != GIT_PKT_REF) + continue; + + pkt = (git_pkt_ref *)p; + + if (list_cb(&pkt->head, payload)) + return GIT_EUSER; + } + + return 0; } int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats) @@ -580,6 +597,11 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) } git_vector_free(&list); + + /* cb error is converted to GIT_EUSER by git_config_foreach */ + if (error == GIT_EUSER) + error = -1; + return error; } diff --git a/src/remote.h b/src/remote.h index 5083b9912..67933a327 100644 --- a/src/remote.h +++ b/src/remote.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_remote_h__ #define INCLUDE_remote_h__ +#include "git2/remote.h" + #include "refspec.h" #include "transport.h" #include "repository.h" diff --git a/src/repository.c b/src/repository.c index a2931713e..6f1f4349b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -388,6 +388,19 @@ int git_repository_open(git_repository **repo_out, const char *path) repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL); } +int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) +{ + git_repository *repo; + + repo = repository_alloc(); + GITERR_CHECK_ALLOC(repo); + + git_repository_set_odb(repo, odb); + *repo_out = repo; + + return 0; +} + int git_repository_discover( char *repository_path, size_t size, @@ -1058,3 +1071,58 @@ int git_repository_head_tree(git_tree **tree, git_repository *repo) *tree = (git_tree *)obj; return 0; } + +#define MERGE_MSG_FILE "MERGE_MSG" + +int git_repository_message(char *buffer, size_t len, git_repository *repo) +{ + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + struct stat st; + ssize_t size; + int error; + + if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0) + return -1; + + error = p_stat(git_buf_cstr(&path), &st); + if (error < 0) { + if (errno == ENOENT) + error = GIT_ENOTFOUND; + + git_buf_free(&path); + return error; + } + + if (buffer == NULL) { + git_buf_free(&path); + return (int)st.st_size; + } + + if (git_futils_readbuffer(&buf, git_buf_cstr(&path)) < 0) + goto on_error; + + memcpy(buffer, git_buf_cstr(&buf), len); + size = git_buf_len(&buf); + + git_buf_free(&path); + git_buf_free(&buf); + return size; + +on_error: + git_buf_free(&path); + return -1; +} + +int git_repository_message_remove(git_repository *repo) +{ + git_buf path = GIT_BUF_INIT; + int error; + + if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0) + return -1; + + error = p_unlink(git_buf_cstr(&path)); + git_buf_free(&path); + + return error; +} diff --git a/src/revparse.c b/src/revparse.c index e2c0de612..3855c29f1 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -338,7 +338,7 @@ cleanup: return error; } -static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, int identifier_len, git_repository* repo, const char *curly_braces_content) +static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository* repo, const char *curly_braces_content) { bool is_numeric; int parsed = 0, error = -1; @@ -547,7 +547,7 @@ static int handle_caret_curly_syntax(git_object **out, git_object *obj, const ch return git_object_peel(out, obj, expected_type); } -static int extract_curly_braces_content(git_buf *buf, const char *spec, int *pos) +static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos) { git_buf_clear(buf); @@ -572,7 +572,7 @@ static int extract_curly_braces_content(git_buf *buf, const char *spec, int *pos return 0; } -static int extract_path(git_buf *buf, const char *spec, int *pos) +static int extract_path(git_buf *buf, const char *spec, size_t *pos) { git_buf_clear(buf); @@ -588,7 +588,7 @@ static int extract_path(git_buf *buf, const char *spec, int *pos) return 0; } -static int extract_how_many(int *n, const char *spec, int *pos) +static int extract_how_many(int *n, const char *spec, size_t *pos) { const char *end_ptr; int parsed, accumulated; @@ -633,7 +633,7 @@ static int object_from_reference(git_object **object, git_reference *reference) return error; } -static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, int identifier_len, git_repository *repo, bool allow_empty_identifier) +static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) { int error; git_buf identifier = GIT_BUF_INIT; @@ -670,7 +670,7 @@ static int ensure_base_rev_is_not_known_yet(git_object *object, const char *spec return revspec_error(spec); } -static bool any_left_hand_identifier(git_object *object, git_reference *reference, int identifier_len) +static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len) { if (object != NULL) return true; @@ -694,7 +694,7 @@ static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_ int git_revparse_single(git_object **out, git_repository *repo, const char *spec) { - int pos = 0, identifier_len = 0; + size_t pos = 0, identifier_len = 0; int error = -1, n; git_buf buf = GIT_BUF_INIT; diff --git a/src/sha1.h b/src/sha1.h index 93a244d76..f0a16f2cf 100644 --- a/src/sha1.h +++ b/src/sha1.h @@ -5,6 +5,9 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#ifndef INCLUDE_sha1_h__ +#define INCLUDE_sha1_h__ + typedef struct { unsigned long long size; unsigned int H[5]; @@ -19,3 +22,5 @@ void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); #define SHA1_Init git__blk_SHA1_Init #define SHA1_Update git__blk_SHA1_Update #define SHA1_Final git__blk_SHA1_Final + +#endif diff --git a/src/status.c b/src/status.c index d78237689..8e462552e 100644 --- a/src/status.c +++ b/src/status.c @@ -81,7 +81,7 @@ int git_status_foreach_ext( git_status_show_t show = opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; git_diff_delta *i2h, *w2i; - unsigned int i, j, i_max, j_max; + size_t i, j, i_max, j_max; assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); @@ -114,7 +114,8 @@ int git_status_foreach_ext( if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { for (i = 0; !err && i < idx2head->deltas.length; i++) { i2h = GIT_VECTOR_GET(&idx2head->deltas, i); - err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata); + if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata)) + err = GIT_EUSER; } git_diff_list_free(idx2head); idx2head = NULL; @@ -130,14 +131,17 @@ int git_status_foreach_ext( cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path); if (cmp < 0) { - err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata); + if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata)) + err = GIT_EUSER; i++; } else if (cmp > 0) { - err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata); + if (cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata)) + err = GIT_EUSER; j++; } else { - err = cb(i2h->old_file.path, index_delta2status(i2h->status) | - workdir_delta2status(w2i->status), cbdata); + if (cb(i2h->old_file.path, index_delta2status(i2h->status) | + workdir_delta2status(w2i->status), cbdata)) + err = GIT_EUSER; i++; j++; } } @@ -146,6 +150,7 @@ cleanup: git_tree_free(head); git_diff_list_free(idx2head); git_diff_list_free(wd2idx); + return err; } @@ -166,9 +171,10 @@ int git_status_foreach( } struct status_file_info { + char *expected; unsigned int count; unsigned int status; - char *expected; + int ambiguous; }; static int get_one_status(const char *path, unsigned int status, void *data) @@ -183,6 +189,7 @@ static int get_one_status(const char *path, unsigned int status, void *data) p_fnmatch(sfi->expected, path, 0) != 0)) { giterr_set(GITERR_INVALID, "Ambiguous path '%s' given to git_status_file", sfi->expected); + sfi->ambiguous = true; return GIT_EAMBIGUOUS; } @@ -215,6 +222,9 @@ int git_status_file( error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi); + if (error < 0 && sfi.ambiguous) + error = GIT_EAMBIGUOUS; + if (!error && !sfi.count) { giterr_set(GITERR_INVALID, "Attempt to get status of nonexistent file '%s'", path); diff --git a/src/transport.h b/src/transport.h index 68b92f7a6..c4306165c 100644 --- a/src/transport.h +++ b/src/transport.h @@ -12,6 +12,7 @@ #include "vector.h" #include "posix.h" #include "common.h" +#include "netops.h" #ifdef GIT_SSL # include <openssl/ssl.h> # include <openssl/err.h> @@ -19,10 +20,12 @@ #define GIT_CAP_OFS_DELTA "ofs-delta" +#define GIT_CAP_MULTI_ACK "multi_ack" typedef struct git_transport_caps { int common:1, - ofs_delta:1; + ofs_delta:1, + multi_ack: 1; } git_transport_caps; #ifdef GIT_SSL @@ -70,19 +73,25 @@ struct git_transport { int direction : 1, /* 0 fetch, 1 push */ connected : 1, check_cert: 1, - encrypt : 1; + use_ssl : 1, + own_logic: 1, /* transitional */ + rpc: 1; /* git-speak for the HTTP transport */ #ifdef GIT_SSL struct gitno_ssl ssl; #endif + git_vector refs; + git_vector common; + gitno_buffer buffer; GIT_SOCKET socket; + git_transport_caps caps; /** * Connect and store the remote heads */ int (*connect)(struct git_transport *transport, int dir); /** - * Give a list of references, useful for ls-remote + * Send our side of a negotiation */ - int (*ls)(struct git_transport *transport, git_headlist_cb list_cb, void *opaque); + int (*negotiation_step)(struct git_transport *transport, void *data, size_t len); /** * Push the changes over */ @@ -97,10 +106,6 @@ struct git_transport { */ int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats); /** - * Fetch the changes - */ - int (*fetch)(struct git_transport *transport); - /** * Close the connection */ int (*close)(struct git_transport *transport); @@ -124,7 +129,6 @@ int git_transport_dummy(struct git_transport **transport); */ int git_transport_valid_url(const char *url); -typedef struct git_transport git_transport; typedef int (*git_transport_cb)(git_transport **transport); #endif diff --git a/src/transports/git.c b/src/transports/git.c index 45f571f20..7a65718f7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -24,12 +24,7 @@ typedef struct { git_transport parent; - git_protocol proto; - git_vector refs; - git_remote_head **heads; - git_transport_caps caps; char buff[1024]; - gitno_buffer buf; #ifdef GIT_WIN32 WSADATA wsd; #endif @@ -127,68 +122,6 @@ on_error: } /* - * Read from the socket and store the references in the vector - */ -static int store_refs(transport_git *t) -{ - gitno_buffer *buf = &t->buf; - int ret = 0; - - while (1) { - if ((ret = gitno_recv(buf)) < 0) - return -1; - if (ret == 0) /* Orderly shutdown, so exit */ - return 0; - - ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset); - if (ret == GIT_EBUFS) { - gitno_consume_n(buf, buf->len); - continue; - } - - if (ret < 0) - return ret; - - gitno_consume_n(buf, buf->offset); - - if (t->proto.flush) { /* No more refs */ - t->proto.flush = 0; - return 0; - } - } -} - -static int detect_caps(transport_git *t) -{ - git_vector *refs = &t->refs; - git_pkt_ref *pkt; - git_transport_caps *caps = &t->caps; - const char *ptr; - - pkt = git_vector_get(refs, 0); - /* No refs or capabilites, odd but not a problem */ - if (pkt == NULL || pkt->capabilities == NULL) - return 0; - - ptr = pkt->capabilities; - while (ptr != NULL && *ptr != '\0') { - if (*ptr == ' ') - ptr++; - - if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { - caps->common = caps->ofs_delta = 1; - ptr += strlen(GIT_CAP_OFS_DELTA); - continue; - } - - /* We don't know this capability, so skip it */ - ptr = strchr(ptr, ' '); - } - - return 0; -} - -/* * Since this is a network connection, we need to parse and store the * pkt-lines at this stage and keep them there. */ @@ -202,202 +135,26 @@ static int git_connect(git_transport *transport, int direction) } t->parent.direction = direction; - if (git_vector_init(&t->refs, 16, NULL) < 0) - return -1; /* Connect and ask for the refs */ if (do_connect(t, transport->url) < 0) - goto cleanup; + return -1; - gitno_buffer_setup(transport, &t->buf, t->buff, sizeof(t->buff)); + gitno_buffer_setup(transport, &transport->buffer, t->buff, sizeof(t->buff)); t->parent.connected = 1; - if (store_refs(t) < 0) - goto cleanup; - - if (detect_caps(t) < 0) - goto cleanup; - - return 0; -cleanup: - git_vector_free(&t->refs); - return -1; -} - -static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque) -{ - transport_git *t = (transport_git *) transport; - git_vector *refs = &t->refs; - unsigned int i; - git_pkt *p = NULL; - - git_vector_foreach(refs, i, p) { - git_pkt_ref *pkt = NULL; - - if (p->type != GIT_PKT_REF) - continue; - - pkt = (git_pkt_ref *)p; - - if (list_cb(&pkt->head, opaque) < 0) { - giterr_set(GITERR_NET, "User callback returned error"); - return -1; - } - } - - return 0; -} - -/* Wait until we get an ack from the */ -static int recv_pkt(gitno_buffer *buf) -{ - const char *ptr = buf->data, *line_end; - git_pkt *pkt; - int pkt_type, error; - - do { - /* Wait for max. 1 second */ - if ((error = gitno_select_in(buf, 1, 0)) < 0) { - return -1; - } else if (error == 0) { - /* - * Some servers don't respond immediately, so if this - * happens, we keep sending information until it - * answers. Pretend we received a NAK to convince higher - * layers to do so. - */ - return GIT_PKT_NAK; - } - - if ((error = gitno_recv(buf)) < 0) - return -1; - - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); - if (error == GIT_EBUFS) - continue; - if (error < 0) - return -1; - } while (error); - - gitno_consume(buf, line_end); - pkt_type = pkt->type; - git__free(pkt); - - return pkt_type; -} - -static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) -{ - transport_git *t = (transport_git *) transport; - git_revwalk *walk; - git_oid oid; - int error; - unsigned int i; - git_buf data = GIT_BUF_INIT; - gitno_buffer *buf = &t->buf; - - if (git_pkt_buffer_wants(wants, &t->caps, &data) < 0) + if (git_protocol_store_refs(transport, 1) < 0) return -1; - if (git_fetch_setup_walk(&walk, repo) < 0) - goto on_error; - - if (gitno_send(transport, data.ptr, data.size, 0) < 0) - goto on_error; - - git_buf_clear(&data); - /* - * We don't support any kind of ACK extensions, so the negotiation - * boils down to sending what we have and listening for an ACK - * every once in a while. - */ - i = 0; - while ((error = git_revwalk_next(&oid, walk)) == 0) { - git_pkt_buffer_have(&oid, &data); - i++; - if (i % 20 == 0) { - int pkt_type; - - git_pkt_buffer_flush(&data); - if (git_buf_oom(&data)) - goto on_error; - - if (gitno_send(transport, data.ptr, data.size, 0) < 0) - goto on_error; - - pkt_type = recv_pkt(buf); - - if (pkt_type == GIT_PKT_ACK) { - break; - } else if (pkt_type == GIT_PKT_NAK) { - continue; - } else { - giterr_set(GITERR_NET, "Unexpected pkt type"); - goto on_error; - } - - } - } - if (error < 0 && error != GIT_REVWALKOVER) - goto on_error; - - /* Tell the other end that we're done negotiating */ - git_buf_clear(&data); - git_pkt_buffer_flush(&data); - git_pkt_buffer_done(&data); - if (gitno_send(transport, data.ptr, data.size, 0) < 0) - goto on_error; + if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0) + return -1; - git_buf_free(&data); - git_revwalk_free(walk); return 0; - -on_error: - git_buf_free(&data); - git_revwalk_free(walk); - return -1; } -static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats) +static int git_negotiation_step(struct git_transport *transport, void *data, size_t len) { - transport_git *t = (transport_git *) transport; - int error = 0, read_bytes; - gitno_buffer *buf = &t->buf; - git_pkt *pkt; - const char *line_end, *ptr; - - /* - * For now, we ignore everything and wait for the pack - */ - do { - ptr = buf->data; - /* Whilst we're searching for the pack */ - while (1) { - if (buf->offset == 0) { - break; - } - - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); - if (error == GIT_EBUFS) - break; - - if (error < 0) - return error; - - if (pkt->type == GIT_PKT_PACK) { - git__free(pkt); - return git_fetch__download_pack(buf->data, buf->offset, transport, repo, bytes, stats); - } - - /* For now we don't care about anything */ - git__free(pkt); - gitno_consume(buf, line_end); - } - - read_bytes = gitno_recv(buf); - } while (read_bytes); - - return read_bytes; + return gitno_send(transport, data, len, 0); } static int git_close(git_transport *t) @@ -408,6 +165,7 @@ static int git_close(git_transport *t) return -1; /* Can't do anything if there's an error, so don't bother checking */ gitno_send(t, buf.ptr, buf.size, 0); + git_buf_free(&buf); if (gitno_close(t->socket) < 0) { giterr_set(GITERR_NET, "Failed to close socket"); @@ -426,17 +184,22 @@ static int git_close(git_transport *t) static void git_free(git_transport *transport) { transport_git *t = (transport_git *) transport; - git_vector *refs = &t->refs; + git_vector *refs = &transport->refs; unsigned int i; for (i = 0; i < refs->length; ++i) { git_pkt *p = git_vector_get(refs, i); git_pkt_free(p); } + git_vector_free(refs); + refs = &transport->common; + for (i = 0; i < refs->length; ++i) { + git_pkt *p = git_vector_get(refs, i); + git_pkt_free(p); + } git_vector_free(refs); - git__free(t->heads); - git_buf_free(&t->proto.buf); + git__free(t->parent.url); git__free(t); } @@ -452,15 +215,16 @@ int git_transport_git(git_transport **out) GITERR_CHECK_ALLOC(t); memset(t, 0x0, sizeof(transport_git)); + if (git_vector_init(&t->parent.common, 8, NULL)) + goto on_error; + + if (git_vector_init(&t->parent.refs, 16, NULL) < 0) + goto on_error; t->parent.connect = git_connect; - t->parent.ls = git_ls; - t->parent.negotiate_fetch = git_negotiate_fetch; - t->parent.download_pack = git_download_pack; + t->parent.negotiation_step = git_negotiation_step; t->parent.close = git_close; t->parent.free = git_free; - t->proto.refs = &t->refs; - t->proto.transport = (git_transport *) t; *out = (git_transport *) t; @@ -474,4 +238,8 @@ int git_transport_git(git_transport **out) #endif return 0; + +on_error: + git__free(t); + return -1; } diff --git a/src/transports/http.c b/src/transports/http.c index f25d639f3..85fec413a 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -29,11 +29,8 @@ enum last_cb { typedef struct { git_transport parent; - git_protocol proto; - git_vector refs; - git_vector common; + http_parser_settings settings; git_buf buf; - git_remote_head **heads; int error; int transfer_finished :1, ct_found :1, @@ -46,7 +43,7 @@ typedef struct { char *host; char *port; char *service; - git_transport_caps caps; + char buffer[4096]; #ifdef GIT_WIN32 WSADATA wsd; #endif @@ -183,17 +180,6 @@ static int on_headers_complete(http_parser *parser) return 0; } -static int on_body_store_refs(http_parser *parser, const char *str, size_t len) -{ - transport_http *t = (transport_http *) parser->data; - - if (parser->status_code == 404) { - return git_buf_put(&t->buf, str, len); - } - - return git_protocol_store_refs(&t->proto, str, len); -} - static int on_message_complete(http_parser *parser) { transport_http *t = (transport_http *) parser->data; @@ -208,51 +194,64 @@ static int on_message_complete(http_parser *parser) return 0; } -static int store_refs(transport_http *t) +static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len) { - http_parser_settings settings; - char buffer[1024]; - gitno_buffer buf; - git_pkt *pkt; - int ret; + git_transport *transport = (git_transport *) parser->data; + transport_http *t = (transport_http *) parser->data; + gitno_buffer *buf = &transport->buffer; - http_parser_init(&t->parser, HTTP_RESPONSE); - t->parser.data = t; - memset(&settings, 0x0, sizeof(http_parser_settings)); - settings.on_header_field = on_header_field; - settings.on_header_value = on_header_value; - settings.on_headers_complete = on_headers_complete; - settings.on_body = on_body_store_refs; - settings.on_message_complete = on_message_complete; + if (buf->len - buf->offset < len) { + giterr_set(GITERR_NET, "Can't fit data in the buffer"); + return t->error = -1; + } - gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer)); + memcpy(buf->data + buf->offset, str, len); + buf->offset += len; - while(1) { - size_t parsed; + return 0; +} - if ((ret = gitno_recv(&buf)) < 0) - return -1; +static int http_recv_cb(gitno_buffer *buf) +{ + git_transport *transport = (git_transport *) buf->cb_data; + transport_http *t = (transport_http *) transport; + size_t old_len; + gitno_buffer inner; + char buffer[2048]; + int error; - parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); - /* Both should happen at the same time */ - if (parsed != buf.offset || t->error < 0) - return t->error; + if (t->transfer_finished) + return 0; - gitno_consume_n(&buf, parsed); + gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer)); - if (ret == 0 || t->transfer_finished) - return 0; - } + if ((error = gitno_recv(&inner)) < 0) + return -1; - pkt = git_vector_get(&t->refs, 0); - if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) { - giterr_set(GITERR_NET, "Invalid HTTP response"); - return t->error = -1; - } else { - git_vector_remove(&t->refs, 0); - } + old_len = buf->offset; + http_parser_execute(&t->parser, &t->settings, inner.data, inner.offset); + if (t->error < 0) + return t->error; - return 0; + return buf->offset - old_len; +} + +/* Set up the gitno_buffer so calling gitno_recv() grabs data from the HTTP response */ +static void setup_gitno_buffer(git_transport *transport) +{ + transport_http *t = (transport_http *) transport; + + http_parser_init(&t->parser, HTTP_RESPONSE); + t->parser.data = t; + t->transfer_finished = 0; + memset(&t->settings, 0x0, sizeof(http_parser_settings)); + t->settings.on_header_field = on_header_field; + t->settings.on_header_value = on_header_value; + t->settings.on_headers_complete = on_headers_complete; + t->settings.on_body = on_body_fill_buffer; + t->settings.on_message_complete = on_message_complete; + + gitno_buffer_setup_callback(transport, &transport->buffer, t->buffer, sizeof(t->buffer), http_recv_cb, t); } static int http_connect(git_transport *transport, int direction) @@ -263,6 +262,7 @@ static int http_connect(git_transport *transport, int direction) const char *service = "upload-pack"; const char *url = t->parent.url, *prefix_http = "http://", *prefix_https = "https://"; const char *default_port; + git_pkt *pkt; if (direction == GIT_DIR_PUSH) { giterr_set(GITERR_NET, "Pushing over HTTP is not implemented"); @@ -270,8 +270,6 @@ static int http_connect(git_transport *transport, int direction) } t->parent.direction = direction; - if (git_vector_init(&t->refs, 16, NULL) < 0) - return -1; if (!git__prefixcmp(url, prefix_http)) { url = t->parent.url + strlen(prefix_http); @@ -304,307 +302,61 @@ static int http_connect(git_transport *transport, int direction) if (gitno_send(transport, request.ptr, request.size, 0) < 0) goto cleanup; - ret = store_refs(t); - -cleanup: - git_buf_free(&request); - git_buf_clear(&t->buf); - - return ret; -} - -static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque) -{ - transport_http *t = (transport_http *) transport; - git_vector *refs = &t->refs; - unsigned int i; - git_pkt_ref *p; - - git_vector_foreach(refs, i, p) { - if (p->type != GIT_PKT_REF) - continue; - - if (list_cb(&p->head, opaque) < 0) { - giterr_set(GITERR_NET, "The user callback returned error"); - return -1; - } - } - - return 0; -} - -static int on_body_parse_response(http_parser *parser, const char *str, size_t len) -{ - transport_http *t = (transport_http *) parser->data; - git_buf *buf = &t->buf; - git_vector *common = &t->common; - int error; - const char *line_end, *ptr; - - if (len == 0) { /* EOF */ - if (git_buf_len(buf) != 0) { - giterr_set(GITERR_NET, "Unexpected EOF"); - return t->error = -1; - } else { - return 0; - } - } - - git_buf_put(buf, str, len); - ptr = buf->ptr; - while (1) { - git_pkt *pkt; - - if (git_buf_len(buf) == 0) - return 0; - - error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf)); - if (error == GIT_EBUFS) { - return 0; /* Ask for more */ - } - if (error < 0) - return t->error = -1; - - git_buf_consume(buf, line_end); - - if (pkt->type == GIT_PKT_PACK) { - git__free(pkt); - t->pack_ready = 1; - return 0; - } - - if (pkt->type == GIT_PKT_NAK) { - git__free(pkt); - return 0; - } - - if (pkt->type != GIT_PKT_ACK) { - git__free(pkt); - continue; - } - - if (git_vector_insert(common, pkt) < 0) - return -1; - } - - return error; - -} - -static int parse_response(transport_http *t) -{ - int ret = 0; - http_parser_settings settings; - char buffer[1024]; - gitno_buffer buf; - - http_parser_init(&t->parser, HTTP_RESPONSE); - t->parser.data = t; - t->transfer_finished = 0; - memset(&settings, 0x0, sizeof(http_parser_settings)); - settings.on_header_field = on_header_field; - settings.on_header_value = on_header_value; - settings.on_headers_complete = on_headers_complete; - settings.on_body = on_body_parse_response; - settings.on_message_complete = on_message_complete; - - gitno_buffer_setup((git_transport *)t, &buf, buffer, sizeof(buffer)); - - while(1) { - size_t parsed; - - if ((ret = gitno_recv(&buf)) < 0) - return -1; - - parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); - /* Both should happen at the same time */ - if (parsed != buf.offset || t->error < 0) - return t->error; - - gitno_consume_n(&buf, parsed); + setup_gitno_buffer(transport); + if ((ret = git_protocol_store_refs(transport, 2)) < 0) + goto cleanup; - if (ret == 0 || t->transfer_finished || t->pack_ready) { - return 0; - } + pkt = git_vector_get(&transport->refs, 0); + if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) { + giterr_set(GITERR_NET, "Invalid HTTP response"); + return t->error = -1; + } else { + /* Remove the comment and flush pkts */ + git_vector_remove(&transport->refs, 0); + git__free(pkt); + pkt = git_vector_get(&transport->refs, 0); + git_vector_remove(&transport->refs, 0); + git__free(pkt); } - return ret; -} - -static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) -{ - transport_http *t = (transport_http *) transport; - int ret; - unsigned int i; - char buff[128]; - gitno_buffer buf; - git_revwalk *walk = NULL; - git_oid oid; - git_pkt_ack *pkt; - git_vector *common = &t->common; - git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT; - - gitno_buffer_setup(transport, &buf, buff, sizeof(buff)); - - if (git_vector_init(common, 16, NULL) < 0) - return -1; - - if (git_fetch_setup_walk(&walk, repo) < 0) - return -1; - - do { - if ((ret = do_connect(t, t->host, t->port)) < 0) - goto cleanup; - - if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0) - goto cleanup; - - /* We need to send these on each connection */ - git_vector_foreach (common, i, pkt) { - if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0) - goto cleanup; - } - - i = 0; - while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) { - if ((ret = git_pkt_buffer_have(&oid, &data)) < 0) - goto cleanup; - - i++; - } - - git_pkt_buffer_done(&data); - - if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", data.size, 0)) < 0) - goto cleanup; - - if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0) - goto cleanup; - - if ((ret = gitno_send(transport, data.ptr, data.size, 0)) < 0) - goto cleanup; - - git_buf_clear(&request); - git_buf_clear(&data); - - if (ret < 0 || i >= 256) - break; - - if ((ret = parse_response(t)) < 0) - goto cleanup; - - if (t->pack_ready) { - ret = 0; - goto cleanup; - } - - } while(1); + if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0) + return t->error = -1; cleanup: git_buf_free(&request); - git_buf_free(&data); - git_revwalk_free(walk); - return ret; -} - -typedef struct { - git_indexer_stream *idx; - git_indexer_stats *stats; - transport_http *transport; -} download_pack_cbdata; - -static int on_message_complete_download_pack(http_parser *parser) -{ - download_pack_cbdata *data = (download_pack_cbdata *) parser->data; - - data->transport->transfer_finished = 1; - - return 0; -} -static int on_body_download_pack(http_parser *parser, const char *str, size_t len) -{ - download_pack_cbdata *data = (download_pack_cbdata *) parser->data; - transport_http *t = data->transport; - git_indexer_stream *idx = data->idx; - git_indexer_stats *stats = data->stats; + git_buf_clear(&t->buf); - return t->error = git_indexer_stream_add(idx, str, len, stats); + return ret; } -/* - * As the server is probably using Transfer-Encoding: chunked, we have - * to use the HTTP parser to download the pack instead of giving it to - * the simple downloader. Furthermore, we're using keep-alive - * connections, so the simple downloader would just hang. - */ -static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats) +static int http_negotiation_step(struct git_transport *transport, void *data, size_t len) { transport_http *t = (transport_http *) transport; - git_buf *oldbuf = &t->buf; - int recvd; - http_parser_settings settings; - char buffer[1024]; - gitno_buffer buf; - git_buf path = GIT_BUF_INIT; - git_indexer_stream *idx = NULL; - download_pack_cbdata data; - - gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer)); - - if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) { - giterr_set(GITERR_NET, "The pack doesn't start with a pack signature"); - return -1; - } - - if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0) - return -1; + git_buf request = GIT_BUF_INIT; + int ret; - if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0) + /* First, send the data as a HTTP POST request */ + if ((ret = do_connect(t, t->host, t->port)) < 0) return -1; - /* - * This is part of the previous response, so we don't want to - * re-init the parser, just set these two callbacks. - */ - memset(stats, 0, sizeof(git_indexer_stats)); - data.stats = stats; - data.idx = idx; - data.transport = t; - t->parser.data = &data; - t->transfer_finished = 0; - memset(&settings, 0x0, sizeof(settings)); - settings.on_message_complete = on_message_complete_download_pack; - settings.on_body = on_body_download_pack; - *bytes = git_buf_len(oldbuf); - - if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0) + if ((ret = gen_request(&request, t->path, t->host, "POST", "upload-pack", len, 0)) < 0) goto on_error; - gitno_buffer_setup(transport, &buf, buffer, sizeof(buffer)); - - do { - size_t parsed; - - if ((recvd = gitno_recv(&buf)) < 0) - goto on_error; + if ((ret = gitno_send(transport, request.ptr, request.size, 0)) < 0) + goto on_error; - parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset); - if (parsed != buf.offset || t->error < 0) - goto on_error; + if ((ret = gitno_send(transport, data, len, 0)) < 0) + goto on_error; - *bytes += recvd; - gitno_consume_n(&buf, parsed); - } while (recvd > 0 && !t->transfer_finished); + git_buf_free(&request); - if (git_indexer_stream_finalize(idx, stats) < 0) - goto on_error; + /* Then we need to set up the buffer to grab data from the HTTP response */ + setup_gitno_buffer(transport); - git_indexer_stream_free(idx); return 0; on_error: - git_indexer_stream_free(idx); - git_buf_free(&path); + git_buf_free(&request); return -1; } @@ -627,8 +379,8 @@ static int http_close(git_transport *transport) static void http_free(git_transport *transport) { transport_http *t = (transport_http *) transport; - git_vector *refs = &t->refs; - git_vector *common = &t->common; + git_vector *refs = &transport->refs; + git_vector *common = &transport->common; unsigned int i; git_pkt *p; @@ -649,8 +401,6 @@ static void http_free(git_transport *transport) } git_vector_free(common); git_buf_free(&t->buf); - git_buf_free(&t->proto.buf); - git__free(t->heads); git__free(t->content_type); git__free(t->host); git__free(t->port); @@ -669,13 +419,15 @@ int git_transport_http(git_transport **out) memset(t, 0x0, sizeof(transport_http)); t->parent.connect = http_connect; - t->parent.ls = http_ls; - t->parent.negotiate_fetch = http_negotiate_fetch; - t->parent.download_pack = http_download_pack; + t->parent.negotiation_step = http_negotiation_step; t->parent.close = http_close; t->parent.free = http_free; - t->proto.refs = &t->refs; - t->proto.transport = (git_transport *) t; + t->parent.rpc = 1; + + if (git_vector_init(&t->parent.refs, 16, NULL) < 0) { + git__free(t); + return -1; + } #ifdef GIT_WIN32 /* on win32, the WSA context needs to be initialized @@ -698,7 +450,7 @@ int git_transport_https(git_transport **out) if (git_transport_http((git_transport **)&t) < 0) return -1; - t->parent.encrypt = 1; + t->parent.use_ssl = 1; t->parent.check_cert = 1; *out = (git_transport *) t; diff --git a/src/transports/local.c b/src/transports/local.c index 0e1ae3752..561c84fa0 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -15,11 +15,11 @@ #include "posix.h" #include "path.h" #include "buffer.h" +#include "pkt.h" typedef struct { git_transport parent; git_repository *repo; - git_vector refs; } transport_local; static int add_ref(transport_local *t, const char *name) @@ -27,19 +27,32 @@ static int add_ref(transport_local *t, const char *name) const char peeled[] = "^{}"; git_remote_head *head; git_object *obj = NULL, *target = NULL; + git_transport *transport = (git_transport *) t; git_buf buf = GIT_BUF_INIT; + git_pkt_ref *pkt; head = git__malloc(sizeof(git_remote_head)); GITERR_CHECK_ALLOC(head); + pkt = git__malloc(sizeof(git_pkt_ref)); + GITERR_CHECK_ALLOC(pkt); head->name = git__strdup(name); GITERR_CHECK_ALLOC(head->name); - if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 || - git_vector_insert(&t->refs, head) < 0) - { - git__free(head->name); + if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0) { git__free(head); + git__free(pkt->head.name); + git__free(pkt); + } + + pkt->type = GIT_PKT_REF; + memcpy(&pkt->head, head, sizeof(git_remote_head)); + git__free(head); + + if (git_vector_insert(&transport->refs, pkt) < 0) + { + git__free(pkt->head.name); + git__free(pkt); return -1; } @@ -47,7 +60,7 @@ static int add_ref(transport_local *t, const char *name) if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return 0; - if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0) + if (git_object_lookup(&obj, t->repo, &pkt->head.oid, GIT_OBJ_ANY) < 0) return -1; head = NULL; @@ -66,14 +79,20 @@ static int add_ref(transport_local *t, const char *name) head->name = git_buf_detach(&buf); + pkt = git__malloc(sizeof(git_pkt_ref)); + GITERR_CHECK_ALLOC(pkt); + pkt->type = GIT_PKT_REF; + 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); + memcpy(&pkt->head, head, sizeof(git_remote_head)); + git__free(head); - if (git_vector_insert(&t->refs, head) < 0) + if (git_vector_insert(&transport->refs, pkt) < 0) return -1; return 0; @@ -88,11 +107,12 @@ static int store_refs(transport_local *t) { unsigned int i; git_strarray ref_names = {0}; + git_transport *transport = (git_transport *) t; assert(t); if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 || - git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0) + git_vector_init(&transport->refs, (unsigned int)ref_names.count, NULL) < 0) goto on_error; /* Sort the references first */ @@ -111,28 +131,11 @@ static int store_refs(transport_local *t) return 0; on_error: - git_vector_free(&t->refs); + git_vector_free(&transport->refs); git_strarray_free(&ref_names); return -1; } -static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload) -{ - transport_local *t = (transport_local *) transport; - git_vector *refs = &t->refs; - unsigned int i; - git_remote_head *h; - - assert(transport && transport->connected); - - git_vector_foreach(refs, i, h) { - if (list_cb(h, payload) < 0) - return -1; - } - - return 0; -} - /* * Try to open the url as a git directory. The direction doesn't * matter in this case because we're calulating the heads ourselves. @@ -201,14 +204,14 @@ static void local_free(git_transport *transport) { unsigned int i; transport_local *t = (transport_local *) transport; - git_vector *vec = &t->refs; - git_remote_head *h; + git_vector *vec = &transport->refs; + git_pkt_ref *pkt; assert(transport); - git_vector_foreach (vec, i, h) { - git__free(h->name); - git__free(h); + git_vector_foreach (vec, i, pkt) { + git__free(pkt->head.name); + git__free(pkt); } git_vector_free(vec); @@ -229,8 +232,8 @@ int git_transport_local(git_transport **out) memset(t, 0x0, sizeof(transport_local)); + t->parent.own_logic = 1; t->parent.connect = local_connect; - t->parent.ls = local_ls; t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.close = local_close; t->parent.free = local_free; diff --git a/src/tree.c b/src/tree.c index 9d793cbb8..19250fe5e 100644 --- a/src/tree.c +++ b/src/tree.c @@ -234,12 +234,27 @@ const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename return entry_fromname(tree, filename, strlen(filename)); } -const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx) +const git_tree_entry *git_tree_entry_byindex(git_tree *tree, size_t idx) { assert(tree); return git_vector_get(&tree->entries, idx); } +const git_tree_entry *git_tree_entry_byoid(git_tree *tree, const git_oid *oid) +{ + unsigned int i; + git_tree_entry *e; + + assert(tree); + + git_vector_foreach(&tree->entries, i, e) { + if (memcmp(&e->oid.id, &oid->id, sizeof(oid->id)) == 0) + return e; + } + + return NULL; +} + int git_tree__prefix_position(git_tree *tree, const char *path) { git_vector *entries = &tree->entries; @@ -270,7 +285,7 @@ int git_tree__prefix_position(git_tree *tree, const char *path) unsigned int git_tree_entrycount(git_tree *tree) { assert(tree); - return tree->entries.length; + return (unsigned int)tree->entries.length; } static int tree_error(const char *str) @@ -501,7 +516,7 @@ static void sort_entries(git_treebuilder *bld) int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) { git_treebuilder *bld; - unsigned int i, source_entries = DEFAULT_TREE_SIZE; + size_t i, source_entries = DEFAULT_TREE_SIZE; assert(builder_p); @@ -724,7 +739,7 @@ int git_tree_entry_bypath( } switch (path[filename_len]) { - case '/': + case '/': /* If there are more components in the path... * then this entry *must* be a tree */ if (!git_tree_entry__is_tree(entry)) { @@ -772,8 +787,13 @@ static int tree_walk( for (i = 0; i < tree->entries.length; ++i) { git_tree_entry *entry = tree->entries.contents[i]; - if (preorder && callback(path->ptr, entry, payload) < 0) - return -1; + if (preorder) { + error = callback(path->ptr, entry, payload); + if (error > 0) + continue; + if (error < 0) + return GIT_EUSER; + } if (git_tree_entry__is_tree(entry)) { git_tree *subtree; @@ -790,18 +810,21 @@ static int tree_walk( if (git_buf_oom(path)) return -1; - if (tree_walk(subtree, callback, path, payload, preorder) < 0) - return -1; + error = tree_walk(subtree, callback, path, payload, preorder); + if (error != 0) + break; git_buf_truncate(path, path_len); git_tree_free(subtree); } - if (!preorder && callback(path->ptr, entry, payload) < 0) - return -1; + if (!preorder && callback(path->ptr, entry, payload) < 0) { + error = GIT_EUSER; + break; + } } - return 0; + return error; } int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload) diff --git a/src/util.c b/src/util.c index 90bb3d02a..51bf843de 100644 --- a/src/util.c +++ b/src/util.c @@ -22,6 +22,18 @@ void git_libgit2_version(int *major, int *minor, int *rev) *rev = LIBGIT2_VER_REVISION; } +int git_libgit2_capabilities() +{ + return 0 +#ifdef GIT_THREADS + | GIT_CAP_THREADS +#endif +#ifdef GIT_SSL + | GIT_CAP_HTTPS +#endif + ; +} + void git_strarray_free(git_strarray *array) { size_t i; diff --git a/src/vector.c b/src/vector.c index 6f9aacccf..0308ce26e 100644 --- a/src/vector.c +++ b/src/vector.c @@ -35,7 +35,7 @@ void git_vector_free(git_vector *v) v->_alloc_size = 0; } -int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp) +int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) { assert(v); diff --git a/src/vector.h b/src/vector.h index 9139db345..f75e634ba 100644 --- a/src/vector.h +++ b/src/vector.h @@ -12,16 +12,16 @@ typedef int (*git_vector_cmp)(const void *, const void *); typedef struct git_vector { - unsigned int _alloc_size; + size_t _alloc_size; git_vector_cmp _cmp; void **contents; - unsigned int length; + size_t length; int sorted; } git_vector; #define GIT_VECTOR_INIT {0} -int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp); +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_clear(git_vector *v); void git_vector_swap(git_vector *a, git_vector *b); @@ -45,12 +45,12 @@ GIT_INLINE(int) git_vector_bsearch2( return git_vector_bsearch3(NULL, v, cmp, key); } -GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position) +GIT_INLINE(void *) git_vector_get(git_vector *v, size_t position) { return (position < v->length) ? v->contents[position] : NULL; } -GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position) +GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, size_t position) { return (position < v->length) ? v->contents[position] : NULL; } diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 557760b94..aa34ad3ac 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -61,6 +61,7 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) static int do_lstat(const char *file_name, struct stat *buf) { WIN32_FILE_ATTRIBUTE_DATA fdata; + DWORD last_error; wchar_t* fbuf = gitwin_to_utf16(file_name); if (!fbuf) return -1; @@ -94,6 +95,12 @@ static int do_lstat(const char *file_name, struct stat *buf) return 0; } + last_error = GetLastError(); + if (last_error == ERROR_FILE_NOT_FOUND) + errno = ENOENT; + else if (last_error == ERROR_PATH_NOT_FOUND) + errno = ENOTDIR; + git__free(fbuf); return -1; } diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c index c37ff544a..4a317e4f3 100644 --- a/tests-clar/attr/repo.c +++ b/tests-clar/attr/repo.c @@ -113,6 +113,22 @@ static int count_attrs( return 0; } +static int cancel_iteration( + const char *name, + const char *value, + void *payload) +{ + GIT_UNUSED(name); + GIT_UNUSED(value); + + *((int *)payload) -= 1; + + if (*((int *)payload) < 0) + return -1; + + return 0; +} + void test_attr_repo__foreach(void) { int count; @@ -131,6 +147,12 @@ void test_attr_repo__foreach(void) cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt", &count_attrs, &count)); cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */ + + count = 2; + cl_assert_equal_i( + GIT_EUSER, git_attr_foreach( + g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count) + ); } void test_attr_repo__manpage_example(void) diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index a8504da02..fcd22463d 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -226,7 +226,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(-100, ret); + cl_assert_equal_i(GIT_EUSER, ret); git_config_free(cfg); } @@ -266,6 +266,22 @@ void test_config_read__foreach_match(void) git_config_free(cfg); } +void test_config_read__whitespace_not_required_around_assignment(void) +{ + git_config *cfg; + const char *str; + + cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14"))); + + cl_git_pass(git_config_get_string(&str, cfg, "a.b")); + cl_assert_equal_s(str, "c"); + + cl_git_pass(git_config_get_string(&str, cfg, "d.e")); + cl_assert_equal_s(str, "f"); + + git_config_free(cfg); +} + #if 0 BEGIN_TEST(config10, "a repo's config overrides the global config") diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index 1d9f6121c..18daa080b 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -5,7 +5,7 @@ git_tree *resolve_commit_oid_to_tree( git_repository *repo, const char *partial_oid) { - unsigned int len = (unsigned int)strlen(partial_oid); + size_t len = strlen(partial_oid); git_oid oid; git_object *obj = NULL; git_tree *tree = NULL; diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c index 171815df5..89e65e3b7 100644 --- a/tests-clar/diff/index.c +++ b/tests-clar/diff/index.c @@ -90,3 +90,53 @@ void test_diff_index__0(void) git_tree_free(a); git_tree_free(b); } + +static int diff_stop_after_2_files( + void *cb_data, + git_diff_delta *delta, + float progress) +{ + diff_expects *e = cb_data; + + GIT_UNUSED(progress); + GIT_UNUSED(delta); + + e->files++; + + return (e->files == 2); +} + +void test_diff_index__1(void) +{ + /* grabbed a couple of commit oids from the history of the attr repo */ + const char *a_commit = "26a125ee1bf"; /* the current HEAD */ + const char *b_commit = "0017bd4ab1ec3"; /* the start */ + git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); + git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit); + git_diff_options opts = {0}; + git_diff_list *diff = NULL; + diff_expects exp; + + cl_assert(a); + cl_assert(b); + + opts.context_lines = 1; + opts.interhunk_lines = 1; + + memset(&exp, 0, sizeof(exp)); + + cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff)); + + cl_assert_equal_i( + GIT_EUSER, + git_diff_foreach(diff, &exp, diff_stop_after_2_files, NULL, NULL) + ); + + cl_assert(exp.files == 2); + + git_diff_list_free(diff); + diff = NULL; + + git_tree_free(a); + git_tree_free(b); +} diff --git a/tests-clar/network/remotelocal.c b/tests-clar/network/remotelocal.c index 16e3fe2dd..63016db5f 100644 --- a/tests-clar/network/remotelocal.c +++ b/tests-clar/network/remotelocal.c @@ -107,7 +107,7 @@ void test_network_remotelocal__retrieve_advertised_references(void) cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); - cl_assert_equal_i(how_many_refs, 23); + cl_assert_equal_i(how_many_refs, 25); } void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void) @@ -121,7 +121,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); - cl_assert_equal_i(how_many_refs, 23); + cl_assert_equal_i(how_many_refs, 25); git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */ remote = NULL; diff --git a/tests-clar/notes/notes.c b/tests-clar/notes/notes.c index e1387782e..dfd7f5231 100644 --- a/tests-clar/notes/notes.c +++ b/tests-clar/notes/notes.c @@ -95,11 +95,39 @@ void test_notes_notes__can_retrieve_a_list_of_notes_for_a_given_namespace(void) create_note(¬e_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n"); create_note(¬e_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); - cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes)); + cl_git_pass(git_note_foreach +(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes)); cl_assert_equal_i(4, retrieved_notes); } +static int note_cancel_cb(git_note_data *note_data, void *payload) +{ + unsigned int *count = (unsigned int *)payload; + + GIT_UNUSED(note_data); + + (*count)++; + + return (*count > 2); +} + +void test_notes_notes__can_cancel_foreach(void) +{ + git_oid note_oid1, note_oid2, note_oid3, note_oid4; + unsigned int retrieved_notes = 0; + + create_note(¬e_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n"); + create_note(¬e_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n"); + create_note(¬e_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n"); + create_note(¬e_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); + + cl_assert_equal_i( + GIT_EUSER, + git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", + note_cancel_cb, &retrieved_notes)); +} + void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void) { int error; diff --git a/tests-clar/object/blob/fromchunks.c b/tests-clar/object/blob/fromchunks.c index 228e969b6..dc57d4fbe 100644 --- a/tests-clar/object/blob/fromchunks.c +++ b/tests-clar/object/blob/fromchunks.c @@ -30,7 +30,7 @@ static int text_chunked_source_cb(char *content, size_t max_length, void *payloa return 0; strcpy(content, textual_content); - return strlen(textual_content); + return (int)strlen(textual_content); } void test_object_blob_fromchunks__can_create_a_blob_from_a_in_memory_chunk_provider(void) diff --git a/tests-clar/object/commit/commitstagedfile.c b/tests-clar/object/commit/commitstagedfile.c index 628ef43c2..882fb49ae 100644 --- a/tests-clar/object/commit/commitstagedfile.c +++ b/tests-clar/object/commit/commitstagedfile.c @@ -109,7 +109,7 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void) cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid)); - cl_git_pass(git_message_prettify(buffer, 128, "Initial commit", 0)); + cl_assert_equal_i(16, git_message_prettify(buffer, 128, "Initial commit", 0)); cl_git_pass(git_commit_create_v( &commit_oid, @@ -128,3 +128,68 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void) git_tree_free(tree); git_index_free(index); } + +void test_object_commit_commitstagedfile__message_prettify(void) +{ + char buffer[100]; + + cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 0) == 1); + cl_assert_equal_s(buffer, ""); + cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 1) == 1); + cl_assert_equal_s(buffer, ""); + + cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 0)); + cl_assert_equal_s("Short\n", buffer); + cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 1)); + cl_assert_equal_s("Short\n", buffer); + + cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 0) > 0); + cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n# with some comments still in\n"); + + cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 1) > 0); + cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n"); + + /* try out overflow */ + cl_assert(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "12345678", + 0) > 0); + cl_assert_equal_s(buffer, + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n"); + + cl_assert(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n", + 0) > 0); + cl_assert_equal_s(buffer, + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n"); + + cl_git_fail(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "123456789", + 0)); + cl_git_fail(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "123456789\n", + 0)); + cl_git_fail(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890", + 0)); + cl_git_fail(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890""x", + 0)); + + cl_assert(git_message_prettify(buffer, sizeof(buffer), + "1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n" + "# 1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n" + "1234567890", + 1) > 0); + + cl_assert(git_message_prettify(NULL, 0, "", 0) == 1); + cl_assert(git_message_prettify(NULL, 0, "Short test", 0) == 12); + cl_assert(git_message_prettify(NULL, 0, "Test\n# with\nComments", 1) == 15); +} diff --git a/tests-clar/object/tree/walk.c b/tests-clar/object/tree/walk.c new file mode 100644 index 000000000..58b0bca4c --- /dev/null +++ b/tests-clar/object/tree/walk.c @@ -0,0 +1,103 @@ +#include "clar_libgit2.h" +#include "tree.h" + +static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; +static git_repository *g_repo; + +void test_object_tree_walk__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_object_tree_walk__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static int treewalk_count_cb( + const char *root, const git_tree_entry *entry, void *payload) +{ + int *count = payload; + + GIT_UNUSED(root); + GIT_UNUSED(entry); + + (*count) += 1; + + return 0; +} + +void test_object_tree_walk__0(void) +{ + git_oid id; + git_tree *tree; + int ct; + + git_oid_fromstr(&id, tree_oid); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + + ct = 0; + cl_git_pass(git_tree_walk(tree, treewalk_count_cb, GIT_TREEWALK_PRE, &ct)); + cl_assert_equal_i(3, ct); + + ct = 0; + cl_git_pass(git_tree_walk(tree, treewalk_count_cb, GIT_TREEWALK_POST, &ct)); + cl_assert_equal_i(3, ct); + + git_tree_free(tree); +} + + +static int treewalk_stop_cb( + const char *root, const git_tree_entry *entry, void *payload) +{ + int *count = payload; + + GIT_UNUSED(root); + GIT_UNUSED(entry); + + (*count) += 1; + + return (*count == 2) ? -1 : 0; +} + +static int treewalk_stop_immediately_cb( + const char *root, const git_tree_entry *entry, void *payload) +{ + GIT_UNUSED(root); + GIT_UNUSED(entry); + GIT_UNUSED(payload); + return -100; +} + +void test_object_tree_walk__1(void) +{ + git_oid id; + git_tree *tree; + int ct; + + git_oid_fromstr(&id, tree_oid); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + + ct = 0; + cl_assert_equal_i( + GIT_EUSER, git_tree_walk(tree, treewalk_stop_cb, GIT_TREEWALK_PRE, &ct)); + cl_assert_equal_i(2, ct); + + ct = 0; + cl_assert_equal_i( + GIT_EUSER, git_tree_walk(tree, treewalk_stop_cb, GIT_TREEWALK_POST, &ct)); + cl_assert_equal_i(2, ct); + + cl_assert_equal_i( + GIT_EUSER, git_tree_walk( + tree, treewalk_stop_immediately_cb, GIT_TREEWALK_PRE, NULL)); + + cl_assert_equal_i( + GIT_EUSER, git_tree_walk( + tree, treewalk_stop_immediately_cb, GIT_TREEWALK_POST, NULL)); + + git_tree_free(tree); +} diff --git a/tests-clar/odb/foreach.c b/tests-clar/odb/foreach.c index e5d01eafd..c1304a2e4 100644 --- a/tests-clar/odb/foreach.c +++ b/tests-clar/odb/foreach.c @@ -7,16 +7,13 @@ static git_odb *_odb; static git_repository *_repo; static int nobj; -void test_odb_foreach__initialize(void) -{ - cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); - git_repository_odb(&_odb, _repo); -} - void test_odb_foreach__cleanup(void) { git_odb_free(_odb); git_repository_free(_repo); + + _odb = NULL; + _repo = NULL; } static int foreach_cb(git_oid *oid, void *data) @@ -41,6 +38,43 @@ static int foreach_cb(git_oid *oid, void *data) */ void test_odb_foreach__foreach(void) { + 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_assert_equal_i(43 + 1640, nobj); /* count + in-pack */ } + +void test_odb_foreach__one_pack(void) +{ + git_odb_backend *backend = NULL; + + 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_assert(nobj == 1628); +} + +static int foreach_stop_cb(git_oid *oid, void *data) +{ + GIT_UNUSED(data); + GIT_UNUSED(oid); + + nobj++; + + return (nobj == 1000); +} + +void test_odb_foreach__interrupt_foreach(void) +{ + nobj = 0; + cl_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(nobj == 1000); +} diff --git a/tests-clar/odb/pack_data_one.h b/tests-clar/odb/pack_data_one.h new file mode 100644 index 000000000..13570ba78 --- /dev/null +++ b/tests-clar/odb/pack_data_one.h @@ -0,0 +1,19 @@ +/* Just a few to make sure it's working, the rest is tested already */ +static const char *packed_objects_one[] = { + "9fcf811e00fa469688943a9152c16d4ee90fb9a9", + "a93f42a5b5e9de40fa645a9ff1e276a021c9542b", + "12bf5f3e3470d90db177ccf1b5e8126409377fc6", + "ed1ea164cdbe3c4b200fb4fa19861ea90eaee222", + "dfae6ed8f6dd8acc3b40a31811ea316239223559", + "aefe66d192771201e369fde830530f4475beec30", + "775e4b4c1296e9e3104f2a36ca9cf9356a130959", + "412ec4e4a6a7419bc1be00561fe474e54cb499fe", + "236e7579fed7763be77209efb8708960982f3cb3", + "09fe9364461cf60dd1c46b0e9545b1e47bb1a297", + "d76d8a6390d1cf32138d98a91b1eb7e0275a12f5", + "d0fdf2dcff2f548952eec536ccc6d266550041bc", + "a20d733a9fa79fa5b4cbb9639864f93325ec27a6", + "785d3fe8e7db5ade2c2242fecd46c32a7f4dc59f", + "4d8d0fd9cb6045075385701c3f933ec13345e9c4", + "0cfd861bd547b6520d1fc2e190e8359e0a9c9b90" +}; diff --git a/tests-clar/odb/packed_one.c b/tests-clar/odb/packed_one.c new file mode 100644 index 000000000..a064829dd --- /dev/null +++ b/tests-clar/odb/packed_one.c @@ -0,0 +1,58 @@ +#include "clar_libgit2.h" +#include "odb.h" +#include "pack_data_one.h" +#include "pack.h" + +static git_odb *_odb; + +void test_odb_packed_one__initialize(void) +{ + git_odb_backend *backend = NULL; + + 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)); +} + +void test_odb_packed_one__cleanup(void) +{ + git_odb_free(_odb); +} + +void test_odb_packed_one__mass_read(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) { + git_oid id; + git_odb_object *obj; + + cl_git_pass(git_oid_fromstr(&id, packed_objects_one[i])); + cl_assert(git_odb_exists(_odb, &id) == 1); + cl_git_pass(git_odb_read(&obj, _odb, &id)); + + git_odb_object_free(obj); + } +} + +void test_odb_packed_one__read_header_0(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) { + git_oid id; + git_odb_object *obj; + size_t len; + git_otype type; + + cl_git_pass(git_oid_fromstr(&id, packed_objects_one[i])); + + cl_git_pass(git_odb_read(&obj, _odb, &id)); + cl_git_pass(git_odb_read_header(&len, &type, _odb, &id)); + + cl_assert(obj->raw.len == len); + cl_assert(obj->raw.type == type); + + git_odb_object_free(obj); + } +} diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c index 794233cc9..aca11ecd9 100644 --- a/tests-clar/refs/branches/foreach.c +++ b/tests-clar/refs/branches/foreach.c @@ -47,7 +47,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count) void test_refs_branches_foreach__retrieve_all_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 11); + assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 13); } void test_refs_branches_foreach__retrieve_remote_branches(void) @@ -57,7 +57,7 @@ void test_refs_branches_foreach__retrieve_remote_branches(void) void test_refs_branches_foreach__retrieve_local_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL, 9); + assert_retrieval(GIT_BRANCH_LOCAL, 11); } struct expectations { @@ -125,3 +125,28 @@ void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void assert_branch_has_been_found(exp, "nulltoken/HEAD"); assert_branch_has_been_found(exp, "nulltoken/HEAD"); } + +static int branch_list_interrupt_cb( + const char *branch_name, git_branch_t branch_type, void *payload) +{ + int *count; + + GIT_UNUSED(branch_type); + GIT_UNUSED(branch_name); + + count = (int *)payload; + (*count)++; + + return (*count == 5); +} + +void test_refs_branches_foreach__can_cancel(void) +{ + int count = 0; + + cl_assert_equal_i(GIT_EUSER, + git_branch_foreach(repo, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, + branch_list_interrupt_cb, &count)); + + cl_assert_equal_i(5, count); +} diff --git a/tests-clar/refs/foreachglob.c b/tests-clar/refs/foreachglob.c index b024d36d4..054846fe6 100644 --- a/tests-clar/refs/foreachglob.c +++ b/tests-clar/refs/foreachglob.c @@ -45,8 +45,8 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_ void test_refs_foreachglob__retrieve_all_refs(void) { - /* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */ - assert_retrieval("*", GIT_REF_LISTALL, 18); + /* 8 heads (including one packed head) + 1 note + 2 remotes + 6 tags */ + assert_retrieval("*", GIT_REF_LISTALL, 20); } void test_refs_foreachglob__retrieve_remote_branches(void) @@ -56,7 +56,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void) void test_refs_foreachglob__retrieve_local_branches(void) { - assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 9); + assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 11); } void test_refs_foreachglob__retrieve_partially_named_references(void) @@ -68,3 +68,25 @@ void test_refs_foreachglob__retrieve_partially_named_references(void) assert_retrieval("*test*", GIT_REF_LISTALL, 4); } + + +static int interrupt_cb(const char *reference_name, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(reference_name); + + (*count)++; + + return (*count == 11); +} + +void test_refs_foreachglob__can_cancel(void) +{ + int count = 0; + + cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( + repo, "*", GIT_REF_LISTALL, interrupt_cb, &count) ); + + cl_assert_equal_i(11, count); +} diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c index 1948e0a56..f33658754 100644 --- a/tests-clar/refs/read.c +++ b/tests-clar/refs/read.c @@ -193,6 +193,30 @@ void test_refs_read__loose_first(void) git_reference_free(reference); } +void test_refs_read__chomped(void) +{ + git_reference *test, *chomped; + + cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); + cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped")); + cl_git_pass(git_oid_cmp(git_reference_oid(test), git_reference_oid(chomped))); + + git_reference_free(test); + git_reference_free(chomped); +} + +void test_refs_read__trailing(void) +{ + git_reference *test, *trailing; + + cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); + cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing")); + cl_git_pass(git_oid_cmp(git_reference_oid(test), git_reference_oid(trailing))); + + git_reference_free(test); + git_reference_free(trailing); +} + void test_refs_read__unfound_return_ENOTFOUND(void) { git_reference *reference; diff --git a/tests-clar/repo/message.c b/tests-clar/repo/message.c new file mode 100644 index 000000000..4a6f13b9d --- /dev/null +++ b/tests-clar/repo/message.c @@ -0,0 +1,47 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "refs.h" +#include "posix.h" + +static git_repository *_repo; +static git_buf _path; +static char *_actual; + +void test_repo_message__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_repo_message__cleanup(void) +{ + cl_git_sandbox_cleanup(); + git_buf_free(&_path); + git__free(_actual); + _actual = NULL; +} + +void test_repo_message__none(void) +{ + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo)); +} + +void test_repo_message__message(void) +{ + const char expected[] = "Test\n\nThis is a test of the emergency broadcast system\n"; + ssize_t len; + + cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), "MERGE_MSG")); + cl_git_mkfile(git_buf_cstr(&_path), expected); + + len = git_repository_message(NULL, 0, _repo); + cl_assert(len > 0); + _actual = git__malloc(len + 1); + cl_assert(_actual != NULL); + + cl_assert(git_repository_message(_actual, len, _repo) > 0); + _actual[len] = '\0'; + cl_assert_equal_s(expected, _actual); + + cl_git_pass(p_unlink(git_buf_cstr(&_path))); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo)); +} diff --git a/tests-clar/resources/config/config14 b/tests-clar/resources/config/config14 new file mode 100644 index 000000000..ef2198c45 --- /dev/null +++ b/tests-clar/resources/config/config14 @@ -0,0 +1,4 @@ +[a] + b=c +[d] + e = f diff --git a/tests-clar/resources/testrepo.git/refs/heads/chomped b/tests-clar/resources/testrepo.git/refs/heads/chomped new file mode 100644 index 000000000..0166a7f92 --- /dev/null +++ b/tests-clar/resources/testrepo.git/refs/heads/chomped @@ -0,0 +1 @@ +e90810b8df3e80c413d903f631643c716887138d
\ No newline at end of file diff --git a/tests-clar/resources/testrepo.git/refs/heads/trailing b/tests-clar/resources/testrepo.git/refs/heads/trailing new file mode 100644 index 000000000..2a4a6e62f --- /dev/null +++ b/tests-clar/resources/testrepo.git/refs/heads/trailing @@ -0,0 +1 @@ +e90810b8df3e80c413d903f631643c716887138d diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 6e21e44bf..2abf36833 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -530,7 +530,7 @@ void test_status_worktree__bracket_in_filename(void) cl_git_pass(git_repository_init(&repo, "with_bracket", 0)); cl_git_mkfile("with_bracket/" FILE_WITH_BRACKET, "I have a bracket in my name\n"); - + /* file is new to working directory */ memset(&result, 0, sizeof(result)); @@ -578,7 +578,7 @@ void test_status_worktree__bracket_in_filename(void) cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET)); cl_assert(status_flags == GIT_STATUS_INDEX_NEW); - + /* Create file without bracket */ cl_git_mkfile("with_bracket/" FILE_WITHOUT_BRACKET, "I have no bracket in my name!\n"); @@ -591,7 +591,7 @@ void test_status_worktree__bracket_in_filename(void) error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET); cl_git_fail(error); - cl_assert(error == GIT_EAMBIGUOUS); + cl_assert_equal_i(GIT_EAMBIGUOUS, error); git_index_free(index); git_repository_free(repo); @@ -769,6 +769,31 @@ void test_status_worktree__disable_pathspec_match(void) cl_git_pass( git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL) ); - + git_repository_free(repo); } + + +static int cb_status__interrupt(const char *p, unsigned int s, void *payload) +{ + volatile int *count = (int *)payload; + + GIT_UNUSED(p); + GIT_UNUSED(s); + + (*count)++; + + return (*count == 8); +} + +void test_status_worktree__interruptable_foreach(void) +{ + int count = 0; + git_repository *repo = cl_git_sandbox_init("status"); + + cl_assert_equal_i( + GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count) + ); + + cl_assert_equal_i(8, count); +} |
