Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--.travis.yml4
-rw-r--r--CHANGELOG.md53
-rw-r--r--CMakeLists.txt35
-rw-r--r--CODE_OF_CONDUCT.md75
-rw-r--r--CONVENTIONS.md32
-rw-r--r--PROJECTS.md6
-rw-r--r--README.md6
-rw-r--r--THREADING.md16
-rw-r--r--appveyor.yml6
-rw-r--r--deps/http-parser/http_parser.c7
-rw-r--r--examples/general.c5
-rw-r--r--include/git2/commit.h64
-rw-r--r--include/git2/common.h15
-rw-r--r--include/git2/diff.h2
-rw-r--r--include/git2/errors.h5
-rw-r--r--include/git2/merge.h8
-rw-r--r--include/git2/rebase.h36
-rw-r--r--include/git2/stash.h2
-rw-r--r--include/git2/sys/filter.h59
-rw-r--r--include/git2/sys/index.h2
-rw-r--r--include/git2/version.h6
-rwxr-xr-xscript/cibuild.sh6
-rwxr-xr-xscript/coverity.sh5
-rw-r--r--script/user_nodefs.h34
-rw-r--r--src/attr_file.c5
-rw-r--r--src/checkout.c11
-rw-r--r--src/commit.c332
-rw-r--r--src/common.h11
-rw-r--r--src/crlf.c2
-rw-r--r--src/curl_stream.c6
-rw-r--r--src/diff.c8
-rw-r--r--src/diff_print.c6
-rw-r--r--src/diff_tform.c27
-rw-r--r--src/fileops.c23
-rw-r--r--src/filter.c279
-rw-r--r--src/filter.h2
-rw-r--r--src/global.c220
-rw-r--r--src/index.c102
-rw-r--r--src/index.h2
-rw-r--r--src/iterator.c17
-rw-r--r--src/iterator.h1
-rw-r--r--src/merge.c19
-rw-r--r--src/netops.c4
-rw-r--r--src/object.c26
-rw-r--r--src/object.h23
-rw-r--r--src/openssl_stream.c138
-rw-r--r--src/openssl_stream.h2
-rw-r--r--src/pack-objects.c24
-rw-r--r--src/pack.c20
-rw-r--r--src/path.c10
-rw-r--r--src/path.h18
-rw-r--r--src/pathspec.c3
-rw-r--r--src/pool.c108
-rw-r--r--src/pool.h28
-rw-r--r--src/posix.c7
-rw-r--r--src/rebase.c400
-rw-r--r--src/refdb_fs.c7
-rw-r--r--src/refs.c8
-rw-r--r--src/refspec.c8
-rw-r--r--src/remote.c4
-rw-r--r--src/revwalk.c3
-rw-r--r--src/settings.c9
-rw-r--r--src/signature.c5
-rw-r--r--src/stash.c8
-rw-r--r--src/submodule.c10
-rw-r--r--src/thread-utils.h2
-rw-r--r--src/transport.c2
-rw-r--r--src/transports/smart_pkt.c24
-rw-r--r--src/transports/smart_protocol.c2
-rw-r--r--src/transports/ssh.c65
-rw-r--r--src/transports/ssh.h12
-rw-r--r--src/transports/winhttp.c16
-rw-r--r--src/tree.c42
-rw-r--r--src/unix/map.c2
-rw-r--r--src/unix/posix.h16
-rw-r--r--src/win32/posix.h10
-rw-r--r--src/win32/posix_w32.c20
-rw-r--r--src/win32/utf-conv.c21
-rw-r--r--src/win32/w32_util.h55
-rw-r--r--src/win32/win32-compat.h10
-rw-r--r--src/xdiff/xmerge.c9
-rw-r--r--tests/checkout/checkout_helpers.c2
-rw-r--r--tests/commit/parse.c71
-rw-r--r--tests/commit/write.c150
-rw-r--r--tests/core/opts.c6
-rw-r--r--tests/core/pool.c6
-rw-r--r--tests/core/posix.c2
-rw-r--r--tests/diff/format_email.c2
-rw-r--r--tests/diff/index.c8
-rw-r--r--tests/diff/iterator.c4
-rw-r--r--tests/diff/workdir.c12
-rw-r--r--tests/index/add.c84
-rw-r--r--tests/index/bypath.c3
-rw-r--r--tests/index/cache.c6
-rw-r--r--tests/index/conflicts.c34
-rw-r--r--tests/index/filemodes.c7
-rw-r--r--tests/index/nsec.c57
-rw-r--r--tests/index/racy.c37
-rw-r--r--tests/merge/workdir/dirty.c12
-rw-r--r--tests/object/tree/write.c98
-rw-r--r--tests/path/core.c12
-rw-r--r--tests/rebase/inmemory.c116
-rw-r--r--tests/rebase/iterator.c50
-rw-r--r--tests/rebase/merge.c30
-rw-r--r--tests/refs/create.c27
-rw-r--r--tests/repo/iterator.c50
-rw-r--r--tests/reset/hard.c2
-rw-r--r--tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863bin0 -> 56 bytes
-rw-r--r--tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46bin0 -> 38 bytes
-rw-r--r--tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/HEAD1
-rw-r--r--tests/resources/win32-forbidden/.gitted/config7
-rw-r--r--tests/resources/win32-forbidden/.gitted/indexbin0 -> 577 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/info/exclude6
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356bin0 -> 143 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623bin0 -> 28 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40bin0 -> 105 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db3
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a2
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37bin0 -> 59 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba3
-rw-r--r--tests/resources/win32-forbidden/.gitted/refs/heads/master1
-rw-r--r--tests/transport/register.c10
-rw-r--r--tests/win32/forbidden.c183
-rw-r--r--tests/win32/longpath.c2
127 files changed, 2910 insertions, 942 deletions
diff --git a/.mailmap b/.mailmap
index c656f64c7..8479cf6c4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -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 0b8615e11..30161821a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,18 @@
-v0.23 + 1
+v0.24 + 1
+-------
+
+### Changes or improvements
+
+### API additions
+
+* `git_commit_create_buffer()` creates a commit and writes it into a
+ user-provided buffer instead of writing it into the object db.
+
+### API removals
+
+### Breaking API changes
+
+v0.24
-------
### Changes or improvements
@@ -20,6 +34,23 @@ 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.
+
+* When adding objects to the index, or when creating new tree or commit
+ objects, the inputs are validated to ensure that the dependent objects
+ exist and are of the correct type. This object validation can be
+ disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option.
+
+* The WinHTTP transport's handling of bad credentials now behaves like
+ the others, asking for credentials again.
+
### API additions
* `git_config_lock()` has been added, which allow for
@@ -35,13 +66,21 @@ 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
+* No APIs were removed in this version.
+
### Breaking API changes
* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently,
@@ -75,6 +114,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
------
@@ -313,8 +358,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 0aa58625a..1801c938d 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,21 +91,25 @@ 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)
- OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" OFF )
+ OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" ON )
ENDIF()
# This variable will contain the libraries we need to put into
@@ -539,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)
@@ -669,6 +682,10 @@ IF (BUILD_CLAR)
ELSE ()
ADD_TEST(libgit2_clar libgit2_clar -v)
ENDIF ()
+
+ # Add a test target which runs the cred callback tests, to be
+ # called after setting the url and user
+ ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback)
ENDIF ()
IF (TAGS)
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
diff --git a/README.md b/README.md
index bcc80d017..8ea787b3e 100644
--- a/README.md
+++ b/README.md
@@ -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/THREADING.md b/THREADING.md
index 3717d6c88..0b9e50286 100644
--- a/THREADING.md
+++ b/THREADING.md
@@ -72,13 +72,19 @@ which locking function it should use. This means that libgit2 cannot
know what to set as the user of libgit2 may use OpenSSL independently
and the locking settings must survive libgit2 shutting down.
+Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used
+by libssh2 depending on the configuration. If OpenSSL is used both by
+libgit2 and libssh2, you only need to set up threading for OpenSSL once.
+
libgit2 does provide a last-resort convenience function
`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
platform-native mutex mechanisms to perform the locking, which you may
rely on if you do not want to use OpenSSL outside of libgit2, or you
know that libgit2 will outlive the rest of the operations. It is not
safe to use OpenSSL multi-threaded after libgit2's shutdown function
-has been called.
+has been called. Note `git_openssl_set_locking()` only works if
+libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency
+of libssh2 as described above, `git_openssl_set_locking()` is a no-op.
If your programming language offers a package/bindings for OpenSSL,
you should very strongly prefer to use that in order to set up
@@ -87,14 +93,14 @@ when using this function.
See the
[OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html)
-on threading for more details.
+on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading
+for a specific example of providing the threading callbacks.
Be also aware that libgit2 does not always link against OpenSSL
if there are alternatives provided by the system.
-libssh2 may be linked against OpenSSL or libgcrypt. If it uses
-OpenSSL, you only need to set up threading for OpenSSL once and the
-above paragraphs are enough. If it uses libgcrypt, then you need to
+libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL,
+see the above paragraphs. If it uses libgcrypt, then you need to
set up its locking before using it multi-threaded. libgit2 has no
direct connection to libgcrypt and thus has not convenience functions for
it (but libgcrypt has macros). Read libgcrypt's
diff --git a/appveyor.yml b/appveyor.yml
index 166fa56b1..3ed3c49a1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -36,4 +36,8 @@ build_script:
- cmd: |
if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
test_script:
-- ps: ctest -V .
+- ps: |
+ ctest -V -R libgit2_clar
+ $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
+ $env:GITTEST_REMOTE_USER="libgit2test"
+ ctest -V -R libgit2_clar-cred_callback
diff --git a/deps/http-parser/http_parser.c b/deps/http-parser/http_parser.c
index 203530254..27bdd2081 100644
--- a/deps/http-parser/http_parser.c
+++ b/deps/http-parser/http_parser.c
@@ -99,7 +99,7 @@ do { \
FOR##_mark = NULL; \
} \
} while (0)
-
+
/* Run the data callback FOR and consume the current byte */
#define CALLBACK_DATA(FOR) \
CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
@@ -444,6 +444,9 @@ parse_url_char(enum state s, const char ch)
return s_req_path;
}
+ /* The schema must start with an alpha character. After that, it may
+ * consist of digits, '+', '-' or '.', followed by a ':'.
+ */
if (IS_ALPHA(ch)) {
return s_req_schema;
}
@@ -451,7 +454,7 @@ parse_url_char(enum state s, const char ch)
break;
case s_req_schema:
- if (IS_ALPHA(ch)) {
+ if (IS_ALPHANUM(ch) || ch == '+' || ch == '-' || ch == '.') {
return s;
}
diff --git a/examples/general.c b/examples/general.c
index 706650b67..32fdaf407 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -28,11 +28,11 @@
// **libgit2** (for the most part) only implements the core plumbing
// functions, not really the higher level porcelain stuff. For a primer on
// Git Internals that you will need to know to work with Git at this level,
-// check out [Chapter 9][pg] of the Pro Git book.
+// check out [Chapter 10][pg] of the Pro Git book.
//
// [lg]: http://libgit2.github.com
// [ap]: http://libgit2.github.com/libgit2
-// [pg]: http://progit.org/book/ch9-0.html
+// [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
// ### Includes
@@ -528,4 +528,3 @@ int main (int argc, char** argv)
return 0;
}
-
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 34d29ed81..44ea8882b 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
@@ -376,6 +394,52 @@ GIT_EXTERN(int) git_commit_amend(
const char *message,
const git_tree *tree);
+/**
+ * Create a commit and write it into a buffer
+ *
+ * Create a commit as with `git_commit_create()` but instead of
+ * writing it to the objectdb, write the contents of the object into a
+ * buffer.
+ *
+ * @param out the buffer into which to write the commit object content
+ *
+ * @param repo Repository where the referenced tree and parents live
+ *
+ * @param author Signature with author and author time of commit
+ *
+ * @param committer Signature with committer and * commit time of commit
+ *
+ * @param message_encoding The encoding for the message in the
+ * commit, represented with a standard encoding name.
+ * E.g. "UTF-8". If NULL, no encoding header is written and
+ * UTF-8 is assumed.
+ *
+ * @param message Full message for this commit
+ *
+ * @param tree An instance of a `git_tree` object that will
+ * be used as the tree for the commit. This tree object must
+ * also be owned by the given `repo`.
+ *
+ * @param parent_count Number of parents for this commit
+ *
+ * @param parents Array of `parent_count` pointers to `git_commit`
+ * objects that will be used as the parents for this commit. This
+ * array may be NULL if `parent_count` is 0 (root commit). All the
+ * given commits must be owned by the `repo`.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_create_buffer(
+ git_buf *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[]);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/common.h b/include/git2/common.h
index ee230dfae..4f43185f8 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -147,6 +147,7 @@ typedef enum {
GIT_OPT_SET_TEMPLATE_PATH,
GIT_OPT_SET_SSL_CERT_LOCATIONS,
GIT_OPT_SET_USER_AGENT,
+ GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
} git_libgit2_opt_t;
/**
@@ -245,6 +246,20 @@ 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.
+ *
+ * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
+ *
+ * > Enable strict input validation when creating new objects
+ * > to ensure that all inputs to the new objects are valid. For
+ * > example, when this is enabled, the parent(s) and tree inputs
+ * > will be validated when creating a new commit. This defaults
+ * > to disabled.
+ *
* @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 3af96d4d7..67ce666ef 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -1197,7 +1197,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/include/git2/version.h b/include/git2/version.h
index 6837e90ef..66a6623cd 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,12 +7,12 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.23.0"
+#define LIBGIT2_VERSION "0.24.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 23
+#define LIBGIT2_VER_MINOR 24
#define LIBGIT2_VER_REVISION 0
#define LIBGIT2_VER_PATCH 0
-#define LIBGIT2_SOVERSION 23
+#define LIBGIT2_SOVERSION 24
#endif
diff --git a/script/cibuild.sh b/script/cibuild.sh
index de5df9ea8..00cde0ada 100755
--- a/script/cibuild.sh
+++ b/script/cibuild.sh
@@ -25,7 +25,7 @@ git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$H
export GITTEST_REMOTE_URL="git://localhost/test.git"
# Run the test suite
-ctest -V . || exit $?
+ctest -V -R libgit2_clar || exit $?
# Now that we've tested the raw git protocol, let's set up ssh to we
# can do the push tests over it
@@ -56,3 +56,7 @@ if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::clone::cred_callback || exit $?
fi
fi
+
+export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
+export GITTEST_REMOTE_USER="libgit2test"
+ctest -V -R libgit2_clar-cred_callback
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..9d675ac97 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -17,6 +17,8 @@
#include "signature.h"
#include "message.h"
#include "refs.h"
+#include "object.h"
+#include "oidarray.h"
void git_commit__free(void *_commit)
{
@@ -36,86 +38,160 @@ void git_commit__free(void *_commit)
git__free(commit);
}
-int git_commit_create_from_callback(
- git_oid *id,
+static int git_commit__create_buffer_internal(
+ git_buf *out,
git_repository *repo,
- const char *update_ref,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_oid *tree,
- git_commit_parent_callback parent_cb,
- void *parent_payload)
+ git_array_oid_t *parents)
{
- git_reference *ref = NULL;
- int error = 0, matched_parent = 0;
- const git_oid *current_id = NULL;
- git_buf commit = GIT_BUF_INIT;
size_t i = 0;
- git_odb *odb;
const git_oid *parent;
- assert(id && repo && tree && parent_cb);
+ assert(out && repo && tree);
- if (update_ref) {
- error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
+ git_oid__writebuf(out, "tree ", tree);
+
+ for (i = 0; i < git_array_size(*parents); i++) {
+ parent = git_array_get(*parents, i);
+ git_oid__writebuf(out, "parent ", parent);
}
- giterr_clear();
- if (ref)
- current_id = git_reference_target(ref);
+ git_signature__writebuf(out, "author ", author);
+ git_signature__writebuf(out, "committer ", committer);
+
+ if (message_encoding != NULL)
+ git_buf_printf(out, "encoding %s\n", message_encoding);
+
+ git_buf_putc(out, '\n');
+
+ if (git_buf_puts(out, message) < 0)
+ goto on_error;
+
+ return 0;
- git_oid__writebuf(&commit, "tree ", tree);
+on_error:
+ git_buf_free(out);
+ return -1;
+}
+static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
+ git_commit_parent_callback parent_cb, void *parent_payload,
+ const git_oid *current_id, bool validate)
+{
+ size_t i;
+ int error;
+ git_oid *parent_cpy;
+ const git_oid *parent;
+
+ if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
+ return -1;
+
+ i = 0;
while ((parent = parent_cb(i, parent_payload)) != NULL) {
- git_oid__writebuf(&commit, "parent ", parent);
- if (i == 0 && current_id && git_oid_equal(current_id, parent))
- matched_parent = 1;
+ if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
+ error = -1;
+ goto on_error;
+ }
+
+ parent_cpy = git_array_alloc(*parents);
+ GITERR_CHECK_ALLOC(parent_cpy);
+
+ git_oid_cpy(parent_cpy, parent);
i++;
}
- if (ref && !matched_parent) {
- git_reference_free(ref);
- git_buf_free(&commit);
+ if (current_id && git_oid_cmp(current_id, git_array_get(*parents, 0))) {
giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
- return GIT_EMODIFIED;
+ error = GIT_EMODIFIED;
+ goto on_error;
}
- git_signature__writebuf(&commit, "author ", author);
- git_signature__writebuf(&commit, "committer ", committer);
+ return 0;
- if (message_encoding != NULL)
- git_buf_printf(&commit, "encoding %s\n", message_encoding);
+on_error:
+ git_array_clear(*parents);
+ return error;
+}
- git_buf_putc(&commit, '\n');
+static int git_commit__create_internal(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_commit_parent_callback parent_cb,
+ void *parent_payload,
+ bool validate)
+{
+ int error;
+ git_odb *odb;
+ git_reference *ref = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ const git_oid *current_id = NULL;
+ git_array_oid_t parents = GIT_ARRAY_INIT;
- if (git_buf_puts(&commit, message) < 0)
- goto on_error;
+ if (update_ref) {
+ error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+ }
+ giterr_clear();
+
+ if (ref)
+ current_id = git_reference_target(ref);
+
+ if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
+ goto cleanup;
+
+ error = git_commit__create_buffer_internal(&buf, repo, author, committer,
+ message_encoding, message, tree,
+ &parents);
+
+ if (error < 0)
+ goto cleanup;
if (git_repository_odb__weakptr(&odb, repo) < 0)
- goto on_error;
+ goto cleanup;
- if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
- goto on_error;
+ if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
+ goto cleanup;
- git_buf_free(&commit);
if (update_ref != NULL) {
error = git_reference__update_for_commit(
repo, ref, update_ref, id, "commit");
- git_reference_free(ref);
- return error;
+ goto cleanup;
}
- return 0;
+cleanup:
+ git_array_clear(parents);
+ git_reference_free(ref);
+ git_buf_free(&buf);
+ return error;
+}
-on_error:
- git_buf_free(&commit);
- giterr_set(GITERR_OBJECT, "Failed to create commit.");
- return -1;
+int git_commit_create_from_callback(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_commit_parent_callback parent_cb,
+ void *parent_payload)
+{
+ return git_commit__create_internal(
+ id, repo, update_ref, author, committer, message_encoding, message,
+ tree, parent_cb, parent_payload, true);
}
typedef struct {
@@ -153,10 +229,10 @@ int git_commit_create_v(
data.total = parent_count;
va_start(data.args, parent_count);
- error = git_commit_create_from_callback(
+ error = git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, git_tree_id(tree),
- commit_parent_from_varargs, &data);
+ commit_parent_from_varargs, &data, false);
va_end(data.args);
return error;
@@ -187,10 +263,10 @@ int git_commit_create_from_ids(
{
commit_parent_oids data = { parent_count, parents };
- return git_commit_create_from_callback(
+ return git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, tree,
- commit_parent_from_ids, &data);
+ commit_parent_from_ids, &data, true);
}
typedef struct {
@@ -227,10 +303,10 @@ int git_commit_create(
assert(tree && git_tree_owner(tree) == repo);
- return git_commit_create_from_callback(
+ return git_commit__create_internal(
id, repo, update_ref, author, committer,
message_encoding, message, git_tree_id(tree),
- commit_parent_from_array, &data);
+ commit_parent_from_array, &data, false);
}
static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
@@ -290,9 +366,9 @@ int git_commit_amend(
}
}
- error = git_commit_create_from_callback(
+ error = git_commit__create_internal(
id, repo, NULL, author, committer, message_encoding, message,
- &tree_id, commit_parent_for_amend, (void *)commit_to_amend);
+ &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
if (!error && update_ref) {
error = git_reference__update_for_commit(
@@ -564,17 +640,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 +748,75 @@ 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;
+}
+
+int git_commit_create_buffer(git_buf *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[])
+{
+ int error;
+ commit_parent_data data = { parent_count, parents, repo };
+ git_array_oid_t parents_arr = GIT_ARRAY_INIT;
+ const git_oid *tree_id;
+
+ assert(tree && git_tree_owner(tree) == repo);
+
+ tree_id = git_tree_id(tree);
+
+ if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
+ return error;
+
+ error = git_commit__create_buffer_internal(
+ out, repo, author, committer,
+ message_encoding, message, tree_id,
+ &parents_arr);
+
+ git_array_clear(parents_arr);
+ 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 4e3c20dad..f6c8b58d6 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(&reg->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(&reg->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(
- &reg->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(&reg->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..0bfde1e04 100644
--- a/src/global.c
+++ b/src/global.c
@@ -8,9 +8,12 @@
#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"
+#include "transports/ssh.h"
+
#if defined(GIT_MSVC_CRTDBG)
#include "win32/w32_stack.h"
#include "win32/w32_crtdbg_stacktrace.h"
@@ -20,14 +23,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 +44,49 @@ 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_transport_ssh_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 +134,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 +154,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 +164,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 +173,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 +238,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 +264,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 +303,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 +320,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..b97f8091d 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;
+
+ /* 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,
- GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) {
- giterr_set(GITERR_INDEX, "Invalid path: '%s'", path);
+ 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);
@@ -1231,17 +1245,22 @@ static void index_existing_and_best(
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
* actual entry in the index (and free the passed in one).
+ *
* trust_path is whether we use the given path, or whether (on case
* insensitive systems only) we try to canonicalize the given path to
* be within an existing directory.
+ *
* trust_mode is whether we trust the mode in entry_ptr.
+ *
+ * trust_id is whether we trust the id or it should be validated.
*/
static int index_insert(
git_index *index,
git_index_entry **entry_ptr,
int replace,
bool trust_path,
- bool trust_mode)
+ bool trust_mode,
+ bool trust_id)
{
int error = 0;
size_t path_length, position;
@@ -1274,6 +1293,15 @@ static int index_insert(
if (!trust_path)
error = canonicalize_directory_path(index, entry, best);
+ /* ensure that the given id exists (unless it's a submodule) */
+ if (!error && !trust_id && INDEX_OWNER(index) &&
+ (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) {
+
+ if (!git_object__is_valid(INDEX_OWNER(index), &entry->id,
+ git_object__type_from_filemode(entry->mode)))
+ error = -1;
+ }
+
/* look for tree / blob name collisions, removing conflicts if requested */
if (!error)
error = check_file_directory_collision(index, entry, position, replace);
@@ -1381,7 +1409,7 @@ int git_index_add_frombuffer(
git_oid_cpy(&entry->id, &id);
entry->file_size = len;
- if ((error = index_insert(index, &entry, 1, true, true)) < 0)
+ if ((error = index_insert(index, &entry, 1, true, true, true)) < 0)
return error;
/* Adding implies conflict was resolved, move conflict entries to REUC */
@@ -1402,7 +1430,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)
@@ -1440,7 +1468,7 @@ int git_index_add_bypath(git_index *index, const char *path)
assert(index && path);
if ((ret = index_entry_init(&entry, index, path)) == 0)
- ret = index_insert(index, &entry, 1, false, false);
+ ret = index_insert(index, &entry, 1, false, false, true);
/* If we were given a directory, let's see if it's a submodule */
if (ret < 0 && ret != GIT_EDIRECTORY)
@@ -1466,7 +1494,7 @@ int git_index_add_bypath(git_index *index, const char *path)
if ((ret = add_repo_as_submodule(&entry, index, path)) < 0)
return ret;
- if ((ret = index_insert(index, &entry, 1, false, false)) < 0)
+ if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0)
return ret;
} else if (ret < 0) {
return ret;
@@ -1515,7 +1543,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;
@@ -1555,7 +1583,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
}
if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 ||
- (ret = index_insert(index, &entry, 1, true, true)) < 0)
+ (ret = index_insert(index, &entry, 1, true, true, false)) < 0)
return ret;
git_tree_cache_invalidate_path(index->tree, entry->path);
@@ -1717,7 +1745,7 @@ int git_index_conflict_add(git_index *index,
/* Make sure stage is correct */
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
- if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0)
+ if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0)
goto on_error;
entries[i] = NULL; /* don't free if later entry fails */
@@ -2121,11 +2149,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 +2207,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 +2231,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 +2826,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 +2945,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/object.c b/src/object.c
index b0a8199bc..ebf77fb47 100644
--- a/src/object.c
+++ b/src/object.c
@@ -14,6 +14,8 @@
#include "blob.h"
#include "tag.h"
+bool git_object__strict_input_validation = true;
+
typedef struct {
const char *str; /* type name string */
size_t size; /* size in bytes of the object structure */
@@ -465,3 +467,27 @@ int git_object_short_id(git_buf *out, const git_object *obj)
return error;
}
+bool git_object__is_valid(
+ git_repository *repo, const git_oid *id, git_otype expected_type)
+{
+ git_odb *odb;
+ git_otype actual_type;
+ size_t len;
+ int error;
+
+ if (!git_object__strict_input_validation)
+ return true;
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
+ return false;
+
+ if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) {
+ giterr_set(GITERR_INVALID,
+ "the requested type does not match the type in the ODB");
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/src/object.h b/src/object.h
index d187c55b7..dd227d16d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -7,6 +7,10 @@
#ifndef INCLUDE_object_h__
#define INCLUDE_object_h__
+#include "repository.h"
+
+extern bool git_object__strict_input_validation;
+
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
@@ -28,4 +32,23 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+bool git_object__is_valid(
+ git_repository *repo, const git_oid *id, git_otype expected_type);
+
+GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode)
+{
+ switch (mode) {
+ case GIT_FILEMODE_TREE:
+ return GIT_OBJ_TREE;
+ case GIT_FILEMODE_COMMIT:
+ return GIT_OBJ_COMMIT;
+ case GIT_FILEMODE_BLOB:
+ case GIT_FILEMODE_BLOB_EXECUTABLE:
+ case GIT_FILEMODE_LINK:
+ return GIT_OBJ_BLOB;
+ default:
+ return GIT_OBJ_BAD;
+ }
+}
+
#endif
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 f6cb3a548..52c652178 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -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;
}
@@ -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/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/posix.c b/src/posix.c
index 8d86aa8bf..c7201ba14 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -62,8 +62,11 @@ int p_getaddrinfo(
ai = ainfo;
for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) {
- ai->ai_next = malloc(sizeof(struct addrinfo));
- memcpy(&ai->ai_next, ainfo, sizeof(struct addrinfo));
+ if (!(ai->ai_next = malloc(sizeof(struct addrinfo)))) {
+ p_freeaddrinfo(ainfo);
+ return -1;
+ }
+ memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo));
memcpy(&ai->ai_next->ai_addr_in.sin_addr,
ainfo->ai_hostent->h_addr_list[p],
ainfo->ai_hostent->h_length);
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(&current_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(&current_commit, rebase->repo, &operation->id)) < 0 ||
+ (error = git_commit_tree(&current_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(&current_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(&current_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/refs.c b/src/refs.c
index 7b538659d..a15e31b53 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -377,15 +377,9 @@ static int reference__create(
return error;
if (oid != NULL) {
- git_odb *odb;
-
assert(symbolic == NULL);
- /* Sanity check the reference being created - target must exist. */
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
- return error;
-
- if (!git_odb_exists(odb, oid)) {
+ if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) {
giterr_set(GITERR_REFERENCE,
"Target OID for the reference doesn't exist on the repository");
return -1;
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..88602bad0 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -14,6 +14,7 @@
#include "sysdir.h"
#include "cache.h"
#include "global.h"
+#include "object.h"
void git_libgit2_version(int *major, int *minor, int *rev)
{
@@ -181,6 +182,14 @@ int git_libgit2_opts(int key, ...)
}
break;
+
+ case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
+ git_object__strict_input_validation = (va_arg(ap, int) != 0);
+ 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/transport.c b/src/transport.c
index 5c65c7c06..327052fa3 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -35,6 +35,8 @@ static transport_definition transports[] = {
{ "file://", git_transport_local, NULL },
#ifdef GIT_SSH
{ "ssh://", git_transport_smart, &ssh_subtransport_definition },
+ { "ssh+git://", git_transport_smart, &ssh_subtransport_definition },
+ { "git+ssh://", git_transport_smart, &ssh_subtransport_definition },
#endif
{ NULL, 0, 0 }
};
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/ssh.c b/src/transports/ssh.c
index 239e0bae7..cfd573665 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -15,12 +15,14 @@
#include "smart.h"
#include "cred.h"
#include "socket_stream.h"
+#include "ssh.h"
#ifdef GIT_SSH
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
-static const char prefix_ssh[] = "ssh://";
+static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" };
+
static const char cmd_uploadpack[] = "git-upload-pack";
static const char cmd_receivepack[] = "git-receive-pack";
@@ -62,17 +64,24 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
{
char *repo;
int len;
+ size_t i;
- if (!git__prefixcmp(url, prefix_ssh)) {
- url = url + strlen(prefix_ssh);
- repo = strchr(url, '/');
- if (repo && repo[1] == '~')
- ++repo;
- } else {
- repo = strchr(url, ':');
- if (repo) repo++;
+ for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
+ const char *p = ssh_prefixes[i];
+
+ if (!git__prefixcmp(url, p)) {
+ url = url + strlen(p);
+ repo = strchr(url, '/');
+ if (repo && repo[1] == '~')
+ ++repo;
+
+ goto done;
+ }
}
+ repo = strchr(url, ':');
+ if (repo) repo++;
+done:
if (!repo) {
giterr_set(GITERR_NET, "Malformed git protocol URL");
return -1;
@@ -499,6 +508,7 @@ static int _git_ssh_setup_conn(
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *default_port="22";
int auth_methods, error = 0;
+ size_t i;
ssh_stream *s;
git_cred *cred = NULL;
LIBSSH2_SESSION* session=NULL;
@@ -514,16 +524,22 @@ static int _git_ssh_setup_conn(
s->session = NULL;
s->channel = NULL;
- if (!git__prefixcmp(url, prefix_ssh)) {
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
- goto done;
- } else {
- if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
- goto done;
- port = git__strdup(default_port);
- GITERR_CHECK_ALLOC(port);
+ for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
+ const char *p = ssh_prefixes[i];
+
+ if (!git__prefixcmp(url, p)) {
+ if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
+ goto done;
+
+ goto post_extract;
+ }
}
+ if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
+ goto done;
+ port = git__strdup(default_port);
+ GITERR_CHECK_ALLOC(port);
+post_extract:
if ((error = git_socket_stream_new(&s->io, host, port)) < 0 ||
(error = git_stream_connect(s->io)) < 0)
goto done;
@@ -876,3 +892,18 @@ int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *p
return -1;
#endif
}
+
+int git_transport_ssh_global_init(void)
+{
+#ifdef GIT_SSH
+
+ libssh2_init(0);
+ return 0;
+
+#else
+
+ /* Nothing to initialize */
+ return 0;
+
+#endif
+}
diff --git a/src/transports/ssh.h b/src/transports/ssh.h
new file mode 100644
index 000000000..2db2cc5df
--- /dev/null
+++ b/src/transports/ssh.h
@@ -0,0 +1,12 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_ssh_h__
+#define INCLUDE_ssh_h__
+
+int git_transport_ssh_global_init(void);
+
+#endif
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 9e97a279d..32b838084 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);
@@ -921,10 +926,11 @@ replay:
if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
return -1;
- if (allowed_types &&
- (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+ if (allowed_types) {
int cred_error = 1;
+ git_cred_free(t->cred);
+ t->cred = NULL;
/* Start with the user-supplied credential callback, if present */
if (t->owner->cred_acquire_cb) {
cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url,
diff --git a/src/tree.c b/src/tree.c
index aab4b58ad..48b9f121d 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));
@@ -720,6 +726,18 @@ on_error:
return -1;
}
+static git_otype otype_from_mode(git_filemode_t filemode)
+{
+ switch (filemode) {
+ case GIT_FILEMODE_TREE:
+ return GIT_OBJ_TREE;
+ case GIT_FILEMODE_COMMIT:
+ return GIT_OBJ_COMMIT;
+ default:
+ return GIT_OBJ_BLOB;
+ }
+}
+
int git_treebuilder_insert(
const git_tree_entry **entry_out,
git_treebuilder *bld,
@@ -739,6 +757,10 @@ int git_treebuilder_insert(
if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
+ if (filemode != GIT_FILEMODE_COMMIT &&
+ !git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
+ return tree_error("Failed to insert entry; invalid object specified", filename);
+
pos = git_strmap_lookup_index(bld->map, filename);
if (git_strmap_valid_index(bld->map, pos)) {
entry = git_strmap_value_at(bld->map, pos);
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..6448b5542 100644
--- a/src/xdiff/xmerge.c
+++ b/src/xdiff/xmerge.c
@@ -633,8 +633,11 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
result->ptr = NULL;
result->size = 0;
- if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||
- xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) {
+ return -1;
+ }
+ if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+ xdl_free_env(&xe1);
return -1;
}
if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
@@ -646,6 +649,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/commit/write.c b/tests/commit/write.c
index 176965cbd..9d1ae78fb 100644
--- a/tests/commit/write.c
+++ b/tests/commit/write.c
@@ -1,10 +1,12 @@
#include "clar_libgit2.h"
+#include "git2/sys/commit.h"
static const char *committer_name = "Vicent Marti";
static const char *committer_email = "vicent@github.com";
static const char *commit_message = "This commit has been created in memory\n\
This is a commit created in memory and it will be written back to disk\n";
-static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static const char *tree_id_str = "1810dff58d8a660512d4832e740f692884338ccd";
+static const char *parent_id_str = "8496071c1b46c854b31185ea97743be6a8774479";
static const char *root_commit_message = "This is a root commit\n\
This is a root commit and should be the only one in this branch\n";
static const char *root_reflog_message = "commit (initial): This is a root commit \
@@ -35,6 +37,8 @@ void test_commit_write__cleanup(void)
head_old = NULL;
cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
}
@@ -46,12 +50,11 @@ void test_commit_write__from_memory(void)
const git_signature *author1, *committer1;
git_commit *parent;
git_tree *tree;
- const char *commit_id_str = "8496071c1b46c854b31185ea97743be6a8774479";
- git_oid_fromstr(&tree_id, tree_oid);
+ git_oid_fromstr(&tree_id, tree_id_str);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
- git_oid_fromstr(&parent_id, commit_id_str);
+ git_oid_fromstr(&parent_id, parent_id_str);
cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
/* create signatures */
@@ -95,6 +98,45 @@ void test_commit_write__from_memory(void)
cl_assert_equal_s(commit_message, git_commit_message(commit));
}
+void test_commit_write__into_buf(void)
+{
+ git_oid tree_id;
+ git_signature *author, *committer;
+ git_tree *tree;
+ git_commit *parent;
+ git_oid parent_id;
+ git_buf commit = GIT_BUF_INIT;
+
+ git_oid_fromstr(&tree_id, tree_id_str);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ git_oid_fromstr(&parent_id, parent_id_str);
+ cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
+
+ cl_git_pass(git_commit_create_buffer(&commit, g_repo, author, committer,
+ NULL, root_commit_message, tree, 1, (const git_commit **) &parent));
+
+ cl_assert_equal_s(commit.ptr,
+ "tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Vicent Marti <vicent@github.com> 987654321 +0130\n\
+committer Vicent Marti <vicent@github.com> 123456789 +0100\n\
+\n\
+This is a root commit\n\
+ This is a root commit and should be the only one in this branch\n\
+");
+
+ git_buf_free(&commit);
+ git_tree_free(tree);
+ git_commit_free(parent);
+ git_signature_free(author);
+ git_signature_free(committer);
+}
+
// create a root commit
void test_commit_write__root(void)
{
@@ -106,7 +148,7 @@ void test_commit_write__root(void)
git_reflog *log;
const git_reflog_entry *entry;
- git_oid_fromstr(&tree_id, tree_oid);
+ git_oid_fromstr(&tree_id, tree_id_str);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
/* create signatures */
@@ -158,3 +200,101 @@ void test_commit_write__root(void)
git_signature_free(committer);
git_reflog_free(log);
}
+
+static int create_commit_from_ids(
+ git_oid *result,
+ const git_oid *tree_id,
+ const git_oid *parent_id)
+{
+ git_signature *author, *committer;
+ const git_oid *parent_ids[1];
+ int ret;
+
+ cl_git_pass(git_signature_new(
+ &committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(
+ &author, committer_name, committer_email, 987654321, 90));
+
+ parent_ids[0] = parent_id;
+
+ ret = git_commit_create_from_ids(
+ result,
+ g_repo,
+ NULL,
+ author,
+ committer,
+ NULL,
+ root_commit_message,
+ tree_id,
+ 1,
+ parent_ids);
+
+ git_signature_free(committer);
+ git_signature_free(author);
+
+ return ret;
+}
+
+void test_commit_write__can_write_invalid_objects(void)
+{
+ git_oid expected_id, tree_id, parent_id, commit_id;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
+ /* this is a valid tree and parent */
+ git_oid_fromstr(&tree_id, tree_id_str);
+ git_oid_fromstr(&parent_id, parent_id_str);
+
+ git_oid_fromstr(&expected_id, "c8571bbec3a72c4bcad31648902e5a453f1adece");
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* this is a wholly invented tree id */
+ git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890");
+ git_oid_fromstr(&parent_id, parent_id_str);
+
+ git_oid_fromstr(&expected_id, "996008340b8e68d69bf3c28d7c57fb7ec3c8e202");
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* this is a wholly invented parent id */
+ git_oid_fromstr(&tree_id, tree_id_str);
+ git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890");
+
+ git_oid_fromstr(&expected_id, "d78f660cab89d9791ca6714b57978bf2a7e709fd");
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* these are legitimate objects, but of the wrong type */
+ git_oid_fromstr(&tree_id, parent_id_str);
+ git_oid_fromstr(&parent_id, tree_id_str);
+
+ git_oid_fromstr(&expected_id, "5d80c07414e3f18792949699dfcacadf7748f361");
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+}
+
+void test_commit_write__can_validate_objects(void)
+{
+ git_oid tree_id, parent_id, commit_id;
+
+ /* this is a valid tree and parent */
+ git_oid_fromstr(&tree_id, tree_id_str);
+ git_oid_fromstr(&parent_id, parent_id_str);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* this is a wholly invented tree id */
+ git_oid_fromstr(&tree_id, "1234567890123456789012345678901234567890");
+ git_oid_fromstr(&parent_id, parent_id_str);
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* this is a wholly invented parent id */
+ git_oid_fromstr(&tree_id, tree_id_str);
+ git_oid_fromstr(&parent_id, "1234567890123456789012345678901234567890");
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* these are legitimate objects, but of the wrong type */
+ git_oid_fromstr(&tree_id, parent_id_str);
+ git_oid_fromstr(&parent_id, tree_id_str);
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+}
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/format_email.c b/tests/diff/format_email.c
index 8a0128898..e55afe958 100644
--- a/tests/diff/format_email.c
+++ b/tests/diff/format_email.c
@@ -124,7 +124,7 @@ void test_diff_format_email__with_message(void)
" file3\n" \
"+file3\n" \
"--\n" \
- "libgit2 0.23.0\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
"\n";
opts.body = "Modify content of file3.txt by appending a new line. Make this\n" \
diff --git a/tests/diff/index.c b/tests/diff/index.c
index df45ad236..0293b7821 100644
--- a/tests/diff/index.c
+++ b/tests/diff/index.c
@@ -185,9 +185,9 @@ static void do_conflicted_diff(diff_expects *exp, unsigned long flags)
ancestor.path = ours.path = theirs.path = "staged_changes";
ancestor.mode = ours.mode = theirs.mode = GIT_FILEMODE_BLOB;
- git_oid_fromstr(&ancestor.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- git_oid_fromstr(&ours.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+ git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
+ git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
+ git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts));
@@ -255,7 +255,7 @@ void test_diff_index__not_in_head_conflicted(void)
theirs.path = "file_not_in_head";
theirs.mode = GIT_FILEMODE_BLOB;
- git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+ git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_conflict_add(index, NULL, NULL, &theirs));
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, NULL));
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..e1bbce8fb 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -85,9 +85,11 @@ void test_diff_workdir__to_index_with_conflicts(void)
/* Adding an entry that represents a rename gets two files in conflict */
our_entry.path = "subdir/modified_file";
our_entry.mode = 0100644;
+ git_oid_fromstr(&our_entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
their_entry.path = "subdir/rename_conflict";
their_entry.mode = 0100644;
+ git_oid_fromstr(&their_entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry));
@@ -1755,7 +1757,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))
@@ -1975,9 +1977,9 @@ void test_diff_workdir__to_index_conflicted(void) {
ancestor.path = ours.path = theirs.path = "_file";
ancestor.mode = ours.mode = theirs.mode = 0100644;
- git_oid_fromstr(&ancestor.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- git_oid_fromstr(&ours.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- git_oid_fromstr(&theirs.id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+ git_oid_fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
+ git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
+ git_oid_fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
cl_git_pass(git_diff_tree_to_index(&diff1, g_repo, a, index, NULL));
@@ -2006,7 +2008,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/add.c b/tests/index/add.c
new file mode 100644
index 000000000..f101ea266
--- /dev/null
+++ b/tests/index/add.c
@@ -0,0 +1,84 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo = NULL;
+static git_index *g_index = NULL;
+
+static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
+static const char *valid_tree_id = "181037049a54a1eb5fab404658a3a250b44335d7";
+static const char *valid_commit_id = "763d71aadf09a7951596c9746c024e7eece7c7af";
+static const char *invalid_id = "1234567890123456789012345678901234567890";
+
+void test_index_add__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_index(&g_index, g_repo));
+}
+
+void test_index_add__cleanup(void)
+{
+ git_index_free(g_index);
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+}
+
+static void test_add_entry(
+ bool should_succeed, const char *idstr, git_filemode_t mode)
+{
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_oid_fromstr(&entry.id, idstr));
+
+ entry.path = mode == GIT_FILEMODE_TREE ? "test_folder" : "test_file";
+ entry.mode = mode;
+
+ if (should_succeed)
+ cl_git_pass(git_index_add(g_index, &entry));
+ else
+ cl_git_fail(git_index_add(g_index, &entry));
+}
+
+void test_index_add__invalid_entries_succeeds_by_default(void)
+{
+ /*
+ * Ensure that there is validation on object ids by default
+ */
+
+ /* ensure that we can add some actually good entries */
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK);
+
+ /* test that we fail to add some invalid (missing) blobs and trees */
+ test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB);
+ test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(false, invalid_id, GIT_FILEMODE_LINK);
+
+ /* test that we validate the types of objects */
+ test_add_entry(false, valid_commit_id, GIT_FILEMODE_BLOB);
+ test_add_entry(false, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(false, valid_commit_id, GIT_FILEMODE_LINK);
+
+ /*
+ * Ensure that there we can disable validation
+ */
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
+ /* ensure that we can add some actually good entries */
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK);
+
+ /* test that we can now add some invalid (missing) blobs and trees */
+ test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, invalid_id, GIT_FILEMODE_LINK);
+
+ /* test that we do not validate the types of objects */
+ test_add_entry(true, valid_commit_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK);
+}
+
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
index 88a76178a..34a7412a8 100644
--- a/tests/index/bypath.c
+++ b/tests/index/bypath.c
@@ -134,6 +134,7 @@ void test_index_bypath__add_honors_existing_case_2(void)
clar__skip();
dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b"));
/* note that `git_index_add` does no checking to canonical directories */
dummy.path = "Just_a_dir/file0.txt";
@@ -189,6 +190,7 @@ void test_index_bypath__add_honors_existing_case_3(void)
clar__skip();
dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b"));
dummy.path = "just_a_dir/filea.txt";
cl_git_pass(git_index_add(g_idx, &dummy));
@@ -219,6 +221,7 @@ void test_index_bypath__add_honors_existing_case_4(void)
clar__skip();
dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid_fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b"));
dummy.path = "just_a_dir/a/b/c/d/e/file1.txt";
cl_git_pass(git_index_add(g_idx, &dummy));
diff --git a/tests/index/cache.c b/tests/index/cache.c
index 3982bf183..56885aff7 100644
--- a/tests/index/cache.c
+++ b/tests/index/cache.c
@@ -111,7 +111,7 @@ void test_index_cache__read_tree_no_children(void)
memset(&entry, 0x0, sizeof(git_index_entry));
entry.path = "new.txt";
entry.mode = GIT_FILEMODE_BLOB;
- git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+ git_oid_fromstr(&entry.id, "d4bcc68acd4410bf836a39f20afb2c2ece09584e");
cl_git_pass(git_index_add(index, &entry));
cl_assert_equal_i(-1, index->tree->entry_count);
@@ -191,7 +191,7 @@ void test_index_cache__read_tree_children(void)
memset(&entry, 0x0, sizeof(git_index_entry));
entry.path = "top-level";
entry.mode = GIT_FILEMODE_BLOB;
- git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+ git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
cl_git_pass(git_index_add(index, &entry));
@@ -217,7 +217,7 @@ void test_index_cache__read_tree_children(void)
/* override with a slightly different id, also dummy */
entry.path = "subdir/some-file";
- git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb058");
+ git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
cl_git_pass(git_index_add(index, &entry));
cl_assert_equal_i(-1, index->tree->entry_count);
diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c
index 8e94cd441..d4004686f 100644
--- a/tests/index/conflicts.c
+++ b/tests/index/conflicts.c
@@ -16,11 +16,6 @@ static git_index *repo_index;
#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
-#define TEST_STAGED_OID "beefdadafeedabedcafedeedbabedeadbeaddeaf"
-#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f"
-#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b"
-#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567"
-
// Fixture setup and teardown
void test_index_conflicts__initialize(void)
{
@@ -49,17 +44,17 @@ void test_index_conflicts__add(void)
ancestor_entry.path = "test-one.txt";
ancestor_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1);
- git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID);
our_entry.path = "test-one.txt";
our_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&our_entry, 2);
- git_oid_fromstr(&our_entry.id, TEST_OUR_OID);
+ git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID);
their_entry.path = "test-one.txt";
their_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 2);
- git_oid_fromstr(&their_entry.id, TEST_THEIR_OID);
+ git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID);
cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
@@ -80,17 +75,17 @@ void test_index_conflicts__add_fixes_incorrect_stage(void)
ancestor_entry.path = "test-one.txt";
ancestor_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3);
- git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID);
our_entry.path = "test-one.txt";
our_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&our_entry, 1);
- git_oid_fromstr(&our_entry.id, TEST_OUR_OID);
+ git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID);
their_entry.path = "test-one.txt";
their_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&their_entry, 2);
- git_oid_fromstr(&their_entry.id, TEST_THEIR_OID);
+ git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID);
cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
@@ -105,36 +100,33 @@ void test_index_conflicts__add_fixes_incorrect_stage(void)
void test_index_conflicts__add_removes_stage_zero(void)
{
- git_index_entry staged, ancestor_entry, our_entry, their_entry;
+ git_index_entry ancestor_entry, our_entry, their_entry;
const git_index_entry *conflict_entry[3];
cl_assert(git_index_entrycount(repo_index) == 8);
- memset(&staged, 0x0, sizeof(git_index_entry));
memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
memset(&our_entry, 0x0, sizeof(git_index_entry));
memset(&their_entry, 0x0, sizeof(git_index_entry));
- staged.path = "test-one.txt";
- staged.mode = 0100644;
- git_oid_fromstr(&staged.id, TEST_STAGED_OID);
- cl_git_pass(git_index_add(repo_index, &staged));
+ cl_git_mkfile("./mergedrepo/test-one.txt", "new-file\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "test-one.txt"));
cl_assert(git_index_entrycount(repo_index) == 9);
ancestor_entry.path = "test-one.txt";
ancestor_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 3);
- git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID);
our_entry.path = "test-one.txt";
our_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&our_entry, 1);
- git_oid_fromstr(&our_entry.id, TEST_OUR_OID);
+ git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID);
their_entry.path = "test-one.txt";
their_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&their_entry, 2);
- git_oid_fromstr(&their_entry.id, TEST_THEIR_OID);
+ git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID);
cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
@@ -330,7 +322,7 @@ void test_index_conflicts__partial(void)
ancestor_entry.path = "test-one.txt";
ancestor_entry.mode = 0100644;
GIT_IDXENTRY_STAGE_SET(&ancestor_entry, 1);
- git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID);
cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
cl_assert(git_index_entrycount(repo_index) == 9);
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
index 6442d7755..2efad5b33 100644
--- a/tests/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -236,12 +236,19 @@ void test_index_filemodes__invalid(void)
{
git_index *index;
git_index_entry entry;
+ const git_index_entry *dummy;
cl_git_pass(git_repository_index(&index, g_repo));
+ /* add a dummy file so that we have a valid id */
+ cl_git_mkfile("./filemodes/dummy-file.txt", "new-file\n");
+ cl_git_pass(git_index_add_bypath(index, "dummy-file.txt"));
+ cl_assert((dummy = git_index_get_bypath(index, "dummy-file.txt", 0)));
+
GIT_IDXENTRY_STAGE_SET(&entry, 0);
entry.path = "foo";
entry.mode = GIT_OBJ_BLOB;
+ git_oid_cpy(&entry.id, &dummy->id);
cl_git_fail(git_index_add(index, &entry));
entry.mode = GIT_FILEMODE_BLOB;
diff --git a/tests/index/nsec.c b/tests/index/nsec.c
index 5004339f0..326b19540 100644
--- a/tests/index/nsec.c
+++ b/tests/index/nsec.c
@@ -24,6 +24,43 @@ void test_index_nsec__cleanup(void)
cl_git_sandbox_cleanup();
}
+static bool try_create_file_with_nsec_timestamp(const char *path)
+{
+ struct stat st;
+ int try;
+
+ /* retry a few times to avoid nanos *actually* equal 0 race condition */
+ for (try = 0; try < 3; try++) {
+ cl_git_mkfile(path, "This is hopefully a file with nanoseconds!");
+
+ cl_must_pass(p_stat(path, &st));
+
+ if (st.st_ctime_nsec && st.st_mtime_nsec)
+ return true;
+ }
+
+ return false;
+}
+
+/* try to determine if the underlying filesystem supports a resolution
+ * higher than a single second. (i'm looking at you, hfs+)
+ */
+static bool should_expect_nsecs(void)
+{
+ git_buf nsec_path = GIT_BUF_INIT;
+ bool expect;
+
+ git_buf_joinpath(&nsec_path, clar_sandbox_path(), "nsec_test");
+
+ expect = try_create_file_with_nsec_timestamp(nsec_path.ptr);
+
+ p_unlink(nsec_path.ptr);
+
+ git_buf_clear(&nsec_path);
+
+ return expect;
+}
+
static bool has_nsecs(void)
{
const git_index_entry *entry;
@@ -50,8 +87,13 @@ void test_index_nsec__has_nanos(void)
void test_index_nsec__staging_maintains_other_nanos(void)
{
const git_index_entry *entry;
+ bool expect_nsec, test_file_has_nsec;
+
+ expect_nsec = should_expect_nsecs();
+ test_file_has_nsec = try_create_file_with_nsec_timestamp("nsecs/a.txt");
+
+ cl_assert_equal_b(expect_nsec, test_file_has_nsec);
- cl_git_rewritefile("nsecs/a.txt", "This is file A");
cl_git_pass(git_index_add_bypath(repo_index, "a.txt"));
cl_git_pass(git_index_write(repo_index));
@@ -61,8 +103,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)));
- cl_assert_equal_i(0, entry->ctime.nanoseconds);
- cl_assert_equal_i(0, entry->mtime.nanoseconds);
+
+ /* if we are writing nanoseconds to the index, expect them to be
+ * nonzero.
+ */
+ if (expect_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);
+ }
}
void test_index_nsec__status_doesnt_clear_nsecs(void)
diff --git a/tests/index/racy.c b/tests/index/racy.c
index e2275ea14..1768f5efd 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));
@@ -105,8 +105,8 @@ static void setup_race(void)
{
git_buf path = GIT_BUF_INIT;
git_index *index;
- const git_index_entry *entry;
- int i, found_race = 0;
+ git_index_entry *entry;
+ struct stat st;
/* Make sure we do have a timestamp */
cl_git_pass(git_repository_index__weakptr(&index, g_repo));
@@ -114,27 +114,20 @@ static void setup_race(void)
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A"));
- /* Make sure writing the file, adding and rewriting happen in the same second */
- for (i = 0; i < 10; i++) {
- struct stat st;
- cl_git_mkfile(path.ptr, "A");
+ cl_git_mkfile(path.ptr, "A");
+ cl_git_pass(git_index_add_bypath(index, "A"));
- cl_git_pass(git_index_add_bypath(index, "A"));
- cl_git_mkfile(path.ptr, "B");
- cl_git_pass(git_index_write(index));
+ cl_git_mkfile(path.ptr, "B");
+ cl_git_pass(git_index_write(index));
- cl_git_mkfile(path.ptr, "");
+ cl_git_mkfile(path.ptr, "");
- cl_git_pass(p_stat(path.ptr, &st));
- cl_assert(entry = git_index_get_bypath(index, "A", 0));
- if (entry->mtime.seconds == (int32_t) st.st_mtime) {
- found_race = 1;
- break;
- }
- }
+ cl_git_pass(p_stat(path.ptr, &st));
+ cl_assert(entry = (git_index_entry *)git_index_get_bypath(index, "A", 0));
- if (!found_race)
- cl_fail("failed to find race after 10 attempts");
+ /* force a race */
+ entry->mtime.seconds = st.st_mtime;
+ entry->mtime.nanoseconds = st.st_mtime_nsec;
git_buf_free(&path);
}
@@ -178,6 +171,7 @@ static void setup_uptodate_files(void)
{
git_buf path = GIT_BUF_INIT;
git_index *index;
+ const git_index_entry *a_entry;
git_index_entry new_entry = {{0}};
cl_git_pass(git_repository_index(&index, g_repo));
@@ -188,9 +182,12 @@ static void setup_uptodate_files(void)
/* Put 'A' into the index */
cl_git_pass(git_index_add_bypath(index, "A"));
+ cl_assert((a_entry = git_index_get_bypath(index, "A", 0)));
+
/* Put 'B' into the index */
new_entry.path = "B";
new_entry.mode = GIT_FILEMODE_BLOB;
+ git_oid_cpy(&new_entry.id, &a_entry->id);
cl_git_pass(git_index_add(index, &new_entry));
/* Put 'C' into the index */
diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c
index f168963b2..a69919f53 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,11 +162,11 @@ 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;
+ entry->ctime.nanoseconds = statbuf.st_ctime_nsec;
+ entry->mtime.nanoseconds = statbuf.st_mtime_nsec;
#else
entry->ctime.nanoseconds = 0;
entry->mtime.nanoseconds = 0;
@@ -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/object/tree/write.c b/tests/object/tree/write.c
index 5433e5f03..a9decf9c1 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -18,6 +18,8 @@ void test_object_tree_write__initialize(void)
void test_object_tree_write__cleanup(void)
{
cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
}
void test_object_tree_write__from_memory(void)
@@ -131,15 +133,18 @@ void test_object_tree_write__sorted_subtrees(void)
{ GIT_FILEMODE_TREE, "vendors"}
};
- git_oid blank_oid, tree_oid;
+ git_oid bid, tid, tree_oid;
- memset(&blank_oid, 0x0, sizeof(blank_oid));
+ cl_git_pass(git_oid_fromstr(&bid, blob_oid));
+ cl_git_pass(git_oid_fromstr(&tid, first_tree));
cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid;
+
cl_git_pass(git_treebuilder_insert(NULL,
- builder, entries[i].filename, &blank_oid, entries[i].attr));
+ builder, entries[i].filename, id, entries[i].attr));
}
cl_git_pass(git_treebuilder_write(&tree_oid, builder));
@@ -187,10 +192,10 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
{
git_treebuilder *builder;
int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i;
- git_oid blank_oid, tree_oid;
+ git_oid entry_oid, tree_oid;
git_tree *tree;
- memset(&blank_oid, 0x0, sizeof(blank_oid));
+ cl_git_pass(git_oid_fromstr(&entry_oid, blob_oid));
cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
@@ -198,7 +203,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
for (i = 0; _entries[i].filename; ++i)
cl_git_pass(git_treebuilder_insert(NULL,
- builder, _entries[i].filename, &blank_oid, _entries[i].attr));
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
@@ -209,12 +214,12 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_insert(
- NULL, builder, "before_last", &blank_oid, GIT_FILEMODE_BLOB));
+ NULL, builder, "before_last", &entry_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
/* reinsert apple_after */
cl_git_pass(git_treebuilder_insert(
- NULL, builder, "apple_after", &blank_oid, GIT_FILEMODE_BLOB));
+ NULL, builder, "apple_after", &entry_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_remove(builder, "last"));
@@ -222,11 +227,11 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
/* reinsert last */
cl_git_pass(git_treebuilder_insert(
- NULL, builder, "last", &blank_oid, GIT_FILEMODE_BLOB));
+ NULL, builder, "last", &entry_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_insert(
- NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB));
+ NULL, builder, "apple_extra", &entry_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_write(&tree_oid, builder));
@@ -278,16 +283,16 @@ void test_object_tree_write__filtering(void)
{
git_treebuilder *builder;
int i;
- git_oid blank_oid, tree_oid;
+ git_oid entry_oid, tree_oid;
git_tree *tree;
- memset(&blank_oid, 0x0, sizeof(blank_oid));
+ git_oid_fromstr(&entry_oid, blob_oid);
cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
for (i = 0; _entries[i].filename; ++i)
cl_git_pass(git_treebuilder_insert(NULL,
- builder, _entries[i].filename, &blank_oid, _entries[i].attr));
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
@@ -406,6 +411,8 @@ void test_object_tree_write__protect_filesystems(void)
git_treebuilder *builder;
git_oid bid;
+ cl_git_pass(git_oid_fromstr(&bid, "fa49b077972391ad58037050f2a75f74e3671e92"));
+
/* Ensure that (by default) we can write objects with funny names on
* platforms that are not affected.
*/
@@ -440,3 +447,68 @@ void test_object_tree_write__protect_filesystems(void)
git_treebuilder_free(builder);
}
+
+static void test_invalid_objects(bool should_allow_invalid)
+{
+ git_treebuilder *builder;
+ git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id;
+
+#define assert_allowed(expr) \
+ clar__assert(!(expr) == should_allow_invalid, __FILE__, __LINE__, \
+ (should_allow_invalid ? \
+ "Expected function call to succeed: " #expr : \
+ "Expected function call to fail: " #expr), \
+ NULL, 1)
+
+ cl_git_pass(git_oid_fromstr(&valid_blob_id, blob_oid));
+ cl_git_pass(git_oid_fromstr(&invalid_blob_id,
+ "1234567890123456789012345678901234567890"));
+ cl_git_pass(git_oid_fromstr(&valid_tree_id, first_tree));
+ cl_git_pass(git_oid_fromstr(&invalid_tree_id,
+ "0000000000111111111122222222223333333333"));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ /* test valid blobs and trees (these should always pass) */
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE));
+
+ /* replace valid files and folders with invalid ones */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert new invalid files and folders */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert valid blobs as trees and trees as blobs */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB));
+
+#undef assert_allowed
+
+ git_treebuilder_free(builder);
+}
+
+static void test_inserting_submodule(void)
+{
+ git_treebuilder *bld;
+ git_oid sm_id;
+
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT));
+ git_treebuilder_free(bld);
+}
+
+void test_object_tree_write__object_validity(void)
+{
+ /* Ensure that we cannot add invalid objects by default */
+ test_invalid_objects(false);
+ test_inserting_submodule();
+
+ /* Ensure that we can turn off validation */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+ test_invalid_objects(true);
+ test_inserting_submodule();
+}
+
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/refs/create.c b/tests/refs/create.c
index 48194ae3b..b96d0c90a 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -18,6 +18,8 @@ void test_refs_create__initialize(void)
void test_refs_create__cleanup(void)
{
cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
}
void test_refs_create__symbolic(void)
@@ -119,9 +121,9 @@ void test_refs_create__oid(void)
git_reference_free(looked_up_ref);
}
-void test_refs_create__oid_unknown(void)
+/* Can by default create a reference that targets at an unknown id */
+void test_refs_create__oid_unknown_succeeds_by_default(void)
{
- // Can not create a new OID reference which targets at an unknown id
git_reference *new_reference, *looked_up_ref;
git_oid id;
@@ -130,6 +132,27 @@ void test_refs_create__oid_unknown(void)
git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
/* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
+ git_reference_free(new_reference);
+
+ /* Ensure the reference can't be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+ git_reference_free(looked_up_ref);
+}
+
+/* Strict object enforcement enforces valid object id */
+void test_refs_create__oid_unknown_fails_strict_mode(void)
+{
+ git_reference *new_reference, *looked_up_ref;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+
+ /* Create and write the new object id reference */
cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
/* Ensure the reference can't be looked-up... */
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/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
new file mode 100644
index 000000000..d10ca636b
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
new file mode 100644
index 000000000..0b3611ae4
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 000000000..974b72dfd
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 000000000..974b72dfd
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
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
new file mode 100644
index 000000000..1202dd9f4
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/index
Binary files differ
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
new file mode 100644
index 000000000..2d3b31adc
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
new file mode 100644
index 000000000..ef8316670
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40
new file mode 100644
index 000000000..baddb1fc6
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40
Binary files differ
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:B VRO ͿҖ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
new file mode 100644
index 000000000..923462306
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37
Binary files differ
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/transport/register.c b/tests/transport/register.c
index ea917d5d3..67a2efd99 100644
--- a/tests/transport/register.c
+++ b/tests/transport/register.c
@@ -44,8 +44,13 @@ void test_transport_register__custom_transport_ssh(void)
#ifndef GIT_SSH
cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1);
+ cl_git_fail_with(git_transport_new(&transport, NULL, "ssh+git://somehost:somepath"), -1);
+ cl_git_fail_with(git_transport_new(&transport, NULL, "git+ssh://somehost:somepath"), -1);
cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1);
#else
+ cl_git_pass(git_transport_new(&transport, NULL, "ssh://somehost:somepath"));
+ cl_git_pass(git_transport_new(&transport, NULL, "ssh+git://somehost:somepath"));
+ cl_git_pass(git_transport_new(&transport, NULL, "git+ssh://somehost:somepath"));
cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
transport->free(transport);
#endif
@@ -60,8 +65,13 @@ void test_transport_register__custom_transport_ssh(void)
#ifndef GIT_SSH
cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1);
+ cl_git_fail_with(git_transport_new(&transport, NULL, "ssh+git://somehost:somepath"), -1);
+ cl_git_fail_with(git_transport_new(&transport, NULL, "git+ssh://somehost:somepath"), -1);
cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1);
#else
+ cl_git_pass(git_transport_new(&transport, NULL, "ssh://somehost:somepath"));
+ cl_git_pass(git_transport_new(&transport, NULL, "ssh+git://somehost:somepath"));
+ cl_git_pass(git_transport_new(&transport, NULL, "git+ssh://somehost:somepath"));
cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
transport->free(transport);
#endif
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);