diff options
101 files changed, 2023 insertions, 772 deletions
@@ -16,6 +16,7 @@ Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk> Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org> Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de> Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com> -Edward Thomson <ethomson@microsoft.com> <ethomson@edwardthomson.com> +Edward Thomson <ethomson@github.com> <ethomson@microsoft.com> +Edward Thomson <ethomson@github.com> <ethomson@edwardthomson.com> J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com> Russell Belfer <rb@github.com> <arrbee@arrbee.com> diff --git a/.travis.yml b/.travis.yml index 9022fdec2..2f3ffe355 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,13 +46,13 @@ matrix: - compiler: gcc env: - VALGRIND=1 - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" os: linux allow_failures: - env: COVERITY=1 - env: - VALGRIND=1 - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug" install: - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index c84d27da3..ec5a0d336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,15 @@ v0.23 + 1 correctly formed, it will give bad results. This is the git approach and cuts a significant amount of time when reading the trees. +* Filter registration is now protected against concurrent + registration. + +* Filenames which are not valid on Windows in an index no longer cause + to fail to parse it on that OS. + +* Rebases can now be performed purely in-memory, without touching the + repository's workdir. + ### API additions * `git_config_lock()` has been added, which allow for @@ -35,11 +44,17 @@ v0.23 + 1 * `git_fetch_options` and `git_push_options` have gained a `custom_headers` field to set the extra HTTP header fields to send. - * `git_stream_register_tls()` lets you register a callback to be used as the constructor for a TLS stream instead of the libgit2 built-in one. +* `git_commit_header_field()` allows you to look up a specific header + field in a commit. + +* `git_commit_extract_signature()` extracts the signature from a + commit and gives you both the signature and the signed data so you + can verify it. + ### API removals ### Breaking API changes @@ -75,6 +90,12 @@ v0.23 + 1 `GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent to the system level configuration. +* `git_rebase_init()` not also takes a merge options. + +* The index no longer performs locking itself. This is not something + users of the library should have been relying on as it's not part of + the concurrency guarantees. + v0.23 ------ @@ -307,8 +328,8 @@ v0.23 * `git_rebase_options` now contains a `git_checkout_options` struct that will be used for functions that modify the working directory, - namely `git_checkout_init`, `git_checkout_next` and - `git_checkout_abort`. As a result, `git_rebase_open` now also takes + namely `git_rebase_init`, `git_rebase_next` and + `git_rebase_abort`. As a result, `git_rebase_open` now also takes a `git_rebase_options` and only the `git_rebase_init` and `git_rebase_open` functions take a `git_rebase_options`, where they will persist the options to subsequent `git_rebase` calls. diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a52bc01..931b06459 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,11 @@ OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) OPTION( VALGRIND "Configure build for valgrind" OFF ) OPTION( CURL "User curl for HTTP if available" ON) +OPTION( DEBUG_POOL "Enable debug pool allocator" OFF ) + +IF(DEBUG_POOL) + ADD_DEFINITIONS(-DGIT_DEBUG_POOL) +ENDIF() IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET( USE_ICONV ON ) @@ -86,17 +91,21 @@ IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OPTION( USE_OPENSSL "Link with and use openssl library" ON ) ENDIF() -CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atim "sys/types.h;sys/stat.h" - HAVE_STRUCT_STAT_ST_ATIM LANGUAGE C) -CHECK_STRUCT_HAS_MEMBER ("struct stat" st_atimespec "sys/types.h;sys/stat.h" - HAVE_STRUCT_STAT_ST_ATIMESPEC LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h" + HAVE_STRUCT_STAT_ST_MTIMESPEC LANGUAGE C) +CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_nsec sys/stat.h + HAVE_STRUCT_STAT_MTIME_NSEC LANGUAGE C) -IF (HAVE_STRUCT_STAT_ST_ATIM) +IF (HAVE_STRUCT_STAT_ST_MTIM) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) -ELSEIF (HAVE_STRUCT_STAT_ST_ATIMESPEC) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec sys/stat.h HAVE_STRUCT_STAT_NSEC LANGUAGE C) +ELSE () + SET( HAVE_STRUCT_STAT_NSEC ON ) ENDIF() IF (HAVE_STRUCT_STAT_NSEC OR WIN32) @@ -257,7 +266,8 @@ IF (WIN32 AND WINHTTP) LINK_DIRECTORIES(${LIBWINHTTP_PATH}) ENDIF () - LINK_LIBRARIES(winhttp rpcrt4 crypt32) + LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32) + LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32") ELSE () IF (CURL) PKG_CHECK_MODULES(CURL libcurl) @@ -538,8 +548,12 @@ IF (USE_NSEC) ADD_DEFINITIONS(-DGIT_USE_NSEC) ENDIF() -IF (HAVE_STRUCT_STAT_ST_ATIMESPEC) - ADD_DEFINITIONS(-DGIT_USE_STAT_ATIMESPEC) +IF (HAVE_STRUCT_STAT_ST_MTIM) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIM) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIMESPEC) +ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC) + ADD_DEFINITIONS(-DGIT_USE_STAT_MTIME_NSEC) ENDIF() ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..0a0e4ebab --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [libgit2@gmail.com][email]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[email]: mailto:libgit2@gmail.com +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 5b8238a78..0be4b33cc 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -3,6 +3,38 @@ We like to keep the source consistent and readable. Herein are some guidelines that should help with that. +## External API + +We have a few rules to avoid surprising ways of calling functions and +some rules for consumers of the library to avoid stepping on each +other's toes. + + - Property accessors return the value directly (e.g. an `int` or + `const char *`) but if a function can fail, we return a `int` value + and the output parameters go first in the parameter list, followed + by the object that a function is operating on, and then any other + arguments the function may need. + + - If a function returns an object as a return value, that function is + a getter and the object's lifetime is tied to the parent + object. Objects which are returned as the first argument as a + pointer-to-pointer are owned by the caller and it is repsponsible + for freeing it. Strings are returned via `git_buf` in order to + allow for re-use and safe freeing. + + - Most of what libgit2 does relates to I/O so you as a general rule + you should assume that any function can fail due to errors as even + getting data from the filesystem can result in all sorts of errors + and complex failure cases. + + - Paths inside the Git system are separated by a slash (0x2F). If a + function accepts a path on disk, then backslashes (0x5C) are also + accepted on Windows. + + - Do not mix allocators. If something has been allocated by libgit2, + you do not know which is the right free function in the general + case. Use the free functions provided for each object type. + ## Compatibility `libgit2` runs on many different platforms with many different compilers. diff --git a/PROJECTS.md b/PROJECTS.md index 4f200b7f9..87ce78f02 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -48,7 +48,7 @@ These are good small projects to get started with libgit2. a new example that mirrors the behavior. Examples don't have to be perfect emulations, but should demonstrate how to use the libgit2 APIs to get results that are similar to Git commands. This lets you (and us) - easily exercise a particular facet of the API and measure compatability + easily exercise a particular facet of the API and measure compatibility and feature parity with core git. * Submit a PR to clarify documentation! While we do try to document all of the APIs, your fresh eyes on the documentation will find areas that are @@ -75,8 +75,6 @@ might make good smaller projects by themselves. * Extract the Git tests that exercise that command * Convert the tests to call our emulation * These tests could go in examples/tests/... -* Fix symlink support for files in the .git directory (i.e. don't overwrite - the symlinks when writing the file contents back out) * Add hooks API to enumerate and manage hooks (not run them at this point) * Enumeration of available hooks * Lookup API to see which hooks have a script and get the script @@ -85,8 +83,6 @@ might make good smaller projects by themselves. executes the action in question * Isolate logic of ignore evaluation into a standalone API * Upgrade internal libxdiff code to latest from core Git -* Improve index internals with hashtable lookup for files instead of - using binary search every time * Tree builder improvements: * Extend to allow building a tree hierarchy * Apply-patch API @@ -80,6 +80,12 @@ Threading See [THREADING](THREADING.md) for information +Conventions +=========== + +See [CONVENTIONS](CONVENTIONS.md) for an overview of the external +and internal API/coding conventions we use. + Building libgit2 - Using CMake ============================== diff --git a/include/git2/commit.h b/include/git2/commit.h index 34d29ed81..3488c7440 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -264,6 +264,24 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field); /** + * Extract the signature from a commit + * + * If the id is not for a commit, the error class will be + * `GITERR_INVALID`. If the commit does not have a signature, the + * error class will be `GITERR_OBJECT`. + * + * @param signature the signature block + * @param signed_data signed data; this is the commit contents minus the signature block + * @param repo the repository in which the commit exists + * @param commit_id the commit from which to extract the data + * @param field the name of the header field containing the signature + * block; pass `NULL` to extract the default 'gpgsig' + * @return 0 on success, GIT_ENOTFOUND if the id is not for a commit + * or the commit does not have a signature. + */ +GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field); + +/** * Create new commit in the repository from a list of `git_object` pointers * * The message will **not** be cleaned up automatically. You can do that diff --git a/include/git2/common.h b/include/git2/common.h index ee230dfae..c26030840 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -245,6 +245,12 @@ typedef enum { * * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent) * + * > Set the value of the User-Agent header. This value will be + * > appended to "git/1.0", for compatibility with other git clients. + * > + * > - `user_agent` is the value that will be delivered as the + * > User-Agent header on HTTP requests. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/include/git2/diff.h b/include/git2/diff.h index 3eb265652..c35701a46 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -1194,7 +1194,7 @@ typedef enum { } git_diff_stats_format_t; /** - * Accumlate diff statistics for all patches. + * Accumulate diff statistics for all patches. * * @param out Structure containg the diff statistics. * @param diff A git_diff generated by one of the above functions. diff --git a/include/git2/errors.h b/include/git2/errors.h index 1b528cf25..3ecea34bf 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -126,11 +126,6 @@ GIT_EXTERN(void) giterr_clear(void); * This error message is stored in thread-local storage and only applies * to the particular thread that this libgit2 call is made from. * - * NOTE: Passing the `error_class` as GITERR_OS has a special behavior: we - * attempt to append the system default error message for the last OS error - * that occurred and then clear the last error. The specific implementation - * of looking up and clearing this last OS error will vary by platform. - * * @param error_class One of the `git_error_t` enum above describing the * general subsystem that is responsible for the error. * @param string The formatted error message to keep diff --git a/include/git2/merge.h b/include/git2/merge.h index af53ead22..560797a0c 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -527,10 +527,6 @@ GIT_EXTERN(int) git_merge_trees( * or checked out. If the index is to be converted to a tree, the caller * should resolve any conflicts that arose as part of the merge. * - * The merge performed uses the first common ancestor, unlike the - * `git-merge-recursive` strategy, which may produce an artificial common - * ancestor tree when there are multiple ancestors. - * * The returned index must be freed explicitly with `git_index_free`. * * @param out pointer to store the index result in @@ -553,10 +549,6 @@ GIT_EXTERN(int) git_merge_commits( * to the index. Callers should inspect the repository's index after this * completes, resolve any conflicts and prepare a commit. * - * The merge performed uses the first common ancestor, unlike the - * `git-merge-recursive` strategy, which may produce an artificial common - * ancestor tree when there are multiple ancestors. - * * For compatibility with git, the repository is put into a merging * state. Once the commit is done (or if the uses wishes to abort), * you should clear this state by calling diff --git a/include/git2/rebase.h b/include/git2/rebase.h index d9aa175c7..9b9065ee4 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -39,9 +39,18 @@ typedef struct { int quiet; /** + * Used by `git_rebase_init`, this will begin an in-memory rebase, + * which will allow callers to step through the rebase operations and + * commit the rebased changes, but will not rewind HEAD or update the + * repository to be in a rebasing state. This will not interfere with + * the working directory (if there is one). + */ + int inmemory; + + /** * Used by `git_rebase_finish`, this is the name of the notes reference * used to rewrite notes for rebased commits when finishing the rebase; - * if NULL, the contents of the coniguration option `notes.rewriteRef` + * if NULL, the contents of the configuration option `notes.rewriteRef` * is examined, unless the configuration option `notes.rewrite.rebase` * is set to false. If `notes.rewriteRef` is also NULL, notes will * not be rewritten. @@ -49,8 +58,13 @@ typedef struct { const char *rewrite_notes_ref; /** + * Options to control how trees are merged during `git_rebase_next`. + */ + git_merge_options merge_options; + + /** * Options to control how files are written during `git_rebase_init`, - * `git_checkout_next` and `git_checkout_abort`. Note that a minimum + * `git_rebase_next` and `git_rebase_abort`. Note that a minimum * strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`, * and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in * `abort` to match git semantics. @@ -101,7 +115,8 @@ typedef enum { #define GIT_REBASE_OPTIONS_VERSION 1 #define GIT_REBASE_OPTIONS_INIT \ - {GIT_REBASE_OPTIONS_VERSION, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT} + { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \ + GIT_CHECKOUT_OPTIONS_INIT} /** Indicates that a rebase operation is not (yet) in progress. */ #define GIT_REBASE_NO_OPERATION SIZE_MAX @@ -227,6 +242,21 @@ GIT_EXTERN(int) git_rebase_next( git_rebase *rebase); /** + * Gets the index produced by the last operation, which is the result + * of `git_rebase_next` and which will be committed by the next + * invocation of `git_rebase_commit`. This is useful for resolving + * conflicts in an in-memory rebase before committing them. You must + * call `git_index_free` when you are finished with this. + * + * This is only applicable for in-memory rebases; for rebases within + * a working directory, the changes were applied to the repository's + * index. + */ +GIT_EXTERN(int) git_rebase_inmemory_index( + git_index **index, + git_rebase *rebase); + +/** * Commits the current patch. You must have resolved any conflicts that * were introduced during the patch application from the `git_rebase_next` * invocation. diff --git a/include/git2/stash.h b/include/git2/stash.h index b321dc34e..733d75a7f 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -150,7 +150,7 @@ typedef struct git_stash_apply_options { * `GIT_STASH_APPLY_OPTIONS_INIT` here. * @return Zero on success; -1 on failure. */ -int git_stash_apply_init_options( +GIT_EXTERN(int) git_stash_apply_init_options( git_stash_apply_options *opts, unsigned int version); /** diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h index baf1515d6..d0e5d4d6f 100644 --- a/include/git2/sys/filter.h +++ b/include/git2/sys/filter.h @@ -127,17 +127,6 @@ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *sr */ GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src); -/* - * struct git_filter - * - * The filter lifecycle: - * - initialize - first use of filter - * - shutdown - filter removed/unregistered from system - * - check - considering filter for file - * - apply - apply filter to file contents - * - cleanup - done with file - */ - /** * Initialize callback on filter * @@ -233,31 +222,51 @@ typedef void (*git_filter_cleanup_fn)( * To associate extra data with a filter, allocate extra data and put the * `git_filter` struct at the start of your data buffer, then cast the * `self` pointer to your larger structure when your callback is invoked. - * - * `version` should be set to GIT_FILTER_VERSION - * - * `attributes` is a whitespace-separated list of attribute names to check - * for this filter (e.g. "eol crlf text"). If the attribute name is bare, - * it will be simply loaded and passed to the `check` callback. If it has - * a value (i.e. "name=value"), the attribute must match that value for - * the filter to be applied. The value may be a wildcard (eg, "name=*"), - * in which case the filter will be invoked for any value for the given - * attribute name. See the attribute parameter of the `check` callback - * for the attribute value that was specified. - * - * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks - * are all documented above with the respective function pointer typedefs. */ struct git_filter { + /** The `version` field should be set to `GIT_FILTER_VERSION`. */ unsigned int version; + /** + * A whitespace-separated list of attribute names to check for this + * filter (e.g. "eol crlf text"). If the attribute name is bare, it + * will be simply loaded and passed to the `check` callback. If it + * has a value (i.e. "name=value"), the attribute must match that + * value for the filter to be applied. The value may be a wildcard + * (eg, "name=*"), in which case the filter will be invoked for any + * value for the given attribute name. See the attribute parameter + * of the `check` callback for the attribute value that was specified. + */ const char *attributes; + /** Called when the filter is first used for any file. */ git_filter_init_fn initialize; + + /** Called when the filter is removed or unregistered from the system. */ git_filter_shutdown_fn shutdown; + + /** + * Called to determine whether the filter should be invoked for a + * given file. If this function returns `GIT_PASSTHROUGH` then the + * `apply` function will not be invoked and the contents will be passed + * through unmodified. + */ git_filter_check_fn check; + + /** + * Called to actually apply the filter to file contents. If this + * function returns `GIT_PASSTHROUGH` then the contents will be passed + * through unmodified. + */ git_filter_apply_fn apply; + + /** + * Called to apply the filter in a streaming manner. If this is not + * specified then the system will call `apply` with the whole buffer. + */ git_filter_stream_fn stream; + + /** Called when the system is done filtering for a file. */ git_filter_cleanup_fn cleanup; }; diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h index 29a99f798..2e2b87e68 100644 --- a/include/git2/sys/index.h +++ b/include/git2/sys/index.h @@ -25,7 +25,7 @@ typedef struct git_index_name_entry { /** Representation of a resolve undo entry in the index. */ typedef struct git_index_reuc_entry { - unsigned int mode[3]; + uint32_t mode[3]; git_oid oid[3]; char *path; } git_index_reuc_entry; diff --git a/libgit2.pc.in b/libgit2.pc.in index 880266a30..329a560a7 100644 --- a/libgit2.pc.in +++ b/libgit2.pc.in @@ -6,7 +6,7 @@ Name: libgit2 Description: The git library, take 2 Version: @LIBGIT2_VERSION_STRING@ -Libs: -L${libdir} -lgit2 +Libs: -L"${libdir}" -lgit2 Libs.private: @LIBGIT2_PC_LIBS@ Requires.private: @LIBGIT2_PC_REQUIRES@ diff --git a/script/coverity.sh b/script/coverity.sh index dcfeffc1d..8c826892f 100755 --- a/script/coverity.sh +++ b/script/coverity.sh @@ -33,6 +33,8 @@ if [ ! -d "$TOOL_BASE" ]; then ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis fi +cp script/user_nodefs.h "$TOOL_BASE"/cov-analysis/config/user_nodefs.h + COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build" # Configure and build @@ -48,10 +50,9 @@ COVERITY_UNSUPPORTED=1 \ tar czf libgit2.tgz cov-int SHA=$(git rev-parse --short HEAD) curl \ - --form project=libgit2 \ --form token="$COVERITY_TOKEN" \ --form email=bs@github.com \ --form file=@libgit2.tgz \ --form version="$SHA" \ --form description="Travis build" \ - http://scan5.coverity.com/cgi-bin/upload.py + https://scan.coverity.com/builds?project=libgit2 diff --git a/script/user_nodefs.h b/script/user_nodefs.h new file mode 100644 index 000000000..3c06a706d --- /dev/null +++ b/script/user_nodefs.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); } +#nodef GITERR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ + if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); } + +#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':') + +#nodef git_vector_foreach(v, iter, elem) \ + for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ ) + +#nodef git_vector_rforeach(v, iter, elem) \ + for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- ) diff --git a/src/attr_file.c b/src/attr_file.c index 500c99bd9..11d149358 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -123,7 +123,7 @@ int git_attr_file__load( break; } case GIT_ATTR_FILE__FROM_FILE: { - int fd; + int fd = -1; /* For open or read errors, pretend that we got ENOTFOUND. */ /* TODO: issue warning when warning API is available */ @@ -133,7 +133,8 @@ int git_attr_file__load( (fd = git_futils_open_ro(entry->fullpath)) < 0 || (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0) nonexistent = true; - else + + if (fd >= 0) p_close(fd); break; diff --git a/src/checkout.c b/src/checkout.c index a92ad0825..deeee62e0 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1226,7 +1226,7 @@ static int checkout_verify_paths( int action, git_diff_delta *delta) { - unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT; + unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS; if (action & CHECKOUT_ACTION__REMOVE) { if (!git_path_isvalid(repo, delta->old_file.path, flags)) { @@ -1487,8 +1487,10 @@ static int blob_content_to_file( if (!data->opts.disable_filters && (error = git_filter_list__load_ext( &fl, data->repo, blob, hint_path, - GIT_FILTER_TO_WORKTREE, &filter_opts))) + GIT_FILTER_TO_WORKTREE, &filter_opts))) { + p_close(fd); return error; + } /* setup the writer */ memset(&writer, 0, sizeof(struct checkout_stream)); @@ -2519,7 +2521,8 @@ int git_checkout_iterator( if (data.opts.baseline_index) { if ((error = git_iterator_for_index( - &baseline, data.opts.baseline_index, &baseline_opts)) < 0) + &baseline, git_index_owner(data.opts.baseline_index), + data.opts.baseline_index, &baseline_opts)) < 0) goto cleanup; } else { if ((error = git_iterator_for_tree( @@ -2631,7 +2634,7 @@ int git_checkout_index( return error; GIT_REFCOUNT_INC(index); - if (!(error = git_iterator_for_index(&index_i, index, NULL))) + if (!(error = git_iterator_for_index(&index_i, repo, index, NULL))) error = git_checkout_iterator(index_i, index, opts); if (owned) diff --git a/src/commit.c b/src/commit.c index 81aae489f..5a0509803 100644 --- a/src/commit.c +++ b/src/commit.c @@ -564,17 +564,103 @@ int git_commit_nth_gen_ancestor( int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field) { - const char *buf = commit->raw_header; - const char *h, *eol; + const char *eol, *buf = commit->raw_header; git_buf_sanitize(out); + + while ((eol = strchr(buf, '\n'))) { + /* We can skip continuations here */ + if (buf[0] == ' ') { + buf = eol + 1; + continue; + } + + /* Skip until we find the field we're after */ + if (git__prefixcmp(buf, field)) { + buf = eol + 1; + continue; + } + + buf += strlen(field); + /* Check that we're not matching a prefix but the field itself */ + if (buf[0] != ' ') { + buf = eol + 1; + continue; + } + + buf++; /* skip the SP */ + + git_buf_put(out, buf, eol - buf); + if (git_buf_oom(out)) + goto oom; + + /* If the next line starts with SP, it's multi-line, we must continue */ + while (eol[1] == ' ') { + git_buf_putc(out, '\n'); + buf = eol + 2; + eol = strchr(buf, '\n'); + if (!eol) + goto malformed; + + git_buf_put(out, buf, eol - buf); + } + + if (git_buf_oom(out)) + goto oom; + + return 0; + } + + giterr_set(GITERR_OBJECT, "no such field '%s'", field); + return GIT_ENOTFOUND; + +malformed: + giterr_set(GITERR_OBJECT, "malformed header"); + return -1; +oom: + giterr_set_oom(); + return -1; +} + +int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field) +{ + git_odb_object *obj; + git_odb *odb; + const char *buf; + const char *h, *eol; + int error; + + git_buf_sanitize(signature); + git_buf_sanitize(signed_data); + + if (!field) + field = "gpgsig"; + + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + + if ((error = git_odb_read(&obj, odb, commit_id)) < 0) + return error; + + if (obj->cached.type != GIT_OBJ_COMMIT) { + giterr_set(GITERR_INVALID, "the requested type does not match the type in ODB"); + error = GIT_ENOTFOUND; + goto cleanup; + } + + buf = git_odb_object_data(obj); + while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') { h++; - if (git__prefixcmp(h, field)) { + if (git__prefixcmp(buf, field)) { + if (git_buf_put(signed_data, buf, h - buf) < 0) + return -1; + buf = h; continue; } + h = buf; h += strlen(field); eol = strchr(h, '\n'); if (h[0] != ' ') { @@ -586,33 +672,44 @@ int git_commit_header_field(git_buf *out, const git_commit *commit, const char * h++; /* skip the SP */ - git_buf_put(out, h, eol - h); - if (git_buf_oom(out)) + git_buf_put(signature, h, eol - h); + if (git_buf_oom(signature)) goto oom; /* If the next line starts with SP, it's multi-line, we must continue */ while (eol[1] == ' ') { - git_buf_putc(out, '\n'); + git_buf_putc(signature, '\n'); h = eol + 2; eol = strchr(h, '\n'); if (!eol) goto malformed; - git_buf_put(out, h, eol - h); + git_buf_put(signature, h, eol - h); } - if (git_buf_oom(out)) + if (git_buf_oom(signature)) goto oom; - return 0; + git_odb_object_free(obj); + return git_buf_puts(signed_data, eol+1); } - return GIT_ENOTFOUND; + giterr_set(GITERR_OBJECT, "this commit is not signed"); + error = GIT_ENOTFOUND; + goto cleanup; malformed: giterr_set(GITERR_OBJECT, "malformed header"); - return -1; + error = -1; + goto cleanup; oom: giterr_set_oom(); - return -1; + error = -1; + goto cleanup; + +cleanup: + git_odb_object_free(obj); + git_buf_clear(signature); + git_buf_clear(signed_data); + return error; } diff --git a/src/common.h b/src/common.h index 2913baa92..9abd605cb 100644 --- a/src/common.h +++ b/src/common.h @@ -62,6 +62,12 @@ # endif #define GIT_STDLIB_CALL +#ifdef GIT_USE_STAT_ATIMESPEC +# define st_atim st_atimespec +# define st_ctim st_ctimespec +# define st_mtim st_mtimespec +#endif + # include <arpa/inet.h> #endif @@ -84,6 +90,11 @@ #define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; } /** + * Check a buffer allocation result, returning -1 if it failed. + */ +#define GITERR_CHECK_ALLOC_BUF(buf) if ((void *)(buf) == NULL || git_buf_oom(buf)) { return -1; } + +/** * Check a return value and propagate result if non-zero. */ #define GITERR_CHECK_ERROR(code) \ diff --git a/src/crlf.c b/src/crlf.c index f391137c1..5d7510ac7 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -346,7 +346,7 @@ static int crlf_apply( /* initialize payload in case `check` was bypassed */ if (!*payload) { int error = crlf_check(self, payload, src, NULL); - if (error < 0 && error != GIT_PASSTHROUGH) + if (error < 0) return error; } diff --git a/src/curl_stream.c b/src/curl_stream.c index 798bd5a52..9963d94cc 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -79,6 +79,7 @@ static int curls_certificate(git_cert **out, git_stream *stream) for (slist = certinfo->certinfo[0]; slist; slist = slist->next) { char *str = git__strdup(slist->data); GITERR_CHECK_ALLOC(str); + git_vector_insert(&strings, str); } /* Copy the contents of the vector into a strarray so we can expose them */ @@ -207,11 +208,14 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port) handle = curl_easy_init(); if (handle == NULL) { giterr_set(GITERR_NET, "failed to create curl handle"); + git__free(st); return -1; } - if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) + if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) { + git__free(st); return error; + } curl_easy_setopt(handle, CURLOPT_URL, host); curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error); diff --git a/src/diff.c b/src/diff.c index 67fab0763..9ac5b9250 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1371,7 +1371,7 @@ int git_diff_tree_to_index( DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, &a_opts), iflag, - git_iterator_for_index(&b, index, &b_opts), iflag + git_iterator_for_index(&b, repo, index, &b_opts), iflag ); /* if index is in case-insensitive order, re-sort deltas to match */ @@ -1395,7 +1395,7 @@ int git_diff_index_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_index(&a, index, &a_opts), + git_iterator_for_index(&a, repo, index, &a_opts), GIT_ITERATOR_INCLUDE_CONFLICTS, git_iterator_for_workdir(&b, repo, index, NULL, &b_opts), @@ -1472,8 +1472,8 @@ int git_diff_index_to_index( assert(diff && old_index && new_index); DIFF_FROM_ITERATORS( - git_iterator_for_index(&a, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE, - git_iterator_for_index(&b, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE + git_iterator_for_index(&a, repo, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE, + git_iterator_for_index(&b, repo, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE ); /* if index is in case-insensitive order, re-sort deltas to match */ diff --git a/src/diff_print.c b/src/diff_print.c index bc2d6fab0..dae9e341d 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -92,7 +92,11 @@ static int diff_print_info_init_frompatch( git_diff_line_cb cb, void *payload) { - git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL; + git_repository *repo; + + assert(patch); + + repo = patch->diff ? patch->diff->repo : NULL; memset(pi, 0, sizeof(diff_print_info)); diff --git a/src/diff_tform.c b/src/diff_tform.c index 7cff34159..8577f06b8 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -261,18 +261,23 @@ static int normalize_find_opts( if (!given || (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) { - char *rule = - git_config__get_string_force(cfg, "diff.renames", "true"); - int boolval; - - if (!git__parse_bool(&boolval, rule) && !boolval) - /* don't set FIND_RENAMES if bool value is false */; - else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) - opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; - else - opts->flags |= GIT_DIFF_FIND_RENAMES; + if (diff->repo) { + char *rule = + git_config__get_string_force(cfg, "diff.renames", "true"); + int boolval; + + if (!git__parse_bool(&boolval, rule) && !boolval) + /* don't set FIND_RENAMES if bool value is false */; + else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) + opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + else + opts->flags |= GIT_DIFF_FIND_RENAMES; - git__free(rule); + git__free(rule); + } else { + /* set default flag */ + opts->flags |= GIT_DIFF_FIND_RENAMES; + } } /* some flags imply others */ diff --git a/src/fileops.c b/src/fileops.c index 6aafd06b6..22868b489 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -13,12 +13,6 @@ #include "win32/findfile.h" #endif -#ifdef GIT_USE_STAT_ATIMESPEC -#define st_atim st_atimespec -#define st_ctim st_ctimespec -#define st_mtim st_mtimespec -#endif - GIT__USE_STRMAP int git_futils_mkpath2file(const char *file_path, const mode_t mode) @@ -1040,7 +1034,6 @@ int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path) { struct stat st; - const struct timespec *statmtime = &st.st_mtim; /* if the stamp is NULL, then always reload */ if (stamp == NULL) @@ -1049,17 +1042,17 @@ int git_futils_filestamp_check( if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - if (stamp->mtime.tv_sec == statmtime->tv_sec && + if (stamp->mtime.tv_sec == st.st_mtime && #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec == statmtime->tv_nsec && + stamp->mtime.tv_nsec == st.st_mtime_nsec && #endif stamp->size == (git_off_t)st.st_size && stamp->ino == (unsigned int)st.st_ino) return 0; - stamp->mtime.tv_sec = statmtime->tv_sec; + stamp->mtime.tv_sec = st.st_mtime; #if defined(GIT_USE_NSEC) - stamp->mtime.tv_nsec = statmtime->tv_nsec; + stamp->mtime.tv_nsec = st.st_mtime_nsec; #endif stamp->size = (git_off_t)st.st_size; stamp->ino = (unsigned int)st.st_ino; @@ -1082,11 +1075,11 @@ void git_futils_filestamp_set( void git_futils_filestamp_set_from_stat( git_futils_filestamp *stamp, struct stat *st) { - const struct timespec *statmtime = &st->st_mtim; - if (st) { - stamp->mtime = *statmtime; -#if !defined(GIT_USE_NSEC) + stamp->mtime.tv_sec = st->st_mtime; +#if defined(GIT_USE_NSEC) + stamp->mtime.tv_nsec = st->st_mtime_nsec; +#else stamp->mtime.tv_nsec = 0; #endif stamp->size = (git_off_t)st->st_size; diff --git a/src/filter.c b/src/filter.c index 60473e4e1..a0628d779 100644 --- a/src/filter.c +++ b/src/filter.c @@ -56,80 +56,15 @@ static int filter_def_priority_cmp(const void *a, const void *b) return (pa < pb) ? -1 : (pa > pb) ? 1 : 0; } -struct filter_registry { +struct git_filter_registry { + git_rwlock lock; git_vector filters; }; -static struct filter_registry *git__filter_registry = NULL; +static struct git_filter_registry filter_registry; -static void filter_registry_shutdown(void) -{ - struct filter_registry *reg = NULL; - size_t pos; - git_filter_def *fdef; - - if ((reg = git__swap(git__filter_registry, NULL)) == NULL) - return; - - git_vector_foreach(®->filters, pos, fdef) { - if (fdef->filter && fdef->filter->shutdown) { - fdef->filter->shutdown(fdef->filter); - fdef->initialized = false; - } - - git__free(fdef->filter_name); - git__free(fdef->attrdata); - git__free(fdef); - } - - git_vector_free(®->filters); - git__free(reg); -} - -static int filter_registry_initialize(void) -{ - int error = 0; - struct filter_registry *reg; - - if (git__filter_registry) - return 0; - - reg = git__calloc(1, sizeof(struct filter_registry)); - GITERR_CHECK_ALLOC(reg); - - if ((error = git_vector_init( - ®->filters, 2, filter_def_priority_cmp)) < 0) - goto cleanup; +static void git_filter_global_shutdown(void); - reg = git__compare_and_swap(&git__filter_registry, NULL, reg); - if (reg != NULL) - goto cleanup; - - git__on_shutdown(filter_registry_shutdown); - - /* try to register both default filters */ - { - git_filter *crlf = git_crlf_filter_new(); - git_filter *ident = git_ident_filter_new(); - - if (crlf && git_filter_register( - GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0) - crlf = NULL; - if (ident && git_filter_register( - GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) - ident = NULL; - - if (!crlf || !ident) - return -1; - } - - return 0; - -cleanup: - git_vector_free(®->filters); - git__free(reg); - return error; -} static int filter_def_scan_attrs( git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str) @@ -210,40 +145,14 @@ static int filter_def_filter_key_check(const void *key, const void *fdef) return (key == filter) ? 0 : -1; } -static int filter_registry_find(size_t *pos, const char *name) -{ - return git_vector_search2( - pos, &git__filter_registry->filters, filter_def_name_key_check, name); -} - -static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) -{ - git_filter_def *fdef = NULL; - - if (!filter_registry_find(pos, name)) - fdef = git_vector_get(&git__filter_registry->filters, *pos); - - return fdef; -} - -int git_filter_register( +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_insert( const char *name, git_filter *filter, int priority) { git_filter_def *fdef; size_t nattr = 0, nmatch = 0, alloc_len; git_buf attrs = GIT_BUF_INIT; - assert(name && filter); - - if (filter_registry_initialize() < 0) - return -1; - - if (!filter_registry_find(NULL, name)) { - giterr_set( - GITERR_FILTER, "Attempt to reregister existing filter '%s'", name); - return GIT_EEXISTS; - } - if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0) return -1; @@ -265,21 +174,123 @@ int git_filter_register( filter_def_set_attrs(fdef); - if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { + if (git_vector_insert(&filter_registry.filters, fdef) < 0) { git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); return -1; } - git_vector_sort(&git__filter_registry->filters); + git_vector_sort(&filter_registry.filters); return 0; } +int git_filter_global_init(void) +{ + git_filter *crlf = NULL, *ident = NULL; + int error = 0; + + if (git_rwlock_init(&filter_registry.lock) < 0) + return -1; + + if ((error = git_vector_init(&filter_registry.filters, 2, + filter_def_priority_cmp)) < 0) + goto done; + + if ((crlf = git_crlf_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 || + (ident = git_ident_filter_new()) == NULL || + filter_registry_insert( + GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0) + error = -1; + + git__on_shutdown(git_filter_global_shutdown); + +done: + if (error) { + git_filter_free(crlf); + git_filter_free(ident); + } + + return error; +} + +static void git_filter_global_shutdown(void) +{ + size_t pos; + git_filter_def *fdef; + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) + return; + + git_vector_foreach(&filter_registry.filters, pos, fdef) { + if (fdef->filter && fdef->filter->shutdown) { + fdef->filter->shutdown(fdef->filter); + fdef->initialized = false; + } + + git__free(fdef->filter_name); + git__free(fdef->attrdata); + git__free(fdef); + } + + git_vector_free(&filter_registry.filters); + + git_rwlock_wrunlock(&filter_registry.lock); + git_rwlock_free(&filter_registry.lock); +} + +/* Note: callers must lock the registry before calling this function */ +static int filter_registry_find(size_t *pos, const char *name) +{ + return git_vector_search2( + pos, &filter_registry.filters, filter_def_name_key_check, name); +} + +/* Note: callers must lock the registry before calling this function */ +static git_filter_def *filter_registry_lookup(size_t *pos, const char *name) +{ + git_filter_def *fdef = NULL; + + if (!filter_registry_find(pos, name)) + fdef = git_vector_get(&filter_registry.filters, *pos); + + return fdef; +} + + +int git_filter_register( + const char *name, git_filter *filter, int priority) +{ + int error; + + assert(name && filter); + + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + + if (!filter_registry_find(NULL, name)) { + giterr_set( + GITERR_FILTER, "attempt to reregister existing filter '%s'", name); + error = GIT_EEXISTS; + goto done; + } + + error = filter_registry_insert(name, filter, priority); + +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; +} + int git_filter_unregister(const char *name) { size_t pos; git_filter_def *fdef; + int error = 0; assert(name); @@ -289,12 +300,18 @@ int git_filter_unregister(const char *name) return -1; } + if (git_rwlock_wrlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + if ((fdef = filter_registry_lookup(&pos, name)) == NULL) { giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name); - return GIT_ENOTFOUND; + error = GIT_ENOTFOUND; + goto done; } - (void)git_vector_remove(&git__filter_registry->filters, pos); + git_vector_remove(&filter_registry.filters, pos); if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); @@ -305,21 +322,18 @@ int git_filter_unregister(const char *name) git__free(fdef->attrdata); git__free(fdef); - return 0; +done: + git_rwlock_wrunlock(&filter_registry.lock); + return error; } static int filter_initialize(git_filter_def *fdef) { int error = 0; - if (!fdef->initialized && - fdef->filter && - fdef->filter->initialize && - (error = fdef->filter->initialize(fdef->filter)) < 0) - { - /* auto-unregister if initialize fails */ - git_filter_unregister(fdef->filter_name); - return error; + if (!fdef->initialized && fdef->filter && fdef->filter->initialize) { + if ((error = fdef->filter->initialize(fdef->filter)) < 0) + return error; } fdef->initialized = true; @@ -330,17 +344,22 @@ git_filter *git_filter_lookup(const char *name) { size_t pos; git_filter_def *fdef; + git_filter *filter = NULL; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return NULL; + } - if ((fdef = filter_registry_lookup(&pos, name)) == NULL) - return NULL; + if ((fdef = filter_registry_lookup(&pos, name)) == NULL || + (!fdef->initialized && filter_initialize(fdef) < 0)) + goto done; - if (!fdef->initialized && filter_initialize(fdef) < 0) - return NULL; + filter = fdef->filter; - return fdef->filter; +done: + git_rwlock_rdunlock(&filter_registry.lock); + return filter; } void git_filter_free(git_filter *filter) @@ -478,8 +497,10 @@ int git_filter_list__load_ext( size_t idx; git_filter_def *fdef; - if (filter_registry_initialize() < 0) + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); return -1; + } src.repo = repo; src.path = path; @@ -489,7 +510,7 @@ int git_filter_list__load_ext( if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); - git_vector_foreach(&git__filter_registry->filters, idx, fdef) { + git_vector_foreach(&filter_registry.filters, idx, fdef) { const char **values = NULL; void *payload = NULL; @@ -523,7 +544,7 @@ int git_filter_list__load_ext( else { if (!fl) { if ((error = filter_list_new(&fl, &src)) < 0) - return error; + break; fl->temp_buf = filter_opts->temp_buf; } @@ -537,6 +558,8 @@ int git_filter_list__load_ext( } } + git_rwlock_rdunlock(&filter_registry.lock); + if (error && fl != NULL) { git_array_clear(fl->filters); git__free(fl); @@ -604,20 +627,28 @@ int git_filter_list_push( { int error = 0; size_t pos; - git_filter_def *fdef; + git_filter_def *fdef = NULL; git_filter_entry *fe; assert(fl && filter); + if (git_rwlock_rdlock(&filter_registry.lock) < 0) { + giterr_set(GITERR_OS, "failed to lock filter registry"); + return -1; + } + if (git_vector_search2( - &pos, &git__filter_registry->filters, - filter_def_filter_key_check, filter) < 0) { + &pos, &filter_registry.filters, + filter_def_filter_key_check, filter) == 0) + fdef = git_vector_get(&filter_registry.filters, pos); + + git_rwlock_rdunlock(&filter_registry.lock); + + if (fdef == NULL) { giterr_set(GITERR_FILTER, "Cannot use an unregistered filter"); return -1; } - fdef = git_vector_get(&git__filter_registry->filters, pos); - if (!fdef->initialized && (error = filter_initialize(fdef)) < 0) return error; diff --git a/src/filter.h b/src/filter.h index 5062afba5..9bd835f94 100644 --- a/src/filter.h +++ b/src/filter.h @@ -32,6 +32,8 @@ typedef struct { #define GIT_FILTER_OPTIONS_INIT {0} +extern int git_filter_global_init(void); + extern void git_filter_free(git_filter *filter); extern int git_filter_list__load_ext( diff --git a/src/global.c b/src/global.c index 0eab8d552..c65b21a11 100644 --- a/src/global.c +++ b/src/global.c @@ -8,9 +8,11 @@ #include "global.h" #include "hash.h" #include "sysdir.h" -#include "git2/global.h" -#include "git2/sys/openssl.h" +#include "filter.h" +#include "openssl_stream.h" #include "thread-utils.h" +#include "git2/global.h" + #if defined(GIT_MSVC_CRTDBG) #include "win32/w32_stack.h" #include "win32/w32_crtdbg_stacktrace.h" @@ -20,14 +22,6 @@ git_mutex git__mwindow_mutex; #define MAX_SHUTDOWN_CB 8 -#ifdef GIT_OPENSSL -# include <openssl/ssl.h> -SSL_CTX *git__ssl_ctx; -# ifdef GIT_THREADS -static git_mutex *openssl_locks; -# endif -#endif - static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; static git_atomic git__n_shutdown_callbacks; static git_atomic git__n_inits; @@ -49,118 +43,48 @@ static void git__global_state_cleanup(git_global_st *st) st->error_t.message = NULL; } -static void git__shutdown(void) +static int init_common(void) { - int pos; - - /* Shutdown subsystems that have registered */ - for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL); - if (cb != NULL) - cb(); - } -} - -#if defined(GIT_THREADS) && defined(GIT_OPENSSL) -void openssl_locking_function(int mode, int n, const char *file, int line) -{ - int lock; - - GIT_UNUSED(file); - GIT_UNUSED(line); - - lock = mode & CRYPTO_LOCK; + int ret; - if (lock) { - git_mutex_lock(&openssl_locks[n]); - } else { - git_mutex_unlock(&openssl_locks[n]); - } -} + /* Initialize the CRT debug allocator first, before our first malloc */ +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_init(); + git_win32__stack_init(); +#endif -static void shutdown_ssl_locking(void) -{ - int num_locks, i; + /* Initialize any other subsystems that have global state */ + if ((ret = git_hash_global_init()) == 0 && + (ret = git_sysdir_global_init()) == 0 && + (ret = git_filter_global_init()) == 0) + ret = git_openssl_stream_global_init(); - num_locks = CRYPTO_num_locks(); - CRYPTO_set_locking_callback(NULL); + GIT_MEMORY_BARRIER; - for (i = 0; i < num_locks; ++i) - git_mutex_free(openssl_locks); - git__free(openssl_locks); + return ret; } -#endif -static void init_ssl(void) +static void shutdown_common(void) { -#ifdef GIT_OPENSSL - long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + int pos; - /* Older OpenSSL and MacOS OpenSSL doesn't have this */ -#ifdef SSL_OP_NO_COMPRESSION - ssl_opts |= SSL_OP_NO_COMPRESSION; -#endif + /* Shutdown subsystems that have registered */ + for (pos = git_atomic_get(&git__n_shutdown_callbacks); + pos > 0; + pos = git_atomic_dec(&git__n_shutdown_callbacks)) { - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - /* - * Load SSLv{2,3} and TLSv1 so that we can talk with servers - * which use the SSL hellos, which are often used for - * compatibility. We then disable SSL so we only allow OpenSSL - * to speak TLSv1 to perform the encryption itself. - */ - git__ssl_ctx = SSL_CTX_new(SSLv23_method()); - SSL_CTX_set_options(git__ssl_ctx, ssl_opts); - SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); - if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; - } -#endif -} + git_global_shutdown_fn cb = git__swap( + git__shutdown_callbacks[pos - 1], NULL); -/** - * This function aims to clean-up the SSL context which - * we allocated. - */ -static void uninit_ssl(void) -{ -#ifdef GIT_OPENSSL - if (git__ssl_ctx) { - SSL_CTX_free(git__ssl_ctx); - git__ssl_ctx = NULL; + if (cb != NULL) + cb(); } -#endif -} -int git_openssl_set_locking(void) -{ -#ifdef GIT_OPENSSL -# ifdef GIT_THREADS - int num_locks, i; - - num_locks = CRYPTO_num_locks(); - openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); - GITERR_CHECK_ALLOC(openssl_locks); - - for (i = 0; i < num_locks; i++) { - if (git_mutex_init(&openssl_locks[i]) != 0) { - giterr_set(GITERR_SSL, "failed to initialize openssl locks"); - return -1; - } - } + git__free(git__user_agent); - CRYPTO_set_locking_callback(openssl_locking_function); - git__on_shutdown(shutdown_ssl_locking); - return 0; -# else - giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); - return -1; -# endif -#else - giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); - return -1; +#if defined(GIT_MSVC_CRTDBG) + git_win32__crtdbg_stacktrace_cleanup(); + git_win32__stack_cleanup(); #endif } @@ -208,14 +132,13 @@ static int synchronized_threads_init(void) int error; _tls_index = TlsAlloc(); + + win32_pthread_initialize(); + if (git_mutex_init(&git__mwindow_mutex)) return -1; - /* Initialize any other subsystems that have global state */ - if ((error = git_hash_global_init()) >= 0) - error = git_sysdir_global_init(); - - win32_pthread_initialize(); + error = init_common(); return error; } @@ -229,11 +152,6 @@ int git_libgit2_init(void) /* Only do work on a 0 -> 1 transition of the refcount */ if ((ret = git_atomic_inc(&git__n_inits)) == 1) { -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_init(); - git_win32__stack_init(); -#endif - if (synchronized_threads_init() < 0) ret = -1; } @@ -244,17 +162,6 @@ int git_libgit2_init(void) return ret; } -static void synchronized_threads_shutdown(void) -{ - /* Shut down any subsystems that have global state */ - git__shutdown(); - - git__free_tls_data(); - - TlsFree(_tls_index); - git_mutex_free(&git__mwindow_mutex); -} - int git_libgit2_shutdown(void) { int ret; @@ -264,14 +171,12 @@ int git_libgit2_shutdown(void) /* Only do work on a 1 -> 0 transition of the refcount */ if ((ret = git_atomic_dec(&git__n_inits)) == 0) { - synchronized_threads_shutdown(); + shutdown_common(); -#if defined(GIT_MSVC_CRTDBG) - git_win32__crtdbg_stacktrace_cleanup(); - git_win32__stack_cleanup(); -#endif + git__free_tls_data(); - git__free(git__user_agent); + TlsFree(_tls_index); + git_mutex_free(&git__mwindow_mutex); } /* Exit the lock */ @@ -331,17 +236,10 @@ static void init_once(void) { if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0) return; - pthread_key_create(&_tls_key, &cb__free_status); - - /* Initialize any other subsystems that have global state */ - if ((init_error = git_hash_global_init()) >= 0) - init_error = git_sysdir_global_init(); - - /* OpenSSL needs to be initialized from the main thread */ - init_ssl(); + pthread_key_create(&_tls_key, &cb__free_status); - GIT_MEMORY_BARRIER; + init_error = init_common(); } int git_libgit2_init(void) @@ -364,15 +262,13 @@ int git_libgit2_shutdown(void) return ret; /* Shut down any subsystems that have global state */ - git__shutdown(); - uninit_ssl(); + shutdown_common(); ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); git__global_state_cleanup(ptr); git__free(ptr); - git__free(git__user_agent); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); @@ -405,15 +301,16 @@ static git_global_st __state; int git_libgit2_init(void) { - static int ssl_inited = 0; + int ret; - if (!ssl_inited) { - init_ssl(); - ssl_inited = 1; - } + /* Only init SSL the first time */ + if ((ret = git_atomic_inc(&git__n_inits)) != 1) + return ret; - git_buf_init(&__state.error_buf, 0); - return git_atomic_inc(&git__n_inits); + if ((ret = init_common()) < 0) + return ret; + + return 1; } int git_libgit2_shutdown(void) @@ -421,15 +318,12 @@ int git_libgit2_shutdown(void) int ret; /* Shut down any subsystems that have global state */ - if ((ret = git_atomic_dec(&git__n_inits)) != 0) - return ret; - - git__shutdown(); - git__global_state_cleanup(&__state); - uninit_ssl(); - git__free(git__user_agent); + if ((ret = git_atomic_dec(&git__n_inits)) == 0) { + shutdown_common(); + git__global_state_cleanup(&__state); + } - return 0; + return ret; } git_global_st *git__global_state(void) diff --git a/src/index.c b/src/index.c index 081a1ea59..5704432ae 100644 --- a/src/index.c +++ b/src/index.c @@ -603,14 +603,14 @@ const git_oid *git_index_checksum(git_index *index) */ static int compare_checksum(git_index *index) { - int fd, error; + int fd; ssize_t bytes_read; git_oid checksum = {{ 0 }}; if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0) return fd; - if ((error = p_lseek(fd, -20, SEEK_END)) < 0) { + if (p_lseek(fd, -20, SEEK_END) < 0) { p_close(fd); giterr_set(GITERR_OS, "failed to seek to end of file"); return -1; @@ -826,11 +826,11 @@ const git_index_entry *git_index_get_bypath( void git_index_entry__init_from_stat( git_index_entry *entry, struct stat *st, bool trust_mode) { - entry->ctime.seconds = (git_time_t)st->st_ctime; - entry->mtime.seconds = (git_time_t)st->st_mtime; + entry->ctime.seconds = (int32_t)st->st_ctime; + entry->mtime.seconds = (int32_t)st->st_mtime; #if defined(GIT_USE_NSEC) - entry->mtime.nanoseconds = st->st_mtim.tv_nsec; - entry->ctime.nanoseconds = st->st_ctim.tv_nsec; + entry->mtime.nanoseconds = st->st_mtime_nsec; + entry->ctime.nanoseconds = st->st_ctime_nsec; #endif entry->dev = st->st_rdev; entry->ino = st->st_ino; @@ -838,7 +838,7 @@ void git_index_entry__init_from_stat( git_index__create_mode(0666) : git_index__create_mode(st->st_mode); entry->uid = st->st_uid; entry->gid = st->st_gid; - entry->file_size = st->st_size; + entry->file_size = (uint32_t)st->st_size; } static void index_entry_adjust_namemask( @@ -853,17 +853,31 @@ static void index_entry_adjust_namemask( entry->flags |= GIT_IDXENTRY_NAMEMASK; } +/* When `from_workdir` is true, we will validate the paths to avoid placing + * paths that are invalid for the working directory on the current filesystem + * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This + * function will *always* prevent `.git` and directory traversal `../` from + * being added to the index. + */ static int index_entry_create( git_index_entry **out, git_repository *repo, - const char *path) + const char *path, + bool from_workdir) { size_t pathlen = strlen(path), alloclen; struct entry_internal *entry; + unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS; - if (!git_path_isvalid(repo, path, - GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) { - giterr_set(GITERR_INDEX, "Invalid path: '%s'", path); + /* always reject placing `.git` in the index and directory traversal. + * when requested, disallow platform-specific filenames and upgrade to + * the platform-specific `.git` tests (eg, `git~1`, etc). + */ + if (from_workdir) + path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS; + + if (!git_path_isvalid(repo, path, path_valid_flags)) { + giterr_set(GITERR_INDEX, "invalid path: '%s'", path); return -1; } @@ -895,7 +909,7 @@ static int index_entry_init( "Could not initialize index entry. " "Index is not backed up by an existing repository."); - if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0) + if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0) return -1; /* write the blob to disk and get the oid and stat info */ @@ -975,7 +989,7 @@ static int index_entry_dup( git_index *index, const git_index_entry *src) { - if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) + if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0) return -1; index_entry_cpy(*out, src); @@ -997,7 +1011,7 @@ static int index_entry_dup_nocache( git_index *index, const git_index_entry *src) { - if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0) + if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0) return -1; index_entry_cpy_nocache(*out, src); @@ -1402,7 +1416,7 @@ static int add_repo_as_submodule(git_index_entry **out, git_index *index, const struct stat st; int error; - if (index_entry_create(&entry, INDEX_OWNER(index), path) < 0) + if (index_entry_create(&entry, INDEX_OWNER(index), path, true) < 0) return -1; if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0) @@ -1515,7 +1529,7 @@ int git_index__fill(git_index *index, const git_vector *source_entries) return 0; git_vector_size_hint(&index->entries, source_entries->length); - git_idxmap_resize(index->entries_map, source_entries->length * 1.3); + git_idxmap_resize(index->entries_map, (khint_t)(source_entries->length * 1.3)); git_vector_foreach(source_entries, i, source_entry) { git_index_entry *entry = NULL; @@ -2121,11 +2135,11 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) /* read 3 ASCII octal numbers for stage entries */ for (i = 0; i < 3; i++) { - int tmp; + int64_t tmp; - if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 || + if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 || !endptr || endptr == buffer || *endptr || - (unsigned)tmp > UINT_MAX) { + tmp < 0) { index_entry_reuc_free(lost); return index_error_invalid("reading reuc entry stage"); } @@ -2179,9 +2193,10 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size #define read_conflict_name(ptr) \ len = p_strnlen(buffer, size) + 1; \ - if (size < len) \ - return index_error_invalid("reading conflict name entries"); \ - \ + if (size < len) { \ + index_error_invalid("reading conflict name entries"); \ + goto out_err; \ + } \ if (len == 1) \ ptr = NULL; \ else { \ @@ -2202,7 +2217,16 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size read_conflict_name(conflict_name->theirs); if (git_vector_insert(&index->names, conflict_name) < 0) - return -1; + goto out_err; + + continue; + +out_err: + git__free(conflict_name->ancestor); + git__free(conflict_name->ours); + git__free(conflict_name->theirs); + git__free(conflict_name); + return -1; } #undef read_conflict_name @@ -2788,7 +2812,7 @@ static int read_tree_cb( if (git_buf_joinpath(&path, root, tentry->filename) < 0) return -1; - if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0) + if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, false) < 0) return -1; entry->mode = tentry->attr; @@ -2907,8 +2931,8 @@ int git_index_read_index( opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; - if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 || - (error = git_iterator_for_index(&new_iterator, (git_index *)new_index, &opts)) < 0) + if ((error = git_iterator_for_index(&index_iterator, git_index_owner(index), index, &opts)) < 0 || + (error = git_iterator_for_index(&new_iterator, git_index_owner(new_index), (git_index *)new_index, &opts)) < 0) goto done; if (((error = git_iterator_current(&old_entry, index_iterator)) < 0 && diff --git a/src/index.h b/src/index.h index a64c645b3..8b9b49498 100644 --- a/src/index.h +++ b/src/index.h @@ -92,7 +92,7 @@ GIT_INLINE(bool) git_index_entry_newer_than_index( /* If the timestamp is the same or newer than the index, it's racy */ #if defined(GIT_USE_NSEC) - if ((int32_t)index->stamp.tv_sec < entry->mtime.seconds) + if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds) return true; else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds) return false; diff --git a/src/iterator.c b/src/iterator.c index ee348de6e..024a97573 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -558,6 +558,8 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) { tree_iterator_frame *tf = ti->head; + assert(tf); + if (!tf->up) return false; @@ -581,6 +583,8 @@ static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) while (tree_iterator__pop_frame(ti, final)) /* pop to root */; if (!final) { + assert(ti->head); + ti->head->current = to_end ? ti->head->n_entries : 0; ti->path_ambiguities = 0; git_buf_clear(&ti->path); @@ -773,10 +777,12 @@ static void tree_iterator__free(git_iterator *self) { tree_iterator *ti = (tree_iterator *)self; - tree_iterator__pop_all(ti, true, false); + if (ti->head) { + tree_iterator__pop_all(ti, true, false); + git_tree_free(ti->head->entries[0]->tree); + git__free(ti->head); + } - git_tree_free(ti->head->entries[0]->tree); - git__free(ti->head); git_pool_clear(&ti->pool); git_buf_free(&ti->path); } @@ -1080,6 +1086,7 @@ static void index_iterator__free(git_iterator *self) int git_iterator_for_index( git_iterator **iter, + git_repository *repo, git_index *index, git_iterator_options *options) { @@ -1093,7 +1100,7 @@ int git_iterator_for_index( } ii->index = index; - ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); + ITERATOR_BASE_INIT(ii, index, INDEX, repo); if ((error = iterator__update_ignore_case((git_iterator *)ii, options ? options->flags : 0)) < 0) { git_iterator_free((git_iterator *)ii); @@ -2071,7 +2078,7 @@ int git_iterator_advance_over_with_status( if (!error) continue; - + else if (error == GIT_ENOTFOUND) { /* we entered this directory only hoping to find child matches to * our pathlist (eg, this is `foo` and we had a pathlist entry for diff --git a/src/iterator.h b/src/iterator.h index 59f87e9de..ac17d2970 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -95,6 +95,7 @@ extern int git_iterator_for_tree( */ extern int git_iterator_for_index( git_iterator **out, + git_repository *repo, git_index *index, git_iterator_options *options); diff --git a/src/merge.c b/src/merge.c index 61ff93c19..d2f92ccce 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1985,9 +1985,6 @@ static int create_virtual_base( git_index *index = NULL; git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT; - result = git__calloc(1, sizeof(git_annotated_commit)); - GITERR_CHECK_ALLOC(result); - /* Conflicts in the merge base creation do not propagate to conflicts * in the result; the conflicted base will act as the common ancestor. */ @@ -2001,6 +1998,8 @@ static int create_virtual_base( recursion_level + 1, &virtual_opts)) < 0) return -1; + result = git__calloc(1, sizeof(git_annotated_commit)); + GITERR_CHECK_ALLOC(result); result->type = GIT_ANNOTATED_COMMIT_VIRTUAL; result->index = index; @@ -2084,9 +2083,9 @@ static int iterator_for_annotated_commit( opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if (commit == NULL) { - error = git_iterator_for_nothing(out, &opts); + error = git_iterator_for_nothing(out, &opts); } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) { - error = git_iterator_for_index(out, commit->index, &opts); + error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts); } else { if (!commit->tree && (error = git_commit_tree(&commit->tree, commit->commit)) < 0) @@ -2428,7 +2427,7 @@ static int write_merge_msg( assert(repo && heads); entries = git__calloc(heads_len, sizeof(struct merge_msg_entry)); - GITERR_CHECK_ALLOC(entries); + GITERR_CHECK_ALLOC(entries); if (git_vector_init(&matching, heads_len, NULL) < 0) { git__free(entries); @@ -2482,7 +2481,7 @@ static int write_merge_msg( if (matching.length) sep =','; - + if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 || (error = merge_msg_write_tags(&file, &matching, sep)) < 0) goto cleanup; @@ -2683,8 +2682,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index iter_opts.pathlist.strings = (char **)staged_paths.contents; iter_opts.pathlist.count = staged_paths.length; - if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || + if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0) goto done; @@ -2760,7 +2759,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || + (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 || (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) goto done; diff --git a/src/netops.c b/src/netops.c index 5e8075597..c4241989f 100644 --- a/src/netops.c +++ b/src/netops.c @@ -261,6 +261,10 @@ int gitno_extract_url_parts( *path = git__substrdup(_path, u.field_data[UF_PATH].len); GITERR_CHECK_ALLOC(*path); } else { + git__free(*port); + *port = NULL; + git__free(*host); + *host = NULL; giterr_set(GITERR_NET, "invalid url, missing path"); return GIT_EINVALIDSPEC; } diff --git a/src/openssl_stream.c b/src/openssl_stream.c index 54dd761ca..97736b714 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -15,6 +15,7 @@ #include "socket_stream.h" #include "netops.h" #include "git2/transport.h" +#include "git2/sys/openssl.h" #ifdef GIT_CURL # include "curl_stream.h" @@ -31,6 +32,115 @@ #include <openssl/x509v3.h> #include <openssl/bio.h> +SSL_CTX *git__ssl_ctx; + +#ifdef GIT_THREADS + +static git_mutex *openssl_locks; + +static void openssl_locking_function( + int mode, int n, const char *file, int line) +{ + int lock; + + GIT_UNUSED(file); + GIT_UNUSED(line); + + lock = mode & CRYPTO_LOCK; + + if (lock) { + git_mutex_lock(&openssl_locks[n]); + } else { + git_mutex_unlock(&openssl_locks[n]); + } +} + +static void shutdown_ssl_locking(void) +{ + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < num_locks; ++i) + git_mutex_free(openssl_locks); + git__free(openssl_locks); +} + +#endif /* GIT_THREADS */ + +/** + * This function aims to clean-up the SSL context which + * we allocated. + */ +static void shutdown_ssl(void) +{ + if (git__ssl_ctx) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + } +} + +int git_openssl_stream_global_init(void) +{ +#ifdef GIT_OPENSSL + long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + /* Older OpenSSL and MacOS OpenSSL doesn't have this */ +#ifdef SSL_OP_NO_COMPRESSION + ssl_opts |= SSL_OP_NO_COMPRESSION; +#endif + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + /* + * Load SSLv{2,3} and TLSv1 so that we can talk with servers + * which use the SSL hellos, which are often used for + * compatibility. We then disable SSL so we only allow OpenSSL + * to speak TLSv1 to perform the encryption itself. + */ + git__ssl_ctx = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_options(git__ssl_ctx, ssl_opts); + SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); + if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + return -1; + } +#endif + + git__on_shutdown(shutdown_ssl); + + return 0; +} + +int git_openssl_set_locking(void) +{ +#ifdef GIT_THREADS + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); + GITERR_CHECK_ALLOC(openssl_locks); + + for (i = 0; i < num_locks; i++) { + if (git_mutex_init(&openssl_locks[i]) != 0) { + giterr_set(GITERR_SSL, "failed to initialize openssl locks"); + return -1; + } + } + + CRYPTO_set_locking_callback(openssl_locking_function); + git__on_shutdown(shutdown_ssl_locking); + return 0; +#else + giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); + return -1; +#endif +} + + static int bio_create(BIO *b) { b->init = 1; @@ -158,7 +268,6 @@ static int ssl_teardown(SSL *ssl) else ret = 0; - SSL_free(ssl); return ret; } @@ -274,6 +383,8 @@ static int verify_server_cert(SSL *ssl, const char *host) GITERR_CHECK_ALLOC(peer_cn); memcpy(peer_cn, ASN1_STRING_data(str), size); peer_cn[size] = '\0'; + } else { + goto cert_fail_name; } } else { int size = ASN1_STRING_to_UTF8(&peer_cn, str); @@ -421,6 +532,7 @@ void openssl_free(git_stream *stream) { openssl_stream *st = (openssl_stream *) stream; + SSL_free(st->ssl); git__free(st->host); git__free(st->cert_info.data); git_stream_free(st->io); @@ -435,6 +547,7 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) st = git__calloc(1, sizeof(openssl_stream)); GITERR_CHECK_ALLOC(st); + st->io = NULL; #ifdef GIT_CURL error = git_curl_stream_new(&st->io, host, port); #else @@ -442,12 +555,13 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) #endif if (error < 0) - return error; + goto out_err; st->ssl = SSL_new(git__ssl_ctx); if (st->ssl == NULL) { giterr_set(GITERR_SSL, "failed to create ssl object"); - return -1; + error = -1; + goto out_err; } st->host = git__strdup(host); @@ -466,11 +580,29 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port) *out = (git_stream *) st; return 0; + +out_err: + git_stream_free(st->io); + git__free(st); + + return error; } #else #include "stream.h" +#include "git2/sys/openssl.h" + +int git_openssl_stream_global_init(void) +{ + return 0; +} + +int git_openssl_set_locking(void) +{ + giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); + return -1; +} int git_openssl_stream_new(git_stream **out, const char *host, const char *port) { diff --git a/src/openssl_stream.h b/src/openssl_stream.h index 9ca06489e..82b5110c4 100644 --- a/src/openssl_stream.h +++ b/src/openssl_stream.h @@ -9,6 +9,8 @@ #include "git2/sys/stream.h" +extern int git_openssl_stream_global_init(void); + extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port); #endif diff --git a/src/pack-objects.c b/src/pack-objects.c index fd181fc5e..46fe8f3db 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -91,7 +91,7 @@ static unsigned name_hash(const char *name) static int packbuilder_config(git_packbuilder *pb) { git_config *config; - int ret; + int ret = 0; int64_t val; if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0) @@ -100,8 +100,10 @@ static int packbuilder_config(git_packbuilder *pb) #define config_get(KEY,DST,DFLT) do { \ ret = git_config_get_int64(&val, config, KEY); \ if (!ret) (DST) = val; \ - else if (ret == GIT_ENOTFOUND) (DST) = (DFLT); \ - else if (ret < 0) return -1; } while (0) + else if (ret == GIT_ENOTFOUND) { \ + (DST) = (DFLT); \ + ret = 0; \ + } else if (ret < 0) goto out; } while (0) config_get("pack.deltaCacheSize", pb->max_delta_cache_size, GIT_PACK_DELTA_CACHE_SIZE); @@ -113,9 +115,10 @@ static int packbuilder_config(git_packbuilder *pb) #undef config_get +out: git_config_free(config); - return 0; + return ret; } int git_packbuilder_new(git_packbuilder **out, git_repository *repo) @@ -605,6 +608,7 @@ static git_pobject **compute_write_order(git_packbuilder *pb) } if (wo_end != pb->nr_objects) { + git__free(wo); giterr_set(GITERR_INVALID, "invalid write order"); return NULL; } @@ -625,10 +629,8 @@ static int write_pack(git_packbuilder *pb, int error = 0; write_order = compute_write_order(pb); - if (write_order == NULL) { - error = -1; - goto done; - } + if (write_order == NULL) + return -1; /* Write pack header */ ph.hdr_signature = htonl(PACK_SIGNATURE); @@ -846,9 +848,11 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, git_packbuilder__cache_unlock(pb); - if (overflow || - !(trg_object->delta_data = git__realloc(delta_buf, delta_size))) + if (overflow) return -1; + + trg_object->delta_data = git__realloc(delta_buf, delta_size); + GITERR_CHECK_ALLOC(trg_object->delta_data); } else { /* create delta when writing the pack */ git_packbuilder__cache_unlock(pb); diff --git a/src/pack.c b/src/pack.c index 45dd4d5be..52c652178 100644 --- a/src/pack.c +++ b/src/pack.c @@ -21,7 +21,7 @@ GIT__USE_OIDMAP static int packfile_open(struct git_pack_file *p); static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); -int packfile_unpack_compressed( +static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **w_curs, @@ -365,9 +365,14 @@ static unsigned char *pack_window_open( * pointless to ask for an offset into the middle of that * hash, and the pack_window_contains function above wouldn't match * don't allow an offset too close to the end of the file. + * + * Don't allow a negative offset, as that means we've wrapped + * around. */ if (offset > (p->mwf.size - 20)) return NULL; + if (offset < 0) + return NULL; return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left); } @@ -790,7 +795,6 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, obj->zstream.next_out = Z_NULL; st = inflateInit(&obj->zstream); if (st != Z_OK) { - git__free(obj); giterr_set(GITERR_ZLIB, "failed to init packfile stream"); return -1; } @@ -843,7 +847,7 @@ void git_packfile_stream_free(git_packfile_stream *obj) inflateEnd(&obj->zstream); } -int packfile_unpack_compressed( +static int packfile_unpack_compressed( git_rawobj *obj, struct git_pack_file *p, git_mwindow **w_curs, @@ -1176,6 +1180,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n) { const unsigned char *index = p->index_map.data; + const unsigned char *end = index + p->index_map.len; index += 4 * 256; if (p->index_version == 1) { return ntohl(*((uint32_t *)(index + 24 * n))); @@ -1186,6 +1191,11 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_ if (!(off & 0x80000000)) return off; index += p->num_objects * 4 + (off & 0x7fffffff) * 8; + + /* Make sure we're not being sent out of bounds */ + if (index >= end - 8) + return -1; + return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | ntohl(*((uint32_t *)(index + 4))); } @@ -1265,6 +1275,7 @@ static int pack_entry_find_offset( const unsigned char *index = p->index_map.data; unsigned hi, lo, stride; int pos, found = 0; + git_off_t offset; const unsigned char *current = 0; *offset_out = 0; @@ -1337,7 +1348,12 @@ static int pack_entry_find_offset( if (found > 1) return git_odb__error_ambiguous("found multiple offsets for pack entry"); - *offset_out = nth_packed_object_offset(p, pos); + if ((offset = nth_packed_object_offset(p, pos)) < 0) { + giterr_set(GITERR_ODB, "packfile index is corrupt"); + return -1; + } + + *offset_out = offset; git_oid_fromraw(found_oid, current); #ifdef INDEX_DEBUG_LOOKUP diff --git a/src/pack.h b/src/pack.h index b3d5b2993..d15247b74 100644 --- a/src/pack.h +++ b/src/pack.h @@ -138,13 +138,6 @@ int git_packfile_resolve_header( git_off_t offset); int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset); -int packfile_unpack_compressed( - git_rawobj *obj, - struct git_pack_file *p, - git_mwindow **w_curs, - git_off_t *curpos, - size_t size, - git_otype type); int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos); ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len); diff --git a/src/path.c b/src/path.c index 18b4f03fd..1fd14fcb9 100644 --- a/src/path.c +++ b/src/path.c @@ -705,8 +705,7 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling) char *base, *to, *from, *next; size_t len; - if (!path || git_buf_oom(path)) - return -1; + GITERR_CHECK_ALLOC_BUF(path); if (ceiling > path->size) ceiling = path->size; @@ -1630,9 +1629,12 @@ static bool verify_component( !verify_dotgit_ntfs(repo, component, len)) return false; + /* don't bother rerunning the `.git` test if we ran the HFS or NTFS + * specific tests, they would have already rejected `.git`. + */ if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && - (flags & GIT_PATH_REJECT_DOT_GIT) && + (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL) && len == 4 && component[0] == '.' && (component[1] == 'g' || component[1] == 'G') && @@ -1649,6 +1651,8 @@ GIT_INLINE(unsigned int) dotgit_flags( { int protectHFS = 0, protectNTFS = 0; + flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL; + #ifdef __APPLE__ protectHFS = 1; #endif diff --git a/src/path.h b/src/path.h index 7e156fce8..875c8cb7e 100644 --- a/src/path.h +++ b/src/path.h @@ -564,15 +564,16 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or #define GIT_PATH_REJECT_TRAILING_COLON (1 << 6) #define GIT_PATH_REJECT_DOS_PATHS (1 << 7) #define GIT_PATH_REJECT_NT_CHARS (1 << 8) -#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9) -#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10) +#define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9) +#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10) +#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11) /* Default path safety for writing files to disk: since we use the * Win32 "File Namespace" APIs ("\\?\") we need to protect from * paths that the normal Win32 APIs would not write. */ #ifdef GIT_WIN32 -# define GIT_PATH_REJECT_DEFAULTS \ +# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ GIT_PATH_REJECT_TRAVERSAL | \ GIT_PATH_REJECT_BACKSLASH | \ GIT_PATH_REJECT_TRAILING_DOT | \ @@ -581,9 +582,18 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or GIT_PATH_REJECT_DOS_PATHS | \ GIT_PATH_REJECT_NT_CHARS #else -# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL +# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \ + GIT_PATH_REJECT_TRAVERSAL #endif + /* Paths that should never be written into the working directory. */ +#define GIT_PATH_REJECT_WORKDIR_DEFAULTS \ + GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT + +/* Paths that should never be written to the index. */ +#define GIT_PATH_REJECT_INDEX_DEFAULTS \ + GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT + /* * Determine whether a path is a valid git path or not - this must not contain * a '.' or '..' component, or a component that is ".git" (in any case). diff --git a/src/pathspec.c b/src/pathspec.c index 5bb69ec4b..8a93cdd50 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -550,7 +550,7 @@ int git_pathspec_match_index( iter_opts.flags = pathspec_match_iter_flags(flags); - if (!(error = git_iterator_for_index(&iter, index, &iter_opts))) { + if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) { error = pathspec_match_from_iterator(out, iter, flags, ps); git_iterator_free(iter); } @@ -718,4 +718,3 @@ const char * git_pathspec_match_list_failed_entry( return entry ? *entry : NULL; } - diff --git a/src/pool.c b/src/pool.c index e519b75bb..b4fc50fca 100644 --- a/src/pool.c +++ b/src/pool.c @@ -28,6 +28,7 @@ uint32_t git_pool__system_page_size(void) return size; } +#ifndef GIT_DEBUG_POOL void git_pool_init(git_pool *pool, uint32_t item_size) { assert(pool); @@ -50,18 +51,6 @@ void git_pool_clear(git_pool *pool) pool->pages = NULL; } -void git_pool_swap(git_pool *a, git_pool *b) -{ - git_pool temp; - - if (a == b) - return; - - memcpy(&temp, a, sizeof(temp)); - memcpy(a, b, sizeof(temp)); - memcpy(b, &temp, sizeof(temp)); -} - static void *pool_alloc_page(git_pool *pool, uint32_t size) { git_pool_page *page; @@ -95,6 +84,83 @@ static void *pool_alloc(git_pool *pool, uint32_t size) return ptr; } +uint32_t git_pool__open_pages(git_pool *pool) +{ + uint32_t ct = 0; + git_pool_page *scan; + for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; + return ct; +} + +bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) +{ + git_pool_page *scan; + for (scan = pool->pages; scan != NULL; scan = scan->next) + if ((void *)scan->data <= ptr && + (void *)(((char *)scan->data) + scan->size) > ptr) + return true; + return false; +} + +#else + +static int git_pool__ptr_cmp(const void * a, const void * b) +{ + if(a > b) { + return 1; + } + if(a < b) { + return -1; + } + else { + return 0; + } +} + +void git_pool_init(git_pool *pool, uint32_t item_size) +{ + assert(pool); + assert(item_size >= 1); + + memset(pool, 0, sizeof(git_pool)); + pool->item_size = item_size; + pool->page_size = git_pool__system_page_size(); + git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp); +} + +void git_pool_clear(git_pool *pool) +{ + git_vector_free_deep(&pool->allocations); +} + +static void *pool_alloc(git_pool *pool, uint32_t size) { + void *ptr = NULL; + if((ptr = git__malloc(size)) == NULL) { + return NULL; + } + git_vector_insert_sorted(&pool->allocations, ptr, NULL); + return ptr; +} + +bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) +{ + size_t pos; + return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND; +} +#endif + +void git_pool_swap(git_pool *a, git_pool *b) +{ + git_pool temp; + + if (a == b) + return; + + memcpy(&temp, a, sizeof(temp)); + memcpy(a, b, sizeof(temp)); + memcpy(b, &temp, sizeof(temp)); +} + static uint32_t alloc_size(git_pool *pool, uint32_t count) { const uint32_t align = sizeof(void *) - 1; @@ -168,21 +234,3 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b) } return ptr; } - -uint32_t git_pool__open_pages(git_pool *pool) -{ - uint32_t ct = 0; - git_pool_page *scan; - for (scan = pool->pages; scan != NULL; scan = scan->next) ct++; - return ct; -} - -bool git_pool__ptr_in_pool(git_pool *pool, void *ptr) -{ - git_pool_page *scan; - for (scan = pool->pages; scan != NULL; scan = scan->next) - if ((void *)scan->data <= ptr && - (void *)(((char *)scan->data) + scan->size) > ptr) - return true; - return false; -} diff --git a/src/pool.h b/src/pool.h index d16bd349a..e0fafa997 100644 --- a/src/pool.h +++ b/src/pool.h @@ -8,9 +8,11 @@ #define INCLUDE_pool_h__ #include "common.h" +#include "vector.h" typedef struct git_pool_page git_pool_page; +#ifndef GIT_DEBUG_POOL /** * Chunked allocator. * @@ -33,6 +35,30 @@ typedef struct { uint32_t page_size; /* size of page in bytes */ } git_pool; +#else + +/** + * Debug chunked allocator. + * + * Acts just like `git_pool` but instead of actually pooling allocations it + * passes them through to `git__malloc`. This makes it possible to easily debug + * systems that use `git_pool` using valgrind. + * + * In order to track allocations during the lifetime of the pool we use a + * `git_vector`. When the pool is deallocated everything in the vector is + * freed. + * + * `API is exactly the same as the standard `git_pool` with one exception. + * Since we aren't allocating pages to hand out in chunks we can't easily + * implement `git_pool__open_pages`. + */ +typedef struct { + git_vector allocations; + uint32_t item_size; + uint32_t page_size; +} git_pool; +#endif + /** * Initialize a pool. * @@ -98,7 +124,9 @@ extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b); /* * Misc utilities */ +#ifndef GIT_DEBUG_POOL extern uint32_t git_pool__open_pages(git_pool *pool); +#endif extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); #endif diff --git a/src/rebase.c b/src/rebase.c index 17536c030..bcad9b7cd 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -63,17 +63,23 @@ struct git_rebase { char *state_path; int head_detached : 1, + inmemory : 1, quiet : 1, started : 1; - char *orig_head_name; + git_array_t(git_rebase_operation) operations; + size_t current; + + /* Used by in-memory rebase */ + git_index *index; + git_commit *last_commit; + + /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; + char *orig_head_name; git_oid onto_id; char *onto_name; - - git_array_t(git_rebase_operation) operations; - size_t current; }; #define GIT_REBASE_STATE_INIT {0} @@ -251,12 +257,12 @@ done: return error; } -static git_rebase *rebase_alloc(const git_rebase_options *rebase_opts) +static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts) { git_rebase *rebase = git__calloc(1, sizeof(git_rebase)); + GITERR_CHECK_ALLOC(rebase); - if (!rebase) - return NULL; + *out = NULL; if (rebase_opts) memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options)); @@ -264,14 +270,16 @@ static git_rebase *rebase_alloc(const git_rebase_options *rebase_opts) git_rebase_init_options(&rebase->options, GIT_REBASE_OPTIONS_VERSION); if (rebase_opts && rebase_opts->rewrite_notes_ref) { - if ((rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref)) == NULL) - return NULL; + rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref); + GITERR_CHECK_ALLOC(rebase->options.rewrite_notes_ref); } if ((rebase->options.checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0) rebase->options.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; - return rebase; + *out = rebase; + + return 0; } static int rebase_check_versions(const git_rebase_options *given_opts) @@ -299,8 +307,8 @@ int git_rebase_open( if ((error = rebase_check_versions(given_opts)) < 0) return error; - rebase = rebase_alloc(given_opts); - GITERR_CHECK_ALLOC(rebase); + if (rebase_alloc(&rebase, given_opts) < 0) + return -1; rebase->repo = repo; @@ -393,6 +401,9 @@ done: static int rebase_cleanup(git_rebase *rebase) { + if (!rebase || rebase->inmemory) + return 0; + return git_path_isdir(rebase->state_path) ? git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : 0; @@ -601,61 +612,65 @@ static int rebase_init_merge( const git_annotated_commit *upstream, const git_annotated_commit *onto) { - if (rebase_init_operations(rebase, repo, branch, upstream, onto) < 0) - return -1; - - rebase->onto_name = git__strdup(rebase_onto_name(onto)); - GITERR_CHECK_ALLOC(rebase->onto_name); - - return 0; -} - -static int rebase_init( - git_rebase *rebase, - git_repository *repo, - const git_annotated_commit *branch, - const git_annotated_commit *upstream, - const git_annotated_commit *onto) -{ git_reference *head_ref = NULL; - git_annotated_commit *head_branch = NULL; + git_commit *onto_commit = NULL; + git_buf reflog = GIT_BUF_INIT; git_buf state_path = GIT_BUF_INIT; int error; + GIT_UNUSED(upstream); + if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) goto done; - if (!branch) { - if ((error = git_repository_head(&head_ref, repo)) < 0 || - (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) - goto done; - - branch = head_branch; - } - - rebase->repo = repo; - rebase->type = GIT_REBASE_TYPE_MERGE; rebase->state_path = git_buf_detach(&state_path); + GITERR_CHECK_ALLOC(rebase->state_path); + rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); + GITERR_CHECK_ALLOC(rebase->orig_head_name); + + rebase->onto_name = git__strdup(rebase_onto_name(onto)); + GITERR_CHECK_ALLOC(rebase->onto_name); + rebase->quiet = rebase->options.quiet; git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); - if (!rebase->orig_head_name || !rebase->state_path) - return -1; - - error = rebase_init_merge(rebase, repo, branch, upstream, onto); - - git_buf_free(&state_path); + if ((error = rebase_setupfiles(rebase)) < 0 || + (error = git_buf_printf(&reflog, + "rebase: checkout %s", rebase_onto_name(onto))) < 0 || + (error = git_commit_lookup( + &onto_commit, repo, git_annotated_commit_id(onto))) < 0 || + (error = git_checkout_tree(repo, + (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || + (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, + git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + goto done; done: git_reference_free(head_ref); - git_annotated_commit_free(head_branch); + git_commit_free(onto_commit); + git_buf_free(&reflog); + git_buf_free(&state_path); return error; } +static int rebase_init_inmemory( + git_rebase *rebase, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto) +{ + GIT_UNUSED(branch); + GIT_UNUSED(upstream); + + return git_commit_lookup( + &rebase->last_commit, repo, git_annotated_commit_id(onto)); +} + int git_rebase_init( git_rebase **out, git_repository *repo, @@ -665,9 +680,9 @@ int git_rebase_init( const git_rebase_options *given_opts) { git_rebase *rebase = NULL; - git_buf reflog = GIT_BUF_INIT; - git_commit *onto_commit = NULL; + git_annotated_commit *head_branch = NULL; git_reference *head_ref = NULL; + bool inmemory = (given_opts && given_opts->inmemory); int error; assert(repo && (upstream || onto)); @@ -677,39 +692,51 @@ int git_rebase_init( if (!onto) onto = upstream; - if ((error = rebase_check_versions(given_opts)) < 0 || - (error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || - (error = rebase_ensure_not_in_progress(repo)) < 0 || - (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0 || - (error = git_commit_lookup( - &onto_commit, repo, git_annotated_commit_id(onto))) < 0) - return error; + if ((error = rebase_check_versions(given_opts)) < 0) + goto done; - rebase = rebase_alloc(given_opts); + if (!inmemory) { + if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || + (error = rebase_ensure_not_in_progress(repo)) < 0 || + (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0) + goto done; + } - if ((error = rebase_init( - rebase, repo, branch, upstream, onto)) < 0 || - (error = rebase_setupfiles(rebase)) < 0 || - (error = git_buf_printf(&reflog, - "rebase: checkout %s", rebase_onto_name(onto))) < 0 || - (error = git_checkout_tree( - repo, (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 || - (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, - git_annotated_commit_id(onto), 1, reflog.ptr)) < 0) + if (!branch) { + if ((error = git_repository_head(&head_ref, repo)) < 0 || + (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0) + goto done; + + branch = head_branch; + } + + if (rebase_alloc(&rebase, given_opts) < 0) + return -1; + + rebase->repo = repo; + rebase->inmemory = inmemory; + rebase->type = GIT_REBASE_TYPE_MERGE; + + if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0) goto done; - *out = rebase; + if (inmemory) + error = rebase_init_inmemory(rebase, repo, branch, upstream, onto); + else + rebase_init_merge(rebase, repo, branch ,upstream, onto); + + if (error == 0) + *out = rebase; done: git_reference_free(head_ref); + git_annotated_commit_free(head_branch); + if (error < 0) { rebase_cleanup(rebase); git_rebase_free(rebase); } - git_commit_free(onto_commit); - git_buf_free(&reflog); - return error; } @@ -764,9 +791,6 @@ static int rebase_next_merge( *out = NULL; - if ((error = rebase_movenext(rebase)) < 0) - goto done; - operation = git_array_get(rebase->operations, rebase->current); if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || @@ -791,7 +815,7 @@ static int rebase_next_merge( if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 || (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 || (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 || - (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 || (error = git_merge__check_result(rebase->repo, index)) < 0 || (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 || (error = git_indexwriter_commit(&indexwriter)) < 0) @@ -812,6 +836,49 @@ done: return error; } +static int rebase_next_inmemory( + git_rebase_operation **out, + git_rebase *rebase) +{ + git_commit *current_commit = NULL, *parent_commit = NULL; + git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; + git_rebase_operation *operation; + git_index *index = NULL; + int error; + + *out = NULL; + + operation = git_array_get(rebase->operations, rebase->current); + + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(¤t_tree, current_commit)) < 0 || + (error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 || + (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) + goto done; + + if (!rebase->index) { + rebase->index = index; + index = NULL; + } else { + if ((error = git_index_read_index(rebase->index, index)) < 0) + goto done; + } + + *out = operation; + +done: + git_commit_free(current_commit); + git_commit_free(parent_commit); + git_tree_free(current_tree); + git_tree_free(head_tree); + git_tree_free(parent_tree); + git_index_free(index); + + return error; +} + int git_rebase_next( git_rebase_operation **out, git_rebase *rebase) @@ -820,66 +887,67 @@ int git_rebase_next( assert(out && rebase); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if ((error = rebase_movenext(rebase)) < 0) + return error; + + if (rebase->inmemory) + error = rebase_next_inmemory(out, rebase); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_next_merge(out, rebase); - break; - default: + else abort(); - } return error; } -static int rebase_commit_merge( - git_oid *commit_id, +int git_rebase_inmemory_index( + git_index **out, + git_rebase *rebase) +{ + assert(out && rebase && rebase->index); + + GIT_REFCOUNT_INC(rebase->index); + *out = rebase->index; + + return 0; +} + +static int rebase_commit__create( + git_commit **out, git_rebase *rebase, + git_index *index, + git_commit *parent_commit, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message) { - git_index *index = NULL; - git_reference *head = NULL; - git_commit *current_commit = NULL, *head_commit = NULL, *commit = NULL; git_rebase_operation *operation; - git_tree *head_tree = NULL, *tree = NULL; - git_diff *diff = NULL; - git_oid tree_id; - git_buf reflog_msg = GIT_BUF_INIT; - char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; + git_commit *current_commit = NULL, *commit = NULL; + git_tree *parent_tree = NULL, *tree = NULL; + git_oid tree_id, commit_id; int error; operation = git_array_get(rebase->operations, rebase->current); - assert(operation); - - if ((error = git_repository_index(&index, rebase->repo)) < 0) - goto done; if (git_index_has_conflicts(index)) { - giterr_set(GITERR_REBASE, "Conflicts have not been resolved"); + giterr_set(GITERR_REBASE, "conflicts have not been resolved"); error = GIT_EUNMERGED; goto done; } - if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || - (error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || - (error = git_repository_head(&head, rebase->repo)) < 0 || - (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || - (error = git_commit_tree(&head_tree, head_commit)) < 0 || - (error = git_diff_tree_to_index(&diff, rebase->repo, head_tree, index, NULL)) < 0) + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0 || + (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 || + (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) goto done; - if (git_diff_num_deltas(diff) == 0) { - giterr_set(GITERR_REBASE, "This patch has already been applied"); + if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) { + giterr_set(GITERR_REBASE, "this patch has already been applied"); error = GIT_EAPPLIED; goto done; } - if ((error = git_index_write_tree(&tree_id, index)) < 0 || - (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) - goto done; - if (!author) author = git_commit_author(current_commit); @@ -888,30 +956,100 @@ static int rebase_commit_merge( message = git_commit_message(current_commit); } - if ((error = git_commit_create(commit_id, rebase->repo, NULL, author, - committer, message_encoding, message, tree, 1, - (const git_commit **)&head_commit)) < 0 || - (error = git_commit_lookup(&commit, rebase->repo, commit_id)) < 0 || + if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author, + committer, message_encoding, message, tree, 1, + (const git_commit **)&parent_commit)) < 0 || + (error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0) + goto done; + + *out = commit; + +done: + if (error < 0) + git_commit_free(commit); + + git_commit_free(current_commit); + git_tree_free(parent_tree); + git_tree_free(tree); + + return error; +} + +static int rebase_commit_merge( + git_oid *commit_id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_reference *head = NULL; + git_commit *head_commit = NULL, *commit = NULL; + git_index *index = NULL; + char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; + int error; + + operation = git_array_get(rebase->operations, rebase->current); + assert(operation); + + if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 || + (error = git_repository_head(&head, rebase->repo)) < 0 || + (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || + (error = git_repository_index(&index, rebase->repo)) < 0 || + (error = rebase_commit__create(&commit, rebase, index, head_commit, + author, committer, message_encoding, message)) < 0 || (error = git_reference__update_for_commit( - rebase->repo, NULL, "HEAD", commit_id, "rebase")) < 0) + rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0) goto done; - git_oid_fmt(old_idstr, git_commit_id(current_commit)); - git_oid_fmt(new_idstr, commit_id); + git_oid_fmt(old_idstr, &operation->id); + git_oid_fmt(new_idstr, git_commit_id(commit)); + + if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, + "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0) + goto done; - error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, - "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr); + git_oid_cpy(commit_id, git_commit_id(commit)); done: - git_buf_free(&reflog_msg); - git_commit_free(commit); - git_diff_free(diff); - git_tree_free(tree); - git_tree_free(head_tree); - git_commit_free(head_commit); - git_commit_free(current_commit); - git_reference_free(head); git_index_free(index); + git_reference_free(head); + git_commit_free(head_commit); + git_commit_free(commit); + return error; +} + +static int rebase_commit_inmemory( + git_oid *commit_id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_rebase_operation *operation; + git_commit *commit = NULL; + int error = 0; + + operation = git_array_get(rebase->operations, rebase->current); + + assert(operation); + assert(rebase->index); + assert(rebase->last_commit); + + if ((error = rebase_commit__create(&commit, rebase, rebase->index, + rebase->last_commit, author, committer, message_encoding, message)) < 0) + goto done; + + git_commit_free(rebase->last_commit); + rebase->last_commit = commit; + + git_oid_cpy(commit_id, git_commit_id(commit)); + +done: + if (error < 0) + git_commit_free(commit); return error; } @@ -928,14 +1066,14 @@ int git_rebase_commit( assert(rebase && committer); - switch (rebase->type) { - case GIT_REBASE_TYPE_MERGE: + if (rebase->inmemory) + error = rebase_commit_inmemory( + id, rebase, author, committer, message_encoding, message); + else if (rebase->type == GIT_REBASE_TYPE_MERGE) error = rebase_commit_merge( id, rebase, author, committer, message_encoding, message); - break; - default: + else abort(); - } return error; } @@ -948,6 +1086,9 @@ int git_rebase_abort(git_rebase *rebase) assert(rebase); + if (rebase->inmemory) + return 0; + error = rebase->head_detached ? git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, &rebase->orig_head_id, 1, "rebase: aborting") : @@ -1125,6 +1266,9 @@ int git_rebase_finish( assert(rebase); + if (rebase->inmemory) + return 0; + git_oid_fmt(onto, &rebase->onto_id); if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", @@ -1182,6 +1326,8 @@ void git_rebase_free(git_rebase *rebase) if (rebase == NULL) return; + git_index_free(rebase->index); + git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); git__free(rebase->state_path); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 85b5034d6..f6ed7201a 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -717,7 +717,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * assert(file && backend && name); - if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) { + if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name); return GIT_EINVALIDSPEC; } @@ -1512,8 +1512,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) #undef seek_forward fail: - if (entry) - git_reflog_entry__free(entry); + git_reflog_entry__free(entry); return -1; } @@ -1672,7 +1671,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char repo = backend->repo; - if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) { + if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) { giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname); return GIT_EINVALIDSPEC; } diff --git a/src/refspec.c b/src/refspec.c index f92a6d2b6..debde8692 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -323,8 +323,8 @@ int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) if (git__prefixcmp(spec->src, GIT_REFS_DIR)) { for (j = 0; formatters[j]; j++) { git_buf_clear(&buf); - if (git_buf_printf(&buf, formatters[j], spec->src) < 0) - return -1; + git_buf_printf(&buf, formatters[j], spec->src); + GITERR_CHECK_ALLOC_BUF(&buf); key.name = (char *) git_buf_cstr(&buf); if (!git_vector_search(&pos, refs, &key)) { @@ -348,8 +348,8 @@ int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) git_buf_puts(&buf, GIT_REFS_HEADS_DIR); } - if (git_buf_puts(&buf, spec->dst) < 0) - return -1; + git_buf_puts(&buf, spec->dst); + GITERR_CHECK_ALLOC_BUF(&buf); cur->dst = git_buf_detach(&buf); } diff --git a/src/remote.c b/src/remote.c index 2f8ffcb37..8b7203ee2 100644 --- a/src/remote.c +++ b/src/remote.c @@ -208,8 +208,8 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n remote->repo = repo; - if (git_vector_init(&remote->refs, 32, NULL) < 0 || - canonicalize_url(&canonical_url, url) < 0) + if ((error = git_vector_init(&remote->refs, 32, NULL)) < 0 || + (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH); diff --git a/src/revwalk.c b/src/revwalk.c index 89279ed1f..4815a1089 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -223,8 +223,7 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); - if (git_buf_oom(&buf)) - return -1; + GITERR_CHECK_ALLOC_BUF(&buf); /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); diff --git a/src/settings.c b/src/settings.c index da99b59e2..d7341abe8 100644 --- a/src/settings.c +++ b/src/settings.c @@ -181,6 +181,9 @@ int git_libgit2_opts(int key, ...) } break; + default: + giterr_set(GITERR_INVALID, "invalid option key"); + error = -1; } va_end(ap); diff --git a/src/signature.c b/src/signature.c index 109476efe..d07c93323 100644 --- a/src/signature.c +++ b/src/signature.c @@ -79,10 +79,9 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema GITERR_CHECK_ALLOC(p); p->name = extract_trimmed(name, strlen(name)); + GITERR_CHECK_ALLOC(p->name); p->email = extract_trimmed(email, strlen(email)); - - if (p->name == NULL || p->email == NULL) - return -1; /* oom */ + GITERR_CHECK_ALLOC(p->email); if (p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); diff --git a/src/stash.c b/src/stash.c index 35824659a..43a464e64 100644 --- a/src/stash.c +++ b/src/stash.c @@ -685,8 +685,8 @@ static int merge_indexes( iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || - (error = git_iterator_for_index(&theirs, theirs_index, &iter_opts)) < 0) + (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_index(&theirs, repo, theirs_index, &iter_opts)) < 0) goto done; error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL); @@ -712,7 +712,7 @@ static int merge_index_and_tree( iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 || - (error = git_iterator_for_index(&ours, ours_index, &iter_opts)) < 0 || + (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 || (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0) goto done; @@ -728,7 +728,7 @@ done: static void normalize_apply_options( git_stash_apply_options *opts, const git_stash_apply_options *given_apply_opts) -{ +{ if (given_apply_opts != NULL) { memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options)); } else { diff --git a/src/submodule.c b/src/submodule.c index 6ec4c53e1..38db41529 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -327,7 +327,7 @@ static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cf const git_index_entry *entry; git_buf name = GIT_BUF_INIT; - if ((error = git_iterator_for_index(&i, idx, NULL)) < 0) + if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { @@ -778,9 +778,9 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0) goto cleanup; - entry.ctime.seconds = git_commit_time(head); + entry.ctime.seconds = (int32_t)git_commit_time(head); entry.ctime.nanoseconds = 0; - entry.mtime.seconds = git_commit_time(head); + entry.mtime.seconds = (int32_t)git_commit_time(head); entry.mtime.nanoseconds = 0; git_commit_free(head); @@ -1037,7 +1037,7 @@ static int submodule_repo_create( /** * Repodir: path to the sub-repo. sub-repo goes in: - * <repo-dir>/modules/<name>/ with a gitlink in the + * <repo-dir>/modules/<name>/ with a gitlink in the * sub-repo workdir directory to that repository. */ error = git_buf_join3( @@ -1154,7 +1154,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio clone_options.repository_cb_payload = sm; /* - * Do not perform checkout as part of clone, instead we + * Do not perform checkout as part of clone, instead we * will checkout the specific commit manually. */ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; diff --git a/src/thread-utils.h b/src/thread-utils.h index dd1136caf..14c8a41ff 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -275,7 +275,7 @@ GIT_INLINE(int) git_atomic_get(git_atomic *a) extern int git_online_cpus(void); -#if defined(GIT_THREADS) && defined(GIT_WIN32) +#if defined(GIT_THREADS) && defined(_MSC_VER) # define GIT_MEMORY_BARRIER MemoryBarrier() #elif defined(GIT_THREADS) # define GIT_MEMORY_BARRIER __sync_synchronize() diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index a6ae55d48..2ea57bb64 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -271,6 +271,7 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) line += 3; /* skip "ok " */ if (!(ptr = strchr(line, '\n'))) { giterr_set(GITERR_NET, "Invalid packet line"); + git__free(pkt); return -1; } len = ptr - line; @@ -295,13 +296,12 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); + pkt->ref = NULL; pkt->type = GIT_PKT_NG; line += 3; /* skip "ng " */ - if (!(ptr = strchr(line, ' '))) { - giterr_set(GITERR_NET, "Invalid packet line"); - return -1; - } + if (!(ptr = strchr(line, ' '))) + goto out_err; len = ptr - line; GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1); @@ -312,10 +312,8 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) pkt->ref[len] = '\0'; line = ptr + 1; - if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "Invalid packet line"); - return -1; - } + if (!(ptr = strchr(line, '\n'))) + goto out_err; len = ptr - line; GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1); @@ -327,6 +325,12 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) *out = (git_pkt *)pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "Invalid packet line"); + git__free(pkt->ref); + git__free(pkt); + return -1; } static int unpack_pkt(git_pkt **out, const char *line, size_t len) @@ -540,7 +544,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca "%04xwant %s %s\n", (unsigned int)len, oid, git_buf_cstr(&str)); git_buf_free(&str); - return git_buf_oom(buf); + GITERR_CHECK_ALLOC_BUF(buf); + + return 0; } /* diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 1d46d4bc9..6363378ec 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -108,6 +108,7 @@ static int append_symref(const char **out, git_vector *symrefs, const char *ptr) if (giterr_last()->klass != GITERR_NOMEMORY) goto on_invalid; + git__free(mapping); return error; } @@ -120,6 +121,7 @@ static int append_symref(const char **out, git_vector *symrefs, const char *ptr) on_invalid: giterr_set(GITERR_NET, "remote sent invalid symref"); git_refspec__free(mapping); + git__free(mapping); return -1; } diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 9e97a279d..ded041686 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -53,10 +53,15 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA; #if defined(__MINGW32__) -const CLSID CLSID_InternetSecurityManager = { 0x7B8A2D94, 0x0AC9, 0x11D1, +static const CLSID CLSID_InternetSecurityManager_mingw = + { 0x7B8A2D94, 0x0AC9, 0x11D1, { 0x89, 0x6C, 0x00, 0xC0, 0x4F, 0xB6, 0xBF, 0xC4 } }; -const IID IID_IInternetSecurityManager = { 0x79EAC9EE, 0xBAF9, 0x11CE, +static const IID IID_IInternetSecurityManager_mingw = + { 0x79EAC9EE, 0xBAF9, 0x11CE, { 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B } }; + +# define CLSID_InternetSecurityManager CLSID_InternetSecurityManager_mingw +# define IID_IInternetSecurityManager IID_IInternetSecurityManager_mingw #endif #define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport) @@ -278,7 +283,7 @@ static int winhttp_stream_connect(winhttp_stream *s) unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; - int i; + size_t i; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); diff --git a/src/tree.c b/src/tree.c index aab4b58ad..cfceb3f33 100644 --- a/src/tree.c +++ b/src/tree.c @@ -17,6 +17,9 @@ #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 +#define TREE_ENTRY_CHECK_NAMELEN(n) \ + if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); } + GIT__USE_STRMAP static bool valid_filemode(const int filemode) @@ -89,10 +92,7 @@ static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, si git_tree_entry *entry = NULL; size_t tree_len; - if (filename_len > UINT16_MAX) { - giterr_set(GITERR_INVALID, "tree entry is over UINT16_MAX in length"); - return NULL; - } + TREE_ENTRY_CHECK_NAMELEN(filename_len); if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) || GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1)) @@ -106,7 +106,7 @@ static git_tree_entry *alloc_entry_base(git_pool *pool, const char *filename, si memset(entry, 0x0, sizeof(git_tree_entry)); memcpy(entry->filename, filename, filename_len); entry->filename[filename_len] = 0; - entry->filename_len = filename_len; + entry->filename_len = (uint16_t)filename_len; return entry; } @@ -143,8 +143,8 @@ static int homing_search_cmp(const void *key, const void *array_member) const struct tree_key_search *ksearch = key; const git_tree_entry *entry = array_member; - const size_t len1 = ksearch->filename_len; - const size_t len2 = entry->filename_len; + const uint16_t len1 = ksearch->filename_len; + const uint16_t len2 = entry->filename_len; return memcmp( ksearch->filename, @@ -180,8 +180,10 @@ static int tree_key_search( const git_tree_entry *entry; size_t homing, i; + TREE_ENTRY_CHECK_NAMELEN(filename_len); + ksearch.filename = filename; - ksearch.filename_len = filename_len; + ksearch.filename_len = (uint16_t)filename_len; /* Initial homing search; find an entry on the tree with * the same prefix as the filename we're looking for */ @@ -334,6 +336,7 @@ const git_tree_entry *git_tree_entry_byname( const git_tree *tree, const char *filename) { assert(tree && filename); + return entry_fromname(tree, filename, strlen(filename)); } @@ -364,13 +367,16 @@ int git_tree__prefix_position(const git_tree *tree, const char *path) { const git_vector *entries = &tree->entries; struct tree_key_search ksearch; - size_t at_pos; + size_t at_pos, path_len; if (!path) return 0; + path_len = strlen(path); + TREE_ENTRY_CHECK_NAMELEN(path_len); + ksearch.filename = path; - ksearch.filename_len = strlen(path); + ksearch.filename_len = (uint16_t)path_len; /* be safe when we cast away constness - i.e. don't trigger a sort */ assert(git_vector_is_sorted(&tree->entries)); diff --git a/src/unix/map.c b/src/unix/map.c index 87ee6594b..72abb3418 100644 --- a/src/unix/map.c +++ b/src/unix/map.c @@ -17,7 +17,7 @@ int git__page_size(size_t *page_size) { long sc_page_size = sysconf(_SC_PAGE_SIZE); if (sc_page_size < 0) { - giterr_set_str(GITERR_OS, "Can't determine system page size"); + giterr_set(GITERR_OS, "can't determine system page size"); return -1; } *page_size = (size_t) sc_page_size; diff --git a/src/unix/posix.h b/src/unix/posix.h index 6633689bc..482d2c803 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -21,6 +21,18 @@ typedef int GIT_SOCKET; #define p_lstat(p,b) lstat(p,b) #define p_stat(p,b) stat(p, b) +#if defined(GIT_USE_STAT_MTIMESPEC) +# define st_atime_nsec st_atimespec.tv_nsec +# define st_mtime_nsec st_mtimespec.tv_nsec +# define st_ctime_nsec st_ctimespec.tv_nsec +#elif defined(GIT_USE_STAT_MTIM) +# define st_atime_nsec st_atim.tv_nsec +# define st_mtime_nsec st_mtim.tv_nsec +# define st_ctime_nsec st_ctim.tv_nsec +#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NEC) +# error GIT_USE_NSEC defined but unknown struct stat nanosecond type +#endif + #define p_utimes(f, t) utimes(f, t) #define p_readlink(a, b, c) readlink(a, b, c) @@ -52,8 +64,10 @@ extern char *p_realpath(const char *, char *); #define p_localtime_r(c, r) localtime_r(c, r) #define p_gmtime_r(c, r) gmtime_r(c, r) +#define p_timeval timeval + #ifdef HAVE_FUTIMENS -GIT_INLINE(int) p_futimes(int f, const struct timeval t[2]) +GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2]) { struct timespec s[2]; s[0].tv_sec = t[0].tv_sec; diff --git a/src/win32/posix.h b/src/win32/posix.h index ac98fd864..5fab267c2 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -9,6 +9,7 @@ #include "common.h" #include "../posix.h" +#include "win32-compat.h" #include "path_w32.h" #include "utf-conv.h" #include "dir.h" @@ -16,12 +17,13 @@ typedef SOCKET GIT_SOCKET; #define p_lseek(f,n,w) _lseeki64(f, n, w) -#define p_fstat(f,b) _fstat64(f, b) + +extern int p_fstat(int fd, struct stat *buf); extern int p_lstat(const char *file_name, struct stat *buf); -extern int p_stat(const char* path, struct stat* buf); +extern int p_stat(const char* path, struct stat *buf); -extern int p_utimes(const char *filename, const struct timeval times[2]); -extern int p_futimes(int fd, const struct timeval times[2]); +extern int p_utimes(const char *filename, const struct p_timeval times[2]); +extern int p_futimes(int fd, const struct p_timeval times[2]); extern int p_readlink(const char *path, char *buf, size_t bufsiz); extern int p_symlink(const char *old, const char *new); diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 414cb4701..fea634b00 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -210,7 +210,7 @@ int p_lstat_posixly(const char *filename, struct stat *buf) return do_lstat(filename, buf, true); } -int p_utimes(const char *filename, const struct timeval times[2]) +int p_utimes(const char *filename, const struct p_timeval times[2]) { int fd, error; @@ -223,7 +223,7 @@ int p_utimes(const char *filename, const struct timeval times[2]) return error; } -int p_futimes(int fd, const struct timeval times[2]) +int p_futimes(int fd, const struct p_timeval times[2]) { HANDLE handle; FILETIME atime = {0}, mtime = {0}; @@ -398,6 +398,22 @@ static int follow_and_lstat_link(git_win32_path path, struct stat* buf) return lstat_w(target_w, buf, false); } +int p_fstat(int fd, struct stat *buf) +{ + BY_HANDLE_FILE_INFORMATION fhInfo; + + HANDLE fh = (HANDLE)_get_osfhandle(fd); + + if (fh == INVALID_HANDLE_VALUE || + !GetFileInformationByHandle(fh, &fhInfo)) { + errno = EBADF; + return -1; + } + + git_win32__file_information_to_stat(buf, &fhInfo); + return 0; +} + int p_stat(const char* path, struct stat* buf) { git_win32_path path_w; diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index f1b674ea0..96fd4606e 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -8,20 +8,6 @@ #include "common.h" #include "utf-conv.h" -GIT_INLINE(DWORD) get_wc_flags(void) -{ - static char inited = 0; - static DWORD flags; - - /* Invalid code point check supported on Vista+ only */ - if (!inited) { - flags = git_has_win32_version(6, 0, 0) ? WC_ERR_INVALID_CHARS : 0; - inited = 1; - } - - return flags; -} - GIT_INLINE(void) git__set_errno(void) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) @@ -66,7 +52,7 @@ int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src) /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */ - if ((len = WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0) + if ((len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0) git__set_errno(); return len; @@ -127,12 +113,11 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src) int git__utf16_to_8_alloc(char **dest, const wchar_t *src) { int utf8_size; - DWORD dwFlags = get_wc_flags(); *dest = NULL; /* Length of -1 indicates NULL termination of the input string */ - utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL); + utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL); if (!utf8_size) { git__set_errno(); @@ -146,7 +131,7 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src) return -1; } - utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL); + utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, *dest, utf8_size, NULL, NULL); if (!utf8_size) { git__set_errno(); diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 727ed1cef..2e475e5e9 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -96,7 +96,7 @@ GIT_INLINE(void) git_win32__filetime_to_timespec( } GIT_INLINE(void) git_win32__timeval_to_filetime( - FILETIME *ft, const struct timeval tv) + FILETIME *ft, const struct p_timeval tv) { long long ticks = (tv.tv_sec * 10000000LL) + (tv.tv_usec * 10LL) + 116444736000000000LL; @@ -105,19 +105,25 @@ GIT_INLINE(void) git_win32__timeval_to_filetime( ft->dwLowDateTime = (ticks & 0xffffffffLL); } -GIT_INLINE(int) git_win32__file_attribute_to_stat( +GIT_INLINE(void) git_win32__stat_init( struct stat *st, - const WIN32_FILE_ATTRIBUTE_DATA *attrdata, - const wchar_t *path) + DWORD dwFileAttributes, + DWORD nFileSizeHigh, + DWORD nFileSizeLow, + FILETIME ftCreationTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime) { mode_t mode = S_IREAD; - if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + memset(st, 0, sizeof(struct stat)); + + if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR; else mode |= S_IFREG; - if ((attrdata->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) + if ((dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) mode |= S_IWRITE; st->st_ino = 0; @@ -125,12 +131,39 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_uid = 0; st->st_nlink = 1; st->st_mode = mode; - st->st_size = ((git_off_t)attrdata->nFileSizeHigh << 32) + attrdata->nFileSizeLow; + st->st_size = ((git_off_t)nFileSizeHigh << 32) + nFileSizeLow; st->st_dev = _getdrive() - 1; st->st_rdev = st->st_dev; - git_win32__filetime_to_timespec(&(attrdata->ftLastAccessTime), &(st->st_atim)); - git_win32__filetime_to_timespec(&(attrdata->ftLastWriteTime), &(st->st_mtim)); - git_win32__filetime_to_timespec(&(attrdata->ftCreationTime), &(st->st_ctim)); + git_win32__filetime_to_timespec(&ftLastAccessTime, &(st->st_atim)); + git_win32__filetime_to_timespec(&ftLastWriteTime, &(st->st_mtim)); + git_win32__filetime_to_timespec(&ftCreationTime, &(st->st_ctim)); +} + +GIT_INLINE(void) git_win32__file_information_to_stat( + struct stat *st, + const BY_HANDLE_FILE_INFORMATION *fileinfo) +{ + git_win32__stat_init(st, + fileinfo->dwFileAttributes, + fileinfo->nFileSizeHigh, + fileinfo->nFileSizeLow, + fileinfo->ftCreationTime, + fileinfo->ftLastAccessTime, + fileinfo->ftLastWriteTime); +} + +GIT_INLINE(int) git_win32__file_attribute_to_stat( + struct stat *st, + const WIN32_FILE_ATTRIBUTE_DATA *attrdata, + const wchar_t *path) +{ + git_win32__stat_init(st, + attrdata->dwFileAttributes, + attrdata->nFileSizeHigh, + attrdata->nFileSizeLow, + attrdata->ftCreationTime, + attrdata->ftLastAccessTime, + attrdata->ftLastWriteTime); if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) { git_win32_path target; @@ -139,7 +172,7 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat( st->st_mode = (st->st_mode & ~S_IFMT) | S_IFLNK; /* st_size gets the UTF-8 length of the target name, in bytes, - * not counting the NULL terminator */ + * not counting the NULL terminator */ if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) { giterr_set(GITERR_OS, "Could not convert reparse point name for '%s'", path); return -1; diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h index d3a5b68a3..f888fd69e 100644 --- a/src/win32/win32-compat.h +++ b/src/win32/win32-compat.h @@ -13,6 +13,13 @@ #include <sys/stat.h> #include <sys/types.h> +typedef long suseconds_t; + +struct p_timeval { + time_t tv_sec; + suseconds_t tv_usec; +}; + struct p_timespec { time_t tv_sec; long tv_nsec; @@ -35,6 +42,9 @@ struct p_stat { #define st_atime st_atim.tv_sec #define st_mtime st_mtim.tv_sec #define st_ctime st_ctim.tv_sec +#define st_atime_nsec st_atim.tv_nsec +#define st_mtime_nsec st_mtim.tv_nsec +#define st_ctime_nsec st_ctim.tv_nsec }; #define stat p_stat diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c index 7b7e0e2d3..7928d1418 100644 --- a/src/xdiff/xmerge.c +++ b/src/xdiff/xmerge.c @@ -646,6 +646,8 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 || xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 || xdl_build_script(&xe2, &xscr2) < 0) { + xdl_free_script(xscr1); + xdl_free_env(&xe1); xdl_free_env(&xe2); return -1; } diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index fb2f415e7..d7d24f33f 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -133,7 +133,7 @@ int checkout_count_callback( void tick_index(git_index *index) { struct timespec ts; - struct timeval times[2]; + struct p_timeval times[2]; cl_assert(index->on_disk); cl_assert(git_index_path(index)); diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 388da078a..838cfb467 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -443,16 +443,87 @@ cpxtDQQMGYFpXK/71stq\n\ cl_git_pass(parse_commit(&commit, passing_commit_cases[4])); + cl_git_pass(git_commit_header_field(&buf, commit, "tree")); + cl_assert_equal_s("6b79e22d69bf46e289df0345a14ca059dfc9bdf6", buf.ptr); + git_buf_clear(&buf); + cl_git_pass(git_commit_header_field(&buf, commit, "parent")); cl_assert_equal_s("34734e478d6cf50c27c9d69026d93974d052c454", buf.ptr); git_buf_clear(&buf); cl_git_pass(git_commit_header_field(&buf, commit, "gpgsig")); cl_assert_equal_s(gpgsig, buf.ptr); + git_buf_clear(&buf); cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "awesomeness")); cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "par")); + git_commit__free(commit); + cl_git_pass(parse_commit(&commit, passing_commit_cases[0])); + + cl_git_pass(git_commit_header_field(&buf, commit, "committer")); + cl_assert_equal_s("Vicent Marti <tanoku@gmail.com> 1273848544 +0200", buf.ptr); + git_buf_free(&buf); git_commit__free(commit); } + +void test_commit_parse__extract_signature(void) +{ + git_odb *odb; + git_oid commit_id; + git_buf signature = GIT_BUF_INIT, signed_data = GIT_BUF_INIT; + const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\ +Version: GnuPG v1.4.12 (Darwin)\n\ +\n\ +iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\ +o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\ +JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\ +AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\ +SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\ +who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\ +6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\ +cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\ +c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\ +ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\ +7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\ +cpxtDQQMGYFpXK/71stq\n\ +=ozeK\n\ +-----END PGP SIGNATURE-----"; + + const char *data = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\ +parent 34734e478d6cf50c27c9d69026d93974d052c454\n\ +author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\ +committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\ +\n\ +a simple commit which works\n"; + + + cl_git_pass(git_repository_odb__weakptr(&odb, g_repo)); + cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJ_COMMIT)); + + cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_s(gpgsig, signature.ptr); + cl_assert_equal_s(data, signed_data.ptr); + + git_buf_clear(&signature); + git_buf_clear(&signed_data); + + cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, "gpgsig")); + cl_assert_equal_s(gpgsig, signature.ptr); + cl_assert_equal_s(data, signed_data.ptr); + + /* Try to parse a tree */ + cl_git_pass(git_oid_fromstr(&commit_id, "45dd856fdd4d89b884c340ba0e047752d9b085d6")); + cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_i(GITERR_INVALID, giterr_last()->klass); + + /* Try to parse an unsigned commit */ + cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[1], strlen(passing_commit_cases[1]), GIT_OBJ_COMMIT)); + cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL)); + cl_assert_equal_i(GITERR_OBJECT, giterr_last()->klass); + + git_buf_free(&signature); + git_buf_free(&signed_data); + +} diff --git a/tests/core/opts.c b/tests/core/opts.c index 3173c648b..72408cbe8 100644 --- a/tests/core/opts.c +++ b/tests/core/opts.c @@ -17,3 +17,9 @@ void test_core_opts__readwrite(void) cl_assert(new_val == old_val); } + +void test_core_opts__invalid_option(void) +{ + cl_git_fail(git_libgit2_opts(-1, "foobar")); +} + diff --git a/tests/core/pool.c b/tests/core/pool.c index c43c1db67..b07da0abd 100644 --- a/tests/core/pool.c +++ b/tests/core/pool.c @@ -31,8 +31,10 @@ void test_core_pool__1(void) for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(591, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); git_pool_init(&p, 1); @@ -41,8 +43,10 @@ void test_core_pool__1(void) for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(sizeof(void *) == 8 ? 575 : 573, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); } @@ -69,8 +73,10 @@ void test_core_pool__2(void) cl_git_pass(git_oid_fromstr(oid, oid_hex)); } +#ifndef GIT_DEBUG_POOL /* with fixed page size, allocation must end up with these values */ cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p)); +#endif git_pool_clear(&p); } diff --git a/tests/core/posix.c b/tests/core/posix.c index 5a9e24899..34a67bf47 100644 --- a/tests/core/posix.c +++ b/tests/core/posix.c @@ -100,7 +100,7 @@ void test_core_posix__inet_pton(void) void test_core_posix__utimes(void) { - struct timeval times[2]; + struct p_timeval times[2]; struct stat st; time_t curtime; int fd; diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c index eafb1b9da..8417e8ed4 100644 --- a/tests/diff/iterator.c +++ b/tests/diff/iterator.c @@ -380,7 +380,7 @@ static void index_iterator_test( iter_opts.start = start; iter_opts.end = end; - cl_git_pass(git_iterator_for_index(&i, index, &iter_opts)); + cl_git_pass(git_iterator_for_index(&i, repo, index, &iter_opts)); while (!(error = git_iterator_advance(&entry, i))) { cl_assert(entry); @@ -974,7 +974,7 @@ static void check_index_range( i_opts.start = start; i_opts.end = end; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, repo, index, &i_opts)); cl_assert(git_iterator_ignore_case(i) == ignore_case); diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 4c782339d..892c7b72d 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -1755,7 +1755,7 @@ void test_diff_workdir__with_stale_index(void) static int touch_file(void *payload, git_buf *path) { struct stat st; - struct timeval times[2]; + struct p_timeval times[2]; GIT_UNUSED(payload); if (git_path_isdir(path->ptr)) @@ -2006,7 +2006,7 @@ void test_diff_workdir__only_writes_index_when_necessary(void) git_oid initial, first, second; git_buf path = GIT_BUF_INIT; struct stat st; - struct timeval times[2]; + struct p_timeval times[2]; opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_UPDATE_INDEX; diff --git a/tests/index/nsec.c b/tests/index/nsec.c index 5004339f0..244ab6362 100644 --- a/tests/index/nsec.c +++ b/tests/index/nsec.c @@ -61,8 +61,17 @@ void test_index_nsec__staging_maintains_other_nanos(void) cl_assert_equal_b(true, has_nsecs()); cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0))); + + /* if we are writing nanoseconds to the index, expect them to be + * nonzero. if we are *not*, expect that we truncated the entry. + */ +#ifdef GIT_USE_NSEC + cl_assert(entry->ctime.nanoseconds != 0); + cl_assert(entry->mtime.nanoseconds != 0); +#else cl_assert_equal_i(0, entry->ctime.nanoseconds); cl_assert_equal_i(0, entry->mtime.nanoseconds); +#endif } void test_index_nsec__status_doesnt_clear_nsecs(void) diff --git a/tests/index/racy.c b/tests/index/racy.c index e2275ea14..ace84d585 100644 --- a/tests/index/racy.c +++ b/tests/index/racy.c @@ -54,7 +54,7 @@ void test_index_racy__write_index_just_after_file(void) git_index *index; git_diff *diff; git_buf path = GIT_BUF_INIT; - struct timeval times[2]; + struct p_timeval times[2]; /* Make sure we do have a timestamp */ cl_git_pass(git_repository_index(&index, g_repo)); diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index f168963b2..99e33e0cd 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -133,7 +133,7 @@ static void hack_index(char *files[]) struct stat statbuf; git_buf path = GIT_BUF_INIT; git_index_entry *entry; - struct timeval times[2]; + struct p_timeval times[2]; time_t now; size_t i; @@ -162,8 +162,8 @@ static void hack_index(char *files[]) cl_git_pass(p_utimes(path.ptr, times)); cl_git_pass(p_stat(path.ptr, &statbuf)); - entry->ctime.seconds = (git_time_t)statbuf.st_ctime; - entry->mtime.seconds = (git_time_t)statbuf.st_mtime; + entry->ctime.seconds = (int32_t)statbuf.st_ctime; + entry->mtime.seconds = (int32_t)statbuf.st_mtime; #if defined(GIT_USE_NSEC) entry->ctime.nanoseconds = statbuf.st_ctim.tv_nsec; entry->mtime.nanoseconds = statbuf.st_mtim.tv_nsec; @@ -175,7 +175,7 @@ static void hack_index(char *files[]) entry->ino = statbuf.st_ino; entry->uid = statbuf.st_uid; entry->gid = statbuf.st_gid; - entry->file_size = statbuf.st_size; + entry->file_size = (uint32_t)statbuf.st_size; } git_buf_free(&path); diff --git a/tests/path/core.c b/tests/path/core.c index 064f1492a..3dccfe5fb 100644 --- a/tests/path/core.c +++ b/tests/path/core.c @@ -105,12 +105,12 @@ void test_path_core__isvalid_dot_git(void) cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0)); cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0)); - cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT)); - cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT_LITERAL)); cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0)); cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0)); diff --git a/tests/rebase/inmemory.c b/tests/rebase/inmemory.c new file mode 100644 index 000000000..d5d89c719 --- /dev/null +++ b/tests/rebase/inmemory.c @@ -0,0 +1,116 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" + +#include <fcntl.h> + +static git_repository *repo; + +// Fixture setup and teardown +void test_rebase_inmemory__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); +} + +void test_rebase_inmemory__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_rebase_inmemory__not_in_rebase_state(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + git_rebase_free(rebase); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + + git_reference_free(branch_ref); + git_reference_free(upstream_ref); +} + +void test_rebase_inmemory__can_resolve_conflicts(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_status_list *status_list; + git_oid pick_id, commit_id, expected_commit_id; + git_signature *signature; + git_index *rebase_index, *repo_index; + git_index_entry resolution = {{0}}; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + + cl_git_pass(git_signature_new(&signature, + "Rebaser", "rebaser@rebaser.rb", 1405694510, 0)); + + opts.inmemory = true; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); + + git_oid_fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500"); + + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type); + cl_assert_equal_oid(&pick_id, &rebase_operation->id); + + /* ensure that we did not do anything stupid to the workdir or repo index */ + cl_git_pass(git_repository_index(&repo_index, repo)); + cl_assert(!git_index_has_conflicts(repo_index)); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status_list)); + + /* but that the index returned from rebase does have conflicts */ + cl_git_pass(git_rebase_inmemory_index(&rebase_index, rebase)); + cl_assert(git_index_has_conflicts(rebase_index)); + + cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + /* ensure that we can work with the in-memory index to resolve the conflict */ + resolution.path = "asparagus.txt"; + resolution.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9"); + cl_git_pass(git_index_conflict_remove(rebase_index, "asparagus.txt")); + cl_git_pass(git_index_add(rebase_index, &resolution)); + + /* and finally create a commit for the resolved rebase operation */ + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); + + cl_git_pass(git_oid_fromstr(&expected_commit_id, "db7af47222181e548810da2ab5fec0e9357c5637")); + cl_assert_equal_oid(&commit_id, &expected_commit_id); + + git_signature_free(signature); + git_status_list_free(status_list); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_index_free(repo_index); + git_index_free(rebase_index); + git_rebase_free(rebase); +} diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c index acf2a92db..db57b0a83 100644 --- a/tests/rebase/iterator.c +++ b/tests/rebase/iterator.c @@ -13,7 +13,8 @@ void test_rebase_iterator__initialize(void) { repo = cl_git_sandbox_init("rebase"); cl_git_pass(git_repository_index(&_index, repo)); - cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb")); + cl_git_pass(git_signature_new(&signature, "Rebaser", + "rebaser@rebaser.rb", 1405694510, 0)); } void test_rebase_iterator__cleanup(void) @@ -46,54 +47,77 @@ static void test_operations(git_rebase *rebase, size_t expected_current) } } -void test_rebase_iterator__iterates(void) +void test_iterator(bool inmemory) { git_rebase *rebase; + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; git_reference *branch_ref, *upstream_ref; git_annotated_commit *branch_head, *upstream_head; git_rebase_operation *rebase_operation; - git_oid commit_id; + git_oid commit_id, expected_id; int error; + opts.inmemory = inmemory; + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); - cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL)); + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts)); test_operations(rebase, GIT_REBASE_NO_OPERATION); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 0); + git_oid_fromstr(&expected_id, "776e4c48922799f903f03f5f6e51da8b01e4cce0"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 1); + git_oid_fromstr(&expected_id, "ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 2); - git_rebase_free(rebase); - cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + git_oid_fromstr(&expected_id, "948b12fe18b84f756223a61bece4c307787cd5d4"); + cl_assert_equal_oid(&expected_id, &commit_id); + + if (!inmemory) { + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo, NULL)); + } cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 3); + git_oid_fromstr(&expected_id, "d9d5d59d72c9968687f9462578d79878cd80e781"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 4); + git_oid_fromstr(&expected_id, "9cf383c0a125d89e742c5dec58ed277dd07588b3"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase)); cl_assert_equal_i(GIT_ITEROVER, error); test_operations(rebase, 4); @@ -104,3 +128,13 @@ void test_rebase_iterator__iterates(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_iterator__iterates(void) +{ + test_iterator(false); +} + +void test_rebase_iterator__iterates_inmemory(void) +{ + test_iterator(true); +} diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c index 33eadb7ed..c60113b64 100644 --- a/tests/rebase/merge.c +++ b/tests/rebase/merge.c @@ -565,3 +565,33 @@ void test_rebase_merge__custom_checkout_options(void) git_reference_free(upstream_ref); git_rebase_free(rebase); } + +void test_rebase_merge__custom_merge_options(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_options rebase_options = GIT_REBASE_OPTIONS_INIT; + git_rebase_operation *rebase_operation; + + rebase_options.merge_options.flags |= + GIT_MERGE_FAIL_ON_CONFLICT | + GIT_MERGE_SKIP_REUC; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref)); + cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_options)); + + cl_git_fail_with(GIT_EMERGECONFLICT, git_rebase_next(&rebase_operation, rebase)); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index 83b824691..c18e24a4f 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -134,19 +134,19 @@ void test_repo_iterator__index(void) cl_git_pass(git_repository_index(&index, g_repo)); /* autoexpand with no tree entries for index */ - cl_git_pass(git_iterator_for_index(&i, index, NULL)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, NULL)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); @@ -171,13 +171,13 @@ void test_repo_iterator__index_icase(void) /* autoexpand with no tree entries over range */ i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); @@ -186,13 +186,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); @@ -201,13 +201,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); @@ -219,13 +219,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); @@ -234,13 +234,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); @@ -249,13 +249,13 @@ void test_repo_iterator__index_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); @@ -1108,14 +1108,14 @@ void test_repo_iterator__indexfilelist(void) /* All indexfilelist iterator tests are "autoexpand with no tree entries" */ - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); i_opts.start = "c"; i_opts.end = NULL; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */ expect = ((default_icase) ? 6 : 4); expect_iterator_items(i, expect, NULL, expect, NULL); @@ -1124,7 +1124,7 @@ void test_repo_iterator__indexfilelist(void) i_opts.start = NULL; i_opts.end = "e"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */ expect = ((default_icase) ? 5 : 6); expect_iterator_items(i, expect, NULL, expect, NULL); @@ -1166,7 +1166,7 @@ void test_repo_iterator__indexfilelist_2(void) /* (c D e k/1 k/a ==> 5) vs (c e k/1 ==> 3) */ expect = default_icase ? 5 : 3; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1208,7 +1208,7 @@ void test_repo_iterator__indexfilelist_3(void) /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ expect = default_icase ? 8 : 5; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1250,7 +1250,7 @@ void test_repo_iterator__indexfilelist_4(void) /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */ expect = default_icase ? 8 : 5; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, expect, NULL, expect, NULL); git_iterator_free(i); @@ -1291,13 +1291,13 @@ void test_repo_iterator__indexfilelist_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 1, NULL, 1, NULL); git_iterator_free(i); @@ -1306,13 +1306,13 @@ void test_repo_iterator__indexfilelist_icase(void) i_opts.start = "c"; i_opts.end = "k/D"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); i_opts.start = "k"; i_opts.end = "k/Z"; - cl_git_pass(git_iterator_for_index(&i, index, &i_opts)); + cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts)); expect_iterator_items(i, 2, NULL, 2, NULL); git_iterator_free(i); diff --git a/tests/reset/hard.c b/tests/reset/hard.c index 149973374..0c0af914c 100644 --- a/tests/reset/hard.c +++ b/tests/reset/hard.c @@ -238,7 +238,7 @@ void test_reset_hard__reflog_is_correct(void) void test_reset_hard__switch_file_to_dir(void) { - git_index_entry entry = { 0 }; + git_index_entry entry = {{ 0 }}; git_index *idx; git_object *commit; git_tree *tree; diff --git a/tests/resources/win32-forbidden/.gitted/HEAD b/tests/resources/win32-forbidden/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/win32-forbidden/.gitted/config b/tests/resources/win32-forbidden/.gitted/config new file mode 100644 index 000000000..6c9406b7d --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/config @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff --git a/tests/resources/win32-forbidden/.gitted/index b/tests/resources/win32-forbidden/.gitted/index Binary files differnew file mode 100644 index 000000000..1202dd9f4 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/index diff --git a/tests/resources/win32-forbidden/.gitted/info/exclude b/tests/resources/win32-forbidden/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356 b/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356 Binary files differnew file mode 100644 index 000000000..2d3b31adc --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356 diff --git a/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 Binary files differnew file mode 100644 index 000000000..ef8316670 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 diff --git a/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 Binary files differnew file mode 100644 index 000000000..baddb1fc6 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 diff --git a/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db b/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db new file mode 100644 index 000000000..71b6172c6 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db @@ -0,0 +1,3 @@ +x +![whӢ}/8B*8N~@gu՜S ͡7a +f2"s
e.%Q ؽm[kjmGq_N3LBJŔFG:BVRO ͿҖj!=
\ No newline at end of file diff --git a/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a b/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a new file mode 100644 index 000000000..8bcd980c4 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a @@ -0,0 +1,2 @@ +xA + ֝/k. ]`h"=˕e*U+M%O4c˱
\ No newline at end of file diff --git a/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37 b/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37 Binary files differnew file mode 100644 index 000000000..923462306 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37 diff --git a/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba b/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba new file mode 100644 index 000000000..32b3f02ec --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba @@ -0,0 +1,3 @@ +xM +B!F;BoW}BDۀ +>_m?BgqVJ =FdJTqDdBN'6Rp
S+kpGrAR*Tz! vVGn5l_;U>H
\ No newline at end of file diff --git a/tests/resources/win32-forbidden/.gitted/refs/heads/master b/tests/resources/win32-forbidden/.gitted/refs/heads/master new file mode 100644 index 000000000..47fce0fe3 --- /dev/null +++ b/tests/resources/win32-forbidden/.gitted/refs/heads/master @@ -0,0 +1 @@ +3496991d72d500af36edef68bbfcccd1661d88db diff --git a/tests/win32/forbidden.c b/tests/win32/forbidden.c new file mode 100644 index 000000000..e02f41179 --- /dev/null +++ b/tests/win32/forbidden.c @@ -0,0 +1,183 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "buffer.h" +#include "submodule.h" + +static const char *repo_name = "win32-forbidden"; +static git_repository *repo; + +void test_win32_forbidden__initialize(void) +{ + repo = cl_git_sandbox_init(repo_name); +} + +void test_win32_forbidden__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_win32_forbidden__can_open_index(void) +{ + git_index *index; + cl_git_pass(git_repository_index(&index, repo)); + cl_assert_equal_i(7, git_index_entrycount(index)); + + /* ensure we can even write the unmodified index */ + cl_git_pass(git_index_write(index)); + + git_index_free(index); +} + +void test_win32_forbidden__can_add_forbidden_filename_with_entry(void) +{ + git_index *index; + git_index_entry entry = {{0}}; + + cl_git_pass(git_repository_index(&index, repo)); + + entry.path = "aux"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37"); + + cl_git_pass(git_index_add(index, &entry)); + + git_index_free(index); +} + +void test_win32_forbidden__cannot_add_dot_git_even_with_entry(void) +{ + git_index *index; + git_index_entry entry = {{0}}; + + cl_git_pass(git_repository_index(&index, repo)); + + entry.path = "foo/.git"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37"); + + cl_git_fail(git_index_add(index, &entry)); + + git_index_free(index); +} + +void test_win32_forbidden__cannot_add_forbidden_filename_from_filesystem(void) +{ + git_index *index; + + /* since our function calls are very low-level, we can create `aux.`, + * but we should not be able to add it to the index + */ + cl_git_pass(git_repository_index(&index, repo)); + cl_git_write2file("win32-forbidden/aux.", "foo\n", 4, O_RDWR | O_CREAT, 0666); + +#ifdef GIT_WIN32 + cl_git_fail(git_index_add_bypath(index, "aux.")); +#else + cl_git_pass(git_index_add_bypath(index, "aux.")); +#endif + + cl_must_pass(p_unlink("win32-forbidden/aux.")); + git_index_free(index); +} + +static int dummy_submodule_cb( + git_submodule *sm, const char *name, void *payload) +{ + GIT_UNUSED(sm); + GIT_UNUSED(name); + GIT_UNUSED(payload); + return 0; +} + +void test_win32_forbidden__can_diff_tree_to_index(void) +{ + git_diff *diff; + git_tree *tree; + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, NULL, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + git_tree_free(tree); +} + +void test_win32_forbidden__can_diff_tree_to_tree(void) +{ + git_diff *diff; + git_tree *tree; + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree, tree, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + git_tree_free(tree); +} + +void test_win32_forbidden__can_diff_index_to_workdir(void) +{ + git_index *index; + git_diff *diff; + const git_diff_delta *delta; + git_tree *tree; + size_t i; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL)); + + for (i = 0; i < git_diff_num_deltas(diff); i++) { + delta = git_diff_get_delta(diff, i); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + } + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); +} + +void test_win32_forbidden__checking_out_forbidden_index_fails(void) +{ +#ifdef GIT_WIN32 + git_index *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_diff *diff; + const git_diff_delta *delta; + git_tree *tree; + size_t num_deltas, i; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_fail(git_checkout_index(repo, index, &opts)); + + cl_git_pass(git_repository_head_tree(&tree, repo)); + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL)); + + num_deltas = git_diff_num_deltas(diff); + + cl_assert(num_deltas > 0); + + for (i = 0; i < num_deltas; i++) { + delta = git_diff_get_delta(diff, i); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + } + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); +#endif +} + +void test_win32_forbidden__can_query_submodules(void) +{ + cl_git_pass(git_submodule_foreach(repo, dummy_submodule_cb, NULL)); +} + +void test_win32_forbidden__can_blame_file(void) +{ + git_blame *blame; + + cl_git_pass(git_blame_file(&blame, repo, "aux", NULL)); + git_blame_free(blame); +} diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c index 07eecd394..5a36875ed 100644 --- a/tests/win32/longpath.c +++ b/tests/win32/longpath.c @@ -36,7 +36,7 @@ void assert_name_too_long(void) { const git_error *err; size_t expected_len, actual_len; - const char *expected_msg; + char *expected_msg; err = giterr_last(); actual_len = strlen(err->message); |
