summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--CHANGELOG.md26
-rw-r--r--CMakeLists.txt35
-rw-r--r--CONVENTIONS.md32
-rw-r--r--README.md6
-rw-r--r--THREADING.md16
-rw-r--r--appveyor.yml7
-rw-r--r--deps/http-parser/http_parser.c7
-rw-r--r--examples/general.c5
-rw-r--r--include/git2/commit.h46
-rw-r--r--include/git2/common.h9
-rw-r--r--include/git2/odb.h48
-rw-r--r--include/git2/sys/index.h2
-rw-r--r--include/git2/version.h6
-rwxr-xr-xscript/cibuild.sh6
-rw-r--r--script/user_nodefs.h7
-rw-r--r--src/commit.c211
-rw-r--r--src/common.h5
-rw-r--r--src/crlf.c2
-rw-r--r--src/diff_print.c6
-rw-r--r--src/diff_tform.c27
-rw-r--r--src/fileops.c17
-rw-r--r--src/global.c4
-rw-r--r--src/index.c54
-rw-r--r--src/mwindow.c14
-rw-r--r--src/object.c26
-rw-r--r--src/object.h23
-rw-r--r--src/odb.c258
-rw-r--r--src/odb.h3
-rw-r--r--src/odb_loose.c20
-rw-r--r--src/odb_pack.c8
-rw-r--r--src/oid.h9
-rw-r--r--src/openssl_stream.c14
-rw-r--r--src/pack-objects.c12
-rw-r--r--src/pack.c29
-rw-r--r--src/path.c3
-rw-r--r--src/pool.c108
-rw-r--r--src/pool.h28
-rw-r--r--src/posix.c7
-rw-r--r--src/rebase.c22
-rw-r--r--src/refdb_fs.c3
-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.c6
-rw-r--r--src/transport.c2
-rw-r--r--src/transports/smart_pkt.c25
-rw-r--r--src/transports/ssh.c65
-rw-r--r--src/transports/ssh.h12
-rw-r--r--src/transports/winhttp.c5
-rw-r--r--src/tree.c16
-rw-r--r--src/unix/posix.h12
-rw-r--r--src/win32/win32-compat.h3
-rw-r--r--src/xdiff/xmerge.c9
-rw-r--r--tests/commit/write.c150
-rw-r--r--tests/core/pool.c6
-rw-r--r--tests/diff/format_email.c2
-rw-r--r--tests/diff/index.c8
-rw-r--r--tests/diff/workdir.c8
-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.c35
-rw-r--r--tests/merge/workdir/dirty.c4
-rw-r--r--tests/object/tree/write.c98
-rw-r--r--tests/odb/mixed.c155
-rw-r--r--tests/rebase/abort.c31
-rw-r--r--tests/rebase/merge.c57
-rw-r--r--tests/rebase/setup.c205
-rw-r--r--tests/refs/create.c27
-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/transport/register.c10
79 files changed, 1958 insertions, 382 deletions
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 ec5a0d336..21f972d2e 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
@@ -29,6 +43,14 @@ v0.23 + 1
* 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
@@ -57,6 +79,8 @@ v0.23 + 1
### 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,
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/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/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..24a507ea9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -36,4 +36,9 @@ 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: |
+ $ErrorActionPreference="Stop"
+ 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 3488c7440..44ea8882b 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -394,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 487247d75..c1efee320 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -148,6 +148,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;
/**
@@ -252,6 +253,14 @@ typedef enum {
* > - `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/odb.h b/include/git2/odb.h
index 4f1e18bc1..b3ed2706c 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
+#include "oidarray.h"
/**
* @file git2/odb.h
@@ -159,7 +160,8 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/**
- * Determine if objects can be found in the object database from a short OID.
+ * Determine if an object can be found in the object database by an
+ * abbreviated object ID.
*
* @param out The full OID of the found object if just one is found.
* @param db The database to be searched for the given object.
@@ -172,6 +174,50 @@ GIT_EXTERN(int) git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
/**
+ * The information about object IDs to query in `git_odb_expand_ids`,
+ * which will be populated upon return.
+ */
+typedef struct git_odb_expand_id {
+ /** The object ID to expand */
+ git_oid id;
+
+ /**
+ * The length of the object ID (in nibbles, or packets of 4 bits; the
+ * number of hex characters)
+ * */
+ unsigned short length;
+
+ /**
+ * The (optional) type of the object to search for; leave as `0` or set
+ * to `GIT_OBJ_ANY` to query for any object matching the ID.
+ */
+ git_otype type;
+} git_odb_expand_id;
+
+/**
+ * Determine if one or more objects can be found in the object database
+ * by their abbreviated object ID and type. The given array will be
+ * updated in place: for each abbreviated ID that is unique in the
+ * database, and of the given type (if specified), the full object ID,
+ * object ID length (`GIT_OID_HEXSZ`) and type will be written back to
+ * the array. For IDs that are not found (or are ambiguous), the
+ * array entry will be zeroed.
+ *
+ * Note that since this function operates on multiple objects, the
+ * underlying database will not be asked to be reloaded if an object is
+ * not found (which is unlike other object database operations.)
+ *
+ * @param db The database to be searched for the given objects.
+ * @param ids An array of short object IDs to search for
+ * @param count The length of the `ids` array
+ * @return 0 on success or an error code on failure
+ */
+GIT_EXTERN(int) git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count);
+
+/**
* Refresh the object database to load newly added files.
*
* If the object databases have changed on disk while the library
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/user_nodefs.h b/script/user_nodefs.h
index 3d25d92ec..3c06a706d 100644
--- a/script/user_nodefs.h
+++ b/script/user_nodefs.h
@@ -6,6 +6,7 @@
*/
#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__(); }
@@ -25,3 +26,9 @@
#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/commit.c b/src/commit.c
index 5a0509803..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(
@@ -713,3 +789,34 @@ cleanup:
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 bc4bdd856..9abd605cb 100644
--- a/src/common.h
+++ b/src/common.h
@@ -90,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/diff_print.c b/src/diff_print.c
index bc2d6fab0..dae9e341d 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -92,7 +92,11 @@ static int diff_print_info_init_frompatch(
git_diff_line_cb cb,
void *payload)
{
- git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL;
+ git_repository *repo;
+
+ assert(patch);
+
+ repo = patch->diff ? patch->diff->repo : NULL;
memset(pi, 0, sizeof(diff_print_info));
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 7cff34159..8577f06b8 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -261,18 +261,23 @@ static int normalize_find_opts(
if (!given ||
(given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
{
- char *rule =
- git_config__get_string_force(cfg, "diff.renames", "true");
- int boolval;
-
- if (!git__parse_bool(&boolval, rule) && !boolval)
- /* don't set FIND_RENAMES if bool value is false */;
- else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
- opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
- else
- opts->flags |= GIT_DIFF_FIND_RENAMES;
+ if (diff->repo) {
+ char *rule =
+ git_config__get_string_force(cfg, "diff.renames", "true");
+ int boolval;
+
+ if (!git__parse_bool(&boolval, rule) && !boolval)
+ /* don't set FIND_RENAMES if bool value is false */;
+ else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
+ opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ else
+ opts->flags |= GIT_DIFF_FIND_RENAMES;
- git__free(rule);
+ git__free(rule);
+ } else {
+ /* set default flag */
+ opts->flags |= GIT_DIFF_FIND_RENAMES;
+ }
}
/* some flags imply others */
diff --git a/src/fileops.c b/src/fileops.c
index 150333d7a..22868b489 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -1034,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)
@@ -1043,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;
@@ -1076,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/global.c b/src/global.c
index c65b21a11..0bfde1e04 100644
--- a/src/global.c
+++ b/src/global.c
@@ -12,6 +12,7 @@
#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"
@@ -56,7 +57,8 @@ static int init_common(void)
/* 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_filter_global_init()) == 0 &&
+ (ret = git_transport_ssh_global_init()) == 0)
ret = git_openssl_stream_global_init();
GIT_MEMORY_BARRIER;
diff --git a/src/index.c b/src/index.c
index d0a0da2c5..b97f8091d 100644
--- a/src/index.c
+++ b/src/index.c
@@ -829,8 +829,8 @@ void git_index_entry__init_from_stat(
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;
@@ -1245,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;
@@ -1288,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);
@@ -1395,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 */
@@ -1454,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)
@@ -1480,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;
@@ -1569,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);
@@ -1731,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 */
@@ -2135,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");
}
@@ -2193,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 { \
@@ -2216,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
diff --git a/src/mwindow.c b/src/mwindow.c
index 55c8d894b..d3e9be78b 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -296,8 +296,18 @@ static git_mwindow *new_window(
*/
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- git__free(w);
- return NULL;
+ /*
+ * The first error might be down to memory fragmentation even if
+ * we're below our soft limits, so free up what we can and try again.
+ */
+
+ while (git_mwindow_close_lru(mwf) == 0)
+ /* nop */;
+
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ git__free(w);
+ return NULL;
+ }
}
ctl->mmap_calls++;
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/odb.c b/src/odb.c
index 1c877c9fc..890e6e2f8 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -18,6 +18,7 @@
#include "git2/odb_backend.h"
#include "git2/oid.h"
+#include "git2/oidarray.h"
#define GIT_ALTERNATES_FILE "info/alternates"
@@ -48,8 +49,37 @@ static git_cache *odb_cache(git_odb *odb)
return &odb->own_cache;
}
+static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+static git_otype odb_hardcoded_type(const git_oid *id)
+{
+ static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
+ 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
+ static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
+ 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
+
+ if (!git_oid_cmp(id, &empty_blob))
+ return GIT_OBJ_BLOB;
+
+ if (!git_oid_cmp(id, &empty_tree))
+ return GIT_OBJ_TREE;
+
+ return GIT_OBJ_BAD;
+}
+
+static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
+{
+ git_otype type = odb_hardcoded_type(id);
+ if (type == GIT_OBJ_BAD)
+ return -1;
+
+ raw->type = type;
+ raw->len = 0;
+ raw->data = git__calloc(1, sizeof(uint8_t));
+ return 0;
+}
+
int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
{
const char *type_str = git_object_type2string(obj_type);
@@ -651,7 +681,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
- return (int)true;
+ return 1;
}
if (odb_exists_1(db, id, false))
@@ -716,23 +746,19 @@ int git_odb_exists_prefix(
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
- if (len == GIT_OID_HEXSZ) {
+ if (len >= GIT_OID_HEXSZ) {
if (git_odb_exists(db, short_id)) {
if (out)
git_oid_cpy(out, short_id);
return 0;
} else {
- return git_odb__error_notfound("no match for id prefix", short_id);
+ return git_odb__error_notfound(
+ "no match for id prefix", short_id, len);
}
}
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
+ git_oid__cpy_prefix(&key, short_id, len);
error = odb_exists_prefix_1(out, db, &key, len, false);
@@ -740,11 +766,77 @@ int git_odb_exists_prefix(
error = odb_exists_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id prefix", &key);
+ return git_odb__error_notfound("no match for id prefix", &key, len);
return error;
}
+int git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count)
+{
+ size_t i;
+
+ assert(db && ids);
+
+ for (i = 0; i < count; i++) {
+ git_odb_expand_id *query = &ids[i];
+ int error = GIT_EAMBIGUOUS;
+
+ if (!query->type)
+ query->type = GIT_OBJ_ANY;
+
+ /* if we have a short OID, expand it first */
+ if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) {
+ git_oid actual_id;
+
+ error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false);
+ if (!error) {
+ git_oid_cpy(&query->id, &actual_id);
+ query->length = GIT_OID_HEXSZ;
+ }
+ }
+
+ /*
+ * now we ought to have a 40-char OID, either because we've expanded it
+ * or because the user passed a full OID. Ensure its type is right.
+ */
+ if (query->length >= GIT_OID_HEXSZ) {
+ git_otype actual_type;
+
+ error = odb_otype_fast(&actual_type, db, &query->id);
+ if (!error) {
+ if (query->type != GIT_OBJ_ANY && query->type != actual_type)
+ error = GIT_ENOTFOUND;
+ else
+ query->type = actual_type;
+ }
+ }
+
+ switch (error) {
+ /* no errors, so we've successfully expanded the OID */
+ case 0:
+ continue;
+
+ /* the object is missing or ambiguous */
+ case GIT_ENOTFOUND:
+ case GIT_EAMBIGUOUS:
+ memset(&query->id, 0, sizeof(git_oid));
+ query->length = 0;
+ query->type = 0;
+ break;
+
+ /* something went very wrong with the ODB; bail hard */
+ default:
+ return error;
+ }
+ }
+
+ giterr_clear();
+ return 0;
+}
+
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{
int error;
@@ -758,11 +850,53 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
return error;
}
+static int odb_read_header_1(
+ size_t *len_p, git_otype *type_p, git_odb *db,
+ const git_oid *id, bool only_refreshed)
+{
+ size_t i;
+ git_otype ht;
+ bool passthrough = false;
+ int error;
+
+ if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJ_BAD) {
+ *type_p = ht;
+ *len_p = 0;
+ return 0;
+ }
+
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (!b->read_header) {
+ passthrough = true;
+ continue;
+ }
+
+ error = b->read_header(len_p, type_p, b, id);
+
+ switch (error) {
+ case GIT_PASSTHROUGH:
+ passthrough = true;
+ break;
+ case GIT_ENOTFOUND:
+ break;
+ default:
+ return error;
+ }
+ }
+
+ return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
+}
+
int git_odb__read_header_or_object(
git_odb_object **out, size_t *len_p, git_otype *type_p,
git_odb *db, const git_oid *id)
{
- size_t i;
int error = GIT_ENOTFOUND;
git_odb_object *object;
@@ -776,52 +910,32 @@ int git_odb__read_header_or_object(
}
*out = NULL;
+ error = odb_read_header_1(len_p, type_p, db, id, false);
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_read_header_1(len_p, type_p, db, id, true);
- if (b->read_header != NULL)
- error = b->read_header(len_p, type_p, b, id);
- }
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ);
- if (!error || error == GIT_PASSTHROUGH)
+ /* we found the header; return early */
+ if (!error)
return 0;
- /*
- * no backend could read only the header.
- * try reading the whole object and freeing the contents
- */
- if ((error = git_odb_read(&object, db, id)) < 0)
- return error; /* error already set - pass along */
-
- *len_p = object->cached.size;
- *type_p = object->cached.type;
- *out = object;
-
- return 0;
-}
-
-static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
- 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
-static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
- 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
-
-static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
-{
- if (!git_oid_cmp(id, &empty_blob)) {
- raw->type = GIT_OBJ_BLOB;
- raw->len = 0;
- raw->data = git__calloc(1, sizeof(uint8_t));
- return 0;
- } else if (!git_oid_cmp(id, &empty_tree)) {
- raw->type = GIT_OBJ_TREE;
- raw->len = 0;
- raw->data = git__calloc(1, sizeof(uint8_t));
- return 0;
- } else {
- return GIT_ENOTFOUND;
+ if (error == GIT_PASSTHROUGH) {
+ /*
+ * no backend has header-reading functionality
+ * so try using `git_odb_read` instead
+ */
+ error = git_odb_read(&object, db, id);
+ if (!error) {
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
+ *out = object;
+ }
}
+
+ return error;
}
static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
@@ -832,7 +946,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
git_odb_object *object;
bool found = false;
- if (!hardcoded_objects(&raw, id))
+ if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
found = true;
for (i = 0; i < db->backends.length && !found; ++i) {
@@ -881,7 +995,30 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
error = odb_read_1(out, db, id, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id", id);
+ return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ);
+
+ return error;
+}
+
+static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
+{
+ git_odb_object *object;
+ size_t _unused;
+ int error;
+
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ *type_p = object->cached.type;
+ return 0;
+ }
+
+ error = odb_read_header_1(&_unused, type_p, db, id, false);
+
+ if (error == GIT_PASSTHROUGH) {
+ error = odb_read_1(&object, db, id, false);
+ if (!error)
+ *type_p = object->cached.type;
+ git_odb_object_free(object);
+ }
return error;
}
@@ -956,10 +1093,7 @@ int git_odb_read_prefix(
return 0;
}
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
+ git_oid__cpy_prefix(&key, short_id, len);
error = read_prefix_1(out, db, &key, len, false);
@@ -967,7 +1101,7 @@ int git_odb_read_prefix(
error = read_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for prefix", &key);
+ return git_odb__error_notfound("no match for prefix", &key, len);
return error;
}
@@ -1223,12 +1357,14 @@ int git_odb_refresh(struct git_odb *db)
return 0;
}
-int git_odb__error_notfound(const char *message, const git_oid *oid)
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len)
{
if (oid != NULL) {
char oid_str[GIT_OID_HEXSZ + 1];
- git_oid_tostr(oid_str, sizeof(oid_str), oid);
- giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
+ git_oid_tostr(oid_str, oid_len, oid);
+ giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
+ message, oid_len, oid_str);
} else
giterr_set(GITERR_ODB, "Object not found - %s", message);
diff --git a/src/odb.h b/src/odb.h
index 281bd3a4d..31a9fd1b9 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -82,7 +82,8 @@ int git_odb__hashlink(git_oid *out, const char *path);
/*
* Generate a GIT_ENOTFOUND error for the ODB.
*/
-int git_odb__error_notfound(const char *message, const git_oid *oid);
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len);
/*
* Generate a GIT_EAMBIGUOUS error for the ODB.
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 730c4b1e1..9d9bffd21 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -547,7 +547,8 @@ static int locate_object_short_oid(
/* Check that directory exists */
if (git_path_isdir(object_location->ptr) == false)
- return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
state.dir_len = git_buf_len(object_location);
state.short_oid_len = len;
@@ -560,7 +561,8 @@ static int locate_object_short_oid(
return error;
if (!state.found)
- return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
if (state.found > 1)
return git_odb__error_ambiguous("multiple matches in loose objects");
@@ -613,9 +615,10 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
raw.len = 0;
raw.type = GIT_OBJ_BAD;
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git_odb__error_notfound("no matching loose object", oid);
- else if ((error = read_header_loose(&raw, &object_path)) == 0) {
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, GIT_OID_HEXSZ);
+ } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
*len_p = raw.len;
*type_p = raw.type;
}
@@ -633,9 +636,10 @@ static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p
assert(backend && oid);
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git_odb__error_notfound("no matching loose object", oid);
- else if ((error = read_loose(&raw, &object_path)) == 0) {
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, GIT_OID_HEXSZ);
+ } else if ((error = read_loose(&raw, &object_path)) == 0) {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 77d2c75b9..5a57864ad 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -264,7 +264,8 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
if (!pack_entry_find_inner(e, backend, oid, last_found))
return 0;
- return git_odb__error_notfound("failed to find pack entry", oid);
+ return git_odb__error_notfound(
+ "failed to find pack entry", oid, GIT_OID_HEXSZ);
}
static int pack_entry_find_prefix(
@@ -309,7 +310,8 @@ static int pack_entry_find_prefix(
}
if (!found)
- return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
+ return git_odb__error_notfound("no matching pack entry for prefix",
+ short_oid, len);
else
return 0;
}
@@ -333,7 +335,7 @@ static int pack_backend__refresh(git_odb_backend *backend_)
return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
- return git_odb__error_notfound("failed to refresh packfiles", NULL);
+ return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
git_buf_sets(&path, backend->pack_folder);
diff --git a/src/oid.h b/src/oid.h
index aa1f0bfdc..922a2a347 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -44,4 +44,13 @@ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
return git_oid__hashcmp(a->id, b->id);
}
+GIT_INLINE(void) git_oid__cpy_prefix(
+ git_oid *out, const git_oid *id, size_t len)
+{
+ memcpy(&out->id, id->id, (len + 1) / 2);
+
+ if (len & 1)
+ out->id[len / 2] &= 0xF0;
+}
+
#endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 15cabdfb8..97736b714 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -383,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);
@@ -545,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
@@ -552,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);
@@ -576,6 +580,12 @@ 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
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 0afa28e62..46fe8f3db 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -629,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);
@@ -850,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 081e37084..e8bde71f3 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);
}
@@ -1013,7 +1018,7 @@ static int packfile_open(struct git_pack_file *p)
unsigned char *idx_sha1;
if (p->index_version == -1 && pack_index_open(p) < 0)
- return git_odb__error_notfound("failed to open packfile", NULL);
+ return git_odb__error_notfound("failed to open packfile", NULL, 0);
/* if mwf opened by another thread, return now */
if (git_mutex_lock(&p->lock) < 0)
@@ -1094,7 +1099,7 @@ int git_packfile__name(char **out, const char *path)
path_len = strlen(path);
if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL);
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
return -1;
@@ -1112,7 +1117,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
*pack_out = NULL;
if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL);
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
@@ -1138,7 +1143,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
git__free(p);
- return git_odb__error_notfound("packfile not found", NULL);
+ return git_odb__error_notfound("packfile not found", NULL, 0);
}
/* ok, it looks sane as far as we can check without
@@ -1175,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)));
@@ -1185,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)));
}
@@ -1264,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;
@@ -1332,11 +1344,16 @@ static int pack_entry_find_offset(
}
if (!found)
- return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
+ return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
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 852ef576a..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;
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 b9d1d4fc5..bcad9b7cd 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -257,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));
@@ -270,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)
@@ -305,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;
@@ -708,8 +710,8 @@ int git_rebase_init(
branch = head_branch;
}
- rebase = rebase_alloc(given_opts);
- GITERR_CHECK_ALLOC(rebase);
+ if (rebase_alloc(&rebase, given_opts) < 0)
+ return -1;
rebase->repo = repo;
rebase->inmemory = inmemory;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 1348c67a1..f6ed7201a 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -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;
}
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 d7341abe8..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,11 @@ 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;
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 870f08497..2ea57bb64 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -296,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);
@@ -313,12 +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");
- git__free(pkt->ref);
- git__free(pkt);
- return -1;
- }
+ if (!(ptr = strchr(line, '\n')))
+ goto out_err;
len = ptr - line;
GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
@@ -330,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)
@@ -543,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/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 ded041686..32b838084 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -926,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 cfceb3f33..48b9f121d 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -726,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,
@@ -745,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/posix.h b/src/unix/posix.h
index 83edf2b7e..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)
diff --git a/src/win32/win32-compat.h b/src/win32/win32-compat.h
index dff1f45be..f888fd69e 100644
--- a/src/win32/win32-compat.h
+++ b/src/win32/win32-compat.h
@@ -42,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/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/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/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/workdir.c b/tests/diff/workdir.c
index 892c7b72d..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));
@@ -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));
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 ace84d585..1768f5efd 100644
--- a/tests/index/racy.c
+++ b/tests/index/racy.c
@@ -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 99e33e0cd..a69919f53 100644
--- a/tests/merge/workdir/dirty.c
+++ b/tests/merge/workdir/dirty.c
@@ -165,8 +165,8 @@ static void hack_index(char *files[])
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;
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/odb/mixed.c b/tests/odb/mixed.c
index 2dad4b64e..515eadfde 100644
--- a/tests/odb/mixed.c
+++ b/tests/odb/mixed.c
@@ -108,3 +108,158 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
git_odb_object_free(obj);
}
+
+struct expand_id_test_data {
+ char *lookup_id;
+ char *expected_id;
+ git_otype expected_type;
+};
+
+struct expand_id_test_data expand_id_test_data[] = {
+ /* some prefixes and their expected values */
+ { "dea509d0", NULL, GIT_OBJ_ANY },
+ { "00000000", NULL, GIT_OBJ_ANY },
+ { "dea509d0", NULL, GIT_OBJ_ANY },
+ { "dea509d09", "dea509d097ce692e167dfc6a48a7a280cc5e877e", GIT_OBJ_BLOB },
+ { "dea509d0b", "dea509d0b3cb8ee0650f6ca210bc83f4678851ba", GIT_OBJ_BLOB },
+ { "ce0136250", "ce013625030ba8dba906f756967f9e9ca394464a", GIT_OBJ_BLOB },
+ { "0ddeaded", NULL, GIT_OBJ_ANY },
+ { "4d5979b", "4d5979b468252190cb572ae758aca36928e8a91e", GIT_OBJ_TREE },
+ { "0ddeaded", NULL, GIT_OBJ_ANY },
+ { "0ddeadede", "0ddeadede9e6d6ccddce0ee1e5749eed0485e5ea", GIT_OBJ_BLOB },
+ { "0ddeaded9", "0ddeaded9502971eefe1e41e34d0e536853ae20f", GIT_OBJ_BLOB },
+ { "f00b4e", NULL, GIT_OBJ_ANY },
+
+ /* this OID is too short and should be ambiguous! */
+ { "f00", NULL, GIT_OBJ_ANY },
+
+ /* some full-length object ids */
+ { "0000000000000000000000000000000000000000", NULL, GIT_OBJ_ANY },
+ {
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ GIT_OBJ_BLOB
+ },
+ { "f00f00f00f00f00f00f00f00f00f00f00f00f00f", NULL, GIT_OBJ_ANY },
+ {
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ GIT_OBJ_TREE
+ },
+
+ /*
+ * ensure we're not leaking the return error code for the
+ * last lookup if the last object is invalid
+ */
+ { "0ddeadedfff", NULL, GIT_OBJ_ANY },
+};
+
+static void setup_prefix_query(
+ git_odb_expand_id **out_ids,
+ size_t *out_num)
+{
+ git_odb_expand_id *ids;
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ cl_assert((ids = git__calloc(num, sizeof(git_odb_expand_id))));
+
+ for (i = 0; i < num; i++) {
+ git_odb_expand_id *id = &ids[i];
+
+ size_t len = strlen(expand_id_test_data[i].lookup_id);
+
+ git_oid_fromstrn(&id->id, expand_id_test_data[i].lookup_id, len);
+ id->length = (unsigned short)len;
+ id->type = expand_id_test_data[i].expected_type;
+ }
+
+ *out_ids = ids;
+ *out_num = num;
+}
+
+static void assert_found_objects(git_odb_expand_id *ids)
+{
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ git_oid expected_id = {{0}};
+ size_t expected_len = 0;
+ git_otype expected_type = 0;
+
+ if (expand_id_test_data[i].expected_id) {
+ git_oid_fromstr(&expected_id, expand_id_test_data[i].expected_id);
+ expected_len = GIT_OID_HEXSZ;
+ expected_type = expand_id_test_data[i].expected_type;
+ }
+
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(expected_len, ids[i].length);
+ cl_assert_equal_i(expected_type, ids[i].type);
+ }
+}
+
+static void assert_notfound_objects(git_odb_expand_id *ids)
+{
+ git_oid expected_id = {{0}};
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(0, ids[i].length);
+ cl_assert_equal_i(0, ids[i].type);
+ }
+}
+
+void test_odb_mixed__expand_ids(void)
+{
+ git_odb_expand_id *ids;
+ size_t i, num;
+
+ /* test looking for the actual (correct) types */
+
+ setup_prefix_query(&ids, &num);
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit `type == 0` */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = 0;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit GIT_OBJ_ANY */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = GIT_OBJ_ANY;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for the completely wrong type */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = (ids[i].type == GIT_OBJ_BLOB) ?
+ GIT_OBJ_TREE : GIT_OBJ_BLOB;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_notfound_objects(ids);
+ git__free(ids);
+}
+
diff --git a/tests/rebase/abort.c b/tests/rebase/abort.c
index c4b3890bc..4cf14ddce 100644
--- a/tests/rebase/abort.c
+++ b/tests/rebase/abort.c
@@ -86,19 +86,41 @@ void test_rebase_abort__merge(void)
git_rebase_free(rebase);
}
+void test_rebase_abort__merge_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, onto_id;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+ cl_git_pass(git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
void test_rebase_abort__detached_head(void)
{
git_rebase *rebase;
- git_oid branch_id;
- git_reference *onto_ref;
+ git_oid branch_id, onto_id;
git_signature *signature;
git_annotated_commit *branch_head, *onto_head;
git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64");
- cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+ git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
- cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
@@ -112,7 +134,6 @@ void test_rebase_abort__detached_head(void)
git_annotated_commit_free(branch_head);
git_annotated_commit_free(onto_head);
- git_reference_free(onto_ref);
git_rebase_free(rebase);
}
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index c60113b64..d090e02e8 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -252,6 +252,63 @@ void test_rebase_merge__commit(void)
git_rebase_free(rebase);
}
+void test_rebase_merge__commit_with_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, upstream_id;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id, parent_id;
+ git_signature *author;
+ git_commit *commit;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+ cl_git_pass(git_oid_fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ git_oid_fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0));
+
+ git_oid_fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992");
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ cl_assert_equal_s(NULL, git_commit_message_encoding(commit));
+ cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit));
+
+ cl_git_pass(git_signature_new(&author,
+ "Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60)));
+ cl_assert(git_signature__equal(author, git_commit_author(commit)));
+
+ cl_assert(git_signature__equal(signature, git_commit_committer(commit)));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&parent_id, git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 1 to beef", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_signature_free(author);
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_rebase_free(rebase);
+}
+
void test_rebase_merge__blocked_when_dirty(void)
{
git_rebase *rebase;
diff --git a/tests/rebase/setup.c b/tests/rebase/setup.c
index 627d3b9de..b07a83af6 100644
--- a/tests/rebase/setup.c
+++ b/tests/rebase/setup.c
@@ -196,6 +196,115 @@ void test_rebase_setup__merge_onto_and_upstream(void)
git_rebase_free(rebase);
}
+/* git checkout beef && git rebase --merge --onto master gravy veal */
+void test_rebase_setup__merge_onto_upstream_and_branch(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref, *branch_ref, *onto_ref;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&onto_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_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("3\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_reference_free(upstream_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge --onto `git rev-parse master`
+ * `git rev-parse veal` `git rev-parse gravy`
+ */
+void test_rebase_setup__merge_onto_upstream_and_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid upstream_id, branch_id, onto_id;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_oid_fromstr(&upstream_id, "f87d14a4a236582a0278a916340a793714256864"));
+ cl_git_pass(git_oid_fromstr(&branch_id, "d616d97082eb7bb2dc6f180a7cca940993b7a56f"));
+ cl_git_pass(git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_rebase_free(rebase);
+}
+
/* Ensure merge commits are dropped in a rebase */
/* git checkout veal && git rebase --merge master */
void test_rebase_setup__branch_with_merges(void)
@@ -342,6 +451,102 @@ void test_rebase_setup__merge_null_branch_uses_HEAD(void)
git_rebase_free(rebase);
}
+/* git checkout b146bd7608eac53d9bf9e1a6963543588b555c64 && git rebase --merge master */
+void test_rebase_setup__merge_from_detached(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid branch_id, head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ 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_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 */
+void test_rebase_setup__merge_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id, upstream_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+
+ cl_git_pass(git_oid_fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_rebase_free(rebase);
+}
+
static int rebase_is_blocked(void)
{
git_rebase *rebase = NULL;
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/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/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