diff options
57 files changed, 2288 insertions, 1204 deletions
@@ -27,3 +27,4 @@ CMake* *.cmake .DS_Store *~ +tags diff --git a/.travis.yml b/.travis.yml index 29ef9d40..54da48a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ script: # Run Tests after_script: - ctest -V . - - if [ -f ./libgit2_clar ]; then valgrind --leak-check=full --show-reachable=yes ./libgit2_clar; else echo "Skipping valgrind"; fi + - if [ -f ./libgit2_clar ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar; else echo "Skipping valgrind"; fi # Only watch the development branch branches: diff --git a/include/git2/branch.h b/include/git2/branch.h index 8bf7eb9d..f072799c 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -8,6 +8,7 @@ #define INCLUDE_git_branch_h__ #include "common.h" +#include "oid.h" #include "types.h" /** @@ -55,21 +56,13 @@ GIT_EXTERN(int) git_branch_create( /** * Delete an existing branch reference. * - * @param repo Repository where lives the branch. + * If the branch is successfully deleted, the passed reference + * object will be freed and invalidated. * - * @param branch_name Name of the branch to be deleted; - * this name is validated for consistency. - * - * @param branch_type Type of the considered branch. This should - * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE. - * - * @return 0 on success, GIT_ENOTFOUND if the branch - * doesn't exist or an error code. + * @param branch A valid reference representing a branch + * @return 0 on success, or an error code. */ -GIT_EXTERN(int) git_branch_delete( - git_repository *repo, - const char *branch_name, - git_branch_t branch_type); +GIT_EXTERN(int) git_branch_delete(git_reference *branch); /** * Loop over all the branches and issue a callback for each one. diff --git a/include/git2/diff.h b/include/git2/diff.h index 088e1ecf..85bb308d 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -169,7 +169,7 @@ enum { GIT_DIFF_LINE_CONTEXT = ' ', GIT_DIFF_LINE_ADDITION = '+', GIT_DIFF_LINE_DELETION = '-', - GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED */ + GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED - will not be returned */ GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */ /* The following values will only be sent to a `git_diff_data_fn` when @@ -197,6 +197,11 @@ typedef int (*git_diff_data_fn)( const char *content, size_t content_len); +/** + * The diff iterator object is used to scan a diff list. + */ +typedef struct git_diff_iterator git_diff_iterator; + /** @name Diff List Generator Functions * * These are the functions you would use to create (or destroy) a @@ -354,6 +359,137 @@ GIT_EXTERN(int) git_diff_foreach( git_diff_data_fn line_cb); /** + * Create a diff iterator object that can be used to traverse a diff. + * + * This iterator can be used instead of `git_diff_foreach` in situations + * where callback functions are awkward to use. Because of the way that + * diffs are calculated internally, using an iterator will use somewhat + * more memory than `git_diff_foreach` would. + * + * @param iterator Output parameter of newly created iterator. + * @param diff Diff over which you wish to iterate. + * @return 0 on success, < 0 on error + */ +GIT_EXTERN(int) git_diff_iterator_new( + git_diff_iterator **iterator, + git_diff_list *diff); + +/** + * Release the iterator object. + * + * Call this when you are done using the iterator. + * + * @param iterator The diff iterator to be freed. + */ +GIT_EXTERN(void) git_diff_iterator_free(git_diff_iterator *iterator); + +/** + * Return the number of files in the diff. + * + * Note that there is an uncommon scenario where this number might be too + * high -- if a file in the working directory has been "touched" on disk but + * the contents were then reverted, it might have been added to the + * `git_diff_list` as a MODIFIED file along with a note that the status + * needs to be confirmed when the file contents are loaded into memory. In + * that case, when the file is loaded, we will check the contents and might + * switch it back to UNMODIFIED. The loading of the file is deferred until + * as late as possible. As a result, this might return a value what was too + * high in those circumstances. + * + * This is true of `git_diff_foreach` as well, but the only implication + * there is that the `progress` value would not advance evenly. + * + * @param iterator The iterator object + * @return The maximum number of files to be iterated over + */ +GIT_EXTERN(int) git_diff_iterator_num_files(git_diff_iterator *iterator); + +/** + * Return the number of hunks in the current file + * + * This will return the number of diff hunks in the current file. If the + * diff has not been performed yet, this may result in loading the file and + * performing the diff. + * + * @param iterator The iterator object + * @return The number of hunks in the current file or <0 on loading failure + */ +GIT_EXTERN(int) git_diff_iterator_num_hunks_in_file(git_diff_iterator *iterator); + +/** + * Return the number of lines in the hunk currently being examined. + * + * This will return the number of lines in the current hunk. If the diff + * has not been performed yet, this may result in loading the file and + * performing the diff. + * + * @param iterator The iterator object + * @return The number of lines in the current hunk (context, added, and + * removed all added together) or <0 on loading failure + */ +GIT_EXTERN(int) git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iterator); + +/** + * Return the delta information for the next file in the diff. + * + * This will return a pointer to the next git_diff_delta` to be processed or + * NULL if the iterator is at the end of the diff, then advance. This + * returns the value `GIT_ITEROVER` after processing the last file. + * + * @param delta Output parameter for the next delta object + * @param iterator The iterator object + * @return 0 on success, GIT_ITEROVER when done, other value < 0 on error + */ +GIT_EXTERN(int) git_diff_iterator_next_file( + git_diff_delta **delta, + git_diff_iterator *iterator); + +/** + * Return the hunk information for the next hunk in the current file. + * + * It is recommended that you not call this if the file is a binary + * file, but it is allowed to do so. + * + * Warning! Call this function for the first time on a file is when the + * actual text diff will be computed (it cannot be computed incrementally) + * so the first call for a new file is expensive (at least in relative + * terms - in reality, it is still pretty darn fast). + * + * @param range Pointer where to store the range for the hunk + * @param header Pointer where to store the header for the chunk; + * this string is owned by the library and should not be freed by + * the user + * @param header_len Pointer where to store the length of the returned header + * @param iterator The iterator object + * @return 0 on success, GIT_ITEROVER when done with current file, other + * value < 0 on error + */ +GIT_EXTERN(int) git_diff_iterator_next_hunk( + git_diff_range **range, + const char **header, + size_t *header_len, + git_diff_iterator *iterator); + +/** + * Return the next line of the current hunk of diffs. + * + * @param line_origin Pointer where to store a GIT_DIFF_LINE_ value; + * this value is a single character, not a buffer + * @param content Pointer where to store the content of the line; + * this string is owned by the library and should not be freed by + * the user + * @param Pointer where to store the length of the returned content + * @param iterator The iterator object + * @return 0 on success, GIT_ITEROVER when done with current line, other + * value < 0 on error + */ +GIT_EXTERN(int) git_diff_iterator_next_line( + char *line_origin, /**< GIT_DIFF_LINE_... value from above */ + const char **content, + size_t *content_len, + git_diff_iterator *iterator); + +/** * Iterate over a diff generating text output like "git diff --name-status". * * Returning a non-zero value from the callbacks will terminate the diff --git a/include/git2/errors.h b/include/git2/errors.h index b55f8c30..f6671c49 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -28,7 +28,7 @@ enum { GIT_EUSER = -7, GIT_PASSTHROUGH = -30, - GIT_REVWALKOVER = -31, + GIT_ITEROVER = -31, }; typedef struct { diff --git a/include/git2/object.h b/include/git2/object.h index 722434de..fd6ae95c 100644 --- a/include/git2/object.h +++ b/include/git2/object.h @@ -168,11 +168,14 @@ GIT_EXTERN(int) git_object_typeisloose(git_otype type); GIT_EXTERN(size_t) git_object__size(git_otype type); /** - * Recursively peel an object until an object of the specified - * type is met + * Recursively peel an object until an object of the specified type is met. * - * The retrieved `peeled` object is owned by the repository - * and should be closed with the `git_object_free` method. + * The retrieved `peeled` object is owned by the repository and should be + * closed with the `git_object_free` method. + * + * If you pass `GIT_OBJ_ANY` as the target type, then the object will be + * peeled until the type changes (e.g. a tag will be chased until the + * referenced object is no longer a tag). * * @param peeled Pointer to the peeled git_object * @param object The object to be processed diff --git a/include/git2/refs.h b/include/git2/refs.h index 9e706007..660b48b5 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -376,6 +376,64 @@ GIT_EXTERN(int) git_reference_has_log(git_reference *ref); */ GIT_EXTERN(int) git_reference_is_branch(git_reference *ref); +/** + * Check if a reference is a remote tracking branch + * + * @param ref A git reference + * + * @return 1 when the reference lives in the refs/remotes + * namespace; 0 otherwise. + */ +GIT_EXTERN(int) git_reference_is_remote(git_reference *ref); + +enum { + GIT_REF_FORMAT_NORMAL = 0, + + /** + * Control whether one-level refnames are accepted + * (i.e., refnames that do not contain multiple /-separated + * components) + */ + GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0), + + /** + * Interpret the provided name as a reference pattern for a + * refspec (as used with remote repositories). If this option + * is enabled, the name is allowed to contain a single * (<star>) + * in place of a one full pathname component + * (e.g., foo/<star>/bar but not foo/bar<star>). + */ + GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1), +}; + +/** + * Normalize the reference name by removing any leading + * slash (/) characters and collapsing runs of adjacent slashes + * between name components into a single slash. + * + * Once normalized, if the reference name is valid, it will be + * returned in the user allocated buffer. + * + * TODO: Implement handling of GIT_REF_FORMAT_REFSPEC_PATTERN + * + * @param buffer_out The user allocated buffer where the + * normalized name will be stored. + * + * @param buffer_size buffer_out size + * + * @param name name to be checked. + * + * @param flags Flags to determine the options to be applied while + * checking the validatity of the name. + * + * @return 0 or an error code. + */ +GIT_EXTERN(int) git_reference_normalize_name( + char *buffer_out, + size_t buffer_size, + const char *name, + unsigned int flags); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/reset.h b/include/git2/reset.h index 12517874..cd263fa9 100644 --- a/include/git2/reset.h +++ b/include/git2/reset.h @@ -37,7 +37,7 @@ GIT_BEGIN_DECL * * @return GIT_SUCCESS or an error code */ -GIT_EXTERN(int) git_reset(git_repository *repo, const git_object *target, git_reset_type reset_type); +GIT_EXTERN(int) git_reset(git_repository *repo, git_object *target, git_reset_type reset_type); /** @} */ GIT_END_DECL diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h index d86bb28e..0a85a4c6 100644 --- a/include/git2/revwalk.h +++ b/include/git2/revwalk.h @@ -201,7 +201,7 @@ GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname); * @param oid Pointer where to store the oid of the next commit * @param walk the walker to pop the commit from. * @return 0 if the next commit was found; - * GIT_REVWALKOVER if there are no commits left to iterate + * GIT_ITEROVER if there are no commits left to iterate */ GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk); diff --git a/include/git2/windows.h b/include/git2/windows.h deleted file mode 100644 index 8b743f0a..00000000 --- a/include/git2/windows.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009-2012 the libgit2 contributors - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_git_windows_h__ -#define INCLUDE_git_windows_h__ - -#include "common.h" - -/** - * @file git2/windows.h - * @brief Windows-specific functions - * @ingroup Git - * @{ - */ -GIT_BEGIN_DECL - -/** - * Set the active codepage for Windows syscalls - * - * All syscalls performed by the library will assume - * this codepage when converting paths and strings - * to use by the Windows kernel. - * - * The default value of UTF-8 will work automatically - * with most Git repositories created on Unix systems. - * - * This settings needs only be changed when working - * with repositories that contain paths in specific, - * non-UTF codepages. - * - * A full list of all available codepage identifiers may - * be found at: - * - * http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx - * - * @param codepage numeric codepage identifier - */ -GIT_EXTERN(void) gitwin_set_codepage(unsigned int codepage); - -/** - * Return the active codepage for Windows syscalls - * - * @return numeric codepage identifier - */ -GIT_EXTERN(unsigned int) gitwin_get_codepage(void); - -/** - * Set the active Windows codepage to UTF-8 (this is - * the default value) - */ -GIT_EXTERN(void) gitwin_set_utf8(void); - -/** @} */ -GIT_END_DECL -#endif - diff --git a/libgit2_clar.supp b/libgit2_clar.supp new file mode 100644 index 00000000..f49eb005 --- /dev/null +++ b/libgit2_clar.supp @@ -0,0 +1,12 @@ +{ + ignore-zlib-errors-cond + Memcheck:Cond + obj:*libz.so* +} + +{ + ignore-giterr-set-leak + Memcheck:Leak + ... + fun:giterr_set +} @@ -188,6 +188,7 @@ int git_attr_foreach( error = callback(assign->name, assign->value, payload); if (error) { + giterr_clear(); error = GIT_EUSER; goto cleanup; } diff --git a/src/branch.c b/src/branch.c index 52fed67a..cd5c10ed 100644 --- a/src/branch.c +++ b/src/branch.c @@ -50,6 +50,12 @@ static int create_error_invalid(const char *msg) return -1; } +static int not_a_local_branch(git_reference *ref) +{ + giterr_set(GITERR_INVALID, "Reference '%s' is not a local branch.", git_reference_name(ref)); + return -1; +} + int git_branch_create( git_reference **ref_out, git_repository *repository, @@ -57,7 +63,6 @@ int git_branch_create( const git_object *target, int force) { - git_otype target_type = GIT_OBJ_BAD; git_object *commit = NULL; git_reference *branch = NULL; git_buf canonical_branch_name = GIT_BUF_INIT; @@ -66,27 +71,8 @@ int git_branch_create( assert(branch_name && target && ref_out); assert(git_object_owner(target) == repository); - target_type = git_object_type(target); - - switch (target_type) - { - case GIT_OBJ_TAG: - if (git_tag_peel(&commit, (git_tag *)target) < 0) - goto cleanup; - - if (git_object_type(commit) != GIT_OBJ_COMMIT) { - create_error_invalid("The given target does not resolve to a commit"); - goto cleanup; - } - break; - - case GIT_OBJ_COMMIT: - commit = (git_object *)target; - break; - - default: - return create_error_invalid("Only git_tag and git_commit objects are valid targets."); - } + if (git_object_peel(&commit, (git_object *)target, GIT_OBJ_COMMIT) < 0) + return create_error_invalid("The given target does not resolve to a commit"); if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) goto cleanup; @@ -99,26 +85,24 @@ int git_branch_create( error = 0; cleanup: - if (target_type == GIT_OBJ_TAG) - git_object_free(commit); - + git_object_free(commit); git_buf_free(&canonical_branch_name); return error; } -int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type) +int git_branch_delete(git_reference *branch) { - git_reference *branch = NULL; git_reference *head = NULL; - int error; - assert(repo && branch_name); - assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE)); + assert(branch); - if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0) - return error; + if (!git_reference_is_branch(branch) && + !git_reference_is_remote(branch)) { + giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch)); + return -1; + } - if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) { + if (git_reference_lookup(&head, git_reference_owner(branch), GIT_HEAD_FILE) < 0) { giterr_set(GITERR_REFERENCE, "Cannot locate HEAD."); goto on_error; } @@ -126,7 +110,7 @@ int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_ if ((git_reference_type(head) == GIT_REF_SYMBOLIC) && (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) { giterr_set(GITERR_REFERENCE, - "Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name); + "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch)); goto on_error; } @@ -138,7 +122,6 @@ int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_ on_error: git_reference_free(head); - git_reference_free(branch); return -1; } @@ -185,12 +168,6 @@ int git_branch_foreach( return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter); } -static int not_a_local_branch(git_reference *ref) -{ - giterr_set(GITERR_INVALID, "Reference '%s' is not a local branch.", git_reference_name(ref)); - return -1; -} - int git_branch_move( git_reference *branch, const char *new_branch_name, diff --git a/src/config_file.c b/src/config_file.c index d3fb56aa..c575649a 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -221,6 +221,7 @@ static int file_foreach( /* abort iterator on non-zero return value */ if (fn(key, var->value, data)) { + giterr_clear(); result = GIT_EUSER; goto cleanup; } @@ -316,6 +316,7 @@ static git_diff_list *git_diff_list_alloc( if (diff == NULL) return NULL; + GIT_REFCOUNT_INC(diff); diff->repo = repo; if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 || @@ -391,15 +392,12 @@ fail: return NULL; } -void git_diff_list_free(git_diff_list *diff) +static void diff_list_free(git_diff_list *diff) { git_diff_delta *delta; git_attr_fnmatch *match; unsigned int i; - if (!diff) - return; - git_vector_foreach(&diff->deltas, i, delta) { git__free(delta); diff->deltas.contents[i] = NULL; @@ -416,6 +414,14 @@ void git_diff_list_free(git_diff_list *diff) git__free(diff); } +void git_diff_list_free(git_diff_list *diff) +{ + if (!diff) + return; + + GIT_REFCOUNT_DEC(diff, diff_list_free); +} + static int oid_for_workdir_item( git_repository *repo, const git_index_entry *item, @@ -26,6 +26,7 @@ enum { }; struct git_diff_list { + git_refcount rc; git_repository *repo; git_diff_options opts; git_vector pathspec; diff --git a/src/diff_output.c b/src/diff_output.c index 2bf939f3..2c64b92e 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -16,16 +16,35 @@ #include "fileops.h" #include "filter.h" +/* + * A diff_delta_context represents all of the information that goes into + * processing the diff of an observed file change. In the case of the + * git_diff_foreach() call it is an emphemeral structure that is filled + * in to execute each diff. In the case of a git_diff_iterator, it holds + * most of the information for the diff in progress. + */ typedef struct { - git_diff_list *diff; - void *cb_data; - git_diff_hunk_fn hunk_cb; - git_diff_data_fn line_cb; - unsigned int index; + git_repository *repo; + git_diff_options *opts; + xdemitconf_t xdiff_config; + xpparam_t xdiff_params; git_diff_delta *delta; + uint32_t prepped : 1; + uint32_t loaded : 1; + uint32_t diffable : 1; + uint32_t diffed : 1; + git_iterator_type_t old_src; + git_iterator_type_t new_src; + git_blob *old_blob; + git_blob *new_blob; + git_map old_data; + git_map new_data; + void *cb_data; + git_diff_hunk_fn per_hunk; + git_diff_data_fn per_line; + int cb_error; git_diff_range range; - int error; -} diff_output_info; +} diff_delta_context; static int read_next_int(const char **str, int *value) { @@ -41,71 +60,89 @@ static int read_next_int(const char **str, int *value) return (digits > 0) ? 0 : -1; } -static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len) +static int parse_hunk_header(git_diff_range *range, const char *header) { - diff_output_info *info = priv; - - if (len == 1 && info->hunk_cb) { - git_diff_range range = { -1, 0, -1, 0 }; - const char *scan = bufs[0].ptr; + /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ + if (*header != '@') + return -1; + if (read_next_int(&header, &range->old_start) < 0) + return -1; + if (*header == ',') { + if (read_next_int(&header, &range->old_lines) < 0) + return -1; + } else + range->old_lines = 1; + if (read_next_int(&header, &range->new_start) < 0) + return -1; + if (*header == ',') { + if (read_next_int(&header, &range->new_lines) < 0) + return -1; + } else + range->new_lines = 1; + if (range->old_start < 0 || range->new_start < 0) + return -1; - /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ - if (*scan != '@') - 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)); + return 0; +} - if (info->hunk_cb( - info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size)) - info->error = GIT_EUSER; - } +static int format_hunk_header(char *header, size_t len, git_diff_range *range) +{ + if (range->old_lines != 1) { + if (range->new_lines != 1) + return p_snprintf( + header, len, "@@ -%d,%d +%d,%d @@", + range->old_start, range->old_lines, + range->new_start, range->new_lines); + else + return p_snprintf( + header, len, "@@ -%d,%d +%d @@", + range->old_start, range->old_lines, range->new_start); + } else { + if (range->new_lines != 1) + return p_snprintf( + header, len, "@@ -%d +%d,%d @@", + range->old_start, range->new_start, range->new_lines); + else + return p_snprintf( + header, len, "@@ -%d +%d @@", + range->old_start, range->new_start); } +} - if ((len == 2 || len == 3) && info->line_cb) { - int origin; - - /* expect " "/"-"/"+", then data, then maybe newline */ - origin = - (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : - (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : - GIT_DIFF_LINE_CONTEXT; +static bool diff_delta_is_ambiguous(git_diff_delta *delta) +{ + return (git_oid_iszero(&delta->new_file.oid) && + (delta->new_file.flags & GIT_DIFF_FILE_VALID_OID) == 0 && + delta->status == GIT_DELTA_MODIFIED); +} - if (info->line_cb( - info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size)) - info->error = GIT_EUSER; +static bool diff_delta_should_skip(git_diff_options *opts, git_diff_delta *delta) +{ + if (delta->status == GIT_DELTA_UNMODIFIED && + (opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) + return true; - /* 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. - */ - else if (len == 3) { - origin = (origin == GIT_DIFF_LINE_ADDITION) ? - GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL; + if (delta->status == GIT_DELTA_IGNORED && + (opts->flags & GIT_DIFF_INCLUDE_IGNORED) == 0) + return true; - if (info->line_cb( - info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size)) - info->error = GIT_EUSER; - } - } + if (delta->status == GIT_DELTA_UNTRACKED && + (opts->flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) + return true; - return info->error; + return false; } #define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY) -static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file) +static int update_file_is_binary_by_attr( + git_repository *repo, git_diff_file *file) { const char *value; + + if (!repo) + return 0; + if (git_attr_get(&value, repo, 0, file->path, "diff") < 0) return -1; @@ -129,11 +166,10 @@ static void update_delta_is_binary(git_diff_delta *delta) /* otherwise leave delta->binary value untouched */ } -static int file_is_binary_by_attr( - git_diff_list *diff, - git_diff_delta *delta) +static int diff_delta_is_binary_by_attr(diff_delta_context *ctxt) { int error = 0, mirror_new; + git_diff_delta *delta = ctxt->delta; delta->binary = -1; @@ -148,7 +184,7 @@ static int file_is_binary_by_attr( } /* check if user is forcing us to text diff these files */ - if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) { + if (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) { delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY; delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY; delta->binary = 0; @@ -156,7 +192,7 @@ static int file_is_binary_by_attr( } /* check diff attribute +, -, or 0 */ - if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0) + if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0) return -1; mirror_new = (delta->new_file.path == delta->old_file.path || @@ -164,23 +200,21 @@ static int file_is_binary_by_attr( if (mirror_new) delta->new_file.flags |= (delta->old_file.flags & BINARY_DIFF_FLAGS); else - error = update_file_is_binary_by_attr(diff->repo, &delta->new_file); + error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file); update_delta_is_binary(delta); return error; } -static int file_is_binary_by_content( - git_diff_delta *delta, - git_map *old_data, - git_map *new_data) +static int diff_delta_is_binary_by_content(diff_delta_context *ctxt) { + git_diff_delta *delta = ctxt->delta; git_buf search; if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) { - search.ptr = old_data->data; - search.size = min(old_data->len, 4000); + search.ptr = ctxt->old_data.data; + search.size = min(ctxt->old_data.len, 4000); if (git_buf_is_binary(&search)) delta->old_file.flags |= GIT_DIFF_FILE_BINARY; @@ -189,8 +223,8 @@ static int file_is_binary_by_content( } if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) { - search.ptr = new_data->data; - search.size = min(new_data->len, 4000); + search.ptr = ctxt->new_data.data; + search.size = min(ctxt->new_data.len, 4000); if (git_buf_is_binary(&search)) delta->new_file.flags |= GIT_DIFF_FILE_BINARY; @@ -256,16 +290,21 @@ static int get_workdir_content( return -1; if (S_ISLNK(file->mode)) { - ssize_t read_len; + ssize_t alloc_len, read_len; file->flags |= GIT_DIFF_FILE_FREE_DATA; file->flags |= GIT_DIFF_FILE_BINARY; - map->data = git__malloc((size_t)file->size + 1); + /* link path on disk could be UTF-16, so prepare a buffer that is + * big enough to handle some UTF-8 data expansion + */ + alloc_len = (ssize_t)(file->size * 2) + 1; + + map->data = git__malloc(alloc_len); GITERR_CHECK_ALLOC(map->data); - read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1); - if (read_len != (ssize_t)file->size) { + read_len = p_readlink(path.ptr, map->data, (int)alloc_len); + if (read_len < 0) { giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path); error = -1; } else @@ -286,189 +325,304 @@ static void release_content(git_diff_file *file, git_map *map, git_blob *blob) if (file->flags & GIT_DIFF_FILE_FREE_DATA) { git__free(map->data); - map->data = NULL; + map->data = ""; + map->len = 0; file->flags &= ~GIT_DIFF_FILE_FREE_DATA; } else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) { git_futils_mmap_free(map); - map->data = NULL; + map->data = ""; + map->len = 0; file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA; } } -static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) { - assert(dst && src); +static void diff_delta_init_context( + diff_delta_context *ctxt, + git_repository *repo, + git_diff_options *opts, + git_iterator_type_t old_src, + git_iterator_type_t new_src) +{ + memset(ctxt, 0, sizeof(diff_delta_context)); + + ctxt->repo = repo; + ctxt->opts = opts; + ctxt->old_src = old_src; + ctxt->new_src = new_src; - dst->data = src->ptr; - dst->len = src->size; -#ifdef GIT_WIN32 - dst->fmh = NULL; -#endif + setup_xdiff_options(opts, &ctxt->xdiff_config, &ctxt->xdiff_params); } -int git_diff_foreach( - git_diff_list *diff, - void *data, - git_diff_file_fn file_cb, - git_diff_hunk_fn hunk_cb, - git_diff_data_fn line_cb) +static void diff_delta_init_context_from_diff_list( + diff_delta_context *ctxt, + git_diff_list *diff) { - int error = 0; - diff_output_info info; - git_diff_delta *delta; - xpparam_t xdiff_params; - xdemitconf_t xdiff_config; - xdemitcb_t xdiff_callback; + diff_delta_init_context( + ctxt, diff->repo, &diff->opts, diff->old_src, diff->new_src); +} - memset(&info, 0, sizeof(info)); - info.diff = diff; - info.cb_data = data; - info.hunk_cb = hunk_cb; - info.line_cb = line_cb; +static void diff_delta_unload(diff_delta_context *ctxt) +{ + ctxt->diffed = 0; - setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params); - memset(&xdiff_callback, 0, sizeof(xdiff_callback)); - xdiff_callback.outf = diff_output_cb; - xdiff_callback.priv = &info; + if (ctxt->loaded) { + release_content(&ctxt->delta->old_file, &ctxt->old_data, ctxt->old_blob); + release_content(&ctxt->delta->new_file, &ctxt->new_data, ctxt->new_blob); + ctxt->loaded = 0; + } - git_vector_foreach(&diff->deltas, info.index, delta) { - git_blob *old_blob = NULL, *new_blob = NULL; - git_map old_data, new_data; - mmfile_t old_xdiff_data, new_xdiff_data; + ctxt->delta = NULL; + ctxt->prepped = 0; +} - if (delta->status == GIT_DELTA_UNMODIFIED && - (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) - continue; +static int diff_delta_prep(diff_delta_context *ctxt) +{ + int error; - if (delta->status == GIT_DELTA_IGNORED && - (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0) - continue; + if (ctxt->prepped || !ctxt->delta) + return 0; - if (delta->status == GIT_DELTA_UNTRACKED && - (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) - continue; + error = diff_delta_is_binary_by_attr(ctxt); - if ((error = file_is_binary_by_attr(diff, delta)) < 0) - goto cleanup; + ctxt->prepped = !error; - old_data.data = ""; - old_data.len = 0; - new_data.data = ""; - new_data.len = 0; + return error; +} - /* TODO: Partial blob reading to defer loading whole blob. - * I.e. I want a blob with just the first 4kb loaded, then - * later on I will read the rest of the blob if needed. - */ +static int diff_delta_load(diff_delta_context *ctxt) +{ + int error = 0; + git_diff_delta *delta = ctxt->delta; - /* map files */ - if (delta->binary != 1 && - (hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) && - (delta->status == GIT_DELTA_DELETED || - delta->status == GIT_DELTA_MODIFIED)) - { - if (diff->old_src == GIT_ITERATOR_WORKDIR) - error = get_workdir_content(diff->repo, &delta->old_file, &old_data); - else - error = get_blob_content( - diff->repo, &delta->old_file.oid, &old_data, &old_blob); + if (ctxt->loaded || !ctxt->delta) + return 0; - if (error < 0) - goto cleanup; + if (!ctxt->prepped && (error = diff_delta_prep(ctxt)) < 0) + goto cleanup; + + ctxt->old_data.data = ""; + ctxt->old_data.len = 0; + ctxt->old_blob = NULL; + + if (!error && delta->binary != 1 && + (delta->status == GIT_DELTA_DELETED || + delta->status == GIT_DELTA_MODIFIED)) + { + if (ctxt->old_src == GIT_ITERATOR_WORKDIR) + error = get_workdir_content( + ctxt->repo, &delta->old_file, &ctxt->old_data); + else { + error = get_blob_content( + ctxt->repo, &delta->old_file.oid, + &ctxt->old_data, &ctxt->old_blob); + + if (ctxt->new_src == GIT_ITERATOR_WORKDIR) { + /* TODO: convert crlf of blob content */ + } } + } - if (delta->binary != 1 && - (hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) && - (delta->status == GIT_DELTA_ADDED || - delta->status == GIT_DELTA_MODIFIED)) - { - if (diff->new_src == GIT_ITERATOR_WORKDIR) - error = get_workdir_content(diff->repo, &delta->new_file, &new_data); - else - error = get_blob_content( - diff->repo, &delta->new_file.oid, &new_data, &new_blob); + ctxt->new_data.data = ""; + ctxt->new_data.len = 0; + ctxt->new_blob = NULL; + + if (!error && delta->binary != 1 && + (delta->status == GIT_DELTA_ADDED || + delta->status == GIT_DELTA_MODIFIED)) + { + if (ctxt->new_src == GIT_ITERATOR_WORKDIR) + error = get_workdir_content( + ctxt->repo, &delta->new_file, &ctxt->new_data); + else { + error = get_blob_content( + ctxt->repo, &delta->new_file.oid, + &ctxt->new_data, &ctxt->new_blob); + if (ctxt->old_src == GIT_ITERATOR_WORKDIR) { + /* TODO: convert crlf of blob content */ + } + } + + if (!error && !(delta->new_file.flags & GIT_DIFF_FILE_VALID_OID)) { + error = git_odb_hash( + &delta->new_file.oid, ctxt->new_data.data, + ctxt->new_data.len, GIT_OBJ_BLOB); if (error < 0) goto cleanup; - if ((delta->new_file.flags & GIT_DIFF_FILE_VALID_OID) == 0) { - error = git_odb_hash( - &delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB); + delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID; - if (error < 0) + /* since we did not have the definitive oid, we may have + * incorrect status and need to skip this item. + */ + if (delta->old_file.mode == delta->new_file.mode && + !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) + { + delta->status = GIT_DELTA_UNMODIFIED; + + if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) goto cleanup; - delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID; - - /* since we did not have the definitive oid, we may have - * incorrect status and need to skip this item. - */ - if (delta->old_file.mode == delta->new_file.mode && - !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) - { - delta->status = GIT_DELTA_UNMODIFIED; - if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) - goto cleanup; - } } } + } + + /* if we have not already decided whether file is binary, + * check the first 4K for nul bytes to decide... + */ + if (!error && delta->binary == -1) + error = diff_delta_is_binary_by_content(ctxt); + +cleanup: + ctxt->loaded = !error; + + /* flag if we would want to diff the contents of these files */ + if (ctxt->loaded) + ctxt->diffable = + (delta->binary != 1 && + delta->status != GIT_DELTA_UNMODIFIED && + (ctxt->old_data.len || ctxt->new_data.len) && + git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)); + + return error; +} + +static int diff_delta_cb(void *priv, mmbuffer_t *bufs, int len) +{ + diff_delta_context *ctxt = priv; + + if (len == 1) { + if ((ctxt->cb_error = parse_hunk_header(&ctxt->range, bufs[0].ptr)) < 0) + return ctxt->cb_error; + + if (ctxt->per_hunk != NULL && + ctxt->per_hunk(ctxt->cb_data, ctxt->delta, &ctxt->range, + bufs[0].ptr, bufs[0].size)) + ctxt->cb_error = GIT_EUSER; + } - /* if we have not already decided whether file is binary, - * check the first 4K for nul bytes to decide... + if (len == 2 || len == 3) { + /* expect " "/"-"/"+", then data */ + char origin = + (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : + (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : + GIT_DIFF_LINE_CONTEXT; + + if (ctxt->per_line != NULL && + ctxt->per_line(ctxt->cb_data, ctxt->delta, &ctxt->range, origin, + bufs[1].ptr, bufs[1].size)) + ctxt->cb_error = GIT_EUSER; + } + + if (len == 3 && !ctxt->cb_error) { + /* 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 (delta->binary == -1) { - error = file_is_binary_by_content( - delta, &old_data, &new_data); - if (error < 0) + char origin = + (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : + (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : + GIT_DIFF_LINE_CONTEXT; + + if (ctxt->per_line != NULL && + ctxt->per_line(ctxt->cb_data, ctxt->delta, &ctxt->range, origin, + bufs[2].ptr, bufs[2].size)) + ctxt->cb_error = GIT_EUSER; + } + + return ctxt->cb_error; +} + +static int diff_delta_exec( + diff_delta_context *ctxt, + void *cb_data, + git_diff_hunk_fn per_hunk, + git_diff_data_fn per_line) +{ + int error = 0; + xdemitcb_t xdiff_callback; + mmfile_t old_xdiff_data, new_xdiff_data; + + if (ctxt->diffed || !ctxt->delta) + return 0; + + if (!ctxt->loaded && (error = diff_delta_load(ctxt)) < 0) + goto cleanup; + + if (!ctxt->diffable) + return 0; + + ctxt->cb_data = cb_data; + ctxt->per_hunk = per_hunk; + ctxt->per_line = per_line; + ctxt->cb_error = 0; + + memset(&xdiff_callback, 0, sizeof(xdiff_callback)); + xdiff_callback.outf = diff_delta_cb; + xdiff_callback.priv = ctxt; + + old_xdiff_data.ptr = ctxt->old_data.data; + old_xdiff_data.size = ctxt->old_data.len; + new_xdiff_data.ptr = ctxt->new_data.data; + new_xdiff_data.size = ctxt->new_data.len; + + xdl_diff(&old_xdiff_data, &new_xdiff_data, + &ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback); + + error = ctxt->cb_error; + +cleanup: + ctxt->diffed = !error; + + return error; +} + +int git_diff_foreach( + git_diff_list *diff, + void *data, + git_diff_file_fn file_cb, + git_diff_hunk_fn hunk_cb, + git_diff_data_fn line_cb) +{ + int error = 0; + diff_delta_context ctxt; + size_t idx; + + diff_delta_init_context_from_diff_list(&ctxt, diff); + + git_vector_foreach(&diff->deltas, idx, ctxt.delta) { + if (diff_delta_is_ambiguous(ctxt.delta)) + if ((error = diff_delta_load(&ctxt)) < 0) goto cleanup; - } - /* TODO: if ignore_whitespace is set, then we *must* do text - * diffs to tell if a file has really been changed. - */ + if (diff_delta_should_skip(ctxt.opts, ctxt.delta)) + continue; + + if ((error = diff_delta_load(&ctxt)) < 0) + goto cleanup; if (file_cb != NULL && - file_cb(data, delta, (float)info.index / diff->deltas.length)) + file_cb(data, ctxt.delta, (float)idx / diff->deltas.length) != 0) { error = GIT_EUSER; goto cleanup; } - /* don't do hunk and line diffs if file is binary */ - if (delta->binary == 1) - goto cleanup; - - /* nothing to do if we did not get data */ - if (!old_data.len && !new_data.len) - goto cleanup; - - /* nothing to do if only diff was a mode change */ - if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) - goto cleanup; - - assert(hunk_cb || line_cb); - - info.delta = delta; - old_xdiff_data.ptr = old_data.data; - old_xdiff_data.size = old_data.len; - new_xdiff_data.ptr = new_data.data; - new_xdiff_data.size = new_data.len; - - xdl_diff(&old_xdiff_data, &new_xdiff_data, - &xdiff_params, &xdiff_config, &xdiff_callback); - error = info.error; + error = diff_delta_exec(&ctxt, data, hunk_cb, line_cb); cleanup: - release_content(&delta->old_file, &old_data, old_blob); - release_content(&delta->new_file, &new_data, new_blob); + diff_delta_unload(&ctxt); if (error < 0) break; } + if (error == GIT_EUSER) + giterr_clear(); + return error; } - typedef struct { git_diff_list *diff; git_diff_data_fn print_cb; @@ -531,7 +685,10 @@ static int print_compact(void *data, git_diff_delta *delta, float progress) if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + { + giterr_clear(); return GIT_EUSER; + } return 0; } @@ -628,7 +785,10 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress) return -1; if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + { + giterr_clear(); return GIT_EUSER; + } if (delta->binary != 1) return 0; @@ -642,7 +802,10 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress) if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + { + giterr_clear(); return GIT_EUSER; + } return 0; } @@ -662,7 +825,10 @@ static int print_patch_hunk( if (pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + { + giterr_clear(); return GIT_EUSER; + } return 0; } @@ -691,7 +857,10 @@ static int print_patch_line( if (pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf))) + { + giterr_clear(); return GIT_EUSER; + } return 0; } @@ -726,17 +895,36 @@ int git_diff_entrycount(git_diff_list *diff, int delta_t) assert(diff); - if (delta_t < 0) - return diff->deltas.length; - git_vector_foreach(&diff->deltas, i, delta) { - if (delta->status == (git_delta_t)delta_t) + if (diff_delta_should_skip(&diff->opts, delta)) + continue; + + if (delta_t < 0 || delta->status == (git_delta_t)delta_t) count++; } + /* It is possible that this has overcounted the number of diffs because + * there may be entries that are marked as MODIFIED due to differences + * in stat() output that will turn out to be the same once we calculate + * the actual SHA of the data on disk. + */ + return count; } +static void set_data_from_blob( + git_blob *blob, git_map *map, git_diff_file *file) +{ + if (blob) { + map->data = (char *)git_blob_rawcontent(blob); + file->size = map->len = git_blob_rawsize(blob); + git_oid_cpy(&file->oid, git_object_id((const git_object *)blob)); + } else { + map->data = ""; + file->size = map->len = 0; + } +} + int git_diff_blobs( git_blob *old_blob, git_blob *new_blob, @@ -746,17 +934,11 @@ int git_diff_blobs( git_diff_hunk_fn hunk_cb, git_diff_data_fn line_cb) { - diff_output_info info; + int error; + diff_delta_context ctxt; git_diff_delta delta; - mmfile_t old_data, new_data; - git_map old_map, new_map; - xpparam_t xdiff_params; - xdemitconf_t xdiff_config; - xdemitcb_t xdiff_callback; git_blob *new, *old; - memset(&delta, 0, sizeof(delta)); - new = new_blob; old = old_blob; @@ -766,25 +948,16 @@ int git_diff_blobs( new = swap; } - if (old) { - old_data.ptr = (char *)git_blob_rawcontent(old); - old_data.size = git_blob_rawsize(old); - git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old)); - } else { - old_data.ptr = ""; - old_data.size = 0; - } - - if (new) { - new_data.ptr = (char *)git_blob_rawcontent(new); - new_data.size = git_blob_rawsize(new); - git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new)); - } else { - new_data.ptr = ""; - new_data.size = 0; - } + diff_delta_init_context( + &ctxt, NULL, options, GIT_ITERATOR_TREE, GIT_ITERATOR_TREE); /* populate a "fake" delta record */ + + memset(&delta, 0, sizeof(delta)); + + set_data_from_blob(old, &ctxt.old_data, &delta.old_file); + set_data_from_blob(new, &ctxt.new_data, &delta.new_file); + delta.status = new ? (old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); @@ -792,39 +965,369 @@ int git_diff_blobs( if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0) delta.status = GIT_DELTA_UNMODIFIED; - delta.old_file.size = old_data.size; - delta.new_file.size = new_data.size; + ctxt.delta = δ + + if ((error = diff_delta_prep(&ctxt)) < 0) + goto cleanup; + + if (delta.binary == -1 && + (error = diff_delta_is_binary_by_content(&ctxt)) < 0) + goto cleanup; + + ctxt.loaded = 1; + ctxt.diffable = (delta.binary != 1 && delta.status != GIT_DELTA_UNMODIFIED); + + /* do diffs */ + + if (file_cb != NULL && file_cb(cb_data, &delta, 1)) { + error = GIT_EUSER; + goto cleanup; + } + + error = diff_delta_exec(&ctxt, cb_data, hunk_cb, line_cb); - fill_map_from_mmfile(&old_map, &old_data); - fill_map_from_mmfile(&new_map, &new_data); +cleanup: + if (error == GIT_EUSER) + giterr_clear(); + + diff_delta_unload(&ctxt); + + return error; +} + +typedef struct diffiter_line diffiter_line; +struct diffiter_line { + diffiter_line *next; + char origin; + const char *ptr; + size_t len; +}; + +typedef struct diffiter_hunk diffiter_hunk; +struct diffiter_hunk { + diffiter_hunk *next; + git_diff_range range; + diffiter_line *line_head; + size_t line_count; +}; + +struct git_diff_iterator { + git_diff_list *diff; + diff_delta_context ctxt; + size_t file_index; + size_t next_index; + size_t file_count; + git_pool hunks; + size_t hunk_count; + diffiter_hunk *hunk_head; + diffiter_hunk *hunk_curr; + char hunk_header[128]; + git_pool lines; + diffiter_line *line_curr; +}; + +typedef struct { + git_diff_iterator *iter; + diffiter_hunk *last_hunk; + diffiter_line *last_line; +} diffiter_cb_info; - if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0) +static int diffiter_hunk_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + const char *header, + size_t header_len) +{ + diffiter_cb_info *info = cb_data; + git_diff_iterator *iter = info->iter; + diffiter_hunk *hunk; + + GIT_UNUSED(delta); + GIT_UNUSED(header); + GIT_UNUSED(header_len); + + if ((hunk = git_pool_mallocz(&iter->hunks, 1)) == NULL) { + iter->ctxt.cb_error = -1; return -1; + } - if (file_cb != NULL && file_cb(cb_data, &delta, 1)) - return GIT_EUSER; + if (info->last_hunk) + info->last_hunk->next = hunk; + info->last_hunk = hunk; - /* don't do hunk and line diffs if the two blobs are identical */ - if (delta.status == GIT_DELTA_UNMODIFIED) - return 0; + memcpy(&hunk->range, range, sizeof(hunk->range)); - /* don't do hunk and line diffs if file is binary */ - if (delta.binary == 1) + iter->hunk_count++; + + if (iter->hunk_head == NULL) + iter->hunk_curr = iter->hunk_head = hunk; + + return 0; +} + +static int diffiter_line_cb( + void *cb_data, + git_diff_delta *delta, + git_diff_range *range, + char line_origin, + const char *content, + size_t content_len) +{ + diffiter_cb_info *info = cb_data; + git_diff_iterator *iter = info->iter; + diffiter_line *line; + + GIT_UNUSED(delta); + GIT_UNUSED(range); + + if ((line = git_pool_mallocz(&iter->lines, 1)) == NULL) { + iter->ctxt.cb_error = -1; + return -1; + } + + if (info->last_line) + info->last_line->next = line; + info->last_line = line; + + line->origin = line_origin; + line->ptr = content; + line->len = content_len; + + info->last_hunk->line_count++; + + if (info->last_hunk->line_head == NULL) + info->last_hunk->line_head = line; + + return 0; +} + +static int diffiter_do_diff_file(git_diff_iterator *iter) +{ + int error; + diffiter_cb_info info; + + if (iter->ctxt.diffed || !iter->ctxt.delta) return 0; memset(&info, 0, sizeof(info)); - info.diff = NULL; - info.delta = δ - info.cb_data = cb_data; - info.hunk_cb = hunk_cb; - info.line_cb = line_cb; + info.iter = iter; - setup_xdiff_options(options, &xdiff_config, &xdiff_params); - memset(&xdiff_callback, 0, sizeof(xdiff_callback)); - xdiff_callback.outf = diff_output_cb; - xdiff_callback.priv = &info; + error = diff_delta_exec( + &iter->ctxt, &info, diffiter_hunk_cb, diffiter_line_cb); - xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback); + if (error == GIT_EUSER) + error = iter->ctxt.cb_error; + + return error; +} + +static void diffiter_do_unload_file(git_diff_iterator *iter) +{ + if (iter->ctxt.loaded) { + diff_delta_unload(&iter->ctxt); - return info.error; + git_pool_clear(&iter->lines); + git_pool_clear(&iter->hunks); + } + + iter->ctxt.delta = NULL; + iter->hunk_head = NULL; + iter->hunk_count = 0; +} + +int git_diff_iterator_new( + git_diff_iterator **iterator_ptr, + git_diff_list *diff) +{ + size_t i; + git_diff_delta *delta; + git_diff_iterator *iter; + + assert(diff && iterator_ptr); + + *iterator_ptr = NULL; + + iter = git__malloc(sizeof(git_diff_iterator)); + GITERR_CHECK_ALLOC(iter); + + memset(iter, 0, sizeof(*iter)); + + iter->diff = diff; + GIT_REFCOUNT_INC(iter->diff); + + diff_delta_init_context_from_diff_list(&iter->ctxt, diff); + + if (git_pool_init(&iter->hunks, sizeof(diffiter_hunk), 0) < 0 || + git_pool_init(&iter->lines, sizeof(diffiter_line), 0) < 0) + goto fail; + + git_vector_foreach(&diff->deltas, i, delta) { + if (diff_delta_should_skip(iter->ctxt.opts, delta)) + continue; + iter->file_count++; + } + + *iterator_ptr = iter; + + return 0; + +fail: + git_diff_iterator_free(iter); + + return -1; +} + +void git_diff_iterator_free(git_diff_iterator *iter) +{ + diffiter_do_unload_file(iter); + git_diff_list_free(iter->diff); /* decrement ref count */ + git__free(iter); +} + +int git_diff_iterator_num_files(git_diff_iterator *iter) +{ + return (int)iter->file_count; +} + +int git_diff_iterator_num_hunks_in_file(git_diff_iterator *iter) +{ + int error = diffiter_do_diff_file(iter); + return (error != 0) ? error : (int)iter->hunk_count; +} + +int git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iter) +{ + int error = diffiter_do_diff_file(iter); + if (!error && iter->hunk_curr) + error = iter->hunk_curr->line_count; + return error; +} + +int git_diff_iterator_next_file( + git_diff_delta **delta_ptr, + git_diff_iterator *iter) +{ + int error = 0; + + assert(iter); + + iter->file_index = iter->next_index; + + diffiter_do_unload_file(iter); + + while (!error) { + iter->ctxt.delta = git_vector_get(&iter->diff->deltas, iter->file_index); + if (!iter->ctxt.delta) { + error = GIT_ITEROVER; + break; + } + + if (diff_delta_is_ambiguous(iter->ctxt.delta) && + (error = diff_delta_load(&iter->ctxt)) < 0) + break; + + if (!diff_delta_should_skip(iter->ctxt.opts, iter->ctxt.delta)) + break; + + iter->file_index++; + } + + if (!error) { + iter->next_index = iter->file_index + 1; + + error = diff_delta_prep(&iter->ctxt); + } + + if (iter->ctxt.delta == NULL) { + iter->hunk_curr = NULL; + iter->line_curr = NULL; + } + + if (delta_ptr != NULL) + *delta_ptr = !error ? iter->ctxt.delta : NULL; + + return error; +} + +int git_diff_iterator_next_hunk( + git_diff_range **range_ptr, + const char **header, + size_t *header_len, + git_diff_iterator *iter) +{ + int error = diffiter_do_diff_file(iter); + git_diff_range *range; + + if (error) + return error; + + if (iter->hunk_curr == NULL) { + if (range_ptr) *range_ptr = NULL; + if (header) *header = NULL; + if (header_len) *header_len = 0; + iter->line_curr = NULL; + return GIT_ITEROVER; + } + + range = &iter->hunk_curr->range; + + if (range_ptr) + *range_ptr = range; + + if (header) { + int out = format_hunk_header( + iter->hunk_header, sizeof(iter->hunk_header), range); + + /* TODO: append function name to header */ + + *(iter->hunk_header + out++) = '\n'; + + *header = iter->hunk_header; + + if (header_len) + *header_len = (size_t)out; + } + + iter->line_curr = iter->hunk_curr->line_head; + iter->hunk_curr = iter->hunk_curr->next; + + return error; +} + +int git_diff_iterator_next_line( + char *line_origin, /**< GIT_DIFF_LINE_... value from above */ + const char **content_ptr, + size_t *content_len, + git_diff_iterator *iter) +{ + int error = diffiter_do_diff_file(iter); + + if (error) + return error; + + /* if the user has not called next_hunk yet, call it implicitly (OK?) */ + if (iter->hunk_curr == iter->hunk_head) { + error = git_diff_iterator_next_hunk(NULL, NULL, NULL, iter); + if (error) + return error; + } + + if (iter->line_curr == NULL) { + if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT; + if (content_ptr) *content_ptr = NULL; + if (content_len) *content_len = 0; + return GIT_ITEROVER; + } + + if (line_origin) + *line_origin = iter->line_curr->origin; + if (content_ptr) + *content_ptr = iter->line_curr->ptr; + if (content_len) + *content_len = iter->line_curr->len; + + iter->line_curr = iter->line_curr->next; + + return error; } diff --git a/src/fetch.c b/src/fetch.c index 278ba3c5..98e1f0b1 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -221,7 +221,7 @@ int git_fetch_negotiate(git_remote *remote) } } - if (error < 0 && error != GIT_REVWALKOVER) + if (error < 0 && error != GIT_ITEROVER) goto on_error; /* Tell the other end that we're done negotiating */ diff --git a/src/fileops.c b/src/fileops.c index 76ef8c91..95eacb5f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -54,11 +54,10 @@ int git_futils_creat_locked(const char *path, const mode_t mode) int fd; #ifdef GIT_WIN32 - wchar_t* buf; + wchar_t buf[GIT_WIN_PATH]; - buf = gitwin_to_utf16(path); + git__utf8_to_16(buf, GIT_WIN_PATH, path); fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); - git__free(buf); #else fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); #endif @@ -382,16 +381,16 @@ static int win32_expand_path(struct win32_path *s_root, const wchar_t *templ) static int win32_find_file(git_buf *path, const struct win32_path *root, const char *filename) { - int error = 0; - size_t len; + size_t len, alloc_len; wchar_t *file_utf16 = NULL; - char *file_utf8 = NULL; + char file_utf8[GIT_PATH_MAX]; if (!root || !filename || (len = strlen(filename)) == 0) return GIT_ENOTFOUND; /* allocate space for wchar_t path to file */ - file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t)); + alloc_len = root->len + len + 2; + file_utf16 = git__calloc(alloc_len, sizeof(wchar_t)); GITERR_CHECK_ALLOC(file_utf16); /* append root + '\\' + filename as wchar_t */ @@ -400,29 +399,20 @@ static int win32_find_file(git_buf *path, const struct win32_path *root, const c if (*filename == '/' || *filename == '\\') filename++; - if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) != - (int)len + 1) { - error = -1; - goto cleanup; - } + git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename); /* check access */ if (_waccess(file_utf16, F_OK) < 0) { - error = GIT_ENOTFOUND; - goto cleanup; + git__free(file_utf16); + return GIT_ENOTFOUND; } - /* convert to utf8 */ - if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL) - error = -1; - else { - git_path_mkposix(file_utf8); - git_buf_attach(path, file_utf8, 0); - } + git__utf16_to_8(file_utf8, file_utf16); + git_path_mkposix(file_utf8); + git_buf_sets(path, file_utf8); -cleanup: git__free(file_utf16); - return error; + return 0; } #endif diff --git a/src/netops.c b/src/netops.c index 49a0308b..df502e61 100644 --- a/src/netops.c +++ b/src/netops.c @@ -55,8 +55,44 @@ static void net_set_error(const char *str) static int ssl_set_error(gitno_ssl *ssl, int error) { int err; + unsigned long e; + err = SSL_get_error(ssl->ssl, error); - giterr_set(GITERR_NET, "SSL error: %s", ERR_error_string(err, NULL)); + + assert(err != SSL_ERROR_WANT_READ); + assert(err != SSL_ERROR_WANT_WRITE); + + switch (err) { + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + giterr_set(GITERR_NET, "SSL error: connection failure\n"); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + giterr_set(GITERR_NET, "SSL error: x509 error\n"); + break; + case SSL_ERROR_SYSCALL: + e = ERR_get_error(); + if (e > 0) { + giterr_set(GITERR_NET, "SSL error: %s", + ERR_error_string(e, NULL)); + break; + } else if (error < 0) { + giterr_set(GITERR_OS, "SSL error: syscall failure"); + break; + } + giterr_set(GITERR_NET, "SSL error: received early EOF"); + break; + case SSL_ERROR_SSL: + e = ERR_get_error(); + giterr_set(GITERR_NET, "SSL error: %s", + ERR_error_string(e, NULL)); + break; + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + default: + giterr_set(GITERR_NET, "SSL error: unknown error"); + break; + } return -1; } #endif @@ -238,6 +274,10 @@ static int verify_server_cert(git_transport *t, const char *host) void *addr; int i = -1,j; + if (SSL_get_verify_result(t->ssl.ssl) != X509_V_OK) { + giterr_set(GITERR_SSL, "The SSL certificate is invalid"); + return -1; + } /* Try to parse the host as an IP address to see if it is */ if (inet_pton(AF_INET, host, &addr4)) { @@ -286,7 +326,7 @@ static int verify_server_cert(git_transport *t, const char *host) GENERAL_NAMES_free(alts); if (matched == 0) - goto on_error; + goto cert_fail; if (matched == 1) return 0; @@ -354,7 +394,7 @@ static int ssl_setup(git_transport *t, const char *host) return ssl_set_error(&t->ssl, 0); SSL_CTX_set_mode(t->ssl.ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(t->ssl.ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_verify(t->ssl.ctx, SSL_VERIFY_NONE, NULL); if (!SSL_CTX_set_default_verify_paths(t->ssl.ctx)) return ssl_set_error(&t->ssl, 0); @@ -438,7 +478,7 @@ static int send_ssl(gitno_ssl *ssl, const char *msg, size_t len) while (off < len) { ret = SSL_write(ssl->ssl, msg + off, len - off); - if (ret <= 0) + if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) return ssl_set_error(ssl, ret); off += ret; diff --git a/src/object.c b/src/object.c index 22777404..5130d97a 100644 --- a/src/object.c +++ b/src/object.c @@ -334,6 +334,12 @@ int git_object__resolve_to_type(git_object **obj, git_otype type) return error; } +static int peel_error(int error, const char* msg) +{ + giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg); + return error; +} + static int dereference_object(git_object **dereferenced, git_object *obj) { git_otype type = git_object_type(obj); @@ -341,48 +347,36 @@ static int dereference_object(git_object **dereferenced, git_object *obj) switch (type) { case GIT_OBJ_COMMIT: return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj); - break; case GIT_OBJ_TAG: return git_tag_target(dereferenced, (git_tag*)obj); - break; + + case GIT_OBJ_BLOB: + return peel_error(GIT_ERROR, "cannot dereference blob"); + + case GIT_OBJ_TREE: + return peel_error(GIT_ERROR, "cannot dereference tree"); default: - return GIT_ENOTFOUND; - break; + return peel_error(GIT_ENOTFOUND, "unexpected object type encountered"); } } -static int peel_error(int error, const char* msg) -{ - giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg); - return error; -} - int git_object_peel( - git_object **peeled, - git_object *object, - git_otype target_type) + git_object **peeled, + git_object *object, + git_otype target_type) { git_object *source, *deref = NULL; - assert(object); + assert(object && peeled); if (git_object_type(object) == target_type) return git_object__dup(peeled, object); - if (target_type == GIT_OBJ_BLOB - || target_type == GIT_OBJ_ANY) - return peel_error(GIT_EAMBIGUOUS, "Ambiguous target type"); - - if (git_object_type(object) == GIT_OBJ_BLOB) - return peel_error(GIT_ERROR, "A blob cannot be dereferenced"); - source = object; - while (true) { - if (dereference_object(&deref, source) < 0) - goto cleanup; + while (!dereference_object(&deref, source)) { if (source != object) git_object_free(source); @@ -392,13 +386,20 @@ int git_object_peel( return 0; } + if (target_type == GIT_OBJ_ANY && + git_object_type(deref) != git_object_type(object)) + { + *peeled = deref; + return 0; + } + source = deref; deref = NULL; } -cleanup: if (source != object) git_object_free(source); + git_object_free(deref); return -1; } diff --git a/src/odb_pack.c b/src/odb_pack.c index 8fc6e68e..6e3d3eef 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -435,7 +435,7 @@ static int pack_backend__foreach(git_odb_backend *_backend, int (*cb)(git_oid *o return error; git_vector_foreach(&backend->packs, i, p) { - if ((error = git_pack_foreach_entry(p, cb, &data)) < 0) + if ((error = git_pack_foreach_entry(p, cb, data)) < 0) return error; } @@ -432,14 +432,14 @@ bool git_path_is_empty_dir(const char *path) { git_buf pathbuf = GIT_BUF_INIT; HANDLE hFind = INVALID_HANDLE_VALUE; - wchar_t *wbuf; + wchar_t wbuf[GIT_WIN_PATH]; WIN32_FIND_DATAW ffd; bool retval = true; if (!git_path_isdir(path)) return false; git_buf_printf(&pathbuf, "%s\\*", path); - wbuf = gitwin_to_utf16(git_buf_cstr(&pathbuf)); + git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf)); hFind = FindFirstFileW(wbuf, &ffd); if (INVALID_HANDLE_VALUE == hFind) { @@ -455,7 +455,6 @@ bool git_path_is_empty_dir(const char *path) FindClose(hFind); git_buf_free(&pathbuf); - git__free(wbuf); return retval; } @@ -76,6 +76,17 @@ extern void git_pool_swap(git_pool *a, git_pool *b); extern void *git_pool_malloc(git_pool *pool, uint32_t items); /** + * Allocate space and zero it out. + */ +GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items) +{ + void *ptr = git_pool_malloc(pool, items); + if (ptr) + memset(ptr, 0, (size_t)items * (size_t)pool->item_size); + return ptr; +} + +/** * Allocate space and duplicate string data into it. * * This is allowed only for pools with item_size == sizeof(char) @@ -68,11 +68,6 @@ static int reference_path_available(git_repository *repo, static int reference_delete(git_reference *ref); static int reference_lookup(git_reference *ref); -/* name normalization */ -static int normalize_name(char *buffer_out, size_t out_size, - const char *name, int is_oid_ref); - - void git_reference_free(git_reference *reference) { if (reference == NULL) @@ -1099,9 +1094,12 @@ int git_reference_lookup_resolved( scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char)); GITERR_CHECK_ALLOC(scan->name); - if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) { - git_reference_free(scan); - return result; + if ((result = git_reference__normalize_name( + scan->name, + GIT_REFNAME_MAX, + name)) < 0) { + git_reference_free(scan); + return result; } scan->target.symbolic = git__strdup(scan->name); @@ -1198,8 +1196,11 @@ int git_reference_create_symbolic( char normalized[GIT_REFNAME_MAX]; git_reference *ref = NULL; - if (normalize_name(normalized, sizeof(normalized), name, 0) < 0) - return -1; + if (git_reference__normalize_name( + normalized, + sizeof(normalized), + name) < 0) + return -1; if (reference_can_write(repo, normalized, NULL, force) < 0) return -1; @@ -1234,8 +1235,11 @@ int git_reference_create_oid( git_reference *ref = NULL; char normalized[GIT_REFNAME_MAX]; - if (normalize_name(normalized, sizeof(normalized), name, 1) < 0) - return -1; + if (git_reference__normalize_name_oid( + normalized, + sizeof(normalized), + name) < 0) + return -1; if (reference_can_write(repo, normalized, NULL, force) < 0) return -1; @@ -1314,8 +1318,11 @@ int git_reference_set_target(git_reference *ref, const char *target) return -1; } - if (normalize_name(normalized, sizeof(normalized), target, 0)) - return -1; + if (git_reference__normalize_name( + normalized, + sizeof(normalized), + target)) + return -1; git__free(ref->target.symbolic); ref->target.symbolic = git__strdup(normalized); @@ -1327,15 +1334,23 @@ int git_reference_set_target(git_reference *ref, const char *target) int git_reference_rename(git_reference *ref, const char *new_name, int force) { int result; + unsigned int normalization_flags; git_buf aux_path = GIT_BUF_INIT; char normalized[GIT_REFNAME_MAX]; const char *head_target = NULL; git_reference *head = NULL; - if (normalize_name(normalized, sizeof(normalized), - new_name, ref->flags & GIT_REF_OID) < 0) - return -1; + normalization_flags = ref->flags & GIT_REF_SYMBOLIC ? + GIT_REF_FORMAT_ALLOW_ONELEVEL + : GIT_REF_FORMAT_NORMAL; + + if (git_reference_normalize_name( + normalized, + sizeof(normalized), + new_name, + normalization_flags) < 0) + return -1; if (reference_can_write(ref->owner, normalized, ref->name, force) < 0) return -1; @@ -1565,11 +1580,11 @@ static int is_valid_ref_char(char ch) } } -static int normalize_name( +int git_reference_normalize_name( char *buffer_out, - size_t out_size, + size_t buffer_size, const char *name, - int is_oid_ref) + unsigned int flags) { const char *name_end, *buffer_out_start; const char *current; @@ -1577,12 +1592,17 @@ static int normalize_name( assert(name && buffer_out); + if (flags & GIT_REF_FORMAT_REFSPEC_PATTERN) { + giterr_set(GITERR_INVALID, "Unimplemented"); + return -1; + } + buffer_out_start = buffer_out; current = name; name_end = name + strlen(name); /* Terminating null byte */ - out_size--; + buffer_size--; /* A refname can not be empty */ if (name_end == name) @@ -1592,7 +1612,7 @@ static int normalize_name( if (*(name_end - 1) == '.' || *(name_end - 1) == '/') goto invalid_name; - while (current < name_end && out_size) { + while (current < name_end && buffer_size > 0) { if (!is_valid_ref_char(*current)) goto invalid_name; @@ -1614,20 +1634,30 @@ static int normalize_name( } } - if (*current == '/') - contains_a_slash = 1; + if (*current == '/') { + if (buffer_out > buffer_out_start) + contains_a_slash = 1; + else { + current++; + continue; + } + } *buffer_out++ = *current++; - out_size--; + buffer_size--; } - if (!out_size) - goto invalid_name; + if (current < name_end) { + giterr_set( + GITERR_REFERENCE, + "The provided buffer is too short to hold the normalization of '%s'", name); + return GIT_EBUFS; + } /* Object id refname have to contain at least one slash, except * for HEAD in a detached state or MERGE_HEAD if we're in the * middle of a merge */ - if (is_oid_ref && + if (!(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL) && !contains_a_slash && strcmp(name, GIT_HEAD_FILE) != 0 && strcmp(name, GIT_MERGE_HEAD_FILE) != 0 && @@ -1640,18 +1670,12 @@ static int normalize_name( *buffer_out = '\0'; - /* - * For object id references, name has to start with refs/. Again, - * we need to allow HEAD to be in a detached state. - */ - if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) || - strcmp(buffer_out_start, GIT_HEAD_FILE))) - goto invalid_name; - return 0; invalid_name: - giterr_set(GITERR_REFERENCE, "The given reference name is not valid"); + giterr_set( + GITERR_REFERENCE, + "The given reference name '%s' is not valid", name); return -1; } @@ -1660,7 +1684,11 @@ int git_reference__normalize_name( size_t out_size, const char *name) { - return normalize_name(buffer_out, out_size, name, 0); + return git_reference_normalize_name( + buffer_out, + out_size, + name, + GIT_REF_FORMAT_ALLOW_ONELEVEL); } int git_reference__normalize_name_oid( @@ -1668,7 +1696,11 @@ int git_reference__normalize_name_oid( size_t out_size, const char *name) { - return normalize_name(buffer_out, out_size, name, 1); + return git_reference_normalize_name( + buffer_out, + out_size, + name, + GIT_REF_FORMAT_NORMAL); } #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC) @@ -1804,6 +1836,11 @@ int git_reference_has_log( int git_reference_is_branch(git_reference *ref) { assert(ref); - return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0; } + +int git_reference_is_remote(git_reference *ref) +{ + assert(ref); + return git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0; +} diff --git a/src/repository.c b/src/repository.c index c12df25c..b9d180da 100644 --- a/src/repository.c +++ b/src/repository.c @@ -777,8 +777,8 @@ static int repo_init_config( SET_REPO_CONFIG(string, "core.worktree", work_dir); } else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) { - if ((error = git_config_delete(config, "core.worktree")) < 0) - goto cleanup; + if (git_config_delete(config, "core.worktree") < 0) + giterr_clear(); } } else { if (!are_symlinks_supported(repo_dir)) diff --git a/src/reset.c b/src/reset.c index 1379f644..f9e16f7c 100644 --- a/src/reset.c +++ b/src/reset.c @@ -20,10 +20,9 @@ static int reset_error_invalid(const char *msg) int git_reset( git_repository *repo, - const git_object *target, + git_object *target, git_reset_type reset_type) { - git_otype target_type = GIT_OBJ_BAD; git_object *commit = NULL; git_index *index = NULL; git_tree *tree = NULL; @@ -38,26 +37,9 @@ int git_reset( if (reset_type == GIT_RESET_MIXED && git_repository_is_bare(repo)) return reset_error_invalid("Mixed reset is not allowed in a bare repository."); - target_type = git_object_type(target); - - switch (target_type) - { - case GIT_OBJ_TAG: - if (git_tag_peel(&commit, (git_tag *)target) < 0) - goto cleanup; - - if (git_object_type(commit) != GIT_OBJ_COMMIT) { - reset_error_invalid("The given target does not resolve to a commit."); - goto cleanup; - } - break; - - case GIT_OBJ_COMMIT: - commit = (git_object *)target; - break; - - default: - return reset_error_invalid("Only git_tag and git_commit objects are valid targets."); + if (git_object_peel(&commit, target, GIT_OBJ_COMMIT) < 0) { + reset_error_invalid("The given target does not resolve to a commit"); + goto cleanup; } //TODO: Check for unmerged entries @@ -93,9 +75,7 @@ int git_reset( error = 0; cleanup: - if (target_type == GIT_OBJ_TAG) - git_object_free(commit); - + git_object_free(commit); git_index_free(index); git_tree_free(tree); diff --git a/src/revparse.c b/src/revparse.c index 3855c29f..17266b94 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -493,7 +493,7 @@ static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex) git_object_free(obj); } - if (error < 0 && error == GIT_REVWALKOVER) + if (error < 0 && error == GIT_ITEROVER) error = GIT_ENOTFOUND; return error; diff --git a/src/revwalk.c b/src/revwalk.c index 9dff283f..1a092771 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -264,12 +264,7 @@ static int commit_parse(git_revwalk *walk, commit_object *commit) if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0) return error; - - if (obj->raw.type != GIT_OBJ_COMMIT) { - git_odb_object_free(obj); - giterr_set(GITERR_INVALID, "Failed to parse commit. Object is no commit object"); - return -1; - } + assert(obj->raw.type == GIT_OBJ_COMMIT); error = commit_quick_parse(walk, commit, &obj->raw); git_odb_object_free(obj); @@ -454,6 +449,7 @@ int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *tw if (!result) { git_revwalk_free(walk); + giterr_clear(); return GIT_ENOTFOUND; } @@ -515,8 +511,21 @@ static int process_commit_parents(git_revwalk *walk, commit_object *commit) static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) { + git_object *obj; + git_otype type; commit_object *commit; + if (git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY) < 0) + return -1; + + type = git_object_type(obj); + git_object_free(obj); + + if (type != GIT_OBJ_COMMIT) { + giterr_set(GITERR_INVALID, "Object is no commit object"); + return -1; + } + commit = commit_lookup(walk, oid); if (commit == NULL) return -1; /* error already reported by failed lookup */ @@ -674,7 +683,8 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk) } } - return GIT_REVWALKOVER; + giterr_clear(); + return GIT_ITEROVER; } static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) @@ -692,7 +702,8 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) } } - return GIT_REVWALKOVER; + giterr_clear(); + return GIT_ITEROVER; } static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) @@ -702,8 +713,10 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) for (;;) { next = commit_list_pop(&walk->iterator_topo); - if (next == NULL) - return GIT_REVWALKOVER; + if (next == NULL) { + giterr_clear(); + return GIT_ITEROVER; + } if (next->in_degree > 0) { next->topo_delay = 1; @@ -728,7 +741,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk) { *object_out = commit_list_pop(&walk->iterator_reverse); - return *object_out ? 0 : GIT_REVWALKOVER; + return *object_out ? 0 : GIT_ITEROVER; } @@ -743,8 +756,10 @@ static int prepare_walk(git_revwalk *walk) * If walk->one is NULL, there were no positive references, * so we know that the walk is already over. */ - if (walk->one == NULL) - return GIT_REVWALKOVER; + if (walk->one == NULL) { + giterr_clear(); + return GIT_ITEROVER; + } /* first figure out what the merge bases are */ if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0) @@ -772,7 +787,7 @@ static int prepare_walk(git_revwalk *walk) return -1; } - if (error != GIT_REVWALKOVER) + if (error != GIT_ITEROVER) return error; walk->get_next = &revwalk_next_toposort; @@ -784,7 +799,7 @@ static int prepare_walk(git_revwalk *walk) if (commit_list_insert(next, &walk->iterator_reverse) == NULL) return -1; - if (error != GIT_REVWALKOVER) + if (error != GIT_ITEROVER) return error; walk->get_next = &revwalk_next_reverse; @@ -883,9 +898,10 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) error = walk->get_next(&next, walk); - if (error == GIT_REVWALKOVER) { + if (error == GIT_ITEROVER) { git_revwalk_reset(walk); - return GIT_REVWALKOVER; + giterr_clear(); + return GIT_ITEROVER; } if (!error) diff --git a/src/signature.c b/src/signature.c index 1f788356..0159488a 100644 --- a/src/signature.c +++ b/src/signature.c @@ -125,24 +125,26 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema { time_t now; time_t offset; - struct tm *utc_tm, *local_tm; + struct tm *utc_tm; git_signature *sig; - struct tm _utc, _local; + struct tm _utc; *sig_out = NULL; + /* + * Get the current time as seconds since the epoch and + * transform that into a tm struct containing the time at + * UTC. Give that to mktime which considers it a local time + * (tm_isdst = -1 asks it to take DST into account) and gives + * us that time as seconds since the epoch. The difference + * between its return value and 'now' is our offset to UTC. + */ time(&now); - utc_tm = p_gmtime_r(&now, &_utc); - local_tm = p_localtime_r(&now, &_local); - - offset = mktime(local_tm) - mktime(utc_tm); + utc_tm->tm_isdst = -1; + offset = (time_t)difftime(now, mktime(utc_tm)); offset /= 60; - /* mktime takes care of setting tm_isdst correctly */ - if (local_tm->tm_isdst) - offset += 60; - if (git_signature_new(&sig, name, email, now, (int)offset) < 0) return -1; diff --git a/src/status.c b/src/status.c index 3d3d15d7..0a5fbdcb 100644 --- a/src/status.c +++ b/src/status.c @@ -151,6 +151,9 @@ cleanup: git_diff_list_free(idx2head); git_diff_list_free(wd2idx); + if (err == GIT_EUSER) + giterr_clear(); + return err; } diff --git a/src/submodule.c b/src/submodule.c index a9de9ee6..66f1f84b 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -163,6 +163,7 @@ int git_submodule_foreach( } if (callback(sm, sm->name, payload)) { + giterr_clear(); error = GIT_EUSER; break; } @@ -445,20 +445,5 @@ int git_tag_list(git_strarray *tag_names, git_repository *repo) int git_tag_peel(git_object **tag_target, git_tag *tag) { - int error; - git_object *target; - - assert(tag_target && tag); - - if (git_tag_target(&target, tag) < 0) - return -1; - - if (git_object_type(target) == GIT_OBJ_TAG) { - error = git_tag_peel(tag_target, (git_tag *)target); - git_object_free(target); - return error; - } - - *tag_target = target; - return 0; + return git_object_peel(tag_target, (git_object *)tag, GIT_OBJ_ANY); } diff --git a/src/unix/map.c b/src/unix/map.c index 9dcae584..ee7888c1 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -31,6 +31,8 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs mflag = MAP_SHARED; else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE) mflag = MAP_PRIVATE; + else + mflag = MAP_SHARED; out->data = mmap(NULL, len, mprot, mflag, fd, offset); diff --git a/src/win32/dir.c b/src/win32/dir.c index bc3d40fa..5cb1082b 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -7,7 +7,6 @@ #define GIT__WIN32_NO_WRAP_DIR #include "dir.h" #include "utf-conv.h" -#include "git2/windows.h" static int init_filter(char *filter, size_t n, const char *dir) { @@ -26,8 +25,8 @@ static int init_filter(char *filter, size_t n, const char *dir) git__DIR *git__opendir(const char *dir) { - char filter[4096]; - wchar_t* filter_w = NULL; + char filter[GIT_WIN_PATH]; + wchar_t filter_w[GIT_WIN_PATH]; git__DIR *new = NULL; if (!dir || !init_filter(filter, sizeof(filter), dir)) @@ -41,12 +40,8 @@ git__DIR *git__opendir(const char *dir) if (!new->dir) goto fail; - filter_w = gitwin_to_utf16(filter); - if (!filter_w) - goto fail; - + git__utf8_to_16(filter_w, GIT_WIN_PATH, filter); new->h = FindFirstFileW(filter_w, &new->f); - git__free(filter_w); if (new->h == INVALID_HANDLE_VALUE) { giterr_set(GITERR_OS, "Could not open directory '%s'", dir); @@ -85,16 +80,9 @@ int git__readdir_ext( if (wcslen(d->f.cFileName) >= sizeof(entry->d_name)) return -1; + git__utf16_to_8(entry->d_name, d->f.cFileName); entry->d_ino = 0; - if (WideCharToMultiByte( - gitwin_get_codepage(), 0, d->f.cFileName, -1, - entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0) - { - giterr_set(GITERR_OS, "Could not convert filename to UTF-8"); - return -1; - } - *result = entry; if (is_dir != NULL) @@ -113,8 +101,8 @@ struct git__dirent *git__readdir(git__DIR *d) void git__rewinddir(git__DIR *d) { - char filter[4096]; - wchar_t* filter_w; + char filter[GIT_WIN_PATH]; + wchar_t filter_w[GIT_WIN_PATH]; if (!d) return; @@ -125,12 +113,11 @@ void git__rewinddir(git__DIR *d) d->first = 0; } - if (!init_filter(filter, sizeof(filter), d->dir) || - (filter_w = gitwin_to_utf16(filter)) == NULL) + if (!init_filter(filter, sizeof(filter), d->dir)) return; + git__utf8_to_16(filter_w, GIT_WIN_PATH, filter); d->h = FindFirstFileW(filter_w, &d->f); - git__free(filter_w); if (d->h == INVALID_HANDLE_VALUE) giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir); diff --git a/src/win32/posix.h b/src/win32/posix.h index 14caae41..da46cf51 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -21,13 +21,10 @@ GIT_INLINE(int) p_link(const char *old, const char *new) GIT_INLINE(int) p_mkdir(const char *path, mode_t mode) { - wchar_t* buf = gitwin_to_utf16(path); - int ret = _wmkdir(buf); - + wchar_t buf[GIT_WIN_PATH]; GIT_UNUSED(mode); - - git__free(buf); - return ret; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wmkdir(buf); } extern int p_unlink(const char *path); diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index aa34ad3a..649fe9b9 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -15,16 +15,10 @@ int p_unlink(const char *path) { - int ret = 0; - wchar_t* buf; - - if ((buf = gitwin_to_utf16(path)) != NULL) { - _wchmod(buf, 0666); - ret = _wunlink(buf); - git__free(buf); - } - - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + _wchmod(buf, 0666); + return _wunlink(buf); } int p_fsync(int fd) @@ -61,10 +55,10 @@ 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; + wchar_t fbuf[GIT_WIN_PATH]; DWORD last_error; - wchar_t* fbuf = gitwin_to_utf16(file_name); - if (!fbuf) - return -1; + + git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name); if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { int fMode = S_IREAD; @@ -90,8 +84,6 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); - - git__free(fbuf); return 0; } @@ -101,7 +93,6 @@ static int do_lstat(const char *file_name, struct stat *buf) else if (last_error == ERROR_PATH_NOT_FOUND) errno = ENOTDIR; - git__free(fbuf); return -1; } @@ -143,7 +134,7 @@ int p_readlink(const char *link, char *target, size_t target_len) static fpath_func pGetFinalPath = NULL; HANDLE hFile; DWORD dwRet; - wchar_t* link_w; + wchar_t link_w[GIT_WIN_PATH]; wchar_t* target_w; int error = 0; @@ -166,8 +157,7 @@ int p_readlink(const char *link, char *target, size_t target_len) } } - link_w = gitwin_to_utf16(link); - GITERR_CHECK_ALLOC(link_w); + git__utf8_to_16(link_w, GIT_WIN_PATH, link); hFile = CreateFileW(link_w, // file to open GENERIC_READ, // open for reading @@ -177,8 +167,6 @@ int p_readlink(const char *link, char *target, size_t target_len) FILE_FLAG_BACKUP_SEMANTICS, // normal file NULL); // no attr. template - git__free(link_w); - if (hFile == INVALID_HANDLE_VALUE) { giterr_set(GITERR_OS, "Cannot open '%s' for reading", link); return -1; @@ -235,16 +223,12 @@ int p_symlink(const char *old, const char *new) int p_open(const char *path, int flags, ...) { - int fd; - wchar_t* buf; + wchar_t buf[GIT_WIN_PATH]; mode_t mode = 0; - buf = gitwin_to_utf16(path); - if (!buf) - return -1; + git__utf8_to_16(buf, GIT_WIN_PATH, path); - if (flags & O_CREAT) - { + if (flags & O_CREAT) { va_list arg_list; va_start(arg_list, flags); @@ -252,27 +236,20 @@ int p_open(const char *path, int flags, ...) va_end(arg_list); } - fd = _wopen(buf, flags | _O_BINARY, mode); - - git__free(buf); - return fd; + return _wopen(buf, flags | _O_BINARY, mode); } int p_creat(const char *path, mode_t mode) { - int fd; - wchar_t* buf = gitwin_to_utf16(path); - if (!buf) - return -1; - fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); - git__free(buf); - return fd; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); } int p_getcwd(char *buffer_out, size_t size) { int ret; - wchar_t* buf; + wchar_t *buf; if ((size_t)((int)size) != size) return -1; @@ -296,64 +273,43 @@ int p_stat(const char* path, struct stat* buf) int p_chdir(const char* path) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wchdir(buf); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wchdir(buf); } int p_chmod(const char* path, mode_t mode) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wchmod(buf, mode); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wchmod(buf, mode); } int p_rmdir(const char* path) { - wchar_t* buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - ret = _wrmdir(buf); - git__free(buf); - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _wrmdir(buf); } int p_hide_directory__w32(const char *path) { - int res; - wchar_t* buf = gitwin_to_utf16(path); - if (!buf) - return -1; - - res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN); - git__free(buf); - - return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */ + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1; } char *p_realpath(const char *orig_path, char *buffer) { int ret, buffer_sz = 0; - wchar_t* orig_path_w = gitwin_to_utf16(orig_path); - wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t)); - - if (!orig_path_w || !buffer_w) - return NULL; + wchar_t orig_path_w[GIT_WIN_PATH]; + wchar_t buffer_w[GIT_WIN_PATH]; - ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL); - git__free(orig_path_w); + git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path); + ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL); /* According to MSDN, a return value equals to zero means a failure. */ - if (ret == 0 || ret > GIT_PATH_MAX) { + if (ret == 0 || ret > GIT_WIN_PATH) { buffer = NULL; goto done; } @@ -376,8 +332,7 @@ char *p_realpath(const char *orig_path, char *buffer) } } - if (!git_path_exists(buffer)) - { + if (!git_path_exists(buffer)) { if (buffer_sz > 0) git__free(buffer); @@ -386,9 +341,9 @@ char *p_realpath(const char *orig_path, char *buffer) } done: - git__free(buffer_w); if (buffer) git_path_mkposix(buffer); + return buffer; } @@ -443,32 +398,19 @@ int p_setenv(const char* name, const char* value, int overwrite) int p_access(const char* path, mode_t mode) { - wchar_t *buf = gitwin_to_utf16(path); - int ret; - if (!buf) - return -1; - - ret = _waccess(buf, mode); - git__free(buf); - - return ret; + wchar_t buf[GIT_WIN_PATH]; + git__utf8_to_16(buf, GIT_WIN_PATH, path); + return _waccess(buf, mode); } int p_rename(const char *from, const char *to) { - wchar_t *wfrom = gitwin_to_utf16(from); - wchar_t *wto = gitwin_to_utf16(to); - int ret; - - if (!wfrom || !wto) - return -1; - - ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; - - git__free(wfrom); - git__free(wto); + wchar_t wfrom[GIT_WIN_PATH]; + wchar_t wto[GIT_WIN_PATH]; - return ret; + git__utf8_to_16(wfrom, GIT_WIN_PATH, from); + git__utf8_to_16(wto, GIT_WIN_PATH, to); + return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; } int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index 0a705c0a..88a84141 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -7,86 +7,75 @@ #include "common.h" #include "utf-conv.h" -#include "git2/windows.h" -/* - * Default codepage value - */ -static int _active_codepage = CP_UTF8; - -void gitwin_set_codepage(unsigned int codepage) -{ - _active_codepage = codepage; -} - -unsigned int gitwin_get_codepage(void) -{ - return _active_codepage; -} - -void gitwin_set_utf8(void) -{ - _active_codepage = CP_UTF8; -} +#define U16_LEAD(c) (wchar_t)(((c)>>10)+0xd7c0) +#define U16_TRAIL(c) (wchar_t)(((c)&0x3ff)|0xdc00) -wchar_t* gitwin_to_utf16(const char* str) +#if 0 +void git__utf8_to_16(wchar_t *dest, size_t length, const char *src) { - wchar_t* ret; - int cb; - - if (!str) - return NULL; - - cb = MultiByteToWideChar(_active_codepage, 0, str, -1, NULL, 0); - if (cb == 0) - return (wchar_t *)git__calloc(1, sizeof(wchar_t)); - - ret = (wchar_t *)git__malloc(cb * sizeof(wchar_t)); - if (!ret) - return NULL; - - if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) { - giterr_set(GITERR_OS, "Could not convert string to UTF-16"); - git__free(ret); - ret = NULL; + wchar_t *pDest = dest; + uint32_t ch; + const uint8_t* pSrc = (uint8_t*) src; + + assert(dest && src && length); + + length--; + + while(*pSrc && length > 0) { + ch = *pSrc++; + length--; + + if(ch < 0xc0) { + /* + * ASCII, or a trail byte in lead position which is treated like + * a single-byte sequence for better character boundary + * resynchronization after illegal sequences. + */ + *pDest++ = (wchar_t)ch; + continue; + } else if(ch < 0xe0) { /* U+0080..U+07FF */ + if (pSrc[0]) { + /* 0x3080 = (0xc0 << 6) + 0x80 */ + *pDest++ = (wchar_t)((ch << 6) + *pSrc++ - 0x3080); + continue; + } + } else if(ch < 0xf0) { /* U+0800..U+FFFF */ + if (pSrc[0] && pSrc[1]) { + /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ + /* 0x2080 = (0x80 << 6) + 0x80 */ + ch = (ch << 12) + (*pSrc++ << 6); + *pDest++ = (wchar_t)(ch + *pSrc++ - 0x2080); + continue; + } + } else /* f0..f4 */ { /* U+10000..U+10FFFF */ + if (length >= 1 && pSrc[0] && pSrc[1] && pSrc[2]) { + /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ + ch = (ch << 18) + (*pSrc++ << 12); + ch += *pSrc++ << 6; + ch += *pSrc++ - 0x3c82080; + *(pDest++) = U16_LEAD(ch); + *(pDest++) = U16_TRAIL(ch); + length--; /* two bytes for this character */ + continue; + } + } + + /* truncated character at the end */ + *pDest++ = 0xfffd; + break; } - return ret; + *pDest++ = 0x0; } +#endif -int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len) +void git__utf8_to_16(wchar_t *dest, size_t length, const char *src) { - int result = MultiByteToWideChar( - _active_codepage, 0, str, -1, buffer, (int)len); - if (result == 0) - giterr_set(GITERR_OS, "Could not convert string to UTF-16"); - return result; + MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, length); } -char* gitwin_from_utf16(const wchar_t* str) +void git__utf16_to_8(char *out, const wchar_t *input) { - char* ret; - int cb; - - if (!str) - return NULL; - - cb = WideCharToMultiByte(_active_codepage, 0, str, -1, NULL, 0, NULL, NULL); - if (cb == 0) - return (char *)git__calloc(1, sizeof(char)); - - ret = (char*)git__malloc(cb); - if (!ret) - return NULL; - - if (WideCharToMultiByte( - _active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0) - { - giterr_set(GITERR_OS, "Could not convert string to UTF-8"); - git__free(ret); - ret = NULL; - } - - return ret; - + WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL); } diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h index ae9f29f6..3bd1549b 100644 --- a/src/win32/utf-conv.h +++ b/src/win32/utf-conv.h @@ -10,9 +10,10 @@ #ifndef INCLUDE_git_utfconv_h__ #define INCLUDE_git_utfconv_h__ -wchar_t* gitwin_to_utf16(const char* str); -int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len); -char* gitwin_from_utf16(const wchar_t* str); +#define GIT_WIN_PATH (260 + 1) + +void git__utf8_to_16(wchar_t *dest, size_t length, const char *src); +void git__utf16_to_8(char *dest, const wchar_t *src); #endif diff --git a/tests-clar/clar_helpers.c b/tests-clar/clar_helpers.c index c9147943..8d6a7024 100644 --- a/tests-clar/clar_helpers.c +++ b/tests-clar/clar_helpers.c @@ -9,6 +9,7 @@ void clar_on_init(void) void clar_on_shutdown(void) { git_threads_shutdown(); + giterr_clear(); } void cl_git_mkfile(const char *filename, const char *content) @@ -55,22 +56,23 @@ void cl_git_rewritefile(const char *filename, const char *new_content) char *cl_getenv(const char *name) { - wchar_t *name_utf16 = gitwin_to_utf16(name); - DWORD value_len, alloc_len; + wchar_t name_utf16[GIT_WIN_PATH]; + DWORD alloc_len; wchar_t *value_utf16; char *value_utf8; - cl_assert(name_utf16); + git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0); if (alloc_len <= 0) return NULL; + alloc_len = GIT_WIN_PATH; cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t))); - value_len = GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - cl_assert_equal_i(value_len, alloc_len - 1); + GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - cl_assert(value_utf8 = gitwin_from_utf16(value_utf16)); + cl_assert(value_utf8 = git__malloc(alloc_len)); + git__utf16_to_8(value_utf8, value_utf16); git__free(value_utf16); @@ -79,17 +81,16 @@ char *cl_getenv(const char *name) int cl_setenv(const char *name, const char *value) { - wchar_t *name_utf16 = gitwin_to_utf16(name); - wchar_t *value_utf16 = value ? gitwin_to_utf16(value) : NULL; + wchar_t name_utf16[GIT_WIN_PATH]; + wchar_t value_utf16[GIT_WIN_PATH]; - cl_assert(name_utf16); - cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16)); + git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); - git__free(name_utf16); - git__free(value_utf16); + if (value != NULL) + git__utf8_to_16(value_utf16, GIT_WIN_PATH, value); + cl_assert(SetEnvironmentVariableW(name_utf16, value ? value_utf16 : NULL)); return 0; - } #else diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c index 5d3ab8d5..d5cf41e9 100644 --- a/tests-clar/diff/blob.c +++ b/tests-clar/diff/blob.c @@ -58,59 +58,59 @@ void test_diff_blob__can_compare_text_blobs(void) cl_git_pass(git_diff_blobs( a, b, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_mods == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_mods); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 1); - cl_assert(expected.lines == 6); - cl_assert(expected.line_ctxt == 1); - cl_assert(expected.line_adds == 5); - cl_assert(expected.line_dels == 0); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(6, expected.lines); + cl_assert_equal_i(1, expected.line_ctxt); + cl_assert_equal_i(5, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); /* diff on tests/resources/attr/root_test2 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( b, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_mods == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_mods); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 1); - cl_assert(expected.lines == 15); - cl_assert(expected.line_ctxt == 3); - cl_assert(expected.line_adds == 9); - cl_assert(expected.line_dels == 3); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(15, expected.lines); + cl_assert_equal_i(3, expected.line_ctxt); + cl_assert_equal_i(9, expected.line_adds); + cl_assert_equal_i(3, expected.line_dels); /* diff on tests/resources/attr/root_test3 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( a, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_mods == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_mods); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 1); - cl_assert(expected.lines == 13); - cl_assert(expected.line_ctxt == 0); - cl_assert(expected.line_adds == 12); - cl_assert(expected.line_dels == 1); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(13, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(12, expected.line_adds); + cl_assert_equal_i(1, expected.line_dels); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( c, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_mods == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_mods); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 2); - cl_assert(expected.lines == 14); - cl_assert(expected.line_ctxt == 4); - cl_assert(expected.line_adds == 6); - cl_assert(expected.line_dels == 4); + cl_assert_equal_i(2, expected.hunks); + cl_assert_equal_i(14, expected.lines); + cl_assert_equal_i(4, expected.line_ctxt); + cl_assert_equal_i(6, expected.line_adds); + cl_assert_equal_i(4, expected.line_dels); git_blob_free(a); git_blob_free(b); @@ -124,14 +124,14 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_dels == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_dels); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 1); - cl_assert(expected.hunk_old_lines == 14); - cl_assert(expected.lines == 14); - cl_assert(expected.line_dels == 14); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(14, expected.hunk_old_lines); + cl_assert_equal_i(14, expected.lines); + cl_assert_equal_i(14, expected.line_dels); opts.flags |= GIT_DIFF_REVERSE; memset(&expected, 0, sizeof(expected)); @@ -139,14 +139,14 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.files == 1); - cl_assert(expected.file_adds == 1); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_adds); cl_assert(expected.at_least_one_of_them_is_binary == false); - cl_assert(expected.hunks == 1); - cl_assert(expected.hunk_new_lines == 14); - cl_assert(expected.lines == 14); - cl_assert(expected.line_adds == 14); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(14, expected.hunk_new_lines); + cl_assert_equal_i(14, expected.lines); + cl_assert_equal_i(14, expected.line_adds); opts.flags ^= GIT_DIFF_REVERSE; memset(&expected, 0, sizeof(expected)); @@ -156,10 +156,10 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_assert(expected.at_least_one_of_them_is_binary == true); - cl_assert(expected.files == 1); - cl_assert(expected.file_dels == 1); - cl_assert(expected.hunks == 0); - cl_assert(expected.lines == 0); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_dels); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); memset(&expected, 0, sizeof(expected)); @@ -168,18 +168,18 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_assert(expected.at_least_one_of_them_is_binary == true); - cl_assert(expected.files == 1); - cl_assert(expected.file_adds == 1); - cl_assert(expected.hunks == 0); - cl_assert(expected.lines == 0); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_adds); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); } static void assert_identical_blobs_comparison(diff_expects expected) { - cl_assert(expected.files == 1); - cl_assert(expected.file_unmodified == 1); - cl_assert(expected.hunks == 0); - cl_assert(expected.lines == 0); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_unmodified); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); } void test_diff_blob__can_compare_identical_blobs(void) @@ -209,10 +209,10 @@ static void assert_binary_blobs_comparison(diff_expects expected) { cl_assert(expected.at_least_one_of_them_is_binary == true); - cl_assert(expected.files == 1); - cl_assert(expected.file_mods == 1); - cl_assert(expected.hunks == 0); - cl_assert(expected.lines == 0); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_mods); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); } void test_diff_blob__can_compare_two_binary_blobs(void) @@ -292,7 +292,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) cl_git_pass(git_diff_blobs( old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.hunks == 2); + cl_assert_equal_i(2, expected.hunks); /* Test with inter-hunk-context explicitly set to 0 */ opts.interhunk_lines = 0; @@ -300,7 +300,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) cl_git_pass(git_diff_blobs( old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.hunks == 2); + cl_assert_equal_i(2, expected.hunks); /* Test with inter-hunk-context explicitly set to 1 */ opts.interhunk_lines = 1; @@ -308,7 +308,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) cl_git_pass(git_diff_blobs( old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(expected.hunks == 1); + cl_assert_equal_i(1, expected.hunks); git_blob_free(old_d); } diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index 7b391262..59e01802 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -103,3 +103,74 @@ int diff_line_fn( } return 0; } + +int diff_foreach_via_iterator( + git_diff_list *diff, + void *data, + git_diff_file_fn file_cb, + git_diff_hunk_fn hunk_cb, + git_diff_data_fn line_cb) +{ + int error, curr, total; + git_diff_iterator *iter; + git_diff_delta *delta; + + if ((error = git_diff_iterator_new(&iter, diff)) < 0) + return error; + + curr = 0; + total = git_diff_iterator_num_files(iter); + + while (!(error = git_diff_iterator_next_file(&delta, iter))) { + git_diff_range *range; + const char *hdr; + size_t hdr_len; + + /* call file_cb for this file */ + if (file_cb != NULL && file_cb(data, delta, (float)curr / total) != 0) + goto abort; + + if (!hunk_cb && !line_cb) + continue; + + while (!(error = git_diff_iterator_next_hunk( + &range, &hdr, &hdr_len, iter))) { + char origin; + const char *line; + size_t line_len; + + if (hunk_cb && hunk_cb(data, delta, range, hdr, hdr_len) != 0) + goto abort; + + if (!line_cb) + continue; + + while (!(error = git_diff_iterator_next_line( + &origin, &line, &line_len, iter))) { + + if (line_cb(data, delta, range, origin, line, line_len) != 0) + goto abort; + } + + if (error && error != GIT_ITEROVER) + goto done; + } + + if (error && error != GIT_ITEROVER) + goto done; + } + +done: + git_diff_iterator_free(iter); + + if (error == GIT_ITEROVER) + error = 0; + + return error; + +abort: + git_diff_iterator_free(iter); + giterr_clear(); + + return GIT_EUSER; +} diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h index 0aaa6c11..79e14092 100644 --- a/tests-clar/diff/diff_helpers.h +++ b/tests-clar/diff/diff_helpers.h @@ -45,3 +45,9 @@ extern int diff_line_fn( const char *content, size_t content_len); +extern int diff_foreach_via_iterator( + git_diff_list *diff, + void *data, + git_diff_file_fn file_cb, + git_diff_hunk_fn hunk_cb, + git_diff_data_fn line_cb); diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c new file mode 100644 index 00000000..56c25474 --- /dev/null +++ b/tests-clar/diff/diffiter.c @@ -0,0 +1,116 @@ +#include "clar_libgit2.h" +#include "diff_helpers.h" + +void test_diff_diffiter__initialize(void) +{ +} + +void test_diff_diffiter__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_diff_diffiter__create(void) +{ + git_repository *repo = cl_git_sandbox_init("attr"); + git_diff_list *diff; + git_diff_iterator *iter; + + cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff)); + cl_git_pass(git_diff_iterator_new(&iter, diff)); + git_diff_iterator_free(iter); + git_diff_list_free(diff); +} + +void test_diff_diffiter__iterate_files(void) +{ + git_repository *repo = cl_git_sandbox_init("attr"); + git_diff_list *diff; + git_diff_iterator *iter; + git_diff_delta *delta; + int error, count = 0; + + cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff)); + cl_git_pass(git_diff_iterator_new(&iter, diff)); + + while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) { + cl_assert_equal_i(0, error); + cl_assert(delta != NULL); + count++; + } + + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(delta == NULL); + cl_assert_equal_i(6, count); + + git_diff_iterator_free(iter); + git_diff_list_free(diff); +} + +void test_diff_diffiter__iterate_files_2(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_diff_list *diff; + git_diff_iterator *iter; + git_diff_delta *delta; + int error, count = 0; + + cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff)); + cl_git_pass(git_diff_iterator_new(&iter, diff)); + + while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) { + cl_assert_equal_i(0, error); + cl_assert(delta != NULL); + count++; + } + + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(delta == NULL); + cl_assert_equal_i(8, count); + + git_diff_iterator_free(iter); + git_diff_list_free(diff); +} + +void test_diff_diffiter__iterate_files_and_hunks(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_diff_options opts = {0}; + git_diff_list *diff = NULL; + git_diff_iterator *iter; + git_diff_delta *delta; + git_diff_range *range; + const char *header; + size_t header_len; + int error, file_count = 0, hunk_count = 0; + + opts.context_lines = 3; + opts.interhunk_lines = 1; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + + cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff)); + + cl_git_pass(git_diff_iterator_new(&iter, diff)); + + while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) { + cl_assert_equal_i(0, error); + cl_assert(delta); + + file_count++; + + while ((error = git_diff_iterator_next_hunk( + &range, &header, &header_len, iter)) != GIT_ITEROVER) { + cl_assert_equal_i(0, error); + cl_assert(range); + hunk_count++; + } + } + + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(delta == NULL); + cl_assert_equal_i(13, file_count); + cl_assert_equal_i(8, hunk_count); + + git_diff_iterator_free(iter); + git_diff_list_free(diff); +} diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c index 89e65e3b..2c6e89c4 100644 --- a/tests-clar/diff/index.c +++ b/tests-clar/diff/index.c @@ -44,17 +44,17 @@ void test_diff_index__0(void) * - git diff -U1 --cached 26a125ee1bf * - mv .git .gitted */ - cl_assert(exp.files == 8); - cl_assert(exp.file_adds == 3); - cl_assert(exp.file_dels == 2); - cl_assert(exp.file_mods == 3); + cl_assert_equal_i(8, exp.files); + cl_assert_equal_i(3, exp.file_adds); + cl_assert_equal_i(2, exp.file_dels); + cl_assert_equal_i(3, exp.file_mods); - cl_assert(exp.hunks == 8); + cl_assert_equal_i(8, exp.hunks); - cl_assert(exp.lines == 11); - cl_assert(exp.line_ctxt == 3); - cl_assert(exp.line_adds == 6); - cl_assert(exp.line_dels == 2); + cl_assert_equal_i(11, exp.lines); + cl_assert_equal_i(3, exp.line_ctxt); + cl_assert_equal_i(6, exp.line_adds); + cl_assert_equal_i(2, exp.line_dels); git_diff_list_free(diff); diff = NULL; @@ -72,17 +72,17 @@ void test_diff_index__0(void) * - git diff -U1 --cached 0017bd4ab1ec3 * - mv .git .gitted */ - cl_assert(exp.files == 12); - cl_assert(exp.file_adds == 7); - cl_assert(exp.file_dels == 2); - cl_assert(exp.file_mods == 3); + cl_assert_equal_i(12, exp.files); + cl_assert_equal_i(7, exp.file_adds); + cl_assert_equal_i(2, exp.file_dels); + cl_assert_equal_i(3, exp.file_mods); - cl_assert(exp.hunks == 12); + cl_assert_equal_i(12, exp.hunks); - cl_assert(exp.lines == 16); - cl_assert(exp.line_ctxt == 3); - cl_assert(exp.line_adds == 11); - cl_assert(exp.line_dels == 2); + cl_assert_equal_i(16, exp.lines); + cl_assert_equal_i(3, exp.line_ctxt); + cl_assert_equal_i(11, exp.line_adds); + cl_assert_equal_i(2, exp.line_dels); git_diff_list_free(diff); diff = NULL; @@ -132,7 +132,7 @@ void test_diff_index__1(void) git_diff_foreach(diff, &exp, diff_stop_after_2_files, NULL, NULL) ); - cl_assert(exp.files == 2); + cl_assert_equal_i(2, exp.files); git_diff_list_free(diff); diff = NULL; diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c index be9eb6c1..3003374a 100644 --- a/tests-clar/diff/tree.c +++ b/tests-clar/diff/tree.c @@ -39,17 +39,17 @@ void test_diff_tree__0(void) cl_git_pass(git_diff_foreach( diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(exp.files == 5); - cl_assert(exp.file_adds == 2); - cl_assert(exp.file_dels == 1); - cl_assert(exp.file_mods == 2); + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(2, exp.file_adds); + cl_assert_equal_i(1, exp.file_dels); + cl_assert_equal_i(2, exp.file_mods); - cl_assert(exp.hunks == 5); + cl_assert_equal_i(5, exp.hunks); - cl_assert(exp.lines == 7 + 24 + 1 + 6 + 6); - cl_assert(exp.line_ctxt == 1); - cl_assert(exp.line_adds == 24 + 1 + 5 + 5); - cl_assert(exp.line_dels == 7 + 1); + cl_assert_equal_i(7 + 24 + 1 + 6 + 6, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(24 + 1 + 5 + 5, exp.line_adds); + cl_assert_equal_i(7 + 1, exp.line_dels); git_diff_list_free(diff); diff = NULL; @@ -61,17 +61,17 @@ void test_diff_tree__0(void) cl_git_pass(git_diff_foreach( diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(exp.files == 2); - cl_assert(exp.file_adds == 0); - cl_assert(exp.file_dels == 0); - cl_assert(exp.file_mods == 2); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(2, exp.file_mods); - cl_assert(exp.hunks == 2); + cl_assert_equal_i(2, exp.hunks); - cl_assert(exp.lines == 8 + 15); - cl_assert(exp.line_ctxt == 1); - cl_assert(exp.line_adds == 1); - cl_assert(exp.line_dels == 7 + 14); + cl_assert_equal_i(8 + 15, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(1, exp.line_adds); + cl_assert_equal_i(7 + 14, exp.line_dels); git_diff_list_free(diff); @@ -192,17 +192,17 @@ void test_diff_tree__bare(void) cl_git_pass(git_diff_foreach( diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(exp.files == 3); - cl_assert(exp.file_adds == 2); - cl_assert(exp.file_dels == 0); - cl_assert(exp.file_mods == 1); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(2, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); - cl_assert(exp.hunks == 3); + cl_assert_equal_i(3, exp.hunks); - cl_assert(exp.lines == 4); - cl_assert(exp.line_ctxt == 0); - cl_assert(exp.line_adds == 3); - cl_assert(exp.line_dels == 1); + cl_assert_equal_i(4, exp.lines); + cl_assert_equal_i(0, exp.line_ctxt); + cl_assert_equal_i(3, exp.line_adds); + cl_assert_equal_i(1, exp.line_dels); git_diff_list_free(diff); git_tree_free(a); @@ -242,17 +242,17 @@ void test_diff_tree__merge(void) cl_git_pass(git_diff_foreach( diff1, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert(exp.files == 6); - cl_assert(exp.file_adds == 2); - cl_assert(exp.file_dels == 1); - cl_assert(exp.file_mods == 3); + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(2, exp.file_adds); + cl_assert_equal_i(1, exp.file_dels); + cl_assert_equal_i(3, exp.file_mods); - cl_assert(exp.hunks == 6); + cl_assert_equal_i(6, exp.hunks); - cl_assert(exp.lines == 59); - cl_assert(exp.line_ctxt == 1); - cl_assert(exp.line_adds == 36); - cl_assert(exp.line_dels == 22); + cl_assert_equal_i(59, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(36, exp.line_adds); + cl_assert_equal_i(22, exp.line_dels); git_diff_list_free(diff1); } diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 801439e3..eac7eb87 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -17,6 +17,7 @@ void test_diff_workdir__to_index(void) git_diff_options opts = {0}; git_diff_list *diff = NULL; diff_expects exp; + int use_iterator; g_repo = cl_git_sandbox_init("status"); @@ -24,33 +25,39 @@ void test_diff_workdir__to_index(void) opts.interhunk_lines = 1; opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - - /* to generate these values: - * - cd to tests/resources/status, - * - mv .gitted .git - * - git diff --name-status - * - git diff - * - mv .git .gitted - */ - cl_assert_equal_i(13, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(4, exp.file_dels); - cl_assert_equal_i(4, exp.file_mods); - cl_assert_equal_i(1, exp.file_ignored); - cl_assert_equal_i(4, exp.file_untracked); - - cl_assert_equal_i(8, exp.hunks); - - cl_assert_equal_i(14, exp.lines); - cl_assert_equal_i(5, exp.line_ctxt); - cl_assert_equal_i(4, exp.line_adds); - cl_assert_equal_i(5, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + /* to generate these values: + * - cd to tests/resources/status, + * - mv .gitted .git + * - git diff --name-status + * - git diff + * - mv .git .gitted + */ + cl_assert_equal_i(13, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(4, exp.file_dels); + cl_assert_equal_i(4, exp.file_mods); + cl_assert_equal_i(1, exp.file_ignored); + cl_assert_equal_i(4, exp.file_untracked); + + cl_assert_equal_i(8, exp.hunks); + + cl_assert_equal_i(14, exp.lines); + cl_assert_equal_i(5, exp.line_ctxt); + cl_assert_equal_i(4, exp.line_adds); + cl_assert_equal_i(5, exp.line_dels); + } git_diff_list_free(diff); } @@ -65,6 +72,7 @@ void test_diff_workdir__to_tree(void) git_diff_list *diff = NULL; git_diff_list *diff2 = NULL; diff_expects exp; + int use_iterator; g_repo = cl_git_sandbox_init("status"); @@ -75,8 +83,6 @@ void test_diff_workdir__to_tree(void) opts.interhunk_lines = 1; opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; - memset(&exp, 0, sizeof(exp)); - /* You can't really generate the equivalent of git_diff_workdir_to_tree() * using C git. It really wants to interpose the index into the diff. * @@ -89,15 +95,23 @@ void test_diff_workdir__to_tree(void) */ cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, a, &diff)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); - cl_assert_equal_i(14, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(4, exp.file_dels); - cl_assert_equal_i(4, exp.file_mods); - cl_assert_equal_i(1, exp.file_ignored); - cl_assert_equal_i(5, exp.file_untracked); + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(14, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(4, exp.file_dels); + cl_assert_equal_i(4, exp.file_mods); + cl_assert_equal_i(1, exp.file_ignored); + cl_assert_equal_i(5, exp.file_untracked); + } /* Since there is no git diff equivalent, let's just assume that the * text diffs produced by git_diff_foreach are accurate here. We will @@ -117,22 +131,30 @@ void test_diff_workdir__to_tree(void) cl_git_pass(git_diff_merge(diff, diff2)); git_diff_list_free(diff2); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(15, exp.files); - cl_assert_equal_i(2, exp.file_adds); - cl_assert_equal_i(5, exp.file_dels); - cl_assert_equal_i(4, exp.file_mods); - cl_assert_equal_i(1, exp.file_ignored); - cl_assert_equal_i(3, exp.file_untracked); + cl_assert_equal_i(15, exp.files); + cl_assert_equal_i(2, exp.file_adds); + cl_assert_equal_i(5, exp.file_dels); + cl_assert_equal_i(4, exp.file_mods); + cl_assert_equal_i(1, exp.file_ignored); + cl_assert_equal_i(3, exp.file_untracked); - cl_assert_equal_i(11, exp.hunks); + cl_assert_equal_i(11, exp.hunks); - cl_assert_equal_i(17, exp.lines); - cl_assert_equal_i(4, exp.line_ctxt); - cl_assert_equal_i(8, exp.line_adds); - cl_assert_equal_i(5, exp.line_dels); + cl_assert_equal_i(17, exp.lines); + cl_assert_equal_i(4, exp.line_ctxt); + cl_assert_equal_i(8, exp.line_adds); + cl_assert_equal_i(5, exp.line_dels); + } git_diff_list_free(diff); diff = NULL; @@ -146,22 +168,30 @@ void test_diff_workdir__to_tree(void) cl_git_pass(git_diff_merge(diff, diff2)); git_diff_list_free(diff2); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); - cl_assert_equal_i(16, exp.files); - cl_assert_equal_i(5, exp.file_adds); - cl_assert_equal_i(4, exp.file_dels); - cl_assert_equal_i(3, exp.file_mods); - cl_assert_equal_i(1, exp.file_ignored); - cl_assert_equal_i(3, exp.file_untracked); + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(12, exp.hunks); + cl_assert_equal_i(16, exp.files); + cl_assert_equal_i(5, exp.file_adds); + cl_assert_equal_i(4, exp.file_dels); + cl_assert_equal_i(3, exp.file_mods); + cl_assert_equal_i(1, exp.file_ignored); + cl_assert_equal_i(3, exp.file_untracked); - cl_assert_equal_i(19, exp.lines); - cl_assert_equal_i(3, exp.line_ctxt); - cl_assert_equal_i(12, exp.line_adds); - cl_assert_equal_i(4, exp.line_dels); + cl_assert_equal_i(12, exp.hunks); + + cl_assert_equal_i(19, exp.lines); + cl_assert_equal_i(3, exp.line_ctxt); + cl_assert_equal_i(12, exp.line_adds); + cl_assert_equal_i(4, exp.line_dels); + } git_diff_list_free(diff); @@ -175,6 +205,7 @@ void test_diff_workdir__to_index_with_pathspec(void) git_diff_list *diff = NULL; diff_expects exp; char *pathspec = NULL; + int use_iterator; g_repo = cl_git_sandbox_init("status"); @@ -184,62 +215,93 @@ void test_diff_workdir__to_index_with_pathspec(void) opts.pathspec.strings = &pathspec; opts.pathspec.count = 1; - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); - cl_assert_equal_i(13, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(4, exp.file_dels); - cl_assert_equal_i(4, exp.file_mods); - cl_assert_equal_i(1, exp.file_ignored); - cl_assert_equal_i(4, exp.file_untracked); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, NULL, NULL)); + else + cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); + + cl_assert_equal_i(13, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(4, exp.file_dels); + cl_assert_equal_i(4, exp.file_mods); + cl_assert_equal_i(1, exp.file_ignored); + cl_assert_equal_i(4, exp.file_untracked); + } git_diff_list_free(diff); - memset(&exp, 0, sizeof(exp)); pathspec = "modified_file"; cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(0, exp.file_ignored); - cl_assert_equal_i(0, exp.file_untracked); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, NULL, NULL)); + else + cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(0, exp.file_ignored); + cl_assert_equal_i(0, exp.file_untracked); + } git_diff_list_free(diff); - memset(&exp, 0, sizeof(exp)); pathspec = "subdir"; cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); - cl_assert_equal_i(3, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(1, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(0, exp.file_ignored); - cl_assert_equal_i(1, exp.file_untracked); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, NULL, NULL)); + else + cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(1, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(0, exp.file_ignored); + cl_assert_equal_i(1, exp.file_untracked); + } git_diff_list_free(diff); - memset(&exp, 0, sizeof(exp)); pathspec = "*_deleted"; cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); - cl_assert_equal_i(2, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(2, exp.file_dels); - cl_assert_equal_i(0, exp.file_mods); - cl_assert_equal_i(0, exp.file_ignored); - cl_assert_equal_i(0, exp.file_untracked); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, NULL, NULL)); + else + cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL)); + + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(2, exp.file_dels); + cl_assert_equal_i(0, exp.file_mods); + cl_assert_equal_i(0, exp.file_ignored); + cl_assert_equal_i(0, exp.file_untracked); + } git_diff_list_free(diff); } @@ -249,6 +311,7 @@ void test_diff_workdir__filemode_changes(void) git_config *cfg; git_diff_list *diff = NULL; diff_expects exp; + int use_iterator; if (!cl_is_chmod_supported()) return; @@ -262,13 +325,20 @@ void test_diff_workdir__filemode_changes(void) cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); - cl_assert_equal_i(0, exp.files); - cl_assert_equal_i(0, exp.file_mods); - cl_assert_equal_i(0, exp.hunks); + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(0, exp.files); + cl_assert_equal_i(0, exp.file_mods); + cl_assert_equal_i(0, exp.hunks); + } git_diff_list_free(diff); @@ -278,13 +348,20 @@ void test_diff_workdir__filemode_changes(void) cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(0, exp.hunks); + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(0, exp.hunks); + } git_diff_list_free(diff); @@ -347,6 +424,7 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) diff_expects exp; char *pathspec = "staged_changes_modified_file"; git_tree *tree; + int use_iterator; /* For this file, * - head->index diff has 1 line of context, 1 line of diff @@ -366,46 +444,70 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) cl_git_pass(git_diff_index_to_tree(g_repo, &opts, tree, &diff_i2t)); cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff_w2i)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(1, exp.hunks); - cl_assert_equal_i(2, exp.lines); - cl_assert_equal_i(1, exp.line_ctxt); - cl_assert_equal_i(1, exp.line_adds); - cl_assert_equal_i(0, exp.line_dels); - - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(1, exp.hunks); - cl_assert_equal_i(3, exp.lines); - cl_assert_equal_i(2, exp.line_ctxt); - cl_assert_equal_i(1, exp.line_adds); - cl_assert_equal_i(0, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(1, exp.hunks); + cl_assert_equal_i(2, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(1, exp.line_adds); + cl_assert_equal_i(0, exp.line_dels); + } + + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(1, exp.hunks); + cl_assert_equal_i(3, exp.lines); + cl_assert_equal_i(2, exp.line_ctxt); + cl_assert_equal_i(1, exp.line_adds); + cl_assert_equal_i(0, exp.line_dels); + } cl_git_pass(git_diff_merge(diff_i2t, diff_w2i)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(1, exp.hunks); - cl_assert_equal_i(3, exp.lines); - cl_assert_equal_i(1, exp.line_ctxt); - cl_assert_equal_i(2, exp.line_adds); - cl_assert_equal_i(0, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(1, exp.hunks); + cl_assert_equal_i(3, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(2, exp.line_adds); + cl_assert_equal_i(0, exp.line_dels); + } git_diff_list_free(diff_i2t); git_diff_list_free(diff_w2i); @@ -419,6 +521,7 @@ void test_diff_workdir__eof_newline_changes(void) git_diff_list *diff = NULL; diff_expects exp; char *pathspec = "current_file"; + int use_iterator; g_repo = cl_git_sandbox_init("status"); @@ -427,18 +530,26 @@ void test_diff_workdir__eof_newline_changes(void) cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(0, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(0, exp.file_mods); - cl_assert_equal_i(0, exp.hunks); - cl_assert_equal_i(0, exp.lines); - cl_assert_equal_i(0, exp.line_ctxt); - cl_assert_equal_i(0, exp.line_adds); - cl_assert_equal_i(0, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(0, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(0, exp.file_mods); + cl_assert_equal_i(0, exp.hunks); + cl_assert_equal_i(0, exp.lines); + cl_assert_equal_i(0, exp.line_ctxt); + cl_assert_equal_i(0, exp.line_adds); + cl_assert_equal_i(0, exp.line_dels); + } git_diff_list_free(diff); @@ -446,18 +557,26 @@ void test_diff_workdir__eof_newline_changes(void) cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(1, exp.hunks); - cl_assert_equal_i(2, exp.lines); - cl_assert_equal_i(1, exp.line_ctxt); - cl_assert_equal_i(1, exp.line_adds); - cl_assert_equal_i(0, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(1, exp.hunks); + cl_assert_equal_i(2, exp.lines); + cl_assert_equal_i(1, exp.line_ctxt); + cl_assert_equal_i(1, exp.line_adds); + cl_assert_equal_i(0, exp.line_dels); + } git_diff_list_free(diff); @@ -465,18 +584,26 @@ void test_diff_workdir__eof_newline_changes(void) cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff)); - memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); - cl_assert_equal_i(1, exp.files); - cl_assert_equal_i(0, exp.file_adds); - cl_assert_equal_i(0, exp.file_dels); - cl_assert_equal_i(1, exp.file_mods); - cl_assert_equal_i(1, exp.hunks); - cl_assert_equal_i(3, exp.lines); - cl_assert_equal_i(0, exp.line_ctxt); - cl_assert_equal_i(1, exp.line_adds); - cl_assert_equal_i(2, exp.line_dels); + for (use_iterator = 0; use_iterator <= 1; use_iterator++) { + memset(&exp, 0, sizeof(exp)); + + if (use_iterator) + cl_git_pass(diff_foreach_via_iterator( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + else + cl_git_pass(git_diff_foreach( + diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); + + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(0, exp.file_adds); + cl_assert_equal_i(0, exp.file_dels); + cl_assert_equal_i(1, exp.file_mods); + cl_assert_equal_i(1, exp.hunks); + cl_assert_equal_i(3, exp.lines); + cl_assert_equal_i(0, exp.line_ctxt); + cl_assert_equal_i(1, exp.line_adds); + cl_assert_equal_i(2, exp.line_dels); + } git_diff_list_free(diff); } diff --git a/tests-clar/object/peel.c b/tests-clar/object/peel.c index f6d2a776..f4ea1eb0 100644 --- a/tests-clar/object/peel.c +++ b/tests-clar/object/peel.c @@ -65,7 +65,7 @@ void test_object_peel__can_peel_a_commit(void) void test_object_peel__cannot_peel_a_tree(void) { - assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); + assert_peel_error(GIT_ERROR, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); } void test_object_peel__cannot_peel_a_blob(void) @@ -73,7 +73,17 @@ void test_object_peel__cannot_peel_a_blob(void) assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT); } -void test_object_peel__cannot_target_any_object(void) +void test_object_peel__target_any_object_for_type_change(void) { - assert_peel_error(GIT_EAMBIGUOUS, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY); + /* tag to commit */ + assert_peel("e90810b8df3e80c413d903f631643c716887138d", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_ANY); + + /* commit to tree */ + assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY); + + /* fail to peel tree */ + assert_peel_error(GIT_ERROR, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY); + + /* fail to peel blob */ + assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); } diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c index 699655f2..b261240c 100644 --- a/tests-clar/refs/branches/delete.c +++ b/tests-clar/refs/branches/delete.c @@ -23,37 +23,37 @@ void test_refs_branches_delete__cleanup(void) cl_fixture_cleanup("testrepo.git"); } -void test_refs_branches_delete__can_not_delete_a_non_existing_branch(void) -{ - cl_git_fail(git_branch_delete(repo, "i-am-not-a-local-branch", GIT_BRANCH_LOCAL)); - cl_git_fail(git_branch_delete(repo, "neither/a-remote-one", GIT_BRANCH_REMOTE)); -} - void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void) { git_reference *head; + git_reference *branch; /* Ensure HEAD targets the local master branch */ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); cl_assert(strcmp("refs/heads/master", git_reference_target(head)) == 0); git_reference_free(head); - cl_git_fail(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_git_fail(git_branch_delete(branch)); + git_reference_free(branch); } void test_refs_branches_delete__can_not_delete_a_branch_if_HEAD_is_missing(void) { git_reference *head; + git_reference *branch = NULL; cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); git_reference_delete(head); - cl_git_fail(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_fail(git_branch_delete(branch)); + git_reference_free(branch); } void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void) { - git_reference *master, *head; + git_reference *master, *head, *branch; /* Detach HEAD and make it target the commit that "master" points to */ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master")); @@ -61,30 +61,21 @@ void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD( git_reference_free(head); git_reference_free(master); - cl_git_pass(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); } void test_refs_branches_delete__can_delete_a_local_branch(void) { - cl_git_pass(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL)); + git_reference *branch; + cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); } void test_refs_branches_delete__can_delete_a_remote_branch(void) { - cl_git_pass(git_branch_delete(repo, "nulltoken/master", GIT_BRANCH_REMOTE)); + git_reference *branch; + cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_delete(branch)); } -static void assert_non_exisitng_branch_removal(const char *branch_name, git_branch_t branch_type) -{ - int error; - error = git_branch_delete(repo, branch_name, branch_type); - - cl_git_fail(error); - cl_assert_equal_i(GIT_ENOTFOUND, error); -} - -void test_refs_branches_delete__deleting_a_non_existing_branch_returns_ENOTFOUND(void) -{ - assert_non_exisitng_branch_removal("i-do-not-locally-exist", GIT_BRANCH_LOCAL); - assert_non_exisitng_branch_removal("neither/remotely", GIT_BRANCH_REMOTE); -} diff --git a/tests-clar/refs/normalize.c b/tests-clar/refs/normalize.c index 135d0a9b..4e80e4b0 100644 --- a/tests-clar/refs/normalize.c +++ b/tests-clar/refs/normalize.c @@ -4,70 +4,111 @@ #include "git2/reflog.h" #include "reflog.h" - // Helpers -static void ensure_refname_normalized(int is_oid_ref, +static void ensure_refname_normalized(unsigned int flags, const char *input_refname, const char *expected_refname) { char buffer_out[GIT_REFNAME_MAX]; - if (is_oid_ref) - cl_git_pass(git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname)); - else - cl_git_pass(git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname)); + cl_git_pass(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); - if (expected_refname) - cl_assert(0 == strcmp(buffer_out, expected_refname)); + cl_assert_equal_i(0, strcmp(buffer_out, expected_refname)); } -static void ensure_refname_invalid(int is_oid_ref, const char *input_refname) +static void ensure_refname_invalid(unsigned int flags, const char *input_refname) { char buffer_out[GIT_REFNAME_MAX]; - if (is_oid_ref) - cl_git_fail(git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname)); - else - cl_git_fail(git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname)); + cl_git_fail(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags)); } -#define OID_REF 1 -#define SYM_REF 0 - +void test_refs_normalize__can_normalize_a_direct_reference_name(void) +{ + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/dummy/a", "refs/dummy/a"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/stash", "refs/stash"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/tags/a", "refs/tags/a"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/heads/a/b", "refs/heads/a/b"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/heads/a./b", "refs/heads/a./b"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "/refs///heads///a", "refs/heads/a"); +} +void test_refs_normalize__can_normalize_some_specific_one_level_direct_reference_names(void) +{ + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "HEAD", "HEAD"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "MERGE_HEAD", "MERGE_HEAD"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "FETCH_HEAD", "FETCH_HEAD"); +} -void test_refs_normalize__direct(void) +void test_refs_normalize__cannot_normalize_any_direct_reference_name(void) { - // normalize a direct (OID) reference name - ensure_refname_invalid(OID_REF, "a"); - ensure_refname_invalid(OID_REF, ""); - ensure_refname_invalid(OID_REF, "refs/heads/a/"); - ensure_refname_invalid(OID_REF, "refs/heads/a."); - ensure_refname_invalid(OID_REF, "refs/heads/a.lock"); - ensure_refname_normalized(OID_REF, "refs/dummy/a", NULL); - ensure_refname_normalized(OID_REF, "refs/stash", NULL); - ensure_refname_normalized(OID_REF, "refs/tags/a", "refs/tags/a"); - ensure_refname_normalized(OID_REF, "refs/heads/a/b", "refs/heads/a/b"); - ensure_refname_normalized(OID_REF, "refs/heads/a./b", "refs/heads/a./b"); - ensure_refname_invalid(OID_REF, "refs/heads/foo?bar"); - ensure_refname_invalid(OID_REF, "refs/heads\foo"); - ensure_refname_normalized(OID_REF, "refs/heads/v@ation", "refs/heads/v@ation"); - ensure_refname_normalized(OID_REF, "refs///heads///a", "refs/heads/a"); - ensure_refname_invalid(OID_REF, "refs/heads/.a/b"); - ensure_refname_invalid(OID_REF, "refs/heads/foo/../bar"); - ensure_refname_invalid(OID_REF, "refs/heads/foo..bar"); - ensure_refname_invalid(OID_REF, "refs/heads/./foo"); - ensure_refname_invalid(OID_REF, "refs/heads/v@{ation"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "a"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "/a"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "//a"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, ""); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/a/"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/a."); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/a.lock"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/foo?bar"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads\foo"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation"); + ensure_refname_normalized( + GIT_REF_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/.a/b"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/foo/../bar"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/foo..bar"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/./foo"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "refs/heads/v@{ation"); } void test_refs_normalize__symbolic(void) { - // normalize a symbolic reference name - ensure_refname_normalized(SYM_REF, "a", "a"); - ensure_refname_normalized(SYM_REF, "a/b", "a/b"); - ensure_refname_normalized(SYM_REF, "refs///heads///a", "refs/heads/a"); - ensure_refname_invalid(SYM_REF, ""); - ensure_refname_invalid(SYM_REF, "heads\foo"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, ""); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "heads\foo"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "///"); + + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "a", "a"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "a/b", "a/b"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs///heads///a", "refs/heads/a"); + + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "HEAD", "HEAD"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "MERGE_HEAD", "MERGE_HEAD"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "FETCH_HEAD", "FETCH_HEAD"); } /* Ported from JGit, BSD licence. @@ -77,31 +118,42 @@ void test_refs_normalize__jgit_suite(void) // tests borrowed from JGit /* EmptyString */ - ensure_refname_invalid(SYM_REF, ""); - ensure_refname_invalid(SYM_REF, "/"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, ""); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "/"); /* MustHaveTwoComponents */ - ensure_refname_invalid(OID_REF, "master"); - ensure_refname_normalized(SYM_REF, "heads/master", "heads/master"); + ensure_refname_invalid( + GIT_REF_FORMAT_NORMAL, "master"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "heads/master", "heads/master"); /* ValidHead */ - ensure_refname_normalized(SYM_REF, "refs/heads/master", "refs/heads/master"); - ensure_refname_normalized(SYM_REF, "refs/heads/pu", "refs/heads/pu"); - ensure_refname_normalized(SYM_REF, "refs/heads/z", "refs/heads/z"); - ensure_refname_normalized(SYM_REF, "refs/heads/FoO", "refs/heads/FoO"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master", "refs/heads/master"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/pu", "refs/heads/pu"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/z", "refs/heads/z"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/FoO", "refs/heads/FoO"); /* ValidTag */ - ensure_refname_normalized(SYM_REF, "refs/tags/v1.0", "refs/tags/v1.0"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/tags/v1.0", "refs/tags/v1.0"); /* NoLockSuffix */ - ensure_refname_invalid(SYM_REF, "refs/heads/master.lock"); + ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master.lock"); /* NoDirectorySuffix */ - ensure_refname_invalid(SYM_REF, "refs/heads/master/"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master/"); /* NoSpace */ - ensure_refname_invalid(SYM_REF, "refs/heads/i haz space"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/i haz space"); /* NoAsciiControlCharacters */ { @@ -112,89 +164,153 @@ void test_refs_normalize__jgit_suite(void) strncpy(buffer + 15, (const char *)&c, 1); strncpy(buffer + 16, "er", 2); buffer[18 - 1] = '\0'; - ensure_refname_invalid(SYM_REF, buffer); + ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, buffer); } } /* NoBareDot */ - ensure_refname_invalid(SYM_REF, "refs/heads/."); - ensure_refname_invalid(SYM_REF, "refs/heads/.."); - ensure_refname_invalid(SYM_REF, "refs/heads/./master"); - ensure_refname_invalid(SYM_REF, "refs/heads/../master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/."); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.."); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/./master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/../master"); /* NoLeadingOrTrailingDot */ - ensure_refname_invalid(SYM_REF, "."); - ensure_refname_invalid(SYM_REF, "refs/heads/.bar"); - ensure_refname_invalid(SYM_REF, "refs/heads/..bar"); - ensure_refname_invalid(SYM_REF, "refs/heads/bar."); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "."); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.bar"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/..bar"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/bar."); /* ContainsDot */ - ensure_refname_normalized(SYM_REF, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r"); - ensure_refname_invalid(SYM_REF, "refs/heads/master..pu"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master..pu"); /* NoMagicRefCharacters */ - ensure_refname_invalid(SYM_REF, "refs/heads/master^"); - ensure_refname_invalid(SYM_REF, "refs/heads/^master"); - ensure_refname_invalid(SYM_REF, "^refs/heads/master"); - - ensure_refname_invalid(SYM_REF, "refs/heads/master~"); - ensure_refname_invalid(SYM_REF, "refs/heads/~master"); - ensure_refname_invalid(SYM_REF, "~refs/heads/master"); - - ensure_refname_invalid(SYM_REF, "refs/heads/master:"); - ensure_refname_invalid(SYM_REF, "refs/heads/:master"); - ensure_refname_invalid(SYM_REF, ":refs/heads/master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master^"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/^master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "^refs/heads/master"); + + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master~"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/~master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "~refs/heads/master"); + + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master:"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/:master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, ":refs/heads/master"); /* ShellGlob */ - ensure_refname_invalid(SYM_REF, "refs/heads/master?"); - ensure_refname_invalid(SYM_REF, "refs/heads/?master"); - ensure_refname_invalid(SYM_REF, "?refs/heads/master"); - - ensure_refname_invalid(SYM_REF, "refs/heads/master["); - ensure_refname_invalid(SYM_REF, "refs/heads/[master"); - ensure_refname_invalid(SYM_REF, "[refs/heads/master"); - - ensure_refname_invalid(SYM_REF, "refs/heads/master*"); - ensure_refname_invalid(SYM_REF, "refs/heads/*master"); - ensure_refname_invalid(SYM_REF, "*refs/heads/master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master?"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/?master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "?refs/heads/master"); + + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master["); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/[master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "[refs/heads/master"); + + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master*"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/*master"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "*refs/heads/master"); /* ValidSpecialCharacters */ - ensure_refname_normalized(SYM_REF, "refs/heads/!", "refs/heads/!"); - ensure_refname_normalized(SYM_REF, "refs/heads/\"", "refs/heads/\""); - ensure_refname_normalized(SYM_REF, "refs/heads/#", "refs/heads/#"); - ensure_refname_normalized(SYM_REF, "refs/heads/$", "refs/heads/$"); - ensure_refname_normalized(SYM_REF, "refs/heads/%", "refs/heads/%"); - ensure_refname_normalized(SYM_REF, "refs/heads/&", "refs/heads/&"); - ensure_refname_normalized(SYM_REF, "refs/heads/'", "refs/heads/'"); - ensure_refname_normalized(SYM_REF, "refs/heads/(", "refs/heads/("); - ensure_refname_normalized(SYM_REF, "refs/heads/)", "refs/heads/)"); - ensure_refname_normalized(SYM_REF, "refs/heads/+", "refs/heads/+"); - ensure_refname_normalized(SYM_REF, "refs/heads/,", "refs/heads/,"); - ensure_refname_normalized(SYM_REF, "refs/heads/-", "refs/heads/-"); - ensure_refname_normalized(SYM_REF, "refs/heads/;", "refs/heads/;"); - ensure_refname_normalized(SYM_REF, "refs/heads/<", "refs/heads/<"); - ensure_refname_normalized(SYM_REF, "refs/heads/=", "refs/heads/="); - ensure_refname_normalized(SYM_REF, "refs/heads/>", "refs/heads/>"); - ensure_refname_normalized(SYM_REF, "refs/heads/@", "refs/heads/@"); - ensure_refname_normalized(SYM_REF, "refs/heads/]", "refs/heads/]"); - ensure_refname_normalized(SYM_REF, "refs/heads/_", "refs/heads/_"); - ensure_refname_normalized(SYM_REF, "refs/heads/`", "refs/heads/`"); - ensure_refname_normalized(SYM_REF, "refs/heads/{", "refs/heads/{"); - ensure_refname_normalized(SYM_REF, "refs/heads/|", "refs/heads/|"); - ensure_refname_normalized(SYM_REF, "refs/heads/}", "refs/heads/}"); + ensure_refname_normalized + (GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/!", "refs/heads/!"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\"", "refs/heads/\""); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/#", "refs/heads/#"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/$", "refs/heads/$"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/%", "refs/heads/%"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/&", "refs/heads/&"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/'", "refs/heads/'"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/(", "refs/heads/("); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/)", "refs/heads/)"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/+", "refs/heads/+"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/,", "refs/heads/,"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/-", "refs/heads/-"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/;", "refs/heads/;"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/<", "refs/heads/<"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/=", "refs/heads/="); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/>", "refs/heads/>"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/@", "refs/heads/@"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/]", "refs/heads/]"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/_", "refs/heads/_"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/`", "refs/heads/`"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/{", "refs/heads/{"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/|", "refs/heads/|"); + ensure_refname_normalized( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/}", "refs/heads/}"); // This is valid on UNIX, but not on Windows // hence we make in invalid due to non-portability // - ensure_refname_invalid(SYM_REF, "refs/heads/\\"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\\"); /* UnicodeNames */ /* * Currently this fails. - * ensure_refname_normalized(SYM_REF, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m"); + * ensure_refname_normalized(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m"); */ /* RefLogQueryIsValidRef */ - ensure_refname_invalid(SYM_REF, "refs/heads/master@{1}"); - ensure_refname_invalid(SYM_REF, "refs/heads/master@{1.hour.ago}"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1}"); + ensure_refname_invalid( + GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1.hour.ago}"); +} + +void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_version(void) +{ + char buffer_out[21]; + + cl_git_pass(git_reference_normalize_name( + buffer_out, 21, "//refs//heads/long///name", GIT_REF_FORMAT_NORMAL)); + cl_git_fail(git_reference_normalize_name( + buffer_out, 20, "//refs//heads/long///name", GIT_REF_FORMAT_NORMAL)); } diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c index 67a9917d..f76e8bc3 100644 --- a/tests-clar/repo/init.c +++ b/tests-clar/repo/init.c @@ -378,3 +378,18 @@ void test_repo_init__extended_with_template(void) cleanup_repository("templated.git"); } + +void test_repo_init__can_reinit_an_initialized_repository(void) +{ + git_repository *reinit; + + cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0)); + cl_git_pass(git_repository_init(&_repo, "extended", false)); + + cl_git_pass(git_repository_init(&reinit, "extended", false)); + + cl_assert_equal_s(git_repository_path(_repo), git_repository_path(reinit)); + + git_repository_free(reinit); + cleanup_repository("extended"); +} diff --git a/tests-clar/resources/attr/.gitted/index b/tests-clar/resources/attr/.gitted/index Binary files differindex 943e2243..439ffb15 100644 --- a/tests-clar/resources/attr/.gitted/index +++ b/tests-clar/resources/attr/.gitted/index diff --git a/tests-clar/resources/issue_592/.gitted/index b/tests-clar/resources/issue_592/.gitted/index Binary files differindex eaeb5d76..be7a29d9 100644 --- a/tests-clar/resources/issue_592/.gitted/index +++ b/tests-clar/resources/issue_592/.gitted/index diff --git a/tests-clar/resources/status/.gitted/index b/tests-clar/resources/status/.gitted/index Binary files differindex 9a383ec0..2af99a18 100644 --- a/tests-clar/resources/status/.gitted/index +++ b/tests-clar/resources/status/.gitted/index diff --git a/tests-clar/resources/submod2/gitmodules b/tests-clar/resources/submod2/gitmodules index 7b150b18..4c31108e 100644 --- a/tests-clar/resources/submod2/gitmodules +++ b/tests-clar/resources/submod2/gitmodules @@ -19,3 +19,6 @@ [submodule "sm_added_and_uncommited"] path = sm_added_and_uncommited url = ../submod2_target +[submodule "sm_gitmodules_only"] + path = sm_gitmodules_only + url = ../submod2_target diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index 6f3c1c06..126ca7d9 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -179,3 +179,11 @@ void test_revwalk_basic__push_head_hide_ref_nobase(void) /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */ cl_assert(i == 7); } + +void test_revwalk_basic__disallow_non_commit(void) +{ + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91")); + cl_git_fail(git_revwalk_push(_walk, &oid)); +} diff --git a/tests-clar/submodule/lookup.c b/tests-clar/submodule/lookup.c index 669338f1..94eb19b5 100644 --- a/tests-clar/submodule/lookup.c +++ b/tests-clar/submodule/lookup.c @@ -34,6 +34,10 @@ void test_submodule_lookup__simple_lookup(void) cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); cl_assert(sm); + /* lookup pending change in .gitmodules that is neither in HEAD nor index */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); + cl_assert(sm); + /* lookup git repo subdir that is not added as submodule */ cl_assert(git_submodule_lookup(&sm, g_repo, "not_submodule") == GIT_EEXISTS); @@ -106,5 +110,5 @@ void test_submodule_lookup__foreach(void) sm_lookup_data data; memset(&data, 0, sizeof(data)); cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data)); - cl_assert_equal_i(7, data.count); + cl_assert_equal_i(8, data.count); } |