summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-09-30 04:38:05 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-09-30 04:38:05 +0200
commitaf6cc38fc0acba14277ebfd247e0a46a867a2c33 (patch)
treedef8c9608166385421ca769972b93258e1066db8
parent3a728fb508ea3eea8033a9e338c61a6421ad21b2 (diff)
parenta2a23322193eeca5d2912c0b74c5374f8ec21737 (diff)
downloadlibgit2-af6cc38fc0acba14277ebfd247e0a46a867a2c33.tar.gz
Merge remote-tracking branch 'upstream/master' into cmn/describe
-rw-r--r--.editorconfig14
-rw-r--r--.travis.yml27
-rw-r--r--AUTHORS1
-rw-r--r--CHANGELOG.md60
-rw-r--r--CMakeLists.txt44
-rw-r--r--CONTRIBUTING.md64
-rw-r--r--PROJECTS.md53
-rw-r--r--README.md13
-rw-r--r--cmake/Modules/FindGSSAPI.cmake324
-rw-r--r--deps/http-parser/http_parser.h2
-rw-r--r--deps/zlib/adler32.c68
-rw-r--r--deps/zlib/crc32.c83
-rw-r--r--deps/zlib/crc32.h2
-rw-r--r--deps/zlib/deflate.c263
-rw-r--r--deps/zlib/deflate.h12
-rw-r--r--deps/zlib/infback.c640
-rw-r--r--deps/zlib/inffast.c6
-rw-r--r--deps/zlib/inffixed.h6
-rw-r--r--deps/zlib/inflate.c136
-rw-r--r--deps/zlib/inftrees.c54
-rw-r--r--deps/zlib/trees.c54
-rw-r--r--deps/zlib/zconf.h4
-rw-r--r--deps/zlib/zlib.h345
-rw-r--r--deps/zlib/zutil.c23
-rw-r--r--deps/zlib/zutil.h103
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/Makefile1
-rw-r--r--examples/for-each-ref.c46
-rw-r--r--examples/general.c4
-rw-r--r--examples/log.c49
-rw-r--r--examples/network/fetch.c2
-rw-r--r--examples/rev-list.c4
-rw-r--r--examples/showindex.c4
-rw-r--r--examples/status.c7
-rw-r--r--examples/tag.c2
-rw-r--r--include/git2/attr.h29
-rw-r--r--include/git2/blame.h19
-rw-r--r--include/git2/blob.h2
-rw-r--r--include/git2/buffer.h16
-rw-r--r--include/git2/checkout.h59
-rw-r--r--include/git2/cherrypick.h35
-rw-r--r--include/git2/clone.h200
-rw-r--r--include/git2/commit.h5
-rw-r--r--include/git2/config.h28
-rw-r--r--include/git2/diff.h145
-rw-r--r--include/git2/errors.h34
-rw-r--r--include/git2/filter.h9
-rw-r--r--include/git2/index.h63
-rw-r--r--include/git2/merge.h57
-rw-r--r--include/git2/message.h8
-rw-r--r--include/git2/net.h5
-rw-r--r--include/git2/object.h5
-rw-r--r--include/git2/oid.h12
-rw-r--r--include/git2/oidarray.h40
-rw-r--r--include/git2/pathspec.h3
-rw-r--r--include/git2/push.h4
-rw-r--r--include/git2/reflog.h2
-rw-r--r--include/git2/refs.h15
-rw-r--r--include/git2/remote.h94
-rw-r--r--include/git2/repository.h39
-rw-r--r--include/git2/reset.h10
-rw-r--r--include/git2/revert.h20
-rw-r--r--include/git2/signature.h2
-rw-r--r--include/git2/status.h44
-rw-r--r--include/git2/submodule.h20
-rw-r--r--include/git2/sys/config.h13
-rw-r--r--include/git2/sys/diff.h28
-rw-r--r--include/git2/sys/filter.h12
-rw-r--r--include/git2/sys/odb_backend.h9
-rw-r--r--include/git2/sys/refdb_backend.h9
-rw-r--r--include/git2/sys/repository.h13
-rw-r--r--include/git2/sys/transport.h352
-rw-r--r--include/git2/transport.h415
-rw-r--r--include/git2/tree.h8
-rw-r--r--include/git2/types.h86
-rw-r--r--include/git2/version.h6
-rwxr-xr-xscript/cibuild.sh18
-rwxr-xr-xscript/install-deps-linux.sh6
-rwxr-xr-xscript/install-deps-osx.sh5
-rw-r--r--src/array.h2
-rw-r--r--src/attr.c12
-rw-r--r--src/attr_file.c40
-rw-r--r--src/attr_file.h6
-rw-r--r--src/attrcache.c13
-rw-r--r--src/blame.c14
-rw-r--r--src/blob.c9
-rw-r--r--src/branch.c15
-rw-r--r--src/buf_text.c14
-rw-r--r--src/buf_text.h5
-rw-r--r--src/buffer.c114
-rw-r--r--src/buffer.h6
-rw-r--r--src/cache.c18
-rw-r--r--src/cache.h2
-rw-r--r--src/cc-compat.h8
-rw-r--r--src/checkout.c161
-rw-r--r--src/cherrypick.c93
-rw-r--r--src/clone.c386
-rw-r--r--src/clone.h12
-rw-r--r--src/commit.c99
-rw-r--r--src/common.h6
-rw-r--r--src/config.c70
-rw-r--r--src/config.h6
-rw-r--r--src/config_cache.c50
-rw-r--r--src/config_file.c621
-rw-r--r--src/crlf.c30
-rw-r--r--src/diff.c291
-rw-r--r--src/diff.h6
-rw-r--r--src/diff_driver.c19
-rw-r--r--src/diff_file.c4
-rw-r--r--src/diff_patch.c1
-rw-r--r--src/diff_print.c111
-rw-r--r--src/diff_stats.c2
-rw-r--r--src/diff_tform.c12
-rw-r--r--src/errors.c16
-rw-r--r--src/filebuf.c2
-rw-r--r--src/fileops.c22
-rw-r--r--src/fileops.h2
-rw-r--r--src/filter.c22
-rw-r--r--src/filter.h4
-rw-r--r--src/fnmatch.c36
-rw-r--r--src/fnmatch.h27
-rw-r--r--src/global.c94
-rw-r--r--src/global.h7
-rw-r--r--src/graph.c4
-rw-r--r--src/ignore.c73
-rw-r--r--src/ignore.h9
-rw-r--r--src/index.c73
-rw-r--r--src/indexer.c195
-rw-r--r--src/iterator.c74
-rw-r--r--src/iterator.h3
-rw-r--r--src/map.h1
-rw-r--r--src/merge.c357
-rw-r--r--src/merge.h2
-rw-r--r--src/message.c4
-rw-r--r--src/mwindow.c125
-rw-r--r--src/mwindow.h10
-rw-r--r--src/netops.c68
-rw-r--r--src/netops.h18
-rw-r--r--src/object.c2
-rw-r--r--src/odb.c14
-rw-r--r--src/odb_loose.c6
-rw-r--r--src/odb_pack.c9
-rw-r--r--src/oid.c8
-rw-r--r--src/oid.h11
-rw-r--r--src/oidarray.c21
-rw-r--r--src/oidarray.h18
-rw-r--r--src/pack-objects.c9
-rw-r--r--src/pack.c312
-rw-r--r--src/pack.h13
-rw-r--r--src/path.c206
-rw-r--r--src/path.h28
-rw-r--r--src/pool.c4
-rw-r--r--src/posix.c9
-rw-r--r--src/posix.h73
-rw-r--r--src/push.c13
-rw-r--r--src/refdb.c13
-rw-r--r--src/refdb_fs.c40
-rw-r--r--src/refs.c48
-rw-r--r--src/refs.h7
-rw-r--r--src/refspec.c72
-rw-r--r--src/remote.c469
-rw-r--r--src/remote.h4
-rw-r--r--src/repository.c202
-rw-r--r--src/repository.h4
-rw-r--r--src/revert.c23
-rw-r--r--src/revparse.c33
-rw-r--r--src/revwalk.c5
-rw-r--r--src/signature.c6
-rw-r--r--src/stash.c11
-rw-r--r--src/status.c83
-rw-r--r--src/strnlen.h3
-rw-r--r--src/submodule.c110
-rw-r--r--src/sysdir.c18
-rw-r--r--src/tag.c14
-rw-r--r--src/thread-utils.h23
-rw-r--r--src/trace.h10
-rw-r--r--src/transport.c196
-rw-r--r--src/transports/auth.c71
-rw-r--r--src/transports/auth.h63
-rw-r--r--src/transports/auth_negotiate.c275
-rw-r--r--src/transports/auth_negotiate.h27
-rw-r--r--src/transports/cred.c58
-rw-r--r--src/transports/cred.h14
-rw-r--r--src/transports/cred_helpers.c3
-rw-r--r--src/transports/git.c1
-rw-r--r--src/transports/http.c313
-rw-r--r--src/transports/local.c63
-rw-r--r--src/transports/smart.c59
-rw-r--r--src/transports/smart.h7
-rw-r--r--src/transports/smart_pkt.c1
-rw-r--r--src/transports/smart_protocol.c77
-rw-r--r--src/transports/ssh.c286
-rw-r--r--src/transports/winhttp.c174
-rw-r--r--src/tree.c117
-rw-r--r--src/tree.h5
-rw-r--r--src/unix/map.c6
-rw-r--r--src/unix/posix.h32
-rw-r--r--src/unix/realpath.c15
-rw-r--r--src/util.c5
-rw-r--r--src/util.h13
-rw-r--r--src/win32/map.c5
-rw-r--r--src/win32/mingw-compat.h16
-rw-r--r--src/win32/msvc-compat.h35
-rw-r--r--src/win32/posix.h45
-rw-r--r--src/win32/posix_w32.c82
-rw-r--r--src/win32/pthread.c109
-rw-r--r--src/win32/pthread.h33
-rw-r--r--tests/attr/ignore.c23
-rw-r--r--tests/attr/repo.c48
-rw-r--r--tests/blame/blame_helpers.c4
-rw-r--r--tests/checkout/binaryunicode.c4
-rw-r--r--tests/checkout/conflict.c10
-rw-r--r--tests/checkout/crlf.c19
-rw-r--r--tests/cherrypick/bare.c6
-rw-r--r--tests/cherrypick/workdir.c93
-rw-r--r--tests/clar_libgit2.c45
-rw-r--r--tests/clar_libgit2.h32
-rw-r--r--tests/clone/local.c123
-rw-r--r--tests/clone/nonetwork.c38
-rw-r--r--tests/clone/transport.c51
-rw-r--r--tests/commit/commit.c6
-rw-r--r--tests/commit/signature.c4
-rw-r--r--tests/commit/write.c2
-rw-r--r--tests/config/config_helpers.c28
-rw-r--r--tests/config/config_helpers.h4
-rw-r--r--tests/config/global.c15
-rw-r--r--tests/config/include.c2
-rw-r--r--tests/config/multivar.c6
-rw-r--r--tests/config/refresh.c9
-rw-r--r--tests/config/snapshot.c64
-rw-r--r--tests/config/stress.c13
-rw-r--r--tests/config/write.c43
-rw-r--r--tests/core/buffer.c63
-rw-r--r--tests/core/copy.c26
-rw-r--r--tests/core/env.c26
-rw-r--r--tests/core/mkdir.c31
-rw-r--r--tests/core/path.c53
-rw-r--r--tests/core/pool.c10
-rw-r--r--tests/diff/blob.c14
-rw-r--r--tests/diff/iterator.c4
-rw-r--r--tests/diff/stats.c4
-rw-r--r--tests/diff/tree.c2
-rw-r--r--tests/diff/workdir.c212
-rw-r--r--tests/fetchhead/fetchhead_data.h15
-rw-r--r--tests/fetchhead/nonetwork.c8
-rw-r--r--tests/filter/crlf.c102
-rw-r--r--tests/filter/custom.c15
-rw-r--r--tests/filter/ident.c6
-rw-r--r--tests/index/conflicts.c26
-rw-r--r--tests/index/crlf.c30
-rw-r--r--tests/index/filemodes.c17
-rw-r--r--tests/index/read_tree.c2
-rw-r--r--tests/index/rename.c4
-rw-r--r--tests/index/reuc.c54
-rw-r--r--tests/index/tests.c8
-rw-r--r--tests/main.c6
-rw-r--r--tests/merge/merge_helpers.c2
-rw-r--r--tests/merge/merge_helpers.h8
-rw-r--r--tests/merge/trees/trivial.c2
-rw-r--r--tests/merge/workdir/analysis.c77
-rw-r--r--tests/merge/workdir/dirty.c2
-rw-r--r--tests/network/fetchlocal.c41
-rw-r--r--tests/network/matchhost.c13
-rw-r--r--tests/network/refspecs.c61
-rw-r--r--tests/network/remote/defaultbranch.c108
-rw-r--r--tests/network/remote/delete.c61
-rw-r--r--tests/network/remote/local.c11
-rw-r--r--tests/network/remote/remotes.c120
-rw-r--r--tests/network/remote/rename.c164
-rw-r--r--tests/network/urlparse.c18
-rw-r--r--tests/notes/notes.c8
-rw-r--r--tests/notes/notesref.c4
-rw-r--r--tests/object/blob/filter.c15
-rw-r--r--tests/object/cache.c4
-rw-r--r--tests/object/commit/commitstagedfile.c11
-rw-r--r--tests/object/lookupbypath.c12
-rw-r--r--tests/object/message.c14
-rw-r--r--tests/object/peel.c2
-rw-r--r--tests/object/raw/chars.c2
-rw-r--r--tests/object/raw/compare.c5
-rw-r--r--tests/object/tree/write.c8
-rw-r--r--tests/odb/foreach.c24
-rw-r--r--tests/odb/mixed.c6
-rw-r--r--tests/online/clone.c345
-rw-r--r--tests/online/fetch.c18
-rw-r--r--tests/online/fetchhead.c12
-rw-r--r--tests/online/push.c19
-rw-r--r--tests/online/push_util.c6
-rw-r--r--tests/online/push_util.h2
-rw-r--r--tests/pack/indexer.c4
-rw-r--r--tests/pack/packbuilder.c6
-rw-r--r--tests/pack/sharing.c42
-rw-r--r--tests/path/core.c53
-rw-r--r--tests/refs/branches/create.c68
-rw-r--r--tests/refs/branches/delete.c7
-rw-r--r--tests/refs/branches/ishead.c24
-rw-r--r--tests/refs/branches/move.c17
-rw-r--r--tests/refs/create.c10
-rw-r--r--tests/refs/createwithlog.c2
-rw-r--r--tests/refs/iterator.c33
-rw-r--r--tests/refs/lookup.c2
-rw-r--r--tests/refs/overwrite.c4
-rw-r--r--tests/refs/pack.c4
-rw-r--r--tests/refs/peel.c2
-rw-r--r--tests/refs/read.c14
-rw-r--r--tests/refs/rename.c4
-rw-r--r--tests/refs/settargetwithlog.c4
-rw-r--r--tests/refs/setter.c2
-rw-r--r--tests/refs/unicode.c6
-rw-r--r--tests/repo/config.c38
-rw-r--r--tests/repo/hashfile.c12
-rw-r--r--tests/repo/head.c4
-rw-r--r--tests/repo/init.c79
-rw-r--r--tests/repo/iterator.c32
-rw-r--r--tests/repo/open.c5
-rw-r--r--tests/repo/pathspec.c2
-rw-r--r--tests/repo/state.c4
-rw-r--r--tests/revwalk/basic.c8
-rw-r--r--tests/revwalk/hidecb.c16
-rw-r--r--tests/revwalk/mergebase.c68
-rw-r--r--tests/revwalk/simplify.c6
-rw-r--r--tests/stash/drop.c8
-rw-r--r--tests/stash/save.c25
-rw-r--r--tests/status/ignore.c207
-rw-r--r--tests/status/single.c4
-rw-r--r--tests/status/status_helpers.c3
-rw-r--r--tests/status/worktree.c181
-rw-r--r--tests/stress/diff.c10
-rw-r--r--tests/structinit/structinit.c5
-rw-r--r--tests/submodule/add.c25
-rw-r--r--tests/submodule/repository_init.c40
-rw-r--r--tests/submodule/submodule_helpers.c4
-rw-r--r--tests/threads/basic.c14
-rw-r--r--tests/threads/diff.c14
-rw-r--r--tests/threads/refdb.c31
-rw-r--r--tests/threads/thread_helpers.c2
-rw-r--r--tests/transport/register.c68
337 files changed, 11818 insertions, 4231 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..be59274e8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+; Check http://editorconfig.org/ for more informations
+; Top-most EditorConfig file
+root = true
+
+; tab indentation
+[*]
+indent_style = tab
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+; 4-column space indentation
+[*.md]
+indent_style = space
+indent_size = 4
diff --git a/.travis.yml b/.travis.yml
index f25ff7681..589edfa36 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,10 @@
language: c
+os:
+ - linux
+ - osx
+
compiler:
- gcc
- clang
@@ -17,17 +21,27 @@ env:
matrix:
fast_finish: true
+ exclude:
+ - os: osx
+ compiler: gcc
include:
- compiler: i586-mingw32msvc-gcc
- env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
+ env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF"
+ os: linux
- compiler: gcc
env: COVERITY=1
+ os: linux
+ - compiler: gcc
+ env:
+ - VALGRIND=1
+ OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug"
+ os: linux
allow_failures:
- env: COVERITY=1
+ - env: VALGRIND=1
install:
- - sudo apt-get -qq update
- - sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
+ - ./script/install-deps-${TRAVIS_OS_NAME}.sh
# Run the Build script and tests
script:
@@ -35,13 +49,14 @@ script:
# Run Tests
after_success:
- - sudo apt-get -qq install valgrind
- - valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
+ - if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then sudo apt-get -qq install valgrind; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
-# Only watch the development branch
+# Only watch the development and master branches
branches:
only:
- development
+ - master
# Notify development list when needed
notifications:
diff --git a/AUTHORS b/AUTHORS
index 6854ed016..2eaec0ff6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@ Alexei Sholik
Andreas Ericsson
Anton "antong" Gyllenberg
Ankur Sethi
+Arthur Schreiber
Ben Noordhuis
Ben Straub
Benjamin C Meyer
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..b23e07d93
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,60 @@
+v0.21 + 1
+------
+
+* File unlocks are atomic again via rename. Read-only files on Windows are
+ made read-write if necessary.
+
+* Share open packfiles across repositories to share descriptors and mmaps.
+
+* Use a map for the treebuilder, making insertion O(1)
+
+* LF -> CRLF filter refuses to handle mixed-EOL files
+
+* LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4)
+
+* The git_transport structure definition has moved into the sys/transport.h
+ file.
+
+* The ssh transport supports asking the remote host for accepted
+ credential types as well as multiple challeges using a single
+ connection. This requires to know which username you want to connect
+ as, so this introduces the USERNAME credential type which the ssh
+ transport will use to ask for the username.
+
+* The git_transport_register function no longer takes a priority and takes
+ a URL scheme name (eg "http") instead of a prefix like "http://"
+
+* The git_remote_set_transport function now sets a transport factory function,
+ rather than a pre-existing transport instance.
+
+* A factory function for ssh has been added which allows to change the
+ path of the programs to execute for receive-pack and upload-pack on
+ the server, git_transport_ssh_with_paths.
+
+* The git_clone_options struct no longer provides the ignore_cert_errors or
+ remote_name members for remote customization.
+
+ Instead, the git_clone_options struct has two new members, remote_cb and
+ remote_cb_payload, which allow the caller to completely override the remote
+ creation process. If needed, the caller can use this callback to give their
+ remote a name other than the default (origin) or disable cert checking.
+
+ The remote_callbacks member has been preserved for convenience, although it
+ is not used when a remote creation callback is supplied.
+
+* The git_clone_options struct now provides repository_cb and
+ repository_cb_payload to allow the user to create a repository with
+ custom options.
+
+* The option to ignore certificate errors via git_remote_cert_check()
+ is no longer present. Instead, git_remote_callbacks has gained a new
+ entry which lets the user perform their own certificate checks.
+
+* git_clone_into and git_clone_local_into have been removed from the
+ public API in favour of git_clone callbacks
+
+* Add support for refspecs with the asterisk in the middle of a
+ pattern.
+
+* Introduce git_merge_bases() and the git_oidarray type to expose all
+ merge bases between two commits.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a3890152..76cca4f22 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,7 @@ OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_ICONV "Link with and use iconv library" OFF )
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 )
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
@@ -127,6 +128,9 @@ STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_V
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
+FILE(STRINGS "include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
+STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
+
# Find required dependencies
INCLUDE_DIRECTORIES(src include)
@@ -139,7 +143,7 @@ ELSE ()
FIND_PACKAGE(OpenSSL)
ENDIF ()
- FIND_PACKAGE(HTTP_Parser QUIET)
+ FIND_PACKAGE(HTTP_Parser)
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
@@ -157,7 +161,11 @@ IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
ADD_DEFINITIONS(-DOPENSSL_SHA1)
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
+ IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lssl")
+ ELSE()
+ SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
+ ENDIF ()
ELSE()
FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
ENDIF()
@@ -168,25 +176,21 @@ IF (ENABLE_TRACE STREQUAL "ON")
ENDIF()
# Include POSIX regex when it is required
-IF(WIN32 OR AMIGA OR ANDROID)
+IF(WIN32 OR AMIGA OR ANDROID OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
INCLUDE_DIRECTORIES(deps/regex)
SET(SRC_REGEX deps/regex/regex.c)
ENDIF()
# Optional external dependency: zlib
-# It's optional, but FIND_PACKAGE gives a warning that looks more like an
-# error.
-FIND_PACKAGE(ZLIB QUIET)
+FIND_PACKAGE(ZLIB)
IF (ZLIB_FOUND)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
LINK_LIBRARIES(${ZLIB_LIBRARIES})
- IF(APPLE)
+ IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz")
ELSE()
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
ENDIF()
- # Fake the message CMake would have shown
- MESSAGE(STATUS "Found zlib: ${ZLIB_LIBRARY}")
ELSE()
MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
INCLUDE_DIRECTORIES(deps/zlib)
@@ -195,8 +199,8 @@ ELSE()
ENDIF()
# Optional external dependency: libssh2
-IF (USE_SSH AND NOT MINGW)
- FIND_PACKAGE(LIBSSH2 QUIET)
+IF (USE_SSH)
+ FIND_PACKAGE(LIBSSH2)
ENDIF()
IF (LIBSSH2_FOUND)
ADD_DEFINITIONS(-DGIT_SSH)
@@ -205,9 +209,17 @@ IF (LIBSSH2_FOUND)
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
ENDIF()
+# Optional external dependency: libgssapi
+IF (USE_GSSAPI)
+ FIND_PACKAGE(GSSAPI)
+ENDIF()
+IF (GSSAPI_FOUND)
+ ADD_DEFINITIONS(-DGIT_GSSAPI)
+ENDIF()
+
# Optional external dependency: iconv
IF (USE_ICONV)
- FIND_PACKAGE(ICONV QUIET)
+ FIND_PACKAGE(Iconv)
ENDIF()
IF (ICONV_FOUND)
ADD_DEFINITIONS(-DGIT_USE_ICONV)
@@ -290,6 +302,10 @@ IF (MSVC)
ELSE ()
SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}")
+ IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
+ ENDIF()
+
IF (WIN32 AND NOT CYGWIN)
SET(CMAKE_C_FLAGS_DEBUG "-D_DEBUG")
ENDIF ()
@@ -380,6 +396,7 @@ ENDIF()
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
+TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(git2)
@@ -393,7 +410,7 @@ MSVC_SPLIT_SOURCES(git2)
IF (SONAME)
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
- SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR})
+ SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
IF (LIBGIT2_FILENAME)
ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
@@ -446,6 +463,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
+ TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar)
MSVC_SPLIT_SOURCES(libgit2_clar)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4efe28ed3..8ebb99154 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,17 +22,25 @@ Also, feel free to open an
about any concerns you have. We like to use Issues for that so there is an
easily accessible permanent record of the conversation.
+## Libgit2 Versions
+
+The `master` branch is the main branch where development happens.
+Releases are tagged
+(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) )
+and when a critical bug fix needs to be backported, it will be done on a
+`<tag>-maint` maintenance branch.
+
## Reporting Bugs
First, know which version of libgit2 your problem is in and include it in
your bug report. This can either be a tag (e.g.
-[v0.17.0](https://github.com/libgit2/libgit2/tree/v0.17.0) ) or a commit
-SHA (e.g.
+[v0.17.0](https://github.com/libgit2/libgit2/releases/tag/v0.17.0) ) or a
+commit SHA (e.g.
[01be7863](https://github.com/libgit2/libgit2/commit/01be786319238fd6507a08316d1c265c1a89407f)
-). Using [`git describe`](http://git-scm.com/docs/git-describe) is a great
-way to tell us what version you're working with.
+). Using [`git describe`](http://git-scm.com/docs/git-describe) is a
+great way to tell us what version you're working with.
-If you're not running against the latest `development` branch version,
+If you're not running against the latest `master` branch version,
please compile and test against that to avoid re-reporting an issue that's
already been fixed.
@@ -44,25 +52,33 @@ out a way to help you.
## Pull Requests
-Our work flow is a typical GitHub flow, where contributors fork the
-[libgit2 repository](https://github.com/libgit2/libgit2), make their changes
-on branch, and submit a
-[Pull Request](https://help.github.com/articles/using-pull-requests)
-(a.k.a. "PR").
+Our work flow is a [typical GitHub flow](https://guides.github.com/introduction/flow/index.html),
+where contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2),
+make their changes on branch, and submit a
+[Pull Request](https://help.github.com/articles/using-pull-requests) (a.k.a. "PR").
+Pull requests should usually be targeted at the `master` branch.
Life will be a lot easier for you (and us) if you follow this pattern
-(i.e. fork, named branch, submit PR). If you use your fork's `development`
-branch, things can get messy.
+(i.e. fork, named branch, submit PR). If you use your fork's `master`
+branch directly, things can get messy.
+
+Please include a nice description of your changes when you submit your PR;
+if we have to read the whole diff to figure out why you're contributing
+in the first place, you're less likely to get feedback and have your change
+merged in.
+
+If you are starting to work on a particular area, feel free to submit a PR
+that highlights your work in progress (and note in the PR title that it's
+not ready to merge). These early PRs are welcome and will help in getting
+visibility for your fix, allow others to comment early on the changes and
+also let others know that you are currently working on something.
-Please include a nice description of your changes with your PR; if we have
-to read the whole diff to figure out why you're contributing in the first
-place, you're less likely to get feedback and have your change merged in.
+Before wrapping up a PR, you should be sure to:
-If you are working on a particular area then feel free to submit a PR that
-highlights your work in progress (and flag in the PR title that it's not
-ready to merge). This will help in getting visibility for your fix, allow
-others to comment early on the changes and also let others know that you
-are currently working on something.
+* Write tests to cover any functional changes (ideally tests that would
+ have failed before the PR and now pass)
+* Update documentation for any changed public APIs
+* Add to the [`CHANGELOG.md`](CHANGELOG.md) file describing any major changes
## Porting Code From Other Open-Source Projects
@@ -80,10 +96,10 @@ you're porting code *from* to see what you need to do. As a general rule,
MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
license typically doesn't work due to GPL incompatibility.
-If you are pulling in code from core Git, another project or code you've
-pulled from a forum / Stack Overflow then please flag this in your PR and
-also make sure you've given proper credit to the original author in the
-code snippet.
+If your pull request uses code from core Git, another project, or code
+from a forum / Stack Overflow, then *please* flag this in your PR and make
+sure you've given proper credit to the original author in the code
+snippet.
## Style Guide
diff --git a/PROJECTS.md b/PROJECTS.md
index 9472fb43c..d69988759 100644
--- a/PROJECTS.md
+++ b/PROJECTS.md
@@ -10,10 +10,11 @@ ideas that no one is actively working on.
## Before You Start
-Please start by reading the README.md, CONTRIBUTING.md, and CONVENTIONS.md
-files before diving into one of these projects. Those will explain our
-work flow and coding conventions to help ensure that your work will be
-easily integrated into libgit2.
+Please start by reading the [README.md](README.md),
+[CONTRIBUTING.md](CONTRIBUTING.md), and [CONVENTIONS.md](CONVENTIONS.md)
+files before diving into one of these projects. Those explain our work
+flow and coding conventions to help ensure that your work will be easily
+integrated into libgit2.
Next, work through the build instructions and make sure you can clone the
repository, compile it, and run the tests successfully. That will make
@@ -27,7 +28,7 @@ These are good small projects to get started with libgit2.
* Look at the `examples/` programs, find an existing one that mirrors a
core Git command and add a missing command-line option. There are many
gaps right now and this helps demonstrate how to use the library. Here
- are some specific ideas:
+ are some specific ideas (though there are many more):
* Fix the `examples/diff.c` implementation of the `-B`
(a.k.a. `--break-rewrites`) command line option to actually look for
the optional `[<n>][/<m>]` configuration values. There is an
@@ -39,12 +40,6 @@ These are good small projects to get started with libgit2.
the data is available, you would just need to add the code into the
`print_commit()` routine (along with a way of passing the option
into that function).
- * For `examples/log.c`, implement any one of `--author=<...>`,
- `--committer=<...>`, or `--grep=<...>` but just use simple string
- match with `strstr()` instead of full regular expression
- matching. (I.e. I'm suggesting implementing this as if
- `--fixed-strings` was always turned on, because it will be a simpler
- project.)
* As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that
@@ -58,9 +53,6 @@ These are good small projects to get started with libgit2.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
confusing much more easily.
-* Add support for the symref protocol extension, so we don't guess
- what the remote's default branch is
- [#2006](https://github.com/libgit2/libgit2/issues/2006)
If none of these appeal to you, take a look at our issues list to see if
there are any unresolved issues you'd like to jump in on.
@@ -73,19 +65,44 @@ into one of these as a first project for libgit2 - we'd rather get to
know you first by successfully shipping your work on one of the smaller
projects above.
+Some of these projects are broken down into subprojects and/or have
+some incremental steps listed towards the larger goal. Those steps
+might make good smaller projects by themselves.
+
* Port part of the Git test suite to run against the command line emulation
in examples/
+ * Pick a Git command that is emulated in our examples/ area
+ * Extract the Git tests that exercise that command
+ * Convert the tests to call our emulation
+ * These tests could go in examples/tests/...
* Fix symlink support for files in the .git directory (i.e. don't overwrite
the symlinks when writing the file contents back out)
* Implement a 'git describe' like API
* Add hooks API to enumerate and manage hooks (not run them at this point)
+ * Enumeration of available hooks
+ * Lookup API to see which hooks have a script and get the script
+ * Read/write API to load a hook script and write a hook script
+ * Eventually, callback API to invoke a hook callback when libgit2
+ executes the action in question
* Isolate logic of ignore evaluation into a standalone API
* Upgrade internal libxdiff code to latest from core Git
-* Add a hashtable lookup for files in the index instead of binary search
- every time
+* Improve index internals with hashtable lookup for files instead of
+ using binary search every time
* Make the index write the cache out to disk (with tests to gain
confidence that the caching invalidation works correctly)
-* Have the tree builder use a hash table when building instead of a
- list.
+* Tree builder improvements:
+ * Use a hash table when building instead of a list
+ * Extend to allow building a tree hierarchy
* Move the tagopt mechanism to the newer git 1.9 interpretation of
--tags [#2120](https://github.com/libgit2/libgit2/issues/2120)
+* Apply-patch API
+* Add a patch editing API to enable "git add -p" type operations
+* Textconv API to filter binary data before generating diffs (something
+ like the current Filter API, probably).
+* Performance profiling and improvement
+* Build in handling of "empty tree" and "empty blob" SHAs
+* Support "git replace" ref replacements
+* Include conflicts in diff results and in status
+ * GIT_DELTA_CONFLICT for items in conflict (with multiple files)
+ * Appropriate flags for status
+* Support sparse checkout (i.e. "core.sparsecheckout" and ".git/info/sparse-checkout")
diff --git a/README.md b/README.md
index 8dd073430..a8f546c4b 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ dependencies, it can make use of a few libraries to add to it:
- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
-- LibSSH2 to enable the ssh transport
+- LibSSH2 to enable the SSH transport
- iconv (OSX) to handle the HFS+ path encoding peculiarities
Building libgit2 - Using CMake
@@ -119,8 +119,7 @@ You need to run the CMake commands from the Visual Studio command
prompt, not the regular or Windows SDK one. Select the right generator
for your version with the `-G "Visual Studio X" option.
-See [the wiki]
-(https://github.com/libgit2/libgit2/wiki/Building-libgit2-on-Windows)
+See [the website](https://libgit2.github.com/docs/guides/build-and-link)
for more detailed instructions.
Android
@@ -168,6 +167,8 @@ Here are the bindings to libgit2 that are currently available:
* hgit2 <https://github.com/fpco/gitlib>
* Java
* Jagged <https://github.com/ethomson/jagged>
+* Julia
+ * LibGit2.jl <https://github.com/jakebolewski/LibGit2.jl>
* Lua
* luagit2 <https://github.com/libgit2/luagit2>
* .NET
@@ -182,15 +183,19 @@ Here are the bindings to libgit2 that are currently available:
* Parrot Virtual Machine
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
* Perl
- * Git-Raw <https://github.com/ghedo/p5-Git-Raw>
+ * Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
* PHP
* php-git <https://github.com/libgit2/php-git>
* PowerShell
* GitPowerShell <https://github.com/ethomson/gitpowershell>
* Python
* pygit2 <https://github.com/libgit2/pygit2>
+* R
+ * git2r <https://github.com/ropensci/git2r>
* Ruby
* Rugged <https://github.com/libgit2/rugged>
+* Rust
+ * git2-rs <https://github.com/alexcrichton/git2-rs>
* Vala
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
diff --git a/cmake/Modules/FindGSSAPI.cmake b/cmake/Modules/FindGSSAPI.cmake
new file mode 100644
index 000000000..8520d35df
--- /dev/null
+++ b/cmake/Modules/FindGSSAPI.cmake
@@ -0,0 +1,324 @@
+# - Try to find GSSAPI
+# Once done this will define
+#
+# KRB5_CONFIG - Path to krb5-config
+# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI
+#
+# Read-Only variables:
+# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found
+# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found
+# GSSAPI_FOUND - system has GSSAPI
+# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
+# GSSAPI_LIBRARIES - Link these to use GSSAPI
+# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
+#
+#=============================================================================
+# Copyright (c) 2013 Andreas Schneider <asn@cryptomilk.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+#
+
+find_path(GSSAPI_ROOT_DIR
+ NAMES
+ include/gssapi.h
+ include/gssapi/gssapi.h
+ HINTS
+ ${_GSSAPI_ROOT_HINTS}
+ PATHS
+ ${_GSSAPI_ROOT_PATHS}
+)
+mark_as_advanced(GSSAPI_ROOT_DIR)
+
+if (UNIX)
+ find_program(KRB5_CONFIG
+ NAMES
+ krb5-config
+ PATHS
+ ${GSSAPI_ROOT_DIR}/bin
+ /opt/local/bin)
+ mark_as_advanced(KRB5_CONFIG)
+
+ if (KRB5_CONFIG)
+ # Check if we have MIT KRB5
+ execute_process(
+ COMMAND
+ ${KRB5_CONFIG} --vendor
+ RESULT_VARIABLE
+ _GSSAPI_VENDOR_RESULT
+ OUTPUT_VARIABLE
+ _GSSAPI_VENDOR_STRING)
+
+ if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*")
+ set(GSSAPI_FLAVOR_MIT TRUE)
+ else()
+ execute_process(
+ COMMAND
+ ${KRB5_CONFIG} --libs gssapi
+ RESULT_VARIABLE
+ _GSSAPI_LIBS_RESULT
+ OUTPUT_VARIABLE
+ _GSSAPI_LIBS_STRING)
+
+ if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*")
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ endif()
+ endif()
+
+ # Get the include dir
+ execute_process(
+ COMMAND
+ ${KRB5_CONFIG} --cflags gssapi
+ RESULT_VARIABLE
+ _GSSAPI_INCLUDE_RESULT
+ OUTPUT_VARIABLE
+ _GSSAPI_INCLUDE_STRING)
+ string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}")
+ string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}")
+ endif()
+
+ if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL)
+ # Check for HEIMDAL
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(_GSSAPI heimdal-gssapi)
+ endif (PKG_CONFIG_FOUND)
+
+ if (_GSSAPI_FOUND)
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ else()
+ find_path(_GSSAPI_ROKEN
+ NAMES
+ roken.h
+ PATHS
+ ${GSSAPI_ROOT_DIR}/include
+ ${_GSSAPI_INCLUDEDIR})
+ if (_GSSAPI_ROKEN)
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ endif()
+ endif ()
+ endif()
+endif (UNIX)
+
+find_path(GSSAPI_INCLUDE_DIR
+ NAMES
+ gssapi.h
+ gssapi/gssapi.h
+ PATHS
+ ${GSSAPI_ROOT_DIR}/include
+ ${_GSSAPI_INCLUDEDIR}
+)
+
+if (GSSAPI_FLAVOR_MIT)
+ find_library(GSSAPI_LIBRARY
+ NAMES
+ gssapi_krb5
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(KRB5_LIBRARY
+ NAMES
+ krb5
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(K5CRYPTO_LIBRARY
+ NAMES
+ k5crypto
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(COM_ERR_LIBRARY
+ NAMES
+ com_err
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ if (GSSAPI_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${GSSAPI_LIBRARY}
+ )
+ endif (GSSAPI_LIBRARY)
+
+ if (KRB5_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${KRB5_LIBRARY}
+ )
+ endif (KRB5_LIBRARY)
+
+ if (K5CRYPTO_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${K5CRYPTO_LIBRARY}
+ )
+ endif (K5CRYPTO_LIBRARY)
+
+ if (COM_ERR_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${COM_ERR_LIBRARY}
+ )
+ endif (COM_ERR_LIBRARY)
+endif (GSSAPI_FLAVOR_MIT)
+
+if (GSSAPI_FLAVOR_HEIMDAL)
+ find_library(GSSAPI_LIBRARY
+ NAMES
+ gssapi
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(KRB5_LIBRARY
+ NAMES
+ krb5
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(HCRYPTO_LIBRARY
+ NAMES
+ hcrypto
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(COM_ERR_LIBRARY
+ NAMES
+ com_err
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(HEIMNTLM_LIBRARY
+ NAMES
+ heimntlm
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(HX509_LIBRARY
+ NAMES
+ hx509
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(ASN1_LIBRARY
+ NAMES
+ asn1
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(WIND_LIBRARY
+ NAMES
+ wind
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ find_library(ROKEN_LIBRARY
+ NAMES
+ roken
+ PATHS
+ ${GSSAPI_ROOT_DIR}/lib
+ ${_GSSAPI_LIBDIR}
+ )
+
+ if (GSSAPI_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${GSSAPI_LIBRARY}
+ )
+ endif (GSSAPI_LIBRARY)
+
+ if (KRB5_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${KRB5_LIBRARY}
+ )
+ endif (KRB5_LIBRARY)
+
+ if (HCRYPTO_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${HCRYPTO_LIBRARY}
+ )
+ endif (HCRYPTO_LIBRARY)
+
+ if (COM_ERR_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${COM_ERR_LIBRARY}
+ )
+ endif (COM_ERR_LIBRARY)
+
+ if (HEIMNTLM_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${HEIMNTLM_LIBRARY}
+ )
+ endif (HEIMNTLM_LIBRARY)
+
+ if (HX509_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${HX509_LIBRARY}
+ )
+ endif (HX509_LIBRARY)
+
+ if (ASN1_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${ASN1_LIBRARY}
+ )
+ endif (ASN1_LIBRARY)
+
+ if (WIND_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${WIND_LIBRARY}
+ )
+ endif (WIND_LIBRARY)
+
+ if (ROKEN_LIBRARY)
+ set(GSSAPI_LIBRARIES
+ ${GSSAPI_LIBRARIES}
+ ${WIND_LIBRARY}
+ )
+ endif (ROKEN_LIBRARY)
+endif (GSSAPI_FLAVOR_HEIMDAL)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
+
+if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
+ set(GSSAPI_FOUND TRUE)
+endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
+
+# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
+mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
diff --git a/deps/http-parser/http_parser.h b/deps/http-parser/http_parser.h
index 4f20396c6..67e1d95dd 100644
--- a/deps/http-parser/http_parser.h
+++ b/deps/http-parser/http_parser.h
@@ -40,6 +40,8 @@ typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef SIZE_T size_t;
typedef SSIZE_T ssize_t;
+#elif defined(__sun) || defined(__sun__)
+#include <sys/inttypes.h>
#else
#include <stdint.h>
#endif
diff --git a/deps/zlib/adler32.c b/deps/zlib/adler32.c
index 65ad6a5ad..a868f073d 100644
--- a/deps/zlib/adler32.c
+++ b/deps/zlib/adler32.c
@@ -1,5 +1,5 @@
/* adler32.c -- compute the Adler-32 checksum of a data stream
- * Copyright (C) 1995-2007 Mark Adler
+ * Copyright (C) 1995-2011 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -9,9 +9,9 @@
#define local static
-local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2);
+local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
-#define BASE 65521UL /* largest prime smaller than 65536 */
+#define BASE 65521 /* largest prime smaller than 65536 */
#define NMAX 5552
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
@@ -21,39 +21,44 @@ local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2);
#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
#define DO16(buf) DO8(buf,0); DO8(buf,8);
-/* use NO_DIVIDE if your processor does not do division in hardware */
+/* use NO_DIVIDE if your processor does not do division in hardware --
+ try it both ways to see which is faster */
#ifdef NO_DIVIDE
-# define MOD(a) \
+/* note that this assumes BASE is 65521, where 65536 % 65521 == 15
+ (thank you to John Reiser for pointing this out) */
+# define CHOP(a) \
+ do { \
+ unsigned long tmp = a >> 16; \
+ a &= 0xffffUL; \
+ a += (tmp << 4) - tmp; \
+ } while (0)
+# define MOD28(a) \
do { \
- if (a >= (BASE << 16)) a -= (BASE << 16); \
- if (a >= (BASE << 15)) a -= (BASE << 15); \
- if (a >= (BASE << 14)) a -= (BASE << 14); \
- if (a >= (BASE << 13)) a -= (BASE << 13); \
- if (a >= (BASE << 12)) a -= (BASE << 12); \
- if (a >= (BASE << 11)) a -= (BASE << 11); \
- if (a >= (BASE << 10)) a -= (BASE << 10); \
- if (a >= (BASE << 9)) a -= (BASE << 9); \
- if (a >= (BASE << 8)) a -= (BASE << 8); \
- if (a >= (BASE << 7)) a -= (BASE << 7); \
- if (a >= (BASE << 6)) a -= (BASE << 6); \
- if (a >= (BASE << 5)) a -= (BASE << 5); \
- if (a >= (BASE << 4)) a -= (BASE << 4); \
- if (a >= (BASE << 3)) a -= (BASE << 3); \
- if (a >= (BASE << 2)) a -= (BASE << 2); \
- if (a >= (BASE << 1)) a -= (BASE << 1); \
+ CHOP(a); \
if (a >= BASE) a -= BASE; \
} while (0)
-# define MOD4(a) \
+# define MOD(a) \
do { \
- if (a >= (BASE << 4)) a -= (BASE << 4); \
- if (a >= (BASE << 3)) a -= (BASE << 3); \
- if (a >= (BASE << 2)) a -= (BASE << 2); \
- if (a >= (BASE << 1)) a -= (BASE << 1); \
+ CHOP(a); \
+ MOD28(a); \
+ } while (0)
+# define MOD63(a) \
+ do { /* this assumes a is not negative */ \
+ z_off64_t tmp = a >> 32; \
+ a &= 0xffffffffL; \
+ a += (tmp << 8) - (tmp << 5) + tmp; \
+ tmp = a >> 16; \
+ a &= 0xffffL; \
+ a += (tmp << 4) - tmp; \
+ tmp = a >> 16; \
+ a &= 0xffffL; \
+ a += (tmp << 4) - tmp; \
if (a >= BASE) a -= BASE; \
} while (0)
#else
# define MOD(a) a %= BASE
-# define MOD4(a) a %= BASE
+# define MOD28(a) a %= BASE
+# define MOD63(a) a %= BASE
#endif
/* ========================================================================= */
@@ -92,7 +97,7 @@ uLong ZEXPORT adler32(adler, buf, len)
}
if (adler >= BASE)
adler -= BASE;
- MOD4(sum2); /* only added so many BASE's */
+ MOD28(sum2); /* only added so many BASE's */
return adler | (sum2 << 16);
}
@@ -137,8 +142,13 @@ local uLong adler32_combine_(adler1, adler2, len2)
unsigned long sum2;
unsigned rem;
+ /* for negative len, return invalid adler32 as a clue for debugging */
+ if (len2 < 0)
+ return 0xffffffffUL;
+
/* the derivation of this formula is left as an exercise for the reader */
- rem = (unsigned)(len2 % BASE);
+ MOD63(len2); /* assumes len2 >= 0 */
+ rem = (unsigned)len2;
sum1 = adler1 & 0xffff;
sum2 = rem * sum1;
MOD(sum2);
diff --git a/deps/zlib/crc32.c b/deps/zlib/crc32.c
index 91be372d2..979a7190a 100644
--- a/deps/zlib/crc32.c
+++ b/deps/zlib/crc32.c
@@ -1,5 +1,5 @@
/* crc32.c -- compute the CRC-32 of a data stream
- * Copyright (C) 1995-2006, 2010 Mark Adler
+ * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*
* Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
@@ -17,6 +17,8 @@
of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
first call get_crc_table() to initialize the tables before allowing more than
one thread to use crc32().
+
+ DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
*/
#ifdef MAKECRCH
@@ -30,31 +32,11 @@
#define local static
-/* Find a four-byte integer type for crc32_little() and crc32_big(). */
-#ifndef NOBYFOUR
-# ifdef STDC /* need ANSI C limits.h to determine sizes */
-# include <limits.h>
-# define BYFOUR
-# if (UINT_MAX == 0xffffffffUL)
- typedef unsigned int u4;
-# else
-# if (ULONG_MAX == 0xffffffffUL)
- typedef unsigned long u4;
-# else
-# if (USHRT_MAX == 0xffffffffUL)
- typedef unsigned short u4;
-# else
-# undef BYFOUR /* can't find a four-byte integer type! */
-# endif
-# endif
-# endif
-# endif /* STDC */
-#endif /* !NOBYFOUR */
-
/* Definitions for doing the crc four data bytes at a time. */
+#if !defined(NOBYFOUR) && defined(Z_U4)
+# define BYFOUR
+#endif
#ifdef BYFOUR
-# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \
- (((w)&0xff00)<<8)+(((w)&0xff)<<24))
local unsigned long crc32_little OF((unsigned long,
const unsigned char FAR *, unsigned));
local unsigned long crc32_big OF((unsigned long,
@@ -68,16 +50,16 @@
local unsigned long gf2_matrix_times OF((unsigned long *mat,
unsigned long vec));
local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
-local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2);
+local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));
#ifdef DYNAMIC_CRC_TABLE
local volatile int crc_table_empty = 1;
-local unsigned long FAR crc_table[TBLS][256];
+local z_crc_t FAR crc_table[TBLS][256];
local void make_crc_table OF((void));
#ifdef MAKECRCH
- local void write_table OF((FILE *, const unsigned long FAR *));
+ local void write_table OF((FILE *, const z_crc_t FAR *));
#endif /* MAKECRCH */
/*
Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
@@ -107,9 +89,9 @@ local void make_crc_table OF((void));
*/
local void make_crc_table()
{
- unsigned long c;
+ z_crc_t c;
int n, k;
- unsigned long poly; /* polynomial exclusive-or pattern */
+ z_crc_t poly; /* polynomial exclusive-or pattern */
/* terms of polynomial defining this crc (except x^32): */
static volatile int first = 1; /* flag to limit concurrent making */
static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
@@ -121,13 +103,13 @@ local void make_crc_table()
first = 0;
/* make exclusive-or pattern from polynomial (0xedb88320UL) */
- poly = 0UL;
- for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
- poly |= 1UL << (31 - p[n]);
+ poly = 0;
+ for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
+ poly |= (z_crc_t)1 << (31 - p[n]);
/* generate a crc for every 8-bit value */
for (n = 0; n < 256; n++) {
- c = (unsigned long)n;
+ c = (z_crc_t)n;
for (k = 0; k < 8; k++)
c = c & 1 ? poly ^ (c >> 1) : c >> 1;
crc_table[0][n] = c;
@@ -138,11 +120,11 @@ local void make_crc_table()
and then the byte reversal of those as well as the first table */
for (n = 0; n < 256; n++) {
c = crc_table[0][n];
- crc_table[4][n] = REV(c);
+ crc_table[4][n] = ZSWAP32(c);
for (k = 1; k < 4; k++) {
c = crc_table[0][c & 0xff] ^ (c >> 8);
crc_table[k][n] = c;
- crc_table[k + 4][n] = REV(c);
+ crc_table[k + 4][n] = ZSWAP32(c);
}
}
#endif /* BYFOUR */
@@ -164,7 +146,7 @@ local void make_crc_table()
if (out == NULL) return;
fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
- fprintf(out, "local const unsigned long FAR ");
+ fprintf(out, "local const z_crc_t FAR ");
fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
write_table(out, crc_table[0]);
# ifdef BYFOUR
@@ -184,12 +166,13 @@ local void make_crc_table()
#ifdef MAKECRCH
local void write_table(out, table)
FILE *out;
- const unsigned long FAR *table;
+ const z_crc_t FAR *table;
{
int n;
for (n = 0; n < 256; n++)
- fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n],
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ",
+ (unsigned long)(table[n]),
n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
}
#endif /* MAKECRCH */
@@ -204,13 +187,13 @@ local void write_table(out, table)
/* =========================================================================
* This function can be used by asm versions of crc32()
*/
-const unsigned long FAR * ZEXPORT get_crc_table()
+const z_crc_t FAR * ZEXPORT get_crc_table()
{
#ifdef DYNAMIC_CRC_TABLE
if (crc_table_empty)
make_crc_table();
#endif /* DYNAMIC_CRC_TABLE */
- return (const unsigned long FAR *)crc_table;
+ return (const z_crc_t FAR *)crc_table;
}
/* ========================================================================= */
@@ -232,7 +215,7 @@ unsigned long ZEXPORT crc32(crc, buf, len)
#ifdef BYFOUR
if (sizeof(void *) == sizeof(ptrdiff_t)) {
- u4 endian;
+ z_crc_t endian;
endian = 1;
if (*((unsigned char *)(&endian)))
@@ -266,17 +249,17 @@ local unsigned long crc32_little(crc, buf, len)
const unsigned char FAR *buf;
unsigned len;
{
- register u4 c;
- register const u4 FAR *buf4;
+ register z_crc_t c;
+ register const z_crc_t FAR *buf4;
- c = (u4)crc;
+ c = (z_crc_t)crc;
c = ~c;
while (len && ((ptrdiff_t)buf & 3)) {
c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
len--;
}
- buf4 = (const u4 FAR *)(const void FAR *)buf;
+ buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
while (len >= 32) {
DOLIT32;
len -= 32;
@@ -306,17 +289,17 @@ local unsigned long crc32_big(crc, buf, len)
const unsigned char FAR *buf;
unsigned len;
{
- register u4 c;
- register const u4 FAR *buf4;
+ register z_crc_t c;
+ register const z_crc_t FAR *buf4;
- c = REV((u4)crc);
+ c = ZSWAP32((z_crc_t)crc);
c = ~c;
while (len && ((ptrdiff_t)buf & 3)) {
c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
len--;
}
- buf4 = (const u4 FAR *)(const void FAR *)buf;
+ buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
buf4--;
while (len >= 32) {
DOBIG32;
@@ -333,7 +316,7 @@ local unsigned long crc32_big(crc, buf, len)
c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
} while (--len);
c = ~c;
- return (unsigned long)(REV(c));
+ return (unsigned long)(ZSWAP32(c));
}
#endif /* BYFOUR */
diff --git a/deps/zlib/crc32.h b/deps/zlib/crc32.h
index 8053b6117..9e0c77810 100644
--- a/deps/zlib/crc32.h
+++ b/deps/zlib/crc32.h
@@ -2,7 +2,7 @@
* Generated automatically by crc32.c
*/
-local const unsigned long FAR crc_table[TBLS][256] =
+local const z_crc_t FAR crc_table[TBLS][256] =
{
{
0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
diff --git a/deps/zlib/deflate.c b/deps/zlib/deflate.c
index 5c4022f3d..696957705 100644
--- a/deps/zlib/deflate.c
+++ b/deps/zlib/deflate.c
@@ -1,5 +1,5 @@
/* deflate.c -- compress data using the deflation algorithm
- * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -37,7 +37,7 @@
* REFERENCES
*
* Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
- * Available in http://www.ietf.org/rfc/rfc1951.txt
+ * Available in http://tools.ietf.org/html/rfc1951
*
* A description of the Rabin and Karp algorithm is given in the book
* "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
@@ -52,7 +52,7 @@
#include "deflate.h"
const char deflate_copyright[] =
- " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler ";
+ " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@@ -155,6 +155,9 @@ local const config configuration_table[10] = {
struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
#endif
+/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
+#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0))
+
/* ===========================================================================
* Update a hash value with the given input byte
* IN assertion: all calls to to UPDATE_HASH are made with consecutive
@@ -235,10 +238,19 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
strm->msg = Z_NULL;
if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
strm->zalloc = zcalloc;
strm->opaque = (voidpf)0;
+#endif
}
- if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zfree = zcfree;
+#endif
#ifdef FASTEST
if (level != 0) level = 1;
@@ -293,7 +305,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
s->pending_buf == Z_NULL) {
s->status = FINISH_STATE;
- strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ strm->msg = ERR_MSG(Z_MEM_ERROR);
deflateEnd (strm);
return Z_MEM_ERROR;
}
@@ -314,43 +326,70 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
uInt dictLength;
{
deflate_state *s;
- uInt length = dictLength;
- uInt n;
- IPos hash_head = 0;
+ uInt str, n;
+ int wrap;
+ unsigned avail;
+ z_const unsigned char *next;
- if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
- strm->state->wrap == 2 ||
- (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL)
return Z_STREAM_ERROR;
-
s = strm->state;
- if (s->wrap)
- strm->adler = adler32(strm->adler, dictionary, dictLength);
+ wrap = s->wrap;
+ if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
+ return Z_STREAM_ERROR;
- if (length < MIN_MATCH) return Z_OK;
- if (length > s->w_size) {
- length = s->w_size;
- dictionary += dictLength - length; /* use the tail of the dictionary */
+ /* when using zlib wrappers, compute Adler-32 for provided dictionary */
+ if (wrap == 1)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+ s->wrap = 0; /* avoid computing Adler-32 in read_buf */
+
+ /* if dictionary would fill window, just replace the history */
+ if (dictLength >= s->w_size) {
+ if (wrap == 0) { /* already empty otherwise */
+ CLEAR_HASH(s);
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->insert = 0;
+ }
+ dictionary += dictLength - s->w_size; /* use the tail */
+ dictLength = s->w_size;
}
- zmemcpy(s->window, dictionary, length);
- s->strstart = length;
- s->block_start = (long)length;
- /* Insert all strings in the hash table (except for the last two bytes).
- * s->lookahead stays null, so s->ins_h will be recomputed at the next
- * call of fill_window.
- */
- s->ins_h = s->window[0];
- UPDATE_HASH(s, s->ins_h, s->window[1]);
- for (n = 0; n <= length - MIN_MATCH; n++) {
- INSERT_STRING(s, n, hash_head);
+ /* insert dictionary into window and hash */
+ avail = strm->avail_in;
+ next = strm->next_in;
+ strm->avail_in = dictLength;
+ strm->next_in = (z_const Bytef *)dictionary;
+ fill_window(s);
+ while (s->lookahead >= MIN_MATCH) {
+ str = s->strstart;
+ n = s->lookahead - (MIN_MATCH-1);
+ do {
+ UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+ s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+ s->head[s->ins_h] = (Pos)str;
+ str++;
+ } while (--n);
+ s->strstart = str;
+ s->lookahead = MIN_MATCH-1;
+ fill_window(s);
}
- if (hash_head) hash_head = 0; /* to make compiler happy */
+ s->strstart += s->lookahead;
+ s->block_start = (long)s->strstart;
+ s->insert = s->lookahead;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ strm->next_in = next;
+ strm->avail_in = avail;
+ s->wrap = wrap;
return Z_OK;
}
/* ========================================================================= */
-int ZEXPORT deflateReset (strm)
+int ZEXPORT deflateResetKeep (strm)
z_streamp strm;
{
deflate_state *s;
@@ -380,12 +419,23 @@ int ZEXPORT deflateReset (strm)
s->last_flush = Z_NO_FLUSH;
_tr_init(s);
- lm_init(s);
return Z_OK;
}
/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ int ret;
+
+ ret = deflateResetKeep(strm);
+ if (ret == Z_OK)
+ lm_init(strm->state);
+ return ret;
+}
+
+/* ========================================================================= */
int ZEXPORT deflateSetHeader (strm, head)
z_streamp strm;
gz_headerp head;
@@ -397,14 +447,42 @@ int ZEXPORT deflateSetHeader (strm, head)
}
/* ========================================================================= */
+int ZEXPORT deflatePending (strm, pending, bits)
+ unsigned *pending;
+ int *bits;
+ z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ if (pending != Z_NULL)
+ *pending = strm->state->pending;
+ if (bits != Z_NULL)
+ *bits = strm->state->bi_valid;
+ return Z_OK;
+}
+
+/* ========================================================================= */
int ZEXPORT deflatePrime (strm, bits, value)
z_streamp strm;
int bits;
int value;
{
+ deflate_state *s;
+ int put;
+
if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
- strm->state->bi_valid = bits;
- strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+ s = strm->state;
+ if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
+ return Z_BUF_ERROR;
+ do {
+ put = Buf_size - s->bi_valid;
+ if (put > bits)
+ put = bits;
+ s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
+ s->bi_valid += put;
+ _tr_flush_bits(s);
+ value >>= put;
+ bits -= put;
+ } while (bits);
return Z_OK;
}
@@ -435,6 +513,8 @@ int ZEXPORT deflateParams(strm, level, strategy)
strm->total_in != 0) {
/* Flush the last buffer: */
err = deflate(strm, Z_BLOCK);
+ if (err == Z_BUF_ERROR && s->pending == 0)
+ err = Z_OK;
}
if (s->level != level) {
s->level = level;
@@ -562,19 +642,22 @@ local void putShortMSB (s, b)
local void flush_pending(strm)
z_streamp strm;
{
- unsigned len = strm->state->pending;
+ unsigned len;
+ deflate_state *s = strm->state;
+ _tr_flush_bits(s);
+ len = s->pending;
if (len > strm->avail_out) len = strm->avail_out;
if (len == 0) return;
- zmemcpy(strm->next_out, strm->state->pending_out, len);
+ zmemcpy(strm->next_out, s->pending_out, len);
strm->next_out += len;
- strm->state->pending_out += len;
+ s->pending_out += len;
strm->total_out += len;
strm->avail_out -= len;
- strm->state->pending -= len;
- if (strm->state->pending == 0) {
- strm->state->pending_out = strm->state->pending_buf;
+ s->pending -= len;
+ if (s->pending == 0) {
+ s->pending_out = s->pending_buf;
}
}
@@ -801,7 +884,7 @@ int ZEXPORT deflate (strm, flush)
* flushes. For repeated and useless calls with Z_FINISH, we keep
* returning Z_STREAM_END instead of Z_BUF_ERROR.
*/
- } else if (strm->avail_in == 0 && flush <= old_flush &&
+ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
flush != Z_FINISH) {
ERR_RETURN(strm, Z_BUF_ERROR);
}
@@ -850,6 +933,7 @@ int ZEXPORT deflate (strm, flush)
if (s->lookahead == 0) {
s->strstart = 0;
s->block_start = 0L;
+ s->insert = 0;
}
}
}
@@ -945,12 +1029,12 @@ int ZEXPORT deflateCopy (dest, source)
ss = source->state;
- zmemcpy(dest, source, sizeof(z_stream));
+ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
if (ds == Z_NULL) return Z_MEM_ERROR;
dest->state = (struct internal_state FAR *) ds;
- zmemcpy(ds, ss, sizeof(deflate_state));
+ zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
ds->strm = dest;
ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
@@ -966,8 +1050,8 @@ int ZEXPORT deflateCopy (dest, source)
}
/* following zmemcpy do not work for 16-bit MSDOS */
zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
- zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
- zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
@@ -1001,15 +1085,15 @@ local int read_buf(strm, buf, size)
strm->avail_in -= len;
+ zmemcpy(buf, strm->next_in, len);
if (strm->state->wrap == 1) {
- strm->adler = adler32(strm->adler, strm->next_in, len);
+ strm->adler = adler32(strm->adler, buf, len);
}
#ifdef GZIP
else if (strm->state->wrap == 2) {
- strm->adler = crc32(strm->adler, strm->next_in, len);
+ strm->adler = crc32(strm->adler, buf, len);
}
#endif
- zmemcpy(buf, strm->next_in, len);
strm->next_in += len;
strm->total_in += len;
@@ -1036,6 +1120,7 @@ local void lm_init (s)
s->strstart = 0;
s->block_start = 0L;
s->lookahead = 0;
+ s->insert = 0;
s->match_length = s->prev_length = MIN_MATCH-1;
s->match_available = 0;
s->ins_h = 0;
@@ -1310,6 +1395,8 @@ local void fill_window(s)
unsigned more; /* Amount of free space at the end of the window. */
uInt wsize = s->w_size;
+ Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
+
do {
more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
@@ -1362,7 +1449,7 @@ local void fill_window(s)
#endif
more += wsize;
}
- if (s->strm->avail_in == 0) return;
+ if (s->strm->avail_in == 0) break;
/* If there was no sliding:
* strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
@@ -1381,12 +1468,24 @@ local void fill_window(s)
s->lookahead += n;
/* Initialize the hash value now that we have some input: */
- if (s->lookahead >= MIN_MATCH) {
- s->ins_h = s->window[s->strstart];
- UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+ if (s->lookahead + s->insert >= MIN_MATCH) {
+ uInt str = s->strstart - s->insert;
+ s->ins_h = s->window[str];
+ UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
#if MIN_MATCH != 3
Call UPDATE_HASH() MIN_MATCH-3 more times
#endif
+ while (s->insert) {
+ UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+ s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+ s->head[s->ins_h] = (Pos)str;
+ str++;
+ s->insert--;
+ if (s->lookahead + s->insert < MIN_MATCH)
+ break;
+ }
}
/* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
* but this is not important since only literal bytes will be emitted.
@@ -1427,6 +1526,9 @@ local void fill_window(s)
s->high_water += init;
}
}
+
+ Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+ "not enough room for search");
}
/* ===========================================================================
@@ -1506,8 +1608,14 @@ local block_state deflate_stored(s, flush)
FLUSH_BLOCK(s, 0);
}
}
- FLUSH_BLOCK(s, flush == Z_FINISH);
- return flush == Z_FINISH ? finish_done : block_done;
+ s->insert = 0;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if ((long)s->strstart > s->block_start)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
}
/* ===========================================================================
@@ -1603,8 +1711,14 @@ local block_state deflate_fast(s, flush)
}
if (bflush) FLUSH_BLOCK(s, 0);
}
- FLUSH_BLOCK(s, flush == Z_FINISH);
- return flush == Z_FINISH ? finish_done : block_done;
+ s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->last_lit)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
}
#ifndef FASTEST
@@ -1728,8 +1842,14 @@ local block_state deflate_slow(s, flush)
_tr_tally_lit(s, s->window[s->strstart-1], bflush);
s->match_available = 0;
}
- FLUSH_BLOCK(s, flush == Z_FINISH);
- return flush == Z_FINISH ? finish_done : block_done;
+ s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->last_lit)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
}
#endif /* FASTEST */
@@ -1749,11 +1869,11 @@ local block_state deflate_rle(s, flush)
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
- * for the longest encodable run.
+ * for the longest run, plus one for the unrolled loop.
*/
- if (s->lookahead < MAX_MATCH) {
+ if (s->lookahead <= MAX_MATCH) {
fill_window(s);
- if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) {
+ if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
return need_more;
}
if (s->lookahead == 0) break; /* flush the current block */
@@ -1776,6 +1896,7 @@ local block_state deflate_rle(s, flush)
if (s->match_length > s->lookahead)
s->match_length = s->lookahead;
}
+ Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
}
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
@@ -1796,8 +1917,14 @@ local block_state deflate_rle(s, flush)
}
if (bflush) FLUSH_BLOCK(s, 0);
}
- FLUSH_BLOCK(s, flush == Z_FINISH);
- return flush == Z_FINISH ? finish_done : block_done;
+ s->insert = 0;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->last_lit)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
}
/* ===========================================================================
@@ -1829,6 +1956,12 @@ local block_state deflate_huff(s, flush)
s->strstart++;
if (bflush) FLUSH_BLOCK(s, 0);
}
- FLUSH_BLOCK(s, flush == Z_FINISH);
- return flush == Z_FINISH ? finish_done : block_done;
+ s->insert = 0;
+ if (flush == Z_FINISH) {
+ FLUSH_BLOCK(s, 1);
+ return finish_done;
+ }
+ if (s->last_lit)
+ FLUSH_BLOCK(s, 0);
+ return block_done;
}
diff --git a/deps/zlib/deflate.h b/deps/zlib/deflate.h
index d7d26f8a9..a17c8365c 100644
--- a/deps/zlib/deflate.h
+++ b/deps/zlib/deflate.h
@@ -1,5 +1,5 @@
/* deflate.h -- internal compression state
- * Copyright (C) 1995-2010 Jean-loup Gailly
+ * Copyright (C) 1995-2012 Jean-loup Gailly
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -48,6 +48,9 @@
#define MAX_BITS 15
/* All codes must not exceed MAX_BITS bits */
+#define Buf_size 16
+/* size of bit buffer in bi_buf */
+
#define INIT_STATE 42
#define EXTRA_STATE 69
#define NAME_STATE 73
@@ -101,7 +104,7 @@ typedef struct internal_state {
int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
gz_headerp gzhead; /* gzip header information to write */
uInt gzindex; /* where in extra, name, or comment */
- Byte method; /* STORED (for zip only) or DEFLATED */
+ Byte method; /* can only be DEFLATED */
int last_flush; /* value of flush param for previous deflate call */
/* used by deflate.c: */
@@ -188,7 +191,7 @@ typedef struct internal_state {
int nice_match; /* Stop searching when current match exceeds this */
/* used by trees.c: */
- /* Didn't use ct_data typedef below to supress compiler warning */
+ /* Didn't use ct_data typedef below to suppress compiler warning */
struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
@@ -244,7 +247,7 @@ typedef struct internal_state {
ulg opt_len; /* bit length of current block with optimal trees */
ulg static_len; /* bit length of current block with static trees */
uInt matches; /* number of string matches in current block */
- int last_eob_len; /* bit length of EOB code for last block */
+ uInt insert; /* bytes at end of window left to insert */
#ifdef DEBUG
ulg compressed_len; /* total bit length of compressed file mod 2^32 */
@@ -294,6 +297,7 @@ void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
ulg stored_len, int last));
diff --git a/deps/zlib/infback.c b/deps/zlib/infback.c
new file mode 100644
index 000000000..f3833c2e4
--- /dev/null
+++ b/deps/zlib/infback.c
@@ -0,0 +1,640 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2011 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ This code is largely copied from inflate.c. Normally either infback.o or
+ inflate.o would be linked into an application--not both. The interface
+ with inffast.c is retained so that optimized assembler-coded versions of
+ inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+ strm provides memory allocation functions in zalloc and zfree, or
+ Z_NULL to use the library memory allocation functions.
+
+ windowBits is in the range 8..15, and window is a user-supplied
+ window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_streamp strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL || window == Z_NULL ||
+ windowBits < 8 || windowBits > 15)
+ return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+#endif
+ }
+ if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zfree = zcfree;
+#endif
+ state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+ sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (struct internal_state FAR *)state;
+ state->dmax = 32768U;
+ state->wbits = windowBits;
+ state->wsize = 1U << windowBits;
+ state->window = window;
+ state->wnext = 0;
+ state->whave = 0;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Assure that some input is available. If input is requested, but denied,
+ then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+ do { \
+ if (have == 0) { \
+ have = in(in_desc, &next); \
+ if (have == 0) { \
+ next = Z_NULL; \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+ with an error if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ PULL(); \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflateBack() with
+ an error. */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Assure that some output space is available, by writing out the window
+ if it's full. If the write fails, return from inflateBack() with a
+ Z_BUF_ERROR. */
+#define ROOM() \
+ do { \
+ if (left == 0) { \
+ put = state->window; \
+ left = state->wsize; \
+ state->whave = left; \
+ if (out(out_desc, put, left)) { \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/*
+ strm provides the memory allocation functions and window buffer on input,
+ and provides information on the unused input on return. For Z_DATA_ERROR
+ returns, strm will also provide an error message.
+
+ in() and out() are the call-back input and output functions. When
+ inflateBack() needs more input, it calls in(). When inflateBack() has
+ filled the window with output, or when it completes with data in the
+ window, it calls out() to write out the data. The application must not
+ change the provided input until in() is called again or inflateBack()
+ returns. The application must not change the window/output buffer until
+ inflateBack() returns.
+
+ in() and out() are called with a descriptor parameter provided in the
+ inflateBack() call. This parameter can be a structure that provides the
+ information required to do the read or write, as well as accumulated
+ information on the input and output such as totals and check values.
+
+ in() should return zero on failure. out() should return non-zero on
+ failure. If either in() or out() fails, than inflateBack() returns a
+ Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it
+ was in() or out() that caused in the error. Otherwise, inflateBack()
+ returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+ error, or Z_MEM_ERROR if it could not allocate memory for the state.
+ inflateBack() can also return Z_STREAM_ERROR if the input parameters
+ are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_streamp strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+ struct inflate_state FAR *state;
+ z_const unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code here; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* Check that the strm exists and that the state was initialized */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* Reset the state */
+ strm->msg = Z_NULL;
+ state->mode = TYPE;
+ state->last = 0;
+ state->whave = 0;
+ next = strm->next_in;
+ have = next != Z_NULL ? strm->avail_in : 0;
+ hold = 0;
+ bits = 0;
+ put = state->window;
+ left = state->wsize;
+
+ /* Inflate until end of block marked as last */
+ for (;;)
+ switch (state->mode) {
+ case TYPE:
+ /* determine and dispatch block type */
+ if (state->last) {
+ BYTEBITS();
+ state->mode = DONE;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+
+ case STORED:
+ /* get and verify stored block length */
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+
+ /* copy stored block from input to output */
+ while (state->length != 0) {
+ copy = state->length;
+ PULL();
+ ROOM();
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+
+ case TABLE:
+ /* get dynamic table entries descriptor */
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+
+ /* get code length code lengths (not a typo) */
+ state->have = 0;
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+
+ /* get length and distance code code lengths */
+ state->have = 0;
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.val < 16) {
+ DROPBITS(here.bits);
+ state->lens[state->have++] = here.val;
+ }
+ else {
+ if (here.val == 16) {
+ NEEDBITS(here.bits + 2);
+ DROPBITS(here.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = (unsigned)(state->lens[state->have - 1]);
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (here.val == 17) {
+ NEEDBITS(here.bits + 3);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(here.bits + 7);
+ DROPBITS(here.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* check for end-of-block code (better have one) */
+ if (state->lens[256] == 0) {
+ strm->msg = (char *)"invalid code -- missing end-of-block";
+ state->mode = BAD;
+ break;
+ }
+
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.h
+ concerning the ENOUGH constants, which depend on those values */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+
+ case LEN:
+ /* use inflate_fast() if we have enough input and output */
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ if (state->whave < state->wsize)
+ state->whave = state->wsize - left;
+ inflate_fast(strm, state->wsize);
+ LOAD();
+ break;
+ }
+
+ /* get a literal, length, or end-of-block code */
+ for (;;) {
+ here = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (here.op && (here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(here.bits);
+ state->length = (unsigned)here.val;
+
+ /* process literal */
+ if (here.op == 0) {
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ ROOM();
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ }
+
+ /* process end of block */
+ if (here.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+
+ /* invalid code */
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+
+ /* length code -- get extra bits, if any */
+ state->extra = (unsigned)(here.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+
+ /* get distance code */
+ for (;;) {
+ here = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((here.op & 0xf0) == 0) {
+ last = here;
+ for (;;) {
+ here = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + here.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(here.bits);
+ if (here.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)here.val;
+
+ /* get distance extra bits, if any */
+ state->extra = (unsigned)(here.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->wsize - (state->whave < state->wsize ?
+ left : 0)) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+
+ /* copy match from window to output */
+ do {
+ ROOM();
+ copy = state->wsize - state->offset;
+ if (copy < left) {
+ from = put + copy;
+ copy = left - copy;
+ }
+ else {
+ from = put - state->offset;
+ copy = left;
+ }
+ if (copy > state->length) copy = state->length;
+ state->length -= copy;
+ left -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ } while (state->length != 0);
+ break;
+
+ case DONE:
+ /* inflate stream terminated properly -- write leftover output */
+ ret = Z_STREAM_END;
+ if (left < state->wsize) {
+ if (out(out_desc, state->window, state->wsize - left))
+ ret = Z_BUF_ERROR;
+ }
+ goto inf_leave;
+
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+
+ default: /* can't happen, but makes compilers happy */
+ ret = Z_STREAM_ERROR;
+ goto inf_leave;
+ }
+
+ /* Return unused input */
+ inf_leave:
+ strm->next_in = next;
+ strm->avail_in = have;
+ return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
diff --git a/deps/zlib/inffast.c b/deps/zlib/inffast.c
index 2f1d60b43..bda59ceb6 100644
--- a/deps/zlib/inffast.c
+++ b/deps/zlib/inffast.c
@@ -1,5 +1,5 @@
/* inffast.c -- fast decoding
- * Copyright (C) 1995-2008, 2010 Mark Adler
+ * Copyright (C) 1995-2008, 2010, 2013 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -69,8 +69,8 @@ z_streamp strm;
unsigned start; /* inflate()'s starting value for strm->avail_out */
{
struct inflate_state FAR *state;
- unsigned char FAR *in; /* local strm->next_in */
- unsigned char FAR *last; /* while in < last, enough input available */
+ z_const unsigned char FAR *in; /* local strm->next_in */
+ z_const unsigned char FAR *last; /* have enough input while in < last */
unsigned char FAR *out; /* local strm->next_out */
unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
unsigned char FAR *end; /* while out < end, enough space available */
diff --git a/deps/zlib/inffixed.h b/deps/zlib/inffixed.h
index 75ed4b597..d62832776 100644
--- a/deps/zlib/inffixed.h
+++ b/deps/zlib/inffixed.h
@@ -2,9 +2,9 @@
* Generated automatically by makefixed().
*/
- /* WARNING: this file should *not* be used by applications. It
- is part of the implementation of the compression library and
- is subject to change. Applications should only use zlib.h.
+ /* WARNING: this file should *not* be used by applications.
+ It is part of the implementation of this library and is
+ subject to change. Applications should only use zlib.h.
*/
static const code lenfix[512] = {
diff --git a/deps/zlib/inflate.c b/deps/zlib/inflate.c
index a8431abea..870f89bb4 100644
--- a/deps/zlib/inflate.c
+++ b/deps/zlib/inflate.c
@@ -1,5 +1,5 @@
/* inflate.c -- zlib decompression
- * Copyright (C) 1995-2010 Mark Adler
+ * Copyright (C) 1995-2012 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -93,14 +93,15 @@
/* function prototypes */
local void fixedtables OF((struct inflate_state FAR *state));
-local int updatewindow OF((z_streamp strm, unsigned out));
+local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
+ unsigned copy));
#ifdef BUILDFIXED
void makefixed OF((void));
#endif
-local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
unsigned len));
-int ZEXPORT inflateReset(strm)
+int ZEXPORT inflateResetKeep(strm)
z_streamp strm;
{
struct inflate_state FAR *state;
@@ -109,15 +110,13 @@ z_streamp strm;
state = (struct inflate_state FAR *)strm->state;
strm->total_in = strm->total_out = state->total = 0;
strm->msg = Z_NULL;
- strm->adler = 1; /* to support ill-conceived Java test suite */
+ if (state->wrap) /* to support ill-conceived Java test suite */
+ strm->adler = state->wrap & 1;
state->mode = HEAD;
state->last = 0;
state->havedict = 0;
state->dmax = 32768U;
state->head = Z_NULL;
- state->wsize = 0;
- state->whave = 0;
- state->wnext = 0;
state->hold = 0;
state->bits = 0;
state->lencode = state->distcode = state->next = state->codes;
@@ -127,6 +126,19 @@ z_streamp strm;
return Z_OK;
}
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ state->wsize = 0;
+ state->whave = 0;
+ state->wnext = 0;
+ return inflateResetKeep(strm);
+}
+
int ZEXPORT inflateReset2(strm, windowBits)
z_streamp strm;
int windowBits;
@@ -180,10 +192,19 @@ int stream_size;
if (strm == Z_NULL) return Z_STREAM_ERROR;
strm->msg = Z_NULL; /* in case we return an error */
if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
strm->zalloc = zcalloc;
strm->opaque = (voidpf)0;
+#endif
}
- if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+ return Z_STREAM_ERROR;
+#else
+ strm->zfree = zcfree;
+#endif
state = (struct inflate_state FAR *)
ZALLOC(strm, 1, sizeof(struct inflate_state));
if (state == Z_NULL) return Z_MEM_ERROR;
@@ -321,8 +342,8 @@ void makefixed()
low = 0;
for (;;) {
if ((low % 7) == 0) printf("\n ");
- printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
- state.lencode[low].val);
+ printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
+ state.lencode[low].bits, state.lencode[low].val);
if (++low == size) break;
putchar(',');
}
@@ -355,12 +376,13 @@ void makefixed()
output will fall in the output data, making match copies simpler and faster.
The advantage may be dependent on the size of the processor's data caches.
*/
-local int updatewindow(strm, out)
+local int updatewindow(strm, end, copy)
z_streamp strm;
-unsigned out;
+const Bytef *end;
+unsigned copy;
{
struct inflate_state FAR *state;
- unsigned copy, dist;
+ unsigned dist;
state = (struct inflate_state FAR *)strm->state;
@@ -380,19 +402,18 @@ unsigned out;
}
/* copy state->wsize or less output bytes into the circular window */
- copy = out - strm->avail_out;
if (copy >= state->wsize) {
- zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ zmemcpy(state->window, end - state->wsize, state->wsize);
state->wnext = 0;
state->whave = state->wsize;
}
else {
dist = state->wsize - state->wnext;
if (dist > copy) dist = copy;
- zmemcpy(state->window + state->wnext, strm->next_out - copy, dist);
+ zmemcpy(state->window + state->wnext, end - copy, dist);
copy -= dist;
if (copy) {
- zmemcpy(state->window, strm->next_out - copy, copy);
+ zmemcpy(state->window, end - copy, copy);
state->wnext = copy;
state->whave = state->wsize;
}
@@ -499,11 +520,6 @@ unsigned out;
bits -= bits & 7; \
} while (0)
-/* Reverse the bytes in a 32-bit value */
-#define REVERSE(q) \
- ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
- (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
-
/*
inflate() uses a state machine to process as much input data and generate as
much output data as possible before returning. The state machine is
@@ -591,7 +607,7 @@ z_streamp strm;
int flush;
{
struct inflate_state FAR *state;
- unsigned char FAR *next; /* next input */
+ z_const unsigned char FAR *next; /* next input */
unsigned char FAR *put; /* next output */
unsigned have, left; /* available input and output */
unsigned long hold; /* bit buffer */
@@ -797,7 +813,7 @@ int flush;
#endif
case DICTID:
NEEDBITS(32);
- strm->adler = state->check = REVERSE(hold);
+ strm->adler = state->check = ZSWAP32(hold);
INITBITS();
state->mode = DICT;
case DICT:
@@ -905,7 +921,7 @@ int flush;
while (state->have < 19)
state->lens[order[state->have++]] = 0;
state->next = state->codes;
- state->lencode = (code const FAR *)(state->next);
+ state->lencode = (const code FAR *)(state->next);
state->lenbits = 7;
ret = inflate_table(CODES, state->lens, 19, &(state->next),
&(state->lenbits), state->work);
@@ -925,7 +941,6 @@ int flush;
PULLBYTE();
}
if (here.val < 16) {
- NEEDBITS(here.bits);
DROPBITS(here.bits);
state->lens[state->have++] = here.val;
}
@@ -980,7 +995,7 @@ int flush;
values here (9 and 6) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state->next = state->codes;
- state->lencode = (code const FAR *)(state->next);
+ state->lencode = (const code FAR *)(state->next);
state->lenbits = 9;
ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
&(state->lenbits), state->work);
@@ -989,7 +1004,7 @@ int flush;
state->mode = BAD;
break;
}
- state->distcode = (code const FAR *)(state->next);
+ state->distcode = (const code FAR *)(state->next);
state->distbits = 6;
ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
&(state->next), &(state->distbits), state->work);
@@ -1170,7 +1185,7 @@ int flush;
#ifdef GUNZIP
state->flags ? hold :
#endif
- REVERSE(hold)) != state->check) {
+ ZSWAP32(hold)) != state->check) {
strm->msg = (char *)"incorrect data check";
state->mode = BAD;
break;
@@ -1214,8 +1229,9 @@ int flush;
*/
inf_leave:
RESTORE();
- if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
- if (updatewindow(strm, out)) {
+ if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
+ (state->mode < CHECK || flush != Z_FINISH)))
+ if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
state->mode = MEM;
return Z_MEM_ERROR;
}
@@ -1249,13 +1265,37 @@ z_streamp strm;
return Z_OK;
}
+int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+Bytef *dictionary;
+uInt *dictLength;
+{
+ struct inflate_state FAR *state;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* copy dictionary */
+ if (state->whave && dictionary != Z_NULL) {
+ zmemcpy(dictionary, state->window + state->wnext,
+ state->whave - state->wnext);
+ zmemcpy(dictionary + state->whave - state->wnext,
+ state->window, state->wnext);
+ }
+ if (dictLength != Z_NULL)
+ *dictLength = state->whave;
+ return Z_OK;
+}
+
int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
z_streamp strm;
const Bytef *dictionary;
uInt dictLength;
{
struct inflate_state FAR *state;
- unsigned long id;
+ unsigned long dictid;
+ int ret;
/* check state */
if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
@@ -1263,29 +1303,21 @@ uInt dictLength;
if (state->wrap != 0 && state->mode != DICT)
return Z_STREAM_ERROR;
- /* check for correct dictionary id */
+ /* check for correct dictionary identifier */
if (state->mode == DICT) {
- id = adler32(0L, Z_NULL, 0);
- id = adler32(id, dictionary, dictLength);
- if (id != state->check)
+ dictid = adler32(0L, Z_NULL, 0);
+ dictid = adler32(dictid, dictionary, dictLength);
+ if (dictid != state->check)
return Z_DATA_ERROR;
}
- /* copy dictionary to window */
- if (updatewindow(strm, strm->avail_out)) {
+ /* copy dictionary to window using updatewindow(), which will amend the
+ existing dictionary if appropriate */
+ ret = updatewindow(strm, dictionary + dictLength, dictLength);
+ if (ret) {
state->mode = MEM;
return Z_MEM_ERROR;
}
- if (dictLength > state->wsize) {
- zmemcpy(state->window, dictionary + dictLength - state->wsize,
- state->wsize);
- state->whave = state->wsize;
- }
- else {
- zmemcpy(state->window + state->wsize - dictLength, dictionary,
- dictLength);
- state->whave = dictLength;
- }
state->havedict = 1;
Tracev((stderr, "inflate: dictionary set\n"));
return Z_OK;
@@ -1321,7 +1353,7 @@ gz_headerp head;
*/
local unsigned syncsearch(have, buf, len)
unsigned FAR *have;
-unsigned char FAR *buf;
+const unsigned char FAR *buf;
unsigned len;
{
unsigned got;
@@ -1433,8 +1465,8 @@ z_streamp source;
}
/* copy state */
- zmemcpy(dest, source, sizeof(z_stream));
- zmemcpy(copy, state, sizeof(struct inflate_state));
+ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+ zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
if (state->lencode >= state->codes &&
state->lencode <= state->codes + ENOUGH - 1) {
copy->lencode = copy->codes + (state->lencode - state->codes);
diff --git a/deps/zlib/inftrees.c b/deps/zlib/inftrees.c
index 11e9c52ac..44d89cf24 100644
--- a/deps/zlib/inftrees.c
+++ b/deps/zlib/inftrees.c
@@ -1,5 +1,5 @@
/* inftrees.c -- generate Huffman trees for efficient decoding
- * Copyright (C) 1995-2010 Mark Adler
+ * Copyright (C) 1995-2013 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -9,7 +9,7 @@
#define MAXBITS 15
const char inflate_copyright[] =
- " inflate 1.2.5 Copyright 1995-2010 Mark Adler ";
+ " inflate 1.2.8 Copyright 1995-2013 Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@@ -62,7 +62,7 @@ unsigned short FAR *work;
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
- 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195};
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78};
static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
@@ -208,8 +208,8 @@ unsigned short FAR *work;
mask = used - 1; /* mask for comparing low */
/* check available table space */
- if ((type == LENS && used >= ENOUGH_LENS) ||
- (type == DISTS && used >= ENOUGH_DISTS))
+ if ((type == LENS && used > ENOUGH_LENS) ||
+ (type == DISTS && used > ENOUGH_DISTS))
return 1;
/* process all codes and make table entries */
@@ -277,8 +277,8 @@ unsigned short FAR *work;
/* check for enough space */
used += 1U << curr;
- if ((type == LENS && used >= ENOUGH_LENS) ||
- (type == DISTS && used >= ENOUGH_DISTS))
+ if ((type == LENS && used > ENOUGH_LENS) ||
+ (type == DISTS && used > ENOUGH_DISTS))
return 1;
/* point entry in root table to sub-table */
@@ -289,38 +289,14 @@ unsigned short FAR *work;
}
}
- /*
- Fill in rest of table for incomplete codes. This loop is similar to the
- loop above in incrementing huff for table indices. It is assumed that
- len is equal to curr + drop, so there is no loop needed to increment
- through high index bits. When the current sub-table is filled, the loop
- drops back to the root table to fill in any remaining entries there.
- */
- here.op = (unsigned char)64; /* invalid code marker */
- here.bits = (unsigned char)(len - drop);
- here.val = (unsigned short)0;
- while (huff != 0) {
- /* when done with sub-table, drop back to root table */
- if (drop != 0 && (huff & mask) != low) {
- drop = 0;
- len = root;
- next = *table;
- here.bits = (unsigned char)len;
- }
-
- /* put invalid code marker in table */
- next[huff >> drop] = here;
-
- /* backwards increment the len-bit code huff */
- incr = 1U << (len - 1);
- while (huff & incr)
- incr >>= 1;
- if (incr != 0) {
- huff &= incr - 1;
- huff += incr;
- }
- else
- huff = 0;
+ /* fill in remaining table entry if code is incomplete (guaranteed to have
+ at most one remaining entry, since if the code is incomplete, the
+ maximum code length that was allowed to get this far is one bit) */
+ if (huff != 0) {
+ here.op = (unsigned char)64; /* invalid code marker */
+ here.bits = (unsigned char)(len - drop);
+ here.val = (unsigned short)0;
+ next[huff] = here;
}
/* set return parameters */
diff --git a/deps/zlib/trees.c b/deps/zlib/trees.c
index 3e9a138c7..bb866f012 100644
--- a/deps/zlib/trees.c
+++ b/deps/zlib/trees.c
@@ -1,5 +1,5 @@
/* trees.c -- output deflated data using Huffman coding
- * Copyright (C) 1995-2010 Jean-loup Gailly
+ * Copyright (C) 1995-2012 Jean-loup Gailly
* detect_data_type() function provided freely by Cosmin Truta, 2006
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -74,11 +74,6 @@ local const uch bl_order[BL_CODES]
* probability, to avoid transmitting the lengths for unused bit length codes.
*/
-#define Buf_size (8 * 2*sizeof(char))
-/* Number of bits used within bi_buf. (bi_buf might be implemented on
- * more than 16 bits on some systems.)
- */
-
/* ===========================================================================
* Local data. These are initialized only once.
*/
@@ -151,8 +146,8 @@ local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
local int build_bl_tree OF((deflate_state *s));
local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
int blcodes));
-local void compress_block OF((deflate_state *s, ct_data *ltree,
- ct_data *dtree));
+local void compress_block OF((deflate_state *s, const ct_data *ltree,
+ const ct_data *dtree));
local int detect_data_type OF((deflate_state *s));
local unsigned bi_reverse OF((unsigned value, int length));
local void bi_windup OF((deflate_state *s));
@@ -399,7 +394,6 @@ void ZLIB_INTERNAL _tr_init(s)
s->bi_buf = 0;
s->bi_valid = 0;
- s->last_eob_len = 8; /* enough lookahead for inflate */
#ifdef DEBUG
s->compressed_len = 0L;
s->bits_sent = 0L;
@@ -883,15 +877,17 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
}
/* ===========================================================================
+ * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
+ */
+void ZLIB_INTERNAL _tr_flush_bits(s)
+ deflate_state *s;
+{
+ bi_flush(s);
+}
+
+/* ===========================================================================
* Send one empty static block to give enough lookahead for inflate.
* This takes 10 bits, of which 7 may remain in the bit buffer.
- * The current inflate code requires 9 bits of lookahead. If the
- * last two codes for the previous block (real code plus EOB) were coded
- * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
- * the last real code. In this case we send two empty static blocks instead
- * of one. (There are no problems if the previous block is stored or fixed.)
- * To simplify the code, we assume the worst case of last real code encoded
- * on one bit only.
*/
void ZLIB_INTERNAL _tr_align(s)
deflate_state *s;
@@ -902,20 +898,6 @@ void ZLIB_INTERNAL _tr_align(s)
s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
#endif
bi_flush(s);
- /* Of the 10 bits for the empty block, we have already sent
- * (10 - bi_valid) bits. The lookahead for the last real code (before
- * the EOB of the previous block) was thus at least one plus the length
- * of the EOB plus what we have just sent of the empty static block.
- */
- if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
- send_bits(s, STATIC_TREES<<1, 3);
- send_code(s, END_BLOCK, static_ltree);
-#ifdef DEBUG
- s->compressed_len += 10L;
-#endif
- bi_flush(s);
- }
- s->last_eob_len = 7;
}
/* ===========================================================================
@@ -990,7 +972,8 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
} else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
#endif
send_bits(s, (STATIC_TREES<<1)+last, 3);
- compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+ compress_block(s, (const ct_data *)static_ltree,
+ (const ct_data *)static_dtree);
#ifdef DEBUG
s->compressed_len += 3 + s->static_len;
#endif
@@ -998,7 +981,8 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
send_bits(s, (DYN_TREES<<1)+last, 3);
send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
max_blindex+1);
- compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+ compress_block(s, (const ct_data *)s->dyn_ltree,
+ (const ct_data *)s->dyn_dtree);
#ifdef DEBUG
s->compressed_len += 3 + s->opt_len;
#endif
@@ -1075,8 +1059,8 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
*/
local void compress_block(s, ltree, dtree)
deflate_state *s;
- ct_data *ltree; /* literal tree */
- ct_data *dtree; /* distance tree */
+ const ct_data *ltree; /* literal tree */
+ const ct_data *dtree; /* distance tree */
{
unsigned dist; /* distance of matched string */
int lc; /* match length or unmatched char (if dist == 0) */
@@ -1118,7 +1102,6 @@ local void compress_block(s, ltree, dtree)
} while (lx < s->last_lit);
send_code(s, END_BLOCK, ltree);
- s->last_eob_len = ltree[END_BLOCK].Len;
}
/* ===========================================================================
@@ -1226,7 +1209,6 @@ local void copy_block(s, buf, len, header)
int header; /* true if block header must be written */
{
bi_windup(s); /* align on byte boundary */
- s->last_eob_len = 8; /* enough lookahead for inflate */
if (header) {
put_short(s, (ush)len);
diff --git a/deps/zlib/zconf.h b/deps/zlib/zconf.h
index 150814361..229c40024 100644
--- a/deps/zlib/zconf.h
+++ b/deps/zlib/zconf.h
@@ -14,6 +14,7 @@
* forms, we didn't write zlib */
#if defined(_MSC_VER)
# pragma warning( disable : 4131 )
+# pragma warning( disable : 4142 ) /* benign redefinition of type */
#endif
/* Maximum value for memLevel in deflateInit2 */
@@ -33,10 +34,12 @@
# define FAR
#endif
#define OF(args) args
+#define Z_ARG(args) args
typedef unsigned char Byte; /* 8 bits */
typedef unsigned int uInt; /* 16 bits or more */
typedef unsigned long uLong; /* 32 bits or more */
+typedef unsigned long z_crc_t;
typedef Byte FAR Bytef;
typedef char FAR charf;
@@ -50,5 +53,6 @@ typedef void *voidp;
#define z_off_t git_off_t
#define z_off64_t z_off_t
+#define z_const const
#endif /* ZCONF_H */
diff --git a/deps/zlib/zlib.h b/deps/zlib/zlib.h
index bfbba83e8..3e0c7672a 100644
--- a/deps/zlib/zlib.h
+++ b/deps/zlib/zlib.h
@@ -1,7 +1,7 @@
/* zlib.h -- interface of the 'zlib' general purpose compression library
- version 1.2.5, April 19th, 2010
+ version 1.2.8, April 28th, 2013
- Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -24,8 +24,8 @@
The data format used by the zlib library is described by RFCs (Request for
- Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
- (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
*/
#ifndef ZLIB_H
@@ -37,11 +37,11 @@
extern "C" {
#endif
-#define ZLIB_VERSION "1.2.5"
-#define ZLIB_VERNUM 0x1250
+#define ZLIB_VERSION "1.2.8"
+#define ZLIB_VERNUM 0x1280
#define ZLIB_VER_MAJOR 1
#define ZLIB_VER_MINOR 2
-#define ZLIB_VER_REVISION 5
+#define ZLIB_VER_REVISION 8
#define ZLIB_VER_SUBREVISION 0
/*
@@ -83,15 +83,15 @@ typedef void (*free_func) OF((voidpf opaque, voidpf address));
struct internal_state;
typedef struct z_stream_s {
- Bytef *next_in; /* next input byte */
+ z_const Bytef *next_in; /* next input byte */
uInt avail_in; /* number of bytes available at next_in */
- uLong total_in; /* total nb of input bytes read so far */
+ uLong total_in; /* total number of input bytes read so far */
Bytef *next_out; /* next output byte should be put there */
uInt avail_out; /* remaining free space at next_out */
- uLong total_out; /* total nb of bytes output so far */
+ uLong total_out; /* total number of bytes output so far */
- char *msg; /* last error message, NULL if no error */
+ z_const char *msg; /* last error message, NULL if no error */
struct internal_state FAR *state; /* not visible by applications */
alloc_func zalloc; /* used to allocate the internal state */
@@ -327,8 +327,9 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
Z_FINISH can be used immediately after deflateInit if all the compression
is to be done in a single step. In this case, avail_out must be at least the
- value returned by deflateBound (see below). If deflate does not return
- Z_STREAM_END, then it must be called again as described above.
+ value returned by deflateBound (see below). Then deflate is guaranteed to
+ return Z_STREAM_END. If not enough output space is provided, deflate will
+ not return Z_STREAM_END, and it must be called again as described above.
deflate() sets strm->adler to the adler32 checksum of all input read
so far (that is, total_in bytes).
@@ -451,23 +452,29 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
error. However if all decompression is to be performed in a single step (a
single call of inflate), the parameter flush should be set to Z_FINISH. In
this case all pending input is processed and all pending output is flushed;
- avail_out must be large enough to hold all the uncompressed data. (The size
- of the uncompressed data may have been saved by the compressor for this
- purpose.) The next operation on this stream must be inflateEnd to deallocate
- the decompression state. The use of Z_FINISH is never required, but can be
- used to inform inflate that a faster approach may be used for the single
- inflate() call.
+ avail_out must be large enough to hold all of the uncompressed data for the
+ operation to complete. (The size of the uncompressed data may have been
+ saved by the compressor for this purpose.) The use of Z_FINISH is not
+ required to perform an inflation in one step. However it may be used to
+ inform inflate that a faster approach can be used for the single inflate()
+ call. Z_FINISH also informs inflate to not maintain a sliding window if the
+ stream completes, which reduces inflate's memory footprint. If the stream
+ does not complete, either because not all of the stream is provided or not
+ enough output space is provided, then a sliding window will be allocated and
+ inflate() can be called again to continue the operation as if Z_NO_FLUSH had
+ been used.
In this implementation, inflate() always flushes as much output as
possible to the output buffer, and always uses the faster approach on the
- first call. So the only effect of the flush parameter in this implementation
- is on the return value of inflate(), as noted below, or when it returns early
- because Z_BLOCK or Z_TREES is used.
+ first call. So the effects of the flush parameter in this implementation are
+ on the return value of inflate() as noted below, when inflate() returns early
+ when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
+ memory for a sliding window when Z_FINISH is used.
If a preset dictionary is needed after this call (see inflateSetDictionary
- below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
- strm->adler to the adler32 checksum of all output produced so far (that is,
+ strm->adler to the Adler-32 checksum of all output produced so far (that is,
total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
below. At the end of the stream, inflate() checks that its computed adler32
checksum is equal to that saved by the compressor and returns Z_STREAM_END
@@ -478,7 +485,9 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
initializing with inflateInit2(). Any information contained in the gzip
header is not retained, so applications that need that information should
instead use raw inflate, see inflateInit2() below, or inflateBack() and
- perform their own processing of the gzip header and trailer.
+ perform their own processing of the gzip header and trailer. When processing
+ gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
+ producted so far. The CRC-32 is checked against the gzip trailer.
inflate() returns Z_OK if some progress has been made (more input processed
or more output produced), Z_STREAM_END if the end of the compressed data has
@@ -580,10 +589,15 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
uInt dictLength));
/*
Initializes the compression dictionary from the given byte sequence
- without producing any compressed output. This function must be called
- immediately after deflateInit, deflateInit2 or deflateReset, before any call
- of deflate. The compressor and decompressor must use exactly the same
- dictionary (see inflateSetDictionary).
+ without producing any compressed output. When using the zlib format, this
+ function must be called immediately after deflateInit, deflateInit2 or
+ deflateReset, and before any call of deflate. When doing raw deflate, this
+ function must be called either before any call of deflate, or immediately
+ after the completion of a deflate block, i.e. after all input has been
+ consumed and all output has been delivered when using any of the flush
+ options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The
+ compressor and decompressor must use exactly the same dictionary (see
+ inflateSetDictionary).
The dictionary should consist of strings (byte sequences) that are likely
to be encountered later in the data to be compressed, with the most commonly
@@ -610,8 +624,8 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
inconsistent (for example if deflate has already been called for this stream
- or if the compression method is bsort). deflateSetDictionary does not
- perform any compression: this will be done by deflate().
+ or if not at a block boundary for raw deflate). deflateSetDictionary does
+ not perform any compression: this will be done by deflate().
*/
ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
@@ -688,8 +702,28 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
deflation of sourceLen bytes. It must be called after deflateInit() or
deflateInit2(), and after deflateSetHeader(), if used. This would be used
to allocate an output buffer for deflation in a single pass, and so would be
- called before deflate().
-*/
+ called before deflate(). If that first deflate() call is provided the
+ sourceLen input bytes, an output buffer allocated to the size returned by
+ deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
+ to return Z_STREAM_END. Note that it is possible for the compressed size to
+ be larger than the value returned by deflateBound() if flush options other
+ than Z_FINISH or Z_NO_FLUSH are used.
+*/
+
+ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
+ unsigned *pending,
+ int *bits));
+/*
+ deflatePending() returns the number of bytes and bits of output that have
+ been generated, but not yet provided in the available output. The bytes not
+ provided would be due to the available output space having being consumed.
+ The number of bits of output not provided are between 0 and 7, where they
+ await more bits to join them in order to fill out a full byte. If pending
+ or bits are Z_NULL, then those values are not set.
+
+ deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+ */
ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
int bits,
@@ -703,8 +737,9 @@ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
than or equal to 16, and that many of the least significant bits of value
will be inserted in the output.
- deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent.
+ deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
+ room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
+ source stream state was inconsistent.
*/
ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
@@ -790,10 +825,11 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
can be determined from the adler32 value returned by that call of inflate.
The compressor and decompressor must use exactly the same dictionary (see
- deflateSetDictionary). For raw inflate, this function can be called
- immediately after inflateInit2() or inflateReset() and before any call of
- inflate() to set the dictionary. The application must insure that the
- dictionary that was used for compression is provided.
+ deflateSetDictionary). For raw inflate, this function can be called at any
+ time to set the dictionary. If the provided dictionary is smaller than the
+ window and there is already data in the window, then the provided dictionary
+ will amend what's there. The application must insure that the dictionary
+ that was used for compression is provided.
inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
@@ -803,19 +839,38 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
inflate().
*/
+ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
+ Bytef *dictionary,
+ uInt *dictLength));
+/*
+ Returns the sliding dictionary being maintained by inflate. dictLength is
+ set to the number of bytes in the dictionary, and that many bytes are copied
+ to dictionary. dictionary must have enough space, where 32768 bytes is
+ always enough. If inflateGetDictionary() is called with dictionary equal to
+ Z_NULL, then only the dictionary length is returned, and nothing is copied.
+ Similary, if dictLength is Z_NULL, then it is not set.
+
+ inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+ stream state is inconsistent.
+*/
+
ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
/*
- Skips invalid compressed data until a full flush point (see above the
- description of deflate with Z_FULL_FLUSH) can be found, or until all
+ Skips invalid compressed data until a possible full flush point (see above
+ for the description of deflate with Z_FULL_FLUSH) can be found, or until all
available input is skipped. No output is provided.
- inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
- if no more input was provided, Z_DATA_ERROR if no flush point has been
- found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the
- success case, the application may save the current current value of total_in
- which indicates where valid compressed data was found. In the error case,
- the application may repeatedly call inflateSync, providing more input each
- time, until success or end of the input data.
+ inflateSync searches for a 00 00 FF FF pattern in the compressed data.
+ All full flush points have this pattern, but not all occurrences of this
+ pattern are full flush points.
+
+ inflateSync returns Z_OK if a possible full flush point has been found,
+ Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
+ has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
+ In the success case, the application may save the current current value of
+ total_in which indicates where valid compressed data was found. In the
+ error case, the application may repeatedly call inflateSync, providing more
+ input each time, until success or end of the input data.
*/
ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
@@ -962,12 +1017,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
See inflateBack() for the usage of these routines.
inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
- the paramaters are invalid, Z_MEM_ERROR if the internal state could not be
+ the parameters are invalid, Z_MEM_ERROR if the internal state could not be
allocated, or Z_VERSION_ERROR if the version of the library does not match
the version of the header file.
*/
-typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef unsigned (*in_func) OF((void FAR *,
+ z_const unsigned char FAR * FAR *));
typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
@@ -975,11 +1031,12 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
out_func out, void FAR *out_desc));
/*
inflateBack() does a raw inflate with a single call using a call-back
- interface for input and output. This is more efficient than inflate() for
- file i/o applications in that it avoids copying between the output and the
- sliding window by simply making the window itself the output buffer. This
- function trusts the application to not change the output buffer passed by
- the output function, at least until inflateBack() returns.
+ interface for input and output. This is potentially more efficient than
+ inflate() for file i/o applications, in that it avoids copying between the
+ output and the sliding window by simply making the window itself the output
+ buffer. inflate() can be faster on modern CPUs when used with large
+ buffers. inflateBack() trusts the application to not change the output
+ buffer passed by the output function, at least until inflateBack() returns.
inflateBackInit() must be called first to allocate the internal state
and to initialize the state with the user-provided window buffer.
@@ -1088,6 +1145,7 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
27-31: 0 (reserved)
*/
+#ifndef Z_SOLO
/* utility functions */
@@ -1149,10 +1207,11 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
enough memory, Z_BUF_ERROR if there was not enough room in the output
- buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In
+ the case where there is not enough room, uncompress() will fill the output
+ buffer with the uncompressed data up to that point.
*/
-
/* gzip file access functions */
/*
@@ -1162,7 +1221,7 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
wrapper, documented in RFC 1952, wrapped around a deflate stream.
*/
-typedef voidp gzFile; /* opaque gzip file descriptor */
+typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */
/*
ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
@@ -1172,13 +1231,28 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
for fixed code compression as in "wb9F". (See the description of
- deflateInit2 for more information about the strategy parameter.) Also "a"
- can be used instead of "w" to request that the gzip stream that will be
- written be appended to the file. "+" will result in an error, since reading
- and writing to the same gzip file is not supported.
+ deflateInit2 for more information about the strategy parameter.) 'T' will
+ request transparent writing or appending with no compression and not using
+ the gzip format.
+
+ "a" can be used instead of "w" to request that the gzip stream that will
+ be written be appended to the file. "+" will result in an error, since
+ reading and writing to the same gzip file is not supported. The addition of
+ "x" when writing will create the file exclusively, which fails if the file
+ already exists. On systems that support it, the addition of "e" when
+ reading or writing will set the flag to close the file on an execve() call.
+
+ These functions, as well as gzip, will read and decode a sequence of gzip
+ streams in a file. The append function of gzopen() can be used to create
+ such a file. (Also see gzflush() for another way to do this.) When
+ appending, gzopen does not test whether the file begins with a gzip stream,
+ nor does it look for the end of the gzip streams to begin appending. gzopen
+ will simply append a gzip stream to the existing file.
gzopen can be used to read a file which is not in gzip format; in this
- case gzread will directly read from the file without decompression.
+ case gzread will directly read from the file without decompression. When
+ reading, this will be detected automatically by looking for the magic two-
+ byte gzip header.
gzopen returns NULL if the file could not be opened, if there was
insufficient memory to allocate the gzFile state, or if an invalid mode was
@@ -1197,7 +1271,11 @@ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
mode);. The duplicated descriptor should be saved to avoid a leak, since
- gzdopen does not close fd if it fails.
+ gzdopen does not close fd if it fails. If you are using fileno() to get the
+ file descriptor from a FILE *, then you will have to use dup() to avoid
+ double-close()ing the file descriptor. Both gzclose() and fclose() will
+ close the associated file descriptor, so they need to have different file
+ descriptors.
gzdopen returns NULL if there was insufficient memory to allocate the
gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
@@ -1235,14 +1313,26 @@ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
/*
Reads the given number of uncompressed bytes from the compressed file. If
- the input file was not in gzip format, gzread copies the given number of
- bytes into the buffer.
+ the input file is not in gzip format, gzread copies the given number of
+ bytes into the buffer directly from the file.
After reaching the end of a gzip stream in the input, gzread will continue
- to read, looking for another gzip stream, or failing that, reading the rest
- of the input file directly without decompression. The entire input file
- will be read if gzread is called until it returns less than the requested
- len.
+ to read, looking for another gzip stream. Any number of gzip streams may be
+ concatenated in the input file, and will all be decompressed by gzread().
+ If something other than a gzip stream is encountered after a gzip stream,
+ that remaining trailing garbage is ignored (and no error is returned).
+
+ gzread can be used to read a gzip file that is being concurrently written.
+ Upon reaching the end of the input, gzread will return with the available
+ data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
+ gzclearerr can be used to clear the end of file indicator in order to permit
+ gzread to be tried again. Z_OK indicates that a gzip stream was completed
+ on the last gzread. Z_BUF_ERROR indicates that the input file ended in the
+ middle of a gzip stream. Note that gzread does not return -1 in the event
+ of an incomplete gzip stream. This error is deferred until gzclose(), which
+ will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
+ stream. Alternatively, gzerror can be used before gzclose to detect this
+ case.
gzread returns the number of uncompressed bytes actually read, less than
len for end of file, or -1 for error.
@@ -1256,7 +1346,7 @@ ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
error.
*/
-ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
/*
Converts, formats, and writes the arguments to the compressed file under
control of the format string, as in fprintf. gzprintf returns the number of
@@ -1301,7 +1391,10 @@ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
/*
Reads one byte from the compressed file. gzgetc returns this byte or -1
- in case of end of file or error.
+ in case of end of file or error. This is implemented as a macro for speed.
+ As such, it does not do all of the checking the other functions do. I.e.
+ it does not check to see if file is NULL, nor whether the structure file
+ points to has been clobbered or not.
*/
ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
@@ -1397,9 +1490,7 @@ ZEXTERN int ZEXPORT gzeof OF((gzFile file));
ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
/*
Returns true (1) if file is being copied directly while reading, or false
- (0) if file is a gzip stream being decompressed. This state can change from
- false to true while reading the input file if the end of a gzip stream is
- reached, but is followed by data that is not another gzip stream.
+ (0) if file is a gzip stream being decompressed.
If the input file is empty, gzdirect() will return true, since the input
does not contain a gzip stream.
@@ -1408,6 +1499,13 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
cause buffers to be allocated to allow reading the file to determine if it
is a gzip file. Therefore if gzbuffer() is used, it should be called before
gzdirect().
+
+ When writing, gzdirect() returns true (1) if transparent writing was
+ requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note:
+ gzdirect() is not needed when writing. Transparent writing must be
+ explicitly requested, so the application already knows the answer. When
+ linking statically, using gzdirect() will include all of the zlib code for
+ gzip file reading and decompression, which may not be desired.)
*/
ZEXTERN int ZEXPORT gzclose OF((gzFile file));
@@ -1419,7 +1517,8 @@ ZEXTERN int ZEXPORT gzclose OF((gzFile file));
must not be called more than once on the same allocation.
gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
- file operation error, or Z_OK on success.
+ file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
+ last read ended in the middle of a gzip stream, or Z_OK on success.
*/
ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
@@ -1457,6 +1556,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
file that is being written concurrently.
*/
+#endif /* !Z_SOLO */
/* checksum functions */
@@ -1492,16 +1592,17 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
- seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note
+ that the z_off_t type (like off_t) is a signed integer. If len2 is
+ negative, the result has no meaning or utility.
*/
ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
/*
Update a running CRC-32 with the bytes buf[0..len-1] and return the
updated CRC-32. If buf is Z_NULL, this function returns the required
- initial value for the for the crc. Pre- and post-conditioning (one's
- complement) is performed within this function so it shouldn't be done by the
- application.
+ initial value for the crc. Pre- and post-conditioning (one's complement) is
+ performed within this function so it shouldn't be done by the application.
Usage example:
@@ -1544,17 +1645,42 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
const char *version,
int stream_size));
#define deflateInit(strm, level) \
- deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
#define inflateInit(strm) \
- inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
- (strategy), ZLIB_VERSION, sizeof(z_stream))
+ (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
#define inflateInit2(strm, windowBits) \
- inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+ (int)sizeof(z_stream))
#define inflateBackInit(strm, windowBits, window) \
inflateBackInit_((strm), (windowBits), (window), \
- ZLIB_VERSION, sizeof(z_stream))
+ ZLIB_VERSION, (int)sizeof(z_stream))
+
+#ifndef Z_SOLO
+
+/* gzgetc() macro and its supporting function and exposed data structure. Note
+ * that the real internal state is much larger than the exposed structure.
+ * This abbreviated structure exposes just enough for the gzgetc() macro. The
+ * user should not mess with these exposed elements, since their names or
+ * behavior could change in the future, perhaps even capriciously. They can
+ * only be used by the gzgetc() macro. You have been warned.
+ */
+struct gzFile_s {
+ unsigned have;
+ unsigned char *next;
+ z_off64_t pos;
+};
+ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
+#ifdef Z_PREFIX_SET
+# undef z_gzgetc
+# define z_gzgetc(g) \
+ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#else
+# define gzgetc(g) \
+ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#endif
/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
* change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
@@ -1562,7 +1688,7 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
* functions are changed to 64 bits) -- in case these are set on systems
* without large file support, _LFS64_LARGEFILE must also be true
*/
-#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+#ifdef Z_LARGE64
ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
@@ -1571,14 +1697,23 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
#endif
-#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0
-# define gzopen gzopen64
-# define gzseek gzseek64
-# define gztell gztell64
-# define gzoffset gzoffset64
-# define adler32_combine adler32_combine64
-# define crc32_combine crc32_combine64
-# ifdef _LARGEFILE64_SOURCE
+#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
+# ifdef Z_PREFIX_SET
+# define z_gzopen z_gzopen64
+# define z_gzseek z_gzseek64
+# define z_gztell z_gztell64
+# define z_gzoffset z_gzoffset64
+# define z_adler32_combine z_adler32_combine64
+# define z_crc32_combine z_crc32_combine64
+# else
+# define gzopen gzopen64
+# define gzseek gzseek64
+# define gztell gztell64
+# define gzoffset gzoffset64
+# define adler32_combine adler32_combine64
+# define crc32_combine crc32_combine64
+# endif
+# ifndef Z_LARGE64
ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
@@ -1595,6 +1730,13 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
#endif
+#else /* Z_SOLO */
+
+ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+
+#endif /* !Z_SOLO */
+
/* hack for buggy compilers */
#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
struct internal_state {int dummy;};
@@ -1603,8 +1745,21 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
/* undocumented functions */
ZEXTERN const char * ZEXPORT zError OF((int));
ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
-ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void));
ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
+ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp));
+ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp));
+#if defined(_WIN32) && !defined(Z_SOLO)
+ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
+ const char *mode));
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+# ifndef Z_SOLO
+ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file,
+ const char *format,
+ va_list va));
+# endif
+#endif
#ifdef __cplusplus
}
diff --git a/deps/zlib/zutil.c b/deps/zlib/zutil.c
index 898ed345b..2fe2a7140 100644
--- a/deps/zlib/zutil.c
+++ b/deps/zlib/zutil.c
@@ -1,5 +1,5 @@
/* zutil.c -- target dependent utility functions for the compression library
- * Copyright (C) 1995-2005, 2010 Jean-loup Gailly.
+ * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly.
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -11,7 +11,7 @@
struct internal_state {int dummy;}; /* for buggy compilers */
#endif
-const char * const z_errmsg[10] = {
+z_const char * const z_errmsg[10] = {
"need dictionary", /* Z_NEED_DICT 2 */
"stream end", /* Z_STREAM_END 1 */
"", /* Z_OK 0 */
@@ -85,27 +85,27 @@ uLong ZEXPORT zlibCompileFlags()
#ifdef FASTEST
flags += 1L << 21;
#endif
-#ifdef STDC
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
# ifdef NO_vsnprintf
- flags += 1L << 25;
+ flags += 1L << 25;
# ifdef HAS_vsprintf_void
- flags += 1L << 26;
+ flags += 1L << 26;
# endif
# else
# ifdef HAS_vsnprintf_void
- flags += 1L << 26;
+ flags += 1L << 26;
# endif
# endif
#else
- flags += 1L << 24;
+ flags += 1L << 24;
# ifdef NO_snprintf
- flags += 1L << 25;
+ flags += 1L << 25;
# ifdef HAS_sprintf_void
- flags += 1L << 26;
+ flags += 1L << 26;
# endif
# else
# ifdef HAS_snprintf_void
- flags += 1L << 26;
+ flags += 1L << 26;
# endif
# endif
#endif
@@ -181,6 +181,7 @@ void ZLIB_INTERNAL zmemzero(dest, len)
}
#endif
+#ifndef Z_SOLO
#ifdef SYS16BIT
@@ -316,3 +317,5 @@ void ZLIB_INTERNAL zcfree (opaque, ptr)
}
#endif /* MY_ZCALLOC */
+
+#endif /* !Z_SOLO */
diff --git a/deps/zlib/zutil.h b/deps/zlib/zutil.h
index 258fa8879..24ab06b1c 100644
--- a/deps/zlib/zutil.h
+++ b/deps/zlib/zutil.h
@@ -1,5 +1,5 @@
/* zutil.h -- internal interface and configuration of the compression library
- * Copyright (C) 1995-2010 Jean-loup Gailly.
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -13,7 +13,7 @@
#ifndef ZUTIL_H
#define ZUTIL_H
-#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ)
+#ifdef HAVE_HIDDEN
# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
#else
# define ZLIB_INTERNAL
@@ -21,7 +21,7 @@
#include "zlib.h"
-#ifdef STDC
+#if defined(STDC) && !defined(Z_SOLO)
# if !(defined(_WIN32_WCE) && defined(_MSC_VER))
# include <stddef.h>
# endif
@@ -29,6 +29,10 @@
# include <stdlib.h>
#endif
+#ifdef Z_SOLO
+ typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */
+#endif
+
#ifndef local
# define local static
#endif
@@ -40,13 +44,13 @@ typedef unsigned short ush;
typedef ush FAR ushf;
typedef unsigned long ulg;
-extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
/* (size given to avoid silly warnings with Visual C++) */
#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
#define ERR_RETURN(strm,err) \
- return (strm->msg = (char*)ERR_MSG(err), (err))
+ return (strm->msg = ERR_MSG(err), (err))
/* To be used only when the state is known to be valid */
/* common constants */
@@ -78,16 +82,18 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
# define OS_CODE 0x00
-# if defined(__TURBOC__) || defined(__BORLANDC__)
-# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
- /* Allow compilation with ANSI keywords only enabled */
- void _Cdecl farfree( void *block );
- void *_Cdecl farmalloc( unsigned long nbytes );
-# else
-# include <alloc.h>
+# ifndef Z_SOLO
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
# endif
-# else /* MSC or DJGPP */
-# include <malloc.h>
# endif
#endif
@@ -107,18 +113,20 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
#ifdef OS2
# define OS_CODE 0x06
-# ifdef M_I86
+# if defined(M_I86) && !defined(Z_SOLO)
# include <malloc.h>
# endif
#endif
#if defined(MACOS) || defined(TARGET_OS_MAC)
# define OS_CODE 0x07
-# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
-# include <unix.h> /* for fdopen */
-# else
-# ifndef fdopen
-# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef Z_SOLO
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
# endif
# endif
#endif
@@ -153,14 +161,15 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
# endif
#endif
-#if defined(__BORLANDC__)
+#if defined(__BORLANDC__) && !defined(MSDOS)
#pragma warn -8004
#pragma warn -8008
#pragma warn -8066
#endif
/* provide prototypes for these when building zlib without LFS */
-#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
+#if !defined(_WIN32) && \
+ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
#endif
@@ -177,42 +186,7 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
/* functions */
-#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
-# ifndef HAVE_VSNPRINTF
-# define HAVE_VSNPRINTF
-# endif
-#endif
-#if defined(__CYGWIN__)
-# ifndef HAVE_VSNPRINTF
-# define HAVE_VSNPRINTF
-# endif
-#endif
-#ifndef HAVE_VSNPRINTF
-# ifdef MSDOS
- /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
- but for now we just assume it doesn't. */
-# define NO_vsnprintf
-# endif
-# ifdef __TURBOC__
-# define NO_vsnprintf
-# endif
-# ifdef WIN32
- /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
-# if !defined(vsnprintf) && !defined(NO_vsnprintf)
-# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
-# define vsnprintf _vsnprintf
-# endif
-# endif
-# endif
-# ifdef __SASC
-# define NO_vsnprintf
-# endif
-#endif
-#ifdef VMS
-# define NO_vsnprintf
-#endif
-
-#if defined(pyr)
+#if defined(pyr) || defined(Z_SOLO)
# define NO_MEMCPY
#endif
#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
@@ -261,14 +235,19 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
# define Tracecv(c,x)
#endif
-
-voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
- unsigned size));
-void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
+#ifndef Z_SOLO
+ voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
+ unsigned size));
+ void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
+#endif
#define ZALLOC(strm, items, size) \
(*((strm)->zalloc))((strm)->opaque, (items), (size))
#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+/* Reverse the bytes in a 32-bit value */
+#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
#endif /* ZUTIL_H */
diff --git a/examples/.gitignore b/examples/.gitignore
index b652e28b5..083c8835e 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -9,4 +9,5 @@ log
rev-parse
status
tag
+for-each-ref
*.dSYM
diff --git a/examples/Makefile b/examples/Makefile
index e866b7fee..11b019984 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -4,6 +4,7 @@ CC = gcc
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
LFLAGS = -L../build -lgit2 -lz
APPS = general showindex diff rev-list cat-file status log rev-parse init blame tag
+APPS += for-each-ref
all: $(APPS)
diff --git a/examples/for-each-ref.c b/examples/for-each-ref.c
new file mode 100644
index 000000000..d6846bb0d
--- /dev/null
+++ b/examples/for-each-ref.c
@@ -0,0 +1,46 @@
+#include <git2.h>
+#include <stdio.h>
+#include "common.h"
+
+static int show_ref(git_reference *ref, void *data)
+{
+ git_repository *repo = data;
+ git_reference *resolved = NULL;
+ char hex[GIT_OID_HEXSZ+1];
+ const git_oid *oid;
+ git_object *obj;
+
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
+ check_lg2(git_reference_resolve(&resolved, ref),
+ "Unable to resolve symbolic reference",
+ git_reference_name(ref));
+
+ oid = git_reference_target(resolved ? resolved : ref);
+ git_oid_fmt(hex, oid);
+ hex[GIT_OID_HEXSZ] = 0;
+ check_lg2(git_object_lookup(&obj, repo, oid, GIT_OBJ_ANY),
+ "Unable to lookup object", hex);
+
+ printf("%s %-6s\t%s\n",
+ hex,
+ git_object_type2string(git_object_type(obj)),
+ git_reference_name(ref));
+
+ if (resolved)
+ git_reference_free(resolved);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ git_repository *repo;
+
+ if (argc != 1 || argv[1] /* silence -Wunused-parameter */)
+ fatal("Sorry, no for-each-ref options supported yet", NULL);
+
+ check_lg2(git_repository_open(&repo, "."),
+ "Could not open repository", NULL);
+ check_lg2(git_reference_foreach(repo, show_ref, repo),
+ "Could not iterate over references", NULL);
+ return 0;
+}
diff --git a/examples/general.c b/examples/general.c
index ae8756338..051d69380 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -94,8 +94,8 @@ int main (int argc, char** argv)
// Next we will convert the 20 byte raw SHA1 value to a human readable 40
// char hex value.
printf("\n*Raw to Hex*\n");
- char out[41];
- out[40] = '\0';
+ char out[GIT_OID_HEXSZ+1];
+ out[GIT_OID_HEXSZ] = '\0';
// If you have a oid, you can easily get the hex value of the SHA as well.
git_oid_fmt(out, &oid);
diff --git a/examples/log.c b/examples/log.c
index 471c5ff96..d5f75a297 100644
--- a/examples/log.c
+++ b/examples/log.c
@@ -54,8 +54,9 @@ struct log_options {
int min_parents, max_parents;
git_time_t before;
git_time_t after;
- char *author;
- char *committer;
+ const char *author;
+ const char *committer;
+ const char *grep;
};
/** utility functions that parse options and help with log output */
@@ -65,6 +66,9 @@ static void print_time(const git_time *intime, const char *prefix);
static void print_commit(git_commit *commit);
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
+/** utility functions for filtering */
+static int signature_matches(const git_signature *sig, const char *filter);
+static int log_message_matches(const git_commit *commit, const char *filter);
int main(int argc, char *argv[])
{
@@ -128,6 +132,15 @@ int main(int argc, char *argv[])
continue;
}
+ if (!signature_matches(git_commit_author(commit), opt.author))
+ continue;
+
+ if (!signature_matches(git_commit_committer(commit), opt.committer))
+ continue;
+
+ if (!log_message_matches(commit, opt.grep))
+ continue;
+
if (count++ < opt.skip)
continue;
if (opt.limit != -1 && printed++ >= opt.limit) {
@@ -172,6 +185,32 @@ int main(int argc, char *argv[])
return 0;
}
+/** Determine if the given git_signature does not contain the filter text. */
+static int signature_matches(const git_signature *sig, const char *filter) {
+ if (filter == NULL)
+ return 1;
+
+ if (sig != NULL &&
+ (strstr(sig->name, filter) != NULL ||
+ strstr(sig->email, filter) != NULL))
+ return 1;
+
+ return 0;
+}
+
+static int log_message_matches(const git_commit *commit, const char *filter) {
+ const char *message = NULL;
+
+ if (filter == NULL)
+ return 1;
+
+ if ((message = git_commit_message(commit)) != NULL &&
+ strstr(message, filter) != NULL)
+ return 1;
+
+ return 0;
+}
+
/** Push object (for hide or show) onto revwalker. */
static void push_rev(struct log_state *s, git_object *obj, int hide)
{
@@ -401,6 +440,12 @@ static int parse_options(
set_sorting(s, GIT_SORT_TOPOLOGICAL);
else if (!strcmp(a, "--reverse"))
set_sorting(s, GIT_SORT_REVERSE);
+ else if (match_str_arg(&opt->author, &args, "--author"))
+ /** Found valid --author */;
+ else if (match_str_arg(&opt->committer, &args, "--committer"))
+ /** Found valid --committer */;
+ else if (match_str_arg(&opt->grep, &args, "--grep"))
+ /** Found valid --grep */;
else if (match_str_arg(&s->repodir, &args, "--git-dir"))
/** Found git-dir. */;
else if (match_int_arg(&opt->skip, &args, "--skip", 0))
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 03f1541a4..8d882095b 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -155,7 +155,7 @@ int fetch(git_repository *repo, int argc, char **argv)
// Update the references in the remote's namespace to point to the
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
- // changed but all the neede objects are available locally.
+ // changed but all the needed objects are available locally.
if (git_remote_update_tips(remote, NULL, NULL) < 0)
return -1;
diff --git a/examples/rev-list.c b/examples/rev-list.c
index 5c0d751a3..940a01136 100644
--- a/examples/rev-list.c
+++ b/examples/rev-list.c
@@ -22,7 +22,7 @@ int main (int argc, char **argv)
git_repository *repo;
git_revwalk *walk;
git_oid oid;
- char buf[41];
+ char buf[GIT_OID_HEXSZ+1];
git_threads_init();
@@ -32,7 +32,7 @@ int main (int argc, char **argv)
while (!git_revwalk_next(&oid, walk)) {
git_oid_fmt(buf, &oid);
- buf[40] = '\0';
+ buf[GIT_OID_HEXSZ] = '\0';
printf("%s\n", buf);
}
diff --git a/examples/showindex.c b/examples/showindex.c
index ad4a16e8e..604124f12 100644
--- a/examples/showindex.c
+++ b/examples/showindex.c
@@ -20,8 +20,8 @@ int main (int argc, char** argv)
unsigned int i, ecount;
char *dir = ".";
size_t dirlen;
- char out[41];
- out[40] = '\0';
+ char out[GIT_OID_HEXSZ+1];
+ out[GIT_OID_HEXSZ] = '\0';
git_threads_init();
diff --git a/examples/status.c b/examples/status.c
index 5f619a055..a59f34454 100644
--- a/examples/status.c
+++ b/examples/status.c
@@ -13,7 +13,12 @@
*/
#include "common.h"
-#include <unistd.h>
+#ifdef _WIN32
+# include <Windows.h>
+# define sleep(a) Sleep(a * 1000)
+#else
+# include <unistd.h>
+#endif
/**
* This example demonstrates the use of the libgit2 status APIs,
diff --git a/examples/tag.c b/examples/tag.c
index ebb8e37b2..1d254aee5 100644
--- a/examples/tag.c
+++ b/examples/tag.c
@@ -236,7 +236,7 @@ static void action_create_tag(tag_state *state)
git_signature_free(tagger);
}
-static void print_usage()
+static void print_usage(void)
{
fprintf(stderr, "usage: see `git help tag`\n");
exit(1);
diff --git a/include/git2/attr.h b/include/git2/attr.h
index 3adbb2c24..0238f3dd7 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -76,25 +76,28 @@ GIT_BEGIN_DECL
*/
#define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_T)
+/**
+ * Possible states for an attribute
+ */
typedef enum {
- GIT_ATTR_UNSPECIFIED_T = 0,
- GIT_ATTR_TRUE_T,
- GIT_ATTR_FALSE_T,
- GIT_ATTR_VALUE_T,
+ GIT_ATTR_UNSPECIFIED_T = 0, /**< The attribute has been left unspecified */
+ GIT_ATTR_TRUE_T, /**< The attribute has been set */
+ GIT_ATTR_FALSE_T, /**< The attribute has been unset */
+ GIT_ATTR_VALUE_T, /**< This attribute has a value */
} git_attr_t;
-/*
- * Return the value type for a given attribute.
+/**
+ * Return the value type for a given attribute.
*
- * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute
- * was not set at all), or `VALUE`, if the attribute was set to
- * an actual string.
+ * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute
+ * was not set at all), or `VALUE`, if the attribute was set to an
+ * actual string.
*
- * If the attribute has a `VALUE` string, it can be accessed normally
- * as a NULL-terminated C string.
+ * If the attribute has a `VALUE` string, it can be accessed normally
+ * as a NULL-terminated C string.
*
- * @param attr The attribute
- * @return the value type for the attribute
+ * @param attr The attribute
+ * @return the value type for the attribute
*/
GIT_EXTERN(git_attr_t) git_attr_value(const char *attr);
diff --git a/include/git2/blame.h b/include/git2/blame.h
index b7fa9aeda..7f0de1731 100644
--- a/include/git2/blame.h
+++ b/include/git2/blame.h
@@ -83,17 +83,16 @@ typedef struct git_blame_options {
#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
/**
-* Initializes a `git_blame_options` with default values. Equivalent to
-* creating an instance with GIT_BLAME_OPTIONS_INIT.
-*
-* @param opts the `git_blame_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_BLAME_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_blame_options` with default values. Equivalent to
+ * creating an instance with GIT_BLAME_OPTIONS_INIT.
+ *
+ * @param opts The `git_blame_options` struct to initialize
+ * @param version Version of struct; pass `GIT_BLAME_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_blame_init_options(
- git_blame_options* opts,
- int version);
+ git_blame_options *opts,
+ unsigned int version);
/**
* Structure that represents a blame hunk.
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 1b6583309..c24ff7e7f 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -210,7 +210,7 @@ GIT_EXTERN(int) git_blob_create_frombuffer(
*
* The heuristic used to guess if a file is binary is taken from core git:
* Searching for NUL bytes and looking for a reasonable ratio of printable
- * to non-printable characters among the first 4000 bytes.
+ * to non-printable characters among the first 8000 bytes.
*
* @param blob The blob which content should be analyzed
* @return 1 if the content of the blob is detected
diff --git a/include/git2/buffer.h b/include/git2/buffer.h
index 36a61e6c9..1c216bf3a 100644
--- a/include/git2/buffer.h
+++ b/include/git2/buffer.h
@@ -105,6 +105,22 @@ GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
GIT_EXTERN(int) git_buf_set(
git_buf *buffer, const void *data, size_t datalen);
+/**
+* Check quickly if buffer looks like it contains binary data
+*
+* @param buf Buffer to check
+* @return 1 if buffer looks like non-text data
+*/
+GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf);
+
+/**
+* Check quickly if buffer contains a NUL byte
+*
+* @param buf Buffer to check
+* @return 1 if buffer contains a NUL byte
+*/
+GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf);
+
GIT_END_DECL
/** @} */
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 69addb7d9..fe4966aa2 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -43,17 +43,17 @@ GIT_BEGIN_DECL
* In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
* both of which only make modifications that will not lose changes.
*
- * | target == baseline | target != baseline |
- * ---------------------|-----------------------|----------------------|
- * workdir == baseline | no action | create, update, or |
- * | | delete file |
- * ---------------------|-----------------------|----------------------|
- * workdir exists and | no action | conflict (notify |
- * is != baseline | notify dirty MODIFIED | and cancel checkout) |
- * ---------------------|-----------------------|----------------------|
- * workdir missing, | create if SAFE_CREATE | create file |
- * baseline present | notify dirty DELETED | |
- * ---------------------|-----------------------|----------------------|
+ * | target == baseline | target != baseline |
+ * ---------------------|-----------------------|----------------------|
+ * workdir == baseline | no action | create, update, or |
+ * | | delete file |
+ * ---------------------|-----------------------|----------------------|
+ * workdir exists and | no action | conflict (notify |
+ * is != baseline | notify dirty MODIFIED | and cancel checkout) |
+ * ---------------------|-----------------------|----------------------|
+ * workdir missing, | create if SAFE_CREATE | create file |
+ * baseline present | notify dirty DELETED | |
+ * ---------------------|-----------------------|----------------------|
*
* The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE
* will cause a file to be checked out if it is missing from the working
@@ -106,7 +106,7 @@ GIT_BEGIN_DECL
* target contains that file.
*/
typedef enum {
- GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */
+ GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
/** Allow safe updates that cannot overwrite uncommitted data */
GIT_CHECKOUT_SAFE = (1u << 0),
@@ -233,18 +233,18 @@ typedef void (*git_checkout_progress_cb)(
typedef struct git_checkout_options {
unsigned int version;
- unsigned int checkout_strategy; /** default will be a dry run */
+ unsigned int checkout_strategy; /**< default will be a dry run */
- int disable_filters; /** don't apply filters like CRLF conversion */
- unsigned int dir_mode; /** default is 0755 */
- unsigned int file_mode; /** default is 0644 or 0755 as dictated by blob */
- int file_open_flags; /** default is O_CREAT | O_TRUNC | O_WRONLY */
+ int disable_filters; /**< don't apply filters like CRLF conversion */
+ unsigned int dir_mode; /**< default is 0755 */
+ unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */
+ int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */
- unsigned int notify_flags; /** see `git_checkout_notify_t` above */
+ unsigned int notify_flags; /**< see `git_checkout_notify_t` above */
git_checkout_notify_cb notify_cb;
void *notify_payload;
- /* Optional callback to notify the consumer of checkout progress. */
+ /** Optional callback to notify the consumer of checkout progress. */
git_checkout_progress_cb progress_cb;
void *progress_payload;
@@ -254,13 +254,13 @@ typedef struct git_checkout_options {
*/
git_strarray paths;
- git_tree *baseline; /** expected content of workdir, defaults to HEAD */
+ git_tree *baseline; /**< expected content of workdir, defaults to HEAD */
- const char *target_directory; /** alternative checkout path to workdir */
+ const char *target_directory; /**< alternative checkout path to workdir */
- const char *ancestor_label; /** the name of the common ancestor side of conflicts */
- const char *our_label; /** the name of the "our" side of conflicts */
- const char *their_label; /** the name of the "their" side of conflicts */
+ const char *ancestor_label; /**< the name of the common ancestor side of conflicts */
+ const char *our_label; /**< the name of the "our" side of conflicts */
+ const char *their_label; /**< the name of the "their" side of conflicts */
} git_checkout_options;
#define GIT_CHECKOUT_OPTIONS_VERSION 1
@@ -270,14 +270,13 @@ typedef struct git_checkout_options {
* Initializes a `git_checkout_options` with default values. Equivalent to
* creating an instance with GIT_CHECKOUT_OPTIONS_INIT.
*
-* @param opts the `git_checkout_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_CHECKOUT_OPTIONS_VERSION` here.
+* @param opts the `git_checkout_options` struct to initialize.
+* @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_checkout_init_opts(
- git_checkout_options* opts,
- int version);
+GIT_EXTERN(int) git_checkout_init_options(
+ git_checkout_options *opts,
+ unsigned int version);
/**
* Updates files in the index and the working tree to match the content of
diff --git a/include/git2/cherrypick.h b/include/git2/cherrypick.h
index 7c48e6659..dea67c1ff 100644
--- a/include/git2/cherrypick.h
+++ b/include/git2/cherrypick.h
@@ -28,23 +28,22 @@ typedef struct {
git_merge_options merge_opts;
git_checkout_options checkout_opts;
-} git_cherry_pick_options;
+} git_cherrypick_options;
-#define GIT_CHERRY_PICK_OPTIONS_VERSION 1
-#define GIT_CHERRY_PICK_OPTIONS_INIT {GIT_CHERRY_PICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
+#define GIT_CHERRYPICK_OPTIONS_VERSION 1
+#define GIT_CHERRYPICK_OPTIONS_INIT {GIT_CHERRYPICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
/**
- * Initializes a `git_cherry_pick_options` with default values. Equivalent to
- * creating an instance with GIT_CHERRY_PICK_OPTIONS_INIT.
+ * Initializes a `git_cherrypick_options` with default values. Equivalent to
+ * creating an instance with GIT_CHERRYPICK_OPTIONS_INIT.
*
- * @param opts the `git_cherry_pick_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_CHERRY_PICK_OPTIONS_VERSION` here.
+ * @param opts the `git_cherrypick_options` struct to initialize
+ * @param version Version of struct; pass `GIT_CHERRYPICK_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_cherry_pick_init_opts(
- git_cherry_pick_options* opts,
- int version);
+GIT_EXTERN(int) git_cherrypick_init_options(
+ git_cherrypick_options *opts,
+ unsigned int version);
/**
* Cherry-picks the given commit against the given "our" commit, producing an
@@ -54,16 +53,16 @@ GIT_EXTERN(int) git_cherry_pick_init_opts(
*
* @param out pointer to store the index result in
* @param repo the repository that contains the given commits
- * @param cherry_pick_commit the commit to cherry-pick
+ * @param cherrypick_commit the commit to cherry-pick
* @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge
- * @param merge_tree_opts the merge tree options (or null for defaults)
+ * @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure.
*/
-GIT_EXTERN(int) git_cherry_pick_commit(
+GIT_EXTERN(int) git_cherrypick_commit(
git_index **out,
git_repository *repo,
- git_commit *cherry_pick_commit,
+ git_commit *cherrypick_commit,
git_commit *our_commit,
unsigned int mainline,
const git_merge_options *merge_options);
@@ -73,13 +72,13 @@ GIT_EXTERN(int) git_cherry_pick_commit(
*
* @param repo the repository to cherry-pick
* @param commit the commit to cherry-pick
- * @param cherry_pick_options the cherry-pick options (or null for defaults)
+ * @param cherrypick_options the cherry-pick options (or null for defaults)
* @return zero on success, -1 on failure.
*/
-GIT_EXTERN(int) git_cherry_pick(
+GIT_EXTERN(int) git_cherrypick(
git_repository *repo,
git_commit *commit,
- const git_cherry_pick_options *cherry_pick_options);
+ const git_cherrypick_options *cherrypick_options);
/** @} */
GIT_END_DECL
diff --git a/include/git2/clone.h b/include/git2/clone.h
index 20be1a105..fa2e25b60 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -12,6 +12,7 @@
#include "indexer.h"
#include "checkout.h"
#include "remote.h"
+#include "transport.h"
/**
@@ -24,66 +25,175 @@
GIT_BEGIN_DECL
/**
- * Clone options structure
+ * Options for bypassing the git-aware transport on clone. Bypassing
+ * it means that instead of a fetch, libgit2 will copy the object
+ * database directory instead of figuring out what it needs, which is
+ * faster. If possible, it will hardlink the files to save space.
+ */
+typedef enum {
+ /**
+ * Auto-detect (default), libgit2 will bypass the git-aware
+ * transport for local paths, but use a normal fetch for
+ * `file://` urls.
+ */
+ GIT_CLONE_LOCAL_AUTO,
+ /**
+ * Bypass the git-aware transport even for a `file://` url.
+ */
+ GIT_CLONE_LOCAL,
+ /**
+ * Do no bypass the git-aware transport
+ */
+ GIT_CLONE_NO_LOCAL,
+ /**
+ * Bypass the git-aware transport, but do not try to use
+ * hardlinks.
+ */
+ GIT_CLONE_LOCAL_NO_LINKS,
+} git_clone_local_t;
+
+/**
+ * The signature of a function matching git_remote_create, with an additional
+ * void* as a callback payload.
*
- * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this:
+ * Callers of git_clone may provide a function matching this signature to override
+ * the remote creation and customization process during a clone operation.
*
- * git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @param payload an opaque payload
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+typedef int (*git_remote_create_cb)(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload);
+
+/**
+ * The signature of a function matchin git_repository_init, with an
+ * aditional void * as callback payload.
*
- * - `checkout_opts` are option passed to the checkout step. To disable
- * checkout, set the `checkout_strategy` to GIT_CHECKOUT_NONE.
- * Generally you will want the use GIT_CHECKOUT_SAFE_CREATE to create
- * all files in the working directory for the newly cloned repository.
- * - `bare` should be set to zero (false) to create a standard repo,
- * or non-zero for a bare repo
- * - `ignore_cert_errors` should be set to 1 if errors validating the
- * remote host's certificate should be ignored.
+ * Callers of git_clone my provide a function matching this signature
+ * to override the repository creation and customization process
+ * during a clone operation.
*
- * ** "origin" remote options: **
+ * @param out the resulting repository
+ * @param path path in which to create the repository
+ * @param bare whether the repository is bare. This is the value from the clone options
+ * @param payload payload specified by the options
+ * @return 0, or a negative value to indicate error
+ */
+typedef int (*git_repository_create_cb)(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload);
+
+/**
+ * Clone options structure
+ *
+ * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this:
*
- * - `remote_name` is the name to be given to the "origin" remote. The
- * default is "origin".
- * - `checkout_branch` gives the name of the branch to checkout. NULL
- * means use the remote's HEAD.
- * - `signature` is the identity used when updating the reflog. NULL means to
- * use the default signature using the config.
+ * git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
*/
typedef struct git_clone_options {
unsigned int version;
+ /**
+ * These options are passed to the checkout step. To disable
+ * checkout, set the `checkout_strategy` to
+ * `GIT_CHECKOUT_NONE`. Generally you will want the use
+ * GIT_CHECKOUT_SAFE_CREATE to create all files in the working
+ * directory for the newly cloned repository.
+ */
git_checkout_options checkout_opts;
+
+ /**
+ * Callbacks to use for reporting fetch progress, and for acquiring
+ * credentials in the event they are needed. This parameter is ignored if
+ * the remote_cb parameter is set; if you provide a remote creation
+ * callback, then you have the opportunity to configure remote callbacks in
+ * provided function.
+ */
git_remote_callbacks remote_callbacks;
+ /**
+ * Set to zero (false) to create a standard repo, or non-zero
+ * for a bare repo
+ */
int bare;
- int ignore_cert_errors;
- const char *remote_name;
+
+ /**
+ * Whether to use a fetch or copy the object database.
+ */
+ git_clone_local_t local;
+
+ /**
+ * The name of the branch to checkout. NULL means use the
+ * remote's default branch.
+ */
const char* checkout_branch;
+
+ /**
+ * The identity used when updating the reflog. NULL means to
+ * use the default signature using the config.
+ */
git_signature *signature;
+
+ /**
+ * A callback used to create the new repository into which to
+ * clone. If NULL, the 'bare' field will be used to determine
+ * whether to create a bare repository.
+ */
+ git_repository_create_cb repository_cb;
+
+ /**
+ * An opaque payload to pass to the git_repository creation callback.
+ * This parameter is ignored unless repository_cb is non-NULL.
+ */
+ void *repository_cb_payload;
+
+ /**
+ * A callback used to create the git_remote, prior to its being
+ * used to perform the clone operation. See the documentation for
+ * git_remote_create_cb for details. This parameter may be NULL,
+ * indicating that git_clone should provide default behavior.
+ */
+ git_remote_create_cb remote_cb;
+
+ /**
+ * An opaque payload to pass to the git_remote creation callback.
+ * This parameter is ignored unless remote_cb is non-NULL.
+ */
+ void *remote_cb_payload;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION 1
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
/**
-* Initializes a `git_clone_options` with default values. Equivalent to
-* creating an instance with GIT_CLONE_OPTIONS_INIT.
-*
-* @param opts the `git_clone_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_CLONE_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_clone_options` with default values. Equivalent to
+ * creating an instance with GIT_CLONE_OPTIONS_INIT.
+ *
+ * @param opts The `git_clone_options` struct to initialize
+ * @param version Version of struct; pass `GIT_CLONE_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_clone_init_options(
- git_clone_options* opts,
- int version);
+ git_clone_options *opts,
+ unsigned int version);
/**
* Clone a remote repository.
*
- * This version handles the simple case. If you'd like to create the
- * repository or remote with non-default settings, you can create and
- * configure them and then use `git_clone_into()`.
+ * By default this creates its repository and initial remote to match
+ * git's defaults. You can use the options in the callback to
+ * customize how these are created.
*
* @param out pointer that will receive the resulting repository object
* @param url the remote repository to clone
@@ -100,30 +210,6 @@ GIT_EXTERN(int) git_clone(
const char *local_path,
const git_clone_options *options);
-/**
- * Clone into a repository
- *
- * After creating the repository and remote and configuring them for
- * paths and callbacks respectively, you can call this function to
- * perform the clone operation and optionally checkout files.
- *
- * @param repo the repository to use
- * @param remote the remote repository to clone from
- * @param co_opts options to use during checkout
- * @param branch the branch to checkout after the clone, pass NULL for the
- * remote's default branch
- * @param signature The identity used when updating the reflog.
- * @return 0 on success, any non-zero return value from a callback
- * function, or a negative value to indicate an error (use
- * `giterr_last` for a detailed error message)
- */
-GIT_EXTERN(int) git_clone_into(
- git_repository *repo,
- git_remote *remote,
- const git_checkout_options *co_opts,
- const char *branch,
- const git_signature *signature);
-
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 834330b5d..fb53a701b 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -254,7 +254,8 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
* is not direct, it will be resolved to a direct reference.
* Use "HEAD" to update the HEAD of the current branch and
* make it point to this commit. If the reference doesn't
- * exist yet, it will be created.
+ * exist yet, it will be created. If it does exist, the first
+ * parent must be the tip of this branch.
*
* @param author Signature with author and author time of commit
*
@@ -329,7 +330,7 @@ GIT_EXTERN(int) git_commit_create_v(
*
* The `update_ref` value works as in the regular `git_commit_create()`,
* updating the ref to point to the newly rewritten commit. If you want
- * to amend a commit that is not currently the HEAD of the branch and then
+ * to amend a commit that is not currently the tip of the branch and then
* rewrite the following commits to reach a ref, pass this as NULL and
* update the rest of the commit chain and ref separately.
*
diff --git a/include/git2/config.h b/include/git2/config.h
index 663b4f6ba..21a5825a5 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -226,6 +226,22 @@ GIT_EXTERN(int) git_config_open_level(
*/
GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
+/**
+ * Create a snapshot of the configuration
+ *
+ * Create a snapshot of the current state of a configuration, which
+ * allows you to look into a consistent view of the configuration for
+ * looking up complex values (e.g. a remote, submodule).
+ *
+ * The string returned when querying such a config object is valid
+ * until it is freed.
+ *
+ * @param out pointer in which to store the snapshot config object
+ * @param config configuration to snapshot
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config);
+
/**
* Reload changed config files
@@ -312,7 +328,8 @@ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char
* Get the value of a string config variable.
*
* The string is owned by the variable and should not be freed by the
- * user.
+ * user. The pointer will be valid until the next operation on this
+ * config object.
*
* All config files will be looked into, in the order of their
* defined level. A higher level means a higher priority. The
@@ -353,6 +370,9 @@ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, cons
/**
* Return the current entry and advance the iterator
*
+ * The pointers returned by this function are valid until the iterator
+ * is freed.
+ *
* @param entry pointer to store the entry
* @param iter the iterator
* @return 0 or an error code. GIT_ITEROVER if the iteration has completed
@@ -451,6 +471,9 @@ GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, co
* If the callback returns a non-zero value, the function stops iterating
* and returns that value to the caller.
*
+ * The pointers passed to the callback are only valid as long as the
+ * iteration is ongoing.
+ *
* @param cfg where to get the variables from
* @param callback the function to call on each variable
* @param payload the data to pass to the callback
@@ -491,6 +514,9 @@ GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const gi
* regular expression that filters which config keys are passed to the
* callback.
*
+ * The pointers passed to the callback are only valid as long as the
+ * iteration is ongoing.
+ *
* @param cfg where to get the variables from
* @param regexp regular expression to match against config names
* @param callback the function to call on each variable
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 273f471b6..8147fd31c 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -145,6 +145,19 @@ typedef enum {
*/
GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
+ /** When diff finds a file in the working directory with stat
+ * information different from the index, but the OID ends up being the
+ * same, write the correct stat information into the index. Note:
+ * without this flag, diff will always leave the index untouched.
+ */
+ GIT_DIFF_UPDATE_INDEX = (1u << 15),
+
+ /** Include unreadable files in the diff */
+ GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16),
+
+ /** Include unreadable files in the diff */
+ GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17),
+
/*
* Options controlling how output will be generated
*/
@@ -205,9 +218,9 @@ typedef struct git_diff git_diff;
* considered reserved for internal or future use.
*/
typedef enum {
- GIT_DIFF_FLAG_BINARY = (1u << 0), /** file(s) treated as binary data */
- GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */
- GIT_DIFF_FLAG_VALID_ID = (1u << 2), /** `id` value is known correct */
+ GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */
+ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */
+ GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */
} git_diff_flag_t;
/**
@@ -221,15 +234,16 @@ typedef enum {
* DELETED pairs).
*/
typedef enum {
- GIT_DELTA_UNMODIFIED = 0, /** no changes */
- GIT_DELTA_ADDED = 1, /** entry does not exist in old version */
- GIT_DELTA_DELETED = 2, /** entry does not exist in new version */
- GIT_DELTA_MODIFIED = 3, /** entry content changed between old and new */
- GIT_DELTA_RENAMED = 4, /** entry was renamed between old and new */
- GIT_DELTA_COPIED = 5, /** entry was copied from another old entry */
- GIT_DELTA_IGNORED = 6, /** entry is ignored item in workdir */
- GIT_DELTA_UNTRACKED = 7, /** entry is untracked item in workdir */
- GIT_DELTA_TYPECHANGE = 8, /** type of entry changed between old and new */
+ GIT_DELTA_UNMODIFIED = 0, /**< no changes */
+ GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
+ GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */
+ GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */
+ GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */
+ GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
+ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
+ GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
+ GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
+ GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
} git_delta_t;
/**
@@ -381,17 +395,16 @@ typedef struct {
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
/**
-* Initializes a `git_diff_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_OPTIONS_INIT.
-*
-* @param opts the `git_diff_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_options` with default values. Equivalent to
+ * creating an instance with GIT_DIFF_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_init_options(
- git_diff_options* opts,
- int version);
+ git_diff_options *opts,
+ unsigned int version);
/**
* When iterating over a diff, callback that will be made per file.
@@ -410,12 +423,12 @@ typedef int (*git_diff_file_cb)(
*/
typedef struct git_diff_hunk git_diff_hunk;
struct git_diff_hunk {
- int old_start; /** Starting line number in old_file */
- int old_lines; /** Number of lines in old_file */
- int new_start; /** Starting line number in new_file */
- int new_lines; /** Number of lines in new_file */
- size_t header_len; /** Number of bytes in header text */
- char header[128]; /** Header text, NUL-byte terminated */
+ int old_start; /**< Starting line number in old_file */
+ int old_lines; /**< Number of lines in old_file */
+ int new_start; /**< Starting line number in new_file */
+ int new_lines; /**< Number of lines in new_file */
+ size_t header_len; /**< Number of bytes in header text */
+ char header[128]; /**< Header text, NUL-byte terminated */
};
/**
@@ -458,13 +471,13 @@ typedef enum {
*/
typedef struct git_diff_line git_diff_line;
struct git_diff_line {
- char origin; /** A git_diff_line_t value */
- int old_lineno; /** Line number in old file or -1 for added line */
- int new_lineno; /** Line number in new file or -1 for deleted line */
- int num_lines; /** Number of newline characters in content */
- size_t content_len; /** Number of bytes of data */
- git_off_t content_offset; /** Offset in the original file to the content */
- const char *content; /** Pointer to diff text, not NUL-byte terminated */
+ char origin; /**< A git_diff_line_t value */
+ int old_lineno; /**< Line number in old file or -1 for added line */
+ int new_lineno; /**< Line number in new file or -1 for deleted line */
+ int num_lines; /**< Number of newline characters in content */
+ size_t content_len; /**< Number of bytes of data */
+ git_off_t content_offset; /**< Offset in the original file to the content */
+ const char *content; /**< Pointer to diff text, not NUL-byte terminated */
};
/**
@@ -476,10 +489,10 @@ struct git_diff_line {
* of lines of file and hunk headers.
*/
typedef int (*git_diff_line_cb)(
- const git_diff_delta *delta, /** delta that contains this data */
- const git_diff_hunk *hunk, /** hunk containing this data */
- const git_diff_line *line, /** line data */
- void *payload); /** user reference data */
+ const git_diff_delta *delta, /**< delta that contains this data */
+ const git_diff_hunk *hunk, /**< hunk containing this data */
+ const git_diff_line *line, /**< line data */
+ void *payload); /**< user reference data */
/**
* Flags to control the behavior of diff rename/copy detection.
@@ -622,17 +635,16 @@ typedef struct {
#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
/**
-* Initializes a `git_diff_find_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
-*
-* @param opts the `git_diff_find_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_FIND_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_find_options` with default values. Equivalent to
+ * creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_find_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_FIND_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_find_init_options(
- git_diff_find_options* opts,
- int version);
+ git_diff_find_options *opts,
+ unsigned int version);
/** @name Diff Generator Functions
*
@@ -804,23 +816,6 @@ GIT_EXTERN(int) git_diff_find_similar(
git_diff *diff,
const git_diff_find_options *options);
-/**
- * Initialize diff options structure
- *
- * In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to
- * initialize the diff options structure, but in some cases that is not
- * going to work. You can call this function instead. Note that you
- * must pass both a pointer to the structure to be initialized and the
- * `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with.
- *
- * @param options Pointer to git_diff_options memory to be initialized
- * @param version Should be `GIT_DIFF_OPTIONS_VERSION`
- * @return 0 on success, negative on failure (such as unsupported version)
- */
-GIT_EXTERN(int) git_diff_options_init(
- git_diff_options *options,
- unsigned int version);
-
/**@}*/
@@ -1233,17 +1228,17 @@ GIT_EXTERN(int) git_diff_commit_as_email(
const git_diff_options *diff_opts);
/**
-* Initializes a `git_diff_format_email_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
-*
-* @param opts the `git_diff_format_email_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_format_email_options` with default values.
+ *
+ * Equivalent to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_format_email_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_format_email_init_options(
git_diff_format_email_options *opts,
- int version);
+ unsigned int version);
GIT_END_DECL
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 287498423..1e3ed3acb 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -19,13 +19,13 @@ GIT_BEGIN_DECL
/** Generic return codes */
typedef enum {
- GIT_OK = 0, /*< No error */
+ GIT_OK = 0, /**< No error */
- GIT_ERROR = -1, /*< Generic error */
- GIT_ENOTFOUND = -3, /*< Requested object could not be found */
- GIT_EEXISTS = -4, /*< Object exists preventing operation */
- GIT_EAMBIGUOUS = -5, /*< More than one object matches */
- GIT_EBUFS = -6, /*< Output buffer too short to hold data */
+ GIT_ERROR = -1, /**< Generic error */
+ GIT_ENOTFOUND = -3, /**< Requested object could not be found */
+ GIT_EEXISTS = -4, /**< Object exists preventing operation */
+ GIT_EAMBIGUOUS = -5, /**< More than one object matches */
+ GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration)
@@ -33,17 +33,19 @@ typedef enum {
*/
GIT_EUSER = -7,
- GIT_EBAREREPO = -8, /*< Operation not allowed on bare repository */
- GIT_EUNBORNBRANCH = -9, /*< HEAD refers to branch with no commits */
- GIT_EUNMERGED = -10, /*< Merge in progress prevented operation */
- GIT_ENONFASTFORWARD = -11, /*< Reference was not fast-forwardable */
- GIT_EINVALIDSPEC = -12, /*< Name/ref spec was not in a valid format */
- GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */
- GIT_ELOCKED = -14, /*< Lock file prevented operation */
- GIT_EMODIFIED = -15, /*< Reference value does not match expected */
+ GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
+ GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
+ GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
+ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
+ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
+ GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
+ GIT_ELOCKED = -14, /**< Lock file prevented operation */
+ GIT_EMODIFIED = -15, /**< Reference value does not match expected */
+ GIT_EAUTH = -16, /**< Authentication error */
+ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
- GIT_PASSTHROUGH = -30, /*< Internal only */
- GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */
+ GIT_PASSTHROUGH = -30, /**< Internal only */
+ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
} git_error_code;
/**
diff --git a/include/git2/filter.h b/include/git2/filter.h
index f96b6766b..e57a67e73 100644
--- a/include/git2/filter.h
+++ b/include/git2/filter.h
@@ -35,6 +35,11 @@ typedef enum {
GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
} git_filter_mode_t;
+typedef enum {
+ GIT_FILTER_OPT_DEFAULT = 0u,
+ GIT_FILTER_OPT_ALLOW_UNSAFE = (1u << 0),
+} git_filter_opt_t;
+
/**
* A filter that can transform file data
*
@@ -75,6 +80,7 @@ typedef struct git_filter_list git_filter_list;
* @param blob The blob to which the filter will be applied (if known)
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @param options Combination of `git_filter_opt_t` flags
* @return 0 on success (which could still return NULL if no filters are
* needed for the requested file), <0 on error
*/
@@ -83,7 +89,8 @@ GIT_EXTERN(int) git_filter_list_load(
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
- git_filter_mode_t mode);
+ git_filter_mode_t mode,
+ uint32_t options);
/**
* Apply filter list to a data buffer.
diff --git a/include/git2/index.h b/include/git2/index.h
index 05e58a632..0b4476b4e 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -61,7 +61,7 @@ typedef struct git_index_entry {
unsigned short flags;
unsigned short flags_extended;
- char *path;
+ const char *path;
} git_index_entry;
/**
@@ -73,10 +73,13 @@ typedef struct git_index_entry {
*/
#define GIT_IDXENTRY_NAMEMASK (0x0fff)
#define GIT_IDXENTRY_STAGEMASK (0x3000)
-#define GIT_IDXENTRY_EXTENDED (0x4000)
-#define GIT_IDXENTRY_VALID (0x8000)
#define GIT_IDXENTRY_STAGESHIFT 12
+typedef enum {
+ GIT_IDXENTRY_EXTENDED = (0x4000),
+ GIT_IDXENTRY_VALID = (0x8000),
+} git_indxentry_flag_t;
+
#define GIT_IDXENTRY_STAGE(E) \
(((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
@@ -92,36 +95,36 @@ typedef struct git_index_entry {
* in-memory only and used by libgit2. Only the flags in
* `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk.
*
- * These bitmasks match the three fields in the `git_index_entry`
- * `flags_extended` value that belong on disk. You can use them to
- * interpret the data in the `flags_extended`.
+ * Thee first three bitmasks match the three fields in the
+ * `git_index_entry` `flags_extended` value that belong on disk. You
+ * can use them to interpret the data in the `flags_extended`.
+ *
+ * The rest of the bitmasks match the other fields in the `git_index_entry`
+ * `flags_extended` value that are only used in-memory by libgit2.
+ * You can use them to interpret the data in the `flags_extended`.
+ *
*/
-#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
-#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
-/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */
-#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
+typedef enum {
-#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
+ GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
+ GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
+ /** Reserved for future extension */
+ GIT_IDXENTRY_EXTENDED2 = (1 << 15),
-/**
- * Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended`
- *
- * These bitmasks match the other fields in the `git_index_entry`
- * `flags_extended` value that are only used in-memory by libgit2. You
- * can use them to interpret the data in the `flags_extended`.
- */
-#define GIT_IDXENTRY_UPDATE (1 << 0)
-#define GIT_IDXENTRY_REMOVE (1 << 1)
-#define GIT_IDXENTRY_UPTODATE (1 << 2)
-#define GIT_IDXENTRY_ADDED (1 << 3)
+ GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE),
+ GIT_IDXENTRY_UPDATE = (1 << 0),
+ GIT_IDXENTRY_REMOVE = (1 << 1),
+ GIT_IDXENTRY_UPTODATE = (1 << 2),
+ GIT_IDXENTRY_ADDED = (1 << 3),
-#define GIT_IDXENTRY_HASHED (1 << 4)
-#define GIT_IDXENTRY_UNHASHED (1 << 5)
-#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */
-#define GIT_IDXENTRY_CONFLICTED (1 << 7)
+ GIT_IDXENTRY_HASHED = (1 << 4),
+ GIT_IDXENTRY_UNHASHED = (1 << 5),
+ GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
+ GIT_IDXENTRY_CONFLICTED = (1 << 7),
-#define GIT_IDXENTRY_UNPACKED (1 << 8)
-#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
+ GIT_IDXENTRY_UNPACKED = (1 << 8),
+ GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
+} git_idxentry_extended_flag_t;
/** Capabilities of system that affect index actions. */
typedef enum {
@@ -412,10 +415,10 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
*
* This entry is calculated from the entry's flag attribute like this:
*
- * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
*
* @param entry The entry
- * @returns the stage number
+ * @return the stage number
*/
GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 6d97e81e6..bd5ebc1bd 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
+#include "oidarray.h"
#include "checkout.h"
#include "index.h"
@@ -57,11 +58,11 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_file_init_input(
git_merge_file_input *opts,
- int version);
+ unsigned int version);
/**
* Flags for `git_merge_tree` options. A combination of these flags can be
- * passed in via the `flags` value in the `git_merge_tree_opts`.
+ * passed in via the `flags` value in the `git_merge_options`.
*/
typedef enum {
/**
@@ -73,7 +74,7 @@ typedef enum {
} git_merge_tree_flag_t;
/**
- * Merge file favor options for `git_merge_trees_opts` instruct the file-level
+ * Merge file favor options for `git_merge_options` instruct the file-level
* merging functionality how to deal with conflicting regions of the files.
*/
typedef enum {
@@ -164,7 +165,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_file_init_options(
git_merge_file_options *opts,
- int version);
+ unsigned int version);
typedef struct {
/**
@@ -232,7 +233,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_init_options(
git_merge_options *opts,
- int version);
+ unsigned int version);
/**
* The results of `git_merge_analysis` indicate the merge opportunities.
@@ -268,6 +269,26 @@ typedef enum {
GIT_MERGE_ANALYSIS_UNBORN = (1 << 3),
} git_merge_analysis_t;
+typedef enum {
+ /*
+ * No configuration was found that suggests a preferred behavior for
+ * merge.
+ */
+ GIT_MERGE_PREFERENCE_NONE = 0,
+
+ /**
+ * There is a `merge.ff=false` configuration setting, suggesting that
+ * the user does not want to allow a fast-forward merge.
+ */
+ GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0),
+
+ /**
+ * There is a `merge.ff=only` configuration setting, suggesting that
+ * the user only wants fast-forward merges.
+ */
+ GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1),
+} git_merge_preference_t;
+
/**
* Analyzes the given branch(es) and determines the opportunities for
* merging them into the HEAD of the repository.
@@ -280,6 +301,7 @@ typedef enum {
*/
GIT_EXTERN(int) git_merge_analysis(
git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
git_repository *repo,
const git_merge_head **their_heads,
size_t their_heads_len);
@@ -300,6 +322,21 @@ GIT_EXTERN(int) git_merge_base(
const git_oid *two);
/**
+ * Find merge bases between two commits
+ *
+ * @param out array in which to store the resulting ids
+ * @param repo the repository where the commits exist
+ * @param one one of the commits
+ * @param two the other commit
+ * @return 0 on success, GIT_ENOTFOUND if not found or error code
+ */
+GIT_EXTERN(int) git_merge_bases(
+ git_oidarray *out,
+ git_repository *repo,
+ const git_oid *one,
+ const git_oid *two);
+
+/**
* Find a merge base given a list of commits
*
* @param out the OID of a merge base considering all the commits
@@ -378,8 +415,8 @@ GIT_EXTERN(int) git_merge_head_from_id(
/**
* Gets the commit ID that the given `git_merge_head` refers to.
*
- * @param id pointer to commit id to be filled in
* @param head the given merge head
+ * @return commit id
*/
GIT_EXTERN(const git_oid *) git_merge_head_id(
const git_merge_head *head);
@@ -424,8 +461,8 @@ GIT_EXTERN(int) git_merge_file(
* @param out The git_merge_file_result to be filled in
* @param repo The repository
* @param ancestor The index entry for the ancestor file (stage level 1)
- * @param our_path The index entry for our file (stage level 2)
- * @param their_path The index entry for their file (stage level 3)
+ * @param ours The index entry for our file (stage level 2)
+ * @param theirs The index entry for their file (stage level 3)
* @param opts The merge file options or NULL
* @return 0 on success or error code
*/
@@ -497,8 +534,8 @@ GIT_EXTERN(int) git_merge_commits(
* completes, resolve any conflicts and prepare a commit.
*
* @param repo the repository to merge
- * @param merge_heads the heads to merge into
- * @param merge_heads_len the number of heads to merge
+ * @param their_heads the heads to merge into
+ * @param their_heads_len the number of heads to merge
* @param merge_opts merge options
* @param checkout_opts checkout options
* @return 0 on success or error code
diff --git a/include/git2/message.h b/include/git2/message.h
index bcdb72f6a..d78b1dce5 100644
--- a/include/git2/message.h
+++ b/include/git2/message.h
@@ -29,12 +29,14 @@ GIT_BEGIN_DECL
*
* @param message The message to be prettified.
*
- * @param strip_comments Non-zero to remove lines starting with "#", 0 to
- * leave them in.
+ * @param strip_comments Non-zero to remove comment lines, 0 to leave them in.
+ *
+ * @param comment_char Comment character. Lines starting with this character
+ * are considered to be comments and removed if `strip_comments` is non-zero.
*
* @return 0 or an error code.
*/
-GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments);
+GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
/** @} */
GIT_END_DECL
diff --git a/include/git2/net.h b/include/git2/net.h
index e70ba1f71..a727696a2 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -41,6 +41,11 @@ struct git_remote_head {
git_oid oid;
git_oid loid;
char *name;
+ /**
+ * If the server send a symref mapping for this ref, this will
+ * point to the target.
+ */
+ char *symref_target;
};
/**
diff --git a/include/git2/object.h b/include/git2/object.h
index 7417ea913..9b13d824e 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -107,6 +107,11 @@ GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj);
/**
* Get a short abbreviated OID string for the object
*
+ * This starts at the "core.abbrev" length (default 7 characters) and
+ * iteratively extends to a longer string if that length is ambiguous.
+ * The result will be unambiguous (at least until new objects are added to
+ * the repository).
+ *
* @param out Buffer to write string into
* @param obj The object to get an ID for
* @return 0 on success, <0 for error
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 1cfd4e5e2..db2f3af70 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -116,13 +116,17 @@ GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id);
GIT_EXTERN(void) git_oid_pathfmt(char *out, const git_oid *id);
/**
- * Format a git_oid into a newly allocated c-string.
+ * Format a git_oid into a statically allocated c-string.
+ *
+ * The c-string is owned by the library and should not be freed
+ * by the user. If libgit2 is built with thread support, the string
+ * will be stored in TLS (i.e. one buffer per thread) to allow for
+ * concurrent calls of the function.
*
* @param id the oid structure to format
- * @return the c-string; NULL if memory is exhausted. Caller must
- * deallocate the string with git__free().
+ * @return the c-string
*/
-GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id);
+GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid);
/**
* Format a git_oid into a buffer as a hex format c-string.
diff --git a/include/git2/oidarray.h b/include/git2/oidarray.h
new file mode 100644
index 000000000..0b3204597
--- /dev/null
+++ b/include/git2/oidarray.h
@@ -0,0 +1,40 @@
+/*
+ * 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_git_oidarray_h__
+#define INCLUDE_git_oidarray_h__
+
+#include "common.h"
+#include "oid.h"
+
+GIT_BEGIN_DECL
+
+/** Array of object ids */
+typedef struct git_oidarray {
+ git_oid *ids;
+ size_t count;
+} git_oidarray;
+
+/**
+ * Free the OID array
+ *
+ * This method must (and must only) be called on `git_oidarray`
+ * objects where the array is allocated by the library. Not doing so,
+ * will result in a memory leak.
+ *
+ * This does not free the `git_oidarray` itself, since the library will
+ * never allocate that object directly itself (it is more commonly embedded
+ * inside another struct or created on the stack).
+ *
+ * @param array git_oidarray from which to free oid data
+ */
+GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/include/git2/pathspec.h b/include/git2/pathspec.h
index 2fb0bb716..de6f027c5 100644
--- a/include/git2/pathspec.h
+++ b/include/git2/pathspec.h
@@ -12,6 +12,8 @@
#include "strarray.h"
#include "diff.h"
+GIT_BEGIN_DECL
+
/**
* Compiled pathspec
*/
@@ -257,4 +259,5 @@ GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
const git_pathspec_match_list *m, size_t pos);
+GIT_END_DECL
#endif
diff --git a/include/git2/push.h b/include/git2/push.h
index 7a8bec12c..cbf115661 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -49,8 +49,8 @@ typedef struct {
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_push_init_options(
- git_push_options* opts,
- int version);
+ git_push_options *opts,
+ unsigned int version);
/** Push network progress notification function */
typedef int (*git_push_transfer_progress)(
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index df06e1b8e..ac42a231c 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -69,7 +69,7 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const g
*
* @param repo the repository
* @param old_name the old name of the reference
- * @param new_name the new name of the reference
+ * @param name the new name of the reference
* @return 0 on success, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 6a1db65a8..e5bb15c7c 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -178,7 +178,6 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
- * @param force Overwrite existing references
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
@@ -221,7 +220,6 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo,
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
- * @param force Overwrite existing references
* @param current_id The expected value of the reference at the time of update
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
@@ -415,7 +413,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
* This method removes the named reference from the repository without
* looking at its old value.
*
- * @param ref The reference to remove
+ * @param name The reference to remove
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name);
@@ -525,6 +523,17 @@ GIT_EXTERN(int) git_reference_iterator_glob_new(
*/
GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
+/**
+ * Get the next reference's name
+ *
+ * This function is provided for convenience in case only the names
+ * are interesting as it avoids the allocation of the `git_reference`
+ * object which `git_reference_next()` needs.
+ *
+ * @param out pointer in which to store the string
+ * @param iter the iterator
+ * @return 0, GIT_ITEROVER if there are no more; or an error code
+ */
GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
/**
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 11e1e26d0..055f5e517 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -384,15 +384,12 @@ GIT_EXTERN(int) git_remote_fetch(
const char *reflog_message);
/**
- * Return whether a string is a valid remote URL
*
- * @param url the url to check
- * @return 1 if the url is valid, 0 otherwise
- */
-GIT_EXTERN(int) git_remote_valid_url(const char *url);
-
-/**
- * Return whether the passed URL is supported by this version of the library.
+ * Return whether the library supports a particular URL scheme
+ *
+ * Both the built-in and externally-registered transport lists are
+ * searched for a transport which supports the scheme of the given
+ * URL.
*
* @param url the url to check
* @return 1 if the url is supported, 0 otherwise
@@ -411,30 +408,6 @@ GIT_EXTERN(int) git_remote_supported_url(const char* url);
GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo);
/**
- * Choose whether to check the server's certificate (applies to HTTPS only)
- *
- * @param remote the remote to configure
- * @param check whether to check the server's certificate (defaults to yes)
- */
-GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
-
-/**
- * Sets a custom transport for the remote. The caller can use this function
- * to bypass the automatic discovery of a transport by URL scheme (i.e.
- * http://, https://, git://) and supply their own transport to be used
- * instead. After providing the transport to a remote using this function,
- * the transport object belongs exclusively to that remote, and the remote will
- * free it when it is freed with git_remote_free.
- *
- * @param remote the remote to configure
- * @param transport the transport object for the remote to use
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_set_transport(
- git_remote *remote,
- git_transport *transport);
-
-/**
* Argument to the completion callback which tells it which operation
* finished.
*/
@@ -475,6 +448,14 @@ struct git_remote_callbacks {
git_cred_acquire_cb credentials;
/**
+ * If cert verification fails, this will be called to let the
+ * user make the final decision of whether to allow the
+ * connection to proceed. Returns 1 to allow the connection, 0
+ * to disallow it or a negative value to indicate an error.
+ */
+ git_transport_certificate_check_cb certificate_check;
+
+ /**
* During the download of new data, this will be regularly
* called with the current count of progress done by the
* indexer.
@@ -501,14 +482,13 @@ struct git_remote_callbacks {
* Initializes a `git_remote_callbacks` with default values. Equivalent to
* creating an instance with GIT_REMOTE_CALLBACKS_INIT.
*
- * @param opts the `git_remote_callbacks` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REMOTE_CALLBACKS_VERSION` here.
+ * @param opts the `git_remote_callbacks` struct to initialize
+ * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_remote_init_callbacks(
- git_remote_callbacks* opts,
- int version);
+ git_remote_callbacks *opts,
+ unsigned int version);
/**
* Set the callbacks for a remote
@@ -573,18 +553,17 @@ GIT_EXTERN(void) git_remote_set_autotag(
*
* A temporary in-memory remote cannot be given a name with this method.
*
+ * @param problems non-default refspecs cannot be renamed and will be
+ * stored here for further processing by the caller. Always free this
+ * strarray on succesful return.
* @param remote the remote to rename
* @param new_name the new name the remote should bear
- * @param callback Optional callback to notify the consumer of fetch refspecs
- * that haven't been automatically updated and need potential manual tweaking.
- * @param payload Additional data to pass to the callback
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
*/
GIT_EXTERN(int) git_remote_rename(
+ git_strarray *problems,
git_remote *remote,
- const char *new_name,
- git_remote_rename_problem_cb callback,
- void *payload);
+ const char *new_name);
/**
* Retrieve the update FETCH_HEAD setting.
@@ -611,6 +590,35 @@ GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
*/
GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
+/**
+* Delete an existing persisted remote.
+*
+* All remote-tracking branches and configuration settings
+* for the remote will be removed.
+*
+* @param remote A valid remote
+* @return 0 on success, or an error code.
+*/
+GIT_EXTERN(int) git_remote_delete(git_remote *remote);
+
+/**
+ * Retrieve the name of the remote's default branch
+ *
+ * The default branch of a repository is the branch which HEAD points
+ * to. If the remote does not support reporting this information
+ * directly, it performs the guess as git does; that is, if there are
+ * multiple branches which point to the same commit, the first one is
+ * chosen. If the master branch is a candidate, it wins.
+ *
+ * This function must only be called after connecting.
+ *
+ * @param out the buffern in which to store the reference name
+ * @param remote the remote
+ * @return 0, GIT_ENOTFOUND if the remote does not have any references
+ * or none of them point to HEAD's commit, or an error message.
+ */
+GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 4433e71a2..268782648 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -196,6 +196,8 @@ GIT_EXTERN(int) git_repository_init(
* looking the "template_path" from the options if set, or the
* `init.templatedir` global config if not, or falling back on
* "/usr/share/git-core/templates" if it exists.
+ * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is
+ * specified, use relative paths for the gitdir and core.worktree.
*/
typedef enum {
GIT_REPOSITORY_INIT_BARE = (1u << 0),
@@ -204,6 +206,7 @@ typedef enum {
GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6),
} git_repository_init_flag_t;
/**
@@ -271,14 +274,13 @@ typedef struct {
* Initializes a `git_repository_init_options` with default values. Equivalent
* to creating an instance with GIT_REPOSITORY_INIT_OPTIONS_INIT.
*
- * @param opts the `git_repository_init_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REPOSITORY_INIT_OPTIONS_VERSION` here.
+ * @param opts the `git_repository_init_options` struct to initialize
+ * @param version Version of struct; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_repository_init_init_options(
- git_repository_init_options* opts,
- int version);
+ git_repository_init_options *opts,
+ unsigned int version);
/**
* Create a new Git repository in the given folder with extended controls.
@@ -409,13 +411,29 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
* The configuration file must be freed once it's no longer
* being used by the user.
*
- * @param out Pointer to store the loaded config file
+ * @param out Pointer to store the loaded configuration
* @param repo A repository object
* @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
/**
+ * Get a snapshot of the repository's configuration
+ *
+ * Convenience function to take a snapshot from the repository's
+ * configuration. The contents of this snapshot will not change,
+ * even if the underlying config files are modified.
+ *
+ * The configuration file must be freed once it's no longer
+ * being used by the user.
+ *
+ * @param out Pointer to store the loaded configuration
+ * @param repo the repository
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo);
+
+/**
* Get the Object Database for this repository.
*
* If a custom ODB has not been set, the default
@@ -547,6 +565,10 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(
* hash a file in the repository and you want to apply filtering rules (e.g.
* crlf filters) before generating the SHA, then use this function.
*
+ * Note: if the repository has `core.safecrlf` set to fail and the
+ * filtering triggers that failure, then this function will return an
+ * error and not calculate the hash of the file.
+ *
* @param out Output value of calculated SHA
* @param repo Repository pointer
* @param path Path to file on disk whose contents should be hashed. If the
@@ -556,6 +578,7 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(
* NULL, then the `path` parameter will be used instead. If
* this is passed as the empty string, then no filters will be
* applied when calculating the hash.
+ * @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_hashfile(
git_oid *out,
@@ -629,7 +652,7 @@ GIT_EXTERN(int) git_repository_set_head_detached(
*
* @param repo Repository pointer
* @param signature The identity that will used to populate the reflog entry
- * @param log_message The one line long message to be appended to the reflog
+ * @param reflog_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch or an error code
*/
@@ -642,7 +665,7 @@ typedef enum {
GIT_REPOSITORY_STATE_NONE,
GIT_REPOSITORY_STATE_MERGE,
GIT_REPOSITORY_STATE_REVERT,
- GIT_REPOSITORY_STATE_CHERRY_PICK,
+ GIT_REPOSITORY_STATE_CHERRYPICK,
GIT_REPOSITORY_STATE_BISECT,
GIT_REPOSITORY_STATE_REBASE,
GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
diff --git a/include/git2/reset.h b/include/git2/reset.h
index 1759cc036..ea7217efe 100644
--- a/include/git2/reset.h
+++ b/include/git2/reset.h
@@ -7,6 +7,10 @@
#ifndef INCLUDE_git_reset_h__
#define INCLUDE_git_reset_h__
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+
/**
* @file git2/reset.h
* @brief Git reset management routines
@@ -19,9 +23,9 @@ GIT_BEGIN_DECL
* Kinds of reset operation
*/
typedef enum {
- GIT_RESET_SOFT = 1, /** Move the head to the given commit */
- GIT_RESET_MIXED = 2, /** SOFT plus reset index to the commit */
- GIT_RESET_HARD = 3, /** MIXED plus changes in working tree discarded */
+ GIT_RESET_SOFT = 1, /**< Move the head to the given commit */
+ GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */
+ GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */
} git_reset_t;
/**
diff --git a/include/git2/revert.h b/include/git2/revert.h
index 3a6beb6b8..ab9dd9af9 100644
--- a/include/git2/revert.h
+++ b/include/git2/revert.h
@@ -37,14 +37,13 @@ typedef struct {
* Initializes a `git_revert_options` with default values. Equivalent to
* creating an instance with GIT_REVERT_OPTIONS_INIT.
*
- * @param opts the `git_revert_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REVERT_OPTIONS_VERSION` here.
+ * @param opts the `git_revert_options` struct to initialize
+ * @param version Version of struct; pass `GIT_REVERT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_revert_init_opts(
- git_revert_options* opts,
- int version);
+GIT_EXTERN(int) git_revert_init_options(
+ git_revert_options *opts,
+ unsigned int version);
/**
* Reverts the given commit against the given "our" commit, producing an
@@ -57,10 +56,10 @@ GIT_EXTERN(int) git_revert_init_opts(
* @param revert_commit the commit to revert
* @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge
- * @param merge_tree_opts the merge tree options (or null for defaults)
+ * @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure.
*/
-int git_revert_commit(
+GIT_EXTERN(int) git_revert_commit(
git_index **out,
git_repository *repo,
git_commit *revert_commit,
@@ -72,9 +71,8 @@ int git_revert_commit(
* Reverts the given commit, producing changes in the working directory.
*
* @param repo the repository to revert
- * @param commits the commits to revert
- * @param commits_len the number of commits to revert
- * @param flags merge flags
+ * @param commit the commit to revert
+ * @param given_opts merge flags
* @return zero on success, -1 on failure.
*/
GIT_EXTERN(int) git_revert(
diff --git a/include/git2/signature.h b/include/git2/signature.h
index a1dd1ec7a..feb1b4073 100644
--- a/include/git2/signature.h
+++ b/include/git2/signature.h
@@ -69,7 +69,7 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo)
* Call `git_signature_free()` to free the data.
*
* @param dest pointer where to store the copy
- * @param entry signature to duplicate
+ * @param sig signature to duplicate
* @return 0 or an error code
*/
GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig);
diff --git a/include/git2/status.h b/include/git2/status.h
index 6af45c7dd..3c86e5d7b 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -43,6 +43,7 @@ typedef enum {
GIT_STATUS_WT_DELETED = (1u << 9),
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
GIT_STATUS_WT_RENAMED = (1u << 11),
+ GIT_STATUS_WT_UNREADABLE = (1u << 12),
GIT_STATUS_IGNORED = (1u << 14),
} git_status_t;
@@ -121,6 +122,11 @@ typedef enum {
* - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
* doing a "soft" index reload (i.e. reloading the index data if the
* file on disk has been modified outside libgit2).
+ * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
+ * in the index for files that are unchanged but have out of date stat
+ * information in the index. It will result in less work being done on
+ * subsequent calls to get status. This is mutually exclusive with the
+ * NO_REFRESH option.
*
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
@@ -128,19 +134,22 @@ typedef enum {
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
*/
typedef enum {
- GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
- GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
- GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
- GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
- GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
- GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
- GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
- GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
- GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
- GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
- GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
- GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
+ GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
+ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
+ GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
+ GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+ GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15),
} git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \
@@ -178,14 +187,13 @@ typedef struct {
* Initializes a `git_status_options` with default values. Equivalent to
* creating an instance with GIT_STATUS_OPTIONS_INIT.
*
- * @param opts the `git_status_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_STATUS_OPTIONS_VERSION` here.
+ * @param opts The `git_status_options` instance to initialize.
+ * @param version Version of struct; pass `GIT_STATUS_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_status_init_options(
- git_status_options* opts,
- int version);
+ git_status_options *opts,
+ unsigned int version);
/**
* A status entry, providing the differences between the file as it exists
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 28e235725..616890df6 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -283,7 +283,7 @@ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
* Resolve a submodule url relative to the given repository.
*
* @param out buffer to store the absolute submodule url in
- * @param repository Pointer to repository object
+ * @param repo Pointer to repository object
* @param url Relative url
* @return 0 or an error code
*/
@@ -471,6 +471,24 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules(
GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
/**
+ * Set up the subrepository for a submodule in preparation for clone.
+ *
+ * This function can be called to init and set up a submodule
+ * repository from a submodule in preparation to clone it from
+ * its remote.
+ *
+ * @param out Output pointer to the created git repository.
+ * @param sm The submodule to create a new subrepository from.
+ * @param use_gitlink Should the workdir contain a gitlink to
+ * the repo in .git/modules vs. repo directly in workdir.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink);
+
+/**
* Copy submodule remote info into submodule repo.
*
* This copies the information about the submodules URL into the checked out
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 3df2ba327..85e0d6417 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -57,13 +57,15 @@ struct git_config_backend {
/* Open means open the file/database and parse if necessary */
int (*open)(struct git_config_backend *, git_config_level_t level);
- int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
+ int (*get)(struct git_config_backend *, const char *key, const git_config_entry **entry);
int (*set)(struct git_config_backend *, const char *key, const char *value);
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_backend *, const char *key);
int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
int (*iterator)(git_config_iterator **, struct git_config_backend *);
int (*refresh)(struct git_config_backend *);
+ /** Produce a read-only version of this backend */
+ int (*snapshot)(struct git_config_backend **, struct git_config_backend *);
void (*free)(struct git_config_backend *);
};
#define GIT_CONFIG_BACKEND_VERSION 1
@@ -73,14 +75,13 @@ struct git_config_backend {
* Initializes a `git_config_backend` with default values. Equivalent to
* creating an instance with GIT_CONFIG_BACKEND_INIT.
*
- * @param opts the `git_config_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_CONFIG_BACKEND_VERSION` here.
+ * @param opts the `git_config_backend` struct to initialize.
+ * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_config_init_backend(
- git_config_backend* backend,
- int version);
+ git_config_backend *backend,
+ unsigned int version);
/**
* Add a generic config file instance to an existing config
diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h
index bc6cdf393..48d72f4f9 100644
--- a/include/git2/sys/diff.h
+++ b/include/git2/sys/diff.h
@@ -10,6 +10,8 @@
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
+#include "git2/diff.h"
+#include "git2/status.h"
/**
* @file git2/sys/diff.h
@@ -58,6 +60,32 @@ GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
const git_diff_line *line,
void *payload); /*< payload must be a `FILE *` */
+
+typedef struct {
+ unsigned int version;
+ size_t stat_calls;
+ size_t oid_calculations;
+} git_diff_perfdata;
+
+#define GIT_DIFF_PERFDATA_VERSION 1
+#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0}
+
+/**
+ * Get performance data for a diff object.
+ *
+ * @param out Structure to be filled with diff performance data
+ * @param diff Diff to read performance data from
+ * @return 0 for success, <0 for error
+ */
+GIT_EXTERN(int) git_diff_get_perfdata(
+ git_diff_perfdata *out, const git_diff *diff);
+
+/**
+ * Get performance data for diffs from a git_status_list
+ */
+GIT_EXTERN(int) git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h
index 8fe21c9c0..60248271a 100644
--- a/include/git2/sys/filter.h
+++ b/include/git2/sys/filter.h
@@ -55,7 +55,10 @@ GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
* your own chains of filters.
*/
GIT_EXTERN(int) git_filter_list_new(
- git_filter_list **out, git_repository *repo, git_filter_mode_t mode);
+ git_filter_list **out,
+ git_repository *repo,
+ git_filter_mode_t mode,
+ uint32_t options);
/**
* Add a filter to a filter list with the given payload.
@@ -115,10 +118,15 @@ GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
/**
- * Get the git_filter_mode_t to be applied
+ * Get the git_filter_mode_t to be used
*/
GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
+/**
+ * Get the combination git_filter_opt_t options to be applied
+ */
+GIT_EXTERN(uint32_t) git_filter_source_options(const git_filter_source *src);
+
/*
* struct git_filter
*
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
index 77fe0dd31..1fc3c3159 100644
--- a/include/git2/sys/odb_backend.h
+++ b/include/git2/sys/odb_backend.h
@@ -93,14 +93,13 @@ struct git_odb_backend {
* Initializes a `git_odb_backend` with default values. Equivalent to
* creating an instance with GIT_ODB_BACKEND_INIT.
*
- * @param opts the `git_odb_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_ODB_BACKEND_VERSION` here.
+ * @param opts the `git_odb_backend` struct to initialize.
+ * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_odb_init_backend(
- git_odb_backend* backend,
- int version);
+ git_odb_backend *backend,
+ unsigned int version);
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index dce142c77..3b216a287 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -162,14 +162,13 @@ struct git_refdb_backend {
* Initializes a `git_refdb_backend` with default values. Equivalent to
* creating an instance with GIT_REFDB_BACKEND_INIT.
*
- * @param opts the `git_refdb_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REFDB_BACKEND_VERSION` here.
+ * @param opts the `git_refdb_backend` struct to initialize
+ * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_refdb_init_backend(
- git_refdb_backend* backend,
- int version);
+ git_refdb_backend *backend,
+ unsigned int version);
/**
* Constructors for default filesystem-based refdb backend
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index 36f8b5836..dd7b22e06 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -119,6 +119,19 @@ GIT_EXTERN(void) git_repository_set_refdb(git_repository *repo, git_refdb *refdb
*/
GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
+/**
+ * Set a repository to be bare.
+ *
+ * Clear the working directory and set core.bare to true. You may also
+ * want to call `git_repository_set_index(repo, NULL)` since a bare repo
+ * typically does not have an index, but this function will not do that
+ * for you.
+ *
+ * @param repo Repo to make bare
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_repository_set_bare(git_repository *repo);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
new file mode 100644
index 000000000..1e8f4e4ed
--- /dev/null
+++ b/include/git2/sys/transport.h
@@ -0,0 +1,352 @@
+/*
+ * 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_sys_git_transport_h
+#define INCLUDE_sys_git_transport_h
+
+#include "git2/net.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/transport.h
+ * @brief Git custom transport registration interfaces and functions
+ * @defgroup git_transport Git custom transport registration
+ * @ingroup Git
+ * @{
+ */
+
+GIT_BEGIN_DECL
+
+typedef enum {
+ GIT_TRANSPORTFLAGS_NONE = 0,
+} git_transport_flags_t;
+
+typedef struct git_transport git_transport;
+
+struct git_transport {
+ unsigned int version;
+ /* Set progress and error callbacks */
+ int (*set_callbacks)(
+ git_transport *transport,
+ git_transport_message_cb progress_cb,
+ git_transport_message_cb error_cb,
+ git_transport_certificate_check_cb certificate_check_cb,
+ void *payload);
+
+ /* Connect the transport to the remote repository, using the given
+ * direction. */
+ int (*connect)(
+ git_transport *transport,
+ const char *url,
+ git_cred_acquire_cb cred_acquire_cb,
+ void *cred_acquire_payload,
+ int direction,
+ int flags);
+
+ /* This function may be called after a successful call to
+ * connect(). The array returned is owned by the transport and
+ * is guranteed until the next call of a transport function. */
+ int (*ls)(
+ const git_remote_head ***out,
+ size_t *size,
+ git_transport *transport);
+
+ /* Executes the push whose context is in the git_push object. */
+ int (*push)(git_transport *transport, git_push *push);
+
+ /* This function may be called after a successful call to connect(), when
+ * the direction is FETCH. The function performs a negotiation to calculate
+ * the wants list for the fetch. */
+ int (*negotiate_fetch)(
+ git_transport *transport,
+ git_repository *repo,
+ const git_remote_head * const *refs,
+ size_t count);
+
+ /* This function may be called after a successful call to negotiate_fetch(),
+ * when the direction is FETCH. This function retrieves the pack file for
+ * the fetch from the remote end. */
+ int (*download_pack)(
+ git_transport *transport,
+ git_repository *repo,
+ git_transfer_progress *stats,
+ git_transfer_progress_cb progress_cb,
+ void *progress_payload);
+
+ /* Checks to see if the transport is connected */
+ int (*is_connected)(git_transport *transport);
+
+ /* Reads the flags value previously passed into connect() */
+ int (*read_flags)(git_transport *transport, int *flags);
+
+ /* Cancels any outstanding transport operation */
+ void (*cancel)(git_transport *transport);
+
+ /* This function is the reverse of connect() -- it terminates the
+ * connection to the remote end. */
+ int (*close)(git_transport *transport);
+
+ /* Frees/destructs the git_transport object. */
+ void (*free)(git_transport *transport);
+};
+
+#define GIT_TRANSPORT_VERSION 1
+#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
+
+/**
+ * Initializes a `git_transport` with default values. Equivalent to
+ * creating an instance with GIT_TRANSPORT_INIT.
+ *
+ * @param opts the `git_transport` struct to initialize
+ * @param version Version of struct; pass `GIT_TRANSPORT_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_transport_init(
+ git_transport *opts,
+ unsigned int version);
+
+/**
+ * Function to use to create a transport from a URL. The transport database
+ * is scanned to find a transport that implements the scheme of the URI (i.e.
+ * git:// or http://) and a transport object is returned to the caller.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param url The URL to connect to
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url);
+
+/**
+ * Create an ssh transport with custom git command paths
+ *
+ * This is a factory function suitable for setting as the transport
+ * callback in a remote (or for a clone in the options).
+ *
+ * The payload argument must be a strarray pointer with the paths for
+ * the `git-upload-pack` and `git-receive-pack` at index 0 and 1.
+ *
+ * @param out the resulting transport
+ * @param owner the owning remote
+ * @param payload a strarray with the paths
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload);
+
+/* Signature of a function which creates a transport */
+typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
+
+/**
+ * Add a custom transport definition, to be used in addition to the built-in
+ * set of transports that come with libgit2.
+ *
+ * The caller is responsible for synchronizing calls to git_transport_register
+ * and git_transport_unregister with other calls to the library that
+ * instantiate transports.
+ *
+ * @param prefix The scheme (ending in "://") to match, i.e. "git://"
+ * @param cb The callback used to create an instance of the transport
+ * @param param A fixed parameter to pass to cb at creation time
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_register(
+ const char *prefix,
+ git_transport_cb cb,
+ void *param);
+
+/**
+ *
+ * Unregister a custom transport definition which was previously registered
+ * with git_transport_register.
+ *
+ * @param prefix From the previous call to git_transport_register
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_unregister(
+ const char *prefix);
+
+/* Transports which come with libgit2 (match git_transport_cb). The expected
+ * value for "param" is listed in-line below. */
+
+/**
+ * Create an instance of the dummy transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_dummy(
+ git_transport **out,
+ git_remote *owner,
+ /* NULL */ void *payload);
+
+/**
+ * Create an instance of the local transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_local(
+ git_transport **out,
+ git_remote *owner,
+ /* NULL */ void *payload);
+
+/**
+ * Create an instance of the smart transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload A pointer to a git_smart_subtransport_definition
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_smart(
+ git_transport **out,
+ git_remote *owner,
+ /* (git_smart_subtransport_definition *) */ void *payload);
+
+/*
+ *** End of base transport interface ***
+ *** Begin interface for subtransports for the smart transport ***
+ */
+
+/* The smart transport knows how to speak the git protocol, but it has no
+ * knowledge of how to establish a connection between it and another endpoint,
+ * or how to move data back and forth. For this, a subtransport interface is
+ * declared, and the smart transport delegates this work to the subtransports.
+ * Three subtransports are implemented: git, http, and winhttp. (The http and
+ * winhttp transports each implement both http and https.) */
+
+/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
+ * (request/response). The smart transport handles the differences in its own
+ * logic. The git subtransport is RPC = 0, while http and winhttp are both
+ * RPC = 1. */
+
+/* Actions that the smart transport can ask
+ * a subtransport to perform */
+typedef enum {
+ GIT_SERVICE_UPLOADPACK_LS = 1,
+ GIT_SERVICE_UPLOADPACK = 2,
+ GIT_SERVICE_RECEIVEPACK_LS = 3,
+ GIT_SERVICE_RECEIVEPACK = 4,
+} git_smart_service_t;
+
+typedef struct git_smart_subtransport git_smart_subtransport;
+typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
+
+/* A stream used by the smart transport to read and write data
+ * from a subtransport */
+struct git_smart_subtransport_stream {
+ /* The owning subtransport */
+ git_smart_subtransport *subtransport;
+
+ int (*read)(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read);
+
+ int (*write)(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len);
+
+ void (*free)(
+ git_smart_subtransport_stream *stream);
+};
+
+/* An implementation of a subtransport which carries data for the
+ * smart transport */
+struct git_smart_subtransport {
+ int (* action)(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *transport,
+ const char *url,
+ git_smart_service_t action);
+
+ /* Subtransports are guaranteed a call to close() between
+ * calls to action(), except for the following two "natural" progressions
+ * of actions against a constant URL.
+ *
+ * 1. UPLOADPACK_LS -> UPLOADPACK
+ * 2. RECEIVEPACK_LS -> RECEIVEPACK */
+ int (*close)(git_smart_subtransport *transport);
+
+ void (*free)(git_smart_subtransport *transport);
+};
+
+/* A function which creates a new subtransport for the smart transport */
+typedef int (*git_smart_subtransport_cb)(
+ git_smart_subtransport **out,
+ git_transport* owner);
+
+typedef struct git_smart_subtransport_definition {
+ /* The function to use to create the git_smart_subtransport */
+ git_smart_subtransport_cb callback;
+
+ /* True if the protocol is stateless; false otherwise. For example,
+ * http:// is stateless, but git:// is not. */
+ unsigned rpc;
+} git_smart_subtransport_definition;
+
+/* Smart transport subtransports that come with libgit2 */
+
+/**
+ * Create an instance of the http subtransport. This subtransport
+ * also supports https. On Win32, this subtransport may be implemented
+ * using the WinHTTP library.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_http(
+ git_smart_subtransport **out,
+ git_transport* owner);
+
+/**
+ * Create an instance of the git subtransport.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_git(
+ git_smart_subtransport **out,
+ git_transport* owner);
+
+/**
+ * Create an instance of the ssh subtransport.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_ssh(
+ git_smart_subtransport **out,
+ git_transport* owner);
+
+/**
+ * Sets a custom transport factory for the remote. The caller can use this
+ * function to override the transport used for this remote when performing
+ * network operations.
+ *
+ * @param remote the remote to configure
+ * @param transport_cb the function to use to create a transport
+ * @param payload opaque parameter passed to transport_cb
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_set_transport(
+ git_remote *remote,
+ git_transport_cb transport_cb,
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/transport.h b/include/git2/transport.h
index a33146ca8..39df479c7 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -11,10 +11,6 @@
#include "net.h"
#include "types.h"
-#ifdef GIT_SSH
-#include <libssh2.h>
-#endif
-
/**
* @file git2/transport.h
* @brief Git transport interfaces and functions
@@ -24,6 +20,63 @@
*/
GIT_BEGIN_DECL
+/**
+ * Type of SSH host fingerprint
+ */
+typedef enum {
+ /** MD5 is available */
+ GIT_CERT_SSH_MD5 = (1 << 0),
+ /** SHA-1 is available */
+ GIT_CERT_SSH_SHA1 = (1 << 1),
+} git_cert_ssh_t;
+
+/**
+ * Hostkey information taken from libssh2
+ */
+typedef struct {
+ /**
+ * Type of certificate. Here to share the header with
+ * `git_cert`.
+ */
+ git_cert_t cert_type;
+ /**
+ * A hostkey type from libssh2, either
+ * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
+ */
+ git_cert_ssh_t type;
+
+ /**
+ * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
+ * have the MD5 hash of the hostkey.
+ */
+ unsigned char hash_md5[16];
+
+ /**
+ * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
+ * have the SHA-1 hash of the hostkey.
+ */
+ unsigned char hash_sha1[20];
+} git_cert_hostkey;
+
+/**
+ * X.509 certificate information
+ */
+typedef struct {
+ /**
+ * Type of certificate. Here to share the header with
+ * `git_cert`.
+ */
+ git_cert_t cert_type;
+ /**
+ * Pointer to the X.509 certificate data
+ */
+ void *data;
+ /**
+ * Length of the memory block pointed to by `data`.
+ */
+ size_t len;
+} git_cert_x509;
+
/*
*** Begin interface for credentials acquisition ***
*/
@@ -44,6 +97,14 @@ typedef enum {
/* git_cred_ssh_interactive */
GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4),
+
+ /**
+ * Username-only information
+ *
+ * If the SSH transport does not know which username to use,
+ * it will ask via this credential type.
+ */
+ GIT_CREDTYPE_USERNAME = (1u << 5),
} git_credtype_t;
/* The base structure for all credential types */
@@ -61,14 +122,20 @@ typedef struct {
char *password;
} git_cred_userpass_plaintext;
-#ifdef GIT_SSH
-typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
-typedef LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*git_cred_ssh_interactive_callback));
-#else
-typedef int (*git_cred_sign_callback)(void *, ...);
-typedef int (*git_cred_ssh_interactive_callback)(void *, ...);
+
+/*
+ * If the user hasn't included libssh2.h before git2.h, we need to
+ * define a few types for the callback signatures.
+ */
+#ifndef LIBSSH2_VERSION
+typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
+typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT;
+typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE;
#endif
+typedef int (*git_cred_sign_callback)(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract);
+typedef void (*git_cred_ssh_interactive_callback)(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract);
+
/**
* A ssh key from disk
*/
@@ -105,6 +172,12 @@ typedef struct git_cred_ssh_custom {
/** A key for NTLM/Kerberos "default" credentials */
typedef struct git_cred git_cred_default;
+/** Username-only credential information */
+typedef struct git_cred_username {
+ git_cred parent;
+ char username[1];
+} git_cred_username;
+
/**
* Check whether a credential object contains username information.
*
@@ -207,6 +280,14 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
GIT_EXTERN(int) git_cred_default_new(git_cred **out);
/**
+ * Create a credential to specify a username.
+ *
+ * This is used with ssh authentication to query for the username if
+ * none is specified in the url.
+ */
+GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username);
+
+/**
* Signature of a function which acquires a credential object.
*
* - cred: The newly created credential object.
@@ -225,320 +306,6 @@ typedef int (*git_cred_acquire_cb)(
unsigned int allowed_types,
void *payload);
-/*
- *** End interface for credentials acquisition ***
- *** Begin base transport interface ***
- */
-
-typedef enum {
- GIT_TRANSPORTFLAGS_NONE = 0,
- /* If the connection is secured with SSL/TLS, the authenticity
- * of the server certificate should not be verified. */
- GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
-} git_transport_flags_t;
-
-typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
-
-typedef struct git_transport git_transport;
-
-struct git_transport {
- unsigned int version;
- /* Set progress and error callbacks */
- int (*set_callbacks)(
- git_transport *transport,
- git_transport_message_cb progress_cb,
- git_transport_message_cb error_cb,
- void *payload);
-
- /* Connect the transport to the remote repository, using the given
- * direction. */
- int (*connect)(
- git_transport *transport,
- const char *url,
- git_cred_acquire_cb cred_acquire_cb,
- void *cred_acquire_payload,
- int direction,
- int flags);
-
- /* This function may be called after a successful call to
- * connect(). The array returned is owned by the transport and
- * is guranteed until the next call of a transport function. */
- int (*ls)(
- const git_remote_head ***out,
- size_t *size,
- git_transport *transport);
-
- /* Executes the push whose context is in the git_push object. */
- int (*push)(git_transport *transport, git_push *push);
-
- /* This function may be called after a successful call to connect(), when
- * the direction is FETCH. The function performs a negotiation to calculate
- * the wants list for the fetch. */
- int (*negotiate_fetch)(
- git_transport *transport,
- git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
-
- /* This function may be called after a successful call to negotiate_fetch(),
- * when the direction is FETCH. This function retrieves the pack file for
- * the fetch from the remote end. */
- int (*download_pack)(
- git_transport *transport,
- git_repository *repo,
- git_transfer_progress *stats,
- git_transfer_progress_cb progress_cb,
- void *progress_payload);
-
- /* Checks to see if the transport is connected */
- int (*is_connected)(git_transport *transport);
-
- /* Reads the flags value previously passed into connect() */
- int (*read_flags)(git_transport *transport, int *flags);
-
- /* Cancels any outstanding transport operation */
- void (*cancel)(git_transport *transport);
-
- /* This function is the reverse of connect() -- it terminates the
- * connection to the remote end. */
- int (*close)(git_transport *transport);
-
- /* Frees/destructs the git_transport object. */
- void (*free)(git_transport *transport);
-};
-
-#define GIT_TRANSPORT_VERSION 1
-#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
-
-/**
- * Initializes a `git_transport` with default values. Equivalent to
- * creating an instance with GIT_TRANSPORT_INIT.
- *
- * @param opts the `git_transport` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_TRANSPORT_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_transport_init(
- git_transport* opts,
- int version);
-
-/**
- * Function to use to create a transport from a URL. The transport database
- * is scanned to find a transport that implements the scheme of the URI (i.e.
- * git:// or http://) and a transport object is returned to the caller.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param url The URL to connect to
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url);
-
-/* Signature of a function which creates a transport */
-typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
-
-/**
- * Add a custom transport definition, to be used in addition to the built-in
- * set of transports that come with libgit2.
- *
- * The caller is responsible for synchronizing calls to git_transport_register
- * and git_transport_unregister with other calls to the library that
- * instantiate transports.
- *
- * @param prefix The scheme (ending in "://") to match, i.e. "git://"
- * @param priority The priority of this transport relative to others with
- * the same prefix. Built-in transports have a priority of 1.
- * @param cb The callback used to create an instance of the transport
- * @param param A fixed parameter to pass to cb at creation time
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_register(
- const char *prefix,
- unsigned priority,
- git_transport_cb cb,
- void *param);
-
-/**
- *
- * Unregister a custom transport definition which was previously registered
- * with git_transport_register.
- *
- * @param prefix From the previous call to git_transport_register
- * @param priority From the previous call to git_transport_register
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_unregister(
- const char *prefix,
- unsigned priority);
-
-/* Transports which come with libgit2 (match git_transport_cb). The expected
- * value for "param" is listed in-line below. */
-
-/**
- * Create an instance of the dummy transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload You must pass NULL for this parameter.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_dummy(
- git_transport **out,
- git_remote *owner,
- /* NULL */ void *payload);
-
-/**
- * Create an instance of the local transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload You must pass NULL for this parameter.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_local(
- git_transport **out,
- git_remote *owner,
- /* NULL */ void *payload);
-
-/**
- * Create an instance of the smart transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload A pointer to a git_smart_subtransport_definition
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_smart(
- git_transport **out,
- git_remote *owner,
- /* (git_smart_subtransport_definition *) */ void *payload);
-
-/*
- *** End of base transport interface ***
- *** Begin interface for subtransports for the smart transport ***
- */
-
-/* The smart transport knows how to speak the git protocol, but it has no
- * knowledge of how to establish a connection between it and another endpoint,
- * or how to move data back and forth. For this, a subtransport interface is
- * declared, and the smart transport delegates this work to the subtransports.
- * Three subtransports are implemented: git, http, and winhttp. (The http and
- * winhttp transports each implement both http and https.) */
-
-/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
- * (request/response). The smart transport handles the differences in its own
- * logic. The git subtransport is RPC = 0, while http and winhttp are both
- * RPC = 1. */
-
-/* Actions that the smart transport can ask
- * a subtransport to perform */
-typedef enum {
- GIT_SERVICE_UPLOADPACK_LS = 1,
- GIT_SERVICE_UPLOADPACK = 2,
- GIT_SERVICE_RECEIVEPACK_LS = 3,
- GIT_SERVICE_RECEIVEPACK = 4,
-} git_smart_service_t;
-
-typedef struct git_smart_subtransport git_smart_subtransport;
-typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
-
-/* A stream used by the smart transport to read and write data
- * from a subtransport */
-struct git_smart_subtransport_stream {
- /* The owning subtransport */
- git_smart_subtransport *subtransport;
-
- int (*read)(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read);
-
- int (*write)(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len);
-
- void (*free)(
- git_smart_subtransport_stream *stream);
-};
-
-/* An implementation of a subtransport which carries data for the
- * smart transport */
-struct git_smart_subtransport {
- int (* action)(
- git_smart_subtransport_stream **out,
- git_smart_subtransport *transport,
- const char *url,
- git_smart_service_t action);
-
- /* Subtransports are guaranteed a call to close() between
- * calls to action(), except for the following two "natural" progressions
- * of actions against a constant URL.
- *
- * 1. UPLOADPACK_LS -> UPLOADPACK
- * 2. RECEIVEPACK_LS -> RECEIVEPACK */
- int (*close)(git_smart_subtransport *transport);
-
- void (*free)(git_smart_subtransport *transport);
-};
-
-/* A function which creates a new subtransport for the smart transport */
-typedef int (*git_smart_subtransport_cb)(
- git_smart_subtransport **out,
- git_transport* owner);
-
-typedef struct git_smart_subtransport_definition {
- /* The function to use to create the git_smart_subtransport */
- git_smart_subtransport_cb callback;
-
- /* True if the protocol is stateless; false otherwise. For example,
- * http:// is stateless, but git:// is not. */
- unsigned rpc;
-} git_smart_subtransport_definition;
-
-/* Smart transport subtransports that come with libgit2 */
-
-/**
- * Create an instance of the http subtransport. This subtransport
- * also supports https. On Win32, this subtransport may be implemented
- * using the WinHTTP library.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_http(
- git_smart_subtransport **out,
- git_transport* owner);
-
-/**
- * Create an instance of the git subtransport.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_git(
- git_smart_subtransport **out,
- git_transport* owner);
-
-/**
- * Create an instance of the ssh subtransport.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_ssh(
- git_smart_subtransport **out,
- git_transport* owner);
-
-/*
- *** End interface for subtransports for the smart transport ***
- */
-
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 6669652ae..42b68193e 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -151,7 +151,7 @@ GIT_EXTERN(int) git_tree_entry_bypath(
* and must be freed explicitly with `git_tree_entry_free()`.
*
* @param dest pointer where to store the copy
- * @param entry tree entry to duplicate
+ * @param source tree entry to duplicate
* @return 0 or an error code
*/
GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source);
@@ -301,8 +301,10 @@ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
* If an entry named `filename` already exists, its attributes
* will be updated with the given ones.
*
- * The optional pointer `out` can be used to retrieve a pointer to
- * the newly created/updated entry. Pass NULL if you do not need it.
+ * The optional pointer `out` can be used to retrieve a pointer to the
+ * newly created/updated entry. Pass NULL if you do not need it. The
+ * pointer may not be valid past the next operation in this
+ * builder. Duplicate the entry if you want to keep it.
*
* No attempt is being made to ensure that the provided oid points
* to an existing git object in the object database, nor that the
diff --git a/include/git2/types.h b/include/git2/types.h
index 1b6f4cca1..7ee7cc344 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -154,15 +154,15 @@ typedef struct git_packbuilder git_packbuilder;
/** Time in a signature */
typedef struct git_time {
- git_time_t time; /** time in seconds from epoch */
- int offset; /** timezone offset, in minutes */
+ git_time_t time; /**< time in seconds from epoch */
+ int offset; /**< timezone offset, in minutes */
} git_time;
/** An action signature (e.g. for committers, taggers, etc) */
typedef struct git_signature {
- char *name; /** full name of the author */
- char *email; /** email of the author */
- git_time when; /** time when the action happened */
+ char *name; /**< full name of the author */
+ char *email; /**< email of the author */
+ git_time when; /**< time when the action happened */
} git_signature;
/** In-memory representation of a reference. */
@@ -183,9 +183,9 @@ typedef struct git_status_list git_status_list;
/** Basic type of any Git reference. */
typedef enum {
- GIT_REF_INVALID = 0, /** Invalid reference */
- GIT_REF_OID = 1, /** A reference which points at an object id */
- GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */
+ GIT_REF_INVALID = 0, /**< Invalid reference */
+ GIT_REF_OID = 1, /**< A reference which points at an object id */
+ GIT_REF_SYMBOLIC = 2, /**< A reference which points at another reference */
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
} git_ref_t;
@@ -198,12 +198,12 @@ typedef enum {
/** Valid modes for index and tree entries. */
typedef enum {
- GIT_FILEMODE_NEW = 0000000,
- GIT_FILEMODE_TREE = 0040000,
- GIT_FILEMODE_BLOB = 0100644,
- GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
- GIT_FILEMODE_LINK = 0120000,
- GIT_FILEMODE_COMMIT = 0160000,
+ GIT_FILEMODE_UNREADABLE = 0000000,
+ GIT_FILEMODE_TREE = 0040000,
+ GIT_FILEMODE_BLOB = 0100644,
+ GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
+ GIT_FILEMODE_LINK = 0120000,
+ GIT_FILEMODE_COMMIT = 0160000,
} git_filemode_t;
typedef struct git_refspec git_refspec;
@@ -244,6 +244,54 @@ typedef struct git_transfer_progress {
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
/**
+ * Type for messages delivered by the transport. Return a negative value
+ * to cancel the network operation.
+ *
+ * @param str The message from the transport
+ * @param len The length of the message
+ * @param payload Payload provided by the caller
+ */
+typedef int (*git_transport_message_cb)(const char *str, int len, void *payload);
+
+/**
+ * Type of host certificate structure that is passed to the check callback
+ */
+typedef enum git_cert_t {
+ /**
+ * The `data` argument to the callback will be a pointer to
+ * the DER-encoded data.
+ */
+ GIT_CERT_X509,
+ /**
+ * The `data` argument to the callback will be a pointer to a
+ * `git_cert_hostkey` structure.
+ */
+ GIT_CERT_HOSTKEY_LIBSSH2,
+} git_cert_t;
+
+/**
+ * Parent type for `git_cert_hostkey` and `git_cert_x509`.
+ */
+typedef struct {
+ /**
+ * Type of certificate. A `GIT_CERT_` value.
+ */
+ git_cert_t cert_type;
+} git_cert;
+
+/**
+ * Callback for the user's custom certificate checks.
+ *
+ * @param type The type of certificate or host info, SSH or X.509
+ * @param data The data for the certificate or host info
+ * @param len The size of the certificate or host info
+ * @param valid Whether the libgit2 checks (OpenSSL or WinHTTP) think
+ * this certificate is valid
+ * @param payload Payload provided by the caller
+ */
+typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, void *payload);
+
+/**
* Opaque structure representing a submodule.
*/
typedef struct git_submodule git_submodule;
@@ -314,12 +362,12 @@ typedef enum {
* when we don't want any particular ignore rule to be specified.
*/
typedef enum {
- GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */
+ GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */
- GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */
- GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
- GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */
- GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */
+ GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t;
diff --git a/include/git2/version.h b/include/git2/version.h
index c4c5e8eb1..5bda42735 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,9 +7,11 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.20.0"
+#define LIBGIT2_VERSION "0.21.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 20
+#define LIBGIT2_VER_MINOR 21
#define LIBGIT2_VER_REVISION 0
+#define LIBGIT2_SOVERSION 21
+
#endif
diff --git a/script/cibuild.sh b/script/cibuild.sh
index 1f15e851e..abe31d0dc 100755
--- a/script/cibuild.sh
+++ b/script/cibuild.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-if [ "$COVERITY" -eq 1 ];
+if [ -n "$COVERITY" ];
then
./script/coverity.sh;
exit $?;
@@ -15,18 +15,27 @@ export GITTEST_REMOTE_URL="git://localhost/test.git"
mkdir _build
cd _build
cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
-cmake --build . --target install || exit $?
+make -j2 install || exit $?
ctest -V . || exit $?
# Now that we've tested the raw git protocol, let's set up ssh to we
# can do the push tests over it
killall git-daemon
-sudo start ssh
+
+if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config
+else
+ sudo start ssh
+fi
+
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
+# Get the fingerprint for localhost and remove the colons so we can parse it as a hex number
+export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F localhost -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
+
export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
export GITTEST_REMOTE_USER=$USER
export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
@@ -34,5 +43,6 @@ export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
if [ -e ./libgit2_clar ]; then
- ./libgit2_clar -sonline::push -sonline::clone::cred_callback_failure
+ ./libgit2_clar -sonline::push -sonline::clone::cred_callback -sonline::clone::ssh_cert &&
+ ./libgit2_clar -sonline::clone::ssh_with_paths
fi
diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh
new file mode 100755
index 000000000..347922b89
--- /dev/null
+++ b/script/install-deps-linux.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -x
+
+sudo apt-get -qq update &&
+sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh
new file mode 100755
index 000000000..c2e0162d8
--- /dev/null
+++ b/script/install-deps-osx.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -x
+
+brew install libssh2 cmake
diff --git a/src/array.h b/src/array.h
index f8a48722a..af9eafa43 100644
--- a/src/array.h
+++ b/src/array.h
@@ -44,7 +44,7 @@ typedef git_array_t(char) git_array_generic_t;
/* use a generic array for growth so this can return the new item */
GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
{
- git_array_generic_t *a = _a;
+ volatile git_array_generic_t *a = _a;
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
char *new_array = git__realloc(a->ptr, new_size * item_size);
if (!new_array) {
diff --git a/src/attr.c b/src/attr.c
index 05b0c1b3c..a02172689 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -377,7 +377,7 @@ static int push_attr_file(
return error;
}
-static int push_one_attr(void *ref, git_buf *path)
+static int push_one_attr(void *ref, const char *path)
{
int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref;
@@ -388,7 +388,7 @@ static int push_one_attr(void *ref, git_buf *path)
for (i = 0; !error && i < n_src; ++i)
error = push_attr_file(
- info->repo, info->files, src[i], path->ptr, GIT_ATTR_FILE);
+ info->repo, info->files, src[i], path, GIT_ATTR_FILE);
return error;
}
@@ -411,7 +411,7 @@ static int collect_attr_files(
const char *path,
git_vector *files)
{
- int error;
+ int error = 0;
git_buf dir = GIT_BUF_INIT;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info = { NULL };
@@ -447,7 +447,11 @@ static int collect_attr_files(
giterr_clear(); /* no error even if there is no index */
info.files = files;
- error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
+ if (!strcmp(dir.ptr, "."))
+ error = push_one_attr(&info, "");
+ else
+ error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
+
if (error < 0)
goto cleanup;
diff --git a/src/attr_file.c b/src/attr_file.c
index 156a23d91..07ffacbaf 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -281,7 +281,7 @@ uint32_t git_attr_file__name_hash(const char *name)
int git_attr_file__lookup_one(
git_attr_file *file,
- const git_attr_path *path,
+ git_attr_path *path,
const char *attr,
const char **value)
{
@@ -342,14 +342,11 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path)
bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
- const git_attr_path *path)
+ git_attr_path *path)
{
const char *filename;
int flags = 0;
- if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir)
- return false;
-
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
flags |= FNM_CASEFOLD;
if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
@@ -365,12 +362,40 @@ bool git_attr_fnmatch__match(
flags |= FNM_LEADING_DIR;
}
+ if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
+ int matchval;
+
+ /* for attribute checks or root ignore checks, fail match */
+ if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
+ path->basename == path->path)
+ return false;
+
+ /* for ignore checks, use container of current item for check */
+ path->basename[-1] = '\0';
+ flags |= FNM_LEADING_DIR;
+ matchval = p_fnmatch(match->pattern, path->path, flags);
+ path->basename[-1] = '/';
+ return (matchval != FNM_NOMATCH);
+ }
+
+ /* if path is a directory prefix of a negated pattern, then match */
+ if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
+ size_t pathlen = strlen(path->path);
+ bool prefixed = (pathlen <= match->length) &&
+ ((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
+ !strncasecmp(match->pattern, path->path, pathlen) :
+ !strncmp(match->pattern, path->path, pathlen));
+
+ if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
+ return true;
+ }
+
return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
}
bool git_attr_rule__match(
git_attr_rule *rule,
- const git_attr_path *path)
+ git_attr_path *path)
{
bool matched = git_attr_fnmatch__match(&rule->match, path);
@@ -509,7 +534,8 @@ int git_attr_fnmatch__parse(
}
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
+ spec->flags = spec->flags |
+ GIT_ATTR_FNMATCH_NEGATIVE | GIT_ATTR_FNMATCH_LEADINGDIR;
pattern++;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index e50aec07c..87cde7e35 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -138,7 +138,7 @@ int git_attr_file__clear_rules(
int git_attr_file__lookup_one(
git_attr_file *file,
- const git_attr_path *path,
+ git_attr_path *path,
const char *attr,
const char **value);
@@ -162,13 +162,13 @@ extern int git_attr_fnmatch__parse(
extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule,
- const git_attr_path *path);
+ git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule);
extern bool git_attr_rule__match(
git_attr_rule *rule,
- const git_attr_path *path);
+ git_attr_path *path);
extern git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name);
diff --git a/src/attrcache.c b/src/attrcache.c
index f1bc70467..b4579bfc0 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -53,7 +53,7 @@ int git_attr_cache__alloc_file_entry(
cachesize++;
}
- ce = git_pool_mallocz(pool, cachesize);
+ ce = git_pool_mallocz(pool, (uint32_t)cachesize);
GITERR_CHECK_ALLOC(ce);
if (baselen) {
@@ -349,14 +349,11 @@ int git_attr_cache__do_init(git_repository *repo)
{
int ret = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
- git_config *cfg;
+ git_config *cfg = NULL;
if (cache)
return 0;
- if ((ret = git_repository_config__weakptr(&cfg, repo)) < 0)
- return ret;
-
cache = git__calloc(1, sizeof(git_attr_cache));
GITERR_CHECK_ALLOC(cache);
@@ -367,6 +364,9 @@ int git_attr_cache__do_init(git_repository *repo)
return -1;
}
+ if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
+ goto cancel;
+
/* cache config settings for attributes and ignores */
ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
@@ -390,11 +390,14 @@ int git_attr_cache__do_init(git_repository *repo)
if (cache)
goto cancel; /* raced with another thread, free this but no error */
+ git_config_free(cfg);
+
/* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
cancel:
attr_cache__free(cache);
+ git_config_free(cfg);
return ret;
}
diff --git a/src/blame.c b/src/blame.c
index e45c0ee1c..2cc5e552b 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -316,7 +316,6 @@ static int blame_internal(git_blame *blame)
ent->suspect = o;
blame->ent = ent;
- blame->path = blame->path;
git_blame__like_git(blame, blame->options.flags);
@@ -480,14 +479,9 @@ int git_blame_buffer(
return 0;
}
-int git_blame_init_options(git_blame_options* opts, int version)
+int git_blame_init_options(git_blame_options *opts, unsigned int version)
{
- if (version != GIT_BLAME_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_blame_options", version);
- return -1;
- } else {
- git_blame_options o = GIT_BLAME_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/blob.c b/src/blob.c
index 0aa2516db..30d5b705b 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -198,7 +198,8 @@ int git_blob__create_from_paths(
if (try_load_filters)
/* Load the filters for writing this file to the ODB */
error = git_filter_list_load(
- &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
+ &fl, repo, NULL, hint_path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT);
if (error < 0)
/* well, that didn't work */;
@@ -333,7 +334,8 @@ int git_blob_is_binary(const git_blob *blob)
assert(blob);
content.ptr = blob->odb_object->buffer;
- content.size = min(blob->odb_object->cached.size, 4000);
+ content.size =
+ min(blob->odb_object->cached.size, GIT_FILTER_BYTES_TO_CHECK_NUL);
content.asize = 0;
return git_buf_text_is_binary(&content);
@@ -356,7 +358,8 @@ int git_blob_filtered_content(
return 0;
if (!(error = git_filter_list_load(
- &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
+ &fl, git_blob_owner(blob), blob, path,
+ GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT))) {
error = git_filter_list_apply_to_blob(out, fl, blob);
diff --git a/src/branch.c b/src/branch.c
index 63c6ec110..52760853b 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -306,17 +306,13 @@ int git_branch_name(
static int retrieve_upstream_configuration(
const char **out,
- git_repository *repo,
+ const git_config *config,
const char *canonical_branch_name,
const char *format)
{
- git_config *config;
git_buf buf = GIT_BUF_INIT;
int error;
- if (git_repository_config__weakptr(&config, repo) < 0)
- return -1;
-
if (git_buf_printf(&buf, format,
canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
return -1;
@@ -336,6 +332,7 @@ int git_branch_upstream_name(
int error = -1;
git_remote *remote = NULL;
const git_refspec *refspec;
+ git_config *config;
assert(out && refname);
@@ -344,12 +341,15 @@ int git_branch_upstream_name(
if (!git_reference__is_branch(refname))
return not_a_local_branch(refname);
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ return error;
+
if ((error = retrieve_upstream_configuration(
- &remote_name, repo, refname, "branch.%s.remote")) < 0)
+ &remote_name, config, refname, "branch.%s.remote")) < 0)
goto cleanup;
if ((error = retrieve_upstream_configuration(
- &merge_name, repo, refname, "branch.%s.merge")) < 0)
+ &merge_name, config, refname, "branch.%s.merge")) < 0)
goto cleanup;
if (!*remote_name || !*merge_name) {
@@ -378,6 +378,7 @@ int git_branch_upstream_name(
error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
cleanup:
+ git_config_free(config);
git_remote_free(remote);
git_buf_free(&buf);
return error;
diff --git a/src/buf_text.c b/src/buf_text.c
index 631feb3f8..8d2b141b2 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -123,9 +123,13 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
size_t copylen = next - scan;
- /* don't convert existing \r\n to \r\r\n */
- size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
- size_t needsize = tgt->size + copylen + extralen + 1;
+ size_t needsize = tgt->size + copylen + 2 + 1;
+
+ /* if we find mixed line endings, bail */
+ if (next > start && next[-1] == '\r') {
+ git_buf_free(tgt);
+ return GIT_PASSTHROUGH;
+ }
if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
return -1;
@@ -134,8 +138,8 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
memcpy(tgt->ptr + tgt->size, scan, copylen);
tgt->size += copylen;
}
- if (extralen == 2)
- tgt->ptr[tgt->size++] = '\r';
+
+ tgt->ptr[tgt->size++] = '\r';
tgt->ptr[tgt->size++] = '\n';
}
diff --git a/src/buf_text.h b/src/buf_text.h
index 3ac9d1443..e753a0244 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,9 +56,10 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf);
/**
- * Replace all \r\n with \n. Does not modify \r without trailing \n.
+ * Replace all \r\n with \n.
*
- * @return 0 on success, -1 on memory error
+ * @return 0 on success, -1 on memory error, GIT_PASSTHROUGH if the
+ * source buffer has mixed line endings.
*/
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
diff --git a/src/buffer.c b/src/buffer.c
index 5169c3e09..e9c420e16 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -7,6 +7,7 @@
#include "buffer.h"
#include "posix.h"
#include "git2/buffer.h"
+#include "buf_text.h"
#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
@@ -104,17 +105,20 @@ void git_buf_free(git_buf *buf)
void git_buf_sanitize(git_buf *buf)
{
if (buf->ptr == NULL) {
- assert (buf->size == 0 && buf->asize == 0);
+ assert(buf->size == 0 && buf->asize == 0);
buf->ptr = git_buf__initbuf;
- }
+ } else if (buf->asize > buf->size)
+ buf->ptr[buf->size] = '\0';
}
void git_buf_clear(git_buf *buf)
{
buf->size = 0;
- if (!buf->ptr)
+ if (!buf->ptr) {
buf->ptr = git_buf__initbuf;
+ buf->asize = 0;
+ }
if (buf->asize > 0)
buf->ptr[0] = '\0';
@@ -129,12 +133,25 @@ int git_buf_set(git_buf *buf, const void *data, size_t len)
ENSURE_SIZE(buf, len + 1);
memmove(buf->ptr, data, len);
}
+
buf->size = len;
- buf->ptr[buf->size] = '\0';
+ if (buf->asize > buf->size)
+ buf->ptr[buf->size] = '\0';
+
}
return 0;
}
+int git_buf_is_binary(const git_buf *buf)
+{
+ return git_buf_text_is_binary(buf);
+}
+
+int git_buf_contains_nul(const git_buf *buf)
+{
+ return git_buf_text_contains_nul(buf);
+}
+
int git_buf_sets(git_buf *buf, const char *string)
{
return git_buf_set(buf, string, string ? strlen(string) : 0);
@@ -172,10 +189,10 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
-static const char b64str[] =
+static const char base64_encode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
+int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
{
size_t extra = len % 3;
uint8_t *write, a, b, c;
@@ -190,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
b = *read++;
c = *read++;
- *write++ = b64str[a >> 2];
- *write++ = b64str[(a & 0x03) << 4 | b >> 4];
- *write++ = b64str[(b & 0x0f) << 2 | c >> 6];
- *write++ = b64str[c & 0x3f];
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
+ *write++ = base64_encode[c & 0x3f];
}
if (extra > 0) {
a = *read++;
b = (extra > 1) ? *read++ : 0;
- *write++ = b64str[a >> 2];
- *write++ = b64str[(a & 0x03) << 4 | b >> 4];
- *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
*write++ = '=';
}
@@ -212,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
+/* The inverse of base64_encode, offset by '+' == 43. */
+static const int8_t base64_decode[] = {
+ 62,
+ -1, -1, -1,
+ 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, 0, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ -1, -1, -1, -1, -1, -1,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
+
+int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
+{
+ size_t i;
+ int8_t a, b, c, d;
+ size_t orig_size = buf->size;
+
+ assert(len % 4 == 0);
+ ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
+
+ for (i = 0; i < len; i += 4) {
+ if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
+ (b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
+ (c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
+ (d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
+ buf->size = orig_size;
+ buf->ptr[buf->size] = '\0';
+
+ giterr_set(GITERR_INVALID, "Invalid base64 input");
+ return -1;
+ }
+
+ buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
+ buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
+ buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
+ }
+
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
static const char b85str[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
-int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
+int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
@@ -326,19 +389,20 @@ void git_buf_consume(git_buf *buf, const char *end)
void git_buf_truncate(git_buf *buf, size_t len)
{
- if (len < buf->size) {
- buf->size = len;
+ if (len >= buf->size)
+ return;
+
+ buf->size = len;
+ if (buf->size < buf->asize)
buf->ptr[buf->size] = '\0';
- }
}
void git_buf_shorten(git_buf *buf, size_t amount)
{
- if (amount > buf->size)
- amount = buf->size;
-
- buf->size = buf->size - amount;
- buf->ptr[buf->size] = '\0';
+ if (buf->size > amount)
+ git_buf_truncate(buf, buf->size - amount);
+ else
+ git_buf_clear(buf);
}
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
@@ -574,7 +638,8 @@ void git_buf_rtrim(git_buf *buf)
buf->size--;
}
- buf->ptr[buf->size] = '\0';
+ if (buf->asize > buf->size)
+ buf->ptr[buf->size] = '\0';
}
int git_buf_cmp(const git_buf *a, const git_buf *b)
@@ -598,8 +663,7 @@ int git_buf_splice(
/* Ported from git.git
* https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
*/
- if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0)
- return -1;
+ ENSURE_SIZE(buf, buf->size + nb_to_insert - nb_to_insert + 1);
memmove(buf->ptr + where + nb_to_insert,
buf->ptr + where + nb_to_remove,
diff --git a/src/buffer.h b/src/buffer.h
index 70d6d73b3..8ee4b532c 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */
-int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
+int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
+/* Decode the given bas64 and write the result to the buffer */
+int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
/* Write data as "base85" encoded in buffer */
-int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
+int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
/*
* Insert, remove or replace a portion of the buffer.
diff --git a/src/cache.c b/src/cache.c
index 36ce66570..8dc9cbf9c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -68,8 +68,8 @@ int git_cache_init(git_cache *cache)
{
memset(cache, 0, sizeof(*cache));
cache->map = git_oidmap_alloc();
- if (git_mutex_init(&cache->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize cache mutex");
+ if (git_rwlock_init(&cache->lock)) {
+ giterr_set(GITERR_OS, "Failed to initialize cache rwlock");
return -1;
}
return 0;
@@ -94,19 +94,19 @@ static void clear_cache(git_cache *cache)
void git_cache_clear(git_cache *cache)
{
- if (git_mutex_lock(&cache->lock) < 0)
+ if (git_rwlock_wrlock(&cache->lock) < 0)
return;
clear_cache(cache);
- git_mutex_unlock(&cache->lock);
+ git_rwlock_wrunlock(&cache->lock);
}
void git_cache_free(git_cache *cache)
{
git_cache_clear(cache);
git_oidmap_free(cache->map);
- git_mutex_free(&cache->lock);
+ git_rwlock_free(&cache->lock);
git__memzero(cache, sizeof(*cache));
}
@@ -152,7 +152,7 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
khiter_t pos;
git_cached_obj *entry = NULL;
- if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0)
+ if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
return NULL;
pos = kh_get(oid, cache->map, oid);
@@ -166,7 +166,7 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
}
}
- git_mutex_unlock(&cache->lock);
+ git_rwlock_rdunlock(&cache->lock);
return entry;
}
@@ -185,7 +185,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
if (!cache_should_store(entry->type, entry->size))
return entry;
- if (git_mutex_lock(&cache->lock) < 0)
+ if (git_rwlock_wrlock(&cache->lock) < 0)
return entry;
/* soften the load on the cache */
@@ -227,7 +227,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
}
}
- git_mutex_unlock(&cache->lock);
+ git_rwlock_wrunlock(&cache->lock);
return entry;
}
diff --git a/src/cache.h b/src/cache.h
index 53fbcf4e9..697123739 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -30,7 +30,7 @@ typedef struct {
typedef struct {
git_oidmap *map;
- git_mutex lock;
+ git_rwlock lock;
ssize_t used_memory;
} git_cache;
diff --git a/src/cc-compat.h b/src/cc-compat.h
index e73cb6de8..0b66d8ba1 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -35,6 +35,14 @@
# define GIT_TYPEOF(x)
#endif
+#if defined(__GNUC__)
+# define GIT_ALIGN(x,size) x __attribute__ ((aligned(size)))
+#elif defined(_MSC_VER)
+# define GIT_ALIGN(x,size) __declspec(align(size)) x
+#else
+# define GIT_ALIGN(x,size) x
+#endif
+
#define GIT_UNUSED(x) ((void)(x))
/* Define the printf format specifer to use for size_t output */
diff --git a/src/checkout.c b/src/checkout.c
index bc976b854..f25a6eff0 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -46,6 +46,7 @@ enum {
typedef struct {
git_repository *repo;
+ git_iterator *target;
git_diff *diff;
git_checkout_options opts;
bool opts_free_baseline;
@@ -54,6 +55,8 @@ typedef struct {
git_pool pool;
git_vector removes;
git_vector conflicts;
+ git_vector *reuc;
+ git_vector *names;
git_buf path;
size_t workdir_len;
git_buf tmp;
@@ -116,6 +119,7 @@ static int checkout_notify(
case GIT_DELTA_ADDED:
case GIT_DELTA_IGNORED:
case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
target = &delta->new_file;
break;
case GIT_DELTA_DELETED:
@@ -138,6 +142,7 @@ static int checkout_notify(
static bool checkout_is_workdir_modified(
checkout_data *data,
const git_diff_file *baseitem,
+ const git_diff_file *newitem,
const git_index_entry *wditem)
{
git_oid oid;
@@ -169,13 +174,16 @@ static bool checkout_is_workdir_modified(
/* Look at the cache to decide if the workdir is modified. If not,
* we can simply compare the oid in the cache to the baseitem instead
- * of hashing the file.
+ * of hashing the file. If so, we allow the checkout to proceed if the
+ * oid is identical (ie, the staged item is what we're trying to check
+ * out.)
*/
if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
if (wditem->mtime.seconds == ie->mtime.seconds &&
wditem->mtime.nanoseconds == ie->mtime.nanoseconds &&
wditem->file_size == ie->file_size)
- return (git_oid__cmp(&baseitem->id, &ie->id) != 0);
+ return (git_oid__cmp(&baseitem->id, &ie->id) != 0 &&
+ git_oid_cmp(&newitem->id, &ie->id) != 0);
}
/* depending on where base is coming from, we may or may not know
@@ -184,9 +192,7 @@ static bool checkout_is_workdir_modified(
if (baseitem->size && wditem->file_size != baseitem->size)
return true;
- if (git_diff__oid_for_file(
- data->repo, wditem->path, wditem->mode,
- wditem->file_size, &oid) < 0)
+ if (git_diff__oid_for_entry(&oid, data->diff, wditem, NULL) < 0)
return false;
return (git_oid__cmp(&baseitem->id, &oid) != 0);
@@ -403,7 +409,7 @@ static int checkout_action_with_wd(
switch (delta->status) {
case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd)) {
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
GITERR_CHECK_ERROR(
checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
@@ -416,13 +422,13 @@ static int checkout_action_with_wd(
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
break;
case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
break;
case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
@@ -445,7 +451,7 @@ static int checkout_action_with_wd(
} else
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
}
- else if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
@@ -790,11 +796,16 @@ done:
static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
{
git_index_conflict_iterator *iterator = NULL;
+ git_index *index;
const git_index_entry *ancestor, *ours, *theirs;
checkout_conflictdata *conflict;
int error = 0;
- if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
+ /* Only write conficts from sources that have them: indexes. */
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
+ if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0)
goto done;
data->conflicts._cmp = checkout_conflictdata_cmp;
@@ -821,6 +832,10 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g
git_vector_insert(&data->conflicts, conflict);
}
+ /* Collect the REUC and NAME entries */
+ data->reuc = &index->reuc;
+ data->names = &index->names;
+
if (error == GIT_ITEROVER)
error = 0;
@@ -959,16 +974,20 @@ done:
static int checkout_conflicts_coalesce_renames(
checkout_data *data)
{
+ git_index *index;
const git_index_name_entry *name_entry;
checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
size_t i, names;
int error = 0;
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
/* Juggle entries based on renames */
- names = git_index_name_entrycount(data->index);
+ names = git_index_name_entrycount(index);
for (i = 0; i < names; i++) {
- name_entry = git_index_name_get_byindex(data->index, i);
+ name_entry = git_index_name_get_byindex(index, i);
if ((error = checkout_conflicts_load_byname_entry(
&ancestor_conflict, &our_conflict, &their_conflict,
@@ -1012,13 +1031,17 @@ done:
static int checkout_conflicts_mark_directoryfile(
checkout_data *data)
{
+ git_index *index;
checkout_conflictdata *conflict;
const git_index_entry *entry;
size_t i, j, len;
const char *path;
int prefixed, error = 0;
- len = git_index_entrycount(data->index);
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
+ len = git_index_entrycount(index);
/* Find d/f conflicts */
git_vector_foreach(&data->conflicts, i, conflict) {
@@ -1029,7 +1052,7 @@ static int checkout_conflicts_mark_directoryfile(
path = conflict->ours ?
conflict->ours->path : conflict->theirs->path;
- if ((error = git_index_find(&j, data->index, path)) < 0) {
+ if ((error = git_index_find(&j, index, path)) < 0) {
if (error == GIT_ENOTFOUND)
giterr_set(GITERR_INDEX,
"Index inconsistency, could not find entry for expected conflict '%s'", path);
@@ -1038,7 +1061,7 @@ static int checkout_conflicts_mark_directoryfile(
}
for (; j < len; j++) {
- if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
+ if ((entry = git_index_get_byindex(index, j)) == NULL) {
giterr_set(GITERR_INDEX,
"Index inconsistency, truncated index while loading expected conflict '%s'", path);
error = -1;
@@ -1214,7 +1237,8 @@ static int blob_content_to_file(
if (!opts->disable_filters)
error = git_filter_list_load(
- &fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
+ &fl, git_blob_owner(blob), blob, hint_path,
+ GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT);
if (!error)
error = git_filter_list_apply_to_blob(&out, fl, blob);
@@ -1803,6 +1827,24 @@ done:
return error;
}
+static int checkout_conflict_update_index(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if (conflict->ancestor)
+ error = git_index_add(data->index, conflict->ancestor);
+
+ if (!error && conflict->ours)
+ error = git_index_add(data->index, conflict->ours);
+
+ if (!error && conflict->theirs)
+ error = git_index_add(data->index, conflict->theirs);
+
+ return error;
+}
+
static int checkout_create_conflicts(checkout_data *data)
{
checkout_conflictdata *conflict;
@@ -1865,6 +1907,12 @@ static int checkout_create_conflicts(checkout_data *data)
else if (!error)
error = checkout_write_merge(data, conflict);
+ /* Update the index extensions (REUC and NAME) if we're checking
+ * out a different index. (Otherwise just leave them there.)
+ */
+ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ error = checkout_conflict_update_index(data, conflict);
+
if (error)
break;
@@ -1877,6 +1925,37 @@ static int checkout_create_conflicts(checkout_data *data)
return error;
}
+static int checkout_extensions_update_index(checkout_data *data)
+{
+ const git_index_reuc_entry *reuc_entry;
+ const git_index_name_entry *name_entry;
+ size_t i;
+ int error = 0;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ return 0;
+
+ if (data->reuc) {
+ git_vector_foreach(data->reuc, i, reuc_entry) {
+ if ((error = git_index_reuc_add(data->index, reuc_entry->path,
+ reuc_entry->mode[0], &reuc_entry->oid[0],
+ reuc_entry->mode[1], &reuc_entry->oid[1],
+ reuc_entry->mode[2], &reuc_entry->oid[2])) < 0)
+ goto done;
+ }
+ }
+
+ if (data->names) {
+ git_vector_foreach(data->names, i, name_entry) {
+ if ((error = git_index_name_add(data->index, name_entry->ancestor,
+ name_entry->ours, name_entry->theirs)) < 0)
+ goto done;
+ }
+ }
+
+done:
+ return error;
+}
static void checkout_data_clear(checkout_data *data)
{
@@ -1920,6 +1999,7 @@ static int checkout_data_init(
return error;
data->repo = repo;
+ data->target = target;
GITERR_CHECK_VERSION(
proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
@@ -1944,15 +2024,15 @@ static int checkout_data_init(
(error = git_config_refresh(cfg)) < 0)
goto cleanup;
- /* if we are checking out the index, don't reload,
- * otherwise get index and force reload
+ /* Get the repository index and reload it (unless we're checking
+ * out the index; then it has the changes we're trying to check
+ * out and those should not be overwritten.)
*/
- if ((data->index = git_iterator_get_index(target)) != NULL) {
- GIT_REFCOUNT_INC(data->index);
- } else {
- /* otherwise, grab and reload the index */
- if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
- (error = git_index_read(data->index, true)) < 0)
+ if ((error = git_repository_index(&data->index, data->repo)) < 0)
+ goto cleanup;
+
+ if (data->index != git_iterator_get_index(target)) {
+ if ((error = git_index_read(data->index, true)) < 0)
goto cleanup;
/* cannot checkout if unresolved conflicts exist */
@@ -1964,7 +2044,7 @@ static int checkout_data_init(
goto cleanup;
}
- /* clean conflict data when doing a tree or commit checkout */
+ /* clean conflict data in the current index */
git_index_name_clear(data->index);
git_index_reuc_clear(data->index);
}
@@ -2064,6 +2144,7 @@ int git_checkout_iterator(
diff_opts.flags =
GIT_DIFF_INCLUDE_UNMODIFIED |
+ GIT_DIFF_INCLUDE_UNREADABLE |
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
GIT_DIFF_INCLUDE_IGNORED |
@@ -2133,6 +2214,10 @@ int git_checkout_iterator(
(error = checkout_create_conflicts(&data)) < 0)
goto cleanup;
+ if (data.index != git_iterator_get_index(target) &&
+ (error = checkout_extensions_update_index(&data)) < 0)
+ goto cleanup;
+
assert(data.completed_steps == data.total_steps);
cleanup:
@@ -2155,7 +2240,7 @@ int git_checkout_index(
git_index *index,
const git_checkout_options *opts)
{
- int error;
+ int error, owned = 0;
git_iterator *index_i;
if (!index && !repo) {
@@ -2163,10 +2248,16 @@ int git_checkout_index(
"Must provide either repository or index to checkout");
return -1;
}
- if (index && repo && git_index_owner(index) != repo) {
+
+ if (index && repo &&
+ git_index_owner(index) &&
+ git_index_owner(index) != repo) {
giterr_set(GITERR_CHECKOUT,
"Index to checkout does not match repository");
return -1;
+ } else if(index && repo && !git_index_owner(index)) {
+ GIT_REFCOUNT_OWN(index, repo);
+ owned = 1;
}
if (!repo)
@@ -2179,6 +2270,9 @@ int git_checkout_index(
if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
error = git_checkout_iterator(index_i, opts);
+ if (owned)
+ GIT_REFCOUNT_OWN(index, NULL);
+
git_iterator_free(index_i);
git_index_free(index);
@@ -2242,14 +2336,9 @@ int git_checkout_head(
return git_checkout_tree(repo, NULL, opts);
}
-int git_checkout_init_opts(git_checkout_options* opts, int version)
+int git_checkout_init_options(git_checkout_options *opts, unsigned int version)
{
- if (version != GIT_CHECKOUT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_checkout_options", version);
- return -1;
- } else {
- git_checkout_options o = GIT_CHECKOUT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/cherrypick.c b/src/cherrypick.c
index 6a5ca834c..e58d0ab4c 100644
--- a/src/cherrypick.c
+++ b/src/cherrypick.c
@@ -17,9 +17,9 @@
#include "git2/commit.h"
#include "git2/sys/commit.h"
-#define GIT_CHERRY_PICK_FILE_MODE 0666
+#define GIT_CHERRYPICK_FILE_MODE 0666
-static int write_cherry_pick_head(
+static int write_cherrypick_head(
git_repository *repo,
const char *commit_oidstr)
{
@@ -27,8 +27,8 @@ static int write_cherry_pick_head(
git_buf file_path = GIT_BUF_INIT;
int error = 0;
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRY_PICK_HEAD_FILE)) >= 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) >= 0 &&
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 &&
(error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
error = git_filebuf_commit(&file);
@@ -49,7 +49,7 @@ static int write_merge_msg(
int error = 0;
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 ||
(error = git_filebuf_printf(&file, "%s", commit_msg)) < 0)
goto cleanup;
@@ -64,10 +64,10 @@ cleanup:
return error;
}
-static int cherry_pick_normalize_opts(
+static int cherrypick_normalize_opts(
git_repository *repo,
- git_cherry_pick_options *opts,
- const git_cherry_pick_options *given,
+ git_cherrypick_options *opts,
+ const git_cherrypick_options *given,
const char *their_label)
{
int error = 0;
@@ -77,10 +77,10 @@ static int cherry_pick_normalize_opts(
GIT_UNUSED(repo);
if (given != NULL)
- memcpy(opts, given, sizeof(git_cherry_pick_options));
+ memcpy(opts, given, sizeof(git_cherrypick_options));
else {
- git_cherry_pick_options default_opts = GIT_CHERRY_PICK_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_cherry_pick_options));
+ git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_cherrypick_options));
}
if (!opts->checkout_opts.checkout_strategy)
@@ -95,14 +95,14 @@ static int cherry_pick_normalize_opts(
return error;
}
-static int cherry_pick_state_cleanup(git_repository *repo)
+static int cherrypick_state_cleanup(git_repository *repo)
{
- const char *state_files[] = { GIT_CHERRY_PICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
+ const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
-static int cherry_pick_seterr(git_commit *commit, const char *fmt)
+static int cherrypick_seterr(git_commit *commit, const char *fmt)
{
char commit_oidstr[GIT_OID_HEXSZ + 1];
@@ -112,71 +112,71 @@ static int cherry_pick_seterr(git_commit *commit, const char *fmt)
return -1;
}
-int git_cherry_pick_commit(
+int git_cherrypick_commit(
git_index **out,
git_repository *repo,
- git_commit *cherry_pick_commit,
+ git_commit *cherrypick_commit,
git_commit *our_commit,
unsigned int mainline,
const git_merge_options *merge_opts)
{
git_commit *parent_commit = NULL;
- git_tree *parent_tree = NULL, *our_tree = NULL, *cherry_pick_tree = NULL;
+ git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL;
int parent = 0, error = 0;
- assert(out && repo && cherry_pick_commit && our_commit);
+ assert(out && repo && cherrypick_commit && our_commit);
- if (git_commit_parentcount(cherry_pick_commit) > 1) {
+ if (git_commit_parentcount(cherrypick_commit) > 1) {
if (!mainline)
- return cherry_pick_seterr(cherry_pick_commit,
+ return cherrypick_seterr(cherrypick_commit,
"Mainline branch is not specified but %s is a merge commit");
parent = mainline;
} else {
if (mainline)
- return cherry_pick_seterr(cherry_pick_commit,
+ return cherrypick_seterr(cherrypick_commit,
"Mainline branch specified but %s is not a merge commit");
- parent = git_commit_parentcount(cherry_pick_commit);
+ parent = git_commit_parentcount(cherrypick_commit);
}
if (parent &&
- ((error = git_commit_parent(&parent_commit, cherry_pick_commit, (parent - 1))) < 0 ||
+ ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 ||
(error = git_commit_tree(&parent_tree, parent_commit)) < 0))
goto done;
- if ((error = git_commit_tree(&cherry_pick_tree, cherry_pick_commit)) < 0 ||
+ if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 ||
(error = git_commit_tree(&our_tree, our_commit)) < 0)
goto done;
- error = git_merge_trees(out, repo, parent_tree, our_tree, cherry_pick_tree, merge_opts);
+ error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts);
done:
git_tree_free(parent_tree);
git_tree_free(our_tree);
- git_tree_free(cherry_pick_tree);
+ git_tree_free(cherrypick_tree);
git_commit_free(parent_commit);
return error;
}
-int git_cherry_pick(
+int git_cherrypick(
git_repository *repo,
git_commit *commit,
- const git_cherry_pick_options *given_opts)
+ const git_cherrypick_options *given_opts)
{
- git_cherry_pick_options opts;
+ git_cherrypick_options opts;
git_reference *our_ref = NULL;
git_commit *our_commit = NULL;
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg, *commit_summary;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
int error = 0;
assert(repo && commit);
- GITERR_CHECK_VERSION(given_opts, GIT_CHERRY_PICK_OPTIONS_VERSION, "git_cherry_pick_options");
+ GITERR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options");
if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0)
return error;
@@ -191,25 +191,22 @@ int git_cherry_pick(
if ((error = write_merge_msg(repo, commit_msg)) < 0 ||
(error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 ||
- (error = cherry_pick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
- (error = write_cherry_pick_head(repo, commit_oidstr)) < 0 ||
+ (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
+ (error = write_cherrypick_head(repo, commit_oidstr)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_cherry_pick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ (error = git_cherrypick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
goto on_error;
-
goto done;
on_error:
- cherry_pick_state_cleanup(repo);
+ cherrypick_state_cleanup(repo);
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
@@ -217,14 +214,10 @@ done:
return error;
}
-int git_cherry_pick_init_opts(git_cherry_pick_options* opts, int version)
+int git_cherrypick_init_options(
+ git_cherrypick_options *opts, unsigned int version)
{
- if (version != GIT_CHERRY_PICK_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_cherry_pick_options", version);
- return -1;
- } else {
- git_cherry_pick_options o = GIT_CHERRY_PICK_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/clone.c b/src/clone.c
index 62f103561..43b839003 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -22,6 +22,9 @@
#include "refs.h"
#include "path.h"
#include "repository.h"
+#include "odb.h"
+
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature);
static int create_branch(
git_reference **branch,
@@ -105,54 +108,6 @@ static int create_tracking_branch(
git_reference_name(*branch));
}
-struct head_info {
- git_repository *repo;
- git_oid remote_head_oid;
- git_buf branchname;
- const git_refspec *refspec;
- bool found;
-};
-
-static int reference_matches_remote_head(
- const char *reference_name,
- void *payload)
-{
- struct head_info *head_info = (struct head_info *)payload;
- git_oid oid;
- int error;
-
- /* TODO: Should we guard against references
- * which name doesn't start with refs/heads/ ?
- */
-
- error = git_reference_name_to_id(&oid, head_info->repo, reference_name);
- if (error == GIT_ENOTFOUND) {
- /* If the reference doesn't exists, it obviously cannot match the
- * expected oid. */
- giterr_clear();
- return 0;
- }
-
- if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) {
- /* Determine the local reference name from the remote tracking one */
- error = git_refspec_rtransform(
- &head_info->branchname, head_info->refspec, reference_name);
-
- if (!error &&
- git_buf_len(&head_info->branchname) > 0 &&
- !(error = git_buf_sets(
- &head_info->branchname,
- git_buf_cstr(&head_info->branchname) +
- strlen(GIT_REFS_HEADS_DIR))))
- {
- head_info->found = true;
- error = GIT_ITEROVER;
- }
- }
-
- return error;
-}
-
static int update_head_to_new_branch(
git_repository *repo,
const git_oid *target,
@@ -161,7 +116,12 @@ static int update_head_to_new_branch(
const char *reflog_message)
{
git_reference *tracking_branch = NULL;
- int error = create_tracking_branch(&tracking_branch, repo, target, name,
+ int error;
+
+ if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
+ name += strlen(GIT_REFS_HEADS_DIR);
+
+ error = create_tracking_branch(&tracking_branch, repo, target, name,
signature, reflog_message);
if (!error)
@@ -171,6 +131,10 @@ static int update_head_to_new_branch(
git_reference_free(tracking_branch);
+ /* if it already existed, then the user's refspec created it for us, ignore it' */
+ if (error == GIT_EEXISTS)
+ error = 0;
+
return error;
}
@@ -182,76 +146,58 @@ static int update_head_to_remote(
{
int error = 0;
size_t refs_len;
- git_refspec dummy_spec;
+ git_refspec *refspec;
const git_remote_head *remote_head, **refs;
- struct head_info head_info;
+ const git_oid *remote_head_id;
git_buf remote_master_name = GIT_BUF_INIT;
+ git_buf branch = GIT_BUF_INIT;
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
return error;
- /* Did we just clone an empty repository? */
- if (refs_len == 0)
+ /* We cloned an empty repository or one with an unborn HEAD */
+ if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
return setup_tracking_config(
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
- /* Get the remote's HEAD. This is always the first ref in the list. */
+ /* We know we have HEAD, let's see where it points */
remote_head = refs[0];
assert(remote_head);
- memset(&head_info, 0, sizeof(head_info));
- git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
- head_info.repo = repo;
- head_info.refspec =
- git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
+ remote_head_id = &remote_head->oid;
- if (head_info.refspec == NULL) {
- memset(&dummy_spec, 0, sizeof(git_refspec));
- head_info.refspec = &dummy_spec;
+ error = git_remote_default_branch(&branch, remote);
+ if (error == GIT_ENOTFOUND) {
+ error = git_repository_set_head_detached(
+ repo, remote_head_id, signature, reflog_message);
+ goto cleanup;
}
- /* Determine the remote tracking reference name from the local master */
- if ((error = git_refspec_transform(
- &remote_master_name,
- head_info.refspec,
- GIT_REFS_HEADS_MASTER_FILE)) < 0)
- return error;
-
- /* Check to see if the remote HEAD points to the remote master */
- error = reference_matches_remote_head(
- git_buf_cstr(&remote_master_name), &head_info);
- if (error < 0 && error != GIT_ITEROVER)
- goto cleanup;
+ refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
- if (head_info.found) {
- error = update_head_to_new_branch(
- repo,
- &head_info.remote_head_oid,
- git_buf_cstr(&head_info.branchname),
- signature, reflog_message);
+ if (refspec == NULL) {
+ giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration");
+ error = GIT_EINVALIDSPEC;
goto cleanup;
}
- /* Not master. Check all the other refs. */
- error = git_reference_foreach_name(
- repo, reference_matches_remote_head, &head_info);
- if (error < 0 && error != GIT_ITEROVER)
+ /* Determine the remote tracking reference name from the local master */
+ if ((error = git_refspec_transform(
+ &remote_master_name,
+ refspec,
+ git_buf_cstr(&branch))) < 0)
goto cleanup;
- if (head_info.found) {
- error = update_head_to_new_branch(
- repo,
- &head_info.remote_head_oid,
- git_buf_cstr(&head_info.branchname),
- signature, reflog_message);
- } else {
- error = git_repository_set_head_detached(
- repo, &head_info.remote_head_oid, signature, reflog_message);
- }
+ error = update_head_to_new_branch(
+ repo,
+ remote_head_id,
+ git_buf_cstr(&branch),
+ signature, reflog_message);
cleanup:
git_buf_free(&remote_master_name);
- git_buf_free(&head_info.branchname);
+ git_buf_free(&branch);
+
return error;
}
@@ -284,6 +230,29 @@ cleanup:
return retcode;
}
+static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return git_repository_init(out, path, bare);
+}
+
+static int default_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+ git_remote_callbacks *callbacks = payload;
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ return git_remote_set_callbacks(*out, callbacks);
+}
+
/*
* submodules?
*/
@@ -296,16 +265,24 @@ static int create_and_configure_origin(
{
int error;
git_remote *origin = NULL;
- const char *name;
+ char buf[GIT_PATH_MAX];
+ git_remote_create_cb remote_create = options->remote_cb;
+ void *payload = options->remote_cb_payload;
- name = options->remote_name ? options->remote_name : "origin";
- if ((error = git_remote_create(&origin, repo, name, url)) < 0)
- goto on_error;
+ /* If the path exists and is a dir, the url should be the absolute path */
+ if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
+ if (p_realpath(url, buf) == NULL)
+ return -1;
+
+ url = buf;
+ }
- if (options->ignore_cert_errors)
- git_remote_check_cert(origin, 0);
+ if (!remote_create) {
+ remote_create = default_remote_create;
+ payload = (void *)&options->remote_callbacks;
+ }
- if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
+ if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
goto on_error;
if ((error = git_remote_save(origin)) < 0)
@@ -336,57 +313,87 @@ static bool should_checkout(
return !git_repository_head_unborn(repo);
}
-int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
+static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature, const char *reflog_message)
+{
+ int error;
+
+ if (branch)
+ error = update_head_to_branch(repo, git_remote_name(remote), branch,
+ signature, reflog_message);
+ /* Point HEAD to the same ref as the remote's head */
+ else
+ error = update_head_to_remote(repo, remote, signature, reflog_message);
+
+ if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+ error = git_checkout_head(repo, co_opts);
+
+ return error;
+}
+
+static int clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
- int error = 0, old_fetchhead;
- git_strarray refspecs;
+ int error;
git_buf reflog_message = GIT_BUF_INIT;
+ git_remote *remote;
+ const git_remote_callbacks *callbacks;
- assert(repo && remote);
+ assert(repo && _remote);
if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
}
-
- if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+ if ((error = git_remote_dup(&remote, _remote)) < 0)
return error;
+ callbacks = git_remote_get_callbacks(_remote);
+ if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
+ (error = git_remote_set_callbacks(remote, callbacks)) < 0)
+ goto cleanup;
+
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
- return error;
+ goto cleanup;
- old_fetchhead = git_remote_update_fetchhead(remote);
git_remote_set_update_fetchhead(remote, 0);
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup;
- if (branch)
- error = update_head_to_branch(repo, git_remote_name(remote), branch,
- signature, git_buf_cstr(&reflog_message));
- /* Point HEAD to the same ref as the remote's head */
- else
- error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
-
- if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
- error = git_checkout_head(repo, co_opts);
+ error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
cleanup:
- git_remote_set_update_fetchhead(remote, old_fetchhead);
+ git_remote_free(remote);
+ git_buf_free(&reflog_message);
+
+ return error;
+}
+
+int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
+{
+ git_buf fromurl = GIT_BUF_INIT;
+ const char *path = url_or_path;
+ bool is_url, is_local;
+
+ if (local == GIT_CLONE_NO_LOCAL)
+ return 0;
- /* Go back to the original refspecs */
- {
- int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs);
- if (!error)
- error = error_alt;
+ if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) {
+ if (git_path_fromurl(&fromurl, url_or_path) < 0) {
+ is_local = -1;
+ goto done;
+ }
+
+ path = fromurl.ptr;
}
- git_strarray_free(&refspecs);
- git_buf_free(&reflog_message);
+ is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
+ git_path_isdir(path);
- return error;
+done:
+ git_buf_free(&fromurl);
+ return is_local;
}
int git_clone(
@@ -400,6 +407,7 @@ int git_clone(
git_remote *origin;
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+ git_repository_create_cb repository_cb;
assert(out && url && local_path);
@@ -419,12 +427,28 @@ int git_clone(
if (git_path_exists(local_path))
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
- if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
+ if (options.repository_cb)
+ repository_cb = options.repository_cb;
+ else
+ repository_cb = default_repository_create;
+
+ if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
return error;
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
- error = git_clone_into(
- repo, origin, &options.checkout_opts, options.checkout_branch, options.signature);
+ int clone_local = git_clone__should_clone_local(url, options.local);
+ int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
+
+ if (clone_local == 1)
+ error = clone_local_into(
+ repo, origin, &options.checkout_opts,
+ options.checkout_branch, link, options.signature);
+ else if (clone_local == 0)
+ error = clone_into(
+ repo, origin, &options.checkout_opts,
+ options.checkout_branch, options.signature);
+ else
+ error = -1;
git_remote_free(origin);
}
@@ -445,14 +469,100 @@ int git_clone(
return error;
}
-int git_clone_init_options(git_clone_options* opts, int version)
+int git_clone_init_options(git_clone_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
+ return 0;
+}
+
+static const char *repository_base(git_repository *repo)
+{
+ if (git_repository_is_bare(repo))
+ return git_repository_path(repo);
+
+ return git_repository_workdir(repo);
+}
+
+static bool can_link(const char *src, const char *dst, int link)
{
- if (version != GIT_CLONE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_clone_options", version);
+#ifdef GIT_WIN32
+ GIT_UNUSED(src);
+ GIT_UNUSED(dst);
+ GIT_UNUSED(link);
+ return false;
+#else
+
+ struct stat st_src, st_dst;
+
+ if (!link)
+ return false;
+
+ if (p_stat(src, &st_src) < 0)
+ return false;
+
+ if (p_stat(dst, &st_dst) < 0)
+ return false;
+
+ return st_src.st_dev == st_dst.st_dev;
+#endif
+}
+
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
+{
+ int error, flags;
+ git_repository *src;
+ git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
+ git_buf reflog_message = GIT_BUF_INIT;
+
+ assert(repo && remote);
+
+ if (!git_repository_is_empty(repo)) {
+ giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
- } else {
- git_clone_options o = GIT_CLONE_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
}
+
+ /*
+ * Let's figure out what path we should use for the source
+ * repo, if it's not rooted, the path should be relative to
+ * the repository's worktree/gitdir.
+ */
+ if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
+ return error;
+
+ /* Copy .git/objects/ from the source to the target */
+ if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
+ git_buf_free(&src_path);
+ return error;
+ }
+
+ git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
+ git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
+ if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ flags = 0;
+ if (can_link(git_repository_path(src), git_repository_path(repo), link))
+ flags |= GIT_CPDIR_LINK_FILES;
+
+ if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
+ flags, GIT_OBJECT_DIR_MODE)) < 0)
+ goto cleanup;
+
+ git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+
+ if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
+ goto cleanup;
+
+ error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
+
+cleanup:
+ git_buf_free(&reflog_message);
+ git_buf_free(&src_path);
+ git_buf_free(&src_odb);
+ git_buf_free(&dst_odb);
+ git_repository_free(src);
+ return error;
}
diff --git a/src/clone.h b/src/clone.h
new file mode 100644
index 000000000..14ca5d44c
--- /dev/null
+++ b/src/clone.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_clone_h__
+#define INCLUDE_clone_h__
+
+extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
+
+#endif
diff --git a/src/commit.c b/src/commit.c
index 255debe82..227d5c4a5 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -34,6 +34,35 @@ void git_commit__free(void *_commit)
git__free(commit);
}
+static int update_ref_for_commit(git_repository *repo, git_reference *ref, const char *update_ref, const git_oid *id, const git_signature *committer)
+{
+ git_reference *ref2 = NULL;
+ int error;
+ git_commit *c;
+ const char *shortmsg;
+ git_buf reflog_msg = GIT_BUF_INIT;
+
+ if ((error = git_commit_lookup(&c, repo, id)) < 0) {
+ return error;
+ }
+
+ shortmsg = git_commit_summary(c);
+ git_buf_printf(&reflog_msg, "commit%s: %s",
+ git_commit_parentcount(c) == 0 ? " (initial)" : "",
+ shortmsg);
+ git_commit_free(c);
+
+ if (ref) {
+ error = git_reference_set_target(&ref2, ref, id, committer, git_buf_cstr(&reflog_msg));
+ git_reference_free(ref2);
+ } else {
+ error = git_reference__update_terminal(repo, update_ref, id, committer, git_buf_cstr(&reflog_msg));
+ }
+
+ git_buf_free(&reflog_msg);
+ return error;
+}
+
int git_commit_create_from_callback(
git_oid *id,
git_repository *repo,
@@ -46,6 +75,9 @@ int git_commit_create_from_callback(
git_commit_parent_callback parent_cb,
void *parent_payload)
{
+ 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;
@@ -53,10 +85,31 @@ int git_commit_create_from_callback(
assert(id && repo && tree && parent_cb);
+ 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);
+
git_oid__writebuf(&commit, "tree ", tree);
- while ((parent = parent_cb(i++, parent_payload)) != NULL)
+ 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;
+ i++;
+ }
+
+ if (ref && !matched_parent) {
+ git_reference_free(ref);
+ git_buf_free(&commit);
+ giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
+ return GIT_EMODIFIED;
+ }
git_signature__writebuf(&commit, "author ", author);
git_signature__writebuf(&commit, "committer ", committer);
@@ -78,24 +131,8 @@ int git_commit_create_from_callback(
git_buf_free(&commit);
if (update_ref != NULL) {
- int error;
- git_commit *c;
- const char *shortmsg;
- git_buf reflog_msg = GIT_BUF_INIT;
-
- if (git_commit_lookup(&c, repo, id) < 0)
- goto on_error;
-
- shortmsg = git_commit_summary(c);
- git_buf_printf(&reflog_msg, "commit%s: %s",
- git_commit_parentcount(c) == 0 ? " (initial)" : "",
- shortmsg);
- git_commit_free(c);
-
- error = git_reference__update_terminal(repo, update_ref, id,
- committer, git_buf_cstr(&reflog_msg));
-
- git_buf_free(&reflog_msg);
+ error = update_ref_for_commit(repo, ref, update_ref, id, committer);
+ git_reference_free(ref);
return error;
}
@@ -242,6 +279,8 @@ int git_commit_amend(
{
git_repository *repo;
git_oid tree_id;
+ git_reference *ref;
+ int error;
assert(id && commit_to_amend);
@@ -266,9 +305,27 @@ int git_commit_amend(
git_oid_cpy(&tree_id, git_tree_id(tree));
}
- return git_commit_create_from_callback(
- id, repo, update_ref, author, committer, message_encoding, message,
+ if (update_ref) {
+ if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
+ return error;
+
+ if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
+ git_reference_free(ref);
+ giterr_set(GITERR_REFERENCE, "commit to amend is not the tip of the given branch");
+ return -1;
+ }
+ }
+
+ error = git_commit_create_from_callback(
+ id, repo, NULL, author, committer, message_encoding, message,
&tree_id, commit_parent_for_amend, (void *)commit_to_amend);
+
+ if (!error && update_ref) {
+ error = update_ref_for_commit(repo, ref, NULL, id, committer);
+ git_reference_free(ref);
+ }
+
+ return error;
}
int git_commit__parse(void *_commit, git_odb_object *odb_obj)
diff --git a/src/common.h b/src/common.h
index 9c8bdc18a..807e5fa39 100644
--- a/src/common.h
+++ b/src/common.h
@@ -44,6 +44,7 @@
#else
# include <unistd.h>
+# include <strings.h>
# ifdef GIT_THREADS
# include <pthread.h>
# include <sched.h>
@@ -169,6 +170,11 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
}
#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
+#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
+ TYPE _tmpl = TPL; \
+ GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
+ memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
/* NOTE: other giterr functions are in the public errors.h header file */
#include "util.h"
diff --git a/src/config.c b/src/config.c
index b3168f735..8a0fb653c 100644
--- a/src/config.c
+++ b/src/config.c
@@ -137,6 +137,38 @@ int git_config_open_ondisk(git_config **out, const char *path)
return error;
}
+int git_config_snapshot(git_config **out, git_config *in)
+{
+ int error = 0;
+ size_t i;
+ file_internal *internal;
+ git_config *config;
+
+ *out = NULL;
+
+ if (git_config_new(&config) < 0)
+ return -1;
+
+ git_vector_foreach(&in->files, i, internal) {
+ git_config_backend *b;
+
+ if ((error = internal->file->snapshot(&b, internal->file)) < 0)
+ break;
+
+ if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
+ b->free(b);
+ break;
+ }
+ }
+
+ if (error < 0)
+ git_config_free(config);
+ else
+ *out = config;
+
+ return error;
+}
+
static int find_internal_file_by_level(
file_internal **internal_out,
const git_config *cfg,
@@ -967,16 +999,19 @@ void git_config_iterator_free(git_config_iterator *iter)
int git_config_find_global(git_buf *path)
{
+ git_buf_sanitize(path);
return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
}
int git_config_find_xdg(git_buf *path)
{
+ git_buf_sanitize(path);
return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
}
int git_config_find_system(git_buf *path)
{
+ git_buf_sanitize(path);
return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
@@ -984,24 +1019,22 @@ int git_config__global_location(git_buf *buf)
{
const git_buf *paths;
const char *sep, *start;
- size_t len;
if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
return -1;
/* no paths, so give up */
- if (git_buf_len(paths) == 0)
+ if (!paths || !git_buf_len(paths))
return -1;
- start = git_buf_cstr(paths);
- sep = strchr(start, GIT_PATH_LIST_SEPARATOR);
-
- if (sep)
- len = sep - start;
- else
- len = paths->size;
+ /* find unescaped separator or end of string */
+ for (sep = start = git_buf_cstr(paths); *sep; ++sep) {
+ if (*sep == GIT_PATH_LIST_SEPARATOR &&
+ (sep <= start || sep[-1] != '\\'))
+ break;
+ }
- if (git_buf_set(buf, start, len) < 0)
+ if (git_buf_set(buf, start, (size_t)(sep - start)) < 0)
return -1;
return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
@@ -1144,7 +1177,7 @@ int git_config_parse_int64(int64_t *out, const char *value)
}
fail_parse:
- giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)");
return -1;
}
@@ -1164,7 +1197,7 @@ int git_config_parse_int32(int32_t *out, const char *value)
return 0;
fail_parse:
- giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
return -1;
}
@@ -1276,14 +1309,9 @@ cleanup:
return error;
}
-int git_config_init_backend(git_config_backend* backend, int version)
+int git_config_init_backend(git_config_backend *backend, unsigned int version)
{
- if (version != GIT_CONFIG_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_config_backend", version);
- return -1;
- } else {
- git_config_backend b = GIT_CONFIG_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
+ return 0;
}
diff --git a/src/config.h b/src/config.h
index 00b6063e7..b0dcb49ac 100644
--- a/src/config.h
+++ b/src/config.h
@@ -76,4 +76,10 @@ extern int git_config__get_bool_force(
extern int git_config__get_int_force(
const git_config *cfg, const char *key, int fallback_value);
+/* API for repository cvar-style lookups from config - not cached, but
+ * uses cvar value maps and fallbacks
+ */
+extern int git_config__cvar(
+ int *out, git_config *config, git_cvar_cached cvar);
+
#endif
diff --git a/src/config_cache.c b/src/config_cache.c
index 4bcbf02bf..45c39ce17 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -7,11 +7,11 @@
#include "common.h"
#include "fileops.h"
+#include "repository.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
#include "filter.h"
-#include "repository.h"
struct map_data {
const char *cvar_name;
@@ -51,6 +51,12 @@ static git_cvar_map _cvar_map_autocrlf[] = {
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
};
+static git_cvar_map _cvar_map_safecrlf[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE},
+ {GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL},
+ {GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN}
+};
+
/*
* Generic map for integer values
*/
@@ -68,33 +74,39 @@ static struct map_data _cvar_maps[] = {
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
- {"core.safecrlf", NULL, 0, GIT_SAFE_CRLF_DEFAULT},
+ {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
+ {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
};
+int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
+{
+ int error = 0;
+ struct map_data *data = &_cvar_maps[(int)cvar];
+ const git_config_entry *entry;
+
+ git_config__lookup_entry(&entry, config, data->cvar_name, false);
+
+ if (!entry)
+ *out = data->default_value;
+ else if (data->maps)
+ error = git_config_lookup_map_value(
+ out, data->maps, data->map_count, entry->value);
+ else
+ error = git_config_parse_bool(out, entry->value);
+
+ return error;
+}
+
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
{
*out = repo->cvar_cache[(int)cvar];
if (*out == GIT_CVAR_NOT_CACHED) {
- struct map_data *data = &_cvar_maps[(int)cvar];
- git_config *config;
int error;
- const git_config_entry *entry;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- git_config__lookup_entry(&entry, config, data->cvar_name, false);
-
- if (!entry)
- *out = data->default_value;
- else if (data->maps)
- error = git_config_lookup_map_value(
- out, data->maps, data->map_count, entry->value);
- else
- error = git_config_parse_bool(out, entry->value);
+ git_config *config;
- if (error < 0)
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
+ (error = git_config__cvar(out, config, cvar)) < 0)
return error;
repo->cvar_cache[(int)cvar] = *out;
diff --git a/src/config_file.c b/src/config_file.c
index bb26aa8a3..7106f18db 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -26,7 +26,7 @@ GIT__USE_STRMAP;
typedef struct cvar_t {
struct cvar_t *next;
git_config_entry *entry;
- int included; /* whether this is part of [include] */
+ bool included; /* whether this is part of [include] */
} cvar_t;
typedef struct git_config_file_iter {
@@ -87,28 +87,54 @@ struct reader {
};
typedef struct {
+ git_atomic refcount;
+ git_strmap *values;
+} refcounted_strmap;
+
+typedef struct {
git_config_backend parent;
+ /* mutex to coordinate accessing the values */
+ git_mutex values_mutex;
+ refcounted_strmap *values;
+ int readonly;
+} diskfile_header;
- git_strmap *values;
+typedef struct {
+ diskfile_header header;
+
+ git_config_level_t level;
git_array_t(struct reader) readers;
char *file_path;
-
- git_config_level_t level;
} diskfile_backend;
-static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
+typedef struct {
+ diskfile_header header;
+
+ diskfile_backend *snapshot_from;
+} diskfile_readonly_backend;
+
+static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
static int parse_variable(struct reader *reader, char **var_name, char **var_value);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
+int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
+static int config_snapshot(git_config_backend **out, git_config_backend *in);
+
static void set_parse_error(struct reader *reader, int col, const char *error_str)
{
giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
error_str, reader->file_path, reader->line_number, col);
}
+static int config_error_readonly(void)
+{
+ giterr_set(GITERR_CONFIG, "this backend is read-only");
+ return -1;
+}
+
static void cvar_free(cvar_t *var)
{
if (var == NULL)
@@ -120,18 +146,6 @@ static void cvar_free(cvar_t *var)
git__free(var);
}
-static int cvar_length(cvar_t *var)
-{
- int length = 0;
-
- while (var) {
- length++;
- var = var->next;
- }
-
- return length;
-}
-
int git_config_file_normalize_section(char *start, char *end)
{
char *scan;
@@ -155,6 +169,30 @@ int git_config_file_normalize_section(char *start, char *end)
return 0;
}
+/* Add or append the new config option */
+static int append_entry(git_strmap *values, cvar_t *var)
+{
+ git_strmap_iter pos;
+ cvar_t *existing;
+ int error = 0;
+
+ pos = git_strmap_lookup_index(values, var->entry->name);
+ if (!git_strmap_valid_index(values, pos)) {
+ git_strmap_insert(values, var->entry->name, var, error);
+ } else {
+ existing = git_strmap_value_at(values, pos);
+ while (existing->next != NULL) {
+ existing = existing->next;
+ }
+ existing->next = var;
+ }
+
+ if (error > 0)
+ error = 0;
+
+ return error;
+}
+
static void free_vars(git_strmap *values)
{
cvar_t *var = NULL;
@@ -172,6 +210,55 @@ static void free_vars(git_strmap *values)
git_strmap_free(values);
}
+static void refcounted_strmap_free(refcounted_strmap *map)
+{
+ if (!map)
+ return;
+
+ if (git_atomic_dec(&map->refcount) != 0)
+ return;
+
+ free_vars(map->values);
+ git__free(map);
+}
+
+/**
+ * Take the current values map from the backend and increase its
+ * refcount. This is its own function to make sure we use the mutex to
+ * avoid the map pointer from changing under us.
+ */
+static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
+{
+ refcounted_strmap *map;
+
+ git_mutex_lock(&h->values_mutex);
+
+ map = h->values;
+ git_atomic_inc(&map->refcount);
+
+ git_mutex_unlock(&h->values_mutex);
+
+ return map;
+}
+
+static int refcounted_strmap_alloc(refcounted_strmap **out)
+{
+ refcounted_strmap *map;
+ int error;
+
+ map = git__calloc(1, sizeof(refcounted_strmap));
+ GITERR_CHECK_ALLOC(map);
+
+ git_atomic_set(&map->refcount, 1);
+
+ if ((error = git_strmap_alloc(&map->values)) < 0)
+ git__free(map);
+ else
+ *out = map;
+
+ return error;
+}
+
static int config_open(git_config_backend *cfg, git_config_level_t level)
{
int res;
@@ -180,13 +267,13 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
b->level = level;
- if ((res = git_strmap_alloc(&b->values)) < 0)
+ if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res;
git_array_init(b->readers);
reader = git_array_alloc(b->readers);
if (!reader) {
- git_strmap_free(b->values);
+ refcounted_strmap_free(b->header.values);
return -1;
}
memset(reader, 0, sizeof(struct reader));
@@ -202,9 +289,9 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if (res == GIT_ENOTFOUND)
return 0;
- if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
- free_vars(b->values);
- b->values = NULL;
+ if (res < 0 || (res = config_parse(b->header.values->values, b, reader, level, 0)) < 0) {
+ refcounted_strmap_free(b->header.values);
+ b->header.values = NULL;
}
reader = git_array_get(b->readers, 0);
@@ -213,44 +300,62 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
return res;
}
+/* The meat of the refresh, as we want to use it in different places */
+static int config__refresh(git_config_backend *cfg)
+{
+ refcounted_strmap *values = NULL, *tmp;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+ struct reader *reader = NULL;
+ int error = 0;
+
+ if ((error = refcounted_strmap_alloc(&values)) < 0)
+ goto out;
+
+ reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
+ GITERR_CHECK_ALLOC(reader);
+
+ if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
+ goto out;
+
+ git_mutex_lock(&b->header.values_mutex);
+
+ tmp = b->header.values;
+ b->header.values = values;
+ values = tmp;
+
+ git_mutex_unlock(&b->header.values_mutex);
+
+out:
+ refcounted_strmap_free(values);
+ if (reader)
+ git_buf_free(&reader->buffer);
+ return error;
+}
+
static int config_refresh(git_config_backend *cfg)
{
- int res = 0, updated = 0, any_updated = 0;
+ int error = 0, updated = 0, any_updated = 0;
diskfile_backend *b = (diskfile_backend *)cfg;
- git_strmap *old_values;
struct reader *reader = NULL;
uint32_t i;
for (i = 0; i < git_array_size(b->readers); i++) {
reader = git_array_get(b->readers, i);
-
- res = git_futils_readbuffer_updated(
+ error = git_futils_readbuffer_updated(
&reader->buffer, reader->file_path,
&reader->file_mtime, &reader->file_size, &updated);
- if (res < 0)
- return (res == GIT_ENOTFOUND) ? 0 : res;
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
if (updated)
any_updated = 1;
}
if (!any_updated)
- return (res == GIT_ENOTFOUND) ? 0 : res;
-
- /* need to reload - store old values and prep for reload */
- old_values = b->values;
- if ((res = git_strmap_alloc(&b->values)) < 0) {
- b->values = old_values;
- } else if ((res = config_parse(b, reader, b->level, 0)) < 0) {
- free_vars(b->values);
- b->values = old_values;
- } else {
- free_vars(old_values);
- }
+ return (error == GIT_ENOTFOUND) ? 0 : error;
- git_buf_free(&reader->buffer);
- return res;
+ return config__refresh(cfg);
}
static void backend_free(git_config_backend *_backend)
@@ -268,13 +373,15 @@ static void backend_free(git_config_backend *_backend)
git_array_clear(backend->readers);
git__free(backend->file_path);
- free_vars(backend->values);
+ refcounted_strmap_free(backend->header.values);
+ git_mutex_free(&backend->header.values_mutex);
git__free(backend);
}
static void config_iterator_free(
git_config_iterator* iter)
{
+ iter->backend->free(iter->backend);
git__free(iter);
}
@@ -283,12 +390,13 @@ static int config_iterator_next(
git_config_iterator *iter)
{
git_config_file_iter *it = (git_config_file_iter *) iter;
- diskfile_backend *b = (diskfile_backend *) it->parent.backend;
+ diskfile_header *h = (diskfile_header *) it->parent.backend;
+ git_strmap *values = h->values->values;
int err = 0;
cvar_t * var;
if (it->next_var == NULL) {
- err = git_strmap_next((void**) &var, &(it->iter), b->values);
+ err = git_strmap_next((void**) &var, &(it->iter), values);
} else {
var = it->next_var;
}
@@ -308,15 +416,28 @@ static int config_iterator_new(
git_config_iterator **iter,
struct git_config_backend* backend)
{
- diskfile_backend *b = (diskfile_backend *)backend;
- git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
+ diskfile_header *h;
+ git_config_file_iter *it;
+ git_config_backend *snapshot;
+ diskfile_backend *b = (diskfile_backend *) backend;
+ int error;
+
+ if ((error = config_snapshot(&snapshot, backend)) < 0)
+ return error;
- GIT_UNUSED(b);
+ if ((error = snapshot->open(snapshot, b->level)) < 0)
+ return error;
+ it = git__calloc(1, sizeof(git_config_file_iter));
GITERR_CHECK_ALLOC(it);
- it->parent.backend = backend;
- it->iter = git_strmap_begin(b->values);
+ h = (diskfile_header *)snapshot;
+
+ /* strmap_begin() is currently a macro returning 0 */
+ GIT_UNUSED(h);
+
+ it->parent.backend = snapshot;
+ it->iter = git_strmap_begin(h->values);
it->next_var = NULL;
it->parent.next = config_iterator_next;
@@ -328,8 +449,9 @@ static int config_iterator_new(
static int config_set(git_config_backend *cfg, const char *name, const char *value)
{
- cvar_t *var = NULL, *old_var = NULL;
diskfile_backend *b = (diskfile_backend *)cfg;
+ refcounted_strmap *map;
+ git_strmap *values;
char *key, *esc_value = NULL;
khiter_t pos;
int rval, ret;
@@ -337,93 +459,82 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval;
+ map = refcounted_strmap_take(&b->header);
+ values = map->values;
+
/*
* Try to find it in the existing values and update it if it
* only has one value.
*/
- pos = git_strmap_lookup_index(b->values, key);
- if (git_strmap_valid_index(b->values, pos)) {
- cvar_t *existing = git_strmap_value_at(b->values, pos);
- char *tmp = NULL;
-
- git__free(key);
+ pos = git_strmap_lookup_index(values, key);
+ if (git_strmap_valid_index(values, pos)) {
+ cvar_t *existing = git_strmap_value_at(values, pos);
if (existing->next != NULL) {
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
- return -1;
+ ret = -1;
+ goto out;
}
/* don't update if old and new values already match */
if ((!existing->entry->value && !value) ||
- (existing->entry->value && value && !strcmp(existing->entry->value, value)))
- return 0;
-
- if (value) {
- tmp = git__strdup(value);
- GITERR_CHECK_ALLOC(tmp);
- esc_value = escape_value(value);
- GITERR_CHECK_ALLOC(esc_value);
+ (existing->entry->value && value &&
+ !strcmp(existing->entry->value, value))) {
+ ret = 0;
+ goto out;
}
-
- git__free((void *)existing->entry->value);
- existing->entry->value = tmp;
-
- ret = config_write(b, existing->entry->name, NULL, esc_value);
-
- git__free(esc_value);
- return ret;
}
- var = git__malloc(sizeof(cvar_t));
- GITERR_CHECK_ALLOC(var);
- memset(var, 0x0, sizeof(cvar_t));
- var->entry = git__malloc(sizeof(git_config_entry));
- GITERR_CHECK_ALLOC(var->entry);
- memset(var->entry, 0x0, sizeof(git_config_entry));
-
- var->entry->name = key;
- var->entry->value = NULL;
+ /* No early returns due to sanity checks, let's write it out and refresh */
if (value) {
- var->entry->value = git__strdup(value);
- GITERR_CHECK_ALLOC(var->entry->value);
esc_value = escape_value(value);
GITERR_CHECK_ALLOC(esc_value);
}
- if ((ret = config_write(b, key, NULL, esc_value)) < 0) {
- git__free(esc_value);
- cvar_free(var);
- return ret;
- }
+ if ((ret = config_write(b, key, NULL, esc_value)) < 0)
+ goto out;
- git__free(esc_value);
- git_strmap_insert2(b->values, key, var, old_var, rval);
- if (rval < 0)
- return -1;
- if (old_var != NULL)
- cvar_free(old_var);
+ ret = config_refresh(cfg);
- return 0;
+out:
+ refcounted_strmap_free(map);
+ git__free(esc_value);
+ git__free(key);
+ return ret;
}
/*
* Internal function that actually gets the value in string form
*/
-static int config_get(const git_config_backend *cfg, const char *key, const git_config_entry **out)
+static int config_get(git_config_backend *cfg, const char *key, const git_config_entry **out)
{
- diskfile_backend *b = (diskfile_backend *)cfg;
- khiter_t pos = git_strmap_lookup_index(b->values, key);
+ diskfile_header *h = (diskfile_header *)cfg;
+ refcounted_strmap *map;
+ git_strmap *values;
+ khiter_t pos;
cvar_t *var;
+ int error;
+
+ if (!h->readonly && ((error = config_refresh(cfg)) < 0))
+ return error;
+
+ map = refcounted_strmap_take(h);
+ values = map->values;
+
+ pos = git_strmap_lookup_index(values, key);
/* no error message; the config system will write one */
- if (!git_strmap_valid_index(b->values, pos))
+ if (!git_strmap_valid_index(values, pos)) {
+ refcounted_strmap_free(map);
return GIT_ENOTFOUND;
+ }
- var = git_strmap_value_at(b->values, pos);
+ var = git_strmap_value_at(values, pos);
while (var->next)
var = var->next;
+ refcounted_strmap_free(map);
*out = var->entry;
return 0;
}
@@ -431,9 +542,9 @@ static int config_get(const git_config_backend *cfg, const char *key, const git_
static int config_set_multivar(
git_config_backend *cfg, const char *name, const char *regexp, const char *value)
{
- int replaced = 0;
- cvar_t *var, *newvar;
diskfile_backend *b = (diskfile_backend *)cfg;
+ refcounted_strmap *map;
+ git_strmap *values;
char *key;
regex_t preg;
int result;
@@ -444,62 +555,33 @@ static int config_set_multivar(
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- pos = git_strmap_lookup_index(b->values, key);
- if (!git_strmap_valid_index(b->values, pos)) {
+ map = refcounted_strmap_take(&b->header);
+ values = b->header.values->values;
+
+ pos = git_strmap_lookup_index(values, key);
+ if (!git_strmap_valid_index(values, pos)) {
/* If we don't have it, behave like a normal set */
result = config_set(cfg, name, value);
+ refcounted_strmap_free(map);
git__free(key);
return result;
}
- var = git_strmap_value_at(b->values, pos);
-
result = regcomp(&preg, regexp, REG_EXTENDED);
if (result < 0) {
- git__free(key);
giterr_set_regex(&preg, result);
- regfree(&preg);
- return -1;
- }
-
- for (;;) {
- if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
- char *tmp = git__strdup(value);
- GITERR_CHECK_ALLOC(tmp);
-
- git__free((void *)var->entry->value);
- var->entry->value = tmp;
- replaced = 1;
- }
-
- if (var->next == NULL)
- break;
-
- var = var->next;
+ result = -1;
+ goto out;
}
- /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
- if (!replaced) {
- newvar = git__malloc(sizeof(cvar_t));
- GITERR_CHECK_ALLOC(newvar);
- memset(newvar, 0x0, sizeof(cvar_t));
- newvar->entry = git__malloc(sizeof(git_config_entry));
- GITERR_CHECK_ALLOC(newvar->entry);
- memset(newvar->entry, 0x0, sizeof(git_config_entry));
+ /* If we do have it, set call config_write() and reload */
+ if ((result = config_write(b, key, &preg, value)) < 0)
+ goto out;
- newvar->entry->name = git__strdup(var->entry->name);
- GITERR_CHECK_ALLOC(newvar->entry->name);
-
- newvar->entry->value = git__strdup(value);
- GITERR_CHECK_ALLOC(newvar->entry->value);
-
- newvar->entry->level = var->entry->level;
-
- var->next = newvar;
- }
-
- result = config_write(b, key, &preg, value);
+ result = config_refresh(cfg);
+out:
+ refcounted_strmap_free(map);
git__free(key);
regfree(&preg);
@@ -510,6 +592,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
{
cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg;
+ refcounted_strmap *map; git_strmap *values;
char *key;
int result;
khiter_t pos;
@@ -517,35 +600,37 @@ static int config_delete(git_config_backend *cfg, const char *name)
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- pos = git_strmap_lookup_index(b->values, key);
+ map = refcounted_strmap_take(&b->header);
+ values = b->header.values->values;
+
+ pos = git_strmap_lookup_index(values, key);
git__free(key);
- if (!git_strmap_valid_index(b->values, pos)) {
+ if (!git_strmap_valid_index(values, pos)) {
+ refcounted_strmap_free(map);
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
}
- var = git_strmap_value_at(b->values, pos);
+ var = git_strmap_value_at(values, pos);
+ refcounted_strmap_free(map);
if (var->next != NULL) {
giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
return -1;
}
- git_strmap_delete_at(b->values, pos);
-
- result = config_write(b, var->entry->name, NULL, NULL);
+ if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
+ return result;
- cvar_free(var);
- return result;
+ return config_refresh(cfg);
}
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
{
- cvar_t *var, *prev = NULL, *new_head = NULL;
- cvar_t **to_delete;
- int to_delete_idx;
diskfile_backend *b = (diskfile_backend *)cfg;
+ refcounted_strmap *map;
+ git_strmap *values;
char *key;
regex_t preg;
int result;
@@ -554,66 +639,45 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- pos = git_strmap_lookup_index(b->values, key);
+ map = refcounted_strmap_take(&b->header);
+ values = b->header.values->values;
- if (!git_strmap_valid_index(b->values, pos)) {
- giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
+ pos = git_strmap_lookup_index(values, key);
+
+ if (!git_strmap_valid_index(values, pos)) {
+ refcounted_strmap_free(map);
git__free(key);
+ giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
return GIT_ENOTFOUND;
}
- var = git_strmap_value_at(b->values, pos);
+ refcounted_strmap_free(map);
result = regcomp(&preg, regexp, REG_EXTENDED);
if (result < 0) {
- git__free(key);
giterr_set_regex(&preg, result);
- regfree(&preg);
- return -1;
- }
-
- to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
- GITERR_CHECK_ALLOC(to_delete);
- to_delete_idx = 0;
-
- while (var != NULL) {
- cvar_t *next = var->next;
-
- if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
- // If we are past the head, reattach previous node to next one,
- // otherwise set the new head for the strmap.
- if (prev != NULL) {
- prev->next = next;
- } else {
- new_head = next;
- }
-
- to_delete[to_delete_idx++] = var;
- } else {
- prev = var;
- }
-
- var = next;
- }
-
- if (new_head != NULL) {
- git_strmap_set_value_at(b->values, pos, new_head);
- } else {
- git_strmap_delete_at(b->values, pos);
+ result = -1;
+ goto out;
}
- if (to_delete_idx > 0)
- result = config_write(b, key, &preg, NULL);
+ if ((result = config_write(b, key, &preg, NULL)) < 0)
+ goto out;
- while (to_delete_idx-- > 0)
- cvar_free(to_delete[to_delete_idx]);
+ result = config_refresh(cfg);
+out:
git__free(key);
- git__free(to_delete);
regfree(&preg);
return result;
}
+static int config_snapshot(git_config_backend **out, git_config_backend *in)
+{
+ diskfile_backend *b = (diskfile_backend *) in;
+
+ return git_config_file__snapshot(out, b);
+}
+
int git_config_file__ondisk(git_config_backend **out, const char *path)
{
diskfile_backend *backend;
@@ -621,20 +685,122 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
backend = git__calloc(1, sizeof(diskfile_backend));
GITERR_CHECK_ALLOC(backend);
- backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->header.values_mutex);
backend->file_path = git__strdup(path);
GITERR_CHECK_ALLOC(backend->file_path);
- backend->parent.open = config_open;
- backend->parent.get = config_get;
- backend->parent.set = config_set;
- backend->parent.set_multivar = config_set_multivar;
- backend->parent.del = config_delete;
- backend->parent.del_multivar = config_delete_multivar;
- backend->parent.iterator = config_iterator_new;
- backend->parent.refresh = config_refresh;
- backend->parent.free = backend_free;
+ backend->header.parent.open = config_open;
+ backend->header.parent.get = config_get;
+ backend->header.parent.set = config_set;
+ backend->header.parent.set_multivar = config_set_multivar;
+ backend->header.parent.del = config_delete;
+ backend->header.parent.del_multivar = config_delete_multivar;
+ backend->header.parent.iterator = config_iterator_new;
+ backend->header.parent.refresh = config_refresh;
+ backend->header.parent.snapshot = config_snapshot;
+ backend->header.parent.free = backend_free;
+
+ *out = (git_config_backend *)backend;
+
+ return 0;
+}
+
+static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ return config_error_readonly();
+}
+
+static int config_set_multivar_readonly(
+ git_config_backend *cfg, const char *name, const char *regexp, const char *value)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+ GIT_UNUSED(value);
+
+ return config_error_readonly();
+}
+
+static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+
+ return config_error_readonly();
+}
+
+static int config_delete_readonly(git_config_backend *cfg, const char *name)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+
+ return config_error_readonly();
+}
+
+static int config_refresh_readonly(git_config_backend *cfg)
+{
+ GIT_UNUSED(cfg);
+
+ return config_error_readonly();
+}
+
+static void backend_readonly_free(git_config_backend *_backend)
+{
+ diskfile_backend *backend = (diskfile_backend *)_backend;
+
+ if (backend == NULL)
+ return;
+
+ refcounted_strmap_free(backend->header.values);
+ git_mutex_free(&backend->header.values_mutex);
+ git__free(backend);
+}
+
+static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
+{
+ diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
+ diskfile_backend *src = b->snapshot_from;
+ refcounted_strmap *src_map;
+
+ /* We're just copying data, don't care about the level */
+ GIT_UNUSED(level);
+
+ src_map = refcounted_strmap_take(&src->header);
+ b->header.values = src_map;
+
+ return 0;
+}
+
+int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
+{
+ diskfile_readonly_backend *backend;
+
+ backend = git__calloc(1, sizeof(diskfile_readonly_backend));
+ GITERR_CHECK_ALLOC(backend);
+
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->header.values_mutex);
+
+ backend->snapshot_from = in;
+
+ backend->header.readonly = 1;
+ backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
+ backend->header.parent.open = config_readonly_open;
+ backend->header.parent.get = config_get;
+ backend->header.parent.set = config_set_readonly;
+ backend->header.parent.set_multivar = config_set_multivar_readonly;
+ backend->header.parent.del = config_delete_readonly;
+ backend->header.parent.del_multivar = config_delete_multivar_readonly;
+ backend->header.parent.iterator = config_iterator_new;
+ backend->header.parent.refresh = config_refresh_readonly;
+ backend->header.parent.free = backend_readonly_free;
*out = (git_config_backend *)backend;
@@ -1014,16 +1180,15 @@ static int included_path(git_buf *out, const char *dir, const char *path)
return git_path_join_unrooted(out, path, dir, NULL);
}
-static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
+static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
{
int c;
char *current_section = NULL;
char *var_name;
char *var_value;
- cvar_t *var, *existing;
+ cvar_t *var;
git_buf buf = GIT_BUF_INIT;
int result = 0;
- khiter_t pos;
uint32_t reader_idx;
if (depth >= MAX_INCLUDE_DEPTH) {
@@ -1088,21 +1253,13 @@ static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_c
var->entry->level = level;
var->included = !!depth;
- /* Add or append the new config option */
- pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
- if (!git_strmap_valid_index(cfg_file->values, pos)) {
- git_strmap_insert(cfg_file->values, var->entry->name, var, result);
- if (result < 0)
- break;
+
+ if ((result = append_entry(values, var)) < 0)
+ break;
+ else
result = 0;
- } else {
- existing = git_strmap_value_at(cfg_file->values, pos);
- while (existing->next != NULL) {
- existing = existing->next;
- }
- existing->next = var;
- }
+ /* Add or append the new config option */
if (!git__strcmp(var->entry->name, "include.path")) {
struct reader *r;
git_buf path = GIT_BUF_INIT;
@@ -1131,7 +1288,7 @@ static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_c
&r->file_size, NULL)) < 0)
break;
- result = config_parse(cfg_file, r, level, depth+1);
+ result = config_parse(values, cfg_file, r, level, depth+1);
r = git_array_get(cfg_file->readers, index);
git_buf_free(&r->buffer);
@@ -1365,6 +1522,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
+ if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
+ git_filebuf_write(&file, "\n", 1);
+
/* And now if we just need to add a variable */
if (!section_matches && write_section(&file, section) < 0)
goto rewrite_fail;
@@ -1374,14 +1534,11 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* this, but instead we'll handle it gracefully with an error. */
if (value == NULL) {
giterr_set(GITERR_CONFIG,
- "Race condition when writing a config file (a cvar has been removed)");
+ "race condition when writing a config file (a cvar has been removed)");
goto rewrite_fail;
}
/* If we are here, there is at least a section line */
- if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
- git_filebuf_write(&file, "\n", 1);
-
q = quotes_for_value(value);
git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
@@ -1492,7 +1649,7 @@ static int is_multiline_var(const char *str)
}
/* An odd number means last backslash wasn't escaped, so it's multiline */
- return (end > str) && (count & 1);
+ return count & 1;
}
static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
diff --git a/src/crlf.c b/src/crlf.c
index 8be1b9a05..93448760d 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -138,11 +138,24 @@ static int crlf_apply_to_odb(
if (git_buf_text_gather_stats(&stats, from, false))
return GIT_PASSTHROUGH;
+ /* If there are no CR characters to filter out, then just pass */
+ if (!stats.cr)
+ return GIT_PASSTHROUGH;
+
/* If safecrlf is enabled, sanity-check the result. */
- if (ca->safe_crlf && (stats.cr != stats.crlf || stats.lf != stats.crlf)) {
- giterr_set(GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
- git_filter_source_path(src));
- return -1;
+ if (stats.cr != stats.crlf || stats.lf != stats.crlf) {
+ switch (ca->safe_crlf) {
+ case GIT_SAFE_CRLF_FAIL:
+ giterr_set(
+ GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
+ git_filter_source_path(src));
+ return -1;
+ case GIT_SAFE_CRLF_WARN:
+ /* TODO: issue warning when warning API is available */;
+ break;
+ default:
+ break;
+ }
}
/*
@@ -267,12 +280,14 @@ static int crlf_check(
if (ca.crlf_action == GIT_CRLF_GUESS ||
(ca.crlf_action == GIT_CRLF_AUTO &&
git_filter_source_mode(src) == GIT_FILTER_SMUDGE)) {
+
error = git_repository__cvar(
&ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
if (error < 0)
return error;
- if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+ if (ca.crlf_action == GIT_CRLF_GUESS &&
+ ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
return GIT_PASSTHROUGH;
if (ca.auto_crlf == GIT_AUTO_CRLF_INPUT &&
@@ -285,6 +300,11 @@ static int crlf_check(
&ca.safe_crlf, git_filter_source_repo(src), GIT_CVAR_SAFE_CRLF);
if (error < 0)
return error;
+
+ /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */
+ if ((git_filter_source_options(src) & GIT_FILTER_OPT_ALLOW_UNSAFE) &&
+ ca.safe_crlf == GIT_SAFE_CRLF_FAIL)
+ ca.safe_crlf = GIT_SAFE_CRLF_WARN;
}
*payload = git__malloc(sizeof(ca));
diff --git a/src/diff.c b/src/diff.c
index 4b6fbe25a..2691d7ca0 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -92,6 +92,10 @@ static int diff_delta__from_one(
if (status == GIT_DELTA_UNTRACKED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0;
+
+ if (status == GIT_DELTA_UNREADABLE &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
+ return 0;
if (!git_pathspec__match(
&diff->pathspec, entry->path,
@@ -196,6 +200,7 @@ static git_diff_delta *diff_delta__last_for_item(
if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
return delta;
break;
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_UNTRACKED:
if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
git_oid__cmp(&delta->new_file.id, &item->id) == 0)
@@ -293,6 +298,10 @@ bool git_diff_delta__should_skip(
(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
return true;
+ if (delta->status == GIT_DELTA_UNREADABLE &&
+ (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
+ return true;
+
return false;
}
@@ -381,7 +390,7 @@ static int diff_list_apply_options(
git_diff *diff,
const git_diff_options *opts)
{
- git_config *cfg;
+ git_config *cfg = NULL;
git_repository *repo = diff->repo;
git_pool *pool = &diff->pool;
int val;
@@ -406,20 +415,20 @@ static int diff_list_apply_options(
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* load config values that affect diff behavior */
- if ((val = git_repository_config__weakptr(&cfg, repo)) < 0)
+ if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
return val;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
- !git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
+ !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
@@ -442,6 +451,14 @@ static int diff_list_apply_options(
diff->new_src = tmp_src;
}
+ /* Unset UPDATE_INDEX unless diffing workdir and index */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
+ (!(diff->old_src == GIT_ITERATOR_TYPE_WORKDIR ||
+ diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ||
+ !(diff->old_src == GIT_ITERATOR_TYPE_INDEX ||
+ diff->new_src == GIT_ITERATOR_TYPE_INDEX)))
+ diff->opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
+
/* if ignore_submodules not explicitly set, check diff config */
if (diff->opts.ignore_submodules <= 0) {
const git_config_entry *entry;
@@ -473,8 +490,6 @@ static int diff_list_apply_options(
/* strdup prefix from pool so we're not dependent on external data */
diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix);
diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix);
- if (!diff->opts.old_prefix || !diff->opts.new_prefix)
- return -1;
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
const char *tmp_prefix = diff->opts.old_prefix;
@@ -482,7 +497,10 @@ static int diff_list_apply_options(
diff->opts.new_prefix = tmp_prefix;
}
- return 0;
+ git_config_free(cfg);
+
+ /* check strdup results for error */
+ return (!diff->opts.old_prefix || !diff->opts.new_prefix) ? -1 : 0;
}
static void diff_list_free(git_diff *diff)
@@ -510,76 +528,106 @@ void git_diff_addref(git_diff *diff)
}
int git_diff__oid_for_file(
- git_repository *repo,
+ git_oid *out,
+ git_diff *diff,
const char *path,
uint16_t mode,
- git_off_t size,
- git_oid *oid)
+ git_off_t size)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.mode = mode;
+ entry.file_size = size;
+ entry.path = (char *)path;
+
+ return git_diff__oid_for_entry(out, diff, &entry, NULL);
+}
+
+int git_diff__oid_for_entry(
+ git_oid *out,
+ git_diff *diff,
+ const git_index_entry *src,
+ const git_oid *update_match)
{
- int result = 0;
+ int error = 0;
git_buf full_path = GIT_BUF_INIT;
+ git_index_entry entry = *src;
+ git_filter_list *fl = NULL;
+
+ memset(out, 0, sizeof(*out));
if (git_buf_joinpath(
- &full_path, git_repository_workdir(repo), path) < 0)
+ &full_path, git_repository_workdir(diff->repo), entry.path) < 0)
return -1;
- if (!mode) {
+ if (!entry.mode) {
struct stat st;
- if (p_stat(path, &st) < 0) {
- giterr_set(GITERR_OS, "Could not stat '%s'", path);
- result = -1;
- goto cleanup;
+ diff->perf.stat_calls++;
+
+ if (p_stat(full_path.ptr, &st) < 0) {
+ error = git_path_set_error(errno, entry.path, "stat");
+ git_buf_free(&full_path);
+ return error;
}
- mode = st.st_mode;
- size = st.st_size;
+ git_index_entry__init_from_stat(
+ &entry, &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
}
/* calculate OID for file if possible */
- if (S_ISGITLINK(mode)) {
+ if (S_ISGITLINK(entry.mode)) {
git_submodule *sm;
- memset(oid, 0, sizeof(*oid));
-
- if (!git_submodule_lookup(&sm, repo, path)) {
+ if (!git_submodule_lookup(&sm, diff->repo, entry.path)) {
const git_oid *sm_oid = git_submodule_wd_id(sm);
if (sm_oid)
- git_oid_cpy(oid, sm_oid);
+ git_oid_cpy(out, sm_oid);
git_submodule_free(sm);
} else {
/* if submodule lookup failed probably just in an intermediate
* state where some init hasn't happened, so ignore the error
*/
giterr_clear();
- memset(oid, 0, sizeof(*oid));
}
- } else if (S_ISLNK(mode)) {
- result = git_odb__hashlink(oid, full_path.ptr);
- } else if (!git__is_sizet(size)) {
- giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
- result = -1;
- } else {
- git_filter_list *fl = NULL;
-
- result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
- if (!result) {
- int fd = git_futils_open_ro(full_path.ptr);
- if (fd < 0)
- result = fd;
- else {
- result = git_odb__hashfd_filtered(
- oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
- p_close(fd);
- }
-
- git_filter_list_free(fl);
+ } else if (S_ISLNK(entry.mode)) {
+ error = git_odb__hashlink(out, full_path.ptr);
+ diff->perf.oid_calculations++;
+ } else if (!git__is_sizet(entry.file_size)) {
+ giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'",
+ entry.path);
+ error = -1;
+ } else if (!(error = git_filter_list_load(
+ &fl, diff->repo, NULL, entry.path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)))
+ {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ error = fd;
+ else {
+ error = git_odb__hashfd_filtered(
+ out, fd, (size_t)entry.file_size, GIT_OBJ_BLOB, fl);
+ p_close(fd);
+ diff->perf.oid_calculations++;
}
+
+ git_filter_list_free(fl);
}
-cleanup:
+ /* update index for entry if requested */
+ if (!error && update_match && git_oid_equal(out, update_match)) {
+ git_index *idx;
+
+ if (!(error = git_repository_index(&idx, diff->repo))) {
+ memcpy(&entry.id, out, sizeof(entry.id));
+ error = git_index_add(idx, &entry);
+ git_index_free(idx);
+ }
+ }
+
git_buf_free(&full_path);
- return result;
+ return error;
}
static bool diff_time_eq(
@@ -595,7 +643,6 @@ typedef struct {
git_iterator *new_iter;
const git_index_entry *oitem;
const git_index_entry *nitem;
- git_buf ignore_prefix;
} diff_in_progress;
#define MODE_BITS_MASK 0000777
@@ -660,6 +707,7 @@ static int maybe_modified(
unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode;
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
+ bool modified_uncertain = false;
const char *matched_pathspec;
int error = 0;
@@ -695,6 +743,11 @@ static int maybe_modified(
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
status = GIT_DELTA_TYPECHANGE;
+ else if (nmode == GIT_FILEMODE_UNREADABLE) {
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
+ error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem);
+ return error;
+ }
else {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
@@ -727,15 +780,21 @@ static int maybe_modified(
/* if the stat data looks different, then mark modified - this just
* means that the OID will be recalculated below to confirm change
*/
- else if (omode != nmode ||
- oitem->file_size != nitem->file_size ||
- !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
+ else if (omode != nmode || oitem->file_size != nitem->file_size) {
+ status = GIT_DELTA_MODIFIED;
+ modified_uncertain =
+ (oitem->file_size <= 0 && nitem->file_size > 0);
+ }
+ else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
(use_ctime &&
!diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
oitem->ino != nitem->ino ||
oitem->uid != nitem->uid ||
oitem->gid != nitem->gid)
+ {
status = GIT_DELTA_MODIFIED;
+ modified_uncertain = true;
+ }
}
/* if mode is GITLINK and submodules are ignored, then skip */
@@ -746,10 +805,14 @@ static int maybe_modified(
/* if we got here and decided that the files are modified, but we
* haven't calculated the OID of the new item, then calculate it now
*/
- if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->id)) {
+ if (modified_uncertain && git_oid_iszero(&nitem->id)) {
if (git_oid_iszero(&noid)) {
- if ((error = git_diff__oid_for_file(diff->repo,
- nitem->path, nitem->mode, nitem->file_size, &noid)) < 0)
+ const git_oid *update_check =
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) ?
+ &oitem->id : NULL;
+
+ if ((error = git_diff__oid_for_entry(
+ &noid, diff, nitem, update_check)) < 0)
return error;
}
@@ -795,24 +858,13 @@ static int handle_unmatched_new_item(
/* check if this is a prefix of the other side */
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
- /* check if this is contained in an ignored parent directory */
- if (git_buf_len(&info->ignore_prefix)) {
- if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
- delta_type = GIT_DELTA_IGNORED;
- else
- git_buf_clear(&info->ignore_prefix);
- }
+ /* update delta_type if this item is ignored */
+ if (git_iterator_current_is_ignored(info->new_iter))
+ delta_type = GIT_DELTA_IGNORED;
if (nitem->mode == GIT_FILEMODE_TREE) {
bool recurse_into_dir = contains_oitem;
- /* if not already inside an ignored dir, check if this is ignored */
- if (delta_type != GIT_DELTA_IGNORED &&
- git_iterator_current_is_ignored(info->new_iter)) {
- delta_type = GIT_DELTA_IGNORED;
- git_buf_sets(&info->ignore_prefix, nitem->path);
- }
-
/* check if user requests recursion into this type of dir */
recurse_into_dir = contains_oitem ||
(delta_type == GIT_DELTA_UNTRACKED &&
@@ -889,27 +941,12 @@ static int handle_unmatched_new_item(
}
}
- /* In core git, the next two checks are effectively reversed --
- * i.e. when an file contained in an ignored directory is explicitly
- * ignored, it shows up as an ignored file in the diff list, even though
- * other untracked files in the same directory are skipped completely.
- *
- * To me, this seems odd. If the directory is ignored and the file is
- * untracked, we should skip it consistently, regardless of whether it
- * happens to match a pattern in the ignore file.
- *
- * To match the core git behavior, reverse the following two if checks
- * so that individual file ignores are checked before container
- * directory exclusions are used to skip the file.
- */
else if (delta_type == GIT_DELTA_IGNORED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
+ git_iterator_current_tree_is_ignored(info->new_iter))
/* item contained in ignored directory, so skip over it */
return git_iterator_advance(&info->nitem, info->new_iter);
- else if (git_iterator_current_is_ignored(info->new_iter))
- delta_type = GIT_DELTA_IGNORED;
-
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED;
@@ -931,6 +968,13 @@ static int handle_unmatched_new_item(
}
}
+ else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
+ delta_type = GIT_DELTA_UNTRACKED;
+ else
+ delta_type = GIT_DELTA_UNREADABLE;
+ }
+
/* Actually create the record for this item if necessary */
if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
return error;
@@ -1019,7 +1063,6 @@ int git_diff__from_iterators(
info.repo = repo;
info.old_iter = old_iter;
info.new_iter = new_iter;
- git_buf_init(&info.ignore_prefix, 0);
/* make iterators have matching icase behavior */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
@@ -1066,14 +1109,14 @@ int git_diff__from_iterators(
error = 0;
}
+ diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls;
+
cleanup:
if (!error)
*diff_ptr = diff;
else
git_diff_free(diff);
- git_buf_free(&info.ignore_prefix);
-
return error;
}
@@ -1174,6 +1217,9 @@ int git_diff_index_to_workdir(
&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
);
+ if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX))
+ error = git_index_write(index);
+
return error;
}
@@ -1226,20 +1272,6 @@ int git_diff_tree_to_workdir_with_index(
return error;
}
-int git_diff_options_init(git_diff_options *options, unsigned int version)
-{
- git_diff_options template = GIT_DIFF_OPTIONS_INIT;
-
- if (version != template.version) {
- giterr_set(GITERR_INVALID,
- "Invalid version %d for git_diff_options", (int)version);
- return -1;
- }
-
- memcpy(options, &template, sizeof(*options));
- return 0;
-}
-
size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
@@ -1271,6 +1303,15 @@ int git_diff_is_sorted_icase(const git_diff *diff)
return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
}
+int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
+{
+ assert(out);
+ GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+ out->stat_calls = diff->perf.stat_calls;
+ out->oid_calculations = diff->perf.oid_calculations;
+ return 0;
+}
+
int git_diff__paired_foreach(
git_diff *head2idx,
git_diff *idx2wd,
@@ -1573,38 +1614,26 @@ int git_diff_commit_as_email(
return error;
}
-int git_diff_init_options(git_diff_options* opts, int version)
+int git_diff_init_options(git_diff_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_options", version);
- return -1;
- } else {
- git_diff_options o = GIT_DIFF_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
+ return 0;
}
-int git_diff_find_init_options(git_diff_find_options* opts, int version)
+int git_diff_find_init_options(
+ git_diff_find_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_FIND_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_find_options", version);
- return -1;
- } else {
- git_diff_find_options o = GIT_DIFF_FIND_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
+ return 0;
}
-int git_diff_format_email_init_options(git_diff_format_email_options* opts, int version)
+int git_diff_format_email_init_options(
+ git_diff_format_email_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_format_email_options", version);
- return -1;
- } else {
- git_diff_format_email_options o = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_format_email_options,
+ GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/diff.h b/src/diff.h
index aae8fbff1..3305238d0 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -8,6 +8,7 @@
#define INCLUDE_diff_h__
#include "git2/diff.h"
+#include "git2/sys/diff.h"
#include "git2/oid.h"
#include <stdio.h>
@@ -62,6 +63,7 @@ struct git_diff {
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
+ git_diff_perfdata perf;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
@@ -90,7 +92,9 @@ extern int git_diff_delta__format_file_header(
int oid_strlen);
extern int git_diff__oid_for_file(
- git_repository *, const char *, uint16_t, git_off_t, git_oid *);
+ git_oid *out, git_diff *, const char *, uint16_t, git_off_t);
+extern int git_diff__oid_for_entry(
+ git_oid *out, git_diff *, const git_index_entry *, const git_oid *update);
extern int git_diff__from_iterators(
git_diff **diff_ptr,
diff --git a/src/diff_driver.c b/src/diff_driver.c
index 8136e0dd9..c3c5f365b 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -233,17 +233,17 @@ static int git_diff_driver_load(
return 0;
}
- /* if you can't read config for repo, just use default driver */
- if (git_repository_config__weakptr(&cfg, repo) < 0) {
- giterr_clear();
- goto done;
- }
-
drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
GITERR_CHECK_ALLOC(drv);
drv->type = DIFF_DRIVER_AUTO;
memcpy(drv->name, driver_name, namelen);
+ /* if you can't read config for repo, just use default driver */
+ if (git_repository_config_snapshot(&cfg, repo) < 0) {
+ giterr_clear();
+ goto done;
+ }
+
if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0)
goto done;
@@ -321,6 +321,7 @@ static int git_diff_driver_load(
done:
git_buf_free(&name);
+ git_config_free(cfg);
if (!*out) {
int error2 = git_diff_driver_builtin(out, reg, driver_name);
@@ -396,7 +397,11 @@ void git_diff_driver_update_options(
int git_diff_driver_content_is_binary(
git_diff_driver *driver, const char *content, size_t content_len)
{
- const git_buf search = { (char *)content, 0, min(content_len, 4000) };
+ git_buf search;
+
+ search.ptr = (char *)content;
+ search.size = min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL);
+ search.asize = 0;
GIT_UNUSED(driver);
diff --git a/src/diff_file.c b/src/diff_file.c
index b9f92df3f..96be0942b 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -112,6 +112,7 @@ int git_diff_file_content__init_from_diff(
has_data = !use_old &&
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
@@ -300,7 +301,8 @@ static int diff_file_content_load_workdir_file(
goto cleanup;
if ((error = git_filter_list_load(
- &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
+ &fl, fc->repo, NULL, fc->file->path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)) < 0)
goto cleanup;
/* if there are no filters, try to mmap the file */
diff --git a/src/diff_patch.c b/src/diff_patch.c
index 38d5f4257..4e0672aa1 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -274,6 +274,7 @@ int git_diff_foreach(
return error;
memset(&xo, 0, sizeof(xo));
+ memset(&patch, 0, sizeof(patch));
diff_output_init(
&xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, &diff->opts);
diff --git a/src/diff_print.c b/src/diff_print.c
index 07c1f8577..43a90b3d8 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -82,14 +82,15 @@ char git_diff_status_char(git_delta_t status)
char code;
switch (status) {
- case GIT_DELTA_ADDED: code = 'A'; break;
- case GIT_DELTA_DELETED: code = 'D'; break;
- case GIT_DELTA_MODIFIED: code = 'M'; break;
- case GIT_DELTA_RENAMED: code = 'R'; break;
- case GIT_DELTA_COPIED: code = 'C'; break;
- case GIT_DELTA_IGNORED: code = 'I'; break;
- case GIT_DELTA_UNTRACKED: code = '?'; break;
- default: code = ' '; break;
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ case GIT_DELTA_UNREADABLE: code = 'X'; break;
+ default: code = ' '; break;
}
return code;
@@ -286,33 +287,46 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
{
git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL;
const void *old_data, *new_data;
- size_t old_data_len, new_data_len, delta_data_len, inflated_len, remain;
+ git_off_t old_data_len, new_data_len;
+ unsigned long delta_data_len, inflated_len;
const char *out_type = "literal";
- char *ptr;
+ char *scan, *end;
int error;
old_data = old ? git_blob_rawcontent(old) : NULL;
new_data = new ? git_blob_rawcontent(new) : NULL;
- old_data_len = old ? (size_t)git_blob_rawsize(old) : 0;
- new_data_len = new ? (size_t)git_blob_rawsize(new) : 0;
+ old_data_len = old ? git_blob_rawsize(old) : 0;
+ new_data_len = new ? git_blob_rawsize(new) : 0;
+
+ /* The git_delta function accepts unsigned long only */
+ if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len))
+ return GIT_EBUFS;
out = &deflate;
- inflated_len = new_data_len;
+ inflated_len = (unsigned long)new_data_len;
if ((error = git_zstream_deflatebuf(
- &deflate, new_data, new_data_len)) < 0)
+ out, new_data, (size_t)new_data_len)) < 0)
goto done;
- if (old && new) {
- void *delta_data;
+ /* The git_delta function accepts unsigned long only */
+ if (!git__is_ulong((git_off_t)deflate.size)) {
+ error = GIT_EBUFS;
+ goto done;
+ }
- delta_data = git_delta(old_data, old_data_len, new_data,
- new_data_len, &delta_data_len, deflate.size);
+ if (old && new) {
+ void *delta_data = git_delta(
+ old_data, (unsigned long)old_data_len,
+ new_data, (unsigned long)new_data_len,
+ &delta_data_len, (unsigned long)deflate.size);
if (delta_data) {
- error = git_zstream_deflatebuf(&delta, delta_data, delta_data_len);
- free(delta_data);
+ error = git_zstream_deflatebuf(
+ &delta, delta_data, (size_t)delta_data_len);
+
+ git__free(delta_data);
if (error < 0)
goto done;
@@ -325,18 +339,20 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
}
}
- git_buf_printf(pi->buf, "%s %" PRIuZ "\n", out_type, inflated_len);
+ git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len);
pi->line.num_lines++;
- for (ptr = out->ptr, remain = out->size; remain > 0; ) {
- size_t chunk_len = (52 < remain) ? 52 : remain;
+ for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) {
+ size_t chunk_len = end - scan;
+ if (chunk_len > 52)
+ chunk_len = 52;
if (chunk_len <= 26)
- git_buf_putc(pi->buf, chunk_len + 'A' - 1);
+ git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
else
- git_buf_putc(pi->buf, chunk_len - 26 + 'a' - 1);
+ git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
- git_buf_put_base85(pi->buf, ptr, chunk_len);
+ git_buf_encode_base85(pi->buf, scan, chunk_len);
git_buf_putc(pi->buf, '\n');
if (git_buf_oom(pi->buf)) {
@@ -344,8 +360,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
goto done;
}
- ptr += chunk_len;
- remain -= chunk_len;
+ scan += chunk_len;
pi->line.num_lines++;
}
@@ -364,26 +379,33 @@ static int diff_print_patch_file_binary(
git_blob *old = NULL, *new = NULL;
const git_oid *old_id, *new_id;
int error;
+ size_t pre_binary_size;
- if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) {
- pi->line.num_lines = 1;
- return diff_delta_format_with_paths(
- pi->buf, delta, oldpfx, newpfx,
- "Binary files %s%s and %s%s differ\n");
- }
+ if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
+ goto noshow;
+ pre_binary_size = pi->buf->size;
git_buf_printf(pi->buf, "GIT binary patch\n");
pi->line.num_lines++;
old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL;
new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL;
- if ((old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) ||
- (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) ||
- (error = print_binary_hunk(pi, old, new)) < 0 ||
+ if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0)
+ goto done;
+ if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0)
+ goto done;
+
+ if ((error = print_binary_hunk(pi, old, new)) < 0 ||
(error = git_buf_putc(pi->buf, '\n')) < 0 ||
(error = print_binary_hunk(pi, new, old)) < 0)
- goto done;
+ {
+ if (error == GIT_EBUFS) {
+ giterr_clear();
+ git_buf_truncate(pi->buf, pre_binary_size);
+ goto noshow;
+ }
+ }
pi->line.num_lines++;
@@ -392,6 +414,12 @@ done:
git_blob_free(new);
return error;
+
+noshow:
+ pi->line.num_lines = 1;
+ return diff_delta_format_with_paths(
+ pi->buf, delta, oldpfx, newpfx,
+ "Binary files %s%s and %s%s differ\n");
}
static int diff_print_patch_file(
@@ -414,6 +442,7 @@ static int diff_print_patch_file(
if (S_ISDIR(delta->new_file.mode) ||
delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
(delta->status == GIT_DELTA_UNTRACKED &&
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
@@ -597,9 +626,9 @@ int git_diff_print_callback__to_file_handle(
}
/* print a git_patch to a git_buf */
-int git_patch_to_buf(
- git_buf *out,
- git_patch *patch)
+int git_patch_to_buf(git_buf *out, git_patch *patch)
{
+ assert(out && patch);
+ git_buf_sanitize(out);
return git_patch_print(patch, git_diff_print_callback__to_buf, out);
}
diff --git a/src/diff_stats.c b/src/diff_stats.c
index 6ad670c42..42ccbfb87 100644
--- a/src/diff_stats.c
+++ b/src/diff_stats.c
@@ -284,6 +284,8 @@ int git_diff_stats_to_buf(
if (width < STATS_FULL_MIN_SCALE)
width = STATS_FULL_MIN_SCALE;
}
+ if (width > stats->max_filestat)
+ width = 0;
for (i = 0; i < stats->files_changed; ++i) {
if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 97fbc2883..423a0ca33 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -114,7 +114,7 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
if ((dup = diff_delta__dup(a, pool)) == NULL)
return NULL;
- if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
+ if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED || b->status == GIT_DELTA_UNREADABLE)
return dup;
if (dup->status == GIT_DELTA_DELETED) {
@@ -574,14 +574,14 @@ static int similarity_measure(
if (exact_match) {
if (git_oid_iszero(&a_file->id) &&
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(diff->repo, a_file->path,
- a_file->mode, a_file->size, &a_file->id))
+ !git_diff__oid_for_file(&a_file->id,
+ diff, a_file->path, a_file->mode, a_file->size))
a_file->flags |= GIT_DIFF_FLAG_VALID_ID;
if (git_oid_iszero(&b_file->id) &&
diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(diff->repo, b_file->path,
- b_file->mode, b_file->size, &b_file->id))
+ !git_diff__oid_for_file(&b_file->id,
+ diff, b_file->path, b_file->mode, b_file->size))
b_file->flags |= GIT_DIFF_FLAG_VALID_ID;
}
@@ -732,6 +732,7 @@ static bool is_rename_source(
switch (delta->status) {
case GIT_DELTA_ADDED:
case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_IGNORED:
return false;
@@ -786,6 +787,7 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
{
return (delta->status == GIT_DELTA_ADDED ||
delta->status == GIT_DELTA_UNTRACKED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
delta->status == GIT_DELTA_IGNORED);
}
diff --git a/src/errors.c b/src/errors.c
index 393a7875f..7a2600586 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -45,15 +45,19 @@ void giterr_set(int error_class, const char *string, ...)
#endif
int error_code = (error_class == GITERR_OS) ? errno : 0;
- va_start(arglist, string);
- git_buf_vprintf(&buf, string, arglist);
- va_end(arglist);
+ if (string) {
+ va_start(arglist, string);
+ git_buf_vprintf(&buf, string, arglist);
+ va_end(arglist);
+
+ if (error_class == GITERR_OS)
+ git_buf_PUTS(&buf, ": ");
+ }
if (error_class == GITERR_OS) {
#ifdef GIT_WIN32
char * win32_error = git_win32_get_error_message(win32_error_code);
if (win32_error) {
- git_buf_PUTS(&buf, ": ");
git_buf_puts(&buf, win32_error);
git__free(win32_error);
@@ -61,10 +65,8 @@ void giterr_set(int error_class, const char *string, ...)
}
else
#endif
- if (error_code) {
- git_buf_PUTS(&buf, ": ");
+ if (error_code)
git_buf_puts(&buf, strerror(error_code));
- }
if (error_code)
errno = 0;
diff --git a/src/filebuf.c b/src/filebuf.c
index d23bcc11c..25f6e52ef 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -334,8 +334,6 @@ int git_filebuf_commit(git_filebuf *file)
file->fd = -1;
- p_unlink(file->path_original);
-
if (p_rename(file->path_lock, file->path_original) < 0) {
giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
goto on_error;
diff --git a/src/fileops.c b/src/fileops.c
index 13b8f6a39..bd9d27c7a 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -355,8 +355,9 @@ int git_futils_mkdir(
if (p_mkdir(make_path.ptr, mode) < 0) {
int tmp_errno = giterr_system_last();
- /* ignore error if directory already exists */
- if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ /* ignore error if not at end or if directory already exists */
+ if (lastch == '\0' &&
+ (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
giterr_system_set(tmp_errno);
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
goto done;
@@ -374,7 +375,8 @@ int git_futils_mkdir(
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
st.st_mode != mode &&
- (error = p_chmod(make_path.ptr, mode)) < 0) {
+ (error = p_chmod(make_path.ptr, mode)) < 0 &&
+ lastch == '\0') {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
goto done;
}
@@ -503,15 +505,15 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
return error;
}
-static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
+static int futils__rmdir_empty_parent(void *opaque, const char *path)
{
futils__rmdir_data *data = opaque;
int error = 0;
- if (git_buf_len(path) <= data->baselen)
+ if (strlen(path) <= data->baselen)
error = GIT_ITEROVER;
- else if (p_rmdir(git_buf_cstr(path)) < 0) {
+ else if (p_rmdir(path) < 0) {
int en = errno;
if (en == ENOENT || en == ENOTDIR) {
@@ -519,7 +521,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
error = GIT_ITEROVER;
} else {
- error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
+ error = git_path_set_error(errno, path, "rmdir");
}
}
@@ -740,9 +742,11 @@ static int _cp_r_callback(void *ref, git_buf *from)
return error;
/* make symlink or regular file */
- if (S_ISLNK(from_st.st_mode))
+ if (info->flags & GIT_CPDIR_LINK_FILES) {
+ error = p_link(from->ptr, info->to.ptr);
+ } else if (S_ISLNK(from_st.st_mode)) {
error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
- else {
+ } else {
mode_t usemode = from_st.st_mode;
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
diff --git a/src/fileops.h b/src/fileops.h
index 62227abae..4f5700a99 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -173,6 +173,7 @@ extern int git_futils_cp(
* - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
* source file to the target; with this flag, always use 0666 (or 0777 if
* source has exec bits set) for target.
+ * - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
*/
typedef enum {
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
@@ -181,6 +182,7 @@ typedef enum {
GIT_CPDIR_OVERWRITE = (1u << 3),
GIT_CPDIR_CHMOD_DIRS = (1u << 4),
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
+ GIT_CPDIR_LINK_FILES = (1u << 6),
} git_futils_cpdir_flags;
/**
diff --git a/src/filter.c b/src/filter.c
index b2f57964a..b9e4f9ec8 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -23,6 +23,7 @@ struct git_filter_source {
git_oid oid; /* zero if unknown (which is likely) */
uint16_t filemode; /* zero if unknown */
git_filter_mode_t mode;
+ uint32_t options;
};
typedef struct {
@@ -358,6 +359,11 @@ git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
return src->mode;
}
+uint32_t git_filter_source_options(const git_filter_source *src)
+{
+ return src->options;
+}
+
static int filter_list_new(
git_filter_list **out, const git_filter_source *src)
{
@@ -372,6 +378,7 @@ static int filter_list_new(
fl->source.repo = src->repo;
fl->source.path = fl->path;
fl->source.mode = src->mode;
+ fl->source.options = src->options;
*out = fl;
return 0;
@@ -419,12 +426,16 @@ static int filter_list_check_attributes(
}
int git_filter_list_new(
- git_filter_list **out, git_repository *repo, git_filter_mode_t mode)
+ git_filter_list **out,
+ git_repository *repo,
+ git_filter_mode_t mode,
+ uint32_t options)
{
git_filter_source src = { 0 };
src.repo = repo;
src.path = NULL;
src.mode = mode;
+ src.options = options;
return filter_list_new(out, &src);
}
@@ -433,7 +444,8 @@ int git_filter_list_load(
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
- git_filter_mode_t mode)
+ git_filter_mode_t mode,
+ uint32_t options)
{
int error = 0;
git_filter_list *fl = NULL;
@@ -448,6 +460,7 @@ int git_filter_list_load(
src.repo = repo;
src.path = path;
src.mode = mode;
+ src.options = options;
if (blob)
git_oid_cpy(&src.oid, git_blob_id(blob));
@@ -578,6 +591,9 @@ int git_filter_list_apply_to_data(
git_buf *dbuffer[2], local = GIT_BUF_INIT;
unsigned int si = 0;
+ git_buf_sanitize(tgt);
+ git_buf_sanitize(src);
+
if (!fl)
return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size);
@@ -613,7 +629,7 @@ int git_filter_list_apply_to_data(
/* PASSTHROUGH means filter decided not to process the buffer */
error = 0;
} else if (!error) {
- git_buf_shorten(dbuffer[di], 0); /* force NUL termination */
+ git_buf_sanitize(dbuffer[di]); /* force NUL termination */
si = di; /* swap buffers */
} else {
tgt->size = 0;
diff --git a/src/filter.h b/src/filter.h
index d0ace0f9a..5a366108b 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -10,6 +10,10 @@
#include "common.h"
#include "git2/filter.h"
+/* Amount of file to examine for NUL byte when checking binary-ness */
+#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
+
+/* Possible CRLF values */
typedef enum {
GIT_CRLF_GUESS = -1,
GIT_CRLF_BINARY = 0,
diff --git a/src/fnmatch.c b/src/fnmatch.c
index 3846bab3c..d7899e3e6 100644
--- a/src/fnmatch.c
+++ b/src/fnmatch.c
@@ -6,6 +6,40 @@
*/
/*
+ * This file contains code originally derrived from OpenBSD fnmatch.c
+ *
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
@@ -62,6 +96,8 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
flags &= ~FNM_PATHNAME;
while (c == '*')
c = *++pattern;
+ if (c == '/')
+ c = *++pattern;
}
if (*string == '.' && (flags & FNM_PERIOD) &&
diff --git a/src/fnmatch.h b/src/fnmatch.h
index 920e7de4d..88af45939 100644
--- a/src/fnmatch.h
+++ b/src/fnmatch.h
@@ -1,8 +1,29 @@
/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
+ * Copyright (C) 2008 The Android Open Source Project
+ * 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
#ifndef INCLUDE_fnmatch__compat_h__
#define INCLUDE_fnmatch__compat_h__
diff --git a/src/global.c b/src/global.c
index 15baf1eb8..4a0b680ef 100644
--- a/src/global.c
+++ b/src/global.c
@@ -16,6 +16,14 @@ git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+SSL_CTX *git__ssl_ctx;
+# ifdef GIT_THREADS
+static git_mutex *openssl_locks;
+# endif
+#endif
+
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
@@ -23,7 +31,7 @@ static git_atomic git__n_inits;
void git__on_shutdown(git_global_shutdown_fn callback)
{
int count = git_atomic_inc(&git__n_shutdown_callbacks);
- assert(count <= MAX_SHUTDOWN_CB);
+ assert(count <= MAX_SHUTDOWN_CB && count > 0);
git__shutdown_callbacks[count - 1] = callback;
}
@@ -31,10 +39,68 @@ static void git__shutdown(void)
{
int pos;
- while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
- if (git__shutdown_callbacks[pos])
- git__shutdown_callbacks[pos]();
+ for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
+ git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL);
+ if (cb != NULL)
+ cb();
+ }
+
+}
+
+#if defined(GIT_THREADS) && defined(GIT_SSL)
+void openssl_locking_function(int mode, int n, const char *file, int line)
+{
+ int lock;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ lock = mode & CRYPTO_LOCK;
+
+ if (lock) {
+ git_mutex_lock(&openssl_locks[n]);
+ } else {
+ git_mutex_unlock(&openssl_locks[n]);
+ }
+}
+#endif
+
+
+static void init_ssl(void)
+{
+#ifdef GIT_SSL
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+ git__ssl_ctx = SSL_CTX_new(SSLv23_method());
+ SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
+ SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
+ if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ }
+
+# ifdef GIT_THREADS
+ {
+ int num_locks, i;
+
+ num_locks = CRYPTO_num_locks();
+ openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+ if (openssl_locks == NULL) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ }
+
+ for (i = 0; i < num_locks; i++) {
+ if (git_mutex_init(&openssl_locks[i]) != 0) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ }
+ }
+
+ CRYPTO_set_locking_callback(openssl_locking_function);
}
+# endif
+#endif
}
/**
@@ -74,9 +140,9 @@ static void git__shutdown(void)
#if defined(GIT_THREADS) && defined(GIT_WIN32)
static DWORD _tls_index;
-static DWORD _mutex = 0;
+static volatile LONG _mutex = 0;
-static int synchronized_threads_init()
+static int synchronized_threads_init(void)
{
int error;
@@ -110,7 +176,7 @@ int git_threads_init(void)
return error;
}
-static void synchronized_threads_shutdown()
+static void synchronized_threads_shutdown(void)
{
/* Shut down any subsystems that have global state */
git__shutdown();
@@ -157,6 +223,9 @@ int init_error = 0;
static void cb__free_status(void *st)
{
+ git_global_st *state = (git_global_st *) st;
+ git__free(state->error_t.message);
+
git__free(st);
}
@@ -166,10 +235,14 @@ static void init_once(void)
return;
pthread_key_create(&_tls_key, &cb__free_status);
+
/* Initialize any other subsystems that have global state */
if ((init_error = git_hash_global_init()) >= 0)
init_error = git_sysdir_global_init();
+ /* OpenSSL needs to be initialized from the main thread */
+ init_ssl();
+
GIT_MEMORY_BARRIER;
}
@@ -223,6 +296,13 @@ static git_global_st __state;
int git_threads_init(void)
{
+ static int ssl_inited = 0;
+
+ if (!ssl_inited) {
+ init_ssl();
+ ssl_inited = 1;
+ }
+
git_atomic_inc(&git__n_inits);
return 0;
}
diff --git a/src/global.h b/src/global.h
index 778250376..a89a8d6ab 100644
--- a/src/global.h
+++ b/src/global.h
@@ -7,14 +7,21 @@
#ifndef INCLUDE_global_h__
#define INCLUDE_global_h__
+#include "common.h"
#include "mwindow.h"
#include "hash.h"
typedef struct {
git_error *last_error;
git_error error_t;
+ char oid_fmt[GIT_OID_HEXSZ+1];
} git_global_st;
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+extern SSL_CTX *git__ssl_ctx;
+#endif
+
git_global_st *git__global_state(void);
extern git_mutex git__mwindow_mutex;
diff --git a/src/graph.c b/src/graph.c
index 1c264d997..8accd808c 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -124,9 +124,9 @@ static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
(commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2))
continue;
else if (commit->flags & PARENT1)
- (*behind)++;
- else if (commit->flags & PARENT2)
(*ahead)++;
+ else if (commit->flags & PARENT2)
+ (*behind)++;
for (i = 0; i < commit->out_degree; i++) {
git_commit_list_node *p = commit->parents[i];
diff --git a/src/ignore.c b/src/ignore.c
index f373c9482..520bcfe41 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -91,11 +91,11 @@ static int push_ignore_file(
return error;
}
-static int push_one_ignore(void *payload, git_buf *path)
+static int push_one_ignore(void *payload, const char *path)
{
git_ignores *ign = payload;
ign->depth++;
- return push_ignore_file(ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
+ return push_ignore_file(ign, &ign->ign_path, path, GIT_IGNORE_FILE);
}
static int get_internal_ignores(git_attr_file **out, git_repository *repo)
@@ -248,14 +248,15 @@ void git_ignore__free(git_ignores *ignores)
}
static bool ignore_lookup_in_rules(
- git_attr_file *file, git_attr_path *path, int *ignored)
+ int *ignored, git_attr_file *file, git_attr_path *path)
{
size_t j;
git_attr_fnmatch *match;
git_vector_rforeach(&file->rules, j, match) {
if (git_attr_fnmatch__match(match, path)) {
- *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
+ *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ?
+ GIT_IGNORE_TRUE : GIT_IGNORE_FALSE;
return true;
}
}
@@ -264,34 +265,34 @@ static bool ignore_lookup_in_rules(
}
int git_ignore__lookup(
- git_ignores *ignores, const char *pathname, int *ignored)
+ int *out, git_ignores *ignores, const char *pathname)
{
unsigned int i;
git_attr_file *file;
git_attr_path path;
+ *out = GIT_IGNORE_NOTFOUND;
+
if (git_attr_path__init(
&path, pathname, git_repository_workdir(ignores->repo)) < 0)
return -1;
/* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(ignores->ign_internal, &path, ignored))
+ if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(out, file, &path))
goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(out, file, &path))
goto cleanup;
}
- *ignored = 0;
-
cleanup:
git_attr_path__free(&path);
return 0;
@@ -335,8 +336,6 @@ int git_ignore_path_is_ignored(
int error;
const char *workdir;
git_attr_path path;
- char *tail, *end;
- bool full_is_dir;
git_ignores ignores;
unsigned int i;
git_attr_file *file;
@@ -345,55 +344,42 @@ int git_ignore_path_is_ignored(
workdir = repo ? git_repository_workdir(repo) : NULL;
- if ((error = git_attr_path__init(&path, pathname, workdir)) < 0)
- return error;
+ memset(&path, 0, sizeof(path));
+ memset(&ignores, 0, sizeof(ignores));
- tail = path.path;
- end = &path.full.ptr[path.full.size];
- full_is_dir = path.is_dir;
+ if ((error = git_attr_path__init(&path, pathname, workdir)) < 0 ||
+ (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
+ goto cleanup;
while (1) {
- /* advance to next component of path */
- path.basename = tail;
-
- while (tail < end && *tail != '/') tail++;
- *tail = '\0';
-
- path.full.size = (tail - path.full.ptr);
- path.is_dir = (tail == end) ? full_is_dir : true;
-
- /* initialize ignores the first time through */
- if (path.basename == path.path &&
- (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
- break;
-
/* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores.ign_path, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores.ign_global, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup;
}
- /* if we found no rules before reaching the end, we're done */
- if (tail == end)
+ /* move up one directory */
+ if (path.basename == path.path)
break;
-
- /* now add this directory to list of ignores */
- if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
+ path.basename[-1] = '\0';
+ while (path.basename > path.path && *path.basename != '/')
+ path.basename--;
+ if (path.basename > path.path)
+ path.basename++;
+ path.is_dir = 1;
+
+ if ((error = git_ignore__pop_dir(&ignores)) < 0)
break;
-
- /* reinstate divider in path */
- *tail = '/';
- while (*tail == '/') tail++;
}
*ignored = 0;
@@ -404,7 +390,6 @@ cleanup:
return error;
}
-
int git_ignore__check_pathspec_for_exact_ignores(
git_repository *repo,
git_vector *vspec,
diff --git a/src/ignore.h b/src/ignore.h
index ff9369000..77668c661 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -42,7 +42,14 @@ extern int git_ignore__pop_dir(git_ignores *ign);
extern void git_ignore__free(git_ignores *ign);
-extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
+enum {
+ GIT_IGNORE_UNCHECKED = -2,
+ GIT_IGNORE_NOTFOUND = -1,
+ GIT_IGNORE_FALSE = 0,
+ GIT_IGNORE_TRUE = 1,
+};
+
+extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path);
/* command line Git sometimes generates an error message if given a
* pathspec that contains an exact match to an ignored file (provided
diff --git a/src/index.c b/src/index.c
index c044af402..8b757f269 100644
--- a/src/index.c
+++ b/src/index.c
@@ -842,7 +842,7 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
{
- char *tgt_path = tgt->path;
+ const char *tgt_path = tgt->path;
memcpy(tgt, src, sizeof(*tgt));
tgt->path = tgt_path; /* reset to existing path data */
}
@@ -1104,6 +1104,15 @@ int git_index_remove_bypath(git_index *index, const char *path)
return 0;
}
+static bool valid_filemode(const int filemode)
+{
+ return (filemode == GIT_FILEMODE_BLOB ||
+ filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
+ filemode == GIT_FILEMODE_LINK ||
+ filemode == GIT_FILEMODE_COMMIT);
+}
+
+
int git_index_add(git_index *index, const git_index_entry *source_entry)
{
git_index_entry *entry = NULL;
@@ -1111,6 +1120,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
assert(index && source_entry && source_entry->path);
+ if (!valid_filemode(source_entry->mode)) {
+ giterr_set(GITERR_INDEX, "invalid filemode");
+ return -1;
+ }
+
if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0)
return ret;
@@ -1753,35 +1767,42 @@ static size_t read_entry(
git_index_entry **out, const void *buffer, size_t buffer_size)
{
size_t path_length, entry_size;
- uint16_t flags_raw;
const char *path_ptr;
- const struct entry_short *source = buffer;
+ struct entry_short source;
git_index_entry entry = {{0}};
if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
return 0;
- entry.ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
- entry.ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
- entry.mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
- entry.mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
- entry.dev = ntohl(source->dev);
- entry.ino = ntohl(source->ino);
- entry.mode = ntohl(source->mode);
- entry.uid = ntohl(source->uid);
- entry.gid = ntohl(source->gid);
- entry.file_size = ntohl(source->file_size);
- git_oid_cpy(&entry.id, &source->oid);
- entry.flags = ntohs(source->flags);
+ /* buffer is not guaranteed to be aligned */
+ memcpy(&source, buffer, sizeof(struct entry_short));
+
+ entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds);
+ entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds);
+ entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds);
+ entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds);
+ entry.dev = ntohl(source.dev);
+ entry.ino = ntohl(source.ino);
+ entry.mode = ntohl(source.mode);
+ entry.uid = ntohl(source.uid);
+ entry.gid = ntohl(source.gid);
+ entry.file_size = ntohl(source.file_size);
+ git_oid_cpy(&entry.id, &source.oid);
+ entry.flags = ntohs(source.flags);
if (entry.flags & GIT_IDXENTRY_EXTENDED) {
- const struct entry_long *source_l = (const struct entry_long *)source;
- path_ptr = source_l->path;
+ uint16_t flags_raw;
+ size_t flags_offset;
- flags_raw = ntohs(source_l->flags_extended);
- memcpy(&entry.flags_extended, &flags_raw, 2);
+ flags_offset = offsetof(struct entry_long, flags_extended);
+ memcpy(&flags_raw, (const char *) buffer + flags_offset,
+ sizeof(flags_raw));
+ flags_raw = ntohs(flags_raw);
+
+ memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw));
+ path_ptr = (const char *) buffer + offsetof(struct entry_long, path);
} else
- path_ptr = source->path;
+ path_ptr = (const char *) buffer + offsetof(struct entry_short, path);
path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
@@ -1832,14 +1853,12 @@ static int read_header(struct index_header *dest, const void *buffer)
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
{
- const struct index_extension *source;
struct index_extension dest;
size_t total_size;
- source = (const struct index_extension *)(buffer);
-
- memcpy(dest.signature, source->signature, 4);
- dest.extension_size = ntohl(source->extension_size);
+ /* buffer is not guaranteed to be aligned */
+ memcpy(&dest, buffer, sizeof(struct index_extension));
+ dest.extension_size = ntohl(dest.extension_size);
total_size = dest.extension_size + sizeof(struct index_extension);
@@ -2282,9 +2301,7 @@ static int read_tree_cb(
entry->mode == old_entry->mode &&
git_oid_equal(&entry->id, &old_entry->id))
{
- char *oldpath = entry->path;
- memcpy(entry, old_entry, sizeof(*entry));
- entry->path = oldpath;
+ index_entry_cpy(entry, old_entry);
entry->flags_extended = 0;
}
diff --git a/src/indexer.c b/src/indexer.c
index adf5ceaa7..010de329b 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,8 @@
#include "oidmap.h"
#include "zstream.h"
+extern git_mutex git__mwindow_mutex;
+
#define UINT31_MAX (0x7FFFFFFF)
struct entry {
@@ -34,7 +36,6 @@ struct git_indexer {
have_delta :1;
struct git_pack_header hdr;
struct git_pack_file *pack;
- git_filebuf pack_file;
unsigned int mode;
git_off_t off;
git_off_t entry_start;
@@ -67,33 +68,18 @@ const git_oid *git_indexer_hash(const git_indexer *idx)
return &idx->hash;
}
-static int open_pack(struct git_pack_file **out, const char *filename)
-{
- struct git_pack_file *pack;
-
- if (git_packfile_alloc(&pack, filename) < 0)
- return -1;
-
- if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
- giterr_set(GITERR_OS, "Failed to open packfile.");
- git_packfile_free(pack);
- return -1;
- }
-
- *out = pack;
- return 0;
-}
-
static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
{
int error;
+ git_map map;
- /* Verify we recognize this pack file format. */
- if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
- giterr_set(GITERR_OS, "Failed to read in pack header");
+ if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
return error;
- }
+ memcpy(hdr, map.data, sizeof(*hdr));
+ p_munmap(&map);
+
+ /* Verify we recognize this pack file format. */
if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
giterr_set(GITERR_INDEXER, "Wrong pack signature");
return -1;
@@ -124,9 +110,9 @@ int git_indexer_new(
void *progress_payload)
{
git_indexer *idx;
- git_buf path = GIT_BUF_INIT;
+ git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT;
static const char suff[] = "/pack";
- int error;
+ int error, fd = -1;
idx = git__calloc(1, sizeof(git_indexer));
GITERR_CHECK_ALLOC(idx);
@@ -140,19 +126,30 @@ int git_indexer_new(
if (error < 0)
goto cleanup;
- error = git_filebuf_open(&idx->pack_file, path.ptr,
- GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
- idx->mode);
+ fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
git_buf_free(&path);
+ if (fd < 0)
+ goto cleanup;
+
+ error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path));
+ git_buf_free(&tmp_path);
+
if (error < 0)
goto cleanup;
+ idx->pack->mwf.fd = fd;
+ if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
+ goto cleanup;
+
*out = idx;
return 0;
cleanup:
+ if (fd != -1)
+ p_close(fd);
+
git_buf_free(&path);
- git_filebuf_cleanup(&idx->pack_file);
+ git_buf_free(&tmp_path);
git__free(idx);
return -1;
}
@@ -429,6 +426,47 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
idx->inbuf_len += size - to_expell;
}
+static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
+{
+ git_file fd = idx->pack->mwf.fd;
+ long page_size = git__page_size();
+ git_off_t page_start, page_offset;
+ unsigned char *map_data;
+ git_map map;
+ int error;
+
+ assert(data && size);
+
+ /* the offset needs to be at the beginning of the a page boundary */
+ page_start = (offset / page_size) * page_size;
+ page_offset = offset - page_start;
+
+ if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
+ return error;
+
+ map_data = (unsigned char *)map.data;
+ memcpy(map_data + page_offset, data, size);
+ p_munmap(&map);
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ git_off_t current_size = idx->pack->mwf.size;
+
+ if (!size)
+ return 0;
+
+ /* add the extra space we need at the end */
+ if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) {
+ giterr_set(GITERR_OS, "Failed to increase size of pack file '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
+ return write_at(idx, data, idx->pack->mwf.size, size);
+}
+
int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error = -1;
@@ -440,22 +478,13 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran
processed = stats->indexed_objects;
- if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0)
+ if ((error = append_to_pack(idx, data, size)) < 0)
return error;
hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */
- if (idx->opened_pack) {
- idx->pack->mwf.size += size;
- } else {
- if ((error = open_pack(&idx->pack, idx->pack_file.path_lock)) < 0)
- return error;
- idx->opened_pack = 1;
- mwf = &idx->pack->mwf;
- if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
- return error;
- }
+ idx->pack->mwf.size += size;
if (!idx->parsed_header) {
unsigned int total_objects;
@@ -616,17 +645,10 @@ static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
* Rewind the packfile by the trailer, as we might need to fix the
* packfile by injecting objects at the tail and must overwrite it.
*/
-static git_off_t seek_back_trailer(git_indexer *idx)
+static void seek_back_trailer(git_indexer *idx)
{
- git_off_t off;
-
- if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
- return -1;
-
idx->pack->mwf.size -= GIT_OID_RAWSZ;
git_mwindow_free_all(&idx->pack->mwf);
-
- return off;
}
static int inject_object(git_indexer *idx, git_oid *id)
@@ -642,7 +664,8 @@ static int inject_object(git_indexer *idx, git_oid *id)
size_t len, hdr_len;
int error;
- entry_start = seek_back_trailer(idx);
+ seek_back_trailer(idx);
+ entry_start = idx->pack->mwf.size;
if (git_odb_read(&obj, idx->odb, id) < 0)
return -1;
@@ -657,7 +680,9 @@ static int inject_object(git_indexer *idx, git_oid *id)
/* Write out the object header */
hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
- git_filebuf_write(&idx->pack_file, hdr, hdr_len);
+ if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
+ goto cleanup;
+
idx->pack->mwf.size += hdr_len;
entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
@@ -665,13 +690,16 @@ static int inject_object(git_indexer *idx, git_oid *id)
goto cleanup;
/* And then the compressed object */
- git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
+ if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
+ goto cleanup;
+
idx->pack->mwf.size += buf.size;
entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
git_buf_free(&buf);
/* Write a fake trailer so the pack functions play ball */
- if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
+
+ if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0)
goto cleanup;
idx->pack->mwf.size += GIT_OID_RAWSZ;
@@ -703,7 +731,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
size_t size;
git_otype type;
git_mwindow *w = NULL;
- git_off_t curpos;
+ git_off_t curpos = 0;
unsigned char *base_info;
unsigned int left = 0;
git_oid base;
@@ -717,6 +745,9 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
/* Loop until we find the first REF delta */
git_vector_foreach(&idx->deltas, i, delta) {
+ if (!delta)
+ continue;
+
curpos = delta->delta_off;
error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
git_mwindow_close(&w);
@@ -756,13 +787,18 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
{
unsigned int i;
struct delta_info *delta;
- int progressed = 0, progress_cb_result;
+ int progressed = 0, non_null = 0, progress_cb_result;
while (idx->deltas.length > 0) {
progressed = 0;
+ non_null = 0;
git_vector_foreach(&idx->deltas, i, delta) {
git_rawobj obj;
+ if (!delta)
+ continue;
+
+ non_null = 1;
idx->off = delta->delta_off;
if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
continue;
@@ -777,16 +813,15 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
if ((progress_cb_result = do_progress_callback(idx, stats)) < 0)
return progress_cb_result;
- /*
- * Remove this delta from the list and
- * decrease i so we don't skip over the next
- * delta.
- */
- git_vector_remove(&idx->deltas, i);
+ /* remove from the list */
+ git_vector_set(NULL, &idx->deltas, i, NULL);
git__free(delta);
- i--;
}
+ /* if none were actually set, we're done */
+ if (!non_null)
+ break;
+
if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
giterr_set(GITERR_INDEXER, "missing delta bases");
return -1;
@@ -810,19 +845,12 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
ctx = &idx->trailer;
git_hash_ctx_init(ctx);
- git_mwindow_free_all(mwf);
+
/* Update the header to include the numer of local objects we injected */
idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
- if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
- giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
- return -1;
- }
-
- if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
- giterr_set(GITERR_OS, "failed to update the pack header");
+ if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
return -1;
- }
/*
* We now use the same technique as before to determine the
@@ -830,6 +858,7 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
* hash_partially() keep the existing trailer out of the
* calculation.
*/
+ git_mwindow_free_all(mwf);
idx->inbuf_len = 0;
while (hashed < mwf->size) {
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
@@ -899,13 +928,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
return -1;
git_hash_final(&trailer_hash, &idx->trailer);
- if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
- return -1;
-
- if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
- giterr_set(GITERR_OS, "failed to update pack trailer");
- return -1;
- }
+ write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
}
git_vector_sort(&idx->objects);
@@ -988,14 +1011,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
git_mwindow_free_all(&idx->pack->mwf);
/* We need to close the descriptor here so Windows doesn't choke on commit_at */
- p_close(idx->pack->mwf.fd);
+ if (p_close(idx->pack->mwf.fd) < 0) {
+ giterr_set(GITERR_OS, "failed to close packfile");
+ goto on_error;
+ }
+
idx->pack->mwf.fd = -1;
if (index_path(&filename, idx, ".pack") < 0)
goto on_error;
+
/* And don't forget to rename the packfile to its new place. */
- if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0)
- return -1;
+ p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
git_buf_free(&filename);
return 0;
@@ -1015,7 +1042,7 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->objects);
- if (idx->pack) {
+ if (idx->pack && idx->pack->idx_cache) {
struct git_pack_entry *pentry;
kh_foreach_value(
idx->pack->idx_cache, pentry, { git__free(pentry); });
@@ -1024,7 +1051,11 @@ void git_indexer_free(git_indexer *idx)
}
git_vector_free_deep(&idx->deltas);
- git_packfile_free(idx->pack);
- git_filebuf_cleanup(&idx->pack_file);
+
+ if (!git_mutex_lock(&git__mwindow_mutex)) {
+ git_packfile_free(idx->pack);
+ git_mutex_unlock(&git__mwindow_mutex);
+ }
+
git__free(idx);
}
diff --git a/src/iterator.c b/src/iterator.c
index ef27fa71f..c664f17cd 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -897,6 +897,7 @@ struct fs_iterator_frame {
fs_iterator_frame *next;
git_vector entries;
size_t index;
+ int is_ignored;
};
typedef struct fs_iterator fs_iterator;
@@ -1016,6 +1017,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
fs_iterator__free_frame(ff);
return GIT_ENOTFOUND;
}
+ fi->base.stat_calls += ff->entries.length;
fs_iterator__seek_frame_start(fi, ff);
@@ -1289,24 +1291,37 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
static int workdir_iterator__enter_dir(fs_iterator *fi)
{
+ workdir_iterator *wi = (workdir_iterator *)fi;
fs_iterator_frame *ff = fi->stack;
size_t pos;
git_path_with_stat *entry;
bool found_submodules = false;
- /* only push new ignores if this is not top level directory */
+ /* check if this directory is ignored */
+ if (git_ignore__lookup(
+ &ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len) < 0) {
+ giterr_clear();
+ ff->is_ignored = GIT_IGNORE_NOTFOUND;
+ }
+
+ /* if this is not the top level directory... */
if (ff->next != NULL) {
- workdir_iterator *wi = (workdir_iterator *)fi;
ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
+ /* inherit ignored from parent if no rule specified */
+ if (ff->is_ignored <= GIT_IGNORE_NOTFOUND)
+ ff->is_ignored = ff->next->is_ignored;
+
+ /* push new ignores for files in this directory */
(void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
}
/* convert submodules to GITLINK and remove trailing slashes */
git_vector_foreach(&ff->entries, pos, entry) {
- if (S_ISDIR(entry->st.st_mode) &&
- git_submodule__is_submodule(fi->base.repo, entry->path))
- {
+ if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
+ continue;
+
+ if (git_submodule__is_submodule(fi->base.repo, entry->path)) {
entry->st.st_mode = GIT_FILEMODE_COMMIT;
entry->path_len--;
entry->path[entry->path_len] = '\0';
@@ -1340,7 +1355,7 @@ static int workdir_iterator__update_entry(fs_iterator *fi)
return GIT_ENOTFOUND;
/* reset is_ignored since we haven't checked yet */
- wi->is_ignored = -1;
+ wi->is_ignored = GIT_IGNORE_UNCHECKED;
return 0;
}
@@ -1485,6 +1500,19 @@ int git_iterator_current_parent_tree(
return 0;
}
+static void workdir_iterator_update_is_ignored(workdir_iterator *wi)
+{
+ if (git_ignore__lookup(
+ &wi->is_ignored, &wi->ignores, wi->fi.entry.path) < 0) {
+ giterr_clear();
+ wi->is_ignored = GIT_IGNORE_NOTFOUND;
+ }
+
+ /* use ignore from containing frame stack */
+ if (wi->is_ignored <= GIT_IGNORE_NOTFOUND)
+ wi->is_ignored = wi->fi.stack->is_ignored;
+}
+
bool git_iterator_current_is_ignored(git_iterator *iter)
{
workdir_iterator *wi = (workdir_iterator *)iter;
@@ -1492,14 +1520,22 @@ bool git_iterator_current_is_ignored(git_iterator *iter)
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
return false;
- if (wi->is_ignored != -1)
- return (bool)(wi->is_ignored != 0);
+ if (wi->is_ignored != GIT_IGNORE_UNCHECKED)
+ return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
- if (git_ignore__lookup(
- &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
- wi->is_ignored = true;
+ workdir_iterator_update_is_ignored(wi);
+
+ return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
+}
+
+bool git_iterator_current_tree_is_ignored(git_iterator *iter)
+{
+ workdir_iterator *wi = (workdir_iterator *)iter;
+
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
+ return false;
- return (bool)wi->is_ignored;
+ return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
}
int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
@@ -1547,10 +1583,8 @@ int git_iterator_advance_over_with_status(
return error;
if (!S_ISDIR(entry->mode)) {
- if (git_ignore__lookup(
- &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
- wi->is_ignored = true;
- if (wi->is_ignored)
+ workdir_iterator_update_is_ignored(wi);
+ if (wi->is_ignored == GIT_IGNORE_TRUE)
*status = GIT_ITERATOR_STATUS_IGNORED;
return git_iterator_advance(entryptr, iter);
}
@@ -1562,14 +1596,12 @@ int git_iterator_advance_over_with_status(
/* scan inside directory looking for a non-ignored item */
while (entry && !iter->prefixcomp(entry->path, base)) {
- if (git_ignore__lookup(
- &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
- wi->is_ignored = true;
+ workdir_iterator_update_is_ignored(wi);
/* if we found an explicitly ignored item, then update from
* EMPTY to IGNORED
*/
- if (wi->is_ignored)
+ if (wi->is_ignored == GIT_IGNORE_TRUE)
*status = GIT_ITERATOR_STATUS_IGNORED;
else if (S_ISDIR(entry->mode)) {
error = git_iterator_advance_into(&entry, iter);
@@ -1578,7 +1610,7 @@ int git_iterator_advance_over_with_status(
continue;
else if (error == GIT_ENOTFOUND) {
error = 0;
- wi->is_ignored = true; /* mark empty directories as ignored */
+ wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
} else
break; /* real error, stop here */
} else {
diff --git a/src/iterator.h b/src/iterator.h
index ba9c1e486..d88ad5191 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -52,6 +52,7 @@ struct git_iterator {
char *start;
char *end;
int (*prefixcomp)(const char *str, const char *prefix);
+ size_t stat_calls;
unsigned int flags;
};
@@ -244,6 +245,8 @@ extern int git_iterator_current_parent_tree(
extern bool git_iterator_current_is_ignored(git_iterator *iter);
+extern bool git_iterator_current_tree_is_ignored(git_iterator *iter);
+
extern int git_iterator_cmp(
git_iterator *iter, const char *path_prefix);
diff --git a/src/map.h b/src/map.h
index da3d1e19a..722eb7a30 100644
--- a/src/map.h
+++ b/src/map.h
@@ -42,5 +42,6 @@ typedef struct { /* memory mapped buffer */
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map);
+extern long git__page_size(void);
#endif /* INCLUDE_map_h__ */
diff --git a/src/merge.c b/src/merge.c
index 69c42bc0c..ddeea8752 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -27,6 +27,7 @@
#include "index.h"
#include "filebuf.h"
#include "config.h"
+#include "oidarray.h"
#include "git2/types.h"
#include "git2/repository.h"
@@ -39,6 +40,7 @@
#include "git2/signature.h"
#include "git2/config.h"
#include "git2/tree.h"
+#include "git2/oidarray.h"
#include "git2/sys/index.h"
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
@@ -139,7 +141,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co
return 0;
}
-int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two)
{
git_revwalk *walk;
git_vector list;
@@ -173,13 +175,63 @@ int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const
return GIT_ENOTFOUND;
}
+ *out = result;
+ *walk_out = walk;
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
+
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result;
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
git_oid_cpy(out, &result->item->oid);
git_commit_list_free(&result);
git_revwalk_free(walk);
return 0;
+}
+
+int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result, *list;
+ git_array_oid_t array;
+
+ git_array_init(array);
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
+ list = result;
+ while (list) {
+ git_oid *id = git_array_alloc(array);
+ if (id == NULL)
+ goto on_error;
+
+ git_oid_cpy(id, &list->item->oid);
+ list = list->next;
+ }
+
+ git_oidarray__from_array(out, &array);
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
on_error:
+ git_commit_list_free(&result);
git_revwalk_free(walk);
return -1;
}
@@ -228,8 +280,11 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
return -1;
git_vector_foreach(twos, i, two) {
- git_commit_list_parse(walk, two);
+ if (git_commit_list_parse(walk, two) < 0)
+ return -1;
+
two->flags |= PARENT2;
+
if (git_pqueue_insert(&list, two) < 0)
return -1;
}
@@ -2193,8 +2248,7 @@ static int merge_normalize_checkout_opts(
memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options));
else {
git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
- default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_ALLOW_CONFLICTS;
+ default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options));
}
@@ -2226,64 +2280,6 @@ static int merge_normalize_checkout_opts(
return error;
}
-static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new)
-{
- git_tree *head_tree = NULL;
- git_iterator *iter_head = NULL, *iter_new = NULL;
- git_diff *merged_list = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_delta *delta;
- size_t i;
- const git_index_entry *e;
- char *path;
- int error = 0;
-
- if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
- (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
- (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
- (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
- goto done;
-
- git_vector_foreach(&merged_list->deltas, i, delta) {
- path = git__strdup(delta->new_file.path);
- GITERR_CHECK_ALLOC(path);
-
- if ((error = git_vector_insert(paths, path)) < 0)
- goto on_error;
- }
-
- for (i = 0; i < git_index_entrycount(index_new); i++) {
- e = git_index_get_byindex(index_new, i);
-
- if (git_index_entry_stage(e) != 0 &&
- (git_vector_last(paths) == NULL ||
- strcmp(git_vector_last(paths), e->path) != 0)) {
-
- path = git__strdup(e->path);
- GITERR_CHECK_ALLOC(path);
-
- if ((error = git_vector_insert(paths, path)) < 0)
- goto on_error;
- }
- }
-
- goto done;
-
-on_error:
- git_vector_foreach(paths, i, path)
- git__free(path);
-
- git_vector_clear(paths);
-
-done:
- git_tree_free(head_tree);
- git_iterator_free(iter_head);
- git_iterator_free(iter_new);
- git_diff_free(merged_list);
-
- return error;
-}
-
static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
{
git_tree *head_tree = NULL;
@@ -2342,7 +2338,6 @@ done:
static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
{
- git_index *index_repo = NULL;
git_diff *wd_diff_list = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
int error = 0;
@@ -2351,6 +2346,16 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde
*conflicts = 0;
+ /* We need to have merged at least 1 file for the possibility to exist to
+ * have conflicts with the workdir. Passing 0 as the pathspec count paramter
+ * will consider all files in the working directory, that is, we may detect
+ * a conflict if there were untracked files in the workdir prior to starting
+ * the merge. This typically happens when cherry-picking a commmit whose
+ * changes have already been applied.
+ */
+ if (merged_paths->length == 0)
+ return 0;
+
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* Workdir changes may exist iff they do not conflict with changes that
@@ -2360,111 +2365,69 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde
opts.pathspec.count = merged_paths->length;
opts.pathspec.strings = (char **)merged_paths->contents;
- if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, index_repo, &opts)) < 0)
+ if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0)
goto done;
*conflicts = wd_diff_list->deltas.length;
done:
- git_index_free(index_repo);
git_diff_free(wd_diff_list);
return error;
}
-int git_merge__indexes(git_repository *repo, git_index *index_new)
+int git_merge__check_result(git_repository *repo, git_index *index_new)
{
- git_index *index_repo = NULL;
- int index_repo_caps = 0;
+ git_tree *head_tree = NULL;
+ git_iterator *iter_head = NULL, *iter_new = NULL;
+ git_diff *merged_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_delta *delta;
git_vector paths = GIT_VECTOR_INIT;
- size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i;
- char *path;
+ size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts;
const git_index_entry *e;
- const git_index_name_entry *name;
- const git_index_reuc_entry *reuc;
int error = 0;
- if ((error = git_repository_index(&index_repo, repo)) < 0)
- goto done;
-
- /* Set the index to case sensitive to handle the merge */
- index_repo_caps = git_index_caps(index_repo);
-
- if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0)
- goto done;
-
- /* Make sure the index and workdir state do not prevent merging */
- if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 ||
- (error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
- (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
- goto done;
-
- if ((conflicts = index_conflicts + wd_conflicts) > 0) {
- giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
- conflicts, (conflicts != 1) ? "s" : "");
- error = GIT_EMERGECONFLICT;
-
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
goto done;
- }
-
- /* Remove removed items from the index */
- git_vector_foreach(&paths, i, path) {
- if (git_index_get_bypath(index_new, path, 0) == NULL) {
- if ((error = git_index_remove(index_repo, path, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto done;
- }
- }
- /* Add updated items to the index */
- git_vector_foreach(&paths, i, path) {
- if ((e = git_index_get_bypath(index_new, path, 0)) != NULL) {
- if ((error = git_index_add(index_repo, e)) < 0)
- goto done;
- }
+ git_vector_foreach(&merged_list->deltas, i, delta) {
+ if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0)
+ goto done;
}
- /* Add conflicts */
- git_index_conflict_cleanup(index_repo);
-
for (i = 0; i < git_index_entrycount(index_new); i++) {
e = git_index_get_byindex(index_new, i);
if (git_index_entry_stage(e) != 0 &&
- (error = git_index_add(index_repo, e)) < 0)
- goto done;
- }
+ (git_vector_last(&paths) == NULL ||
+ strcmp(git_vector_last(&paths), e->path) != 0)) {
- /* Add name entries */
- git_index_name_clear(index_repo);
-
- for (i = 0; i < git_index_name_entrycount(index_new); i++) {
- name = git_index_name_get_byindex(index_new, i);
-
- if ((error = git_index_name_add(index_repo,
- name->ancestor, name->ours, name->theirs)) < 0)
- goto done;
+ if ((error = git_vector_insert(&paths, (char *)e->path)) < 0)
+ goto done;
+ }
}
- /* Add the reuc */
- git_index_reuc_clear(index_repo);
-
- for (i = 0; i < git_index_reuc_entrycount(index_new); i++) {
- reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i);
+ /* Make sure the index and workdir state do not prevent merging */
+ if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
+ (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
+ goto done;
- if ((error = git_index_reuc_add(index_repo, reuc->path,
- reuc->mode[0], &reuc->oid[0],
- reuc->mode[1], &reuc->oid[1],
- reuc->mode[2], &reuc->oid[2])) < 0)
- goto done;
+ if ((conflicts = index_conflicts + wd_conflicts) > 0) {
+ giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+ conflicts, (conflicts != 1) ? "s" : "");
+ error = GIT_EMERGECONFLICT;
}
done:
- if (index_repo != NULL)
- git_index_set_caps(index_repo, index_repo_caps);
-
- git_index_free(index_repo);
- git_vector_free_deep(&paths);
+ git_vector_free(&paths);
+ git_tree_free(head_tree);
+ git_iterator_free(iter_head);
+ git_iterator_free(iter_new);
+ git_diff_free(merged_list);
return error;
}
@@ -2479,12 +2442,14 @@ int git_merge__append_conflicts_to_merge_msg(
size_t i;
int error;
+ if (!git_index_has_conflicts(index))
+ return 0;
+
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
goto cleanup;
- if (git_index_has_conflicts(index))
- git_filebuf_printf(&file, "\nConflicts:\n");
+ git_filebuf_printf(&file, "\nConflicts:\n");
for (i = 0; i < git_index_entrycount(index); i++) {
const git_index_entry *e = git_index_get_byindex(index, i);
@@ -2509,7 +2474,6 @@ cleanup:
return error;
}
-
static int merge_state_cleanup(git_repository *repo)
{
const char *state_files[] = {
@@ -2564,8 +2528,42 @@ done:
return error;
}
+static int merge_preference(git_merge_preference_t *out, git_repository *repo)
+{
+ git_config *config;
+ const char *value;
+ int bool_value, error = 0;
+
+ *out = GIT_MERGE_PREFERENCE_NONE;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if (git_config_parse_bool(&bool_value, value) == 0) {
+ if (!bool_value)
+ *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
+ } else {
+ if (strcasecmp(value, "only") == 0)
+ *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
+ }
+
+done:
+ git_config_free(config);
+ return error;
+}
+
int git_merge_analysis(
- git_merge_analysis_t *out,
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
git_repository *repo,
const git_merge_head **their_heads,
size_t their_heads_len)
@@ -2573,14 +2571,7 @@ int git_merge_analysis(
git_merge_head *ancestor_head = NULL, *our_head = NULL;
int error = 0;
- assert(out && repo && their_heads);
-
- *out = GIT_MERGE_ANALYSIS_NONE;
-
- if (git_repository_head_unborn(repo)) {
- *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
- goto done;
- }
+ assert(analysis_out && preference_out && repo && their_heads);
if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "Can only merge a single branch");
@@ -2588,20 +2579,30 @@ int git_merge_analysis(
goto done;
}
+ *analysis_out = GIT_MERGE_ANALYSIS_NONE;
+
+ if ((error = merge_preference(preference_out, repo)) < 0)
+ goto done;
+
+ if (git_repository_head_unborn(repo)) {
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
+ goto done;
+ }
+
if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
goto done;
/* We're up-to-date if we're trying to merge our own common ancestor. */
if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid))
- *out = GIT_MERGE_ANALYSIS_UP_TO_DATE;
+ *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
/* We're fastforwardable if we're our own common ancestor. */
else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid))
- *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
/* Otherwise, just a normal merge is possible. */
else
- *out = GIT_MERGE_ANALYSIS_NORMAL;
+ *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
done:
git_merge_head_free(ancestor_head);
@@ -2620,7 +2621,7 @@ int git_merge(
git_checkout_options checkout_opts;
git_merge_head *ancestor_head = NULL, *our_head = NULL;
git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
size_t i;
int error = 0;
@@ -2660,10 +2661,9 @@ int git_merge(
/* TODO: recursive, octopus, etc... */
if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &checkout_opts)) < 0)
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &checkout_opts)) < 0)
goto on_error;
goto done;
@@ -2673,7 +2673,6 @@ on_error:
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_tree_free(ancestor_tree);
git_tree_free(our_tree);
@@ -2803,38 +2802,24 @@ void git_merge_head_free(git_merge_head *head)
git__free(head);
}
-int git_merge_init_options(git_merge_options *opts, int version)
+int git_merge_init_options(git_merge_options *opts, unsigned int version)
{
- if (version != GIT_MERGE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_options", version);
- return -1;
- } else {
- git_merge_options default_opts = GIT_MERGE_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_merge_options));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT);
+ return 0;
}
-int git_merge_file_init_input(git_merge_file_input *input, int version)
+int git_merge_file_init_input(git_merge_file_input *input, unsigned int version)
{
- if (version != GIT_MERGE_FILE_INPUT_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_input", version);
- return -1;
- } else {
- git_merge_file_input i = GIT_MERGE_FILE_INPUT_INIT;
- memcpy(input, &i, sizeof(i));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT);
+ return 0;
}
-int git_merge_file_init_options(git_merge_file_options *opts, int version)
+int git_merge_file_init_options(
+ git_merge_file_options *opts, unsigned int version)
{
- if (version != GIT_MERGE_FILE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_options", version);
- return -1;
- } else {
- git_merge_file_options o = GIT_MERGE_FILE_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/merge.h b/src/merge.h
index 00f6197bf..cc17389ab 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -149,7 +149,7 @@ int git_merge__setup(
const git_merge_head *heads[],
size_t heads_len);
-int git_merge__indexes(git_repository *repo, git_index *index_new);
+int git_merge__check_result(git_repository *repo, git_index *index_new);
int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index);
diff --git a/src/message.c b/src/message.c
index 07b2569ad..6c5a2379f 100644
--- a/src/message.c
+++ b/src/message.c
@@ -21,7 +21,7 @@ static size_t line_length_without_trailing_spaces(const char *line, size_t len)
/* Greatly inspired from git.git "stripspace" */
/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
-int git_message_prettify(git_buf *message_out, const char *message, int strip_comments)
+int git_message_prettify(git_buf *message_out, const char *message, int strip_comments, char comment_char)
{
const size_t message_len = strlen(message);
@@ -40,7 +40,7 @@ int git_message_prettify(git_buf *message_out, const char *message, int strip_co
line_length = message_len - i;
}
- if (strip_comments && line_length && message[i] == '#')
+ if (strip_comments && line_length && message[i] == comment_char)
continue;
rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
diff --git a/src/mwindow.c b/src/mwindow.c
index 7e5fcdfbc..1d64d26a4 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -11,6 +11,10 @@
#include "fileops.h"
#include "map.h"
#include "global.h"
+#include "strmap.h"
+#include "pack.h"
+
+GIT__USE_STRMAP;
#define DEFAULT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
@@ -26,20 +30,127 @@ size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
/* Whenever you want to read or modify this, grab git__mwindow_mutex */
static git_mwindow_ctl mem_ctl;
-/*
- * Free all the windows in a sequence, typically because we're done
- * with the file
+/* Global list of mwindow files, to open packs once across repos */
+git_strmap *git__pack_cache = NULL;
+
+/**
+ * Run under mwindow lock
*/
-void git_mwindow_free_all(git_mwindow_file *mwf)
+int git_mwindow_files_init(void)
{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t i;
+ if (git__pack_cache)
+ return 0;
+
+ return git_strmap_alloc(&git__pack_cache);
+}
+
+void git_mwindow_files_free(void)
+{
+ git_strmap *tmp = git__pack_cache;
+
+ git__pack_cache = NULL;
+ git_strmap_free(tmp);
+}
+
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
+{
+ int error;
+ char *packname;
+ git_strmap_iter pos;
+ struct git_pack_file *pack;
+
+ if ((error = git_packfile__name(&packname, path)) < 0)
+ return error;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0) {
+ giterr_set(GITERR_OS, "failed to lock mwindow mutex");
+ return -1;
+ }
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ git__free(packname);
+ return -1;
+ }
+
+ pos = git_strmap_lookup_index(git__pack_cache, packname);
+ git__free(packname);
+
+ if (git_strmap_valid_index(git__pack_cache, pos)) {
+ pack = git_strmap_value_at(git__pack_cache, pos);
+ git_atomic_inc(&pack->refcount);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ *out = pack;
+ return 0;
+ }
+
+ /* If we didn't find it, we need to create it */
+ if ((error = git_packfile_alloc(&pack, path)) < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return error;
+ }
+
+ git_atomic_inc(&pack->refcount);
+
+ git_strmap_insert(git__pack_cache, pack->pack_name, pack, error);
+ git_mutex_unlock(&git__mwindow_mutex);
+
+ if (error < 0) {
+ git_packfile_free(pack);
+ return -1;
+ }
+
+ *out = pack;
+ return 0;
+}
+
+void git_mwindow_put_pack(struct git_pack_file *pack)
+{
+ int count;
+ git_strmap_iter pos;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0)
+ return;
+
+ /* put before get would be a corrupted state */
+ assert(git__pack_cache);
+
+ pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name);
+ /* if we cannot find it, the state is corrupted */
+ assert(git_strmap_valid_index(git__pack_cache, pos));
+
+ count = git_atomic_dec(&pack->refcount);
+ if (count == 0) {
+ git_strmap_delete_at(git__pack_cache, pos);
+ git_packfile_free(pack);
+ }
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ return;
+}
+void git_mwindow_free_all(git_mwindow_file *mwf)
+{
if (git_mutex_lock(&git__mwindow_mutex)) {
giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
return;
}
+ git_mwindow_free_all_locked(mwf);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+}
+
+/*
+ * Free all the windows in a sequence, typically because we're done
+ * with the file
+ */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf)
+{
+ git_mwindow_ctl *ctl = &mem_ctl;
+ size_t i;
+
/*
* Remove these windows from the global list
*/
@@ -67,8 +178,6 @@ void git_mwindow_free_all(git_mwindow_file *mwf)
mwf->windows = w->next;
git__free(w);
}
-
- git_mutex_unlock(&git__mwindow_mutex);
}
/*
diff --git a/src/mwindow.h b/src/mwindow.h
index 0018ebbf0..63418e458 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -36,10 +36,18 @@ typedef struct git_mwindow_ctl {
} git_mwindow_ctl;
int git_mwindow_contains(git_mwindow *win, git_off_t offset);
-void git_mwindow_free_all(git_mwindow_file *mwf);
+void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
int git_mwindow_file_register(git_mwindow_file *mwf);
void git_mwindow_file_deregister(git_mwindow_file *mwf);
void git_mwindow_close(git_mwindow **w_cursor);
+int git_mwindow_files_init(void);
+void git_mwindow_files_free(void);
+
+struct git_pack_file; /* just declaration to avoid cyclical includes */
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
+void git_mwindow_put_pack(struct git_pack_file *pack);
+
#endif
diff --git a/src/netops.c b/src/netops.c
index ad27d84cf..adbae61c4 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -13,6 +13,7 @@
# include <netinet/in.h>
# include <arpa/inet.h>
#else
+# include <winsock2.h>
# include <ws2tcpip.h>
# ifdef _MSC_VER
# pragma comment(lib, "ws2_32")
@@ -33,6 +34,7 @@
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
+#include "global.h"
#ifdef GIT_WIN32
static void net_set_error(const char *str)
@@ -157,7 +159,7 @@ void gitno_buffer_setup_callback(
void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
{
#ifdef GIT_SSL
- if (socket->ssl.ctx) {
+ if (socket->ssl.ssl) {
gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
return;
}
@@ -202,12 +204,13 @@ static int gitno_ssl_teardown(gitno_ssl *ssl)
ret = 0;
SSL_free(ssl->ssl);
- SSL_CTX_free(ssl->ctx);
return ret;
}
+#endif
+
/* Match host names according to RFC 2818 rules */
-static int match_host(const char *pattern, const char *host)
+int gitno__match_host(const char *pattern, const char *host)
{
for (;;) {
char c = tolower(*pattern++);
@@ -230,9 +233,9 @@ static int match_host(const char *pattern, const char *host)
while(*host) {
char h = tolower(*host);
if (c == h)
- return match_host(pattern, host++);
+ return gitno__match_host(pattern, host++);
if (h == '.')
- return match_host(pattern, host);
+ return gitno__match_host(pattern, host);
host++;
}
return -1;
@@ -250,12 +253,14 @@ static int check_host_name(const char *name, const char *host)
if (!strcasecmp(name, host))
return 0;
- if (match_host(name, host) < 0)
+ if (gitno__match_host(name, host) < 0)
return -1;
return 0;
}
+#ifdef GIT_SSL
+
static int verify_server_cert(gitno_ssl *ssl, const char *host)
{
X509 *cert;
@@ -287,6 +292,10 @@ static int verify_server_cert(gitno_ssl *ssl, const char *host)
cert = SSL_get_peer_certificate(ssl->ssl);
+ if (!cert) {
+ giterr_set(GITERR_SSL, "the server did not provide a certificate");
+ return -1;
+ }
/* Check the alternative names */
alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
@@ -321,7 +330,7 @@ static int verify_server_cert(gitno_ssl *ssl, const char *host)
GENERAL_NAMES_free(alts);
if (matched == 0)
- goto cert_fail;
+ goto cert_fail_name;
if (matched == 1)
return 0;
@@ -358,11 +367,11 @@ static int verify_server_cert(gitno_ssl *ssl, const char *host)
int size = ASN1_STRING_to_UTF8(&peer_cn, str);
GITERR_CHECK_ALLOC(peer_cn);
if (memchr(peer_cn, '\0', size))
- goto cert_fail;
+ goto cert_fail_name;
}
if (check_host_name((char *)peer_cn, host) < 0)
- goto cert_fail;
+ goto cert_fail_name;
OPENSSL_free(peer_cn);
@@ -372,28 +381,22 @@ on_error:
OPENSSL_free(peer_cn);
return ssl_set_error(ssl, 0);
-cert_fail:
+cert_fail_name:
OPENSSL_free(peer_cn);
- giterr_set(GITERR_SSL, "Certificate host name check failed");
- return -1;
+ giterr_set(GITERR_SSL, "hostname does not match certificate");
+ return GIT_ECERTIFICATE;
}
-static int ssl_setup(gitno_socket *socket, const char *host, int flags)
+static int ssl_setup(gitno_socket *socket, const char *host)
{
int ret;
- SSL_library_init();
- SSL_load_error_strings();
- socket->ssl.ctx = SSL_CTX_new(SSLv23_method());
- if (socket->ssl.ctx == NULL)
- return ssl_set_error(&socket->ssl, 0);
-
- SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY);
- SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL);
- if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx))
- return ssl_set_error(&socket->ssl, 0);
+ if (git__ssl_ctx == NULL) {
+ giterr_set(GITERR_NET, "OpenSSL initialization failed");
+ return -1;
+ }
- socket->ssl.ssl = SSL_new(socket->ssl.ctx);
+ socket->ssl.ssl = SSL_new(git__ssl_ctx);
if (socket->ssl.ssl == NULL)
return ssl_set_error(&socket->ssl, 0);
@@ -403,9 +406,6 @@ static int ssl_setup(gitno_socket *socket, const char *host, int flags)
if ((ret = SSL_connect(socket->ssl.ssl)) <= 0)
return ssl_set_error(&socket->ssl, ret);
- if (GITNO_CONNECT_SSL_NO_CHECK_CERT & flags)
- return 0;
-
return verify_server_cert(&socket->ssl, host);
}
#endif
@@ -458,7 +458,7 @@ int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int f
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
- if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) {
+ if ((ret = p_getaddrinfo(host, port, &hints, &info)) != 0) {
giterr_set(GITERR_NET,
"Failed to resolve address for %s: %s", host, p_gai_strerror(ret));
return -1;
@@ -491,8 +491,9 @@ int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int f
p_freeaddrinfo(info);
#ifdef GIT_SSL
- if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0)
- return -1;
+ if ((flags & GITNO_CONNECT_SSL) &&
+ (ret = ssl_setup(s_out, host)) < 0)
+ return ret;
#else
/* SSL is not supported */
if (flags & GITNO_CONNECT_SSL) {
@@ -530,7 +531,7 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
size_t off = 0;
#ifdef GIT_SSL
- if (socket->ssl.ctx)
+ if (socket->ssl.ssl)
return gitno_send_ssl(&socket->ssl, msg, len, flags);
#endif
@@ -551,7 +552,7 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
int gitno_close(gitno_socket *s)
{
#ifdef GIT_SSL
- if (s->ssl.ctx &&
+ if (s->ssl.ssl &&
gitno_ssl_teardown(&s->ssl) < 0)
return -1;
#endif
@@ -715,6 +716,9 @@ int gitno_extract_url_parts(
if (u.field_set & (1 << UF_PATH)) {
*path = git__substrdup(_path, u.field_data[UF_PATH].len);
GITERR_CHECK_ALLOC(*path);
+ } else {
+ giterr_set(GITERR_NET, "invalid url, missing path");
+ return GIT_EINVALIDSPEC;
}
if (u.field_set & (1 << UF_USERINFO)) {
diff --git a/src/netops.h b/src/netops.h
index 666d66b12..beb0e0760 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -16,7 +16,6 @@
struct gitno_ssl {
#ifdef GIT_SSL
- SSL_CTX *ctx;
SSL *ssl;
#else
size_t dummy;
@@ -48,12 +47,21 @@ typedef struct gitno_buffer gitno_buffer;
enum {
/* Attempt to create an SSL connection. */
GITNO_CONNECT_SSL = 1,
-
- /* Valid only when GITNO_CONNECT_SSL is also specified.
- * Indicates that the server certificate should not be validated. */
- GITNO_CONNECT_SSL_NO_CHECK_CERT = 2,
};
+/**
+ * Check if the name in a cert matches the wanted hostname
+ *
+ * Check if a pattern from a certificate matches the hostname we
+ * wanted to connect to according to RFC2818 rules (which specifies
+ * HTTP over TLS). Mainly, an asterisk matches anything, but is
+ * limited to a single url component.
+ *
+ * Note that this does not set an error message. It expects the user
+ * to provide the message for the user.
+ */
+int gitno__match_host(const char *pattern, const char *host);
+
void gitno_buffer_setup(gitno_socket *t, gitno_buffer *buf, char *data, size_t len);
void gitno_buffer_setup_callback(gitno_socket *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
int gitno_recv(gitno_buffer *buf);
diff --git a/src/object.c b/src/object.c
index 3847a0739..93068b85f 100644
--- a/src/object.c
+++ b/src/object.c
@@ -375,7 +375,7 @@ int git_object_lookup_bypath(
assert(out && treeish && path);
- if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE) < 0) ||
+ if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0 ||
(error = git_tree_entry_bypath(&entry, tree, path)) < 0)
{
goto cleanup;
diff --git a/src/odb.c b/src/odb.c
index 00740d2e2..a4fc02686 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -783,6 +783,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
return error;
}
+ giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL)
return -1;
@@ -1123,14 +1124,9 @@ int git_odb__error_ambiguous(const char *message)
return GIT_EAMBIGUOUS;
}
-int git_odb_init_backend(git_odb_backend* backend, int version)
+int git_odb_init_backend(git_odb_backend *backend, unsigned int version)
{
- if (version != GIT_ODB_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_odb_backend", version);
- return -1;
- } else {
- git_odb_backend b = GIT_ODB_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT);
+ return 0;
}
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 7b46a6652..ef6de41a9 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -714,7 +714,7 @@ struct foreach_state {
GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
{
int v, i = 0;
- if (strlen(ptr) != 41)
+ if (strlen(ptr) != GIT_OID_HEXSZ+1)
return -1;
if (ptr[2] != '/') {
@@ -755,6 +755,10 @@ static int foreach_cb(void *_state, git_buf *path)
{
struct foreach_state *state = (struct foreach_state *) _state;
+ /* non-dir is some stray file, ignore it */
+ if (!git_path_isdir(git_buf_cstr(path)))
+ return 0;
+
return git_path_direach(path, 0, foreach_object_dir_cb, state);
}
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 3750da37f..1757cf920 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -210,7 +210,7 @@ static int packfile_load__cb(void *data, git_buf *path)
return 0;
}
- error = git_packfile_alloc(&pack, path->ptr);
+ error = git_mwindow_get_pack(&pack, path->ptr);
/* ignore missing .pack file as git does */
if (error == GIT_ENOTFOUND) {
@@ -605,7 +605,7 @@ static void pack_backend__free(git_odb_backend *_backend)
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
- git_packfile_free(p);
+ git_mwindow_put_pack(p);
}
git_vector_free(&backend->packs);
@@ -647,7 +647,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
if (pack_backend__alloc(&backend, 1) < 0)
return -1;
- if (git_packfile_alloc(&packfile, idx) < 0 ||
+ if (git_mwindow_get_pack(&packfile, idx) < 0 ||
git_vector_insert(&backend->packs, packfile) < 0)
{
pack_backend__free((git_odb_backend *)backend);
@@ -664,6 +664,9 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
+ if (git_mwindow_files_init() < 0)
+ return -1;
+
if (pack_backend__alloc(&backend, 8) < 0)
return -1;
diff --git a/src/oid.c b/src/oid.c
index b640cadd1..969931d04 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -8,6 +8,7 @@
#include "common.h"
#include "git2/oid.h"
#include "repository.h"
+#include "global.h"
#include <string.h>
#include <limits.h>
@@ -99,6 +100,13 @@ void git_oid_pathfmt(char *str, const git_oid *oid)
str = fmt_one(str, oid->id[i]);
}
+char *git_oid_tostr_s(const git_oid *oid)
+{
+ char *str = GIT_GLOBAL->oid_fmt;
+ git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
+ return str;
+}
+
char *git_oid_allocfmt(const git_oid *oid)
{
char *str = git__malloc(GIT_OID_HEXSZ + 1);
diff --git a/src/oid.h b/src/oid.h
index cfe7ca1b2..aa1f0bfdc 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -9,6 +9,17 @@
#include "git2/oid.h"
+/**
+ * Format a git_oid into a newly allocated c-string.
+ *
+ * The c-string is owned by the caller and needs to be manually freed.
+ *
+ * @param id the oid structure to format
+ * @return the c-string; NULL if memory is exhausted. Caller must
+ * deallocate the string with git__free().
+ */
+char *git_oid_allocfmt(const git_oid *id);
+
GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
int i;
diff --git a/src/oidarray.c b/src/oidarray.c
new file mode 100644
index 000000000..1d51a2958
--- /dev/null
+++ b/src/oidarray.c
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#include "git2/oidarray.h"
+#include "oidarray.h"
+#include "array.h"
+
+void git_oidarray_free(git_oidarray *arr)
+{
+ git__free(arr->ids);
+}
+
+void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array)
+{
+ arr->count = array->size;
+ arr->ids = array->ptr;
+}
diff --git a/src/oidarray.h b/src/oidarray.h
new file mode 100644
index 000000000..a7215ae6c
--- /dev/null
+++ b/src/oidarray.h
@@ -0,0 +1,18 @@
+/*
+ * 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_oidarray_h__
+#define INCLUDE_oidarray_h__
+
+#include "common.h"
+#include "git2/oidarray.h"
+#include "array.h"
+
+typedef git_array_t(git_oid) git_array_oid_t;
+
+extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array);
+
+#endif
diff --git a/src/pack-objects.c b/src/pack-objects.c
index ace8afd17..0040a826b 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -90,8 +90,8 @@ static int packbuilder_config(git_packbuilder *pb)
int ret;
int64_t val;
- if (git_repository_config__weakptr(&config, pb->repo) < 0)
- return -1;
+ if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0)
+ return ret;
#define config_get(KEY,DST,DFLT) do { \
ret = git_config_get_int64(&val, config, KEY); \
@@ -109,6 +109,8 @@ static int packbuilder_config(git_packbuilder *pb)
#undef config_get
+ git_config_free(config);
+
return 0;
}
@@ -1207,7 +1209,7 @@ static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
git_mutex_unlock(&target->mutex);
if (!sub_size) {
- git_thread_join(target->thread, NULL);
+ git_thread_join(&target->thread, NULL);
git_cond_free(&target->cond);
git_mutex_free(&target->mutex);
active_threads--;
@@ -1276,6 +1278,7 @@ int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t siz
int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
{
PREPARE_PACK;
+ git_buf_sanitize(buf);
return write_pack(pb, &write_pack_buf, buf);
}
diff --git a/src/pack.c b/src/pack.c
index de038a45c..516e0f3ca 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -83,16 +83,12 @@ static void cache_free(git_pack_cache *cache)
}
git_offmap_free(cache->entries);
- git_mutex_free(&cache->lock);
+ cache->entries = NULL;
}
-
- memset(cache, 0, sizeof(*cache));
}
static int cache_init(git_pack_cache *cache)
{
- memset(cache, 0, sizeof(*cache));
-
cache->entries = git_offmap_alloc();
GITERR_CHECK_ALLOC(cache->entries);
@@ -514,72 +510,102 @@ int git_packfile_resolve_header(
return error;
}
-static int packfile_unpack_delta(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- git_off_t *curpos,
- size_t delta_size,
- git_otype delta_type,
- git_off_t obj_offset)
+#define SMALL_STACK_SIZE 64
+
+/**
+ * Generate the chain of dependencies which we need to get to the
+ * object at `off`. `chain` is used a stack, popping gives the right
+ * order to apply deltas on. If an object is found in the pack's base
+ * cache, we stop calculating there.
+ */
+static int pack_dependency_chain(git_dependency_chain *chain_out,
+ git_pack_cache_entry **cached_out, git_off_t *cached_off,
+ struct pack_chain_elem *small_stack, size_t *stack_sz,
+ struct git_pack_file *p, git_off_t obj_offset)
{
- git_off_t base_offset, base_key;
- git_rawobj base, delta;
- git_pack_cache_entry *cached = NULL;
- int error, found_base = 0;
+ git_dependency_chain chain = GIT_ARRAY_INIT;
+ git_mwindow *w_curs = NULL;
+ git_off_t curpos = obj_offset, base_offset;
+ int error = 0, use_heap = 0;
+ size_t size, elem_pos;
+ git_otype type;
- base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
- git_mwindow_close(w_curs);
- if (base_offset == 0)
- return packfile_error("delta offset is zero");
- if (base_offset < 0) /* must actually be an error code */
- return (int)base_offset;
+ elem_pos = 0;
+ while (true) {
+ struct pack_chain_elem *elem;
+ git_pack_cache_entry *cached = NULL;
- if (!p->bases.entries && (cache_init(&p->bases) < 0))
- return -1;
+ /* if we have a base cached, we can stop here instead */
+ if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
+ *cached_out = cached;
+ *cached_off = obj_offset;
+ break;
+ }
- base_key = base_offset; /* git_packfile_unpack modifies base_offset */
- if ((cached = cache_get(&p->bases, base_offset)) != NULL) {
- memcpy(&base, &cached->raw, sizeof(git_rawobj));
- found_base = 1;
- }
+ /* if we run out of space on the small stack, use the array */
+ if (elem_pos == SMALL_STACK_SIZE) {
+ git_array_init_to_size(chain, elem_pos);
+ GITERR_CHECK_ARRAY(chain);
+ memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
+ chain.size = elem_pos;
+ use_heap = 1;
+ }
+
+ curpos = obj_offset;
+ if (!use_heap) {
+ elem = &small_stack[elem_pos];
+ } else {
+ elem = git_array_alloc(chain);
+ if (!elem) {
+ error = -1;
+ goto on_error;
+ }
+ }
- if (!cached) { /* have to inflate it */
- error = git_packfile_unpack(&base, p, &base_offset);
+ elem->base_key = obj_offset;
+
+ error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+ git_mwindow_close(&w_curs);
- /*
- * TODO: git.git tries to load the base from other packfiles
- * or loose objects.
- *
- * We'll need to do this in order to support thin packs.
- */
if (error < 0)
- return error;
- }
+ goto on_error;
- error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
- git_mwindow_close(w_curs);
+ elem->offset = curpos;
+ elem->size = size;
+ elem->type = type;
+ elem->base_key = obj_offset;
- if (error < 0) {
- if (!found_base)
- git__free(base.data);
- return error;
- }
+ if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
+ break;
- obj->type = base.type;
- error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
- if (error < 0)
- goto on_error;
+ base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+ git_mwindow_close(&w_curs);
- if (found_base)
- git_atomic_dec(&cached->refcount);
- else if (cache_add(&p->bases, &base, base_key) < 0)
- git__free(base.data);
+ if (base_offset == 0) {
+ error = packfile_error("delta offset is zero");
+ goto on_error;
+ }
+ if (base_offset < 0) { /* must actually be an error code */
+ error = (int)base_offset;
+ goto on_error;
+ }
-on_error:
- git__free(delta.data);
+ /* we need to pass the pos *after* the delta-base bit */
+ elem->offset = curpos;
+
+ /* go through the loop again, but with the new object */
+ obj_offset = base_offset;
+ elem_pos++;
+ }
+
+
+ *stack_sz = elem_pos + 1;
+ *chain_out = chain;
+ return error;
- return error; /* error set by git__delta_apply */
+on_error:
+ git_array_clear(chain);
+ return error;
}
int git_packfile_unpack(
@@ -589,48 +615,135 @@ int git_packfile_unpack(
{
git_mwindow *w_curs = NULL;
git_off_t curpos = *obj_offset;
- int error;
-
- size_t size = 0;
- git_otype type;
+ int error, free_base = 0;
+ git_dependency_chain chain = GIT_ARRAY_INIT;
+ struct pack_chain_elem *elem = NULL, *stack;
+ git_pack_cache_entry *cached = NULL;
+ struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
+ size_t stack_size = 0, elem_pos;
+ git_otype base_type;
/*
* TODO: optionally check the CRC on the packfile
*/
+ error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
+ if (error < 0)
+ return error;
+
obj->data = NULL;
obj->len = 0;
obj->type = GIT_OBJ_BAD;
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
- git_mwindow_close(&w_curs);
+ /* let's point to the right stack */
+ stack = chain.ptr ? chain.ptr : small_stack;
- if (error < 0)
- return error;
-
- switch (type) {
- case GIT_OBJ_OFS_DELTA:
- case GIT_OBJ_REF_DELTA:
- error = packfile_unpack_delta(
- obj, p, &w_curs, &curpos,
- size, type, *obj_offset);
- break;
+ elem_pos = stack_size;
+ if (cached) {
+ memcpy(obj, &cached->raw, sizeof(git_rawobj));
+ base_type = obj->type;
+ elem_pos--; /* stack_size includes the base, which isn't actually there */
+ } else {
+ elem = &stack[--elem_pos];
+ base_type = elem->type;
+ }
+ switch (base_type) {
case GIT_OBJ_COMMIT:
case GIT_OBJ_TREE:
case GIT_OBJ_BLOB:
case GIT_OBJ_TAG:
- error = packfile_unpack_compressed(
- obj, p, &w_curs, &curpos,
- size, type);
+ if (!cached) {
+ curpos = elem->offset;
+ error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
+ git_mwindow_close(&w_curs);
+ base_type = elem->type;
+ }
+ if (error < 0)
+ goto cleanup;
break;
-
+ case GIT_OBJ_OFS_DELTA:
+ case GIT_OBJ_REF_DELTA:
+ error = packfile_error("dependency chain ends in a delta");
+ goto cleanup;
default:
- error = packfile_error("invalid packfile type in header");;
- break;
+ error = packfile_error("invalid packfile type in header");
+ goto cleanup;
+ }
+
+ /*
+ * Finding the object we want a cached base element is
+ * problematic, as we need to make sure we don't accidentally
+ * give the caller the cached object, which it would then feel
+ * free to free, so we need to copy the data.
+ */
+ if (cached && stack_size == 1) {
+ void *data = obj->data;
+ obj->data = git__malloc(obj->len + 1);
+ GITERR_CHECK_ALLOC(obj->data);
+ memcpy(obj->data, data, obj->len + 1);
+ git_atomic_dec(&cached->refcount);
+ goto cleanup;
+ }
+
+ /* we now apply each consecutive delta until we run out */
+ while (elem_pos > 0 && !error) {
+ git_rawobj base, delta;
+
+ /*
+ * We can now try to add the base to the cache, as
+ * long as it's not already the cached one.
+ */
+ if (!cached)
+ free_base = !!cache_add(&p->bases, obj, elem->base_key);
+
+ elem = &stack[elem_pos - 1];
+ curpos = elem->offset;
+ error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ break;
+
+ /* the current object becomes the new base, on which we apply the delta */
+ base = *obj;
+ obj->data = NULL;
+ obj->len = 0;
+ obj->type = GIT_OBJ_BAD;
+
+ error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
+ obj->type = base_type;
+ /*
+ * We usually don't want to free the base at this
+ * point, as we put it into the cache in the previous
+ * iteration. free_base lets us know that we got the
+ * base object directly from the packfile, so we can free it.
+ */
+ git__free(delta.data);
+ if (free_base) {
+ free_base = 0;
+ git__free(base.data);
+ }
+
+ if (cached) {
+ git_atomic_dec(&cached->refcount);
+ cached = NULL;
+ }
+
+ if (error < 0)
+ break;
+
+ elem_pos--;
}
- *obj_offset = curpos;
+cleanup:
+ if (error < 0)
+ git__free(obj->data);
+
+ if (elem)
+ *obj_offset = curpos;
+
+ git_array_clear(chain);
return error;
}
@@ -660,7 +773,7 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p,
st = inflateInit(&obj->zstream);
if (st != Z_OK) {
git__free(obj);
- giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ giterr_set(GITERR_ZLIB, "failed to init packfile stream");
return -1;
}
@@ -691,7 +804,7 @@ ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t
written = len - obj->zstream.avail_out;
if (st != Z_OK && st != Z_STREAM_END) {
- giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ giterr_set(GITERR_ZLIB, "error reading from the zlib stream");
return -1;
}
@@ -736,7 +849,7 @@ int packfile_unpack_compressed(
st = inflateInit(&stream);
if (st != Z_OK) {
git__free(buffer);
- giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ giterr_set(GITERR_ZLIB, "failed to init zlib stream on unpack");
return -1;
}
@@ -763,7 +876,7 @@ int packfile_unpack_compressed(
if ((st != Z_STREAM_END) || stream.total_out != size) {
git__free(buffer);
- giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ giterr_set(GITERR_ZLIB, "error inflating zlib stream");
return -1;
}
@@ -852,16 +965,17 @@ void git_packfile_free(struct git_pack_file *p)
cache_free(&p->bases);
- git_mwindow_free_all(&p->mwf);
-
- if (p->mwf.fd >= 0)
+ if (p->mwf.fd >= 0) {
+ git_mwindow_free_all_locked(&p->mwf);
p_close(p->mwf.fd);
+ }
pack_index_free(p);
git__free(p->bad_object_sha1);
git_mutex_free(&p->lock);
+ git_mutex_free(&p->bases.lock);
git__free(p);
}
@@ -946,6 +1060,23 @@ cleanup:
return -1;
}
+int git_packfile__name(char **out, const char *path)
+{
+ size_t path_len;
+ git_buf buf = GIT_BUF_INIT;
+
+ path_len = strlen(path);
+
+ if (path_len < strlen(".idx"))
+ return git_odb__error_notfound("invalid packfile path", NULL);
+
+ if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
+ return -1;
+
+ *out = git_buf_detach(&buf);
+ return 0;
+}
+
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
{
struct stat st;
@@ -997,6 +1128,11 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
return -1;
}
+ if (cache_init(&p->bases) < 0) {
+ git__free(p);
+ return -1;
+ }
+
*pack_out = p;
return 0;
diff --git a/src/pack.h b/src/pack.h
index 58f81e2f0..34d37d907 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -17,6 +17,7 @@
#include "mwindow.h"
#include "odb.h"
#include "oidmap.h"
+#include "array.h"
#define GIT_PACK_FILE_MODE 0444
@@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
git_rawobj raw;
} git_pack_cache_entry;
+struct pack_chain_elem {
+ git_off_t base_key;
+ git_off_t offset;
+ size_t size;
+ git_otype type;
+};
+
+typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
+
#include "offmap.h"
GIT__USE_OFFMAP;
@@ -80,6 +90,7 @@ struct git_pack_file {
git_mwindow_file mwf;
git_map index_map;
git_mutex lock; /* protect updates to mwf and index_map */
+ git_atomic refcount;
uint32_t num_objects;
uint32_t num_bad_objects;
@@ -113,6 +124,8 @@ typedef struct git_packfile_stream {
size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type);
+int git_packfile__name(char **out, const char *path);
+
int git_packfile_unpack_header(
size_t *size_p,
git_otype *type_p,
diff --git a/src/path.c b/src/path.c
index 2690cd8e8..67133f97e 100644
--- a/src/path.c
+++ b/src/path.c
@@ -377,26 +377,33 @@ static int error_invalid_local_file_uri(const char *uri)
return -1;
}
-int git_path_fromurl(git_buf *local_path_out, const char *file_url)
+static int local_file_url_prefixlen(const char *file_url)
{
- int offset = 0, len;
+ int len = -1;
- assert(local_path_out && file_url);
+ if (git__prefixcmp(file_url, "file://") == 0) {
+ if (file_url[7] == '/')
+ len = 8;
+ else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
+ len = 17;
+ }
- if (git__prefixcmp(file_url, "file://") != 0)
- return error_invalid_local_file_uri(file_url);
+ return len;
+}
- offset += 7;
- len = (int)strlen(file_url);
+bool git_path_is_local_file_url(const char *file_url)
+{
+ return (local_file_url_prefixlen(file_url) > 0);
+}
- if (offset < len && file_url[offset] == '/')
- offset++;
- else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)
- offset += 10;
- else
- return error_invalid_local_file_uri(file_url);
+int git_path_fromurl(git_buf *local_path_out, const char *file_url)
+{
+ int offset;
+
+ assert(local_path_out && file_url);
- if (offset >= len || file_url[offset] == '/')
+ if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
+ file_url[offset] == '\0' || file_url[offset] == '/')
return error_invalid_local_file_uri(file_url);
#ifndef GIT_WIN32
@@ -404,14 +411,13 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url)
#endif
git_buf_clear(local_path_out);
-
return git__percent_decode(local_path_out, file_url + offset);
}
int git_path_walk_up(
git_buf *path,
const char *ceiling,
- int (*cb)(void *data, git_buf *),
+ int (*cb)(void *data, const char *),
void *data)
{
int error = 0;
@@ -429,12 +435,20 @@ int git_path_walk_up(
}
scan = git_buf_len(path);
+ /* empty path: yield only once */
+ if (!scan) {
+ error = cb(data, "");
+ if (error)
+ giterr_set_after_callback(error);
+ return error;
+ }
+
iter.ptr = path->ptr;
iter.size = git_buf_len(path);
iter.asize = path->asize;
while (scan >= stop) {
- error = cb(data, &iter);
+ error = cb(data, iter.ptr);
iter.ptr[scan] = oldc;
if (error) {
@@ -454,6 +468,13 @@ int git_path_walk_up(
if (scan >= 0)
iter.ptr[scan] = oldc;
+ /* relative path: yield for the last component */
+ if (!error && stop == 0 && iter.ptr[0] != '/') {
+ error = cb(data, "");
+ if (error)
+ giterr_set_after_callback(error);
+ }
+
return error;
}
@@ -744,6 +765,61 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
+int git_path_make_relative(git_buf *path, const char *parent)
+{
+ const char *p, *q, *p_dirsep, *q_dirsep;
+ size_t plen = path->size, newlen, depth = 1, i;
+
+ for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
+ if (*p == '/' && *q == '/') {
+ p_dirsep = p;
+ q_dirsep = q;
+ }
+ else if (*p != *q)
+ break;
+ }
+
+ /* need at least 1 common path segment */
+ if ((p_dirsep == path->ptr || q_dirsep == parent) &&
+ (*p_dirsep != '/' || *q_dirsep != '/')) {
+ giterr_set(GITERR_INVALID,
+ "%s is not a parent of %s", parent, path->ptr);
+ return GIT_ENOTFOUND;
+ }
+
+ if (*p == '/' && !*q)
+ p++;
+ else if (!*p && *q == '/')
+ q++;
+ else if (!*p && !*q)
+ return git_buf_clear(path), 0;
+ else {
+ p = p_dirsep + 1;
+ q = q_dirsep + 1;
+ }
+
+ plen -= (p - path->ptr);
+
+ if (!*q)
+ return git_buf_set(path, p, plen);
+
+ for (; (q = strchr(q, '/')) && *(q + 1); q++)
+ depth++;
+
+ newlen = (depth * 3) + plen;
+
+ if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0)
+ return -1;
+
+ memmove(path->ptr + (depth * 3), p, plen + 1);
+
+ for (i = 0; i < depth; i++)
+ memcpy(path->ptr + (i * 3), "../", 3);
+
+ path->size = newlen;
+ return 0;
+}
+
bool git_path_has_non_ascii(const char *path, size_t pathlen)
{
const uint8_t *scan = (const uint8_t *)path, *end;
@@ -799,8 +875,11 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
if (rv != (size_t)-1)
break;
+ /* if we cannot convert the data (probably because iconv thinks
+ * it is not valid UTF-8 source data), then use original data
+ */
if (errno != E2BIG)
- goto fail;
+ return 0;
/* make space for 2x the remaining data to be converted
* (with per retry overhead to avoid infinite loops)
@@ -823,6 +902,64 @@ fail:
return -1;
}
+static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
+static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
+
+/* Check if the platform is decomposing unicode data for us. We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
+ */
+bool git_path_does_fs_decompose_unicode(const char *root)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd;
+ bool found_decomposed = false;
+ char tmp[6];
+
+ /* Create a file using a precomposed path and then try to find it
+ * using the decomposed name. If the lookup fails, then we will mark
+ * that we should precompose unicode for this repository.
+ */
+ if (git_buf_joinpath(&path, root, nfc_file) < 0 ||
+ (fd = p_mkstemp(path.ptr)) < 0)
+ goto done;
+ p_close(fd);
+
+ /* record trailing digits generated by mkstemp */
+ memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
+
+ /* try to look up as NFD path */
+ if (git_buf_joinpath(&path, root, nfd_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ found_decomposed = git_path_exists(path.ptr);
+
+ /* remove temporary file (using original precomposed path) */
+ if (git_buf_joinpath(&path, root, nfc_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ (void)p_unlink(path.ptr);
+
+done:
+ git_buf_free(&path);
+ return found_decomposed;
+}
+
+#else
+
+bool git_path_does_fs_decompose_unicode(const char *root)
+{
+ GIT_UNUSED(root);
+ return false;
+}
+
#endif
#if defined(__sun) || defined(__GNU__)
@@ -843,7 +980,7 @@ int git_path_direach(
path_dirent_data de_data;
struct dirent *de, *de_buf = (struct dirent *)&de_data;
- (void)flags;
+ GIT_UNUSED(flags);
#ifdef GIT_USE_ICONV
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -914,7 +1051,7 @@ int git_path_dirload(
path_dirent_data de_data;
struct dirent *de, *de_buf = (struct dirent *)&de_data;
- (void)flags;
+ GIT_UNUSED(flags);
#ifdef GIT_USE_ICONV
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -966,8 +1103,10 @@ int git_path_dirload(
entry_path[path_len] = '/';
memcpy(&entry_path[path_len + need_slash], de_path, de_len);
- if ((error = git_vector_insert(contents, entry_path)) < 0)
+ if ((error = git_vector_insert(contents, entry_path)) < 0) {
+ git__free(entry_path);
break;
+ }
}
closedir(dir);
@@ -1043,20 +1182,29 @@ int git_path_dirload_with_stat(
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
(error = git_path_lstat(full.ptr, &ps->st)) < 0) {
+
if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
+ /* file was removed between readdir and lstat */
git_vector_remove(contents, i--);
- continue;
+ } else {
+ /* Treat the file as unreadable if we get any other error */
+ memset(&ps->st, 0, sizeof(ps->st));
+ ps->st.st_mode = GIT_FILEMODE_UNREADABLE;
}
- break;
+ giterr_clear();
+ error = 0;
+ continue;
}
if (S_ISDIR(ps->st.st_mode)) {
ps->path[ps->path_len++] = '/';
ps->path[ps->path_len] = '\0';
}
+ else if (!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) {
+ /* skip everything but dirs, plain files, and symlinks */
+ git_vector_remove(contents, i--);
+ }
}
/* sort now that directory suffix is added */
@@ -1066,3 +1214,11 @@ int git_path_dirload_with_stat(
return error;
}
+
+int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
+{
+ if (git_path_is_local_file_url(url_or_path))
+ return git_path_fromurl(local_path_out, url_or_path);
+ else
+ return git_buf_sets(local_path_out, url_or_path);
+}
diff --git a/src/path.h b/src/path.h
index 2367d707b..11bb6d173 100644
--- a/src/path.h
+++ b/src/path.h
@@ -8,6 +8,7 @@
#define INCLUDE_path_h__
#include "common.h"
+#include "posix.h"
#include "buffer.h"
#include "vector.h"
@@ -127,6 +128,14 @@ GIT_INLINE(int) git_path_is_relative(const char *p)
return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
}
+/**
+ * Check if string is at end of path segment (i.e. looking at '/' or '\0')
+ */
+GIT_INLINE(int) git_path_at_end_of_segment(const char *p)
+{
+ return !*p || *p == '/';
+}
+
extern int git__percent_decode(git_buf *decoded_out, const char *input);
/**
@@ -188,6 +197,17 @@ extern bool git_path_contains(git_buf *dir, const char *item);
extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
/**
+ * Make the path relative to the given parent path.
+ *
+ * @param path The path to make relative
+ * @param parent The parent path to make path relative to
+ * @return 0 if path was made relative, GIT_ENOTFOUND
+ * if there was not common root between the paths,
+ * or <0.
+ */
+extern int git_path_make_relative(git_buf *path, const char *parent);
+
+/**
* Check if the given path contains the given file.
*
* @param dir Directory path that might contain file
@@ -303,7 +323,7 @@ extern int git_path_cmp(
extern int git_path_walk_up(
git_buf *pathbuf,
const char *ceiling,
- int (*callback)(void *payload, git_buf *path),
+ int (*callback)(void *payload, const char *path),
void *payload);
/**
@@ -436,4 +456,10 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
#endif /* GIT_USE_ICONV */
+extern bool git_path_does_fs_decompose_unicode(const char *root);
+
+/* Used for paths to repositories on the filesystem */
+extern bool git_path_is_local_file_url(const char *file_url);
+extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
+
#endif
diff --git a/src/pool.c b/src/pool.c
index 146f118b4..30555273e 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -7,7 +7,7 @@ struct git_pool_page {
git_pool_page *next;
uint32_t size;
uint32_t avail;
- char data[GIT_FLEX_ARRAY];
+ GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
};
struct pool_freelist {
@@ -146,7 +146,7 @@ GIT_INLINE(void) pool_remove_page(
void *git_pool_malloc(git_pool *pool, uint32_t items)
{
git_pool_page *scan = pool->open, *prev;
- uint32_t size = items * pool->item_size;
+ uint32_t size = ((items * pool->item_size) + 7) & ~7;
void *ptr = NULL;
pool->has_string_alloc = 0;
diff --git a/src/posix.c b/src/posix.c
index 7b2962feb..7aeb0e6c1 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -99,7 +99,7 @@ const char *p_gai_strerror(int ret)
#endif /* NO_ADDRINFO */
-int p_open(const char *path, int flags, ...)
+int p_open(const char *path, volatile int flags, ...)
{
mode_t mode = 0;
@@ -207,6 +207,13 @@ int p_write(git_file fd, const void *buf, size_t cnt)
#include "map.h"
+long git__page_size(void)
+{
+ /* dummy; here we don't need any alignment anyway */
+ return 4096;
+}
+
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
GIT_MMAP_VALIDATE(out, len, prot, flags);
diff --git a/src/posix.h b/src/posix.h
index f85b1aebd..9ef348739 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -12,23 +12,61 @@
#include <time.h>
#include "fnmatch.h"
+/* stat: file mode type testing macros */
#ifndef S_IFGITLINK
#define S_IFGITLINK 0160000
#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
#endif
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#undef _S_IFLNK
+#define _S_IFLNK S_IFLNK
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 00100
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
+#ifndef S_ISFIFO
+#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+#endif
+
/* if S_ISGID is not defined, then don't try to set it */
#ifndef S_ISGID
#define S_ISGID 0
#endif
-#if !defined(O_BINARY)
+#ifndef O_BINARY
#define O_BINARY 0
#endif
-#if !defined(O_CLOEXEC)
+#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
+/* access() mode parameter #defines */
+#ifndef F_OK
+#define F_OK 0 /* existence check */
+#endif
+#ifndef W_OK
+#define W_OK 2 /* write mode check */
+#endif
+#ifndef R_OK
+#define R_OK 4 /* read mode check */
+#endif
+
/* Determine whether an errno value indicates that a read or write failed
* because the descriptor is blocked.
*/
@@ -38,6 +76,12 @@
#define GIT_ISBLOCKED(e) ((e) == EAGAIN)
#endif
+/* define some standard errnos that the runtime may be missing. for example,
+ * mingw lacks EAFNOSUPPORT. */
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT (INT_MAX-1)
+#endif
+
typedef int git_file;
/**
@@ -56,8 +100,6 @@ typedef int git_file;
extern int p_read(git_file fd, void *buf, size_t cnt);
extern int p_write(git_file fd, const void *buf, size_t cnt);
-#define p_fstat(f,b) fstat(f, b)
-#define p_lseek(f,n,w) lseek(f, n, w)
#define p_close(fd) close(fd)
#define p_umask(m) umask(m)
@@ -66,29 +108,6 @@ extern int p_creat(const char *path, mode_t mode);
extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
-#ifndef GIT_WIN32
-
-#define p_stat(p,b) stat(p, b)
-#define p_chdir(p) chdir(p)
-#define p_rmdir(p) rmdir(p)
-#define p_chmod(p,m) chmod(p, m)
-#define p_access(p,m) access(p,m)
-#define p_recv(s,b,l,f) recv(s,b,l,f)
-#define p_send(s,b,l,f) send(s,b,l,f)
-typedef int GIT_SOCKET;
-#define INVALID_SOCKET -1
-
-#define p_localtime_r localtime_r
-#define p_gmtime_r gmtime_r
-
-#else
-
-typedef SOCKET GIT_SOCKET;
-extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
-extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
-
-#endif
-
/**
* Platform-dependent methods
*/
diff --git a/src/push.c b/src/push.c
index 9943f215c..be5ec1c0e 100644
--- a/src/push.c
+++ b/src/push.c
@@ -716,14 +716,9 @@ void git_push_free(git_push *push)
git__free(push);
}
-int git_push_init_options(git_push_options* opts, int version)
+int git_push_init_options(git_push_options *opts, unsigned int version)
{
- if (version != GIT_PUSH_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_push_options", version);
- return -1;
- } else {
- git_push_options o = GIT_PUSH_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/refdb.c b/src/refdb.c
index 3e7a592f8..69bf74734 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -236,14 +236,9 @@ int git_refdb_ensure_log(git_refdb *db, const char *refname)
return db->backend->ensure_log(db->backend, refname);
}
-int git_refdb_init_backend(git_refdb_backend* backend, int version)
+int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
{
- if (version != GIT_REFDB_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_refdb_backend", version);
- return -1;
- } else {
- git_refdb_backend b = GIT_REFDB_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
+ return 0;
}
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f9bd4eab5..0e36ca8ac 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -458,6 +458,7 @@ typedef struct {
git_pool pool;
git_vector loose;
+ git_sortedcache *cache;
size_t loose_pos;
size_t packed_pos;
} refdb_fs_iter;
@@ -468,6 +469,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git_vector_free(&iter->loose);
git_pool_clear(&iter->pool);
+ git_sortedcache_free(iter->cache);
git__free(iter);
}
@@ -539,10 +541,14 @@ static int refdb_fs_backend__iterator_next(
giterr_clear();
}
- git_sortedcache_rlock(backend->refcache);
+ if (!iter->cache) {
+ if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
+ return error;
+ }
- while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
- ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+ error = GIT_ITEROVER;
+ while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
+ ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */
break;
@@ -556,7 +562,6 @@ static int refdb_fs_backend__iterator_next(
break;
}
- git_sortedcache_runlock(backend->refcache);
return error;
}
@@ -579,10 +584,14 @@ static int refdb_fs_backend__iterator_next_name(
giterr_clear();
}
- git_sortedcache_rlock(backend->refcache);
+ if (!iter->cache) {
+ if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
+ return error;
+ }
- while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
- ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+ error = GIT_ITEROVER;
+ while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
+ ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */
break;
@@ -596,7 +605,6 @@ static int refdb_fs_backend__iterator_next_name(
break;
}
- git_sortedcache_runlock(backend->refcache);
return error;
}
@@ -927,19 +935,15 @@ static int has_reflog(git_repository *repo, const char *name);
/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
static int should_write_reflog(int *write, git_repository *repo, const char *name)
{
- git_config *config;
- int error, logall, is_bare;
-
- /* Defaults to the opposite of the repo being bare */
- is_bare = git_repository_is_bare(repo);
- logall = !is_bare;
+ int error, logall;
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
+ if (error < 0)
return error;
- error = git_config_get_bool(&logall, config, "core.logallrefupdates");
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
+ /* Defaults to the opposite of the repo being bare */
+ if (logall == GIT_LOGALLREFUPDATES_UNSET)
+ logall = !git_repository_is_bare(repo);
if (!logall) {
*write = 0;
diff --git a/src/refs.c b/src/refs.c
index 9428f617d..1603876da 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -159,8 +159,7 @@ int git_reference_name_to_id(
}
static int reference_normalize_for_repo(
- char *out,
- size_t out_size,
+ git_refname_t out,
git_repository *repo,
const char *name)
{
@@ -171,7 +170,7 @@ static int reference_normalize_for_repo(
precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
- return git_reference_normalize_name(out, out_size, name, flags);
+ return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
}
int git_reference_lookup_resolved(
@@ -180,7 +179,7 @@ int git_reference_lookup_resolved(
const char *name,
int max_nesting)
{
- char scan_name[GIT_REFNAME_MAX];
+ git_refname_t scan_name;
git_ref_t scan_type;
int error = 0, nesting;
git_reference *ref = NULL;
@@ -197,8 +196,7 @@ int git_reference_lookup_resolved(
scan_type = GIT_REF_SYMBOLIC;
- if ((error = reference_normalize_for_repo(
- scan_name, sizeof(scan_name), repo, name)) < 0)
+ if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
return error;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
@@ -354,7 +352,7 @@ static int reference__create(
const git_oid *old_id,
const char *old_target)
{
- char normalized[GIT_REFNAME_MAX];
+ git_refname_t normalized;
git_refdb *refdb;
git_reference *ref = NULL;
int error = 0;
@@ -365,7 +363,7 @@ static int reference__create(
if (ref_out)
*ref_out = NULL;
- error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name);
+ error = reference_normalize_for_repo(normalized, repo, name);
if (error < 0)
return error;
@@ -388,15 +386,14 @@ static int reference__create(
return -1;
}
- ref = git_reference__alloc(name, oid, NULL);
+ ref = git_reference__alloc(normalized, oid, NULL);
} else {
- char normalized_target[GIT_REFNAME_MAX];
+ git_refname_t normalized_target;
- if ((error = git_reference__normalize_name_lax(
- normalized_target, sizeof(normalized_target), symbolic)) < 0)
+ if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
return error;
- ref = git_reference__alloc_symbolic(name, normalized_target);
+ ref = git_reference__alloc_symbolic(normalized, normalized_target);
}
GITERR_CHECK_ALLOC(ref);
@@ -569,18 +566,14 @@ int git_reference_symbolic_set_target(
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
const git_signature *signature, const char *message)
{
- unsigned int normalization_flags;
- char normalized[GIT_REFNAME_MAX];
+ git_refname_t normalized;
bool should_head_be_updated = false;
int error = 0;
assert(ref && new_name && signature);
- normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
- GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
-
- if ((error = git_reference_normalize_name(
- normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
+ if ((error = reference_normalize_for_repo(
+ normalized, git_reference_owner(ref), new_name)) < 0)
return error;
@@ -590,12 +583,12 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
should_head_be_updated = (error > 0);
- if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0)
+ if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
return error;
/* Update HEAD it was pointing to the reference being renamed */
if (should_head_be_updated &&
- (error = git_repository_set_head(ref->db->repo, new_name, signature, message)) < 0) {
+ (error = git_repository_set_head(ref->db->repo, normalized, signature, message)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
return error;
}
@@ -1018,17 +1011,6 @@ cleanup:
return error;
}
-int git_reference__normalize_name_lax(
- char *buffer_out,
- size_t out_size,
- const char *name)
-{
- return git_reference_normalize_name(
- buffer_out,
- out_size,
- name,
- GIT_REF_FORMAT_ALLOW_ONELEVEL);
-}
#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
int git_reference_cmp(
diff --git a/src/refs.h b/src/refs.h
index d57d67026..2e79bdfca 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -35,7 +35,7 @@
#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
-#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD"
+#define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD"
#define GIT_BISECT_LOG_FILE "BISECT_LOG"
#define GIT_REBASE_MERGE_DIR "rebase-merge/"
#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
@@ -51,6 +51,8 @@
#define GIT_REFNAME_MAX 1024
+typedef char git_refname_t[GIT_REFNAME_MAX];
+
struct git_reference {
git_refdb *db;
git_ref_t type;
@@ -61,12 +63,11 @@ struct git_reference {
} target;
git_oid peel;
- char name[0];
+ char name[GIT_FLEX_ARRAY];
};
git_reference *git_reference__set_name(git_reference *ref, const char *name);
-int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *signature, const char *log_message);
int git_reference__is_valid_name(const char *refname, unsigned int flags);
diff --git a/src/refspec.c b/src/refspec.c
index fa60aa7aa..9f0df35a7 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -181,39 +181,75 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
static int refspec_transform(
git_buf *out, const char *from, const char *to, const char *name)
{
- size_t to_len = to ? strlen(to) : 0;
- size_t from_len = from ? strlen(from) : 0;
- size_t name_len = name ? strlen(name) : 0;
+ const char *from_star, *to_star;
+ const char *name_slash, *from_slash;
+ size_t replacement_len, star_offset;
git_buf_sanitize(out);
+ git_buf_clear(out);
- if (git_buf_set(out, to, to_len) < 0)
- return -1;
+ /*
+ * There are two parts to each side of a refspec, the bit
+ * before the star and the bit after it. The star can be in
+ * the middle of the pattern, so we need to look at each bit
+ * individually.
+ */
+ from_star = strchr(from, '*');
+ to_star = strchr(to, '*');
- if (to_len > 0) {
- /* No '*' at the end of 'to' means that refspec is mapped to one
- * specific branch, so no actual transformation is needed.
- */
- if (out->ptr[to_len - 1] != '*')
- return 0;
- git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */
- }
+ assert(from_star && to_star);
+
+ /* star offset, both in 'from' and in 'name' */
+ star_offset = from_star - from;
- if (from_len > 0) /* ignore trailing '*' from 'from' */
- from_len--;
- if (from_len > name_len)
- from_len = name_len;
+ /* the first half is copied over */
+ git_buf_put(out, to, to_star - to);
- return git_buf_put(out, name + from_len, name_len - from_len);
+ /* then we copy over the replacement, from the star's offset to the next slash in 'name' */
+ name_slash = strchr(name + star_offset, '/');
+ if (!name_slash)
+ name_slash = strrchr(name, '\0');
+
+ /* if there is no slash after the star in 'from', we want to copy everything over */
+ from_slash = strchr(from + star_offset, '/');
+ if (!from_slash)
+ name_slash = strrchr(name, '\0');
+
+ replacement_len = (name_slash - name) - star_offset;
+ git_buf_put(out, name + star_offset, replacement_len);
+
+ return git_buf_puts(out, to_star + 1);
}
int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name)
{
+ assert(out && spec && name);
+ git_buf_sanitize(out);
+
+ if (!git_refspec_src_matches(spec, name)) {
+ giterr_set(GITERR_INVALID, "ref '%s' doesn't match the source", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_buf_puts(out, spec->dst);
+
return refspec_transform(out, spec->src, spec->dst, name);
}
int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name)
{
+ assert(out && spec && name);
+ git_buf_sanitize(out);
+
+ if (!git_refspec_dst_matches(spec, name)) {
+ giterr_set(GITERR_INVALID, "ref '%s' doesn't match the destination", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_buf_puts(out, spec->src);
+
return refspec_transform(out, spec->dst, spec->src, name);
}
diff --git a/src/remote.c b/src/remote.c
index ea638e373..dfad946d5 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -73,13 +73,15 @@ static int ensure_remote_name_is_valid(const char *name)
if (!git_remote_is_valid_name(name)) {
giterr_set(
GITERR_CONFIG,
- "'%s' is not a valid remote name.", name);
+ "'%s' is not a valid remote name.", name ? name : "(null)");
error = GIT_EINVALIDSPEC;
}
return error;
}
+#if 0
+/* We could export this as a helper */
static int get_check_cert(int *out, git_repository *repo)
{
git_config *cfg;
@@ -105,6 +107,7 @@ static int get_check_cert(int *out, git_repository *repo)
*out = git_config__get_bool_force(cfg, "http.sslverify", 1);
return 0;
}
+#endif
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
@@ -121,9 +124,6 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
remote->repo = repo;
remote->update_fetchhead = 1;
- if (get_check_cert(&remote->check_cert, repo) < 0)
- goto on_error;
-
if (git_vector_init(&remote->refs, 32, NULL) < 0)
goto on_error;
@@ -267,12 +267,13 @@ int git_remote_dup(git_remote **dest, git_remote *source)
if (source->pushurl != NULL) {
remote->pushurl = git__strdup(source->pushurl);
- GITERR_CHECK_ALLOC(remote->pushurl);
+ GITERR_CHECK_ALLOC(remote->pushurl);
}
+ remote->transport_cb = source->transport_cb;
+ remote->transport_cb_payload = source->transport_cb_payload;
remote->repo = source->repo;
remote->download_tags = source->download_tags;
- remote->check_cert = source->check_cert;
remote->update_fetchhead = source->update_fetchhead;
if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
@@ -356,8 +357,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if ((error = ensure_remote_name_is_valid(name)) < 0)
return error;
- if (git_repository_config__weakptr(&config, repo) < 0)
- return -1;
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ return error;
remote = git__malloc(sizeof(git_remote));
GITERR_CHECK_ALLOC(remote);
@@ -367,9 +368,6 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
remote->name = git__strdup(name);
GITERR_CHECK_ALLOC(remote->name);
- if ((error = get_check_cert(&remote->check_cert, repo)) < 0)
- goto cleanup;
-
if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
@@ -403,6 +401,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if (!optional_setting_found) {
error = GIT_ENOTFOUND;
+ giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
goto cleanup;
}
@@ -437,6 +436,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
*out = remote;
cleanup:
+ git_config_free(config);
git_buf_free(&buf);
if (error < 0)
@@ -657,18 +657,21 @@ int git_remote_connect(git_remote *remote, git_direction direction)
return -1;
}
- /* A transport could have been supplied in advance with
- * git_remote_set_transport */
+ /* If we don't have a transport object yet, and the caller specified a
+ * custom transport factory, use that */
+ if (!t && remote->transport_cb &&
+ (error = remote->transport_cb(&t, remote, remote->transport_cb_payload)) < 0)
+ return error;
+
+ /* If we still don't have a transport, then use the global
+ * transport registrations which map URI schemes to transport factories */
if (!t && (error = git_transport_new(&t, remote, url)) < 0)
return error;
if (t->set_callbacks &&
- (error = t->set_callbacks(t, remote->callbacks.sideband_progress, NULL, remote->callbacks.payload)) < 0)
+ (error = t->set_callbacks(t, remote->callbacks.sideband_progress, NULL, remote->callbacks.certificate_check, remote->callbacks.payload)) < 0)
goto on_error;
- if (!remote->check_cert)
- flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
-
if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0)
goto on_error;
@@ -689,6 +692,11 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote
{
assert(remote);
+ if (!remote->transport) {
+ giterr_set(GITERR_NET, "No transport bound to this remote");
+ return -1;
+ }
+
return remote->transport->ls(out, size, remote->transport);
}
@@ -1050,16 +1058,20 @@ static int update_tips_for_spec(
if (autotag && !git_odb_exists(odb, &head->oid))
continue;
- if (git_vector_insert(&update_heads, head) < 0)
+ if (!autotag && git_vector_insert(&update_heads, head) < 0)
goto on_error;
error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND)
goto on_error;
- if (error == GIT_ENOTFOUND)
+ if (error == GIT_ENOTFOUND) {
memset(&old, 0, GIT_OID_RAWSZ);
+ if (autotag && git_vector_insert(&update_heads, head) < 0)
+ goto on_error;
+ }
+
if (!git_oid__cmp(&old, &head->oid))
continue;
@@ -1229,13 +1241,6 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
return 0;
}
-void git_remote_check_cert(git_remote *remote, int check)
-{
- assert(remote);
-
- remote->check_cert = check;
-}
-
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
{
assert(remote && callbacks);
@@ -1248,6 +1253,7 @@ int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *cal
return remote->transport->set_callbacks(remote->transport,
remote->callbacks.sideband_progress,
NULL,
+ remote->callbacks.certificate_check,
remote->callbacks.payload);
return 0;
@@ -1260,18 +1266,20 @@ const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote)
return &remote->callbacks;
}
-int git_remote_set_transport(git_remote *remote, git_transport *transport)
+int git_remote_set_transport(
+ git_remote *remote,
+ git_transport_cb transport_cb,
+ void *payload)
{
- assert(remote && transport);
-
- GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+ assert(remote);
if (remote->transport) {
giterr_set(GITERR_NET, "A transport is already bound to this remote");
return -1;
}
- remote->transport = transport;
+ remote->transport_cb = transport_cb;
+ remote->transport_cb_payload = payload;
return 0;
}
@@ -1303,13 +1311,14 @@ static int rename_remote_config_section(
if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
goto cleanup;
- if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
- goto cleanup;
+ if (new_name &&
+ (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
+ goto cleanup;
error = git_config_rename_section(
repo,
git_buf_cstr(&old_section_name),
- git_buf_cstr(&new_section_name));
+ new_name ? git_buf_cstr(&new_section_name) : NULL);
cleanup:
git_buf_free(&old_section_name);
@@ -1356,19 +1365,24 @@ static int update_branch_remote_config_entry(
}
static int rename_one_remote_reference(
- git_reference *reference,
+ git_reference *reference_in,
const char *old_remote_name,
const char *new_remote_name)
{
int error;
+ git_reference *ref = NULL, *dummy = NULL;
+ git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT;
git_buf new_name = GIT_BUF_INIT;
git_buf log_message = GIT_BUF_INIT;
+ size_t pfx_len;
+ const char *target;
- if ((error = git_buf_printf(
- &new_name,
- GIT_REFS_REMOTES_DIR "%s%s",
- new_remote_name,
- reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name))) < 0)
+ if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
+ return error;
+
+ pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
+ git_buf_puts(&new_name, namespace.ptr);
+ if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
goto cleanup;
if ((error = git_buf_printf(&log_message,
@@ -1376,12 +1390,36 @@ static int rename_one_remote_reference(
old_remote_name, new_remote_name)) < 0)
goto cleanup;
- error = git_reference_rename(
- NULL, reference, git_buf_cstr(&new_name), 0,
- NULL, git_buf_cstr(&log_message));
- git_reference_free(reference);
+ if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1,
+ NULL, git_buf_cstr(&log_message))) < 0)
+ goto cleanup;
+
+ if (git_reference_type(ref) != GIT_REF_SYMBOLIC)
+ goto cleanup;
+
+ /* Handle refs like origin/HEAD -> origin/master */
+ target = git_reference_symbolic_target(ref);
+ if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
+ goto cleanup;
+
+ if (git__prefixcmp(target, old_namespace.ptr))
+ goto cleanup;
+
+ git_buf_clear(&new_name);
+ git_buf_puts(&new_name, namespace.ptr);
+ if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0)
+ goto cleanup;
+
+ error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name),
+ NULL, git_buf_cstr(&log_message));
+
+ git_reference_free(dummy);
cleanup:
+ git_reference_free(reference_in);
+ git_reference_free(ref);
+ git_buf_free(&namespace);
+ git_buf_free(&old_namespace);
git_buf_free(&new_name);
git_buf_free(&log_message);
return error;
@@ -1393,18 +1431,20 @@ static int rename_remote_references(
const char *new_name)
{
int error;
+ git_buf buf = GIT_BUF_INIT;
git_reference *ref;
git_reference_iterator *iter;
- if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
return error;
- while ((error = git_reference_next(&ref, iter)) == 0) {
- if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
- git_reference_free(ref);
- continue;
- }
+ error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+
+ if (error < 0)
+ return error;
+ while ((error = git_reference_next(&ref, iter)) == 0) {
if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
break;
}
@@ -1414,11 +1454,7 @@ static int rename_remote_references(
return (error == GIT_ITEROVER) ? 0 : error;
}
-static int rename_fetch_refspecs(
- git_remote *remote,
- const char *new_name,
- int (*callback)(const char *problematic_refspec, void *payload),
- void *payload)
+static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
{
git_config *config;
git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
@@ -1429,6 +1465,9 @@ static int rename_fetch_refspecs(
if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
return error;
+ if ((error = git_vector_init(problems, 1, NULL)) < 0)
+ return error;
+
if ((error = git_buf_printf(
&base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
return error;
@@ -1437,15 +1476,15 @@ static int rename_fetch_refspecs(
if (spec->push)
continue;
- /* Every refspec is a problem refspec for an anonymous remote, OR */
/* Does the dst part of the refspec follow the expected format? */
- if (!remote->name ||
- strcmp(git_buf_cstr(&base), spec->string)) {
+ if (strcmp(git_buf_cstr(&base), spec->string)) {
+ char *dup;
- if ((error = callback(spec->string, payload)) != 0) {
- giterr_set_after_callback(error);
+ dup = git__strdup(spec->string);
+ GITERR_CHECK_ALLOC(dup);
+
+ if ((error = git_vector_insert(problems, dup)) < 0)
break;
- }
continue;
}
@@ -1471,18 +1510,25 @@ static int rename_fetch_refspecs(
git_buf_free(&base);
git_buf_free(&var);
git_buf_free(&val);
+
+ if (error < 0) {
+ char *str;
+ git_vector_foreach(problems, i, str)
+ git__free(str);
+
+ git_vector_free(problems);
+ }
+
return error;
}
-int git_remote_rename(
- git_remote *remote,
- const char *new_name,
- git_remote_rename_problem_cb callback,
- void *payload)
+int git_remote_rename(git_strarray *out, git_remote *remote, const char *new_name)
{
int error;
+ git_vector problem_refspecs;
+ char *tmp, *dup;
- assert(remote && new_name);
+ assert(out && remote && new_name);
if (!remote->name) {
giterr_set(GITERR_INVALID, "Can't rename an anonymous remote.");
@@ -1492,54 +1538,30 @@ int git_remote_rename(
if ((error = ensure_remote_name_is_valid(new_name)) < 0)
return error;
- if (remote->repo) {
- if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
- return error;
+ if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
+ return error;
- if (!remote->name) {
- if ((error = rename_fetch_refspecs(
- remote,
- new_name,
- callback,
- payload)) < 0)
- return error;
+ if ((error = rename_remote_config_section(remote->repo, remote->name, new_name)) < 0)
+ return error;
- remote->name = git__strdup(new_name);
- GITERR_CHECK_ALLOC(remote->name);
+ if ((error = update_branch_remote_config_entry(remote->repo, remote->name, new_name)) < 0)
+ return error;
- return git_remote_save(remote);
- }
+ if ((error = rename_remote_references(remote->repo, remote->name, new_name)) < 0)
+ return error;
- if ((error = rename_remote_config_section(
- remote->repo,
- remote->name,
- new_name)) < 0)
- return error;
-
- if ((error = update_branch_remote_config_entry(
- remote->repo,
- remote->name,
- new_name)) < 0)
- return error;
-
- if ((error = rename_remote_references(
- remote->repo,
- remote->name,
- new_name)) < 0)
- return error;
-
- if ((error = rename_fetch_refspecs(
- remote,
- new_name,
- callback,
- payload)) < 0)
- return error;
- }
+ if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
+ return error;
- git__free(remote->name);
+ out->count = problem_refspecs.length;
+ out->strings = (char **) problem_refspecs.contents;
- remote->name = git__strdup(new_name);
- GITERR_CHECK_ALLOC(remote->name);
+ dup = git__strdup(new_name);
+ GITERR_CHECK_ALLOC(dup);
+
+ tmp = remote->name;
+ remote->name = dup;
+ git__free(tmp);
return 0;
}
@@ -1736,14 +1758,229 @@ const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
return git_vector_get(&remote->refspecs, n);
}
-int git_remote_init_callbacks(git_remote_callbacks* opts, int version)
+int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
+ return 0;
+}
+
+/* asserts a branch.<foo>.remote format */
+static const char *name_offset(size_t *len_out, const char *name)
+{
+ size_t prefix_len;
+ const char *dot;
+
+ prefix_len = strlen("remote.");
+ dot = strchr(name + prefix_len, '.');
+
+ assert(dot);
+
+ *len_out = dot - name - prefix_len;
+ return name + prefix_len;
+}
+
+static int remove_branch_config_related_entries(
+ git_repository *repo,
+ const char *remote_name)
+{
+ int error;
+ git_config *config;
+ git_config_entry *entry;
+ git_config_iterator *iter;
+ git_buf buf = GIT_BUF_INIT;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
+ return error;
+
+ /* find any branches with us as upstream and remove that config */
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ const char *branch;
+ size_t branch_len;
+
+ if (strcmp(remote_name, entry->value))
+ continue;
+
+ branch = name_offset(&branch_len, entry->name);
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
+ break;
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
+ break;
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_buf_free(&buf);
+ git_config_iterator_free(iter);
+ return error;
+}
+
+static int remove_refs(git_repository *repo, const git_refspec *spec)
+{
+ git_reference_iterator *iter = NULL;
+ git_vector refs;
+ const char *name;
+ char *dup;
+ int error;
+ size_t i;
+
+ if ((error = git_vector_init(&refs, 8, NULL)) < 0)
+ return error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ goto cleanup;
+
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ if (!git_refspec_dst_matches(spec, name))
+ continue;
+
+ dup = git__strdup(name);
+ if (!dup) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_vector_insert(&refs, dup)) < 0)
+ goto cleanup;
+ }
+ if (error == GIT_ITEROVER)
+ error = 0;
+ if (error < 0)
+ goto cleanup;
+
+ git_vector_foreach(&refs, i, name) {
+ if ((error = git_reference_remove(repo, name)) < 0)
+ break;
+ }
+
+cleanup:
+ git_reference_iterator_free(iter);
+ git_vector_foreach(&refs, i, dup) {
+ git__free(dup);
+ }
+ git_vector_free(&refs);
+ return error;
+}
+
+static int remove_remote_tracking(git_repository *repo, const char *remote_name)
+{
+ git_remote *remote;
+ int error;
+ size_t i, count;
+
+ /* we want to use what's on the config, regardless of changes to the instance in memory */
+ if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
+ return error;
+
+ count = git_remote_refspec_count(remote);
+ for (i = 0; i < count; i++) {
+ const git_refspec *refspec = git_remote_get_refspec(remote, i);
+
+ /* shouldn't ever actually happen */
+ if (refspec == NULL)
+ continue;
+
+ if ((error = remove_refs(repo, refspec)) < 0)
+ break;
+ }
+
+ git_remote_free(remote);
+ return error;
+}
+
+int git_remote_delete(git_remote *remote)
{
- if (version != GIT_REMOTE_CALLBACKS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_remote_callbacks", version);
+ int error;
+ git_repository *repo;
+
+ assert(remote);
+
+ if (!remote->name) {
+ giterr_set(GITERR_INVALID, "Can't delete an anonymous remote.");
return -1;
- } else {
- git_remote_callbacks o = GIT_REMOTE_CALLBACKS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
}
+
+ repo = git_remote_owner(remote);
+
+ if ((error = remove_branch_config_related_entries(repo,
+ git_remote_name(remote))) < 0)
+ return error;
+
+ if ((error = remove_remote_tracking(repo, git_remote_name(remote))) < 0)
+ return error;
+
+ if ((error = rename_remote_config_section(
+ repo, git_remote_name(remote), NULL)) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_remote_default_branch(git_buf *out, git_remote *remote)
+{
+ const git_remote_head **heads;
+ const git_remote_head *guess = NULL;
+ const git_oid *head_id;
+ size_t heads_len, i;
+ int error;
+
+ assert(out);
+
+ if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
+ return error;
+
+ if (heads_len == 0)
+ return GIT_ENOTFOUND;
+
+ if (strcmp(heads[0]->name, GIT_HEAD_FILE))
+ return GIT_ENOTFOUND;
+
+ git_buf_sanitize(out);
+ /* the first one must be HEAD so if that has the symref info, we're done */
+ if (heads[0]->symref_target)
+ return git_buf_puts(out, heads[0]->symref_target);
+
+ /*
+ * If there's no symref information, we have to look over them
+ * and guess. We return the first match unless the master
+ * branch is a candidate. Then we return the master branch.
+ */
+ head_id = &heads[0]->oid;
+
+ for (i = 1; i < heads_len; i++) {
+ if (git_oid_cmp(head_id, &heads[i]->oid))
+ continue;
+
+ if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
+ continue;
+
+ if (!guess) {
+ guess = heads[i];
+ continue;
+ }
+
+ if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
+ guess = heads[i];
+ break;
+ }
+ }
+
+ if (!guess)
+ return GIT_ENOTFOUND;
+
+ return git_buf_puts(out, guess->name);
}
diff --git a/src/remote.h b/src/remote.h
index 4164a14b3..f88601e9b 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -9,6 +9,7 @@
#include "git2/remote.h"
#include "git2/transport.h"
+#include "git2/sys/transport.h"
#include "refspec.h"
#include "vector.h"
@@ -22,13 +23,14 @@ struct git_remote {
git_vector refs;
git_vector refspecs;
git_vector active_refspecs;
+ git_transport_cb transport_cb;
+ void *transport_cb_payload;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
git_transfer_progress stats;
unsigned int need_pack;
git_remote_autotag_option_t download_tags;
- int check_cert;
int update_fetchhead;
};
diff --git a/src/repository.c b/src/repository.c
index 8daa04d5d..51d39eb6d 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -169,13 +169,9 @@ int git_repository_new(git_repository **out)
return 0;
}
-static int load_config_data(git_repository *repo)
+static int load_config_data(git_repository *repo, const git_config *config)
{
int is_bare;
- git_config *config;
-
- if (git_repository_config__weakptr(&config, repo) < 0)
- return -1;
/* Try to figure out if it's bare, default to non-bare if it's not set */
if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
@@ -186,19 +182,15 @@ static int load_config_data(git_repository *repo)
return 0;
}
-static int load_workdir(git_repository *repo, git_buf *parent_path)
+static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path)
{
int error;
- git_config *config;
const git_config_entry *ce;
git_buf worktree = GIT_BUF_INIT;
if (repo->is_bare)
return 0;
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
if ((error = git_config__lookup_entry(
&ce, config, "core.worktree", false)) < 0)
return error;
@@ -467,16 +459,22 @@ int git_repository_open_ext(
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
repo->is_bare = 1;
- else if ((error = load_config_data(repo)) < 0 ||
- (error = load_workdir(repo, &parent)) < 0)
- {
- git_repository_free(repo);
- return error;
+ else {
+ git_config *config = NULL;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0 ||
+ (error = load_config_data(repo, config)) < 0 ||
+ (error = load_workdir(repo, config, &parent)) < 0)
+ git_repository_free(repo);
+
+ git_config_free(config);
}
+ if (!error)
+ *repo_ptr = repo;
git_buf_free(&parent);
- *repo_ptr = repo;
- return 0;
+
+ return error;
}
int git_repository_open(git_repository **repo_out, const char *path)
@@ -627,6 +625,17 @@ int git_repository_config(git_config **out, git_repository *repo)
return 0;
}
+int git_repository_config_snapshot(git_config **out, git_repository *repo)
+{
+ int error;
+ git_config *weak;
+
+ if ((error = git_repository_config__weakptr(&weak, repo)) < 0)
+ return error;
+
+ return git_config_snapshot(out, weak);
+}
+
void git_repository_set_config(git_repository *repo, git_config *config)
{
assert(repo && config);
@@ -880,60 +889,6 @@ static bool are_symlinks_supported(const char *wd_path)
return symlinks_supported;
}
-#ifdef GIT_USE_ICONV
-
-static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
-static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
-
-/* Check if the platform is decomposing unicode data for us. We will
- * emulate core Git and prefer to use precomposed unicode data internally
- * on these platforms, composing the decomposed unicode on the fly.
- *
- * This mainly happens on the Mac where HDFS stores filenames as
- * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
- * return decomposed unicode from readdir() even when the actual
- * filesystem is storing precomposed unicode.
- */
-static bool does_fs_decompose_unicode_paths(const char *wd_path)
-{
- git_buf path = GIT_BUF_INIT;
- int fd;
- bool found_decomposed = false;
- char tmp[6];
-
- /* Create a file using a precomposed path and then try to find it
- * using the decomposed name. If the lookup fails, then we will mark
- * that we should precompose unicode for this repository.
- */
- if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
- (fd = p_mkstemp(path.ptr)) < 0)
- goto done;
- p_close(fd);
-
- /* record trailing digits generated by mkstemp */
- memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
-
- /* try to look up as NFD path */
- if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
- goto done;
- memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
-
- found_decomposed = git_path_exists(path.ptr);
-
- /* remove temporary file (using original precomposed path) */
- if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
- goto done;
- memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
-
- (void)p_unlink(path.ptr);
-
-done:
- git_buf_free(&path);
- return found_decomposed;
-}
-
-#endif
-
static int create_empty_file(const char *path, mode_t mode)
{
int fd;
@@ -1024,8 +979,9 @@ static int repo_init_fs_configs(
#ifdef GIT_USE_ICONV
if ((error = git_config_set_bool(
cfg, "core.precomposeunicode",
- does_fs_decompose_unicode_paths(work_dir))) < 0)
+ git_path_does_fs_decompose_unicode(work_dir))) < 0)
return error;
+ /* on non-iconv platforms, don't even set core.precomposeunicode */
#endif
return 0;
@@ -1038,7 +994,7 @@ static int repo_init_config(
uint32_t mode)
{
int error = 0;
- git_buf cfg_path = GIT_BUF_INIT;
+ git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
@@ -1063,9 +1019,16 @@ static int repo_init_config(
if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
- SET_REPO_CONFIG(string, "core.worktree", work_dir);
- else if (is_reinit) {
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if ((error = git_buf_sets(&worktree_path, work_dir)) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
+ if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0)
+ goto cleanup;
+
+ SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
+ } else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
@@ -1082,6 +1045,7 @@ static int repo_init_config(
cleanup:
git_buf_free(&cfg_path);
+ git_buf_free(&worktree_path);
git_config_free(config);
return error;
@@ -1170,10 +1134,11 @@ static int repo_write_template(
}
static int repo_write_gitlink(
- const char *in_dir, const char *to_repo)
+ const char *in_dir, const char *to_repo, bool use_relative_path)
{
int error;
git_buf buf = GIT_BUF_INIT;
+ git_buf path_to_repo = GIT_BUF_INIT;
struct stat st;
git_path_dirname_r(&buf, to_repo);
@@ -1201,13 +1166,20 @@ static int repo_write_gitlink(
git_buf_clear(&buf);
- error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
+ error = git_buf_sets(&path_to_repo, to_repo);
+
+ if (!error && use_relative_path)
+ error = git_path_make_relative(&path_to_repo, in_dir);
+
+ if (!error)
+ error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
if (!error)
error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
cleanup:
git_buf_free(&buf);
+ git_buf_free(&path_to_repo);
return error;
}
@@ -1234,6 +1206,7 @@ static int repo_init_structure(
bool external_tpl =
((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
mode_t dmode = pick_dir_mode(opts);
+ bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
/* Hide the ".git" directory */
#ifdef GIT_WIN32
@@ -1250,7 +1223,7 @@ static int repo_init_structure(
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
{
- if (repo_write_gitlink(work_dir, repo_dir) < 0)
+ if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
return -1;
}
@@ -1274,10 +1247,12 @@ static int repo_init_structure(
default_template = true;
}
- if (tdir)
- error = git_futils_cp_r(tdir, repo_dir,
- GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
- GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+ if (tdir) {
+ uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE;
+ if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
+ cpflags |= GIT_CPDIR_CHMOD_DIRS;
+ error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
+ }
git_buf_free(&template_buf);
git_config_free(cfg);
@@ -1298,9 +1273,14 @@ static int repo_init_structure(
* - only create files if no external template was specified
*/
for (tpl = repo_template; !error && tpl->path; ++tpl) {
- if (!tpl->content)
+ if (!tpl->content) {
+ uint32_t mkdir_flags = GIT_MKDIR_PATH;
+ if (chmod)
+ mkdir_flags |= GIT_MKDIR_CHMOD;
+
error = git_futils_mkdir(
- tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
+ tpl->path, repo_dir, dmode, mkdir_flags);
+ }
else if (!external_tpl) {
const char *content = tpl->content;
@@ -1671,7 +1651,7 @@ int git_repository_set_workdir(
if (git_repository_config__weakptr(&config, repo) < 0)
return -1;
- error = repo_write_gitlink(path.ptr, git_repository_path(repo));
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
/* passthrough error means gitlink is unnecessary */
if (error == GIT_PASSTHROUGH)
@@ -1701,6 +1681,32 @@ int git_repository_is_bare(git_repository *repo)
return repo->is_bare;
}
+int git_repository_set_bare(git_repository *repo)
+{
+ int error;
+ git_config *config;
+
+ assert(repo);
+
+ if (repo->is_bare)
+ return 0;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_set_bool(config, "core.bare", false)) < 0)
+ return error;
+
+ if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0)
+ return error;
+
+ git__free(repo->workdir);
+ repo->workdir = NULL;
+ repo->is_bare = 1;
+
+ return 0;
+}
+
int git_repository_head_tree(git_tree **tree, git_repository *repo)
{
git_reference *head;
@@ -1789,7 +1795,8 @@ int git_repository_hashfile(
/* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) {
error = git_filter_list_load(
- &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
+ &fl, repo, NULL, as_path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT);
if (error < 0)
return error;
} else {
@@ -1950,8 +1957,8 @@ int git_repository_state(git_repository *repo)
state = GIT_REPOSITORY_STATE_MERGE;
else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE))
state = GIT_REPOSITORY_STATE_REVERT;
- else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE))
- state = GIT_REPOSITORY_STATE_CHERRY_PICK;
+ else if(git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_CHERRYPICK;
else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
state = GIT_REPOSITORY_STATE_BISECT;
@@ -1993,7 +2000,7 @@ static const char *state_files[] = {
GIT_MERGE_MODE_FILE,
GIT_MERGE_MSG_FILE,
GIT_REVERT_HEAD_FILE,
- GIT_CHERRY_PICK_HEAD_FILE,
+ GIT_CHERRYPICK_HEAD_FILE,
GIT_BISECT_LOG_FILE,
GIT_REBASE_MERGE_DIR,
GIT_REBASE_APPLY_DIR,
@@ -2026,14 +2033,11 @@ int git_repository_is_shallow(git_repository *repo)
return st.st_size == 0 ? 0 : 1;
}
-int git_repository_init_init_options(git_repository_init_options* opts, int version)
+int git_repository_init_init_options(
+ git_repository_init_options *opts, unsigned int version)
{
- if (version != GIT_REPOSITORY_INIT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_repository_init_options", version);
- return -1;
- } else {
- git_repository_init_options o = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_repository_init_options,
+ GIT_REPOSITORY_INIT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/repository.h b/src/repository.h
index 27eec9dd8..aba16a016 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -39,6 +39,7 @@ typedef enum {
GIT_CVAR_ABBREV, /* core.abbrev */
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
+ GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
@@ -92,6 +93,9 @@ typedef enum {
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
/* core.safecrlf */
GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE,
+ /* core.logallrefupdates */
+ GIT_LOGALLREFUPDATES_UNSET = 2,
+ GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
} git_cvar_value;
/* internal repository init flags */
diff --git a/src/revert.c b/src/revert.c
index 29e124f6c..36560a77c 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -174,7 +174,7 @@ int git_revert(
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
int error;
assert(repo && commit);
@@ -199,10 +199,9 @@ int git_revert(
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
(error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
goto on_error;
goto done;
@@ -212,7 +211,6 @@ on_error:
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
@@ -220,14 +218,9 @@ done:
return error;
}
-int git_revert_init_opts(git_revert_options* opts, int version)
+int git_revert_init_options(git_revert_options *opts, unsigned int version)
{
- if (version != GIT_REVERT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_revert_options", version);
- return -1;
- } else {
- git_revert_options o = GIT_REVERT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/revparse.c b/src/revparse.c
index 60872e187..e0ec3941d 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -205,7 +205,6 @@ cleanup:
static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
{
git_reflog *reflog;
- int error = -1;
size_t numentries;
const git_reflog_entry *entry;
bool search_by_pos = (identifier <= 100000000);
@@ -216,21 +215,11 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
numentries = git_reflog_entrycount(reflog);
if (search_by_pos) {
- if (numentries < identifier + 1) {
- giterr_set(
- GITERR_REFERENCE,
- "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
- git_reference_name(ref), numentries, identifier);
-
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
+ if (numentries < identifier + 1)
+ goto notfound;
entry = git_reflog_entry_byindex(reflog, identifier);
git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- error = 0;
- goto cleanup;
-
} else {
size_t i;
git_time commit_time;
@@ -243,16 +232,24 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
continue;
git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- error = 0;
- goto cleanup;
+ break;
}
- error = GIT_ENOTFOUND;
+ if (i == numentries)
+ goto notfound;
}
-cleanup:
git_reflog_free(reflog);
- return error;
+ return 0;
+
+notfound:
+ giterr_set(
+ GITERR_REFERENCE,
+ "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
+ git_reference_name(ref), numentries, identifier);
+
+ git_reflog_free(reflog);
+ return GIT_ENOTFOUND;
}
static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
diff --git a/src/revwalk.c b/src/revwalk.c
index 7aedd1f44..bd07d02cb 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -48,9 +48,6 @@ static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
assert(commit);
- git_array_alloc(pending);
- GITERR_CHECK_ARRAY(pending);
-
do {
commit->uninteresting = 1;
@@ -67,7 +64,7 @@ static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
tmp = git_array_pop(pending);
commit = tmp ? *tmp : NULL;
- } while (git_array_size(pending) > 0);
+ } while (commit != NULL);
git_array_clear(pending);
diff --git a/src/signature.c b/src/signature.c
index f501cd8b6..2a16b484a 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -70,9 +70,9 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
if (p->name == NULL || p->email == NULL)
return -1; /* oom */
- if (p->name[0] == '\0') {
+ if (p->name[0] == '\0' || p->email[0] == '\0') {
git_signature_free(p);
- return signature_error("Signature cannot have an empty name");
+ return signature_error("Signature cannot have an empty name or email");
}
p->when.time = time;
@@ -144,7 +144,7 @@ int git_signature_default(git_signature **out, git_repository *repo)
git_config *cfg;
const char *user_name, *user_email;
- if ((error = git_repository_config(&cfg, repo)) < 0)
+ if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
return error;
if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
diff --git a/src/stash.c b/src/stash.c
index 86e0a627c..caffd0cea 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -232,7 +232,8 @@ static int build_untracked_tree(
}
if (flags & GIT_STASH_INCLUDE_IGNORED) {
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_IGNORED_DIRS;
data.include_ignored = true;
}
@@ -447,10 +448,11 @@ static int ensure_there_are_changes_to_stash(
if (include_untracked_files)
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
if (include_ignored_files)
- opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
@@ -634,7 +636,8 @@ int git_stash_drop(
entry = git_reflog_entry_byindex(reflog, 0);
git_reference_free(stash);
- if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL) < 0))
+ error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL);
+ if (error < 0)
goto cleanup;
/* We need to undo the writing that we just did */
diff --git a/src/status.c b/src/status.c
index c4b990a84..cb2490042 100644
--- a/src/status.c
+++ b/src/status.c
@@ -62,6 +62,9 @@ static unsigned int workdir_delta2status(
case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW;
break;
+ case GIT_DELTA_UNREADABLE:
+ st = GIT_STATUS_WT_UNREADABLE;
+ break;
case GIT_DELTA_DELETED:
st = GIT_STATUS_WT_DELETED;
break;
@@ -81,15 +84,15 @@ static unsigned int workdir_delta2status(
if (git_oid_iszero(&idx2wd->old_file.id) &&
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
!git_diff__oid_for_file(
- diff->repo, idx2wd->old_file.path, idx2wd->old_file.mode,
- idx2wd->old_file.size, &idx2wd->old_file.id))
+ &idx2wd->old_file.id, diff, idx2wd->old_file.path,
+ idx2wd->old_file.mode, idx2wd->old_file.size))
idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
if (git_oid_iszero(&idx2wd->new_file.id) &&
diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
!git_diff__oid_for_file(
- diff->repo, idx2wd->new_file.path, idx2wd->new_file.mode,
- idx2wd->new_file.size, &idx2wd->new_file.id))
+ &idx2wd->new_file.id, diff, idx2wd->new_file.path,
+ idx2wd->new_file.mode, idx2wd->new_file.size))
idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id))
@@ -225,6 +228,28 @@ static git_status_list *git_status_list_alloc(git_index *index)
return status;
}
+static int status_validate_options(const git_status_options *opts)
+{
+ if (!opts)
+ return 0;
+
+ GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+
+ if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) {
+ giterr_set(GITERR_INVALID, "Unknown status 'show' option");
+ return -1;
+ }
+
+ if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 &&
+ (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) {
+ giterr_set(GITERR_INVALID, "Updating index from status "
+ "is not allowed when index refresh is disabled");
+ return -1;
+ }
+
+ return 0;
+}
+
int git_status_list_new(
git_status_list **out,
git_repository *repo,
@@ -240,11 +265,10 @@ int git_status_list_new(
int error = 0;
unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
- assert(show <= GIT_STATUS_SHOW_WORKDIR_ONLY);
-
*out = NULL;
- GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+ if (status_validate_options(opts) < 0)
+ return -1;
if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
(error = git_repository_index(&index, repo)) < 0)
@@ -287,6 +311,12 @@ int git_status_list_new(
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
+ if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED;
if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
findopt.flags = findopt.flags |
@@ -306,8 +336,9 @@ int git_status_list_new(
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
if ((error = git_diff_index_to_workdir(
- &status->idx2wd, repo, index, &diffopt)) < 0)
+ &status->idx2wd, repo, index, &diffopt)) < 0) {
goto done;
+ }
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
@@ -384,8 +415,9 @@ int git_status_foreach_ext(
size_t i;
int error = 0;
- if ((error = git_status_list_new(&status, repo, opts)) < 0)
+ if ((error = git_status_list_new(&status, repo, opts)) < 0) {
return error;
+ }
git_vector_foreach(&status->paired, i, status_entry) {
const char *path = status_entry->head_to_index ?
@@ -495,14 +527,31 @@ int git_status_should_ignore(
return git_ignore_path_is_ignored(ignored, repo, path);
}
-int git_status_init_options(git_status_options* opts, int version)
+int git_status_init_options(git_status_options *opts, unsigned int version)
{
- if (version != GIT_STATUS_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_status_options", version);
- return -1;
- } else {
- git_status_options o = GIT_STATUS_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT);
+ return 0;
+}
+
+int git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status)
+{
+ assert(out);
+ GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+
+ out->stat_calls = 0;
+ out->oid_calculations = 0;
+
+ if (status->head2idx) {
+ out->stat_calls += status->head2idx->perf.stat_calls;
+ out->oid_calculations += status->head2idx->perf.oid_calculations;
}
+ if (status->idx2wd) {
+ out->stat_calls += status->idx2wd->perf.stat_calls;
+ out->oid_calculations += status->idx2wd->perf.oid_calculations;
+ }
+
+ return 0;
}
+
diff --git a/src/strnlen.h b/src/strnlen.h
index fdd7fe39c..eecfe3c02 100644
--- a/src/strnlen.h
+++ b/src/strnlen.h
@@ -7,7 +7,8 @@
#ifndef INCLUDE_strlen_h__
#define INCLUDE_strlen_h__
-#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__)
+#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\
+ (defined(_MSC_VER) && _MSC_VER < 1500)
# define NO_STRNLEN
#endif
diff --git a/src/submodule.c b/src/submodule.c
index 5ddbfe828..ccc8ad117 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -306,6 +306,56 @@ void git_submodule_cache_free(git_repository *repo)
submodule_cache_free(cache);
}
+static int submodule_repo_init(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path,
+ const char *url,
+ bool use_gitlink)
+{
+ int error = 0;
+ git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
+ initopt.origin_url = url;
+
+ /* init submodule repository and add origin remote as needed */
+
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+ * gitlink in the sub-repo workdir directory to that repository
+ *
+ * Old style: sub-repo goes directly into repo/<name>/.git/
+ */
+ if (use_gitlink) {
+ error = git_buf_join3(
+ &repodir, '/', git_repository_path(parent_repo), "modules", path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+ initopt.flags |=
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+ } else
+ error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
+
+cleanup:
+ git_buf_free(&workdir);
+ git_buf_free(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
int git_submodule_add_setup(
git_submodule **out,
git_repository *repo,
@@ -317,7 +367,6 @@ int git_submodule_add_setup(
git_config_backend *mods = NULL;
git_submodule *sm = NULL;
git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
- git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_repository *subrepo = NULL;
assert(repo && url && path);
@@ -371,41 +420,14 @@ int git_submodule_add_setup(
if (error < 0)
goto cleanup;
- /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
- * gitlink in the sub-repo workdir directory to that repository
- *
- * Old style: sub-repo goes directly into repo/<name>/.git/
+ /* if the repo does not already exist, then init a new repo and add it.
+ * Otherwise, just add the existing repo.
*/
-
- initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_NO_REINIT;
- initopt.origin_url = real_url.ptr;
-
- if (git_path_exists(name.ptr) &&
- git_path_contains(&name, DOT_GIT))
- {
- /* repo appears to already exist - reinit? */
- }
- else if (use_gitlink) {
- git_buf repodir = GIT_BUF_INIT;
-
- error = git_buf_join3(
- &repodir, '/', git_repository_path(repo), "modules", path);
- if (error < 0)
+ if (!(git_path_exists(name.ptr) &&
+ git_path_contains(&name, DOT_GIT))) {
+ if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
goto cleanup;
-
- initopt.workdir_path = name.ptr;
- initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
-
- error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
-
- git_buf_free(&repodir);
}
- else {
- error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
- }
- if (error < 0)
- goto cleanup;
/* add submodule to hash and "reload" it */
@@ -437,6 +459,23 @@ cleanup:
return error;
}
+int git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink)
+{
+ int error;
+ git_repository *sub_repo = NULL;
+
+ assert(out && sm);
+
+ error = submodule_repo_init(&sub_repo, sm->repo, sm->path, sm->url, use_gitlink);
+
+ *out = sub_repo;
+
+ return error;
+}
+
int git_submodule_add_finalize(git_submodule *sm)
{
int error;
@@ -641,7 +680,9 @@ int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *ur
{
int error = 0;
- assert(url);
+ assert(out && repo && url);
+
+ git_buf_sanitize(out);
if (git_path_is_relative(url)) {
if (!(error = get_url_base(out, repo)))
@@ -1895,6 +1936,7 @@ static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
}
+
static void submodule_get_wd_status(
unsigned int *status,
git_submodule *sm,
diff --git a/src/sysdir.c b/src/sysdir.c
index aebf23135..cd94a8b57 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -90,6 +90,8 @@ void git_sysdir_global_shutdown(void)
int i;
for (i = 0; i < GIT_SYSDIR__MAX; ++i)
git_buf_free(&git_sysdir__dirs[i]);
+
+ git_sysdir__dirs_shutdown_set = 0;
}
static int git_sysdir_check_selector(git_sysdir_t which)
@@ -194,14 +196,19 @@ static int git_sysdir_find_in_dirlist(
const git_buf *syspath;
GITERR_CHECK_ERROR(git_sysdir_get(&syspath, which));
+ if (!syspath || !git_buf_len(syspath))
+ goto done;
for (scan = git_buf_cstr(syspath); scan; scan = next) {
- for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR);
- next && next > scan && next[-1] == '\\';
- next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR))
- /* find unescaped separator or end of string */;
+ /* find unescaped separator or end of string */
+ for (next = scan; *next; ++next) {
+ if (*next == GIT_PATH_LIST_SEPARATOR &&
+ (next <= scan || next[-1] != '\\'))
+ break;
+ }
- len = next ? (size_t)(next++ - scan) : strlen(scan);
+ len = (size_t)(next - scan);
+ next = (*next ? next + 1 : NULL);
if (!len)
continue;
@@ -213,6 +220,7 @@ static int git_sysdir_find_in_dirlist(
return 0;
}
+done:
git_buf_free(path);
giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
return GIT_ENOTFOUND;
diff --git a/src/tag.c b/src/tag.c
index 1a4ee1e1c..d7b531d34 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -363,20 +363,22 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
}
/* write the buffer */
- if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
- return -1;
+ if ((error = git_odb_open_wstream(
+ &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
+ return error;
- git_odb_stream_write(stream, buffer, strlen(buffer));
+ if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
+ error = git_odb_stream_finalize_write(oid, stream);
- error = git_odb_stream_finalize_write(oid, stream);
git_odb_stream_free(stream);
if (error < 0) {
git_buf_free(&ref_name);
- return -1;
+ return error;
}
- error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL);
+ error = git_reference_create(
+ &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL);
git_reference_free(new_ref);
git_buf_free(&ref_name);
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 50d8610a3..5511a5117 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -40,17 +40,17 @@ typedef git_atomic git_atomic_ssize;
#ifdef GIT_THREADS
-#define git_thread pthread_t
-#define git_thread_create(thread, attr, start_routine, arg) \
- pthread_create(thread, attr, start_routine, arg)
-#define git_thread_kill(thread) pthread_cancel(thread)
-#define git_thread_exit(status) pthread_exit(status)
-#define git_thread_join(id, status) pthread_join(id, status)
+#if !defined(GIT_WIN32)
+
+typedef struct {
+ pthread_t thread;
+} git_thread;
+
+#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \
+ pthread_create(&(git_thread_ptr)->thread, attr, start_routine, arg)
+#define git_thread_join(git_thread_ptr, status) \
+ pthread_join((git_thread_ptr)->thread, status)
-#if defined(GIT_WIN32)
-#define git_thread_yield() Sleep(0)
-#else
-#define git_thread_yield() sched_yield()
#endif
/* Pthreads Mutex */
@@ -179,10 +179,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#define git_thread unsigned int
#define git_thread_create(thread, attr, start_routine, arg) 0
-#define git_thread_kill(thread) (void)0
-#define git_thread_exit(status) (void)0
#define git_thread_join(id, status) (void)0
-#define git_thread_yield() (void)0
/* Pthreads Mutex */
#define git_mutex unsigned int
diff --git a/src/trace.h b/src/trace.h
index 4d4e3bf53..486084d01 100644
--- a/src/trace.h
+++ b/src/trace.h
@@ -46,8 +46,16 @@ GIT_INLINE(void) git_trace__write_fmt(
#else
+GIT_INLINE(void) git_trace__null(
+ git_trace_level_t level,
+ const char *fmt, ...)
+{
+ GIT_UNUSED(level);
+ GIT_UNUSED(fmt);
+}
+
#define git_trace_level() ((void)0)
-#define git_trace(lvl, ...) ((void)0)
+#define git_trace git_trace__null
#endif
diff --git a/src/transport.c b/src/transport.c
index dc074a503..d42c92684 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -9,11 +9,11 @@
#include "git2/remote.h"
#include "git2/net.h"
#include "git2/transport.h"
+#include "git2/sys/transport.h"
#include "path.h"
typedef struct transport_definition {
char *prefix;
- unsigned priority;
git_transport_cb fn;
void *param;
} transport_definition;
@@ -24,52 +24,55 @@ static git_smart_subtransport_definition git_subtransport_definition = { git_sma
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 };
#endif
-static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
-#ifdef GIT_SSH
-static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition };
-#else
-static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
-#endif
+static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
static transport_definition transports[] = {
- {"git://", 1, git_transport_smart, &git_subtransport_definition},
- {"http://", 1, git_transport_smart, &http_subtransport_definition},
- {"https://", 1, git_transport_smart, &http_subtransport_definition},
- {"file://", 1, git_transport_local, NULL},
+ { "git://", git_transport_smart, &git_subtransport_definition },
+ { "http://", git_transport_smart, &http_subtransport_definition },
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+ { "https://", git_transport_smart, &http_subtransport_definition },
+#endif
+ { "file://", git_transport_local, NULL },
#ifdef GIT_SSH
- {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition},
+ { "ssh://", git_transport_smart, &ssh_subtransport_definition },
#endif
- {NULL, 0, 0}
+ { NULL, 0, 0 }
};
-static git_vector additional_transports = GIT_VECTOR_INIT;
+static git_vector custom_transports = GIT_VECTOR_INIT;
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
-static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
+static transport_definition * transport_find_by_url(const char *url)
{
size_t i = 0;
- unsigned priority = 0;
- transport_definition *definition = NULL, *definition_iter;
+ transport_definition *d;
- // First, check to see if it's an obvious URL, which a URL scheme
- for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
- definition_iter = &transports[i];
+ /* Find a user transport who wants to deal with this URI */
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ return d;
+ }
+ }
- if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
- continue;
+ /* Find a system transport for this URI */
+ for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
+ d = &transports[i];
- if (definition_iter->priority > priority)
- definition = definition_iter;
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ return d;
+ }
}
- git_vector_foreach(&additional_transports, i, definition_iter) {
- if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
- continue;
+ return NULL;
+}
- if (definition_iter->priority > priority)
- definition = definition_iter;
- }
+static int transport_find_fn(
+ git_transport_cb *out,
+ const char *url,
+ void **param)
+{
+ transport_definition *definition = transport_find_by_url(url);
#ifdef GIT_WIN32
/* On Windows, it might not be possible to discern between absolute local
@@ -86,12 +89,10 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
/* It could be a SSH remote path. Check to see if there's a :
* SSH is an unsupported transport mechanism in this version of libgit2 */
- if (!definition && strrchr(url, ':'))
-#ifdef GIT_SSH
- definition = &ssh_transport_definition;
-#else
- definition = &dummy_transport_definition;
-#endif
+ if (!definition && strrchr(url, ':')) {
+ // re-search transports again with ssh:// as url so that we can find a third party ssh transport
+ definition = transport_find_by_url("ssh://");
+ }
#ifndef GIT_WIN32
/* Check to see if the path points to a file on the local file system */
@@ -100,9 +101,9 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
#endif
if (!definition)
- return -1;
+ return GIT_ENOTFOUND;
- *callback = definition->fn;
+ *out = definition->fn;
*param = definition->param;
return 0;
@@ -112,15 +113,6 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
* Public API *
**************/
-int git_transport_dummy(git_transport **transport, git_remote *owner, void *param)
-{
- GIT_UNUSED(transport);
- GIT_UNUSED(owner);
- GIT_UNUSED(param);
- giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
- return -1;
-}
-
int git_transport_new(git_transport **out, git_remote *owner, const char *url)
{
git_transport_cb fn;
@@ -128,83 +120,96 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
void *param;
int error;
- if (transport_find_fn(url, &fn, &param) < 0) {
+ if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
giterr_set(GITERR_NET, "Unsupported URL protocol");
return -1;
- }
+ } else if (error < 0)
+ return error;
- error = fn(&transport, owner, param);
- if (error < 0)
+ if ((error = fn(&transport, owner, param)) < 0)
return error;
+ GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+
*out = transport;
return 0;
}
int git_transport_register(
- const char *prefix,
- unsigned priority,
+ const char *scheme,
git_transport_cb cb,
void *param)
{
- transport_definition *d;
+ git_buf prefix = GIT_BUF_INIT;
+ transport_definition *d, *definition = NULL;
+ size_t i;
+ int error = 0;
- d = git__calloc(sizeof(transport_definition), 1);
- GITERR_CHECK_ALLOC(d);
+ assert(scheme);
+ assert(cb);
- d->prefix = git__strdup(prefix);
-
- if (!d->prefix)
+ if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
goto on_error;
- d->priority = priority;
- d->fn = cb;
- d->param = param;
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ error = GIT_EEXISTS;
+ goto on_error;
+ }
+ }
+
+ definition = git__calloc(1, sizeof(transport_definition));
+ GITERR_CHECK_ALLOC(definition);
+
+ definition->prefix = git_buf_detach(&prefix);
+ definition->fn = cb;
+ definition->param = param;
- if (git_vector_insert(&additional_transports, d) < 0)
+ if (git_vector_insert(&custom_transports, definition) < 0)
goto on_error;
return 0;
on_error:
- git__free(d->prefix);
- git__free(d);
- return -1;
+ git_buf_free(&prefix);
+ git__free(definition);
+ return error;
}
-int git_transport_unregister(
- const char *prefix,
- unsigned priority)
+int git_transport_unregister(const char *scheme)
{
+ git_buf prefix = GIT_BUF_INIT;
transport_definition *d;
- unsigned i;
+ size_t i;
+ int error = 0;
+
+ assert(scheme);
+
+ if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
+ goto done;
- git_vector_foreach(&additional_transports, i, d) {
- if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
- if (git_vector_remove(&additional_transports, i) < 0)
- return -1;
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ if ((error = git_vector_remove(&custom_transports, i)) < 0)
+ goto done;
git__free(d->prefix);
git__free(d);
- if (!additional_transports.length)
- git_vector_free(&additional_transports);
+ if (!custom_transports.length)
+ git_vector_free(&custom_transports);
- return 0;
+ error = 0;
+ goto done;
}
}
- return GIT_ENOTFOUND;
-}
-
-/* from remote.h */
-int git_remote_valid_url(const char *url)
-{
- git_transport_cb fn;
- void *param;
+ error = GIT_ENOTFOUND;
- return !transport_find_fn(url, &fn, &param);
+done:
+ git_buf_free(&prefix);
+ return error;
}
int git_remote_supported_url(const char* url)
@@ -212,20 +217,13 @@ int git_remote_supported_url(const char* url)
git_transport_cb fn;
void *param;
- if (transport_find_fn(url, &fn, &param) < 0)
- return 0;
-
- return fn != &git_transport_dummy;
+ /* The only error we expect is ENOTFOUND */
+ return !transport_find_fn(&fn, url, &param);
}
-int git_transport_init(git_transport* opts, int version)
+int git_transport_init(git_transport *opts, unsigned int version)
{
- if (version != GIT_TRANSPORT_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_transport", version);
- return -1;
- } else {
- git_transport o = GIT_TRANSPORT_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_transport, GIT_TRANSPORT_INIT);
+ return 0;
}
diff --git a/src/transports/auth.c b/src/transports/auth.c
new file mode 100644
index 000000000..c1154db34
--- /dev/null
+++ b/src/transports/auth.c
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include "git2.h"
+#include "buffer.h"
+#include "auth.h"
+
+static int basic_next_token(
+ git_buf *out, git_http_auth_context *ctx, git_cred *c)
+{
+ git_cred_userpass_plaintext *cred;
+ git_buf raw = GIT_BUF_INIT;
+ int error = -1;
+
+ GIT_UNUSED(ctx);
+
+ if (c->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ giterr_set(GITERR_INVALID, "invalid credential type for basic auth");
+ goto on_error;
+ }
+
+ cred = (git_cred_userpass_plaintext *)c;
+
+ git_buf_printf(&raw, "%s:%s", cred->username, cred->password);
+
+ if (git_buf_oom(&raw) ||
+ git_buf_puts(out, "Authorization: Basic ") < 0 ||
+ git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 ||
+ git_buf_puts(out, "\r\n") < 0)
+ goto on_error;
+
+ error = 0;
+
+on_error:
+ if (raw.size)
+ git__memzero(raw.ptr, raw.size);
+
+ git_buf_free(&raw);
+ return error;
+}
+
+static git_http_auth_context basic_context = {
+ GIT_AUTHTYPE_BASIC,
+ GIT_CREDTYPE_USERPASS_PLAINTEXT,
+ NULL,
+ basic_next_token,
+ NULL
+};
+
+int git_http_auth_basic(
+ git_http_auth_context **out, const gitno_connection_data *connection_data)
+{
+ GIT_UNUSED(connection_data);
+
+ *out = &basic_context;
+ return 0;
+}
+
+int git_http_auth_dummy(
+ git_http_auth_context **out, const gitno_connection_data *connection_data)
+{
+ GIT_UNUSED(connection_data);
+
+ *out = NULL;
+ return 0;
+}
+
diff --git a/src/transports/auth.h b/src/transports/auth.h
new file mode 100644
index 000000000..52138cf8f
--- /dev/null
+++ b/src/transports/auth.h
@@ -0,0 +1,63 @@
+/*
+ * 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_http_auth_h__
+#define INCLUDE_http_auth_h__
+
+#include "git2.h"
+#include "netops.h"
+
+typedef enum {
+ GIT_AUTHTYPE_BASIC = 1,
+ GIT_AUTHTYPE_NEGOTIATE = 2,
+} git_http_authtype_t;
+
+typedef struct git_http_auth_context git_http_auth_context;
+
+struct git_http_auth_context {
+ /** Type of scheme */
+ git_http_authtype_t type;
+
+ /** Supported credentials */
+ git_credtype_t credtypes;
+
+ /** Sets the challenge on the authentication context */
+ int (*set_challenge)(git_http_auth_context *ctx, const char *challenge);
+
+ /** Gets the next authentication token from the context */
+ int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_cred *cred);
+
+ /** Frees the authentication context */
+ void (*free)(git_http_auth_context *ctx);
+};
+
+typedef struct {
+ /** Type of scheme */
+ git_http_authtype_t type;
+
+ /** Name of the scheme (as used in the Authorization header) */
+ const char *name;
+
+ /** Credential types this scheme supports */
+ git_credtype_t credtypes;
+
+ /** Function to initialize an authentication context */
+ int (*init_context)(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+} git_http_auth_scheme;
+
+int git_http_auth_dummy(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+int git_http_auth_basic(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+#endif
+
diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c
new file mode 100644
index 000000000..8b99fc735
--- /dev/null
+++ b/src/transports/auth_negotiate.c
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+#ifdef GIT_GSSAPI
+
+#include "git2.h"
+#include "common.h"
+#include "buffer.h"
+#include "auth.h"
+
+#include <gssapi.h>
+#include <krb5.h>
+
+static gss_OID_desc negotiate_oid_spnego =
+ { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+static gss_OID_desc negotiate_oid_krb5 =
+ { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+static gss_OID negotiate_oids[] =
+ { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
+
+typedef struct {
+ git_http_auth_context parent;
+ unsigned configured : 1,
+ complete : 1;
+ git_buf target;
+ char *challenge;
+ gss_ctx_id_t gss_context;
+ gss_OID oid;
+} http_auth_negotiate_context;
+
+static void negotiate_err_set(
+ OM_uint32 status_major,
+ OM_uint32 status_minor,
+ const char *message)
+{
+ gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+ OM_uint32 status_display, context = 0;
+
+ if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
+ GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
+ giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
+ message, (int)buffer.length, (const char *)buffer.value,
+ status_major, status_minor);
+ gss_release_buffer(&status_minor, &buffer);
+ } else {
+ giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
+ message, status_major, status_minor);
+ }
+}
+
+static int negotiate_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+
+ assert(ctx && ctx->configured && challenge);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GITERR_CHECK_ALLOC(ctx->challenge);
+
+ return 0;
+}
+
+static int negotiate_next_token(
+ git_buf *buf,
+ git_http_auth_context *c,
+ git_cred *cred)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_major, status_minor;
+ gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
+ input_token = GSS_C_EMPTY_BUFFER,
+ output_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
+ git_buf input_buf = GIT_BUF_INIT;
+ gss_name_t server = NULL;
+ gss_OID mech;
+ size_t challenge_len;
+ int error = 0;
+
+ assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
+
+ if (ctx->complete)
+ return 0;
+
+ target_buffer.value = (void *)ctx->target.ptr;
+ target_buffer.length = ctx->target.size;
+
+ status_major = gss_import_name(&status_minor, &target_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE, &server);
+
+ if (GSS_ERROR(status_major)) {
+ negotiate_err_set(status_major, status_minor,
+ "Could not parse principal");
+ error = -1;
+ goto done;
+ }
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (challenge_len < 9) {
+ giterr_set(GITERR_NET, "No negotiate challenge sent from server");
+ error = -1;
+ goto done;
+ } else if (challenge_len > 9) {
+ if (git_buf_decode_base64(&input_buf,
+ ctx->challenge + 10, challenge_len - 10) < 0) {
+ giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
+ error = -1;
+ goto done;
+ }
+
+ input_token.value = input_buf.ptr;
+ input_token.length = input_buf.size;
+ input_token_ptr = &input_token;
+ } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ giterr_set(GITERR_NET, "Could not restart authentication");
+ error = -1;
+ goto done;
+ }
+
+ mech = &negotiate_oid_spnego;
+
+ if (GSS_ERROR(status_major = gss_init_sec_context(
+ &status_minor,
+ GSS_C_NO_CREDENTIAL,
+ &ctx->gss_context,
+ server,
+ mech,
+ GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ input_token_ptr,
+ NULL,
+ &output_token,
+ NULL,
+ NULL))) {
+ negotiate_err_set(status_major, status_minor, "Negotiate failure");
+ error = -1;
+ goto done;
+ }
+
+ /* This message merely told us auth was complete; we do not respond. */
+ if (status_major == GSS_S_COMPLETE) {
+ ctx->complete = 1;
+ goto done;
+ }
+
+ git_buf_puts(buf, "Authorization: Negotiate ");
+ git_buf_encode_base64(buf, output_token.value, output_token.length);
+ git_buf_puts(buf, "\r\n");
+
+ if (git_buf_oom(buf))
+ error = -1;
+
+done:
+ gss_release_name(&status_minor, &server);
+ gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
+ git_buf_free(&input_buf);
+ return error;
+}
+
+static void negotiate_context_free(git_http_auth_context *c)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_minor;
+
+ if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(
+ &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ }
+
+ git_buf_free(&ctx->target);
+
+ git__free(ctx->challenge);
+
+ ctx->configured = 0;
+ ctx->complete = 0;
+ ctx->oid = NULL;
+
+ git__free(ctx);
+}
+
+static int negotiate_init_context(
+ http_auth_negotiate_context *ctx,
+ const gitno_connection_data *connection_data)
+{
+ OM_uint32 status_major, status_minor;
+ gss_OID item, *oid;
+ gss_OID_set mechanism_list;
+ size_t i;
+
+ /* Query supported mechanisms looking for SPNEGO) */
+ if (GSS_ERROR(status_major =
+ gss_indicate_mechs(&status_minor, &mechanism_list))) {
+ negotiate_err_set(status_major, status_minor,
+ "could not query mechanisms");
+ return -1;
+ }
+
+ if (mechanism_list) {
+ for (oid = negotiate_oids; *oid; oid++) {
+ for (i = 0; i < mechanism_list->count; i++) {
+ item = &mechanism_list->elements[i];
+
+ if (item->length == (*oid)->length &&
+ memcmp(item->elements, (*oid)->elements, item->length) == 0) {
+ ctx->oid = *oid;
+ break;
+ }
+
+ }
+
+ if (ctx->oid)
+ break;
+ }
+ }
+
+ gss_release_oid_set(&status_minor, &mechanism_list);
+
+ if (!ctx->oid) {
+ giterr_set(GITERR_NET, "Negotiate authentication is not supported");
+ return -1;
+ }
+
+ git_buf_puts(&ctx->target, "HTTP@");
+ git_buf_puts(&ctx->target, connection_data->host);
+
+ if (git_buf_oom(&ctx->target))
+ return -1;
+
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ ctx->configured = 1;
+
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data)
+{
+ http_auth_negotiate_context *ctx;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
+ GITERR_CHECK_ALLOC(ctx);
+
+ if (negotiate_init_context(ctx, connection_data) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
+ ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
+ ctx->parent.set_challenge = negotiate_set_challenge;
+ ctx->parent.next_token = negotiate_next_token;
+ ctx->parent.free = negotiate_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_GSSAPI */
+
diff --git a/src/transports/auth_negotiate.h b/src/transports/auth_negotiate.h
new file mode 100644
index 000000000..d7270b7ab
--- /dev/null
+++ b/src/transports/auth_negotiate.h
@@ -0,0 +1,27 @@
+/*
+ * 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_auth_negotiate_h__
+#define INCLUDE_auth_negotiate_h__
+
+#include "git2.h"
+#include "auth.h"
+
+#ifdef GIT_GSSAPI
+
+extern int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+#else
+
+#define git_http_auth_negotiate git_http_auth_dummy
+
+#endif /* GIT_GSSAPI */
+
+#endif
+
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 913ec36cc..1b4d29c0a 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -17,6 +17,40 @@ int git_cred_has_username(git_cred *cred)
return 1;
}
+const char *git_cred__username(git_cred *cred)
+{
+ switch (cred->credtype) {
+ case GIT_CREDTYPE_USERNAME:
+ {
+ git_cred_username *c = (git_cred_username *) cred;
+ return c->username;
+ }
+ case GIT_CREDTYPE_USERPASS_PLAINTEXT:
+ {
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *) cred;
+ return c->username;
+ }
+ case GIT_CREDTYPE_SSH_KEY:
+ {
+ git_cred_ssh_key *c = (git_cred_ssh_key *) cred;
+ return c->username;
+ }
+ case GIT_CREDTYPE_SSH_CUSTOM:
+ {
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *) cred;
+ return c->username;
+ }
+ case GIT_CREDTYPE_SSH_INTERACTIVE:
+ {
+ git_cred_ssh_interactive *c = (git_cred_ssh_interactive *) cred;
+ return c->username;
+ }
+
+ default:
+ return NULL;
+ }
+}
+
static void plaintext_free(struct git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -129,6 +163,11 @@ static void default_free(struct git_cred *cred)
git__free(c);
}
+static void username_free(struct git_cred *cred)
+{
+ git__free(cred);
+}
+
int git_cred_ssh_key_new(
git_cred **cred,
const char *username,
@@ -263,3 +302,22 @@ int git_cred_default_new(git_cred **cred)
*cred = c;
return 0;
}
+
+int git_cred_username_new(git_cred **cred, const char *username)
+{
+ git_cred_username *c;
+ size_t len;
+
+ assert(cred);
+
+ len = strlen(username);
+ c = git__malloc(sizeof(git_cred_username) + len + 1);
+ GITERR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDTYPE_USERNAME;
+ c->parent.free = username_free;
+ memcpy(c->username, username, len + 1);
+
+ *cred = (git_cred *) c;
+ return 0;
+}
diff --git a/src/transports/cred.h b/src/transports/cred.h
new file mode 100644
index 000000000..2de8deee8
--- /dev/null
+++ b/src/transports/cred.h
@@ -0,0 +1,14 @@
+/*
+ * 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_git_cred_h__
+#define INCLUDE_git_cred_h__
+
+#include "git2/transport.h"
+
+const char *git_cred__username(git_cred *cred);
+
+#endif
diff --git a/src/transports/cred_helpers.c b/src/transports/cred_helpers.c
index d420e3e3c..5cc9b0869 100644
--- a/src/transports/cred_helpers.c
+++ b/src/transports/cred_helpers.c
@@ -41,6 +41,9 @@ int git_cred_userpass(
else
return -1;
+ if (GIT_CREDTYPE_USERNAME & allowed_types)
+ return git_cred_username_new(cred, effective_username);
+
if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
return -1;
diff --git a/src/transports/git.c b/src/transports/git.c
index 21507c1c7..e2690fe36 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -8,6 +8,7 @@
#include "git2.h"
#include "buffer.h"
#include "netops.h"
+#include "git2/sys/transport.h"
#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
diff --git a/src/transports/http.c b/src/transports/http.c
index a7eff7365..7ef0b519c 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -11,6 +11,17 @@
#include "buffer.h"
#include "netops.h"
#include "smart.h"
+#include "auth.h"
+#include "auth_negotiate.h"
+
+git_http_auth_scheme auth_schemes[] = {
+ { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
+ { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
+};
+
+#ifdef GIT_SSL
+# include <openssl/x509v3.h>
+#endif
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
@@ -20,7 +31,6 @@ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive
static const char *receive_pack_service_url = "/git-receive-pack";
static const char *get_verb = "GET";
static const char *post_verb = "POST";
-static const char *basic_authtype = "Basic";
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
@@ -35,10 +45,6 @@ enum last_cb {
VALUE
};
-typedef enum {
- GIT_HTTP_AUTH_BASIC = 1,
-} http_authmechanism_t;
-
typedef struct {
git_smart_subtransport_stream parent;
const char *service;
@@ -58,9 +64,6 @@ typedef struct {
transport_smart *owner;
gitno_socket socket;
gitno_connection_data connection_data;
- git_cred *cred;
- git_cred *url_cred;
- http_authmechanism_t auth_mechanism;
bool connected;
/* Parser structures */
@@ -76,6 +79,11 @@ typedef struct {
enum last_cb last_cb;
int parse_error;
unsigned parse_finished : 1;
+
+ /* Authentication */
+ git_cred *cred;
+ git_cred *url_cred;
+ git_vector auth_contexts;
} http_subtransport;
typedef struct {
@@ -88,28 +96,91 @@ typedef struct {
size_t *bytes_read;
} parser_context;
-static int apply_basic_credential(git_buf *buf, git_cred *cred)
+static bool credtype_match(git_http_auth_scheme *scheme, void *data)
{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- git_buf raw = GIT_BUF_INIT;
- int error = -1;
+ unsigned int credtype = *(unsigned int *)data;
- git_buf_printf(&raw, "%s:%s", c->username, c->password);
+ return !!(scheme->credtypes & credtype);
+}
- if (git_buf_oom(&raw) ||
- git_buf_puts(buf, "Authorization: Basic ") < 0 ||
- git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
- git_buf_puts(buf, "\r\n") < 0)
- goto on_error;
+static bool challenge_match(git_http_auth_scheme *scheme, void *data)
+{
+ const char *scheme_name = scheme->name;
+ const char *challenge = (const char *)data;
+ size_t scheme_len;
- error = 0;
+ scheme_len = strlen(scheme_name);
+ return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
+ (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
+}
-on_error:
- if (raw.size)
- memset(raw.ptr, 0x0, raw.size);
+static int auth_context_match(
+ git_http_auth_context **out,
+ http_subtransport *t,
+ bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
+ void *data)
+{
+ git_http_auth_scheme *scheme = NULL;
+ git_http_auth_context *context = NULL, *c;
+ size_t i;
+
+ *out = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
+ if (scheme_match(&auth_schemes[i], data)) {
+ scheme = &auth_schemes[i];
+ break;
+ }
+ }
+
+ if (!scheme)
+ return 0;
- git_buf_free(&raw);
- return error;
+ /* See if authentication has already started for this scheme */
+ git_vector_foreach(&t->auth_contexts, i, c) {
+ if (c->type == scheme->type) {
+ context = c;
+ break;
+ }
+ }
+
+ if (!context) {
+ if (scheme->init_context(&context, &t->connection_data) < 0)
+ return -1;
+ else if (!context)
+ return 0;
+ else if (git_vector_insert(&t->auth_contexts, context) < 0)
+ return -1;
+ }
+
+ *out = context;
+
+ return 0;
+}
+
+static int apply_credentials(git_buf *buf, http_subtransport *t)
+{
+ git_cred *cred = t->cred;
+ git_http_auth_context *context;
+
+ /* Apply the credentials given to us in the URL */
+ if (!cred && t->connection_data.user && t->connection_data.pass) {
+ if (!t->url_cred &&
+ git_cred_userpass_plaintext_new(&t->url_cred,
+ t->connection_data.user, t->connection_data.pass) < 0)
+ return -1;
+
+ cred = t->url_cred;
+ }
+
+ if (!cred)
+ return 0;
+
+ /* Get or create a context for the best scheme for this cred type */
+ if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
+ return -1;
+
+ return context->next_token(buf, context, cred);
}
static int gen_request(
@@ -137,19 +208,9 @@ static int gen_request(
git_buf_puts(buf, "Accept: */*\r\n");
/* Apply credentials to the request */
- if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
- t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
- apply_basic_credential(buf, t->cred) < 0)
+ if (apply_credentials(buf, t) < 0)
return -1;
- /* Use url-parsed basic auth if username and password are both provided */
- if (!t->cred && t->connection_data.user && t->connection_data.pass) {
- if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
- t->connection_data.user, t->connection_data.pass) < 0)
- return -1;
- if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
- }
-
git_buf_puts(buf, "\r\n");
if (git_buf_oom(buf))
@@ -158,20 +219,26 @@ static int gen_request(
return 0;
}
-static int parse_unauthorized_response(
+static int parse_authenticate_response(
git_vector *www_authenticate,
- int *allowed_types,
- http_authmechanism_t *auth_mechanism)
+ http_subtransport *t,
+ int *allowed_types)
{
- unsigned i;
- char *entry;
-
- git_vector_foreach(www_authenticate, i, entry) {
- if (!strncmp(entry, basic_authtype, 5) &&
- (entry[5] == '\0' || entry[5] == ' ')) {
- *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
- *auth_mechanism = GIT_HTTP_AUTH_BASIC;
- }
+ git_http_auth_context *context;
+ char *challenge;
+ size_t i;
+
+ git_vector_foreach(www_authenticate, i, challenge) {
+ if (auth_context_match(&context, t, challenge_match, challenge) < 0)
+ return -1;
+ else if (!context)
+ continue;
+
+ if (context->set_challenge &&
+ context->set_challenge(context, challenge) < 0)
+ return -1;
+
+ *allowed_types |= context->credtypes;
}
return 0;
@@ -248,7 +315,7 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t;
http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT;
- int error = 0, no_callback = 0;
+ int error = 0, no_callback = 0, allowed_auth_types = 0;
/* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */
@@ -256,26 +323,26 @@ static int on_headers_complete(http_parser *parser)
if (on_header_ready(t) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
- /* Check for an authentication failure. */
+ /* Capture authentication headers which may be a 401 (authentication
+ * is not complete) or a 200 (simply informing us that auth *is*
+ * complete.)
+ */
+ if (parse_authenticate_response(&t->www_authenticate, t,
+ &allowed_auth_types) < 0)
+ return t->parse_error = PARSE_ERROR_GENERIC;
- if (parser->status_code == 401 &&
- get_verb == s->verb) {
- if (!t->owner->cred_acquire_payload) {
+ /* Check for an authentication failure. */
+ if (parser->status_code == 401 && get_verb == s->verb) {
+ if (!t->owner->cred_acquire_cb) {
no_callback = 1;
} else {
- int allowed_types = 0;
-
- if (parse_unauthorized_response(&t->www_authenticate,
- &allowed_types, &t->auth_mechanism) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- if (allowed_types &&
- (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+ if (allowed_auth_types &&
+ (!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) {
error = t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
t->connection_data.user,
- allowed_types,
+ allowed_auth_types,
t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) {
@@ -286,7 +353,8 @@ static int on_headers_complete(http_parser *parser)
assert(t->cred);
/* Successfully acquired a credential. */
- return t->parse_error = PARSE_ERROR_REPLAY;
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
}
}
}
@@ -324,7 +392,8 @@ static int on_headers_complete(http_parser *parser)
t->connected = 0;
s->redirect_count++;
- return t->parse_error = PARSE_ERROR_REPLAY;
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
}
/* Check for a 200 HTTP status code. */
@@ -382,6 +451,13 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t;
+ /* If our goal is to replay the request (either an auth failure or
+ * a redirect) then don't bother buffering since we're ignoring the
+ * content anyway.
+ */
+ if (t->parse_error == PARSE_ERROR_REPLAY)
+ return 0;
+
if (ctx->buf_size < len) {
giterr_set(GITERR_NET, "Can't fit data in the buffer");
return t->parse_error = PARSE_ERROR_GENERIC;
@@ -452,11 +528,11 @@ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
static int http_connect(http_subtransport *t)
{
- int flags = 0;
+ int flags = 0, error;
if (t->connected &&
http_should_keep_alive(&t->parser) &&
- http_body_is_final(&t->parser))
+ t->parse_finished)
return 0;
if (t->socket.socket)
@@ -469,13 +545,55 @@ static int http_connect(http_subtransport *t)
return -1;
flags |= GITNO_CONNECT_SSL;
-
- if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
- flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
}
- if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
- return -1;
+ error = gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags);
+
+#ifdef GIT_SSL
+ if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL) {
+ X509 *cert = SSL_get_peer_certificate(t->socket.ssl.ssl);
+ git_cert_x509 cert_info;
+ int len, is_valid;
+ unsigned char *guard, *encoded_cert;
+
+ /* Retrieve the length of the certificate first */
+ len = i2d_X509(cert, NULL);
+ if (len < 0) {
+ giterr_set(GITERR_NET, "failed to retrieve certificate information");
+ return -1;
+ }
+
+
+ encoded_cert = git__malloc(len);
+ GITERR_CHECK_ALLOC(encoded_cert);
+ /* i2d_X509 makes 'copy' point to just after the data */
+ guard = encoded_cert;
+
+ len = i2d_X509(cert, &guard);
+ if (len < 0) {
+ git__free(encoded_cert);
+ giterr_set(GITERR_NET, "failed to retrieve certificate information");
+ return -1;
+ }
+
+ giterr_clear();
+ is_valid = error != GIT_ECERTIFICATE;
+ cert_info.cert_type = GIT_CERT_X509;
+ cert_info.data = encoded_cert;
+ cert_info.len = len;
+ error = t->owner->certificate_check_cb((git_cert *) &cert_info, is_valid, t->owner->message_cb_payload);
+ git__free(encoded_cert);
+
+ if (error < 0) {
+ if (!giterr_last())
+ giterr_set(GITERR_NET, "user cancelled certificate check");
+
+ return error;
+ }
+ }
+#endif
+ if (error < 0)
+ return error;
t->connected = 1;
return 0;
@@ -502,10 +620,8 @@ replay:
clear_parser_state(t);
- if (gen_request(&request, s, 0) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, 0) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request);
@@ -537,7 +653,24 @@ replay:
}
while (!*bytes_read && !t->parse_finished) {
- t->parse_buffer.offset = 0;
+ size_t data_offset;
+ int error;
+
+ /*
+ * Make the parse_buffer think it's as full of data as
+ * the buffer, so it won't try to recv more data than
+ * we can put into it.
+ *
+ * data_offset is the actual data offset from which we
+ * should tell the parser to start reading.
+ */
+ if (buf_size >= t->parse_buffer.len) {
+ t->parse_buffer.offset = 0;
+ } else {
+ t->parse_buffer.offset = t->parse_buffer.len - buf_size;
+ }
+
+ data_offset = t->parse_buffer.offset;
if (gitno_recv(&t->parse_buffer) < 0)
return -1;
@@ -558,8 +691,8 @@ replay:
bytes_parsed = http_parser_execute(&t->parser,
&t->settings,
- t->parse_buffer.data,
- t->parse_buffer.offset);
+ t->parse_buffer.data + data_offset,
+ t->parse_buffer.offset - data_offset);
t->parser.data = NULL;
@@ -568,8 +701,8 @@ replay:
if (PARSE_ERROR_REPLAY == t->parse_error) {
s->sent_request = 0;
- if (http_connect(t) < 0)
- return -1;
+ if ((error = http_connect(t)) < 0)
+ return error;
goto replay;
}
@@ -577,7 +710,7 @@ replay:
if (t->parse_error < 0)
return -1;
- if (bytes_parsed != t->parse_buffer.offset) {
+ if (bytes_parsed != t->parse_buffer.offset - data_offset) {
giterr_set(GITERR_NET,
"HTTP parser error: %s",
http_errno_description((enum http_errno)t->parser.http_errno));
@@ -604,10 +737,8 @@ static int http_stream_write_chunked(
clear_parser_state(t);
- if (gen_request(&request, s, 0) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, 0) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request);
@@ -679,10 +810,8 @@ static int http_stream_write_single(
clear_parser_state(t);
- if (gen_request(&request, s, len) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, len) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
goto on_error;
@@ -825,8 +954,8 @@ static int http_action(
(ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
return ret;
- if (http_connect(t) < 0)
- return -1;
+ if ((ret = http_connect(t)) < 0)
+ return ret;
switch (action) {
case GIT_SERVICE_UPLOADPACK_LS:
@@ -849,6 +978,8 @@ static int http_action(
static int http_close(git_smart_subtransport *subtransport)
{
http_subtransport *t = (http_subtransport *) subtransport;
+ git_http_auth_context *context;
+ size_t i;
clear_parser_state(t);
@@ -867,6 +998,13 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
+ git_vector_foreach(&t->auth_contexts, i, context) {
+ if (context->free)
+ context->free(context);
+ }
+
+ git_vector_clear(&t->auth_contexts);
+
gitno_connection_data_free_ptrs(&t->connection_data);
return 0;
@@ -878,6 +1016,7 @@ static void http_free(git_smart_subtransport *subtransport)
http_close(subtransport);
+ git_vector_free(&t->auth_contexts);
git__free(t);
}
diff --git a/src/transports/local.c b/src/transports/local.c
index 2c17e6271..f859f0b70 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -40,17 +40,29 @@ typedef struct {
have_refs : 1;
} transport_local;
+static void free_head(git_remote_head *head)
+{
+ git__free(head->name);
+ git__free(head->symref_target);
+ git__free(head);
+}
+
static int add_ref(transport_local *t, const char *name)
{
const char peeled[] = "^{}";
- git_oid head_oid;
+ git_reference *ref, *resolved;
git_remote_head *head;
+ git_oid obj_id;
git_object *obj = NULL, *target = NULL;
git_buf buf = GIT_BUF_INIT;
int error;
- error = git_reference_name_to_id(&head_oid, t->repo, name);
+ if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
+ return error;
+
+ error = git_reference_resolve(&resolved, ref);
if (error < 0) {
+ git_reference_free(ref);
if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
/* This is actually okay. Empty repos often have a HEAD that
* points to a nonexistent "refs/heads/master". */
@@ -60,17 +72,25 @@ static int add_ref(transport_local *t, const char *name)
return error;
}
+ git_oid_cpy(&obj_id, git_reference_target(resolved));
+ git_reference_free(resolved);
+
head = git__calloc(1, sizeof(git_remote_head));
GITERR_CHECK_ALLOC(head);
head->name = git__strdup(name);
GITERR_CHECK_ALLOC(head->name);
- git_oid_cpy(&head->oid, &head_oid);
+ git_oid_cpy(&head->oid, &obj_id);
+
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
+ head->symref_target = git__strdup(git_reference_symbolic_target(ref));
+ GITERR_CHECK_ALLOC(head->symref_target);
+ }
+ git_reference_free(ref);
if ((error = git_vector_insert(&t->refs, head)) < 0) {
- git__free(head->name);
- git__free(head);
+ free_head(head);
return error;
}
@@ -103,8 +123,7 @@ static int add_ref(transport_local *t, const char *name)
git_oid_cpy(&head->oid, git_object_id(target));
if ((error = git_vector_insert(&t->refs, head)) < 0) {
- git__free(head->name);
- git__free(head);
+ free_head(head);
}
}
@@ -156,27 +175,9 @@ on_error:
return -1;
}
-static int path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
-{
- int error;
-
- /* If url_or_path begins with file:// treat it as a URL */
- if (!git__prefixcmp(url_or_path, "file://")) {
- if ((error = git_path_fromurl(local_path_out, url_or_path)) < 0) {
- return error;
- }
- } else { /* We assume url_or_path is already a path */
- if ((error = git_buf_sets(local_path_out, url_or_path)) < 0) {
- return error;
- }
- }
-
- return 0;
-}
-
/*
* Try to open the url as a git directory. The direction doesn't
- * matter in this case because we're calulating the heads ourselves.
+ * matter in this case because we're calculating the heads ourselves.
*/
static int local_connect(
git_transport *transport,
@@ -203,7 +204,7 @@ static int local_connect(
t->flags = flags;
/* 'url' may be a url or path; convert to a path */
- if ((error = path_from_url_or_path(&buf, url)) < 0) {
+ if ((error = git_path_from_url_or_path(&buf, url)) < 0) {
git_buf_free(&buf);
return error;
}
@@ -367,7 +368,7 @@ static int local_push(
size_t j;
/* 'push->remote->url' may be a url or path; convert to a path */
- if ((error = path_from_url_or_path(&buf, push->remote->url)) < 0) {
+ if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) {
git_buf_free(&buf);
return error;
}
@@ -626,10 +627,8 @@ static void local_free(git_transport *transport)
size_t i;
git_remote_head *head;
- git_vector_foreach(&t->refs, i, head) {
- git__free(head->name);
- git__free(head);
- }
+ git_vector_foreach(&t->refs, i, head)
+ free_head(head);
git_vector_free(&t->refs);
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 69eaf9b78..d0f9c90e8 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -7,6 +7,7 @@
#include "git2.h"
#include "smart.h"
#include "refs.h"
+#include "refspec.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
@@ -52,18 +53,20 @@ static int git_smart__set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
+ git_transport_certificate_check_cb certificate_check_cb,
void *message_cb_payload)
{
transport_smart *t = (transport_smart *)transport;
t->progress_cb = progress_cb;
t->error_cb = error_cb;
+ t->certificate_check_cb = certificate_check_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
-int git_smart__update_heads(transport_smart *t)
+int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
{
size_t i;
git_pkt *pkt;
@@ -74,6 +77,25 @@ int git_smart__update_heads(transport_smart *t)
if (pkt->type != GIT_PKT_REF)
continue;
+ if (symrefs) {
+ git_refspec *spec;
+ git_buf buf = GIT_BUF_INIT;
+ size_t j;
+ int error = 0;
+
+ git_vector_foreach(symrefs, j, spec) {
+ git_buf_clear(&buf);
+ if (git_refspec_src_matches(spec, ref->head.name) &&
+ !(error = git_refspec_transform(&buf, spec, ref->head.name)))
+ ref->head.symref_target = git_buf_detach(&buf);
+ }
+
+ git_buf_free(&buf);
+
+ if (error < 0)
+ return error;
+ }
+
if (git_vector_insert(&t->heads, &ref->head) < 0)
return -1;
}
@@ -81,6 +103,19 @@ int git_smart__update_heads(transport_smart *t)
return 0;
}
+static void free_symrefs(git_vector *symrefs)
+{
+ git_refspec *spec;
+ size_t i;
+
+ git_vector_foreach(symrefs, i, spec) {
+ git_refspec__free(spec);
+ git__free(spec);
+ }
+
+ git_vector_free(symrefs);
+}
+
static int git_smart__connect(
git_transport *transport,
const char *url,
@@ -94,6 +129,7 @@ static int git_smart__connect(
int error;
git_pkt *pkt;
git_pkt_ref *first;
+ git_vector symrefs;
git_smart_service_t service;
if (git_smart__reset_stream(t, true) < 0)
@@ -147,8 +183,11 @@ static int git_smart__connect(
first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
+ if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
+ return error;
+
/* Detect capabilities */
- if (git_smart__detect_caps(first, &t->caps) < 0)
+ if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
return -1;
/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
@@ -159,7 +198,9 @@ static int git_smart__connect(
}
/* Keep a list of heads for _ls */
- git_smart__update_heads(t);
+ git_smart__update_heads(t, &symrefs);
+
+ free_symrefs(&symrefs);
if (t->rpc && git_smart__reset_stream(t, false) < 0)
return -1;
@@ -272,6 +313,18 @@ static int git_smart__close(git_transport *transport)
unsigned int i;
git_pkt *p;
int ret;
+ git_smart_subtransport_stream *stream;
+ const char flush[] = "0000";
+
+ /*
+ * If we're still connected at this point and not using RPC,
+ * we should say goodbye by sending a flush, or git-daemon
+ * will complain that we disconnected unexpectedly.
+ */
+ if (t->connected && !t->rpc &&
+ !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
+ t->current_stream->write(t->current_stream, flush, 4);
+ }
ret = git_smart__reset_stream(t, true);
diff --git a/src/transports/smart.h b/src/transports/smart.h
index a2b6b2a71..44e241adc 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -9,6 +9,7 @@
#include "netops.h"
#include "buffer.h"
#include "push.h"
+#include "git2/sys/transport.h"
#define GIT_SIDE_BAND_DATA 1
#define GIT_SIDE_BAND_PROGRESS 2
@@ -23,6 +24,7 @@
#define GIT_CAP_DELETE_REFS "delete-refs"
#define GIT_CAP_REPORT_STATUS "report-status"
#define GIT_CAP_THIN_PACK "thin-pack"
+#define GIT_CAP_SYMREF "symref"
enum git_pkt_type {
GIT_PKT_CMD,
@@ -135,6 +137,7 @@ typedef struct {
int flags;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
+ git_transport_certificate_check_cb certificate_check_cb;
void *message_cb_payload;
git_smart_subtransport *wrapped;
git_smart_subtransport_stream *current_stream;
@@ -154,7 +157,7 @@ typedef struct {
/* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes);
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps);
+int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
@@ -174,7 +177,7 @@ int git_smart__download_pack(
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
-int git_smart__update_heads(transport_smart *t);
+int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index e9376ae6f..b5f9d6dbe 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -433,6 +433,7 @@ void git_pkt_free(git_pkt *pkt)
if (pkt->type == GIT_PKT_REF) {
git_pkt_ref *p = (git_pkt_ref *) pkt;
git__free(p->head.name);
+ git__free(p->head.symref_target);
}
if (pkt->type == GIT_PKT_OK) {
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 5dd6bab24..7c20382dc 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -26,17 +26,16 @@ int git_smart__store_refs(transport_smart *t, int flushes)
int error, flush = 0, recvd;
const char *line_end = NULL;
git_pkt *pkt = NULL;
- git_pkt_ref *ref;
size_t i;
/* Clear existing refs in case git_remote_connect() is called again
* after git_remote_disconnect().
*/
- git_vector_foreach(refs, i, ref) {
- git__free(ref->head.name);
- git__free(ref);
+ git_vector_foreach(refs, i, pkt) {
+ git_pkt_free(pkt);
}
git_vector_clear(refs);
+ pkt = NULL;
do {
if (buf->offset > 0)
@@ -78,7 +77,52 @@ int git_smart__store_refs(transport_smart *t, int flushes)
return flush;
}
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
+static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
+{
+ int error;
+ const char *end;
+ git_buf buf = GIT_BUF_INIT;
+ git_refspec *mapping;
+
+ ptr += strlen(GIT_CAP_SYMREF);
+ if (*ptr != '=')
+ goto on_invalid;
+
+ ptr++;
+ if (!(end = strchr(ptr, ' ')) &&
+ !(end = strchr(ptr, '\0')))
+ goto on_invalid;
+
+ if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
+ return error;
+
+ /* symref mapping has refspec format */
+ mapping = git__malloc(sizeof(git_refspec));
+ GITERR_CHECK_ALLOC(mapping);
+
+ error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
+ git_buf_free(&buf);
+
+ /* if the error isn't OOM, then it's a parse error; let's use a nicer message */
+ if (error < 0) {
+ if (giterr_last()->klass != GITERR_NOMEMORY)
+ goto on_invalid;
+
+ return error;
+ }
+
+ if ((error = git_vector_insert(symrefs, mapping)) < 0)
+ return error;
+
+ *out = end;
+ return 0;
+
+on_invalid:
+ giterr_set(GITERR_NET, "remote sent invalid symref");
+ return -1;
+}
+
+int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
{
const char *ptr;
@@ -141,6 +185,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
+ int error;
+
+ if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
+ return error;
+
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
@@ -261,7 +314,7 @@ static int wait_while_ack(gitno_buffer *buf)
break;
if (pkt->type == GIT_PKT_ACK &&
- (pkt->status != GIT_ACK_CONTINUE ||
+ (pkt->status != GIT_ACK_CONTINUE &&
pkt->status != GIT_ACK_COMMON)) {
git__free(pkt);
return 0;
@@ -539,7 +592,9 @@ int git_smart__download_pack(
}
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
- error = writepack->append(writepack, p->data, p->len, stats);
+
+ if (p->len)
+ error = writepack->append(writepack, p->data, p->len, stats);
} else if (pkt->type == GIT_PKT_FLUSH) {
/* A flush indicates the end of the packfile */
git__free(pkt);
@@ -585,9 +640,9 @@ static int gen_pktline(git_buf *buf, git_push *push)
{
push_spec *spec;
size_t i, len;
- char old_id[41], new_id[41];
+ char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
- old_id[40] = '\0'; new_id[40] = '\0';
+ old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
git_vector_foreach(&push->specs, i, spec) {
len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref);
@@ -908,7 +963,7 @@ int git_smart__push(git_transport *transport, git_push *push)
#ifdef PUSH_DEBUG
{
git_remote_head *head;
- char hex[41]; hex[40] = '\0';
+ char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
git_vector_foreach(&push->remote->refs, i, head) {
git_oid_fmt(hex, &head->oid);
@@ -969,7 +1024,7 @@ int git_smart__push(git_transport *transport, git_push *push)
if (error < 0)
goto done;
- error = git_smart__update_heads(t);
+ error = git_smart__update_heads(t, NULL);
}
done:
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index b403727c9..15a45ca86 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -5,15 +5,18 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
+#ifdef GIT_SSH
+#include <libssh2.h>
+#endif
+
#include "git2.h"
#include "buffer.h"
#include "netops.h"
#include "smart.h"
+#include "cred.h"
#ifdef GIT_SSH
-#include <libssh2.h>
-
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
static const char prefix_ssh[] = "ssh://";
@@ -35,8 +38,12 @@ typedef struct {
transport_smart *owner;
ssh_stream *current_stream;
git_cred *cred;
+ char *cmd_uploadpack;
+ char *cmd_receivepack;
} ssh_subtransport;
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
+
static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
{
char *ssherr;
@@ -132,11 +139,22 @@ static int ssh_stream_write(
size_t len)
{
ssh_stream *s = (ssh_stream *)stream;
+ size_t off = 0;
+ ssize_t ret = 0;
if (!s->sent_command && send_command(s) < 0)
return -1;
- if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
+ do {
+ ret = libssh2_channel_write(s->channel, buffer + off, len - off);
+ if (ret < 0)
+ break;
+
+ off += ret;
+
+ } while (off < len);
+
+ if (ret < 0) {
ssh_error(s->session, "SSH could not write data");
return -1;
}
@@ -274,6 +292,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) {
}
shutdown:
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ ssh_error(session, "error authenticating");
+
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
@@ -287,6 +309,7 @@ static int _git_ssh_authenticate_session(
int rc;
do {
+ giterr_clear();
switch (cred->credtype) {
case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -339,11 +362,52 @@ static int _git_ssh_authenticate_session(
}
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+ if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
+ return GIT_EAUTH;
+
if (rc != LIBSSH2_ERROR_NONE) {
- ssh_error(session, "Failed to authenticate SSH session");
+ if (!giterr_last())
+ ssh_error(session, "Failed to authenticate SSH session");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, int auth_methods)
+{
+ int error, no_callback = 0;
+ git_cred *cred = NULL;
+
+ if (!t->owner->cred_acquire_cb) {
+ no_callback = 1;
+ } else {
+ error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
+ t->owner->cred_acquire_payload);
+
+ if (error == GIT_PASSTHROUGH)
+ no_callback = 1;
+ else if (error < 0)
+ return error;
+ else if (!cred) {
+ giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
+ return -1;
+ }
+ }
+
+ if (no_callback) {
+ giterr_set(GITERR_SSH, "authentication required but no callback set");
+ return -1;
+ }
+
+ if (!(cred->credtype & auth_methods)) {
+ cred->free(cred);
+ giterr_set(GITERR_SSH, "callback returned unsupported credentials type");
return -1;
}
+ *out = cred;
+
return 0;
}
@@ -387,8 +451,9 @@ static int _git_ssh_setup_conn(
{
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *default_port="22";
- int no_callback = 0;
+ int auth_methods, error = 0;
ssh_stream *s;
+ git_cred *cred = NULL;
LIBSSH2_SESSION* session=NULL;
LIBSSH2_CHANNEL* channel=NULL;
@@ -399,56 +464,102 @@ static int _git_ssh_setup_conn(
s = (ssh_stream *)*stream;
if (!git__prefixcmp(url, prefix_ssh)) {
- if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
+ if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
goto on_error;
} else {
- if (git_ssh_extract_url_parts(&host, &user, url) < 0)
+ if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
goto on_error;
port = git__strdup(default_port);
GITERR_CHECK_ALLOC(port);
}
- if (gitno_connect(&s->socket, host, port, 0) < 0)
+ if ((error = gitno_connect(&s->socket, host, port, 0)) < 0)
+ goto on_error;
+
+ if ((error = _git_ssh_session_create(&session, s->socket)) < 0)
goto on_error;
- if (user && pass) {
- if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
+ if (t->owner->certificate_check_cb != NULL) {
+ git_cert_hostkey cert = { 0 };
+ const char *key;
+
+ cert.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA1;
+ memcpy(&cert.hash_sha1, key, 20);
+ }
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_MD5;
+ memcpy(&cert.hash_md5, key, 16);
+ }
+
+ if (cert.type == 0) {
+ giterr_set(GITERR_SSH, "unable to get the host key");
+ return -1;
+ }
+
+ /* We don't currently trust any hostkeys */
+ giterr_clear();
+ error = t->owner->certificate_check_cb((git_cert *) &cert, 0, t->owner->message_cb_payload);
+ if (error < 0) {
+ if (!giterr_last())
+ giterr_set(GITERR_NET, "user cancelled hostkey check");
+
goto on_error;
- } else if (!t->owner->cred_acquire_cb) {
- no_callback = 1;
- } else {
- int error;
- error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, user,
- GIT_CREDTYPE_USERPASS_PLAINTEXT |
- GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM |
- GIT_CREDTYPE_SSH_INTERACTIVE,
- t->owner->cred_acquire_payload);
+ }
+ }
- if (error == GIT_PASSTHROUGH)
- no_callback = 1;
- else if (error < 0)
+ /* we need the username to ask for auth methods */
+ if (!user) {
+ if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0)
goto on_error;
- else if (!t->cred) {
- giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
+
+ user = git__strdup(((git_cred_username *) cred)->username);
+ cred->free(cred);
+ cred = NULL;
+ if (!user)
+ goto on_error;
+ } else if (user && pass) {
+ if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0)
goto on_error;
- }
}
- if (no_callback) {
- giterr_set(GITERR_SSH, "authentication required but no callback set");
+ if ((error = list_auth_methods(&auth_methods, session, user)) < 0)
goto on_error;
- }
- assert(t->cred);
+ error = GIT_EAUTH;
+ /* if we already have something to try */
+ if (cred && auth_methods & cred->credtype)
+ error = _git_ssh_authenticate_session(session, cred);
- if (_git_ssh_session_create(&session, s->socket) < 0)
- goto on_error;
+ while (error == GIT_EAUTH) {
+ if (cred) {
+ cred->free(cred);
+ cred = NULL;
+ }
+
+ if ((error = request_creds(&cred, t, user, auth_methods)) < 0)
+ goto on_error;
+
+ if (strcmp(user, git_cred__username(cred))) {
+ giterr_set(GITERR_SSH, "username does not match previous request");
+ error = -1;
+ goto on_error;
+ }
+
+ error = _git_ssh_authenticate_session(session, cred);
+ }
- if (_git_ssh_authenticate_session(session, t->cred) < 0)
+ if (error < 0)
goto on_error;
channel = libssh2_channel_open_session(session);
if (!channel) {
+ error = -1;
ssh_error(session, "Failed to open SSH channel");
goto on_error;
}
@@ -459,6 +570,9 @@ static int _git_ssh_setup_conn(
s->channel = channel;
t->current_stream = s;
+ if (cred)
+ cred->free(cred);
+
git__free(host);
git__free(port);
git__free(path);
@@ -475,6 +589,9 @@ on_error:
if (*stream)
ssh_stream_free(*stream);
+ if (cred)
+ cred->free(cred);
+
git__free(host);
git__free(port);
git__free(user);
@@ -483,7 +600,7 @@ on_error:
if (session)
libssh2_session_free(session);
- return -1;
+ return error;
}
static int ssh_uploadpack_ls(
@@ -491,10 +608,9 @@ static int ssh_uploadpack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0)
- return -1;
+ const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
- return 0;
+ return _git_ssh_setup_conn(t, url, cmd, stream);
}
static int ssh_uploadpack(
@@ -518,10 +634,10 @@ static int ssh_receivepack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0)
- return -1;
+ const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
- return 0;
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
}
static int ssh_receivepack(
@@ -583,8 +699,57 @@ static void _ssh_free(git_smart_subtransport *subtransport)
assert(!t->current_stream);
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
git__free(t);
}
+
+#define SSH_AUTH_PUBLICKEY "publickey"
+#define SSH_AUTH_PASSWORD "password"
+#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
+
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
+{
+ const char *list, *ptr;
+
+ *out = 0;
+
+ list = libssh2_userauth_list(session, username, strlen(username));
+
+ /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
+ if (list == NULL && !libssh2_userauth_authenticated(session))
+ return -1;
+
+ ptr = list;
+ while (ptr) {
+ if (*ptr == ',')
+ ptr++;
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
+ *out |= GIT_CREDTYPE_SSH_KEY;
+ *out |= GIT_CREDTYPE_SSH_CUSTOM;
+ ptr += strlen(SSH_AUTH_PUBLICKEY);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
+ *out |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+ ptr += strlen(SSH_AUTH_PASSWORD);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
+ *out |= GIT_CREDTYPE_SSH_INTERACTIVE;
+ ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
+ continue;
+ }
+
+ /* Skipt it if we don't know it */
+ ptr = strchr(ptr, ',');
+ }
+
+ return 0;
+}
#endif
int git_smart_subtransport_ssh(
@@ -615,3 +780,46 @@ int git_smart_subtransport_ssh(
return -1;
#endif
}
+
+int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
+{
+#ifdef GIT_SSH
+ git_strarray *paths = (git_strarray *) payload;
+ git_transport *transport;
+ transport_smart *smart;
+ ssh_subtransport *t;
+ int error;
+ git_smart_subtransport_definition ssh_definition = {
+ git_smart_subtransport_ssh,
+ 0, /* no RPC */
+ };
+
+ if (paths->count != 2) {
+ giterr_set(GITERR_SSH, "invalid ssh paths, must be two strings");
+ return GIT_EINVALIDSPEC;
+ }
+
+ if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
+ return error;
+
+ smart = (transport_smart *) transport;
+ t = (ssh_subtransport *) smart->wrapped;
+
+ t->cmd_uploadpack = git__strdup(paths->strings[0]);
+ GITERR_CHECK_ALLOC(t->cmd_uploadpack);
+ t->cmd_receivepack = git__strdup(paths->strings[1]);
+ GITERR_CHECK_ALLOC(t->cmd_receivepack);
+
+ *out = transport;
+ return 0;
+#else
+ GIT_UNUSED(owner);
+ GIT_UNUSED(payload);
+
+ assert(out);
+ *out = NULL;
+
+ giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+ return -1;
+#endif
+}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index bd9509cd4..8aef63193 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -16,6 +16,8 @@
#include "remote.h"
#include "repository.h"
+#include <wincrypt.h>
+#pragma comment(lib, "crypt32")
#include <winhttp.h>
#pragma comment(lib, "winhttp")
@@ -35,6 +37,11 @@
#define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109
#define CACHED_POST_BODY_BUF_SIZE 4096
#define UUID_LENGTH_CCH 32
+#define TIMEOUT_INFINITE -1
+#define DEFAULT_CONNECT_TIMEOUT 60000
+#ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH
+#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
+#endif
static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
@@ -97,7 +104,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred)
if (git_buf_oom(&raw) ||
git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
- git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
+ git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
goto on_error;
if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
@@ -198,6 +205,39 @@ static int fallback_cred_acquire_cb(
return error;
}
+static int certificate_check(winhttp_stream *s, int valid)
+{
+ int error;
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ PCERT_CONTEXT cert_ctx;
+ DWORD cert_ctx_size = sizeof(cert_ctx);
+ git_cert_x509 cert;
+
+ /* If there is no override, we should fail if WinHTTP doesn't think it's fine */
+ if (t->owner->certificate_check_cb == NULL && !valid)
+ return GIT_ECERTIFICATE;
+
+ if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl)
+ return 0;
+
+ if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
+ giterr_set(GITERR_OS, "failed to get server certificate");
+ return -1;
+ }
+
+ giterr_clear();
+ cert.cert_type = GIT_CERT_X509;
+ cert.data = cert_ctx->pbCertEncoded;
+ cert.len = cert_ctx->cbCertEncoded;
+ error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->owner->cred_acquire_payload);
+ CertFreeCertificateContext(cert_ctx);
+
+ if (error < 0 && !giterr_last())
+ giterr_set(GITERR_NET, "user cancelled certificate check");
+
+ return error;
+}
+
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -208,6 +248,8 @@ static int winhttp_stream_connect(winhttp_stream *s)
BOOL peerdist = FALSE;
int error = -1;
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
+ int default_timeout = TIMEOUT_INFINITE;
+ int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
/* Prepare URL */
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
@@ -236,6 +278,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
+ if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
+ giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
+ goto on_error;
+ }
+
/* Set proxy if necessary */
if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
goto on_error;
@@ -341,13 +388,6 @@ static int winhttp_stream_connect(winhttp_stream *s)
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
goto on_error;
-
- if ((GIT_TRANSPORTFLAGS_NO_CHECK_CERT & flags) &&
- !WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS,
- (LPVOID)&no_check_cert_flags, sizeof(no_check_cert_flags))) {
- giterr_set(GITERR_OS, "Failed to set options to ignore cert errors");
- goto on_error;
- }
}
/* If we have a credential on the subtransport, apply it to the request */
@@ -463,6 +503,8 @@ static int winhttp_connect(
int32_t port;
const char *default_port = "80";
int error = -1;
+ int default_timeout = TIMEOUT_INFINITE;
+ int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
/* Prepare port */
if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
@@ -487,6 +529,12 @@ static int winhttp_connect(
goto on_error;
}
+ if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
+ giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
+ goto on_error;
+ }
+
+
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
@@ -507,6 +555,74 @@ on_error:
return error;
}
+static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
+{
+ int request_failed = 0, cert_valid = 1, error = 0;
+
+ if (ignore_length) {
+ if (!WinHttpSendRequest(s->request,
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ WINHTTP_NO_REQUEST_DATA, 0,
+ WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) {
+ return -1;
+ }
+ } else {
+ if (!WinHttpSendRequest(s->request,
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ WINHTTP_NO_REQUEST_DATA, 0,
+ len, 0)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int send_request(winhttp_stream *s, size_t len, int ignore_length)
+{
+ int request_failed = 0, cert_valid = 1, error = 0;
+ DWORD ignore_flags;
+
+ if ((error = do_send_request(s, len, ignore_length)) < 0)
+ request_failed = 1;
+
+ if (request_failed) {
+ if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
+ giterr_set(GITERR_OS, "failed to send request");
+ return -1;
+ } else {
+ cert_valid = 0;
+ }
+ }
+
+ giterr_clear();
+ if ((error = certificate_check(s, cert_valid)) < 0) {
+ if (!giterr_last())
+ giterr_set(GITERR_OS, "user cancelled certificate check");
+
+ return error;
+ }
+
+ /* if neither the request nor the certificate check returned errors, we're done */
+ if (!request_failed)
+ return 0;
+
+ ignore_flags =
+ SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
+ SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+ SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
+ giterr_set(GITERR_OS, "failed to set security options");
+ return -1;
+ }
+
+ if ((error = do_send_request(s, len, ignore_length)) < 0)
+ giterr_set(GITERR_OS, "failed to send request");
+
+ return error;
+}
+
static int winhttp_stream_read(
git_smart_subtransport_stream *stream,
char *buffer,
@@ -517,6 +633,7 @@ static int winhttp_stream_read(
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD dw_bytes_read;
char replay_count = 0;
+ int error;
replay:
/* Enforce a reasonable cap on the number of replays */
@@ -533,15 +650,12 @@ replay:
DWORD status_code, status_code_length, content_type_length, bytes_written;
char expected_content_type_8[MAX_CONTENT_TYPE_LEN];
wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN];
+ int request_failed = 0, cert_valid = 1;
if (!s->sent_request) {
- if (!WinHttpSendRequest(s->request,
- WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- WINHTTP_NO_REQUEST_DATA, 0,
- s->post_body_len, 0)) {
- giterr_set(GITERR_OS, "Failed to send request");
- return -1;
- }
+
+ if ((error = send_request(s, s->post_body_len, 0)) < 0)
+ return error;
s->sent_request = 1;
}
@@ -745,9 +859,9 @@ replay:
/* Verify that we got the correct content-type back */
if (post_verb == s->verb)
- snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
else
- snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters");
@@ -795,6 +909,7 @@ static int winhttp_stream_write_single(
winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
DWORD bytes_written;
+ int error;
if (!s->request && winhttp_stream_connect(s) < 0)
return -1;
@@ -805,13 +920,8 @@ static int winhttp_stream_write_single(
return -1;
}
- if (!WinHttpSendRequest(s->request,
- WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- WINHTTP_NO_REQUEST_DATA, 0,
- (DWORD)len, 0)) {
- giterr_set(GITERR_OS, "Failed to send request");
- return -1;
- }
+ if ((error = send_request(s, len, 0)) < 0)
+ return error;
s->sent_request = 1;
@@ -934,6 +1044,7 @@ static int winhttp_stream_write_chunked(
{
winhttp_stream *s = (winhttp_stream *)stream;
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ int error;
if (!s->request && winhttp_stream_connect(s) < 0)
return -1;
@@ -947,13 +1058,8 @@ static int winhttp_stream_write_chunked(
return -1;
}
- if (!WinHttpSendRequest(s->request,
- WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- WINHTTP_NO_REQUEST_DATA, 0,
- WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) {
- giterr_set(GITERR_OS, "Failed to send request");
- return -1;
- }
+ if ((error = send_request(s, 0, 1)) < 0)
+ return error;
s->sent_request = 1;
}
@@ -1112,9 +1218,9 @@ static int winhttp_action(
int ret = -1;
if (!t->connection)
- if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
- winhttp_connect(t, url) < 0)
- return -1;
+ if ((ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0 ||
+ (ret = winhttp_connect(t, url)) < 0)
+ return ret;
if (winhttp_stream_alloc(t, &s) < 0)
return -1;
diff --git a/src/tree.c b/src/tree.c
index 94f779eca..28190d6da 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -17,6 +17,8 @@
#define DEFAULT_TREE_SIZE 16
#define MAX_FILEMODE_BYTES 6
+GIT__USE_STRMAP;
+
static bool valid_filemode(const int filemode)
{
return (filemode == GIT_FILEMODE_TREE
@@ -365,7 +367,8 @@ size_t git_tree_entrycount(const git_tree *tree)
unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
{
assert(bld);
- return (unsigned int)bld->entrycount;
+
+ return git_strmap_num_entries(bld->map);
}
static int tree_error(const char *str, const char *path)
@@ -450,6 +453,7 @@ static int append_entry(
git_filemode_t filemode)
{
git_tree_entry *entry;
+ int error = 0;
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
@@ -460,12 +464,13 @@ static int append_entry(
git_oid_cpy(&entry->oid, id);
entry->attr = (uint16_t)filemode;
- if (git_vector_insert(&bld->entries, entry) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+ if (error < 0) {
+ git_tree_entry_free(entry);
+ giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
return -1;
}
- bld->entrycount++;
return 0;
}
@@ -610,18 +615,17 @@ int git_tree__write_index(
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
{
git_treebuilder *bld;
- size_t i, source_entries = DEFAULT_TREE_SIZE;
+ size_t i;
assert(builder_p);
bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld);
- if (source != NULL)
- source_entries = source->entries.length;
-
- if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
- goto on_error;
+ if (git_strmap_alloc(&bld->map) < 0) {
+ git__free(bld);
+ return -1;
+ }
if (source != NULL) {
git_tree_entry *entry_src;
@@ -651,7 +655,8 @@ int git_treebuilder_insert(
git_filemode_t filemode)
{
git_tree_entry *entry;
- size_t pos;
+ int error;
+ git_strmap_iter pos;
assert(bld && id && filename);
@@ -661,22 +666,20 @@ int git_treebuilder_insert(
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
- if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) {
- entry = git_vector_get(&bld->entries, pos);
- if (entry->removed) {
- entry->removed = 0;
- bld->entrycount++;
- }
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos)) {
+ entry = git_strmap_value_at(bld->map, pos);
} else {
entry = alloc_entry(filename);
GITERR_CHECK_ALLOC(entry);
- if (git_vector_insert(&bld->entries, entry) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+
+ if (error < 0) {
+ git_tree_entry_free(entry);
+ giterr_set(GITERR_TREE, "failed to insert %s", filename);
return -1;
}
-
- bld->entrycount++;
}
git_oid_cpy(&entry->oid, id);
@@ -690,17 +693,14 @@ int git_treebuilder_insert(
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
{
- size_t idx;
- git_tree_entry *entry;
+ git_tree_entry *entry = NULL;
+ git_strmap_iter pos;
assert(bld && filename);
- if (tree_key_search(&idx, &bld->entries, filename, strlen(filename)) < 0)
- return NULL;
-
- entry = git_vector_get(&bld->entries, idx);
- if (entry->removed)
- return NULL;
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos))
+ entry = git_strmap_value_at(bld->map, pos);
return entry;
}
@@ -712,35 +712,44 @@ const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *file
int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
{
- git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
+ git_tree_entry *entry = treebuilder_get(bld, filename);
- if (remove_ptr == NULL || remove_ptr->removed)
+ if (entry == NULL)
return tree_error("Failed to remove entry. File isn't in the tree", filename);
- remove_ptr->removed = 1;
- bld->entrycount--;
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+
return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
int error = 0;
- size_t i;
+ size_t i, entrycount;
git_buf tree = GIT_BUF_INIT;
git_odb *odb;
+ git_tree_entry *entry;
+ git_vector entries;
assert(bld);
- git_vector_sort(&bld->entries);
+ entrycount = git_strmap_num_entries(bld->map);
+ if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
+ return -1;
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, bld->entries.length * 72);
+ git_strmap_foreach_value(bld->map, entry, {
+ if (git_vector_insert(&entries, entry) < 0)
+ return -1;
+ });
- for (i = 0; i < bld->entries.length && !error; ++i) {
- git_tree_entry *entry = git_vector_get(&bld->entries, i);
+ git_vector_sort(&entries);
+
+ /* Grow the buffer beforehand to an estimated size */
+ error = git_buf_grow(&tree, entrycount * 72);
- if (entry->removed)
- continue;
+ for (i = 0; i < entries.length && !error; ++i) {
+ git_tree_entry *entry = git_vector_get(&entries, i);
git_buf_printf(&tree, "%o ", entry->attr);
git_buf_put(&tree, entry->filename, entry->filename_len + 1);
@@ -750,6 +759,8 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
error = -1;
}
+ git_vector_free(&entries);
+
if (!error &&
!(error = git_repository_odb__weakptr(&odb, repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
@@ -763,31 +774,27 @@ void git_treebuilder_filter(
git_treebuilder_filter_cb filter,
void *payload)
{
- size_t i;
+ const char *filename;
git_tree_entry *entry;
assert(bld && filter);
- git_vector_foreach(&bld->entries, i, entry) {
- if (!entry->removed && filter(entry, payload)) {
- entry->removed = 1;
- bld->entrycount--;
- }
- }
+ git_strmap_foreach(bld->map, filename, entry, {
+ if (filter(entry, payload)) {
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+ }
+ });
}
void git_treebuilder_clear(git_treebuilder *bld)
{
- size_t i;
git_tree_entry *e;
assert(bld);
- git_vector_foreach(&bld->entries, i, e)
- git_tree_entry_free(e);
-
- git_vector_clear(&bld->entries);
- bld->entrycount = 0;
+ git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
+ git_strmap_clear(bld->map);
}
void git_treebuilder_free(git_treebuilder *bld)
@@ -796,7 +803,7 @@ void git_treebuilder_free(git_treebuilder *bld)
return;
git_treebuilder_clear(bld);
- git_vector_free(&bld->entries);
+ git_strmap_free(bld->map);
git__free(bld);
}
diff --git a/src/tree.h b/src/tree.h
index f07039a07..5d27eb7c9 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -11,9 +11,9 @@
#include "repository.h"
#include "odb.h"
#include "vector.h"
+#include "strmap.h"
struct git_tree_entry {
- uint16_t removed;
uint16_t attr;
git_oid oid;
size_t filename_len;
@@ -26,8 +26,7 @@ struct git_tree {
};
struct git_treebuilder {
- git_vector entries;
- size_t entrycount; /* vector may contain "removed" entries */
+ git_strmap *map;
};
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
diff --git a/src/unix/map.c b/src/unix/map.c
index e62ab3e76..3d0cbbaf8 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -10,8 +10,14 @@
#include "map.h"
#include <sys/mman.h>
+#include <unistd.h>
#include <errno.h>
+long git__page_size(void)
+{
+ return sysconf(_SC_PAGE_SIZE);
+}
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
int mprot = 0;
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 1e41bcf18..e4f3ac67a 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -4,33 +4,47 @@
* 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_posix__w32_h__
-#define INCLUDE_posix__w32_h__
+#ifndef INCLUDE_posix__unix_h__
+#define INCLUDE_posix__unix_h__
#include <stdio.h>
#include <sys/param.h>
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#define p_lseek(f,n,w) lseek(f, n, w)
+#define p_fstat(f,b) fstat(f, b)
#define p_lstat(p,b) lstat(p,b)
+#define p_stat(p,b) stat(p, b)
+
#define p_readlink(a, b, c) readlink(a, b, c)
#define p_symlink(o,n) symlink(o, n)
#define p_link(o,n) link(o, n)
#define p_unlink(p) unlink(p)
#define p_mkdir(p,m) mkdir(p, m)
#define p_fsync(fd) fsync(fd)
+extern char *p_realpath(const char *, char *);
-/* The OpenBSD realpath function behaves differently */
-#if !defined(__OpenBSD__)
-# define p_realpath(p, po) realpath(p, po)
-#else
-char *p_realpath(const char *, char *);
-#endif
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+#define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
+#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p)
-#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+#define p_chdir(p) chdir(p)
+#define p_chmod(p,m) chmod(p, m)
+#define p_rmdir(p) rmdir(p)
+#define p_access(p,m) access(p,m)
+#define p_ftruncate(fd, sz) ftruncate(fd, sz)
/* see win32/posix.h for explanation about why this exists */
#define p_lstat_posixly(p,b) lstat(p,b)
+#define p_localtime_r(c, r) localtime_r(c, r)
+#define p_gmtime_r(c, r) gmtime_r(c, r)
+
#endif
diff --git a/src/unix/realpath.c b/src/unix/realpath.c
index 15601bd22..2e49150c2 100644
--- a/src/unix/realpath.c
+++ b/src/unix/realpath.c
@@ -6,7 +6,7 @@
*/
#include <git2/common.h>
-#ifdef __OpenBSD__
+#ifndef GIT_WIN32
#include <limits.h>
#include <stdlib.h>
@@ -16,15 +16,16 @@
char *p_realpath(const char *pathname, char *resolved)
{
char *ret;
-
if ((ret = realpath(pathname, resolved)) == NULL)
return NULL;
- /* Figure out if the file exists */
- if (!access(ret, F_OK))
- return ret;
-
- return NULL;
+#ifdef __OpenBSD__
+ /* The OpenBSD realpath function behaves differently,
+ * figure out if the file exists */
+ if (access(ret, F_OK) < 0)
+ ret = NULL;
+#endif
+ return ret;
}
#endif
diff --git a/src/util.c b/src/util.c
index 39858254f..5c305950f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -612,8 +612,9 @@ void git__qsort_r(
#if defined(__MINGW32__) || defined(AMIGA) || \
defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__gnu_hurd__) || defined(__ANDROID_API__) || \
- defined(__sun) || \
- (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)
+ defined(__sun) || defined(__CYGWIN__) || \
+ (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \
+ (defined(_MSC_VER) && _MSC_VER < 1500)
git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
#elif defined(GIT_WIN32)
git__qsort_r_glue glue = { cmp, payload };
diff --git a/src/util.h b/src/util.h
index 6fb2dc0f4..6e57ad8c3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -133,6 +133,13 @@ GIT_INLINE(int) git__is_uint32(size_t p)
return p == (size_t)r;
}
+/** @return true if p fits into the range of an unsigned long */
+GIT_INLINE(int) git__is_ulong(git_off_t p)
+{
+ unsigned long r = (unsigned long)p;
+ return p == (git_off_t)r;
+}
+
/* 32-bit cross-platform rotl */
#ifdef _MSC_VER /* use built-in method in MSVC */
# define git__rotl(v, s) (uint32_t)_rotl(v, s)
@@ -412,7 +419,7 @@ GIT_INLINE(double) git__timer(void)
scaling_factor = (double)info.numer / (double)info.denom;
}
- return (double)time * scaling_factor / 1.0E-9;
+ return (double)time * scaling_factor / 1.0E9;
}
#else
@@ -424,13 +431,13 @@ GIT_INLINE(double) git__timer(void)
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
- return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
+ return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9;
} else {
/* Fall back to using gettimeofday */
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
}
}
diff --git a/src/win32/map.c b/src/win32/map.c
index 902ea3994..ef83f882e 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -23,6 +23,11 @@ static DWORD get_page_size(void)
return page_size;
}
+long git__page_size(void)
+{
+ return (long)get_page_size();
+}
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
HANDLE fh = (HANDLE)_get_osfhandle(fd);
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 8f51d6f5a..83ee28765 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -9,17 +9,11 @@
#if defined(__MINGW32__)
-/* use a 64-bit file offset type */
-# define lseek _lseeki64
-# undef stat
-# define stat _stati64
-# undef fstat
-# define fstat _fstati64
-
-/* stat: file mode type testing macros */
-# define _S_IFLNK 0120000
-# define S_IFLNK _S_IFLNK
-# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#if _WIN32_WINNT >= 0x0601
+#define stat __stat64
+#else
+#define stat _stati64
+#endif
#endif
diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h
index 50865ed17..4789d63df 100644
--- a/src/win32/msvc-compat.h
+++ b/src/win32/msvc-compat.h
@@ -9,41 +9,12 @@
#if defined(_MSC_VER)
-/* access() mode parameter #defines */
-# define F_OK 0 /* existence check */
-# define W_OK 2 /* write mode check */
-# define R_OK 4 /* read mode check */
+/* 64-bit stat information, regardless of USE_32BIT_TIME_T define */
+#define stat __stat64
-# define lseek _lseeki64
-# define stat _stat64
-# define fstat _fstat64
-
-/* stat: file mode type testing macros */
-# define _S_IFLNK 0120000
-# define S_IFLNK _S_IFLNK
-# define S_IXUSR 00100
-
-# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
-# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
-# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
-# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
-
-# define mode_t unsigned short
-
-/* case-insensitive string comparison */
-# define strcasecmp _stricmp
-# define strncasecmp _strnicmp
-
-/* MSVC doesn't define ssize_t at all */
+typedef unsigned short mode_t;
typedef SSIZE_T ssize_t;
-/* define snprintf using variadic macro support if available */
-#if _MSC_VER >= 1400
-# define snprintf(BUF, SZ, FMT, ...) _snprintf_s(BUF, SZ, _TRUNCATE, FMT, __VA_ARGS__)
-#else
-# define snprintf _snprintf
-#endif
-
#endif
#define GIT_STDLIB_CALL __cdecl
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 7f9d57cc3..e055a77d0 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -12,43 +12,35 @@
#include "utf-conv.h"
#include "dir.h"
-/* define some standard errnos that the runtime may be missing. for example,
- * mingw lacks EAFNOSUPPORT. */
+typedef SOCKET GIT_SOCKET;
-#ifndef EAFNOSUPPORT
-# define EAFNOSUPPORT (INT_MAX-1)
-#endif
-
-GIT_INLINE(int) p_link(const char *old, const char *new)
-{
- GIT_UNUSED(old);
- GIT_UNUSED(new);
- errno = ENOSYS;
- return -1;
-}
-
-extern int p_mkdir(const char *path, mode_t mode);
-extern int p_unlink(const char *path);
+#define p_lseek(f,n,w) _lseeki64(f, n, w)
+#define p_fstat(f,b) _fstat64(f, b)
extern int p_lstat(const char *file_name, struct stat *buf);
+extern int p_stat(const char* path, struct stat* buf);
+
extern int p_readlink(const char *path, char *buf, size_t bufsiz);
extern int p_symlink(const char *old, const char *new);
+extern int p_link(const char *old, const char *new);
+extern int p_unlink(const char *path);
+extern int p_mkdir(const char *path, mode_t mode);
+extern int p_fsync(int fd);
extern char *p_realpath(const char *orig_path, char *buffer);
+
+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
+extern int p_inet_pton(int af, const char* src, void* dst);
+
+#define strcasecmp(s1, s2) _stricmp(s1, s2)
+#define strncasecmp(s1, s2, c) _strnicmp(s1, s2, c)
extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
extern int p_mkstemp(char *tmp_path);
-extern int p_stat(const char* path, struct stat* buf);
extern int p_chdir(const char* path);
extern int p_chmod(const char* path, mode_t mode);
extern int p_rmdir(const char* path);
extern int p_access(const char* path, mode_t mode);
-extern int p_fsync(int fd);
-extern int p_open(const char *path, int flags, ...);
-extern int p_creat(const char *path, mode_t mode);
-extern int p_getcwd(char *buffer_out, size_t size);
-extern int p_rename(const char *from, const char *to);
-extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
-extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
-extern int p_inet_pton(int af, const char* src, void* dst);
+extern int p_ftruncate(int fd, long size);
/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
* ENOTDIR is wrong, in that it does not mean precisely that a non-directory
@@ -58,4 +50,7 @@ extern int p_inet_pton(int af, const char* src, void* dst);
*/
extern int p_lstat_posixly(const char *filename, struct stat *buf);
+extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
+extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
+
#endif
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 0d070f6b5..0023f95ff 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -19,6 +19,19 @@
# define FILE_NAME_NORMALIZED 0
#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+
+/* Options which we always provide to _wopen.
+ *
+ * _O_BINARY - Raw access; no translation of CR or LF characters
+ * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
+ * The Windows default is 'not inheritable', but the CRT's default (following
+ * POSIX convention) is 'inheritable'. We have no desire for our handles to be
+ * inheritable on Windows, so specify the flag to get default behavior back. */
+#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
+
/* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
@@ -38,6 +51,15 @@ static int utf8_to_16_with_errno(git_win32_path dest, const char *src)
return len;
}
+int p_ftruncate(int fd, long size)
+{
+#if defined(_MSC_VER) && _MSC_VER >= 1500
+ return _chsize_s(fd, size);
+#else
+ return _chsize(fd, size);
+#endif
+}
+
int p_mkdir(const char *path, mode_t mode)
{
git_win32_path buf;
@@ -50,6 +72,14 @@ int p_mkdir(const char *path, mode_t mode)
return _wmkdir(buf);
}
+int p_link(const char *old, const char *new)
+{
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
+ errno = ENOSYS;
+ return -1;
+}
+
int p_unlink(const char *path)
{
git_win32_path buf;
@@ -317,7 +347,7 @@ int p_open(const char *path, int flags, ...)
va_end(arg_list);
}
- return _wopen(buf, flags | _O_BINARY, mode);
+ return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode);
}
int p_creat(const char *path, mode_t mode)
@@ -327,7 +357,7 @@ int p_creat(const char *path, mode_t mode)
if (utf8_to_16_with_errno(buf, path) < 0)
return -1;
- return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
+ return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode);
}
int p_getcwd(char *buffer_out, size_t size)
@@ -534,11 +564,19 @@ char *p_realpath(const char *orig_path, char *buffer)
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
int len;
- if (count == 0 ||
- (len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr)) < 0)
+ if (count == 0)
+ return _vscprintf(format, argptr);
+
+ #if _MSC_VER >= 1500
+ len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
+ #else
+ len = _vsnprintf(buffer, count, format, argptr);
+ #endif
+
+ if (len < 0)
return _vscprintf(format, argptr);
return len;
@@ -561,7 +599,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...)
int p_mkstemp(char *tmp_path)
{
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && _MSC_VER >= 1500
if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
return -1;
#else
@@ -569,7 +607,7 @@ int p_mkstemp(char *tmp_path)
return -1;
#endif
- return p_creat(tmp_path, 0744); //-V536
+ return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
}
int p_access(const char* path, mode_t mode)
@@ -582,6 +620,31 @@ int p_access(const char* path, mode_t mode)
return _waccess(buf, mode);
}
+static int ensure_writable(wchar_t *fpath)
+{
+ DWORD attrs;
+
+ attrs = GetFileAttributesW(fpath);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ return 0;
+
+ giterr_set(GITERR_OS, "failed to get attributes");
+ return -1;
+ }
+
+ if (!(attrs & FILE_ATTRIBUTE_READONLY))
+ return 0;
+
+ attrs &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(fpath, attrs)) {
+ giterr_set(GITERR_OS, "failed to set attributes");
+ return -1;
+ }
+
+ return 0;
+}
+
int p_rename(const char *from, const char *to)
{
git_win32_path wfrom;
@@ -593,12 +656,13 @@ int p_rename(const char *from, const char *to)
if (utf8_to_16_with_errno(wfrom, from) < 0 ||
utf8_to_16_with_errno(wto, to) < 0)
return -1;
-
+
/* wait up to 50ms if file is locked by another thread or process */
rename_tries = 0;
rename_succeeded = 0;
while (rename_tries < 10) {
- if (MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
+ if (ensure_writable(wto) == 0 &&
+ MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
rename_succeeded = 1;
break;
}
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index db8927471..ec45ecbe5 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -8,32 +8,64 @@
#include "pthread.h"
#include "../global.h"
-int pthread_create(
- pthread_t *GIT_RESTRICT thread,
+#define CLEAN_THREAD_EXIT 0x6F012842
+
+/* The thread procedure stub used to invoke the caller's procedure
+ * and capture the return value for later collection. Windows will
+ * only hold a DWORD, but we need to be able to store an entire
+ * void pointer. This requires the indirection. */
+static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
+{
+ git_win32_thread *thread = lpParameter;
+
+ thread->result = thread->proc(thread->param);
+
+ return CLEAN_THREAD_EXIT;
+}
+
+int git_win32__thread_create(
+ git_win32_thread *GIT_RESTRICT thread,
const pthread_attr_t *GIT_RESTRICT attr,
void *(*start_routine)(void*),
void *GIT_RESTRICT arg)
{
GIT_UNUSED(attr);
- *thread = CreateThread(
- NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
- return *thread ? 0 : -1;
+
+ thread->result = NULL;
+ thread->param = arg;
+ thread->proc = start_routine;
+ thread->thread = CreateThread(
+ NULL, 0, git_win32__threadproc, thread, 0, NULL);
+
+ return thread->thread ? 0 : -1;
}
-int pthread_join(pthread_t thread, void **value_ptr)
+int git_win32__thread_join(
+ git_win32_thread *thread,
+ void **value_ptr)
{
- DWORD ret = WaitForSingleObject(thread, INFINITE);
+ DWORD exit;
+
+ if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0)
+ return -1;
+
+ if (!GetExitCodeThread(thread->thread, &exit)) {
+ CloseHandle(thread->thread);
+ return -1;
+ }
- if (ret == WAIT_OBJECT_0) {
- if (value_ptr != NULL) {
- *value_ptr = NULL;
- GetExitCodeThread(thread, (void *)value_ptr);
- }
- CloseHandle(thread);
- return 0;
+ /* Check for the thread having exited uncleanly. If exit was unclean,
+ * then we don't have a return value to give back to the caller. */
+ if (exit != CLEAN_THREAD_EXIT) {
+ assert(false);
+ thread->result = NULL;
}
- return -1;
+ if (value_ptr)
+ *value_ptr = thread->result;
+
+ CloseHandle(thread->thread);
+ return 0;
}
int pthread_mutex_init(
@@ -144,9 +176,6 @@ int pthread_num_processors_np(void)
return n ? n : 1;
}
-
-static HINSTANCE win32_kernel32_dll;
-
typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
static win32_srwlock_fn win32_srwlock_initialize;
@@ -159,7 +188,7 @@ int pthread_rwlock_init(
pthread_rwlock_t *GIT_RESTRICT lock,
const pthread_rwlockattr_t *GIT_RESTRICT attr)
{
- (void)attr;
+ GIT_UNUSED(attr);
if (win32_srwlock_initialize)
win32_srwlock_initialize(&lock->native.srwl);
@@ -217,38 +246,22 @@ int pthread_rwlock_destroy(pthread_rwlock_t *lock)
return 0;
}
-
-static void win32_pthread_shutdown(void)
-{
- if (win32_kernel32_dll) {
- FreeLibrary(win32_kernel32_dll);
- win32_kernel32_dll = NULL;
- }
-}
-
int win32_pthread_initialize(void)
{
- if (win32_kernel32_dll)
- return 0;
-
- win32_kernel32_dll = LoadLibrary("Kernel32.dll");
- if (!win32_kernel32_dll) {
- giterr_set(GITERR_OS, "Could not load Kernel32.dll!");
- return -1;
+ HMODULE hModule = GetModuleHandleW(L"kernel32");
+
+ if (hModule) {
+ win32_srwlock_initialize = (win32_srwlock_fn)
+ GetProcAddress(hModule, "InitializeSRWLock");
+ win32_srwlock_acquire_shared = (win32_srwlock_fn)
+ GetProcAddress(hModule, "AcquireSRWLockShared");
+ win32_srwlock_release_shared = (win32_srwlock_fn)
+ GetProcAddress(hModule, "ReleaseSRWLockShared");
+ win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
+ GetProcAddress(hModule, "AcquireSRWLockExclusive");
+ win32_srwlock_release_exclusive = (win32_srwlock_fn)
+ GetProcAddress(hModule, "ReleaseSRWLockExclusive");
}
- win32_srwlock_initialize = (win32_srwlock_fn)
- GetProcAddress(win32_kernel32_dll, "InitializeSRWLock");
- win32_srwlock_acquire_shared = (win32_srwlock_fn)
- GetProcAddress(win32_kernel32_dll, "AcquireSRWLockShared");
- win32_srwlock_release_shared = (win32_srwlock_fn)
- GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockShared");
- win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
- GetProcAddress(win32_kernel32_dll, "AcquireSRWLockExclusive");
- win32_srwlock_release_exclusive = (win32_srwlock_fn)
- GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
-
- git__on_shutdown(win32_pthread_shutdown);
-
return 0;
}
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index af5b121f0..e4826ca7f 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -16,13 +16,19 @@
# define GIT_RESTRICT __restrict__
#endif
+typedef struct {
+ HANDLE thread;
+ void *(*proc)(void *);
+ void *param;
+ void *result;
+} git_win32_thread;
+
typedef int pthread_mutexattr_t;
typedef int pthread_condattr_t;
typedef int pthread_attr_t;
typedef int pthread_rwlockattr_t;
typedef CRITICAL_SECTION pthread_mutex_t;
-typedef HANDLE pthread_t;
typedef HANDLE pthread_cond_t;
typedef struct { void *Ptr; } GIT_SRWLOCK;
@@ -36,13 +42,26 @@ typedef struct {
#define PTHREAD_MUTEX_INITIALIZER {(void*)-1}
-int pthread_create(
- pthread_t *GIT_RESTRICT thread,
- const pthread_attr_t *GIT_RESTRICT attr,
- void *(*start_routine)(void*),
- void *GIT_RESTRICT arg);
+int git_win32__thread_create(
+ git_win32_thread *GIT_RESTRICT,
+ const pthread_attr_t *GIT_RESTRICT,
+ void *(*) (void *),
+ void *GIT_RESTRICT);
+
+int git_win32__thread_join(
+ git_win32_thread *,
+ void **);
+
+#ifdef GIT_THREADS
-int pthread_join(pthread_t, void **);
+typedef git_win32_thread git_thread;
+
+#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \
+ git_win32__thread_create(git_thread_ptr, attr, start_routine, arg)
+#define git_thread_join(git_thread_ptr, status) \
+ git_win32__thread_join(git_thread_ptr, status)
+
+#endif
int pthread_mutex_init(
pthread_mutex_t *GIT_RESTRICT mutex,
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index 68875194d..b187db01c 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -81,6 +81,24 @@ void test_attr_ignore__full_paths(void)
assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child");
}
+void test_attr_ignore__more_starstar_cases(void)
+{
+ cl_must_pass(p_unlink("attr/.gitignore"));
+ cl_git_mkfile(
+ "attr/dir/.gitignore",
+ "sub/**/*.html\n");
+
+ assert_is_ignored(false, "aaa.html");
+ assert_is_ignored(false, "dir");
+ assert_is_ignored(false, "dir/sub");
+ assert_is_ignored(true, "dir/sub/sub2/aaa.html");
+ assert_is_ignored(true, "dir/sub/aaa.html");
+ assert_is_ignored(false, "dir/aaa.html");
+ assert_is_ignored(false, "sub");
+ assert_is_ignored(false, "sub/aaa.html");
+ assert_is_ignored(false, "sub/sub2/aaa.html");
+}
+
void test_attr_ignore__leading_stars(void)
{
cl_git_rewritefile(
@@ -130,12 +148,11 @@ void test_attr_ignore__skip_gitignore_directory(void)
void test_attr_ignore__expand_tilde_to_homedir(void)
{
- git_buf cleanup = GIT_BUF_INIT;
git_config *cfg;
assert_is_ignored(false, "example.global_with_tilde");
- cl_fake_home(&cleanup);
+ cl_fake_home();
/* construct fake home with fake global excludes */
cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n");
@@ -150,7 +167,7 @@ void test_attr_ignore__expand_tilde_to_homedir(void)
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_fake_home_cleanup(&cleanup);
+ cl_fake_home_cleanup(NULL);
git_attr_cache_flush(g_repo); /* must reset to pick up change */
diff --git a/tests/attr/repo.c b/tests/attr/repo.c
index 9aab7ed96..8baf50622 100644
--- a/tests/attr/repo.c
+++ b/tests/attr/repo.c
@@ -4,16 +4,12 @@
#include "attr.h"
#include "attr_expect.h"
+#include "git2/sys/repository.h"
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
{
- /* Before each test, instantiate the attr repo from the fixtures and
- * rename the .gitted to .git so it is a repo with a working dir.
- * Also rename gitattributes to .gitattributes, because it contains
- * macro definitions which are only allowed in the root.
- */
g_repo = cl_git_sandbox_init("attr");
}
@@ -338,3 +334,45 @@ void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitat
git_index_free(index);
}
+
+void test_attr_repo__bare_repo_with_index(void)
+{
+ const char *names[4] = { "test1", "test2", "test3", "test4" };
+ const char *values[4];
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile(
+ "attr/.gitattributes",
+ "*.txt test1 test2=foobar -test3\n"
+ "trial.txt -test1 test2=barfoo !test3 test4\n");
+ cl_git_pass(git_index_add_bypath(index, ".gitattributes"));
+ git_index_free(index);
+
+ cl_must_pass(p_unlink("attr/.gitattributes"));
+ cl_assert(!git_path_exists("attr/.gitattributes"));
+
+ cl_git_pass(git_repository_set_bare(g_repo));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert_equal_s("foobar", values[1]);
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names));
+
+ cl_assert(GIT_ATTR_FALSE(values[0]));
+ cl_assert_equal_s("barfoo", values[1]);
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_TRUE(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert_equal_s("foobar", values[1]);
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+}
diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c
index 56240dbde..21cd1a615 100644
--- a/tests/blame/blame_helpers.c
+++ b/tests/blame/blame_helpers.c
@@ -17,7 +17,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
int start_line, int len, char boundary, const char *commit_id, const char *orig_path)
{
- char expected[41] = {0}, actual[41] = {0};
+ char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0};
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
cl_assert(hunk);
@@ -48,7 +48,7 @@ void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
actual, expected);
}
cl_assert_equal_s(actual, expected);
- cl_assert_equal_i(git_oid_cmp(&hunk->final_commit_id, &hunk->orig_commit_id), 0);
+ cl_assert_equal_oid(&hunk->final_commit_id, &hunk->orig_commit_id);
if (strcmp(hunk->orig_path, orig_path)) {
diff --git a/tests/checkout/binaryunicode.c b/tests/checkout/binaryunicode.c
index 1172816c7..27e70d3f1 100644
--- a/tests/checkout/binaryunicode.c
+++ b/tests/checkout/binaryunicode.c
@@ -37,12 +37,12 @@ static void execute_test(void)
/* Verify that the lenna.jpg file was checked out correctly */
cl_git_pass(git_oid_fromstr(&check, "8ab005d890fe53f65eda14b23672f60d9f4ec5a1"));
cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/lenna.jpg", GIT_OBJ_BLOB));
- cl_assert(git_oid_equal(&oid, &check));
+ cl_assert_equal_oid(&oid, &check);
/* Verify that the text file was checked out correctly */
cl_git_pass(git_oid_fromstr(&check, "965b223880dd4249e2c66a0cc0b4cffe1dc40f5a"));
cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/utf16_withbom_noeol_crlf.txt", GIT_OBJ_BLOB));
- cl_assert(git_oid_equal(&oid, &check));
+ cl_assert_equal_oid(&oid, &check);
}
void test_checkout_binaryunicode__noautocrlf(void)
diff --git a/tests/checkout/conflict.c b/tests/checkout/conflict.c
index 2cb7c224d..3d763af12 100644
--- a/tests/checkout/conflict.c
+++ b/tests/checkout/conflict.c
@@ -48,7 +48,7 @@ static git_index *g_index;
struct checkout_index_entry {
uint16_t mode;
- char oid_str[41];
+ char oid_str[GIT_OID_HEXSZ+1];
int stage;
char path[128];
};
@@ -156,7 +156,7 @@ static void ensure_workdir_oid(const char *path, const char *oid_str)
cl_git_pass(git_oid_fromstr(&expected, oid_str));
cl_git_pass(git_repository_hashfile(&actual, g_repo, path, GIT_OBJ_BLOB, NULL));
- cl_assert(git_oid_cmp(&expected, &actual) == 0);
+ cl_assert_equal_oid(&expected, &actual);
}
static void ensure_workdir_mode(const char *path, int mode)
@@ -169,7 +169,7 @@ static void ensure_workdir_mode(const char *path, int mode)
git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
cl_git_pass(p_stat(git_buf_cstr(&fullpath), &st));
- cl_assert_equal_i(mode, st.st_mode);
+ cl_assert_equal_i((mode & S_IRWXU), (st.st_mode & S_IRWXU));
git_buf_free(&fullpath);
#endif
@@ -1078,8 +1078,8 @@ static void collect_progress(
{
git_vector *paths = payload;
- (void)completed_steps;
- (void)total_steps;
+ GIT_UNUSED(completed_steps);
+ GIT_UNUSED(total_steps);
if (path == NULL)
return;
diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c
index 6b2c1b122..496f83d5d 100644
--- a/tests/checkout/crlf.c
+++ b/tests/checkout/crlf.c
@@ -79,10 +79,7 @@ void test_checkout_crlf__more_lf_autocrlf_true(void)
git_checkout_head(g_repo, &opts);
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
- else
- check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
}
void test_checkout_crlf__more_crlf_autocrlf_true(void)
@@ -94,10 +91,7 @@ void test_checkout_crlf__more_crlf_autocrlf_true(void)
git_checkout_head(g_repo, &opts);
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
- else
- check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
}
void test_checkout_crlf__all_crlf_autocrlf_true(void)
@@ -285,8 +279,13 @@ void test_checkout_crlf__autocrlf_false_text_auto_attr(void)
git_checkout_head(g_repo, &opts);
- check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
- check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+ if (GIT_EOL_NATIVE == GIT_EOL_CRLF) {
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
+ } else {
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+ }
}
void test_checkout_crlf__autocrlf_true_text_auto_attr(void)
diff --git a/tests/cherrypick/bare.c b/tests/cherrypick/bare.c
index 7ac1054a1..135336507 100644
--- a/tests/cherrypick/bare.c
+++ b/tests/cherrypick/bare.c
@@ -39,7 +39,7 @@ void test_cherrypick_bare__automerge(void)
git_oid_fromstr(&cherry_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL));
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 3));
git_index_free(index);
@@ -69,7 +69,7 @@ void test_cherrypick_bare__conflicts(void)
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL));
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 7));
git_index_free(index);
@@ -96,7 +96,7 @@ void test_cherrypick_bare__orphan(void)
git_oid_fromstr(&cherry_oid, "74f06b5bfec6d33d7264f73606b57a7c0b963819");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick_commit(&index, repo, commit, head, 0, NULL));
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
cl_assert(merge_test_index(index, merge_index_entries, 4));
git_index_free(index);
diff --git a/tests/cherrypick/workdir.c b/tests/cherrypick/workdir.c
index 581a5f997..feacde323 100644
--- a/tests/cherrypick/workdir.c
+++ b/tests/cherrypick/workdir.c
@@ -36,7 +36,7 @@ void test_cherrypick_workdir__automerge(void)
git_signature *signature = NULL;
size_t i;
- const char *cherry_pick_oids[] = {
+ const char *cherrypick_oids[] = {
"cfc4f0999a8367568e049af4f72e452d40828a15",
"964ea3da044d9083181a88ba6701de9e35778bf4",
"a43a050c588d4e92f11a6b139680923e9728477d",
@@ -62,29 +62,29 @@ void test_cherrypick_workdir__automerge(void)
for (i = 0; i < 3; ++i) {
git_commit *head = NULL, *commit = NULL;
- git_oid cherry_oid, cherry_picked_oid, cherry_picked_tree_oid;
- git_tree *cherry_picked_tree = NULL;
+ git_oid cherry_oid, cherrypicked_oid, cherrypicked_tree_oid;
+ git_tree *cherrypicked_tree = NULL;
cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL));
- git_oid_fromstr(&cherry_oid, cherry_pick_oids[i]);
+ git_oid_fromstr(&cherry_oid, cherrypick_oids[i]);
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, NULL));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
- cl_git_pass(git_index_write_tree(&cherry_picked_tree_oid, repo_index));
- cl_git_pass(git_tree_lookup(&cherry_picked_tree, repo, &cherry_picked_tree_oid));
- cl_git_pass(git_commit_create(&cherry_picked_oid, repo, "HEAD", signature, signature, NULL,
- "Cherry picked!", cherry_picked_tree, 1, (const git_commit **)&head));
+ cl_git_pass(git_index_write_tree(&cherrypicked_tree_oid, repo_index));
+ cl_git_pass(git_tree_lookup(&cherrypicked_tree, repo, &cherrypicked_tree_oid));
+ cl_git_pass(git_commit_create(&cherrypicked_oid, repo, "HEAD", signature, signature, NULL,
+ "Cherry picked!", cherrypicked_tree, 1, (const git_commit **)&head));
cl_assert(merge_test_index(repo_index, merge_index_entries + i * 3, 3));
- git_oid_cpy(&head_oid, &cherry_picked_oid);
+ git_oid_cpy(&head_oid, &cherrypicked_oid);
- git_tree_free(cherry_picked_tree);
+ git_tree_free(cherrypicked_tree);
git_commit_free(head);
git_commit_free(commit);
}
@@ -92,6 +92,47 @@ void test_cherrypick_workdir__automerge(void)
git_signature_free(signature);
}
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d*/
+void test_cherrypick_workdir__empty_result(void)
+{
+ git_oid head_oid;
+ git_signature *signature = NULL;
+ git_commit *head = NULL, *commit = NULL;
+ git_oid cherry_oid;
+
+ const char *cherrypick_oid = "a43a050c588d4e92f11a6b139680923e9728477d";
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt" },
+ };
+
+ cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0));
+
+ git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15");
+
+ /* Create an untracked file that should not conflict */
+ cl_git_mkfile(TEST_REPO_PATH "/file4.txt", "");
+ cl_assert(git_path_exists(TEST_REPO_PATH "/file4.txt"));
+
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL));
+
+ git_oid_fromstr(&cherry_oid, cherrypick_oid);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
+
+ /* The resulting tree should not have changed, the change was already on HEAD */
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(head);
+ git_commit_free(commit);
+
+ git_signature_free(signature);
+}
+
/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8
* git cherry-pick e9b63f3655b2ad80c0ff587389b5a9589a3a7110
*/
@@ -118,7 +159,7 @@ void test_cherrypick_workdir__conflicts(void)
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, NULL));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
@@ -198,7 +239,7 @@ void test_cherrypick_workdir__conflict_use_ours(void)
{
git_commit *head = NULL, *commit = NULL;
git_oid head_oid, cherry_oid;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
@@ -226,7 +267,7 @@ void test_cherrypick_workdir__conflict_use_ours(void)
git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 7));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
@@ -235,7 +276,7 @@ void test_cherrypick_workdir__conflict_use_ours(void)
opts.merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS;
cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_filesystem_entries, 3));
cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
@@ -251,7 +292,7 @@ void test_cherrypick_workdir__rename(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
@@ -268,7 +309,7 @@ void test_cherrypick_workdir__rename(void)
git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
@@ -284,7 +325,7 @@ void test_cherrypick_workdir__both_renamed(void)
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
git_buf mergemsg_buf = GIT_BUF_INIT;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
@@ -303,7 +344,7 @@ void test_cherrypick_workdir__both_renamed(void)
git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 5));
@@ -326,13 +367,13 @@ void test_cherrypick_workdir__nonmerge_fails_mainline_specified(void)
{
git_reference *head;
git_commit *commit;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
cl_git_pass(git_repository_head(&head, repo));
cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT));
opts.mainline = 1;
- cl_must_fail(git_cherry_pick(repo, commit, &opts));
+ cl_must_fail(git_cherrypick(repo, commit, &opts));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
@@ -355,7 +396,7 @@ void test_cherrypick_workdir__merge_fails_without_mainline_specified(void)
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_must_fail(git_cherry_pick(repo, commit, NULL));
+ cl_must_fail(git_cherrypick(repo, commit, NULL));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
@@ -370,7 +411,7 @@ void test_cherrypick_workdir__merge_first_parent(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "f90f9dcbdac2cce5cc166346160e19cb693ef4e8", 0, "file1.txt" },
@@ -387,7 +428,7 @@ void test_cherrypick_workdir__merge_first_parent(void)
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
@@ -402,7 +443,7 @@ void test_cherrypick_workdir__merge_second_parent(void)
{
git_commit *head, *commit;
git_oid head_oid, cherry_oid;
- git_cherry_pick_options opts = GIT_CHERRY_PICK_OPTIONS_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
struct merge_index_entry merge_index_entries[] = {
{ 0100644, "487434cace79238a7091e2220611d4f20a765690", 0, "file1.txt" },
@@ -419,7 +460,7 @@ void test_cherrypick_workdir__merge_second_parent(void)
git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff");
cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
- cl_git_pass(git_cherry_pick(repo, commit, &opts));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index 6f6143dad..faeee031a 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -408,7 +408,8 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg)
int val = 0;
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_get_bool(&val, config, cfg));;
+ if (git_config_get_bool(&val, config, cfg) < 0)
+ giterr_clear();
git_config_free(config);
return val;
}
@@ -470,7 +471,7 @@ void clar__assert_equal_file(
buf, sizeof(buf), "file content mismatch at byte %d",
(int)(total_bytes + pos));
p_close(fd);
- clar__fail(file, line, buf, path, 1);
+ clar__fail(file, line, path, buf, 1);
}
expected_data += bytes;
@@ -484,23 +485,49 @@ void clar__assert_equal_file(
(size_t)expected_bytes, (size_t)total_bytes);
}
-void cl_fake_home(git_buf *restore)
+static char *_cl_restore_home = NULL;
+
+void cl_fake_home_cleanup(void *payload)
+{
+ char *restore = _cl_restore_home;
+ _cl_restore_home = NULL;
+
+ GIT_UNUSED(payload);
+
+ if (restore) {
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
+ git__free(restore);
+ }
+}
+
+void cl_fake_home(void)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_libgit2_opts(
- GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &path));
+
+ _cl_restore_home = git_buf_detach(&path);
+ cl_set_cleanup(cl_fake_home_cleanup, NULL);
- cl_must_pass(p_mkdir("home", 0777));
+ if (!git_path_exists("home"))
+ cl_must_pass(p_mkdir("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_buf_free(&path);
}
-void cl_fake_home_cleanup(git_buf *restore)
+void cl_sandbox_set_search_path_defaults(void)
{
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore->ptr));
- git_buf_free(restore);
+ const char *sandbox_path = clar_sandbox_path();
+
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path);
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path);
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path);
}
+
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
index 082fa9f4a..f51554293 100644
--- a/tests/clar_libgit2.h
+++ b/tests/clar_libgit2.h
@@ -3,6 +3,7 @@
#include "clar.h"
#include <git2.h>
+#include <posix.h>
#include "common.h"
/**
@@ -51,7 +52,7 @@ GIT_INLINE(void) clar__assert_in_range(
{
if (lo > val || hi < val) {
char buf[128];
- snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
+ p_snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
clar__fail(file, line, err, buf, should_abort);
}
}
@@ -78,6 +79,24 @@ void clar__assert_equal_file(
const char *file,
int line);
+GIT_INLINE(void) clar__assert_equal_oid(
+ const char *file, int line, const char *desc,
+ const git_oid *one, const git_oid *two)
+{
+ if (git_oid_cmp(one, two)) {
+ char err[] = "\"........................................\" != \"........................................\"";
+
+ git_oid_fmt(&err[1], one);
+ git_oid_fmt(&err[47], two);
+
+ clar__fail(file, line, desc, err, 1);
+ }
+}
+
+#define cl_assert_equal_oid(one, two) \
+ clar__assert_equal_oid(__FILE__, __LINE__, \
+ "OID mismatch: " #one " != " #two, (one), (two))
+
/*
* Some utility macros for building long strings
*/
@@ -131,7 +150,14 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg);
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
-void cl_fake_home(git_buf *restore);
-void cl_fake_home_cleanup(git_buf *restore);
+/* set up a fake "home" directory and set libgit2 GLOBAL search path.
+ *
+ * automatically configures cleanup function to restore the regular search
+ * path, although you can call it explicitly if you wish (with NULL).
+ */
+void cl_fake_home(void);
+void cl_fake_home_cleanup(void *);
+
+void cl_sandbox_set_search_path_defaults(void);
#endif
diff --git a/tests/clone/local.c b/tests/clone/local.c
new file mode 100644
index 000000000..78d026794
--- /dev/null
+++ b/tests/clone/local.c
@@ -0,0 +1,123 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+#include "fileops.h"
+
+static int file_url(git_buf *buf, const char *host, const char *path)
+{
+ if (path[0] == '/')
+ path++;
+
+ git_buf_clear(buf);
+ return git_buf_printf(buf, "file://%s/%s", host, path);
+}
+
+void test_clone_local__should_clone_local(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* we use a fixture path because it needs to exist for us to want to clone */
+ const char *path = cl_fixture("testrepo.git");
+
+ cl_git_pass(file_url(&buf, "", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_git_pass(file_url(&buf, "localhost", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_git_pass(file_url(&buf, "other-host.mycompany.com", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ /* Ensure that file:/// urls are percent decoded: .git == %2e%67%69%74 */
+ cl_git_pass(file_url(&buf, "", path));
+ git_buf_shorten(&buf, 4);
+ cl_git_pass(git_buf_puts(&buf, "%2e%67%69%74"));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
+
+ git_buf_free(&buf);
+}
+
+void test_clone_local__hardlinks(void)
+{
+ git_repository *repo;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+ struct stat st;
+
+ /*
+ * In this first clone, we just copy over, since the temp dir
+ * will often be in a different filesystem, so we cannot
+ * link. It also allows us to control the number of links
+ */
+ opts.bare = true;
+ opts.local = GIT_CLONE_LOCAL_NO_LINKS;
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
+ git_repository_free(repo);
+
+ /* This second clone is in the same filesystem, so we can hardlink */
+
+ opts.local = GIT_CLONE_LOCAL;
+ cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone2.git", &opts));
+
+#ifndef GIT_WIN32
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(2, st.st_nlink);
+#endif
+
+ git_repository_free(repo);
+ git_buf_clear(&buf);
+
+ opts.local = GIT_CLONE_LOCAL_NO_LINKS;
+ cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone3.git", &opts));
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(1, st.st_nlink);
+
+ git_repository_free(repo);
+
+ /* this one should automatically use links */
+ cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL));
+
+#ifndef GIT_WIN32
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(3, st.st_nlink);
+#endif
+
+ git_buf_free(&buf);
+ git_repository_free(repo);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
index 4bdc6e13b..e9853313e 100644
--- a/tests/clone/nonetwork.c
+++ b/tests/clone/nonetwork.c
@@ -110,12 +110,25 @@ void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(vo
cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
}
+int custom_origin_name_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+
+ return git_remote_create(out, repo, "my_origin", url);
+}
+
void test_clone_nonetwork__custom_origin_name(void)
{
- g_options.remote_name = "my_origin";
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+ g_options.remote_cb = custom_origin_name_remote_create;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
- cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
+ cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
}
void test_clone_nonetwork__defaults(void)
@@ -228,7 +241,7 @@ void test_clone_nonetwork__can_detached_head(void)
cl_assert(git_repository_head_detached(cloned));
cl_git_pass(git_repository_head(&cloned_head, cloned));
- cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head)));
+ cl_assert_equal_oid(git_object_id(obj), git_reference_target(cloned_head));
cl_git_pass(git_reflog_read(&log, cloned, "HEAD"));
entry = git_reflog_entry_byindex(log, 0);
@@ -266,23 +279,6 @@ void test_clone_nonetwork__clone_updates_reflog_properly(void)
assert_correct_reflog("refs/heads/master");
}
-void test_clone_nonetwork__clone_into_updates_reflog_properly(void)
-{
- git_remote *remote;
- git_signature *sig;
- cl_git_pass(git_signature_now(&sig, "Me", "foo@example.com"));
-
- cl_git_pass(git_repository_init(&g_repo, "./foo", false));
- cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_git_fixture_url("testrepo.git")));
- cl_git_pass(git_clone_into(g_repo, remote, NULL, NULL, sig));
-
- assert_correct_reflog("HEAD");
- assert_correct_reflog("refs/heads/master");
-
- git_remote_free(remote);
- git_signature_free(sig);
-}
-
static void cleanup_repository(void *path)
{
if (g_repo) {
diff --git a/tests/clone/transport.c b/tests/clone/transport.c
new file mode 100644
index 000000000..46c16a241
--- /dev/null
+++ b/tests/clone/transport.c
@@ -0,0 +1,51 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "git2/transport.h"
+#include "git2/sys/transport.h"
+#include "fileops.h"
+
+static int custom_transport(
+ git_transport **out,
+ git_remote *owner,
+ void *payload)
+{
+ *((int*)payload) = 1;
+
+ return git_transport_local(out, owner, payload);
+}
+
+static int custom_transport_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ if ((error = git_remote_set_transport(*out, custom_transport, payload)) < 0)
+ return error;
+
+ return 0;
+}
+
+void test_clone_transport__custom_transport(void)
+{
+ git_repository *repo;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ int custom_transport_used = 0;
+
+ clone_opts.remote_cb = custom_transport_remote_create;
+ clone_opts.remote_cb_payload = &custom_transport_used;
+
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./custom_transport.git", &clone_opts));
+ git_repository_free(repo);
+
+ cl_git_pass(git_futils_rmdir_r("./custom_transport.git", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_assert(custom_transport_used == 1);
+}
diff --git a/tests/commit/commit.c b/tests/commit/commit.c
index 38397d2df..f5461cfd3 100644
--- a/tests/commit/commit.c
+++ b/tests/commit/commit.c
@@ -38,8 +38,12 @@ void test_commit_commit__create_unexisting_update_ref(void)
cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
NULL, "some msg", tree, 1, (const git_commit **) &commit));
+ /* fail because the parent isn't the tip of the branch anymore */
+ cl_git_fail(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
+ NULL, "some msg", tree, 1, (const git_commit **) &commit));
+
cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
- cl_assert(!git_oid_cmp(&oid, git_reference_target(ref)));
+ cl_assert_equal_oid(&oid, git_reference_target(ref));
git_tree_free(tree);
git_commit_free(commit);
diff --git a/tests/commit/signature.c b/tests/commit/signature.c
index e9dcfab41..41a74b999 100644
--- a/tests/commit/signature.c
+++ b/tests/commit/signature.c
@@ -56,8 +56,8 @@ void test_commit_signature__create_empties(void)
cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
- cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60));
- cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
}
void test_commit_signature__create_one_char(void)
diff --git a/tests/commit/write.c b/tests/commit/write.c
index b1cdf4485..6212ef641 100644
--- a/tests/commit/write.c
+++ b/tests/commit/write.c
@@ -145,7 +145,7 @@ void test_commit_write__root(void)
cl_assert(git_commit_parentcount(commit) == 0);
cl_git_pass(git_reference_lookup(&branch, g_repo, branch_name));
branch_oid = git_reference_target(branch);
- cl_git_pass(git_oid_cmp(branch_oid, &commit_id));
+ cl_assert_equal_oid(branch_oid, &commit_id);
cl_assert_equal_s(root_commit_message, git_commit_message(commit));
cl_git_pass(git_reflog_read(&log, g_repo, branch_name));
diff --git a/tests/config/config_helpers.c b/tests/config/config_helpers.c
index 53bd945a0..35da720e0 100644
--- a/tests/config/config_helpers.c
+++ b/tests/config/config_helpers.c
@@ -35,3 +35,31 @@ void assert_config_entry_value(
cl_assert_equal_s(expected_value, out);
}
+
+static int count_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ int *how_many = (int *)payload;
+
+ GIT_UNUSED(entry);
+
+ (*how_many)++;
+
+ return 0;
+}
+
+int count_config_entries_match(git_repository *repo, const char *pattern)
+{
+ git_config *config;
+ int how_many = 0;
+
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_assert_equal_i(0, git_config_foreach_match(
+ config, pattern, count_config_entries_cb, &how_many));
+
+ git_config_free(config);
+
+ return how_many;
+}
diff --git a/tests/config/config_helpers.h b/tests/config/config_helpers.h
index b887b3d38..440645730 100644
--- a/tests/config/config_helpers.h
+++ b/tests/config/config_helpers.h
@@ -7,3 +7,7 @@ extern void assert_config_entry_value(
git_repository *repo,
const char *name,
const char *expected_value);
+
+extern int count_config_entries_match(
+ git_repository *repo,
+ const char *pattern);
diff --git a/tests/config/global.c b/tests/config/global.c
index d5f95f504..fc471f90d 100644
--- a/tests/config/global.c
+++ b/tests/config/global.c
@@ -6,18 +6,17 @@ void test_config_global__initialize(void)
{
git_buf path = GIT_BUF_INIT;
- cl_assert_equal_i(0, p_mkdir("home", 0777));
+ cl_git_pass(git_futils_mkdir_r("home", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_assert_equal_i(0, p_mkdir("xdg", 0777));
- cl_assert_equal_i(0, p_mkdir("xdg/git", 0777));
+ cl_git_pass(git_futils_mkdir_r("xdg/git", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
- cl_assert_equal_i(0, p_mkdir("etc", 0777));
+ cl_git_pass(git_futils_mkdir_r("etc", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "etc", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
@@ -27,13 +26,7 @@ void test_config_global__initialize(void)
void test_config_global__cleanup(void)
{
- cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
-
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL);
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL);
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL);
+ cl_sandbox_set_search_path_defaults();
}
void test_config_global__open_global(void)
diff --git a/tests/config/include.c b/tests/config/include.c
index 535573808..58bc690ff 100644
--- a/tests/config/include.c
+++ b/tests/config/include.c
@@ -47,6 +47,8 @@ void test_config_include__homedir(void)
cl_assert_equal_s(str, "huzzah");
git_config_free(cfg);
+
+ cl_sandbox_set_search_path_defaults();
}
void test_config_include__refresh(void)
diff --git a/tests/config/multivar.c b/tests/config/multivar.c
index afdb1e5f4..015008992 100644
--- a/tests/config/multivar.c
+++ b/tests/config/multivar.c
@@ -231,13 +231,13 @@ void test_config_multivar__delete(void)
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
- cl_assert(n == 2);
+ cl_assert_equal_i(2, n);
cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
- cl_assert(n == 1);
+ cl_assert_equal_i(1, n);
git_config_free(cfg);
@@ -245,7 +245,7 @@ void test_config_multivar__delete(void)
n = 0;
cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
- cl_assert(n == 1);
+ cl_assert_equal_i(1, n);
git_config_free(cfg);
}
diff --git a/tests/config/refresh.c b/tests/config/refresh.c
index 99d677f0e..08cd45b95 100644
--- a/tests/config/refresh.c
+++ b/tests/config/refresh.c
@@ -26,9 +26,6 @@ void test_config_refresh__update_value(void)
cl_git_rewritefile(TEST_FILE, "[section]\n\tvalue = 10\n\n");
- cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
- cl_assert_equal_i(1, v);
-
cl_git_pass(git_config_refresh(cfg));
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
@@ -53,9 +50,9 @@ void test_config_refresh__delete_value(void)
cl_git_rewritefile(TEST_FILE, "[section]\n\tnewval = 10\n\n");
- cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
- cl_assert_equal_i(1, v);
- cl_git_fail(git_config_get_int32(&v, cfg, "section.newval"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_int32(&v, cfg, "section.value"));
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.newval"));
cl_git_pass(git_config_refresh(cfg));
diff --git a/tests/config/snapshot.c b/tests/config/snapshot.c
new file mode 100644
index 000000000..c9f15921a
--- /dev/null
+++ b/tests/config/snapshot.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+
+void test_config_snapshot__create_snapshot(void)
+{
+ int32_t tmp;
+ git_config *cfg, *snapshot;
+ const char *filename = "config-ext-change";
+
+ cl_git_mkfile(filename, "[old]\nvalue = 5\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+ cl_assert_equal_i(5, tmp);
+
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+
+ /* Change the value on the file itself (simulate external process) */
+ cl_git_mkfile(filename, "[old]\nvalue = 56\n");
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+ cl_assert_equal_i(56, tmp);
+
+ cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value"));
+ cl_assert_equal_i(5, tmp);
+
+ git_config_free(snapshot);
+ git_config_free(cfg);
+}
+
+static int count_me(const git_config_entry *entry, void *payload)
+{
+ int *n = (int *) payload;
+
+ GIT_UNUSED(entry);
+
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_snapshot__multivar(void)
+{
+ int count = 0;
+ git_config *cfg, *snapshot;
+ const char *filename = "config-file";
+
+ cl_git_mkfile(filename, "[old]\nvalue = 5\nvalue = 6\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count));
+
+ cl_assert_equal_i(2, count);
+
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ git_config_free(cfg);
+
+ count = 0;
+ cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
+
+ cl_assert_equal_i(2, count);
+
+ git_config_free(snapshot);
+}
diff --git a/tests/config/stress.c b/tests/config/stress.c
index eeca54ff4..488915e79 100644
--- a/tests/config/stress.c
+++ b/tests/config/stress.c
@@ -90,3 +90,16 @@ void test_config_stress__trailing_backslash(void)
cl_assert_equal_s(path, str);
git_config_free(config);
}
+
+void test_config_stress__complex(void)
+{
+ git_config *config;
+ const char *str;
+ const char *path = "./config-immediate-multiline";
+
+ cl_git_mkfile(path, "[imm]\n multi = \"\\\nfoo\"");
+ cl_git_pass(git_config_open_ondisk(&config, path));
+ cl_git_pass(git_config_get_string(&str, config, "imm.multi"));
+ cl_assert_equal_s(str, "foo");
+ git_config_free(config);
+}
diff --git a/tests/config/write.c b/tests/config/write.c
index 922d75557..0f11ae8da 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -229,6 +229,22 @@ void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
git_config_free(cfg);
}
+void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_int32(cfg, "diff.context", 10));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "diff.context"));
+ cl_assert_equal_i(10, i);
+
+ git_config_free(cfg);
+}
+
void test_config_write__add_value_which_needs_quotes(void)
{
git_config *cfg;
@@ -304,3 +320,30 @@ void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
git_config_free(cfg);
}
+void test_config_write__outside_change(void)
+{
+ int32_t tmp;
+ git_config *cfg;
+ const char *filename = "config-ext-change";
+
+ cl_git_mkfile(filename, "[old]\nvalue = 5\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+
+ /* Change the value on the file itself (simulate external process) */
+ cl_git_mkfile(filename, "[old]\nvalue = 6\n");
+
+ cl_git_pass(git_config_set_int32(cfg, "new.value", 7));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+ cl_assert_equal_i(6, tmp);
+
+ cl_git_pass(git_config_refresh(cfg));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+ cl_assert_equal_i(6, tmp);
+
+ git_config_free(cfg);
+}
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
index da5ec605c..7482dadbe 100644
--- a/tests/core/buffer.c
+++ b/tests/core/buffer.c
@@ -748,7 +748,7 @@ void test_core_buffer__unescape(void)
assert_unescape("", "");
}
-void test_core_buffer__base64(void)
+void test_core_buffer__encode_base64(void)
{
git_buf buf = GIT_BUF_INIT;
@@ -759,33 +759,54 @@ void test_core_buffer__base64(void)
* 0x 1d 06 21 29 1c 30
* d G h p c w
*/
- cl_git_pass(git_buf_put_base64(&buf, "this", 4));
+ cl_git_pass(git_buf_encode_base64(&buf, "this", 4));
cl_assert_equal_s("dGhpcw==", buf.ptr);
git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
+ cl_git_pass(git_buf_encode_base64(&buf, "this!", 5));
cl_assert_equal_s("dGhpcyE=", buf.ptr);
git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
+ cl_git_pass(git_buf_encode_base64(&buf, "this!\n", 6));
cl_assert_equal_s("dGhpcyEK", buf.ptr);
git_buf_free(&buf);
}
-void test_core_buffer__base85(void)
+void test_core_buffer__decode_base64(void)
{
git_buf buf = GIT_BUF_INIT;
- cl_git_pass(git_buf_put_base85(&buf, "this", 4));
+ cl_git_pass(git_buf_decode_base64(&buf, "dGhpcw==", 8));
+ cl_assert_equal_s("this", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyE=", 8));
+ cl_assert_equal_s("this!", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyEK", 8));
+ cl_assert_equal_s("this!\n", buf.ptr);
+
+ cl_git_fail(git_buf_decode_base64(&buf, "This is not a valid base64 string!!!", 36));
+ cl_assert_equal_s("this!\n", buf.ptr);
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__encode_base85(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_encode_base85(&buf, "this", 4));
cl_assert_equal_s("bZBXF", buf.ptr);
git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base85(&buf, "two rnds", 8));
+ cl_git_pass(git_buf_encode_base85(&buf, "two rnds", 8));
cl_assert_equal_s("ba!tca&BaE", buf.ptr);
git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base85(&buf, "this is base 85 encoded",
+ cl_git_pass(git_buf_encode_base85(&buf, "this is base 85 encoded",
strlen("this is base 85 encoded")));
cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr);
git_buf_clear(&buf);
@@ -1034,18 +1055,14 @@ void test_core_buffer__lf_and_crlf_conversions(void)
git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
- check_buf(src.ptr, tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
- check_buf(src.ptr, tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
@@ -1054,8 +1071,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
@@ -1063,8 +1079,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
@@ -1072,8 +1087,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
@@ -1089,8 +1103,7 @@ void test_core_buffer__lf_and_crlf_conversions(void)
/* blob correspondence tests */
git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
git_buf_free(&src);
@@ -1105,16 +1118,14 @@ void test_core_buffer__lf_and_crlf_conversions(void)
git_buf_free(&tgt);
git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
git_buf_free(&src);
git_buf_free(&tgt);
git_buf_sets(&src, MORE_LF_TEXT_RAW);
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+ cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf(MORE_LF_TEXT_AS_LF, tgt);
git_buf_free(&src);
diff --git a/tests/core/copy.c b/tests/core/copy.c
index c0c59c056..04b2dfab5 100644
--- a/tests/core/copy.c
+++ b/tests/core/copy.c
@@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void)
cl_assert(!git_path_isdir("an_dir"));
}
+void assert_hard_link(const char *path)
+{
+ /* we assert this by checking that there's more than one link to the file */
+ struct stat st;
+
+ cl_assert(git_path_isfile(path));
+ cl_git_pass(p_stat(path, &st));
+ cl_assert(st.st_nlink > 1);
+}
+
void test_core_copy__tree(void)
{
struct stat st;
@@ -122,5 +132,21 @@ void test_core_copy__tree(void)
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("t2"));
+#ifndef GIT_WIN32
+ cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0));
+ cl_assert(git_path_isdir("t3"));
+
+ cl_assert(git_path_isdir("t3"));
+ cl_assert(git_path_isdir("t3/b"));
+ cl_assert(git_path_isdir("t3/c"));
+ cl_assert(git_path_isdir("t3/c/d"));
+ cl_assert(git_path_isdir("t3/c/e"));
+
+ assert_hard_link("t3/f1");
+ assert_hard_link("t3/b/f2");
+ assert_hard_link("t3/c/f3");
+ assert_hard_link("t3/c/d/f4");
+#endif
+
cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
}
diff --git a/tests/core/env.c b/tests/core/env.c
index df1d92a02..293b786db 100644
--- a/tests/core/env.c
+++ b/tests/core/env.c
@@ -40,12 +40,12 @@ void test_core_env__initialize(void)
}
}
-static void reset_global_search_path(void)
+static void set_global_search_path_from_env(void)
{
cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL));
}
-static void reset_system_search_path(void)
+static void set_system_search_path_from_env(void)
{
cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL));
}
@@ -69,9 +69,7 @@ void test_core_env__cleanup(void)
(void)p_rmdir(*val);
}
- /* reset search paths to default */
- reset_global_search_path();
- reset_system_search_path();
+ cl_sandbox_set_search_path_defaults();
}
static void setenv_and_check(const char *name, const char *value)
@@ -124,12 +122,12 @@ void test_core_env__0(void)
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
setenv_and_check("HOME", path.ptr);
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile));
cl_setenv("HOME", env_save[0]);
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
@@ -138,7 +136,7 @@ void test_core_env__0(void)
setenv_and_check("HOMEDRIVE", NULL);
setenv_and_check("HOMEPATH", NULL);
setenv_and_check("USERPROFILE", path.ptr);
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile));
@@ -148,7 +146,7 @@ void test_core_env__0(void)
if (root >= 0) {
setenv_and_check("USERPROFILE", NULL);
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
@@ -158,7 +156,7 @@ void test_core_env__0(void)
setenv_and_check("HOMEDRIVE", path.ptr);
path.ptr[root] = old;
setenv_and_check("HOMEPATH", &path.ptr[root]);
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile));
}
@@ -185,7 +183,7 @@ void test_core_env__1(void)
cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
#endif
- reset_global_search_path();
+ set_global_search_path_from_env();
cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
@@ -195,8 +193,8 @@ void test_core_env__1(void)
cl_git_pass(cl_setenv("HOMEPATH", NULL));
cl_git_pass(cl_setenv("USERPROFILE", NULL));
#endif
- reset_global_search_path();
- reset_system_search_path();
+ set_global_search_path_from_env();
+ set_system_search_path_from_env();
cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
@@ -206,7 +204,7 @@ void test_core_env__1(void)
#ifdef GIT_WIN32
cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
- reset_system_search_path();
+ set_system_search_path_from_env();
cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile"));
diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c
index a8c5b10ae..a0e8491a2 100644
--- a/tests/core/mkdir.c
+++ b/tests/core/mkdir.c
@@ -186,3 +186,34 @@ void test_core_mkdir__chmods(void)
cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
check_mode(0777, st.st_mode);
}
+
+void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
+{
+ struct stat st;
+ mode_t *old;
+
+ /* FAT filesystems don't support exec bit, nor group/world bits */
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_assert((old = git__malloc(sizeof(mode_t))) != NULL);
+ *old = p_umask(022);
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_must_pass(p_chmod("r/mode", 0111));
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0111, st.st_mode);
+
+ cl_git_pass(
+ git_futils_mkdir("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH));
+ cl_git_pass(git_path_lstat("r/mode/is/okay/inside", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_must_pass(p_chmod("r/mode", 0777));
+}
+
diff --git a/tests/core/path.c b/tests/core/path.c
index 471491b87..7d3e4938f 100644
--- a/tests/core/path.c
+++ b/tests/core/path.c
@@ -356,7 +356,7 @@ typedef struct {
#define CANCEL_VALUE 1234
-static int check_one_walkup_step(void *ref, git_buf *path)
+static int check_one_walkup_step(void *ref, const char *path)
{
check_walkup_info *info = (check_walkup_info *)ref;
@@ -367,7 +367,7 @@ static int check_one_walkup_step(void *ref, git_buf *path)
info->cancel_after--;
cl_assert(info->expect[info->expect_idx] != NULL);
- cl_assert_equal_s(info->expect[info->expect_idx], path->ptr);
+ cl_assert_equal_s(info->expect[info->expect_idx], path);
info->expect_idx++;
return 0;
@@ -376,18 +376,42 @@ static int check_one_walkup_step(void *ref, git_buf *path)
void test_core_path__11_walkup(void)
{
git_buf p = GIT_BUF_INIT;
+
char *expect[] = {
- "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- "this is a path", NULL,
- "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
- NULL
+ /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 7 */ "this_is_a_path", "", NULL,
+ /* 8 */ "this_is_a_path/", "", NULL,
+ /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
+ /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
+ /* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
+ /* 12 */ "a/b/c/", "a/b/", "a/", NULL,
+ /* 13 */ "", NULL,
+ /* 14 */ "/", NULL,
+ /* 15 */ NULL
+ };
+
+ char *root[] = {
+ /* 1 */ NULL,
+ /* 2 */ NULL,
+ /* 3 */ "/",
+ /* 4 */ "",
+ /* 5 */ "/a/b",
+ /* 6 */ "/a/b/",
+ /* 7 */ NULL,
+ /* 8 */ NULL,
+ /* 9 */ NULL,
+ /* 10 */ NULL,
+ /* 11 */ NULL,
+ /* 12 */ "a/",
+ /* 13 */ NULL,
+ /* 14 */ NULL,
};
- char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL };
+
int i, j;
check_walkup_info info;
@@ -404,9 +428,8 @@ void test_core_path__11_walkup(void)
);
cl_assert_equal_s(p.ptr, expect[i]);
-
- /* skip to next run of expectations */
- while (expect[i] != NULL) i++;
+ cl_assert(expect[info.expect_idx] == NULL);
+ i = info.expect_idx;
}
git_buf_free(&p);
diff --git a/tests/core/pool.c b/tests/core/pool.c
index 351d0c20f..a7ec8801b 100644
--- a/tests/core/pool.c
+++ b/tests/core/pool.c
@@ -38,19 +38,19 @@ void test_core_pool__1(void)
cl_assert(git_pool_malloc(&p, i) != NULL);
/* with fixed page size, allocation must end up with these values */
- cl_assert(git_pool__open_pages(&p) == 1);
- cl_assert(git_pool__full_pages(&p) == 505);
+ cl_assert_equal_i(1, git_pool__open_pages(&p));
+ cl_assert_equal_i(507, git_pool__full_pages(&p));
git_pool_clear(&p);
- cl_git_pass(git_pool_init(&p, 1, 4100));
+ cl_git_pass(git_pool_init(&p, 1, 4120));
for (i = 2010; i > 0; i--)
cl_assert(git_pool_malloc(&p, i) != NULL);
/* with fixed page size, allocation must end up with these values */
- cl_assert(git_pool__open_pages(&p) == 1);
- cl_assert(git_pool__full_pages(&p) == 492);
+ cl_assert_equal_i(1, git_pool__open_pages(&p));
+ cl_assert_equal_i(492, git_pool__full_pages(&p));
git_pool_clear(&p);
}
diff --git a/tests/diff/blob.c b/tests/diff/blob.c
index d1fff9c5b..51bdbab15 100644
--- a/tests/diff/blob.c
+++ b/tests/diff/blob.c
@@ -26,7 +26,7 @@ void test_diff_blob__initialize(void)
g_repo = cl_git_sandbox_init("attr");
- cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION));
opts.context_lines = 1;
memset(&expected, 0, sizeof(expected));
@@ -137,9 +137,9 @@ static void assert_patch_matches_blobs(
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.id));
+ cl_assert_equal_oid(git_blob_id(a), &delta->old_file.id);
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.id));
+ cl_assert_equal_oid(git_blob_id(b), &delta->new_file.id);
cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p));
@@ -274,7 +274,7 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.id));
+ cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id);
cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
cl_assert(git_oid_iszero(&delta->new_file.id));
cl_assert_equal_sz(0, delta->new_file.size);
@@ -301,7 +301,7 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
cl_assert(git_oid_iszero(&delta->old_file.id));
cl_assert_equal_sz(0, delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.id));
+ cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id);
cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
@@ -392,9 +392,9 @@ void test_diff_blob__can_compare_identical_blobs_with_patch(void)
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
- cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.id));
+ cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id);
cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
- cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.id));
+ cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id);
cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
git_patch_free(p);
diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c
index cdc64eb1d..26f670cfa 100644
--- a/tests/diff/iterator.c
+++ b/tests/diff/iterator.c
@@ -380,7 +380,7 @@ static void index_iterator_test(
if (expected_oids != NULL) {
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oids[count]));
- cl_assert_equal_i(git_oid_cmp(&oid, &entry->id), 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
count++;
@@ -647,7 +647,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void)
{
- workdir_iterator_test("attr", NULL, NULL, 27, 1, NULL, "ign");
+ workdir_iterator_test("attr", NULL, NULL, 23, 5, NULL, "ign");
}
static const char *status_paths[] = {
diff --git a/tests/diff/stats.c b/tests/diff/stats.c
index 055019f69..f731997da 100644
--- a/tests/diff/stats.c
+++ b/tests/diff/stats.c
@@ -54,6 +54,10 @@ void test_diff_stats__stat(void)
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
git_buf_free(&buf);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 80));
+ cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0);
+ git_buf_free(&buf);
}
void test_diff_stats__multiple_hunks(void)
diff --git a/tests/diff/tree.c b/tests/diff/tree.c
index 582174b8b..6ab49fdb0 100644
--- a/tests/diff/tree.c
+++ b/tests/diff/tree.c
@@ -9,7 +9,7 @@ static diff_expects expect;
void test_diff_tree__initialize(void)
{
- cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION));
memset(&expect, 0, sizeof(expect));
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index 6128e820e..963be9481 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -1,13 +1,10 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
#include "repository.h"
+#include "git2/sys/diff.h"
static git_repository *g_repo = NULL;
-void test_diff_workdir__initialize(void)
-{
-}
-
void test_diff_workdir__cleanup(void)
{
cl_git_sandbox_cleanup();
@@ -60,6 +57,14 @@ void test_diff_workdir__to_index(void)
cl_assert_equal_i(5, exp.line_dels);
}
+ {
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(
+ 13 /* in root */ + 3 /* in subdir */, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+ }
+
git_diff_free(diff);
}
@@ -1490,3 +1495,202 @@ void test_diff_workdir__with_stale_index(void)
git_index_free(idx);
}
+
+static int touch_file(void *payload, git_buf *path)
+{
+ int fd;
+ char b;
+
+ GIT_UNUSED(payload);
+ if (git_path_isdir(path->ptr))
+ return 0;
+
+ cl_assert((fd = p_open(path->ptr, O_RDWR)) >= 0);
+ cl_assert_equal_i(1, p_read(fd, &b, 1));
+ cl_must_pass(p_lseek(fd, 0, SEEK_SET));
+ cl_must_pass(p_write(fd, &b, 1));
+ cl_must_pass(p_close(fd));
+
+ return 0;
+}
+
+static void basic_diff_status(git_diff **out, const git_diff_options *opts)
+{
+ diff_expects exp;
+
+ cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ *out, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+}
+
+void test_diff_workdir__can_update_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ /* touch all the files so stat times are different */
+ {
+ git_buf path = GIT_BUF_INIT;
+ cl_git_pass(git_buf_sets(&path, "status"));
+ cl_git_pass(git_path_direach(&path, 0, touch_file, NULL));
+ git_buf_free(&path);
+ }
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now allow diff to update stat cache */
+ opts.flags |= GIT_DIFF_UPDATE_INDEX;
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now if we do it again, we should see fewer OID calculations */
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_diff_free(diff);
+}
+
+#define STR7 "0123456"
+#define STR8 "01234567"
+#define STR40 STR8 STR8 STR8 STR8 STR8
+#define STR200 STR40 STR40 STR40 STR40 STR40
+#define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
+ STR8 STR8 STR8 STR8 STR7 "\0"
+#define STR1000 STR200 STR200 STR200 STR200 STR200
+#define STR3999Z STR1000 STR1000 STR1000 STR999Z
+#define STR4000 STR1000 STR1000 STR1000 STR1000
+
+static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary)
+{
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, idx));
+ delta = git_patch_get_delta(patch);
+ cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary);
+ git_patch_free(patch);
+}
+
+void test_diff_workdir__binary_detection(void)
+{
+ git_index *idx;
+ git_diff *diff = NULL;
+ git_buf b = GIT_BUF_INIT;
+ int i;
+ git_buf data[10] = {
+ { "1234567890", 0, 0 }, /* 0 - all ascii text control */
+ { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */
+ { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */
+ { STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
+ { STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
+ { STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
+ { STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
+ { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
+ "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
+ { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
+ 0, 26 }, /* 8 - All non-printable characters (no NUL) */
+ { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
+ "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
+ };
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* We start with ASCII in index and test data in workdir,
+ * then we will try with test data in index and ASCII in workdir.
+ */
+
+ cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
+ for (i = 0; i < 10; ++i) {
+ b.ptr[b.size - 1] = '0' + i;
+ cl_git_mkfile(b.ptr, "baseline");
+ cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
+
+ if (data[i].size == 0)
+ data[i].size = strlen(data[i].ptr);
+ cl_git_write2file(
+ b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664);
+ }
+ git_index_write(idx);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ cl_assert_equal_i(10, git_diff_num_deltas(diff));
+
+ /* using diff binary detection (i.e. looking for NUL byte) */
+ assert_delta_binary(diff, 0, false);
+ assert_delta_binary(diff, 1, false);
+ assert_delta_binary(diff, 2, false);
+ assert_delta_binary(diff, 3, true);
+ assert_delta_binary(diff, 4, true);
+ assert_delta_binary(diff, 5, true);
+ assert_delta_binary(diff, 6, false);
+ assert_delta_binary(diff, 7, true);
+ assert_delta_binary(diff, 8, false);
+ assert_delta_binary(diff, 9, false);
+ /* The above have been checked to match command-line Git */
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
+ for (i = 0; i < 10; ++i) {
+ b.ptr[b.size - 1] = '0' + i;
+ cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
+
+ cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664);
+ }
+ git_index_write(idx);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ cl_assert_equal_i(10, git_diff_num_deltas(diff));
+
+ /* using diff binary detection (i.e. looking for NUL byte) */
+ assert_delta_binary(diff, 0, false);
+ assert_delta_binary(diff, 1, false);
+ assert_delta_binary(diff, 2, false);
+ assert_delta_binary(diff, 3, true);
+ assert_delta_binary(diff, 4, true);
+ assert_delta_binary(diff, 5, true);
+ assert_delta_binary(diff, 6, false);
+ assert_delta_binary(diff, 7, true);
+ assert_delta_binary(diff, 8, false);
+ assert_delta_binary(diff, 9, false);
+
+ git_diff_free(diff);
+
+ git_index_free(idx);
+ git_buf_free(&b);
+}
diff --git a/tests/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h
index 94402abd5..c75b65b90 100644
--- a/tests/fetchhead/fetchhead_data.h
+++ b/tests/fetchhead/fetchhead_data.h
@@ -16,6 +16,11 @@
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
+#define FETCH_HEAD_WILDCARD_DATA2 \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+
#define FETCH_HEAD_NO_MERGE_DATA \
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
@@ -25,6 +30,16 @@
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
+#define FETCH_HEAD_NO_MERGE_DATA2 \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+
+#define FETCH_HEAD_NO_MERGE_DATA3 \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
#define FETCH_HEAD_EXPLICIT_DATA \
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n"
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index e7ff2ca30..7b64a6339 100644
--- a/tests/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
@@ -120,7 +120,7 @@ static int fetchhead_ref_cb(const char *name, const char *url,
expected = git_vector_get(cb_data->fetchhead_vector, cb_data->idx);
- cl_assert(git_oid_cmp(&expected->oid, oid) == 0);
+ cl_assert_equal_oid(&expected->oid, oid);
cl_assert(expected->is_merge == is_merge);
if (expected->ref_name)
@@ -174,7 +174,7 @@ static int read_old_style_cb(const char *name, const char *url,
cl_assert(name == NULL);
cl_assert(url == NULL);
- cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert_equal_oid(&expected, oid);
cl_assert(is_merge == 1);
return 0;
@@ -201,7 +201,7 @@ static int read_type_missing(const char *ref_name, const char *remote_url,
cl_assert_equal_s("name", ref_name);
cl_assert_equal_s("remote_url", remote_url);
- cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert_equal_oid(&expected, oid);
cl_assert(is_merge == 0);
return 0;
@@ -228,7 +228,7 @@ static int read_name_missing(const char *ref_name, const char *remote_url,
cl_assert(ref_name == NULL);
cl_assert_equal_s("remote_url", remote_url);
- cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert_equal_oid(&expected, oid);
cl_assert(is_merge == 0);
return 0;
diff --git a/tests/filter/crlf.c b/tests/filter/crlf.c
index 75320efee..a31dac965 100644
--- a/tests/filter/crlf.c
+++ b/tests/filter/crlf.c
@@ -25,7 +25,8 @@ void test_filter_crlf__to_worktree(void)
git_filter *crlf;
git_buf in = { 0 }, out = { 0 };
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
@@ -53,7 +54,8 @@ void test_filter_crlf__to_odb(void)
git_filter *crlf;
git_buf in = { 0 }, out = { 0 };
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
@@ -79,7 +81,8 @@ void test_filter_crlf__with_safecrlf(void)
cl_repo_set_bool(g_repo, "core.safecrlf", true);
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
@@ -100,12 +103,55 @@ void test_filter_crlf__with_safecrlf(void)
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
- /* Normalized \n fails with safecrlf */
+ /* Normalized \n is reversible, so does not fail with safecrlf */
in.ptr = "Normal\nLF\nonly\nline-endings.\n";
in.size = strlen(in.ptr);
- cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
- cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_assert_equal_s(in.ptr, out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
+
+void test_filter_crlf__with_safecrlf_and_unsafe_allowed(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf in = {0}, out = GIT_BUF_INIT;
+
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf */
+ in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings fails with safecrlf, but allowed to pass */
+ in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Normalized \n fails with safecrlf, but allowed to pass */
+ in.ptr = "Normal\nLF\nonly\nline-endings.\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
git_filter_list_free(fl);
git_buf_free(&out);
@@ -117,7 +163,8 @@ void test_filter_crlf__no_safecrlf(void)
git_filter *crlf;
git_buf in = {0}, out = GIT_BUF_INIT;
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
@@ -149,3 +196,44 @@ void test_filter_crlf__no_safecrlf(void)
git_buf_free(&out);
}
+void test_filter_crlf__safecrlf_warn(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf in = {0}, out = GIT_BUF_INIT;
+
+ cl_repo_set_string(g_repo, "core.safecrlf", "warn");
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf=warn */
+ in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings succeeds with safecrlf=warn */
+ in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Normalized \n is reversible, so does not fail with safecrlf=warn */
+ in.ptr = "Normal\nLF\nonly\nline-endings.\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+ cl_assert_equal_s(in.ptr, out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
diff --git a/tests/filter/custom.c b/tests/filter/custom.c
index 70524010e..0fd7c3744 100644
--- a/tests/filter/custom.c
+++ b/tests/filter/custom.c
@@ -194,7 +194,7 @@ void test_filter_custom__to_odb(void)
git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB));
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB, 0));
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
@@ -215,7 +215,7 @@ void test_filter_custom__to_workdir(void)
bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN);
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
@@ -233,13 +233,13 @@ void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
git_filter_list *fl;
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse, crlf */
cl_assert_equal_sz(3, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse - possibly crlf depending on global config */
{
size_t flen = git_filter_list_length(fl);
@@ -248,19 +248,20 @@ void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip, reverse */
cl_assert_equal_sz(2, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE, 0));
/* expect: bitflip (because of -reverse) */
cl_assert_equal_sz(1, git_filter_list_length(fl));
git_filter_list_free(fl);
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE));
+ &fl, g_repo, NULL, "doesntapplytome.bin",
+ GIT_FILTER_TO_WORKTREE, 0));
/* expect: none */
cl_assert_equal_sz(0, git_filter_list_length(fl));
git_filter_list_free(fl);
diff --git a/tests/filter/ident.c b/tests/filter/ident.c
index 2c8e6abea..2c9a3eb68 100644
--- a/tests/filter/ident.c
+++ b/tests/filter/ident.c
@@ -39,7 +39,8 @@ void test_filter_ident__to_worktree(void)
git_filter_list *fl;
git_filter *ident;
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
ident = git_filter_lookup(GIT_FILTER_IDENT);
cl_assert(ident != NULL);
@@ -78,7 +79,8 @@ void test_filter_ident__to_odb(void)
git_filter_list *fl;
git_filter *ident;
- cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
ident = git_filter_lookup(GIT_FILTER_IDENT);
cl_assert(ident != NULL);
diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c
index 90aaa442d..427351693 100644
--- a/tests/index/conflicts.c
+++ b/tests/index/conflicts.c
@@ -107,13 +107,13 @@ void test_index_conflicts__get(void)
cl_assert_equal_s("conflicts-one.txt", conflict_entry[0]->path);
git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[0]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[1]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[2]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
&conflict_entry[2], repo_index, "conflicts-two.txt"));
@@ -121,13 +121,13 @@ void test_index_conflicts__get(void)
cl_assert_equal_s("conflicts-two.txt", conflict_entry[0]->path);
git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[0]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[1]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[2]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
}
void test_index_conflicts__iterate(void)
@@ -141,29 +141,29 @@ void test_index_conflicts__iterate(void)
cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[0]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[1]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[2]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[0]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[1]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&conflict_entry[2]->id, &oid) == 0);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER);
@@ -281,7 +281,7 @@ void test_index_conflicts__partial(void)
cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
&conflict_entry[2], repo_index, "test-one.txt"));
- cl_assert(git_oid_cmp(&ancestor_entry.id, &conflict_entry[0]->id) == 0);
+ cl_assert_equal_oid(&ancestor_entry.id, &conflict_entry[0]->id);
cl_assert(conflict_entry[1] == NULL);
cl_assert(conflict_entry[2] == NULL);
}
diff --git a/tests/index/crlf.c b/tests/index/crlf.c
index cf69c6226..23f47932f 100644
--- a/tests/index/crlf.c
+++ b/tests/index/crlf.c
@@ -41,7 +41,7 @@ void test_index_crlf__autocrlf_false_no_attrs(void)
cl_git_pass(git_oid_fromstr(&oid,
(GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_OID_CRLF : FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__autocrlf_true_no_attrs(void)
@@ -58,7 +58,7 @@ void test_index_crlf__autocrlf_true_no_attrs(void)
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__autocrlf_input_no_attrs(void)
@@ -75,7 +75,7 @@ void test_index_crlf__autocrlf_input_no_attrs(void)
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__autocrlf_false_text_auto_attr(void)
@@ -94,7 +94,7 @@ void test_index_crlf__autocrlf_false_text_auto_attr(void)
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__autocrlf_true_text_auto_attr(void)
@@ -113,7 +113,7 @@ void test_index_crlf__autocrlf_true_text_auto_attr(void)
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
}
void test_index_crlf__autocrlf_input_text_auto_attr(void)
@@ -132,5 +132,23 @@ void test_index_crlf__autocrlf_input_text_auto_attr(void)
entry = git_index_get_bypath(g_index, "newfile.txt", 0);
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
- cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__safecrlf_true_no_attrs(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_mkfile("crlf/newfile.txt", ALL_LF_TEXT_RAW);
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", MORE_CRLF_TEXT_RAW);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", MORE_LF_TEXT_RAW);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
}
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
index 013932696..58d7935a0 100644
--- a/tests/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -152,3 +152,20 @@ void test_index_filemodes__trusted(void)
git_index_free(index);
}
+
+void test_index_filemodes__invalid(void)
+{
+ git_index *index;
+ git_index_entry entry;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ entry.path = "foo";
+ entry.mode = GIT_OBJ_BLOB;
+ cl_git_fail(git_index_add(index, &entry));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_index_free(index);
+}
diff --git a/tests/index/read_tree.c b/tests/index/read_tree.c
index 6c6b40121..0e1882818 100644
--- a/tests/index/read_tree.c
+++ b/tests/index/read_tree.c
@@ -37,7 +37,7 @@ void test_index_read_tree__read_write_involution(void)
git_tree_free(tree);
cl_git_pass(git_index_write_tree(&tree_oid, index));
- cl_assert(git_oid_cmp(&expected, &tree_oid) == 0);
+ cl_assert_equal_oid(&expected, &tree_oid);
git_index_free(index);
git_repository_free(repo);
diff --git a/tests/index/rename.c b/tests/index/rename.c
index b6fb61d10..dd3cfa732 100644
--- a/tests/index/rename.c
+++ b/tests/index/rename.c
@@ -27,7 +27,7 @@ void test_index_rename__single_file(void)
cl_assert(!git_index_find(&position, index, "lame.name.txt"));
entry = git_index_get_byindex(index, position);
- cl_assert(git_oid_cmp(&expected, &entry->id) == 0);
+ cl_assert_equal_oid(&expected, &entry->id);
/* This removes the entry from the index, but not from the object database */
cl_git_pass(git_index_remove(index, "lame.name.txt", 0));
@@ -41,7 +41,7 @@ void test_index_rename__single_file(void)
cl_assert(!git_index_find(&position, index, "fancy.name.txt"));
entry = git_index_get_byindex(index, position);
- cl_assert(git_oid_cmp(&expected, &entry->id) == 0);
+ cl_assert_equal_oid(&expected, &entry->id);
git_index_free(index);
git_repository_free(repo);
diff --git a/tests/index/reuc.c b/tests/index/reuc.c
index 27240a30f..0b4d71a6a 100644
--- a/tests/index/reuc.c
+++ b/tests/index/reuc.c
@@ -53,9 +53,9 @@ void test_index_reuc__add(void)
cl_assert(reuc->mode[0] == 0100644);
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
- cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid);
+ cl_assert_equal_oid(&reuc->oid[1], &our_oid);
+ cl_assert_equal_oid(&reuc->oid[2], &their_oid);
}
void test_index_reuc__add_no_ancestor(void)
@@ -78,9 +78,9 @@ void test_index_reuc__add_no_ancestor(void)
cl_assert(reuc->mode[0] == 0);
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
- cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid);
+ cl_assert_equal_oid(&reuc->oid[1], &our_oid);
+ cl_assert_equal_oid(&reuc->oid[2], &their_oid);
}
void test_index_reuc__read_bypath(void)
@@ -97,11 +97,11 @@ void test_index_reuc__read_bypath(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
@@ -110,11 +110,11 @@ void test_index_reuc__read_bypath(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, ONE_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
}
void test_index_reuc__ignore_case(void)
@@ -142,11 +142,11 @@ void test_index_reuc__ignore_case(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
}
void test_index_reuc__read_byindex(void)
@@ -163,11 +163,11 @@ void test_index_reuc__read_byindex(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, ONE_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
@@ -176,11 +176,11 @@ void test_index_reuc__read_byindex(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
}
void test_index_reuc__updates_existing(void)
@@ -216,11 +216,11 @@ void test_index_reuc__updates_existing(void)
cl_assert_equal_s("TWO.txt", reuc->path);
git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
}
void test_index_reuc__remove(void)
@@ -242,11 +242,11 @@ void test_index_reuc__remove(void)
cl_assert(reuc->mode[1] == 0100644);
cl_assert(reuc->mode[2] == 0100644);
git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
}
void test_index_reuc__write(void)
diff --git a/tests/index/tests.c b/tests/index/tests.c
index fa5c0bb1a..373889912 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -243,11 +243,11 @@ void test_index_tests__add(void)
entry = git_index_get_byindex(index, 0);
/* And the built-in hashing mechanism worked as expected */
- cl_assert(git_oid_cmp(&id1, &entry->id) == 0);
+ cl_assert_equal_oid(&id1, &entry->id);
/* Test access by path instead of index */
cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
- cl_assert(git_oid_cmp(&id1, &entry->id) == 0);
+ cl_assert_equal_oid(&id1, &entry->id);
git_index_free(index);
git_repository_free(repo);
@@ -283,14 +283,14 @@ void test_index_tests__add_issue_1397(void)
/* Make sure the initial SHA-1 is correct */
cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
- cl_assert_(git_oid_cmp(&id1, &entry->id) == 0, "first oid check");
+ cl_assert_equal_oid(&id1, &entry->id);
/* Update the index */
cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
/* Check the new SHA-1 */
cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
- cl_assert_(git_oid_cmp(&id1, &entry->id) == 0, "second oid check");
+ cl_assert_equal_oid(&id1, &entry->id);
git_index_free(index);
}
diff --git a/tests/main.c b/tests/main.c
index ffbbcbf48..3de4f9801 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -6,16 +6,12 @@ int __cdecl main(int argc, char *argv[])
int main(int argc, char *argv[])
#endif
{
- const char *sandbox_path;
int res;
clar_test_init(argc, argv);
git_threads_init();
-
- sandbox_path = clar_sandbox_path();
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path);
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path);
+ cl_sandbox_set_search_path_defaults();
/* Run the test suite */
res = clar_test_run();
diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c
index 154985f11..e3e703943 100644
--- a/tests/merge/merge_helpers.c
+++ b/tests/merge/merge_helpers.c
@@ -327,7 +327,7 @@ int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[],
int dircount(void *payload, git_buf *pathbuf)
{
- int *entries = payload;
+ size_t *entries = payload;
size_t len = git_buf_len(pathbuf);
if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
diff --git a/tests/merge/merge_helpers.h b/tests/merge/merge_helpers.h
index fddf8fab1..554c24b7c 100644
--- a/tests/merge/merge_helpers.h
+++ b/tests/merge/merge_helpers.h
@@ -49,7 +49,7 @@
struct merge_index_entry {
uint16_t mode;
- char oid_str[41];
+ char oid_str[GIT_OID_HEXSZ+1];
int stage;
char path[128];
};
@@ -70,9 +70,9 @@ struct merge_reuc_entry {
unsigned int ancestor_mode;
unsigned int our_mode;
unsigned int their_mode;
- char ancestor_oid_str[41];
- char our_oid_str[41];
- char their_oid_str[41];
+ char ancestor_oid_str[GIT_OID_HEXSZ+1];
+ char our_oid_str[GIT_OID_HEXSZ+1];
+ char their_oid_str[GIT_OID_HEXSZ+1];
};
struct merge_index_conflict_data {
diff --git a/tests/merge/trees/trivial.c b/tests/merge/trees/trivial.c
index 62a4574b8..55f38248f 100644
--- a/tests/merge/trees/trivial.c
+++ b/tests/merge/trees/trivial.c
@@ -259,7 +259,7 @@ void test_merge_trees_trivial__13(void)
cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0));
cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
- cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0);
+ cl_assert_equal_oid(&expected_oid, &entry->id);
cl_assert(git_index_reuc_entrycount(result) == 0);
cl_assert(merge_trivial_conflict_entrycount(result) == 0);
diff --git a/tests/merge/workdir/analysis.c b/tests/merge/workdir/analysis.c
index 0e937857f..85918abe4 100644
--- a/tests/merge/workdir/analysis.c
+++ b/tests/merge/workdir/analysis.c
@@ -36,72 +36,105 @@ void test_merge_workdir_analysis__cleanup(void)
cl_git_sandbox_cleanup();
}
-static git_merge_analysis_t analysis_from_branch(const char *branchname)
+static void analysis_from_branch(
+ git_merge_analysis_t *merge_analysis,
+ git_merge_preference_t *merge_pref,
+ const char *branchname)
{
git_buf refname = GIT_BUF_INIT;
git_reference *their_ref;
git_merge_head *their_head;
- git_merge_analysis_t analysis;
git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname);
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname)));
cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
- cl_git_pass(git_merge_analysis(&analysis, repo, (const git_merge_head **)&their_head, 1));
+ cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_merge_head **)&their_head, 1));
git_buf_free(&refname);
git_merge_head_free(their_head);
git_reference_free(their_ref);
-
- return analysis;
}
void test_merge_workdir_analysis__fastforward(void)
{
- git_merge_analysis_t analysis;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
- analysis = analysis_from_branch(FASTFORWARD_BRANCH);
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (analysis & GIT_MERGE_ANALYSIS_NORMAL));
+ analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
}
void test_merge_workdir_analysis__no_fastforward(void)
{
- git_merge_analysis_t analysis;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
- analysis = analysis_from_branch(NOFASTFORWARD_BRANCH);
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, analysis);
+ analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
}
void test_merge_workdir_analysis__uptodate(void)
{
- git_merge_analysis_t analysis;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
- analysis = analysis_from_branch(UPTODATE_BRANCH);
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis);
+ analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
}
void test_merge_workdir_analysis__uptodate_merging_prev_commit(void)
{
- git_merge_analysis_t analysis;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
- analysis = analysis_from_branch(PREVIOUS_BRANCH);
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis);
+ analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
}
void test_merge_workdir_analysis__unborn(void)
{
- git_merge_analysis_t analysis;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
git_buf master = GIT_BUF_INIT;
git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master");
p_unlink(git_buf_cstr(&master));
- analysis = analysis_from_branch(NOFASTFORWARD_BRANCH);
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
- cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
+ analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN));
git_buf_free(&master);
}
+void test_merge_workdir_analysis__fastforward_with_config_noff(void)
+{
+ git_config *config;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ git_repository_config(&config, repo);
+ git_config_set_string(config, "merge.ff", "false");
+
+ analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
+ cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD));
+}
+
+void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void)
+{
+ git_config *config;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ git_repository_config(&config, repo);
+ git_config_set_string(config, "merge.ff", "only");
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
+ cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY));
+}
diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c
index 776e4ea69..2f776853e 100644
--- a/tests/merge/workdir/dirty.c
+++ b/tests/merge/workdir/dirty.c
@@ -97,7 +97,7 @@ static int merge_branch(void)
cl_git_pass(git_oid_fromstr(&their_oids[0], MERGE_BRANCH_OID));
cl_git_pass(git_merge_head_from_id(&their_heads[0], repo, &their_oids[0]));
- checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
error = git_merge(repo, (const git_merge_head **)their_heads, 1, &merge_opts, &checkout_opts);
git_merge_head_free(their_heads[0]);
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index 4c39394bb..8809f427d 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -86,3 +86,44 @@ void test_network_fetchlocal__partial(void)
git_strarray_free(&refnames);
git_remote_free(origin);
}
+
+static int remote_mirror_cb(git_remote **out, git_repository *repo,
+ const char *name, const char *url, void *payload)
+{
+ int error;
+ git_remote *remote;
+
+ GIT_UNUSED(payload);
+
+ if ((error = git_remote_create(&remote, repo, name, url)) < 0)
+ return error;
+
+ git_remote_clear_refspecs(remote);
+
+ if ((error = git_remote_add_fetch(remote, "+refs/*:refs/*")) < 0) {
+ git_remote_free(remote);
+ return error;
+ }
+
+ *out = remote;
+ return 0;
+}
+
+void test_network_fetchlocal__clone_into_mirror(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+ git_reference *head;
+
+ opts.bare = true;
+ opts.remote_cb = remote_mirror_cb;
+ cl_git_pass(git_clone(&repo, cl_git_fixture_url("testrepo.git"), "./foo.git", &opts));
+
+ cl_git_pass(git_reference_lookup(&head, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./foo.git");
+}
diff --git a/tests/network/matchhost.c b/tests/network/matchhost.c
new file mode 100644
index 000000000..3100dc21d
--- /dev/null
+++ b/tests/network/matchhost.c
@@ -0,0 +1,13 @@
+#include "clar_libgit2.h"
+#include "netops.h"
+
+void test_network_matchhost__match(void)
+{
+ cl_git_pass(gitno__match_host("*.example.org", "www.example.org"));
+ cl_git_pass(gitno__match_host("*.foo.example.org", "www.foo.example.org"));
+ cl_git_fail(gitno__match_host("*.foo.example.org", "foo.example.org"));
+ cl_git_fail(gitno__match_host("*.foo.example.org", "www.example.org"));
+ cl_git_fail(gitno__match_host("*.example.org", "example.org"));
+ cl_git_fail(gitno__match_host("*.example.org", "www.foo.example.org"));
+ cl_git_fail(gitno__match_host("*.example.org", "blah.www.www.example.org"));
+}
diff --git a/tests/network/refspecs.c b/tests/network/refspecs.c
index 676a1fa99..c6bcb10e8 100644
--- a/tests/network/refspecs.c
+++ b/tests/network/refspecs.c
@@ -84,4 +84,65 @@ void test_network_refspecs__parsing(void)
assert_refspec(GIT_DIRECTION_FETCH, "master", true);
assert_refspec(GIT_DIRECTION_PUSH, "master", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/pull/*/head:refs/remotes/origin/pr/*", true);
+}
+
+static void assert_valid_transform(const char *refspec, const char *name, const char *result)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_refspec__parse(&spec, refspec, true);
+ cl_git_pass(git_refspec_transform(&buf, &spec, name));
+ cl_assert_equal_s(result, buf.ptr);
+
+ git_buf_free(&buf);
+ git_refspec__free(&spec);
+}
+
+void test_network_refspecs__transform_mid_star(void)
+{
+ assert_valid_transform("refs/pull/*/head:refs/remotes/origin/pr/*", "refs/pull/23/head", "refs/remotes/origin/pr/23");
+ assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/master", "refs/remotes/origin/master");
+ assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/user/feature", "refs/remotes/origin/user/feature");
+ assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/master", "refs/heads/master");
+ assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/user/feature", "refs/heads/user/feature");
+ assert_valid_transform("refs/*:refs/*", "refs/heads/master", "refs/heads/master");
+}
+
+static void assert_invalid_transform(const char *refspec, const char *name)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_refspec__parse(&spec, refspec, true);
+ cl_git_fail(git_refspec_transform(&buf, &spec, name));
+
+ git_buf_free(&buf);
+ git_refspec__free(&spec);
+}
+
+void test_network_refspecs__invalid(void)
+{
+ assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "master");
+ assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "refs/headz/master");
+}
+
+static void assert_invalid_rtransform(const char *refspec, const char *name)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_refspec__parse(&spec, refspec, true);
+ cl_git_fail(git_refspec_rtransform(&buf, &spec, name));
+
+ git_buf_free(&buf);
+ git_refspec__free(&spec);
+}
+
+void test_network_refspecs__invalid_reverse(void)
+{
+ assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "master");
+ assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "refs/remotes/o/master");
}
diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c
new file mode 100644
index 000000000..243369fa2
--- /dev/null
+++ b/tests/network/remote/defaultbranch.c
@@ -0,0 +1,108 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_remote *g_remote;
+static git_repository *g_repo_a, *g_repo_b;
+
+void test_network_remote_defaultbranch__initialize(void)
+{
+ g_repo_a = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true));
+ cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a)));
+}
+
+void test_network_remote_defaultbranch__cleanup(void)
+{
+ git_remote_free(g_remote);
+ git_repository_free(g_repo_b);
+
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("repo-b.git");
+}
+
+static void assert_default_branch(const char *should)
+{
+ git_buf name = GIT_BUF_INIT;
+
+ cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_default_branch(&name, g_remote));
+ cl_assert_equal_s(should, name.ptr);
+ git_buf_free(&name);
+}
+
+void test_network_remote_defaultbranch__master(void)
+{
+ assert_default_branch("refs/heads/master");
+}
+
+void test_network_remote_defaultbranch__master_does_not_win(void)
+{
+ cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good", NULL, NULL));
+ assert_default_branch("refs/heads/not-good");
+}
+
+void test_network_remote_defaultbranch__master_on_detached(void)
+{
+ cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
+ assert_default_branch("refs/heads/master");
+}
+
+void test_network_remote_defaultbranch__no_default_branch(void)
+{
+ git_remote *remote_b;
+ const git_remote_head **heads;
+ size_t len;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b)));
+ cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_ls(&heads, &len, remote_b));
+ cl_assert_equal_i(0, len);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, remote_b));
+
+ git_remote_free(remote_b);
+}
+
+void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void)
+{
+ git_oid id, id_cloned;
+ git_reference *ref;
+ git_buf buf = GIT_BUF_INIT;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_name_to_id(&id, g_repo_a, "HEAD"));
+ cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/master"));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/not-good"));
+ cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote));
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL));
+
+ cl_assert(git_repository_head_detached(cloned_repo));
+ cl_git_pass(git_reference_name_to_id(&id_cloned, g_repo_a, "HEAD"));
+ cl_assert(git_oid_equal(&id, &id_cloned));
+
+ git_repository_free(cloned_repo);
+}
+
+void test_network_remote_defaultbranch__unborn_HEAD_with_branches(void)
+{
+ git_reference *ref;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo_a, "HEAD", "refs/heads/i-dont-exist", 1, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./semi-empty", NULL));
+
+ cl_assert(git_repository_head_unborn(cloned_repo));
+
+ git_repository_free(cloned_repo);
+}
diff --git a/tests/network/remote/delete.c b/tests/network/remote/delete.c
new file mode 100644
index 000000000..664f47a43
--- /dev/null
+++ b/tests/network/remote/delete.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+
+void test_network_remote_delete__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+}
+
+void test_network_remote_delete__cleanup(void)
+{
+ git_remote_free(_remote);
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_delete__cannot_delete_an_anonymous_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, _repo, "git://github.com/libgit2/libgit2", NULL));
+
+ cl_git_fail(git_remote_delete(remote));
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_delete__remove_remote_tracking_branches(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_remote_delete(_remote));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, _repo, "refs/remotes/test/master"));
+}
+
+void test_network_remote_delete__remove_remote_configuration_settings(void)
+{
+ cl_assert(count_config_entries_match(_repo, "remote\\.test\\.+") > 0);
+
+ cl_git_pass(git_remote_delete(_remote));
+
+ cl_assert_equal_i(0, count_config_entries_match(_repo, "remote\\.test\\.+"));
+}
+
+void test_network_remote_delete__remove_branch_upstream_configuration_settings(void)
+{
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", true);
+ assert_config_entry_existence(_repo, "branch.master.remote", true);
+
+ cl_git_pass(git_remote_delete(_remote));
+
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", false);
+ assert_config_entry_existence(_repo, "branch.mergeless.merge", false);
+ assert_config_entry_existence(_repo, "branch.master.remote", false);
+ assert_config_entry_existence(_repo, "branch.master.merge", false);
+}
diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c
index 75f767980..f1084fc38 100644
--- a/tests/network/remote/local.c
+++ b/tests/network/remote/local.c
@@ -55,6 +55,17 @@ void test_network_remote_local__retrieve_advertised_references(void)
cl_assert_equal_i(refs_len, 28);
}
+void test_network_remote_local__retrieve_advertised_before_connect(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len = 0;
+
+ git_buf_sets(&file_path_buf, cl_git_path_url(cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo, git_buf_cstr(&file_path_buf), NULL));
+ cl_git_fail(git_remote_ls(&refs, &refs_len, remote));
+}
+
void test_network_remote_local__retrieve_advertised_references_after_disconnect(void)
{
const git_remote_head **refs;
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index 306ccaee5..df424e961 100644
--- a/tests/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -60,21 +60,29 @@ void test_network_remote_remotes__pushurl(void)
cl_assert(git_remote_pushurl(_remote) == NULL);
}
+void test_network_remote_remotes__error_when_not_found(void)
+{
+ git_remote *r;
+ cl_git_fail_with(git_remote_load(&r, _repo, "does-not-exist"), GIT_ENOTFOUND);
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_CONFIG);
+}
+
void test_network_remote_remotes__error_when_no_push_available(void)
{
git_remote *r;
- git_transport *t;
git_push *p;
cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git"), NULL));
- cl_git_pass(git_transport_local(&t,r,NULL));
+ cl_git_pass(git_remote_set_transport(r, git_transport_local, NULL));
+
+ cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH));
/* Make sure that push is really not available */
- t->push = NULL;
- cl_git_pass(git_remote_set_transport(r, t));
+ r->transport->push = NULL;
- cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH));
cl_git_pass(git_push_new(&p, r));
cl_git_pass(git_push_add_refspec(p, "refs/heads/master"));
cl_git_fail_with(git_push_finish(p), GIT_ERROR);
@@ -83,26 +91,24 @@ void test_network_remote_remotes__error_when_no_push_available(void)
git_remote_free(r);
}
-void test_network_remote_remotes__parsing_ssh_remote(void)
+void test_network_remote_remotes__supported_urls(void)
{
- cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") );
-}
+ int ssh_supported = 0, https_supported = 0;
-void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void)
-{
- cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") );
-}
-
-void test_network_remote_remotes__supported_transport_methods_are_supported(void)
-{
- cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
-}
+#ifdef GIT_SSH
+ ssh_supported = 1;
+#endif
-void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void)
-{
-#ifndef GIT_SSH
- cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") );
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+ https_supported = 1;
#endif
+
+ cl_assert(git_remote_supported_url("git://github.com/libgit2/libgit2"));
+ cl_assert(git_remote_supported_url("http://github.com/libgit2/libgit2"));
+
+ cl_assert_equal_i(ssh_supported, git_remote_supported_url("git@github.com:libgit2/libgit2.git"));
+ cl_assert_equal_i(ssh_supported, git_remote_supported_url("ssh://git@github.com/libgit2/libgit2.git"));
+ cl_assert_equal_i(https_supported, git_remote_supported_url("https://github.com/libgit2/libgit2.git"));
}
void test_network_remote_remotes__refspec_parsing(void)
@@ -429,27 +435,6 @@ void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl
git_remote_load(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND);
}
-void test_network_remote_remotes__check_structure_version(void)
-{
- git_transport transport = GIT_TRANSPORT_INIT;
- const git_error *err;
-
- git_remote_free(_remote);
- _remote = NULL;
- cl_git_pass(git_remote_create_anonymous(&_remote, _repo, "test-protocol://localhost", NULL));
-
- transport.version = 0;
- cl_git_fail(git_remote_set_transport(_remote, &transport));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- transport.version = 1024;
- cl_git_fail(git_remote_set_transport(_remote, &transport));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
void assert_cannot_create_remote(const char *name, int expected_error)
{
git_remote *remote = NULL;
@@ -524,3 +509,54 @@ void test_network_remote_remotes__query_refspecs(void)
git_remote_free(remote);
}
+
+static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ char *fetch_refspecs[] = {
+ "refs/heads/first-merge:refs/remotes/origin/first-merge",
+ };
+ git_strarray fetch_refspecs_strarray = {
+ fetch_refspecs,
+ 1,
+ };
+
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_remote_create(out, repo, name, url));
+ cl_git_pass(git_remote_set_fetch_refspecs(*out, &fetch_refspecs_strarray));
+
+ return 0;
+}
+
+void test_network_remote_remotes__single_branch(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+ git_strarray refs;
+ size_t i, count = 0;
+
+ opts.remote_cb = remote_single_branch;
+ opts.checkout_branch = "first-merge";
+
+ cl_git_pass(git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./single-branch", &opts));
+ cl_git_pass(git_reference_list(&refs, repo));
+
+ for (i = 0; i < refs.count; i++) {
+ if (!git__prefixcmp(refs.strings[i], "refs/heads/"))
+ count++;
+ }
+ cl_assert_equal_i(1, count);
+
+ git_strarray_free(&refs);
+ git_repository_free(repo);
+}
+
+void test_network_remote_remotes__restricted_refspecs(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+
+ opts.remote_cb = remote_single_branch;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts));
+}
diff --git a/tests/network/remote/rename.c b/tests/network/remote/rename.c
index 4d8628425..1b819a445 100644
--- a/tests/network/remote/rename.c
+++ b/tests/network/remote/rename.c
@@ -33,10 +33,14 @@ static int dont_call_me_cb(const char *fetch_refspec, void *payload)
void test_network_remote_rename__renaming_a_remote_moves_related_configuration_section(void)
{
+ git_strarray problems = {0};
+
assert_config_entry_existence(_repo, "remote.test.fetch", true);
assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
assert_config_entry_existence(_repo, "remote.test.fetch", false);
assert_config_entry_existence(_repo, "remote.just/renamed.fetch", true);
@@ -44,16 +48,24 @@ void test_network_remote_rename__renaming_a_remote_moves_related_configuration_s
void test_network_remote_rename__renaming_a_remote_updates_branch_related_configuration_entries(void)
{
+ git_strarray problems = {0};
+
assert_config_entry_value(_repo, "branch.master.remote", "test");
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
assert_config_entry_value(_repo, "branch.master.remote", "just/renamed");
}
void test_network_remote_rename__renaming_a_remote_updates_default_fetchrefspec(void)
{
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ git_strarray problems = {0};
+
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/heads/*:refs/remotes/just/renamed/*");
}
@@ -61,6 +73,7 @@ void test_network_remote_rename__renaming_a_remote_updates_default_fetchrefspec(
void test_network_remote_rename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one(void)
{
git_config *config;
+ git_strarray problems = {0};
git_remote_free(_remote);
cl_git_pass(git_repository_config__weakptr(&config, _repo));
@@ -70,70 +83,64 @@ void test_network_remote_rename__renaming_a_remote_without_a_fetchrefspec_doesnt
assert_config_entry_existence(_repo, "remote.test.fetch", false);
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
}
-static int ensure_refspecs(const char* refspec_name, void *payload)
-{
- int i = 0;
- bool found = false;
- const char ** exp = (const char **)payload;
-
- while (exp[i]) {
- if (strcmp(exp[i++], refspec_name))
- continue;
-
- found = true;
- break;
- }
-
- cl_assert(found);
-
- return 0;
-}
-
void test_network_remote_rename__renaming_a_remote_notifies_of_non_default_fetchrefspec(void)
{
git_config *config;
- char *expected_refspecs[] = {
- "+refs/*:refs/*",
- NULL
- };
+ git_strarray problems = {0};
git_remote_free(_remote);
cl_git_pass(git_repository_config__weakptr(&config, _repo));
cl_git_pass(git_config_set_string(config, "remote.test.fetch", "+refs/*:refs/*"));
cl_git_pass(git_remote_load(&_remote, _repo, "test"));
- cl_git_pass(git_remote_rename(_remote, "just/renamed", ensure_refspecs, &expected_refspecs));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(1, problems.count);
+ cl_assert_equal_s("+refs/*:refs/*", problems.strings[0]);
+ git_strarray_free(&problems);
assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/*:refs/*");
+
+ git_strarray_free(&problems);
}
void test_network_remote_rename__new_name_can_contain_dots(void)
{
- cl_git_pass(git_remote_rename(_remote, "just.renamed", dont_call_me_cb, NULL));
+ git_strarray problems = {0};
+
+ cl_git_pass(git_remote_rename(&problems, _remote, "just.renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
cl_assert_equal_s("just.renamed", git_remote_name(_remote));
}
void test_network_remote_rename__new_name_must_conform_to_reference_naming_conventions(void)
{
+ git_strarray problems = {0};
+
cl_assert_equal_i(
GIT_EINVALIDSPEC,
- git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL));
+ git_remote_rename(&problems, _remote, "new@{name"));
}
void test_network_remote_rename__renamed_name_is_persisted(void)
{
git_remote *renamed;
git_repository *another_repo;
+ git_strarray problems = {0};
cl_git_fail(git_remote_load(&renamed, _repo, "just/renamed"));
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
cl_git_pass(git_repository_open(&another_repo, "testrepo.git"));
cl_git_pass(git_remote_load(&renamed, _repo, "just/renamed"));
@@ -144,19 +151,24 @@ void test_network_remote_rename__renamed_name_is_persisted(void)
void test_network_remote_rename__cannot_overwrite_an_existing_remote(void)
{
- cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test", dont_call_me_cb, NULL));
- cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test_with_pushurl", dont_call_me_cb, NULL));
+ git_strarray problems = {0};
+
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _remote, "test"));
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _remote, "test_with_pushurl"));
}
void test_network_remote_rename__renaming_a_remote_moves_the_underlying_reference(void)
{
git_reference *underlying;
+ git_strarray problems = {0};
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed"));
cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
git_reference_free(underlying);
- cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+ cl_git_pass(git_remote_rename(&problems, _remote, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed/master"));
@@ -166,9 +178,91 @@ void test_network_remote_rename__renaming_a_remote_moves_the_underlying_referenc
void test_network_remote_rename__cannot_rename_an_inmemory_remote(void)
{
git_remote *remote;
+ git_strarray problems = {0};
cl_git_pass(git_remote_create_anonymous(&remote, _repo, "file:///blah", NULL));
- cl_git_fail(git_remote_rename(remote, "newname", NULL, NULL));
+ cl_git_fail(git_remote_rename(&problems, remote, "newname"));
+ git_strarray_free(&problems);
git_remote_free(remote);
}
+
+void test_network_remote_rename__overwrite_ref_in_target(void)
+{
+ git_oid id;
+ char idstr[GIT_OID_HEXSZ + 1] = {0};
+ git_remote *remote;
+ git_reference *ref;
+ git_branch_t btype;
+ git_branch_iterator *iter;
+ git_strarray problems = {0};
+
+ cl_git_pass(git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/remotes/renamed/master", &id, 1, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_load(&remote, _repo, "test"));
+ cl_git_pass(git_remote_rename(&problems, remote, "renamed"));
+ git_remote_free(remote);
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
+
+ /* make sure there's only one remote-tracking branch */
+ cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_next(&ref, &btype, iter));
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref));
+ git_oid_fmt(idstr, git_reference_target(ref));
+ cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr);
+ git_reference_free(ref);
+
+ cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter));
+ git_branch_iterator_free(iter);
+}
+
+void test_network_remote_rename__symref_head(void)
+{
+ int error;
+ git_remote *remote;
+ git_reference *ref;
+ git_branch_t btype;
+ git_branch_iterator *iter;
+ git_strarray problems = {0};
+ char idstr[GIT_OID_HEXSZ + 1] = {0};
+ git_vector refs;
+
+ cl_git_pass(git_reference_symbolic_create(&ref, _repo, "refs/remotes/test/HEAD", "refs/remotes/test/master", 0, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_load(&remote, _repo, "test"));
+ cl_git_pass(git_remote_rename(&problems, remote, "renamed"));
+ git_remote_free(remote);
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
+
+ cl_git_pass(git_vector_init(&refs, 2, (git_vector_cmp) git_reference_cmp));
+ cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE));
+
+ while ((error = git_branch_next(&ref, &btype, iter)) == 0) {
+ cl_git_pass(git_vector_insert(&refs, ref));
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ git_vector_sort(&refs);
+
+ cl_assert_equal_i(2, refs.length);
+
+ ref = git_vector_get(&refs, 0);
+ cl_assert_equal_s("refs/remotes/renamed/HEAD", git_reference_name(ref));
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ ref = git_vector_get(&refs, 1);
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref));
+ git_oid_fmt(idstr, git_reference_target(ref));
+ cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr);
+ git_reference_free(ref);
+
+ git_vector_free(&refs);
+
+ cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter));
+ git_branch_iterator_free(iter);
+}
diff --git a/tests/network/urlparse.c b/tests/network/urlparse.c
index 2a9c2f69f..b3ac8ae60 100644
--- a/tests/network/urlparse.c
+++ b/tests/network/urlparse.c
@@ -33,6 +33,24 @@ void test_network_urlparse__trivial(void)
cl_assert_equal_p(pass, NULL);
}
+void test_network_urlparse__root(void)
+{
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "http://example.com/", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/");
+ cl_assert_equal_p(user, NULL);
+ cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__just_hostname(void)
+{
+ cl_git_fail_with(GIT_EINVALIDSPEC,
+ gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "http://example.com", "8080"));
+}
+
void test_network_urlparse__encoded_password(void)
{
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
diff --git a/tests/notes/notes.c b/tests/notes/notes.c
index e48d9df0e..8b1b57866 100644
--- a/tests/notes/notes.c
+++ b/tests/notes/notes.c
@@ -21,7 +21,7 @@ static void assert_note_equal(git_note *note, char *message, git_oid *note_oid)
git_blob *blob;
cl_assert_equal_s(git_note_message(note), message);
- cl_assert(!git_oid_cmp(git_note_id(note), note_oid));
+ cl_assert_equal_oid(git_note_id(note), note_oid);
cl_git_pass(git_blob_lookup(&blob, _repo, note_oid));
cl_assert_equal_s(git_note_message(note), (const char *)git_blob_rawcontent(blob));
@@ -61,10 +61,10 @@ static int note_list_cb(
cl_assert(*count < EXPECTATIONS_COUNT);
cl_git_pass(git_oid_fromstr(&expected_note_oid, list_expectations[*count].note_sha));
- cl_assert(git_oid_cmp(&expected_note_oid, blob_id) == 0);
+ cl_assert_equal_oid(&expected_note_oid, blob_id);
cl_git_pass(git_oid_fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha));
- cl_assert(git_oid_cmp(&expected_target_oid, annotated_obj_id) == 0);
+ cl_assert_equal_oid(&expected_target_oid, annotated_obj_id);
(*count)++;
@@ -290,7 +290,7 @@ void test_notes_notes__can_read_a_note_in_an_existing_fanout(void)
cl_git_pass(git_note_read(&note, _repo, "refs/notes/fanout", &target_oid));
cl_git_pass(git_oid_fromstr(&note_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
- cl_assert(!git_oid_cmp(git_note_id(note), &note_oid));
+ cl_assert_equal_oid(git_note_id(note), &note_oid);
git_note_free(note);
}
diff --git a/tests/notes/notesref.c b/tests/notes/notesref.c
index a33141979..a59af209c 100644
--- a/tests/notes/notesref.c
+++ b/tests/notes/notesref.c
@@ -46,13 +46,13 @@ void test_notes_notesref__config_corenotesref(void)
cl_git_pass(git_note_read(&_note, _repo, NULL, &oid));
cl_assert_equal_s("test123test\n", git_note_message(_note));
- cl_assert(!git_oid_cmp(git_note_id(_note), &note_oid));
+ cl_assert_equal_oid(git_note_id(_note), &note_oid);
git_note_free(_note);
cl_git_pass(git_note_read(&_note, _repo, "refs/notes/mydefaultnotesref", &oid));
cl_assert_equal_s("test123test\n", git_note_message(_note));
- cl_assert(!git_oid_cmp(git_note_id(_note), &note_oid));
+ cl_assert_equal_oid(git_note_id(_note), &note_oid);
cl_git_pass(git_note_default_ref(&default_ref, _repo));
cl_assert_equal_s("refs/notes/mydefaultnotesref", default_ref);
diff --git a/tests/object/blob/filter.c b/tests/object/blob/filter.c
index 0b2d6bf9e..0aaaee6f3 100644
--- a/tests/object/blob/filter.c
+++ b/tests/object/blob/filter.c
@@ -112,7 +112,7 @@ void test_object_blob_filter__to_odb(void)
git_config *cfg;
int i;
git_blob *blob;
- git_buf out = GIT_BUF_INIT;
+ git_buf out = GIT_BUF_INIT, zeroed;
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_assert(cfg);
@@ -121,19 +121,26 @@ void test_object_blob_filter__to_odb(void)
cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
cl_git_pass(git_filter_list_load(
- &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB));
+ &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB, 0));
cl_assert(fl != NULL);
for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+ /* try once with allocated blob */
cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
-
cl_assert_equal_sz(g_crlf_filtered[i].size, out.size);
-
cl_assert_equal_i(
0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size));
+ /* try again with zeroed blob */
+ memset(&zeroed, 0, sizeof(zeroed));
+ cl_git_pass(git_filter_list_apply_to_blob(&zeroed, fl, blob));
+ cl_assert_equal_sz(g_crlf_filtered[i].size, zeroed.size);
+ cl_assert_equal_i(
+ 0, memcmp(zeroed.ptr, g_crlf_filtered[i].ptr, zeroed.size));
+ git_buf_free(&zeroed);
+
git_blob_free(blob);
}
diff --git a/tests/object/cache.c b/tests/object/cache.c
index b927b2514..bdf12da7a 100644
--- a/tests/object/cache.c
+++ b/tests/object/cache.c
@@ -229,7 +229,7 @@ void test_object_cache__threadmania(void)
#ifdef GIT_THREADS
for (th = 0; th < THREADCOUNT; ++th) {
- cl_git_pass(git_thread_join(t[th], &data));
+ cl_git_pass(git_thread_join(&t[th], &data));
cl_assert_equal_i(th, ((int *)data)[0]);
git__free(data);
}
@@ -276,7 +276,7 @@ void test_object_cache__fast_thread_rush(void)
#ifdef GIT_THREADS
for (th = 0; th < THREADCOUNT*2; ++th) {
void *rval;
- cl_git_pass(git_thread_join(t[th], &rval));
+ cl_git_pass(git_thread_join(&t[th], &rval));
cl_assert_equal_i(th, *((int *)rval));
}
#endif
diff --git a/tests/object/commit/commitstagedfile.c b/tests/object/commit/commitstagedfile.c
index 3e7b3c02c..5b48519b8 100644
--- a/tests/object/commit/commitstagedfile.c
+++ b/tests/object/commit/commitstagedfile.c
@@ -112,7 +112,7 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
memset(&buffer, 0, sizeof(git_buf));
- cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0));
+ cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0, '#'));
cl_git_pass(git_commit_create_v(
&commit_oid,
@@ -175,6 +175,10 @@ void test_object_commit_commitstagedfile__amend_commit(void)
cl_git_pass(git_commit_amend(
&new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+ /* fail because the commit isn't the tip of the branch anymore */
+ cl_git_fail(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+
cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid));
cl_assert_equal_i(0, git_commit_parentcount(new_commit));
@@ -182,6 +186,7 @@ void test_object_commit_commitstagedfile__amend_commit(void)
assert_commit_is_head(new_commit);
git_commit_free(old_commit);
+
old_commit = new_commit;
/* let's amend the tree of that last commit */
@@ -192,6 +197,10 @@ void test_object_commit_commitstagedfile__amend_commit(void)
cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
cl_assert_equal_i(2, git_tree_entrycount(tree));
+ /* fail to amend on a ref which does not exist */
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_amend(
+ &new_oid, old_commit, "refs/heads/nope", NULL, NULL, NULL, "Initial commit", tree));
+
cl_git_pass(git_commit_amend(
&new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", tree));
git_tree_free(tree);
diff --git a/tests/object/lookupbypath.c b/tests/object/lookupbypath.c
index 31aac7647..13cd6a128 100644
--- a/tests/object/lookupbypath.c
+++ b/tests/object/lookupbypath.c
@@ -52,16 +52,16 @@ void test_object_lookupbypath__from_root_tree(void)
{
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
- cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
- git_object_id(g_actualobject)));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
}
void test_object_lookupbypath__from_head_commit(void)
{
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
- cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
- git_object_id(g_actualobject)));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
}
void test_object_lookupbypath__from_subdir_tree(void)
@@ -74,8 +74,8 @@ void test_object_lookupbypath__from_subdir_tree(void)
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
"subdir_test2.txt", GIT_OBJ_BLOB));
- cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
- git_object_id(g_actualobject)));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
git_tree_entry_free(entry);
git_tree_free(tree);
diff --git a/tests/object/message.c b/tests/object/message.c
index ab5565341..40d8e7297 100644
--- a/tests/object/message.c
+++ b/tests/object/message.c
@@ -6,7 +6,7 @@ static void assert_message_prettifying(char *expected_output, char *input, int s
{
git_buf prettified_message = GIT_BUF_INIT;
- git_message_prettify(&prettified_message, input, strip_comments);
+ git_message_prettify(&prettified_message, input, strip_comments, '#');
cl_assert_equal_s(expected_output, git_buf_cstr(&prettified_message));
git_buf_free(&prettified_message);
@@ -175,25 +175,25 @@ void test_object_message__message_prettify(void)
git_buf buffer;
memset(&buffer, 0, sizeof(buffer));
- cl_git_pass(git_message_prettify(&buffer, "", 0));
+ cl_git_pass(git_message_prettify(&buffer, "", 0, '#'));
cl_assert_equal_s(buffer.ptr, "");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "", 1));
+ cl_git_pass(git_message_prettify(&buffer, "", 1, '#'));
cl_assert_equal_s(buffer.ptr, "");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "Short", 0));
+ cl_git_pass(git_message_prettify(&buffer, "Short", 0, '#'));
cl_assert_equal_s("Short\n", buffer.ptr);
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "Short", 1));
+ cl_git_pass(git_message_prettify(&buffer, "Short", 1, '#'));
cl_assert_equal_s("Short\n", buffer.ptr);
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0));
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0, '#'));
cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n# with some comments still in\n");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1));
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1, '#'));
cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n");
git_buf_free(&buffer);
}
diff --git a/tests/object/peel.c b/tests/object/peel.c
index b6c9c7a3b..6310388c4 100644
--- a/tests/object/peel.c
+++ b/tests/object/peel.c
@@ -29,7 +29,7 @@ static void assert_peel(
cl_git_pass(git_object_peel(&peeled, obj, requested_type));
cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
- cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
+ cl_assert_equal_oid(&expected_oid, git_object_id(peeled));
cl_assert_equal_i(expected_type, git_object_type(peeled));
diff --git a/tests/object/raw/chars.c b/tests/object/raw/chars.c
index 206bf7119..cde0bdbf6 100644
--- a/tests/object/raw/chars.c
+++ b/tests/object/raw/chars.c
@@ -12,7 +12,7 @@ void test_object_raw_chars__find_invalid_chars_in_oid(void)
0xb7, 0x75, 0x21, 0x3c, 0x23,
0xa8, 0xbd, 0x74, 0xf5, 0xe0,
};
- char in[41] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0";
+ char in[] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0";
unsigned int i;
for (i = 0; i < 256; i++) {
diff --git a/tests/object/raw/compare.c b/tests/object/raw/compare.c
index 1c9ce4b81..56c016b72 100644
--- a/tests/object/raw/compare.c
+++ b/tests/object/raw/compare.c
@@ -90,7 +90,7 @@ void test_object_raw_compare__compare_fmt_oids(void)
cl_assert_equal_s(exp, out);
}
-void test_object_raw_compare__compare_allocfmt_oids(void)
+void test_object_raw_compare__compare_static_oids(void)
{
const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
git_oid in;
@@ -98,10 +98,9 @@ void test_object_raw_compare__compare_allocfmt_oids(void)
cl_git_pass(git_oid_fromstr(&in, exp));
- out = git_oid_allocfmt(&in);
+ out = git_oid_tostr_s(&in);
cl_assert(out);
cl_assert_equal_s(exp, out);
- git__free(out);
}
void test_object_raw_compare__compare_pathfmt_oids(void)
diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c
index 45356e807..ddb62e278 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -104,6 +104,7 @@ void test_object_tree_write__subtree(void)
void test_object_tree_write__sorted_subtrees(void)
{
git_treebuilder *builder;
+ git_tree *tree;
unsigned int i;
int position_c = -1, position_cake = -1, position_config = -1;
@@ -143,8 +144,9 @@ void test_object_tree_write__sorted_subtrees(void)
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
- for (i = 0; i < builder->entries.length; ++i) {
- git_tree_entry *entry = git_vector_get(&builder->entries, i);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
if (strcmp(entry->filename, "c") == 0)
position_c = i;
@@ -156,6 +158,8 @@ void test_object_tree_write__sorted_subtrees(void)
position_config = i;
}
+ git_tree_free(tree);
+
cl_assert(position_c != -1);
cl_assert(position_cake != -1);
cl_assert(position_config != -1);
diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c
index 256ae9cd7..75448a2f7 100644
--- a/tests/odb/foreach.c
+++ b/tests/odb/foreach.c
@@ -2,6 +2,7 @@
#include "odb.h"
#include "git2/odb_backend.h"
#include "pack.h"
+#include "buffer.h"
static git_odb *_odb;
static git_repository *_repo;
@@ -80,3 +81,26 @@ void test_odb_foreach__interrupt_foreach(void)
cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj));
cl_assert(nobj == 1000);
}
+
+void test_odb_foreach__files_in_objects_dir(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_buf buf = GIT_BUF_INIT;
+ int nobj = 0;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_buf_printf(&buf, "%s/objects/somefile", git_repository_path(repo)));
+ cl_git_mkfile(buf.ptr, "");
+ git_buf_free(&buf);
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_foreach(odb, foreach_cb, &nobj));
+ cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */
+
+ git_odb_free(odb);
+ git_repository_free(repo);
+ cl_fixture_cleanup("testrepo.git");
+}
diff --git a/tests/odb/mixed.c b/tests/odb/mixed.c
index ceba4ec81..2dad4b64e 100644
--- a/tests/odb/mixed.c
+++ b/tests/odb/mixed.c
@@ -58,7 +58,7 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
- cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
git_odb_object_free(obj);
strncpy(hex, "dea509d0b", sizeof(hex));
@@ -79,7 +79,7 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
- cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
git_odb_object_free(obj);
strncpy(hex, "81b5bff5f", sizeof(hex));
@@ -100,7 +100,7 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
- cl_assert(git_oid_equal(&found, git_odb_object_id(obj)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
git_odb_object_free(obj);
strncpy(hex, "0ddeadede", sizeof(hex));
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 6e0e63950..f7f3aaeda 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -8,10 +8,11 @@
#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
-#define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git"
-#define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git"
-#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git"
-#define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git"
+#define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git"
+#define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
+#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git"
+
+#define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
static git_repository *g_repo;
static git_clone_options g_options;
@@ -125,43 +126,61 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
git_buf_free(&path);
}
-void test_online_clone__clone_into(void)
+static int remote_mirror_cb(git_remote **out, git_repository *repo,
+ const char *name, const char *url, void *payload)
{
- git_buf path = GIT_BUF_INIT;
+ int error;
git_remote *remote;
- git_reference *head;
- git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
- git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ git_remote_callbacks *callbacks = (git_remote_callbacks *) payload;
- bool checkout_progress_cb_was_called = false,
- fetch_progress_cb_was_called = false;
- checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- checkout_opts.progress_cb = &checkout_progress;
- checkout_opts.progress_payload = &checkout_progress_cb_was_called;
+ if ((error = git_remote_create(&remote, repo, name, url)) < 0)
+ return error;
+
+ if ((error = git_remote_set_callbacks(remote, callbacks)) < 0) {
+ git_remote_free(remote);
+ return error;
+ }
+
+ git_remote_clear_refspecs(remote);
+
+ if ((error = git_remote_add_fetch(remote, "+refs/*:refs/*")) < 0) {
+ git_remote_free(remote);
+ return error;
+ }
+
+ *out = remote;
+ return 0;
+}
+
+void test_online_clone__clone_mirror(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
- cl_git_pass(git_repository_init(&g_repo, "./foo", false));
- cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
+ bool fetch_progress_cb_was_called = false;
callbacks.transfer_progress = &fetch_progress;
callbacks.payload = &fetch_progress_cb_was_called;
- git_remote_set_callbacks(remote, &callbacks);
- cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL, NULL));
+ opts.bare = true;
+ opts.remote_cb = remote_mirror_cb;
+ opts.remote_cb_payload = &callbacks;
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
- cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo.git", &opts));
cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
- cl_assert_equal_i(true, checkout_progress_cb_was_called);
cl_assert_equal_i(true, fetch_progress_cb_was_called);
- git_remote_free(remote);
git_reference_free(head);
- git_buf_free(&path);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ cl_fixture_cleanup("./foo.git");
}
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
@@ -205,13 +224,65 @@ void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
g_options.remote_callbacks.credentials = cred_failure_cb;
- /* TODO: this should expect -172. */
- cl_git_fail_with(git_clone(&g_repo, remote_url, "./foo", &g_options), -1);
+ cl_git_fail_with(-172, git_clone(&g_repo, remote_url, "./foo", &g_options));
+}
+
+static int cred_count_calls_cb(git_cred **cred, const char *url, const char *user,
+ unsigned int allowed_types, void *data)
+{
+ size_t *counter = (size_t *) data;
+
+ GIT_UNUSED(url); GIT_UNUSED(user); GIT_UNUSED(allowed_types);
+
+ if (allowed_types == GIT_CREDTYPE_USERNAME)
+ return git_cred_username_new(cred, "foo");
+
+ (*counter)++;
+
+ if (*counter == 3)
+ return GIT_EUSER;
+
+ return git_cred_userpass_plaintext_new(cred, "foo", "bar");
+}
+
+void test_online_clone__cred_callback_called_again_on_auth_failure(void)
+{
+ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ size_t counter = 0;
+
+ if (!remote_url || !remote_user)
+ clar__skip();
+
+ g_options.remote_callbacks.credentials = cred_count_calls_cb;
+ g_options.remote_callbacks.payload = &counter;
+
+ cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, remote_url, "./foo", &g_options));
+ cl_assert_equal_i(3, counter);
+}
+
+int cred_default(
+ git_cred **cred,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(user_from_url);
+ GIT_UNUSED(payload);
+
+ if (!(allowed_types & GIT_CREDTYPE_DEFAULT))
+ return 0;
+
+ return git_cred_default_new(cred);
}
void test_online_clone__credentials(void)
{
- /* Remote URL environment variable must be set. User and password are optional. */
+ /* Remote URL environment variable must be set.
+ * User and password are optional.
+ */
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
git_cred_userpass_payload user_pass = {
cl_getenv("GITTEST_REMOTE_USER"),
@@ -220,8 +291,12 @@ void test_online_clone__credentials(void)
if (!remote_url) return;
- g_options.remote_callbacks.credentials = git_cred_userpass;
- g_options.remote_callbacks.payload = &user_pass;
+ if (cl_getenv("GITTEST_REMOTE_DEFAULT")) {
+ g_options.remote_callbacks.credentials = cred_default;
+ } else {
+ g_options.remote_callbacks.credentials = git_cred_userpass;
+ g_options.remote_callbacks.payload = &user_pass;
+ }
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
@@ -254,11 +329,6 @@ void test_online_clone__bitbucket_style(void)
cl_fixture_cleanup("./foo");
}
-void test_online_clone__assembla_style(void)
-{
- cl_git_pass(git_clone(&g_repo, ASSEMBLA_REPO_URL, "./foo", NULL));
-}
-
static int cancel_at_half(const git_transfer_progress *stats, void *payload)
{
GIT_UNUSED(payload);
@@ -276,8 +346,217 @@ void test_online_clone__can_cancel(void)
git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321);
}
+static int cred_cb(git_cred **cred, const char *url, const char *user_from_url,
+ unsigned int allowed_types, void *payload)
+{
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ const char *pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+ const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+ const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+ GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
+ if (allowed_types & GIT_CREDTYPE_USERNAME)
+ return git_cred_username_new(cred, remote_user);
+ if (allowed_types & GIT_CREDTYPE_SSH_KEY)
+ return git_cred_ssh_key_new(cred, remote_user, pubkey, privkey, passphrase);
+
+ giterr_set(GITERR_NET, "unexpected cred type");
+ return -1;
+}
+
+static int check_ssh_auth_methods(git_cred **cred, const char *url, const char *username_from_url,
+ unsigned int allowed_types, void *data)
+{
+ int *with_user = (int *) data;
+ GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data);
+ if (!*with_user)
+ cl_assert_equal_i(GIT_CREDTYPE_USERNAME, allowed_types);
+ else
+ cl_assert(!(allowed_types & GIT_CREDTYPE_USERNAME));
+
+ return GIT_EUSER;
+}
+
+void test_online_clone__ssh_auth_methods(void)
+{
+ int with_user;
+
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ g_options.remote_callbacks.credentials = check_ssh_auth_methods;
+ g_options.remote_callbacks.payload = &with_user;
+
+ with_user = 0;
+ cl_git_fail_with(GIT_EUSER,
+ git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+
+ with_user = 1;
+ cl_git_fail_with(GIT_EUSER,
+ git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+static int custom_remote_ssh_with_paths(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ if ((error = git_remote_set_transport(*out, git_transport_ssh_with_paths, payload)) < 0)
+ return error;
+
+ callbacks.credentials = cred_cb;
+ git_remote_set_callbacks(*out, &callbacks);
+
+ return 0;
+}
+
+void test_online_clone__ssh_with_paths(void)
+{
+ char *bad_paths[] = {
+ "/bin/yes",
+ "/bin/false",
+ };
+ char *good_paths[] = {
+ "/usr/bin/git-upload-pack",
+ "/usr/bin/git-receive-pack",
+ };
+ git_strarray arr = {
+ bad_paths,
+ 2,
+ };
+
+ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0)
+ clar__skip();
+
+ g_options.remote_cb = custom_remote_ssh_with_paths;
+ g_options.remote_cb_payload = &arr;
+
+ cl_git_fail(git_clone(&g_repo, remote_url, "./foo", &g_options));
+
+ arr.strings = good_paths;
+ cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
+}
+
+static int cred_foo_bar(git_cred **cred, const char *url, const char *username_from_url,
+ unsigned int allowed_types, void *data)
+
+{
+ GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data);
+
+ return git_cred_userpass_plaintext_new(cred, "foo", "bar");
+}
+
+void test_online_clone__ssh_cannot_change_username(void)
+{
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ g_options.remote_callbacks.credentials = cred_foo_bar;
+
+ cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+int ssh_certificate_check(git_cert *cert, int valid, void *payload)
+{
+ git_cert_hostkey *key;
+ git_oid expected = {{0}}, actual = {{0}};
+ const char *expected_str;
+
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ expected_str = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT");
+ cl_assert(expected_str);
+
+ cl_git_pass(git_oid_fromstrp(&expected, expected_str));
+ cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type);
+ key = (git_cert_hostkey *) cert;
+
+ /*
+ * We need to figure out how long our input was to check for
+ * the type. Here we abuse the fact that both hashes fit into
+ * our git_oid type.
+ */
+ if (strlen(expected_str) == 32 && key->type & GIT_CERT_SSH_MD5) {
+ memcpy(&actual.id, key->hash_md5, 16);
+ } else if (strlen(expected_str) == 40 && key->type & GIT_CERT_SSH_SHA1) {
+ memcpy(&actual, key->hash_sha1, 20);
+ } else {
+ cl_fail("Cannot find a usable SSH hash");
+ }
+
+ cl_assert(!memcmp(&expected, &actual, 20));
+
+ return GIT_EUSER;
+}
+
+void test_online_clone__ssh_cert(void)
+{
+ g_options.remote_callbacks.certificate_check = ssh_certificate_check;
+
+ if (!cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"))
+ cl_skip();
+
+ cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options));
+}
+
+void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void)
+{
+ cl_git_fail_with(git_clone(&g_repo, "http://github.com", "./foo", &g_options),
+ GIT_EINVALIDSPEC);
+}
+
+static int fail_certificate_check(git_cert *cert, int valid, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ return GIT_ECERTIFICATE;
+}
+
+void test_online_clone__certificate_invalid(void)
+{
+ g_options.remote_callbacks.certificate_check = fail_certificate_check;
+
+ cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options),
+ GIT_ECERTIFICATE);
+
+#ifdef GIT_SSH
+ cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
+ GIT_ECERTIFICATE);
+#endif
+}
+
+static int succeed_certificate_check(git_cert *cert, int valid, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ return 0;
+}
+
+void test_online_clone__certificate_valid(void)
+{
+ g_options.remote_callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
diff --git a/tests/online/fetch.c b/tests/online/fetch.c
index c54ec5673..f03a6faa6 100644
--- a/tests/online/fetch.c
+++ b/tests/online/fetch.c
@@ -184,3 +184,21 @@ void test_online_fetch__ls_disconnected(void)
git_remote_free(remote);
}
+
+void test_online_fetch__remote_symrefs(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ git_remote_disconnect(remote);
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_s("HEAD", refs[0]->name);
+ cl_assert_equal_s("refs/heads/master", refs[0]->symref_target);
+
+ git_remote_free(remote);
+}
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 0b3f20db1..3f27e1331 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -67,6 +67,11 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet
void test_online_fetchhead__wildcard_spec(void)
{
fetchhead_test_clone();
+ fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
}
@@ -87,5 +92,12 @@ void test_online_fetchhead__no_merges(void)
cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
git_config_free(config);
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
}
diff --git a/tests/online/push.c b/tests/online/push.c
index 6da27bb96..70ec705fe 100644
--- a/tests/online/push.c
+++ b/tests/online/push.c
@@ -50,6 +50,15 @@ static int cred_acquire_cb(
GIT_UNUSED(user_from_url);
GIT_UNUSED(payload);
+ if (GIT_CREDTYPE_USERNAME & allowed_types) {
+ if (!_remote_user) {
+ printf("GITTEST_REMOTE_USER must be set\n");
+ return -1;
+ }
+
+ return git_cred_username_new(cred, _remote_user);
+ }
+
if (GIT_CREDTYPE_DEFAULT & allowed_types) {
if (!_remote_default) {
printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
@@ -204,6 +213,8 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r
cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
+
+ git_reference_free(ref);
}
cl_assert_equal_i(error, GIT_ITEROVER);
@@ -852,6 +863,8 @@ void test_online_push__notes(void)
const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
push_status exp_stats[] = { { "refs/notes/commits", 1 } };
expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
+ const char *specs_del[] = { ":refs/notes/commits" };
+
git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
target_oid = &_oid_b6;
@@ -864,5 +877,11 @@ void test_online_push__notes(void)
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ /* And make sure to delete the note */
+
+ do_push(specs_del, ARRAY_SIZE(specs_del),
+ exp_stats, 1,
+ NULL, 0, 0, 0, 0);
+
git_signature_free(signature);
}
diff --git a/tests/online/push_util.c b/tests/online/push_util.c
index 038c144db..68e71eacc 100644
--- a/tests/online/push_util.c
+++ b/tests/online/push_util.c
@@ -110,9 +110,8 @@ failed:
git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
for(i = 0; i < expected_refs_len; i++) {
- cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid));
+ oid_str = git_oid_tostr_s(expected_refs[i].oid);
cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
- git__free(oid_str);
}
git_buf_puts(&msg, "\nACTUAL:\n");
@@ -121,9 +120,8 @@ failed:
if (master_present && !strcmp(actual->name, "refs/heads/master"))
continue;
- cl_assert(oid_str = git_oid_allocfmt(&actual->oid));
+ oid_str = git_oid_tostr_s(&actual->oid);
cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str));
- git__free(oid_str);
}
cl_fail(git_buf_cstr(&msg));
diff --git a/tests/online/push_util.h b/tests/online/push_util.h
index a7207c49e..7736912d6 100644
--- a/tests/online/push_util.h
+++ b/tests/online/push_util.h
@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO;
* @param data pointer to a record_callbacks_data instance
*/
#define RECORD_CALLBACKS_INIT(data) \
- { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, record_update_tips_cb, data }
+ { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, data }
typedef struct {
char *name;
diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c
index 084f8e666..49a106d98 100644
--- a/tests/pack/indexer.c
+++ b/tests/pack/indexer.c
@@ -74,7 +74,7 @@ void test_pack_indexer__fix_thin(void)
/* Store the missing base into your ODB so the indexer can fix the pack */
cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB));
git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
- cl_assert(!git_oid_cmp(&id, &should_id));
+ cl_assert_equal_oid(&should_id, &id);
cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
@@ -86,7 +86,7 @@ void test_pack_indexer__fix_thin(void)
cl_assert_equal_i(stats.local_objects, 1);
git_oid_fromstr(&should_id, "11f0f69b334728fdd8bc86b80499f22f29d85b15");
- cl_assert(!git_oid_cmp(git_indexer_hash(idx), &should_id));
+ cl_assert_equal_oid(&should_id, git_indexer_hash(idx));
git_indexer_free(idx);
git_odb_free(odb);
diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c
index 53db81828..29f3e2d64 100644
--- a/tests/pack/packbuilder.c
+++ b/tests/pack/packbuilder.c
@@ -17,6 +17,7 @@ static git_transfer_progress _stats;
void test_pack_packbuilder__initialize(void)
{
_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(p_chdir("testrepo.git"));
cl_git_pass(git_revwalk_new(&_revwalker, _repo));
cl_git_pass(git_packbuilder_new(&_packbuilder, _repo));
cl_git_pass(git_vector_init(&_commits, 0, NULL));
@@ -46,6 +47,7 @@ void test_pack_packbuilder__cleanup(void)
git_indexer_free(_indexer);
_indexer = NULL;
+ cl_git_pass(p_chdir(".."));
cl_git_sandbox_cleanup();
_repo = NULL;
}
@@ -91,7 +93,7 @@ void test_pack_packbuilder__create_pack(void)
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
git_hash_ctx ctx;
git_oid hash;
- char hex[41]; hex[40] = '\0';
+ char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
seed_packbuilder();
@@ -133,7 +135,7 @@ void test_pack_packbuilder__create_pack(void)
void test_pack_packbuilder__get_hash(void)
{
- char hex[41]; hex[40] = '\0';
+ char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
seed_packbuilder();
diff --git a/tests/pack/sharing.c b/tests/pack/sharing.c
new file mode 100644
index 000000000..a67d65588
--- /dev/null
+++ b/tests/pack/sharing.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "strmap.h"
+#include "mwindow.h"
+#include "pack.h"
+
+extern git_strmap *git__pack_cache;
+
+void test_pack_sharing__open_two_repos(void)
+{
+ git_repository *repo1, *repo2;
+ git_object *obj1, *obj2;
+ git_oid id;
+ git_strmap_iter pos;
+ void *data;
+ int error;
+
+ cl_git_pass(git_repository_open(&repo1, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
+
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ cl_git_pass(git_object_lookup(&obj1, repo1, &id, GIT_OBJ_ANY));
+ cl_git_pass(git_object_lookup(&obj2, repo2, &id, GIT_OBJ_ANY));
+
+ pos = 0;
+ while ((error = git_strmap_next(&data, &pos, git__pack_cache)) == 0) {
+ struct git_pack_file *pack = (struct git_pack_file *) data;
+
+ cl_assert_equal_i(2, pack->refcount.val);
+ }
+
+ cl_assert_equal_i(3, git_strmap_num_entries(git__pack_cache));
+
+ git_object_free(obj1);
+ git_object_free(obj2);
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ /* we don't want to keep the packs open after the repos go away */
+ cl_assert_equal_i(0, git_strmap_num_entries(git__pack_cache));
+}
diff --git a/tests/path/core.c b/tests/path/core.c
new file mode 100644
index 000000000..45f54df29
--- /dev/null
+++ b/tests/path/core.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+static void test_make_relative(
+ const char *expected_path,
+ const char *path,
+ const char *parent,
+ int expected_status)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_puts(&buf, path);
+ cl_assert_equal_i(expected_status, git_path_make_relative(&buf, parent));
+ cl_assert_equal_s(expected_path, buf.ptr);
+ git_buf_free(&buf);
+}
+
+void test_path_core__make_relative(void)
+{
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to", 0);
+ test_make_relative("bar/foo.c", "/path/to/bar/foo.c", "/path/to", 0);
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+
+ test_make_relative("", "/path/to", "/path/to", 0);
+ test_make_relative("", "/path/to", "/path/to/", 0);
+
+ test_make_relative("../", "/path/to", "/path/to/foo", 0);
+
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar", 0);
+ test_make_relative("../bar/foo.c", "/path/to/bar/foo.c", "/path/to/baz", 0);
+
+ test_make_relative("../../foo.c", "/path/to/foo.c", "/path/to/foo/bar", 0);
+ test_make_relative("../../foo/bar.c", "/path/to/foo/bar.c", "/path/to/bar/foo", 0);
+
+ test_make_relative("../../foo.c", "/foo.c", "/bar/foo", 0);
+
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar/", 0);
+
+ test_make_relative("foo.c", "d:/path/to/foo.c", "d:/path/to", 0);
+
+ test_make_relative("../foo", "/foo", "/bar", 0);
+ test_make_relative("path/to/foo.c", "/path/to/foo.c", "/", 0);
+ test_make_relative("../foo", "path/to/foo", "path/to/bar", 0);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND);
+ test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND);
+ test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND);
+ test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND);
+}
diff --git a/tests/refs/branches/create.c b/tests/refs/branches/create.c
index b91eed6e8..3a4f33b6e 100644
--- a/tests/refs/branches/create.c
+++ b/tests/refs/branches/create.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "refs.h"
+#include "path.h"
static git_repository *repo;
static git_commit *target;
@@ -7,10 +8,9 @@ static git_reference *branch;
void test_refs_branches_create__initialize(void)
{
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-
+ repo = cl_git_sandbox_init("testrepo.git");
branch = NULL;
+ target = NULL;
}
void test_refs_branches_create__cleanup(void)
@@ -21,10 +21,8 @@ void test_refs_branches_create__cleanup(void)
git_commit_free(target);
target = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
}
static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha)
@@ -140,3 +138,61 @@ void test_refs_branches_create__default_reflog_message(void)
git_reflog_free(log);
git_signature_free(sig);
}
+
+static void assert_branch_matches_name(
+ const char *expected, const char *lookup_as)
+{
+ git_reference *ref;
+ git_buf b = GIT_BUF_INIT;
+
+ cl_git_pass(git_branch_lookup(&ref, repo, lookup_as, GIT_BRANCH_LOCAL));
+
+ cl_git_pass(git_buf_sets(&b, "refs/heads/"));
+ cl_git_pass(git_buf_puts(&b, expected));
+ cl_assert_equal_s(b.ptr, git_reference_name(ref));
+
+ cl_git_pass(
+ git_oid_cmp(git_reference_target(ref), git_commit_id(target)));
+
+ git_reference_free(ref);
+ git_buf_free(&b);
+}
+
+void test_refs_branches_create__can_create_branch_with_unicode(void)
+{
+ const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+ const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+ const char *emoji = "\xF0\x9F\x8D\xB7";
+ const char *names[] = { nfc, nfd, emoji };
+ const char *alt[] = { nfd, nfc, NULL };
+ const char *expected[] = { nfc, nfd, emoji };
+ unsigned int i;
+ bool fs_decompose_unicode =
+ git_path_does_fs_decompose_unicode(git_repository_path(repo));
+
+ retrieve_known_commit(&target, repo);
+
+ if (cl_repo_get_bool(repo, "core.precomposeunicode"))
+ expected[1] = nfc;
+ /* test decomp. because not all Mac filesystems decompose unicode */
+ else if (fs_decompose_unicode)
+ expected[0] = nfd;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ const char *name;
+ cl_git_pass(git_branch_create(
+ &branch, repo, names[i], target, 0, NULL, NULL));
+ cl_git_pass(git_oid_cmp(
+ git_reference_target(branch), git_commit_id(target)));
+
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(expected[i], name);
+ assert_branch_matches_name(expected[i], names[i]);
+ if (fs_decompose_unicode && alt[i])
+ assert_branch_matches_name(expected[i], alt[i]);
+
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+ branch = NULL;
+ }
+}
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
index ed5f1627b..e3199e230 100644
--- a/tests/refs/branches/delete.c
+++ b/tests/refs/branches/delete.c
@@ -10,8 +10,7 @@ void test_refs_branches_delete__initialize(void)
{
git_oid id;
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL, NULL));
@@ -22,10 +21,8 @@ void test_refs_branches_delete__cleanup(void)
git_reference_free(fake_remote);
fake_remote = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
}
void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
diff --git a/tests/refs/branches/ishead.c b/tests/refs/branches/ishead.c
index 12a8c4449..d16a79652 100644
--- a/tests/refs/branches/ishead.c
+++ b/tests/refs/branches/ishead.c
@@ -7,7 +7,8 @@ static git_reference *branch;
void test_refs_branches_ishead__initialize(void)
{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ repo = cl_git_sandbox_init("testrepo.git");
+ branch = NULL;
}
void test_refs_branches_ishead__cleanup(void)
@@ -15,7 +16,7 @@ void test_refs_branches_ishead__cleanup(void)
git_reference_free(branch);
branch = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
}
@@ -28,34 +29,20 @@ void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
make_head_unborn(repo, NON_EXISTING_HEAD);
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
}
void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
delete_head(repo);
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
}
void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
@@ -95,9 +82,6 @@ void test_refs_branches_ishead__only_direct_references_are_considered(void)
{
git_reference *linked, *super, *head;
- git_repository_free(repo);
- repo = cl_git_sandbox_init("testrepo.git");
-
cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0, NULL, NULL));
cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0, NULL, NULL));
cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1, NULL, NULL));
@@ -111,6 +95,4 @@ void test_refs_branches_ishead__only_direct_references_are_considered(void)
git_reference_free(linked);
git_reference_free(super);
git_reference_free(head);
- cl_git_sandbox_cleanup();
- repo = NULL;
}
diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c
index 6c6dbbe21..f136b00d6 100644
--- a/tests/refs/branches/move.c
+++ b/tests/refs/branches/move.c
@@ -241,3 +241,20 @@ void test_refs_branches_move__default_reflog_message(void)
git_reflog_free(log);
git_signature_free(sig);
}
+
+void test_refs_branches_move__can_move_with_unicode(void)
+{
+ git_reference *original_ref, *new_ref;
+ const char *new_branch_name = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+ cl_git_pass(git_branch_move(&new_ref, original_ref, new_branch_name, 0, NULL, NULL));
+
+ if (cl_repo_get_bool(repo, "core.precomposeunicode"))
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\xC3\x85\x73\x74\x72\xC3\xB6\x6D", git_reference_name(new_ref));
+ else
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D", git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
diff --git a/tests/refs/create.c b/tests/refs/create.c
index 50b8e84f8..8e4d8d70b 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -45,7 +45,7 @@ void test_refs_create__symbolic(void)
cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
/* ...and that it points to the current master tip */
- cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
git_reference_free(looked_up_ref);
git_reference_free(resolved_ref);
@@ -54,7 +54,7 @@ void test_refs_create__symbolic(void)
cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
- cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
git_repository_free(repo2);
@@ -76,7 +76,7 @@ void test_refs_create__deep_symbolic(void)
cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL, NULL));
cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
- cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
git_reference_free(new_reference);
git_reference_free(looked_up_ref);
@@ -104,14 +104,14 @@ void test_refs_create__oid(void)
cl_assert_equal_s(looked_up_ref->name, new_head);
/* ...and that it points to the current master tip */
- cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(looked_up_ref));
git_reference_free(looked_up_ref);
/* Similar test with a fresh new repository */
cl_git_pass(git_repository_open(&repo2, "testrepo"));
cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head));
- cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(looked_up_ref));
git_repository_free(repo2);
diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c
index 026ff6d6a..ab13d7d15 100644
--- a/tests/refs/createwithlog.c
+++ b/tests/refs/createwithlog.c
@@ -42,7 +42,7 @@ void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(vo
entry = git_reflog_entry_byindex(reflog, 0);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
- cl_assert(git_oid_cmp(&id, &entry->oid_cur) == 0);
+ cl_assert_equal_oid(&id, &entry->oid_cur);
cl_assert_equal_s(message, entry->msg);
git_reflog_free(reflog);
diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c
index a29b0cf8b..c77451309 100644
--- a/tests/refs/iterator.c
+++ b/tests/refs/iterator.c
@@ -186,3 +186,36 @@ void test_refs_iterator__foreach_name_can_cancel(void)
-333);
cl_assert_equal_i(0, cancel_after);
}
+
+void test_refs_iterator__concurrent_delete(void)
+{
+ git_reference_iterator *iter;
+ size_t full_count = 0, concurrent_count = 0;
+ const char *name;
+ int error;
+
+ git_repository_free(repo);
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ full_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ cl_git_pass(git_reference_remove(repo, name));
+ concurrent_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_assert_equal_i(full_count, concurrent_count);
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
diff --git a/tests/refs/lookup.c b/tests/refs/lookup.c
index 2e31cf0f6..d076e491f 100644
--- a/tests/refs/lookup.c
+++ b/tests/refs/lookup.c
@@ -44,7 +44,7 @@ void test_refs_lookup__oid(void)
cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob"));
cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
- cl_assert(git_oid_cmp(&tag, &expected) == 0);
+ cl_assert_equal_oid(&expected, &tag);
}
void test_refs_lookup__namespace(void)
diff --git a/tests/refs/overwrite.c b/tests/refs/overwrite.c
index 78ce4ace7..c237d76f4 100644
--- a/tests/refs/overwrite.c
+++ b/tests/refs/overwrite.c
@@ -78,7 +78,7 @@ void test_refs_overwrite__object_id(void)
/* Ensure it has been overwritten */
cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
- cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ cl_assert_equal_oid(&id, git_reference_target(ref));
git_reference_free(ref);
}
@@ -130,7 +130,7 @@ void test_refs_overwrite__symbolic_with_object_id(void)
/* Ensure it points to the right place */
cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
cl_assert(git_reference_type(ref) & GIT_REF_OID);
- cl_assert(!git_oid_cmp(git_reference_target(ref), &id));
+ cl_assert_equal_oid(&id, git_reference_target(ref));
git_reference_free(ref);
}
diff --git a/tests/refs/pack.c b/tests/refs/pack.c
index 7f5c611a7..dbe377d7f 100644
--- a/tests/refs/pack.c
+++ b/tests/refs/pack.c
@@ -91,12 +91,12 @@ void test_refs_pack__symbolic(void)
/* make a bunch of references */
for (i = 0; i < 100; ++i) {
- snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
+ p_snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
cl_git_pass(git_reference_symbolic_create(
&ref, g_repo, name, "refs/heads/master", 0, NULL, NULL));
git_reference_free(ref);
- snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
+ p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL));
git_reference_free(ref);
}
diff --git a/tests/refs/peel.c b/tests/refs/peel.c
index f2fb6e259..542694c47 100644
--- a/tests/refs/peel.c
+++ b/tests/refs/peel.c
@@ -33,7 +33,7 @@ static void assert_peel_generic(
cl_git_pass(git_reference_peel(&peeled, ref, requested_type));
cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
- cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
+ cl_assert_equal_oid(&expected_oid, git_object_id(peeled));
cl_assert_equal_i(expected_type, git_object_type(peeled));
diff --git a/tests/refs/read.c b/tests/refs/read.c
index 52c307eb0..cb42a568b 100644
--- a/tests/refs/read.c
+++ b/tests/refs/read.c
@@ -83,7 +83,7 @@ void test_refs_read__symbolic(void)
cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
git_oid_fromstr(&id, current_master_tip);
- cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
+ cl_assert_equal_oid(&id, git_object_id(object));
git_object_free(object);
@@ -111,7 +111,7 @@ void test_refs_read__nested_symbolic(void)
cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
git_oid_fromstr(&id, current_master_tip);
- cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
+ cl_assert_equal_oid(&id, git_object_id(object));
git_object_free(object);
@@ -130,13 +130,13 @@ void test_refs_read__head_then_master(void)
cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref));
git_reference_free(reference);
git_reference_free(resolved_ref);
cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target));
cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref));
git_reference_free(reference);
git_reference_free(resolved_ref);
@@ -152,7 +152,7 @@ void test_refs_read__master_then_head(void)
cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(master_ref), git_reference_target(resolved_ref)));
+ cl_assert_equal_oid(git_reference_target(master_ref), git_reference_target(resolved_ref));
git_reference_free(reference);
git_reference_free(resolved_ref);
@@ -201,7 +201,7 @@ void test_refs_read__chomped(void)
cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped"));
- cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(chomped)));
+ cl_assert_equal_oid(git_reference_target(test), git_reference_target(chomped));
git_reference_free(test);
git_reference_free(chomped);
@@ -213,7 +213,7 @@ void test_refs_read__trailing(void)
cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing"));
- cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(trailing)));
+ cl_assert_equal_oid(git_reference_target(test), git_reference_target(trailing));
git_reference_free(trailing);
cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD"));
diff --git a/tests/refs/rename.c b/tests/refs/rename.c
index 88f0afd9c..c7901bd8b 100644
--- a/tests/refs/rename.c
+++ b/tests/refs/rename.c
@@ -220,7 +220,7 @@ void test_refs_rename__force_loose_packed(void)
/* Check we actually renamed it */
cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
- cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref));
git_reference_free(looked_up_ref);
/* And that the previous one doesn't exist any longer */
@@ -245,7 +245,7 @@ void test_refs_rename__force_loose(void)
/* Check we actually renamed it */
cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test"));
cl_assert_equal_s(looked_up_ref->name, "refs/heads/test");
- cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref));
git_reference_free(looked_up_ref);
/* And that the previous one doesn't exist any longer */
diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c
index 524ce771c..3a3378186 100644
--- a/tests/refs/settargetwithlog.c
+++ b/tests/refs/settargetwithlog.c
@@ -44,8 +44,8 @@ void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry
cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name));
entry = git_reflog_entry_byindex(reflog, 0);
- cl_assert(git_oid_cmp(&current_id, &entry->oid_old) == 0);
- cl_assert(git_oid_cmp(&target_id, &entry->oid_cur) == 0);
+ cl_assert_equal_oid(&current_id, &entry->oid_old);
+ cl_assert_equal_oid(&target_id, &entry->oid_cur);
cl_assert_equal_s(message, entry->msg);
git_reflog_free(reflog);
diff --git a/tests/refs/setter.c b/tests/refs/setter.c
index 9a945db00..a5d073a56 100644
--- a/tests/refs/setter.c
+++ b/tests/refs/setter.c
@@ -41,7 +41,7 @@ void test_refs_setter__update_direct(void)
cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
cl_assert(git_reference_type(test_ref) == GIT_REF_OID);
- cl_assert(git_oid_cmp(&id, git_reference_target(test_ref)) == 0);
+ cl_assert_equal_oid(&id, git_reference_target(test_ref));
git_reference_free(test_ref);
}
diff --git a/tests/refs/unicode.c b/tests/refs/unicode.c
index 471b0b8d3..9c7527cd7 100644
--- a/tests/refs/unicode.c
+++ b/tests/refs/unicode.c
@@ -32,8 +32,7 @@ void test_refs_unicode__create_and_lookup(void)
cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
- cl_assert_equal_i(
- 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2));
cl_assert_equal_s(REFNAME, git_reference_name(ref2));
git_reference_free(ref2);
@@ -43,8 +42,7 @@ void test_refs_unicode__create_and_lookup(void)
#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
- cl_assert_equal_i(
- 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2));
cl_assert_equal_s(REFNAME, git_reference_name(ref2));
git_reference_free(ref2);
#endif
diff --git a/tests/repo/config.c b/tests/repo/config.c
index 2e7be37aa..93dedd576 100644
--- a/tests/repo/config.c
+++ b/tests/repo/config.c
@@ -8,7 +8,8 @@ static git_buf path = GIT_BUF_INIT;
void test_repo_config__initialize(void)
{
cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
git_buf_clear(&path);
@@ -18,15 +19,19 @@ void test_repo_config__initialize(void)
void test_repo_config__cleanup(void)
{
- cl_git_pass(git_path_prettify(&path, "alternate", NULL));
- cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_sandbox_set_search_path_defaults();
+
git_buf_free(&path);
+
+ cl_git_pass(
+ git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("alternate"));
cl_fixture_cleanup("empty_standard_repo");
+
}
-void test_repo_config__open_missing_global(void)
+void test_repo_config__can_open_global_when_there_is_no_file(void)
{
git_repository *repo;
git_config *config, *global;
@@ -40,23 +45,23 @@ void test_repo_config__open_missing_global(void)
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_set_string(global, "test.set", "42"));
git_config_free(global);
git_config_free(config);
git_repository_free(repo);
-
- git_sysdir_global_shutdown();
}
-void test_repo_config__open_missing_global_with_separators(void)
+void test_repo_config__can_open_missing_global_with_separators(void)
{
git_repository *repo;
git_config *config, *global;
- cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
+ cl_git_pass(git_buf_printf(
+ &path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
@@ -69,20 +74,19 @@ void test_repo_config__open_missing_global_with_separators(void)
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_set_string(global, "test.set", "42"));
git_config_free(global);
git_config_free(config);
git_repository_free(repo);
-
- git_sysdir_global_shutdown();
}
#include "repository.h"
-void test_repo_config__read_no_configs(void)
+void test_repo_config__read_with_no_configs_at_all(void)
{
git_repository *repo;
int val;
@@ -106,9 +110,9 @@ void test_repo_config__read_no_configs(void)
cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
git_repository_free(repo);
- git_sysdir_global_shutdown();
+ /* with no local config, just system */
- /* with just system */
+ cl_sandbox_set_search_path_defaults();
cl_must_pass(p_mkdir("alternate/1", 0777));
cl_git_pass(git_buf_joinpath(&path, path.ptr, "1"));
@@ -123,7 +127,7 @@ void test_repo_config__read_no_configs(void)
cl_assert_equal_i(10, val);
git_repository_free(repo);
- /* with xdg + system */
+ /* with just xdg + system */
cl_must_pass(p_mkdir("alternate/2", 0777));
path.ptr[path.size - 1] = '2';
@@ -204,6 +208,4 @@ void test_repo_config__read_no_configs(void)
cl_assert(!git_path_exists("empty_standard_repo/.git/config"));
cl_assert(!git_path_exists("alternate/3/.gitconfig"));
-
- git_sysdir_global_shutdown();
}
diff --git a/tests/repo/hashfile.c b/tests/repo/hashfile.c
index 4cc9f18b4..ae8e122f6 100644
--- a/tests/repo/hashfile.c
+++ b/tests/repo/hashfile.c
@@ -22,14 +22,14 @@ void test_repo_hashfile__simple(void)
/* hash with repo relative path */
cl_git_pass(git_odb_hashfile(&a, "status/current_file", GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "current_file", GIT_OBJ_BLOB, NULL));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
cl_git_pass(git_buf_joinpath(&full, git_repository_workdir(_repo), "current_file"));
/* hash with full path */
cl_git_pass(git_odb_hashfile(&a, full.ptr, GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJ_BLOB, NULL));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
/* hash with invalid type */
cl_git_fail(git_odb_hashfile(&a, full.ptr, GIT_OBJ_ANY));
@@ -58,12 +58,12 @@ void test_repo_hashfile__filtered(void)
/* equal hashes because filter is binary */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, NULL));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
/* equal hashes when 'as_file' points to binary filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, "foo.bin"));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
/* not equal hashes when 'as_file' points to text filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
@@ -73,11 +73,11 @@ void test_repo_hashfile__filtered(void)
/* equal hashes when 'as_file' is empty and turns off filtering */
cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, ""));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, ""));
- cl_assert(git_oid_equal(&a, &b));
+ cl_assert_equal_oid(&a, &b);
/* some hash type failures */
cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0));
diff --git a/tests/repo/head.c b/tests/repo/head.c
index 79892a3ea..d678e150e 100644
--- a/tests/repo/head.c
+++ b/tests/repo/head.c
@@ -229,13 +229,13 @@ static void test_reflog(git_repository *repo, size_t idx,
if (old_spec) {
git_object *obj;
cl_git_pass(git_revparse_single(&obj, repo, old_spec));
- cl_assert_equal_i(0, git_oid_cmp(git_object_id(obj), git_reflog_entry_id_old(entry)));
+ cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_old(entry));
git_object_free(obj);
}
if (new_spec) {
git_object *obj;
cl_git_pass(git_revparse_single(&obj, repo, new_spec));
- cl_assert_equal_i(0, git_oid_cmp(git_object_id(obj), git_reflog_entry_id_new(entry)));
+ cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_new(entry));
git_object_free(obj);
}
diff --git a/tests/repo/init.c b/tests/repo/init.c
index aea383c64..189b0894a 100644
--- a/tests/repo/init.c
+++ b/tests/repo/init.c
@@ -367,6 +367,85 @@ void test_repo_init__extended_1(void)
cl_fixture_cleanup("root");
}
+void test_repo_init__relative_gitdir(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ opts.workdir_path = "../c_wd";
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
+void test_repo_init__relative_gitdir_2(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+ git_buf full_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_path_prettify(&full_path, ".", NULL));
+ cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));
+
+ opts.workdir_path = full_path.ptr;
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+ git_buf_free(&full_path);
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
static void assert_hooks_match(
diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c
index fb70a9ea0..764c2c6cd 100644
--- a/tests/repo/iterator.c
+++ b/tests/repo/iterator.c
@@ -960,3 +960,35 @@ void test_repo_iterator__fs_preserves_error(void)
git_iterator_free(i);
}
+
+void test_repo_iterator__skips_fifos_and_such(void)
+{
+#ifndef GIT_WIN32
+ git_iterator *i;
+ const git_index_entry *e;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_must_pass(p_mkdir("empty_standard_repo/dir", 0777));
+ cl_git_mkfile("empty_standard_repo/file", "not me");
+
+ cl_assert(!mkfifo("empty_standard_repo/fifo", 0777));
+ cl_assert(!access("empty_standard_repo/fifo", F_OK));
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "empty_standard_repo", GIT_ITERATOR_INCLUDE_TREES |
+ GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* .git */
+ cl_assert(S_ISDIR(e->mode));
+ cl_git_pass(git_iterator_advance(&e, i)); /* dir */
+ cl_assert(S_ISDIR(e->mode));
+ /* skips fifo */
+ cl_git_pass(git_iterator_advance(&e, i)); /* file */
+ cl_assert(S_ISREG(e->mode));
+
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
+
+ git_iterator_free(i);
+#endif
+}
diff --git a/tests/repo/open.c b/tests/repo/open.c
index 190adff1c..637c785d5 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -298,7 +298,8 @@ void test_repo_open__no_config(void)
git_config *config;
cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
/* remove local config */
cl_git_pass(git_futils_rmdir_r(
@@ -325,7 +326,7 @@ void test_repo_open__no_config(void)
git_repository_free(repo);
cl_fixture_cleanup("empty_standard_repo");
- git_sysdir_global_shutdown();
+ cl_sandbox_set_search_path_defaults();
}
void test_repo_open__force_bare(void)
diff --git a/tests/repo/pathspec.c b/tests/repo/pathspec.c
index 334066b67..5b86662bc 100644
--- a/tests/repo/pathspec.c
+++ b/tests/repo/pathspec.c
@@ -167,7 +167,7 @@ void test_repo_pathspec__workdir4(void)
cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m));
- cl_assert_equal_s("这", git_pathspec_match_list_entry(m, 12));
+ cl_assert_equal_s("\xE8\xBF\x99", git_pathspec_match_list_entry(m, 12));
git_pathspec_match_list_free(m);
git_pathspec_free(ps);
diff --git a/tests/repo/state.c b/tests/repo/state.c
index 2d6c780ee..13407bffc 100644
--- a/tests/repo/state.c
+++ b/tests/repo/state.c
@@ -59,8 +59,8 @@ void test_repo_state__revert(void)
void test_repo_state__cherry_pick(void)
{
- setup_simple_state(GIT_CHERRY_PICK_HEAD_FILE);
- assert_repo_state(GIT_REPOSITORY_STATE_CHERRY_PICK);
+ setup_simple_state(GIT_CHERRYPICK_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK);
cl_git_pass(git_repository_state_cleanup(_repo));
assert_repo_state(GIT_REPOSITORY_STATE_NONE);
}
diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c
index b015db18b..ff05fa249 100644
--- a/tests/revwalk/basic.c
+++ b/tests/revwalk/basic.c
@@ -49,12 +49,12 @@ static const int result_bytes = 24;
static int get_commit_index(git_oid *raw_oid)
{
int i;
- char oid[40];
+ char oid[GIT_OID_HEXSZ];
git_oid_fmt(oid, raw_oid);
for (i = 0; i < commit_count; ++i)
- if (memcmp(oid, commit_ids[i], 40) == 0)
+ if (memcmp(oid, commit_ids[i], GIT_OID_HEXSZ) == 0)
return i;
return -1;
@@ -74,9 +74,9 @@ static int test_walk_only(git_revwalk *walk,
while (git_revwalk_next(&oid, walk) == 0) {
result_array[i++] = get_commit_index(&oid);
/*{
- char str[41];
+ char str[GIT_OID_HEXSZ+1];
git_oid_fmt(str, &oid);
- str[40] = 0;
+ str[GIT_OID_HEXSZ] = 0;
printf(" %d) %s\n", i, str);
}*/
}
diff --git a/tests/revwalk/hidecb.c b/tests/revwalk/hidecb.c
index 26ff183fa..14cf39afd 100644
--- a/tests/revwalk/hidecb.c
+++ b/tests/revwalk/hidecb.c
@@ -69,21 +69,15 @@ static int hide_commit_cb(const git_oid *commit_id, void *data)
GIT_UNUSED(commit_id);
GIT_UNUSED(data);
- if (0 == git_oid_cmp(commit_id, &commit_ids[5]))
- return 1;
- else
- return 0;
-
+ return (git_oid_cmp(commit_id, &commit_ids[5]) == 0);
}
/* In payload data, pointer to a commit id is passed */
static int hide_commit_use_payload_cb(const git_oid *commit_id, void *data)
{
git_oid *hide_commit_id = data;
- if (git_oid_cmp(commit_id, hide_commit_id) == 0)
- return 1;
- else
- return 0;
+
+ return (git_oid_cmp(commit_id, hide_commit_id) == 0);
}
void test_revwalk_hidecb__hide_all_cb(void)
@@ -170,7 +164,7 @@ void test_revwalk_hidecb__hide_some_commits(void)
i = 0;
while ((error = git_revwalk_next(&id, walk)) == 0) {
- cl_assert_equal_i(git_oid_cmp(&id, &commit_ids[i]), 0);
+ cl_assert_equal_oid(&commit_ids[i], &id);
i++;
}
@@ -194,7 +188,7 @@ void test_revwalk_hidecb__test_payload(void)
i = 0;
while ((error = git_revwalk_next(&id, walk)) == 0) {
- cl_assert_equal_i(git_oid_cmp(&id, &commit_ids[i]), 0);
+ cl_assert_equal_oid(&commit_ids[i], &id);
i++;
}
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
index 97663502c..677e1a1b6 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -30,15 +30,15 @@ void test_revwalk_mergebase__single1(void)
cl_git_pass(git_oid_fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644"));
cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
+ cl_assert_equal_oid(&expected, &result);
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 2);
- cl_assert_equal_sz(behind, 1);
+ cl_assert_equal_sz(ahead, 1);
+ cl_assert_equal_sz(behind, 2);
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
- cl_assert_equal_sz(ahead, 1);
- cl_assert_equal_sz(behind, 2);
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 1);
}
void test_revwalk_mergebase__single2(void)
@@ -51,15 +51,15 @@ void test_revwalk_mergebase__single2(void)
cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
+ cl_assert_equal_oid(&expected, &result);
cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 4);
- cl_assert_equal_sz(behind, 1);
-
- cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one));
cl_assert_equal_sz(ahead, 1);
cl_assert_equal_sz(behind, 4);
+
+ cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 4);
+ cl_assert_equal_sz(behind, 1);
}
void test_revwalk_mergebase__merged_branch(void)
@@ -72,18 +72,18 @@ void test_revwalk_mergebase__merged_branch(void)
cl_git_pass(git_oid_fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
+ cl_assert_equal_oid(&expected, &result);
cl_git_pass(git_merge_base(&result, _repo, &two, &one));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
+ cl_assert_equal_oid(&expected, &result);
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 0);
- cl_assert_equal_sz(behind, 3);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
cl_assert_equal_sz(ahead, 3);
cl_assert_equal_sz(behind, 0);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 0);
+ cl_assert_equal_sz(behind, 3);
}
void test_revwalk_mergebase__two_way_merge(void)
@@ -95,13 +95,13 @@ void test_revwalk_mergebase__two_way_merge(void)
cl_git_pass(git_oid_fromstr(&two, "a953a018c5b10b20c86e69fef55ebc8ad4c5a417"));
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &one, &two));
- cl_assert_equal_sz(ahead, 2);
- cl_assert_equal_sz(behind, 8);
+ cl_assert_equal_sz(ahead, 8);
+ cl_assert_equal_sz(behind, 2);
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &two, &one));
- cl_assert_equal_sz(ahead, 8);
- cl_assert_equal_sz(behind, 2);
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 8);
}
void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
@@ -119,8 +119,8 @@ void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
cl_assert_equal_i(GIT_ENOTFOUND, error);
cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(2, ahead);
- cl_assert_equal_sz(4, behind);
+ cl_assert_equal_sz(4, ahead);
+ cl_assert_equal_sz(2, behind);
}
void test_revwalk_mergebase__prefer_youngest_merge_base(void)
@@ -132,7 +132,25 @@ void test_revwalk_mergebase__prefer_youngest_merge_base(void)
cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
+ cl_assert_equal_oid(&expected, &result);
+}
+
+void test_revwalk_mergebase__multiple_merge_bases(void)
+{
+ git_oid one, two, expected1, expected2;
+ git_oidarray result = {NULL, 0};
+
+ cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f "));
+ cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+ cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ cl_git_pass(git_merge_bases(&result, _repo, &one, &two));
+ cl_assert_equal_i(2, result.count);
+ cl_assert_equal_oid(&expected1, &result.ids[0]);
+ cl_assert_equal_oid(&expected2, &result.ids[1]);
+
+ git_oidarray_free(&result);
}
void test_revwalk_mergebase__no_off_by_one_missing(void)
@@ -177,7 +195,7 @@ static void assert_mergebase_many(const char *expected_sha, int count, ...)
cl_git_pass(git_merge_base_many(&oid, _repo, count, oids));
cl_git_pass(git_oid_fromstr(&expected, expected_sha));
- cl_assert(git_oid_cmp(&expected, &oid) == 0);
+ cl_assert_equal_oid(&expected, &oid);
}
git__free(oids);
@@ -241,7 +259,7 @@ static void assert_mergebase_octopus(const char *expected_sha, int count, ...)
cl_git_pass(git_merge_base_octopus(&oid, _repo, count, oids));
cl_git_pass(git_oid_fromstr(&expected, expected_sha));
- cl_assert(git_oid_cmp(&expected, &oid) == 0);
+ cl_assert_equal_oid(&expected, &oid);
}
git__free(oids);
diff --git a/tests/revwalk/simplify.c b/tests/revwalk/simplify.c
index 81c19d366..f65ce6c59 100644
--- a/tests/revwalk/simplify.c
+++ b/tests/revwalk/simplify.c
@@ -20,8 +20,8 @@ static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
static const char *expected_str[] = {
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
- "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
- "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 4 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 5 */
};
void test_revwalk_simplify__first_parent(void)
@@ -44,7 +44,7 @@ void test_revwalk_simplify__first_parent(void)
i = 0;
while ((error = git_revwalk_next(&id, walk)) == 0) {
- git_oid_cmp(&id, &expected[i]);
+ cl_assert_equal_oid(&expected[i], &id);
i++;
}
diff --git a/tests/stash/drop.c b/tests/stash/drop.c
index 63ff0377c..89a0ade72 100644
--- a/tests/stash/drop.c
+++ b/tests/stash/drop.c
@@ -115,7 +115,7 @@ void test_stash_drop__dropping_an_entry_rewrites_reflog_history(void)
cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
entry = git_reflog_entry_byindex(reflog, 0);
- cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_id_old(entry)));
+ cl_assert_equal_oid(&oid, git_reflog_entry_id_old(entry));
cl_assert_equal_sz(count - 1, git_reflog_entrycount(reflog));
git_reflog_free(reflog);
@@ -147,7 +147,7 @@ void retrieve_top_stash_id(git_oid *out)
cl_git_pass(git_revparse_single(&top_stash, repo, "stash@{0}"));
cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE));
- cl_assert_equal_i(true, git_oid_cmp(out, git_object_id(top_stash)) == 0);
+ cl_assert_equal_oid(out, git_object_id(top_stash));
git_object_free(top_stash);
}
@@ -162,13 +162,13 @@ void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void)
retrieve_top_stash_id(&oid);
cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}"));
- cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)) != 0);
+ cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)));
cl_git_pass(git_stash_drop(repo, 0));
retrieve_top_stash_id(&oid);
- cl_git_pass(git_oid_cmp(&oid, git_object_id(next_top_stash)));
+ cl_assert_equal_oid(&oid, git_object_id(next_top_stash));
git_object_free(next_top_stash);
}
diff --git a/tests/stash/save.c b/tests/stash/save.c
index 87c6d7e0f..7873d20ba 100644
--- a/tests/stash/save.c
+++ b/tests/stash/save.c
@@ -227,18 +227,12 @@ void test_stash_save__can_stash_against_a_detached_head(void)
void test_stash_save__stashing_updates_the_reflog(void)
{
- char *sha;
-
assert_object_oid("refs/stash@{0}", NULL, GIT_OBJ_COMMIT);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
- sha = git_oid_allocfmt(&stash_tip_oid);
-
- assert_object_oid("refs/stash@{0}", sha, GIT_OBJ_COMMIT);
+ assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJ_COMMIT);
assert_object_oid("refs/stash@{1}", NULL, GIT_OBJ_COMMIT);
-
- git__free(sha);
}
void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
@@ -374,6 +368,23 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE);
}
+void test_stash_save__ignored_directory(void)
+{
+ cl_git_pass(mkdir("stash/ignored_directory", 0777));
+ cl_git_pass(mkdir("stash/ignored_directory/sub", 0777));
+ cl_git_mkfile("stash/ignored_directory/sub/some_file", "stuff");
+
+ assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_WT_NEW);
+ cl_git_pass(git_ignore_add_rule(repo, "ignored_directory/"));
+ assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
+
+ cl_assert(!git_path_exists("stash/ignored_directory/sub/some_file"));
+ cl_assert(!git_path_exists("stash/ignored_directory/sub"));
+ cl_assert(!git_path_exists("stash/ignored_directory"));
+}
+
void test_stash_save__skip_submodules(void)
{
git_repository *untracked_repo;
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index d88b2eb6b..b2af79074 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -364,7 +364,6 @@ void test_status_ignore__leading_slash_ignores(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
- git_buf home = GIT_BUF_INIT;
static const char *paths_2[] = {
"dir/.gitignore",
"dir/a/ignore_me",
@@ -385,7 +384,7 @@ void test_status_ignore__leading_slash_ignores(void)
make_test_data(test_repo_1, test_files_1);
- cl_fake_home(&home);
+ cl_fake_home();
cl_git_mkfile("home/.gitignore", "/ignore_me\n");
{
git_config *cfg;
@@ -412,8 +411,6 @@ void test_status_ignore__leading_slash_ignores(void)
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
-
- cl_fake_home_cleanup(&home);
}
void test_status_ignore__contained_dir_with_matching_name(void)
@@ -684,3 +681,205 @@ void test_status_ignore__issue_1766_negated_ignores(void)
}
}
+static void add_one_to_index(const char *file)
+{
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, file));
+ git_index_free(index);
+}
+
+/* Some further broken scenarios that have been reported */
+void test_status_ignore__more_breakage(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/untracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/untracked",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "/d1/pfx-*\n"
+ "!/d1/pfx-d2/\n"
+ "/d1/pfx-d2/*\n"
+ "!/d1/pfx-d2/d3/\n"
+ "/d1/pfx-d2/d3/*\n"
+ "!/d1/pfx-d2/d3/d4/\n");
+ add_one_to_index("d1/pfx-d2/d3/d4/d5/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore",
+ "d1/pfx-d2/d3/d4/d5/tracked",
+ "d1/pfx-d2/d3/d4/d5/untracked",
+ "d1/pfx-d2/d3/d4/untracked",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/tracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/untracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/untracked");
+}
+
+void test_status_ignore__negative_ignores_inside_ignores(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/top/mid/btm/tracked",
+ "empty_standard_repo/top/mid/btm/untracked",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "top\n!top/mid/btm\n");
+ add_one_to_index("top/mid/btm/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore", "top/mid/btm/tracked", "top/mid/btm/untracked",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_INDEX_NEW, GIT_STATUS_WT_NEW,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 3;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ refute_is_ignored("top/mid/btm/tracked");
+ refute_is_ignored("top/mid/btm/untracked");
+}
+
+void test_status_ignore__negative_ignores_in_slash_star(void)
+{
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *list;
+ int found_look_ma = 0, found_what_about = 0;
+ size_t i;
+ static const char *test_files[] = {
+ "empty_standard_repo/bin/look-ma.txt",
+ "empty_standard_repo/bin/what-about-me.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "bin/*\n"
+ "!bin/w*\n");
+
+ assert_is_ignored("bin/look-ma.txt");
+ refute_is_ignored("bin/what-about-me.txt");
+
+ status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
+ for (i = 0; i < git_status_list_entrycount(list); i++) {
+ const git_status_entry *entry = git_status_byindex(list, i);
+
+ if (!strcmp("bin/look-ma.txt", entry->index_to_workdir->new_file.path))
+ found_look_ma = 1;
+
+ if (!strcmp("bin/what-about-me.txt", entry->index_to_workdir->new_file.path))
+ found_what_about = 1;
+ }
+ git_status_list_free(list);
+
+ cl_assert(found_look_ma);
+ cl_assert(found_what_about);
+}
+
+void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(void)
+{
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *list;
+ int found_parent_file = 0, found_parent_child1_file = 0, found_parent_child2_file = 0;
+ size_t i;
+ static const char *test_files[] = {
+ "empty_standard_repo/parent/file.txt",
+ "empty_standard_repo/parent/force.txt",
+ "empty_standard_repo/parent/child1/file.txt",
+ "empty_standard_repo/parent/child2/file.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "parent/*\n"
+ "!parent/force.txt\n"
+ "!parent/child1\n"
+ "!parent/child2/\n");
+
+ add_one_to_index("parent/force.txt");
+
+ assert_is_ignored("parent/file.txt");
+ refute_is_ignored("parent/force.txt");
+ refute_is_ignored("parent/child1/file.txt");
+ refute_is_ignored("parent/child2/file.txt");
+
+ status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
+ for (i = 0; i < git_status_list_entrycount(list); i++) {
+ const git_status_entry *entry = git_status_byindex(list, i);
+
+ if (!entry->index_to_workdir)
+ continue;
+
+ if (!strcmp("parent/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_file = 1;
+
+ if (!strcmp("parent/force.txt", entry->index_to_workdir->new_file.path))
+ found_parent_file = 1;
+
+ if (!strcmp("parent/child1/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_child1_file = 1;
+
+ if (!strcmp("parent/child2/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_child2_file = 1;
+ }
+ git_status_list_free(list);
+
+ cl_assert(found_parent_file);
+ cl_assert(found_parent_child1_file);
+ cl_assert(found_parent_child2_file);
+}
+
diff --git a/tests/status/single.c b/tests/status/single.c
index 292c9120a..6efaab294 100644
--- a/tests/status/single.c
+++ b/tests/status/single.c
@@ -22,7 +22,7 @@ void test_status_single__hash_single_file(void)
cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB));
- cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0);
+ cl_assert_equal_oid(&expected_id, &actual_id);
}
/* test retrieving OID from an empty file apart from the ODB */
@@ -40,6 +40,6 @@ void test_status_single__hash_single_empty_file(void)
cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB));
- cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0);
+ cl_assert_equal_oid(&expected_id, &actual_id);
}
diff --git a/tests/status/status_helpers.c b/tests/status/status_helpers.c
index 088279252..5d13caa9a 100644
--- a/tests/status/status_helpers.c
+++ b/tests/status/status_helpers.c
@@ -82,6 +82,9 @@ int cb_status__print(
if (status_flags & GIT_STATUS_IGNORED) {
wstatus = 'I'; wcount++;
}
+ if (status_flags & GIT_STATUS_WT_UNREADABLE) {
+ wstatus = 'X'; wcount++;
+ }
fprintf(stderr, "%c%c %s (%d/%d%s)\n",
istatus, wstatus, path, icount, wcount,
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
index def3d60f0..8897bf9b5 100644
--- a/tests/status/worktree.c
+++ b/tests/status/worktree.c
@@ -5,6 +5,8 @@
#include "posix.h"
#include "util.h"
#include "path.h"
+#include "../diff/diff_helpers.h"
+#include "git2/sys/diff.h"
/**
* Cleanup
@@ -40,11 +42,15 @@ void test_status_worktree__whole_repository(void)
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
-void assert_show(const int entry_counts, const char *entry_paths[],
- const unsigned int entry_statuses[], git_status_show_t show)
+void assert_show(
+ const int entry_counts,
+ const char *entry_paths[],
+ const unsigned int entry_statuses[],
+ git_repository *repo,
+ git_status_show_t show,
+ unsigned int extra_flags)
{
status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
memset(&counts, 0x0, sizeof(status_entry_counts));
@@ -52,7 +58,7 @@ void assert_show(const int entry_counts, const char *entry_paths[],
counts.expected_paths = entry_paths;
counts.expected_statuses = entry_statuses;
- opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | extra_flags;
opts.show = show;
cl_git_pass(
@@ -67,19 +73,19 @@ void assert_show(const int entry_counts, const char *entry_paths[],
void test_status_worktree__show_index_and_workdir(void)
{
assert_show(entry_count0, entry_paths0, entry_statuses0,
- GIT_STATUS_SHOW_INDEX_AND_WORKDIR);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_AND_WORKDIR, 0);
}
void test_status_worktree__show_index_only(void)
{
assert_show(entry_count5, entry_paths5, entry_statuses5,
- GIT_STATUS_SHOW_INDEX_ONLY);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_ONLY, 0);
}
void test_status_worktree__show_workdir_only(void)
{
assert_show(entry_count6, entry_paths6, entry_statuses6,
- GIT_STATUS_SHOW_WORKDIR_ONLY);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_WORKDIR_ONLY, 0);
}
/* this test is equivalent to t18-status.c:statuscb1 */
@@ -578,7 +584,11 @@ void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void
cl_git_pass(git_status_file(&status, repo, "current_file"));
- cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+ /* stat data on file should no longer match stat cache, even though
+ * file diff will be empty because of line-ending conversion - matches
+ * the Git command-line behavior here.
+ */
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
}
void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
@@ -873,3 +883,158 @@ void test_status_worktree__long_filenames(void)
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
+/* The update stat cache tests mostly just mirror other tests and try
+ * to make sure that updating the stat cache doesn't change the results
+ * while reducing the amount of work that needs to be done
+ */
+
+static void check_status0(git_status_list *status)
+{
+ size_t i, max_i = git_status_list_entrycount(status);
+ cl_assert_equal_sz(entry_count0, max_i);
+ for (i = 0; i < max_i; ++i) {
+ const git_status_entry *entry = git_status_byindex(status, i);
+ cl_assert_equal_i(entry_statuses0[i], entry->status);
+ }
+}
+
+void test_status_worktree__update_stat_cache_0(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *status;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_status_list_free(status);
+}
+
+void test_status_worktree__unreadable(void)
+{
+#ifndef GIT_WIN32
+ const char *expected_paths[] = { "no_permission/foo" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+#endif
+}
+
+void test_status_worktree__unreadable_not_included(void)
+{
+#ifndef GIT_WIN32
+ const char *expected_paths[] = { "no_permission/" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED);
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+#endif
+}
+
+void test_status_worktree__unreadable_as_untracked(void)
+{
+ const char *expected_paths[] = { "no_permission/foo" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
diff --git a/tests/stress/diff.c b/tests/stress/diff.c
index 3d2092614..0dda44d30 100644
--- a/tests/stress/diff.c
+++ b/tests/stress/diff.c
@@ -97,14 +97,14 @@ void test_stress_diff__rename_big_files(void)
cl_git_pass(git_repository_index(&index, g_repo));
for (i = 0; i < 100; i += 1) {
- snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
for (j = i * 256; j > 0; --j)
git_buf_printf(&b, "more content %d\n", i);
cl_git_mkfile(tmp, b.ptr);
}
for (i = 0; i < 100; i += 1) {
- snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
}
@@ -128,15 +128,15 @@ void test_stress_diff__rename_many_files(void)
git_buf_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0);
for (i = 0; i < 2500; i += 1) {
- snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
- snprintf(b.ptr, 9, "%08d", i);
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ p_snprintf(b.ptr, 9, "%08d", i);
b.ptr[8] = '\n';
cl_git_mkfile(tmp, b.ptr);
}
git_buf_free(&b);
for (i = 0; i < 2500; i += 1) {
- snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
}
diff --git a/tests/structinit/structinit.c b/tests/structinit/structinit.c
index 2942099dd..def9fefc0 100644
--- a/tests/structinit/structinit.c
+++ b/tests/structinit/structinit.c
@@ -2,6 +2,7 @@
#include <git2/sys/config.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/refdb_backend.h>
+#include <git2/sys/transport.h>
#define STRINGIFY(s) #s
@@ -48,7 +49,7 @@ void test_structinit_structinit__compare(void)
/* checkout */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \
- GIT_CHECKOUT_OPTIONS_INIT, git_checkout_init_opts);
+ GIT_CHECKOUT_OPTIONS_INIT, git_checkout_init_options);
/* clone */
CHECK_MACRO_FUNC_INIT_EQUAL( \
@@ -98,7 +99,7 @@ void test_structinit_structinit__compare(void)
/* revert */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_revert_options, GIT_REVERT_OPTIONS_VERSION, \
- GIT_REVERT_OPTIONS_INIT, git_revert_init_opts);
+ GIT_REVERT_OPTIONS_INIT, git_revert_init_options);
/* status */
CHECK_MACRO_FUNC_INIT_EQUAL( \
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index af81713f1..10717809e 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -2,6 +2,7 @@
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
+#include "fileops.h"
static git_repository *g_repo = NULL;
@@ -29,6 +30,10 @@ static void assert_submodule_url(const char* name, const char *url)
void test_submodule_add__url_absolute(void)
{
git_submodule *sm;
+ git_config *cfg;
+ git_repository *repo;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
g_repo = setup_fixture_submod2();
@@ -51,6 +56,21 @@ void test_submodule_add__url_absolute(void)
cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD"));
assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git");
+ cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2"));
+
+ /* Verify worktree path is relative */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_libgit2/", worktree_path);
+
+ /* Verify gitdir path is relative */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr);
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+
/* add a submodule not using a gitlink */
cl_git_pass(
@@ -68,13 +88,16 @@ void test_submodule_add__url_relative(void)
{
git_submodule *sm;
git_remote *remote;
+ git_strarray problems = {0};
/* default remote url is https://github.com/libgit2/false.git */
g_repo = cl_git_sandbox_init("testrepo2");
/* make sure we don't default to origin - rename origin -> test_remote */
cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
- cl_git_pass(git_remote_rename(remote, "test_remote", NULL, NULL));
+ cl_git_pass(git_remote_rename(&problems, remote, "test_remote"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_free(&problems);
cl_git_fail(git_remote_load(&remote, g_repo, "origin"));
git_remote_free(remote);
diff --git a/tests/submodule/repository_init.c b/tests/submodule/repository_init.c
new file mode 100644
index 000000000..24b47aff8
--- /dev/null
+++ b/tests/submodule/repository_init.c
@@ -0,0 +1,40 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_repository_init__basic(void)
+{
+ git_submodule *sm;
+ git_repository *repo;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_git_pass(git_submodule_repo_init(&repo, sm, 1));
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_gitmodules_only/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr);
+
+ cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git"));
+
+ cl_assert(git_path_isdir("submod2/.git/modules"));
+ cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only"));
+ cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD"));
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+}
diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
index 50aa97568..c6d04b40a 100644
--- a/tests/submodule/submodule_helpers.c
+++ b/tests/submodule/submodule_helpers.c
@@ -19,8 +19,8 @@ void rewrite_gitmodules(const char *workdir)
cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules"));
cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules"));
- cl_assert((in = fopen(in_f.ptr, "r")) != NULL);
- cl_assert((out = fopen(out_f.ptr, "w")) != NULL);
+ cl_assert((in = fopen(in_f.ptr, "rb")) != NULL);
+ cl_assert((out = fopen(out_f.ptr, "wb")) != NULL);
while (fgets(line, sizeof(line), in) != NULL) {
char *scan = line;
diff --git a/tests/threads/basic.c b/tests/threads/basic.c
index a329ee7f9..eb15293c7 100644
--- a/tests/threads/basic.c
+++ b/tests/threads/basic.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
+#include "thread_helpers.h"
#include "cache.h"
@@ -34,3 +35,16 @@ void test_threads_basic__multiple_init(void)
cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
git_repository_free(nested_repo);
}
+
+static void *set_error(void *dummy)
+{
+ giterr_set(GITERR_INVALID, "oh no, something happened!\n");
+
+ return dummy;
+}
+
+/* Set errors so we can check that we free it */
+void test_threads_basic__set_error(void)
+{
+ run_in_parallel(1, 4, set_error, NULL, NULL);
+}
diff --git a/tests/threads/diff.c b/tests/threads/diff.c
index 79b85800b..c32811469 100644
--- a/tests/threads/diff.c
+++ b/tests/threads/diff.c
@@ -1,6 +1,20 @@
#include "clar_libgit2.h"
#include "thread_helpers.h"
+#ifdef GIT_THREADS
+
+# if defined(GIT_WIN32)
+# define git_thread_yield() Sleep(0)
+# elif defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__DragonFly__)
+# define git_thread_yield() pthread_yield()
+# else
+# define git_thread_yield() sched_yield()
+# endif
+
+#else
+# define git_thread_yield() (void)0
+#endif
+
static git_repository *_repo;
static git_tree *_a, *_b;
static git_atomic _counts[4];
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
index 3b35b45e3..078267aa8 100644
--- a/tests/threads/refdb.c
+++ b/tests/threads/refdb.c
@@ -58,7 +58,7 @@ void test_threads_refdb__iterator(void)
/* make a bunch of references */
for (r = 0; r < 200; ++r) {
- snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
+ p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL));
git_reference_free(ref);
}
@@ -84,7 +84,7 @@ void test_threads_refdb__iterator(void)
#ifdef GIT_THREADS
for (t = 0; t < THREADS; ++t) {
- cl_git_pass(git_thread_join(th[t], NULL));
+ cl_git_pass(git_thread_join(&th[t], NULL));
}
#endif
@@ -102,7 +102,7 @@ static void *create_refs(void *arg)
cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
for (i = 0; i < 10; ++i) {
- snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
+ p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0, NULL, NULL));
if (i == 5) {
@@ -127,7 +127,7 @@ static void *delete_refs(void *arg)
char name[128];
for (i = 0; i < 10; ++i) {
- snprintf(
+ p_snprintf(
name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i);
if (!git_reference_lookup(&ref, g_repo, name)) {
@@ -167,7 +167,7 @@ void test_threads_refdb__edit_while_iterate(void)
/* make a bunch of references */
for (r = 0; r < 50; ++r) {
- snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
+ p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL));
git_reference_free(ref);
}
@@ -190,17 +190,22 @@ void test_threads_refdb__edit_while_iterate(void)
}
id[t] = t;
-#ifdef GIT_THREADS
- cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t]));
-#else
+
+ /* It appears with all reflog writing changes, etc., that this
+ * test has started to fail quite frequently, so let's disable it
+ * for now by just running on a single thread...
+ */
+/* #ifdef GIT_THREADS */
+/* cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); */
+/* #else */
fn(&id[t]);
-#endif
+/* #endif */
}
#ifdef GIT_THREADS
- for (t = 0; t < THREADS; ++t) {
- cl_git_pass(git_thread_join(th[t], NULL));
- }
+/* for (t = 0; t < THREADS; ++t) { */
+/* cl_git_pass(git_thread_join(th[t], NULL)); */
+/* } */
memset(th, 0, sizeof(th));
@@ -210,7 +215,7 @@ void test_threads_refdb__edit_while_iterate(void)
}
for (t = 0; t < THREADS; ++t) {
- cl_git_pass(git_thread_join(th[t], NULL));
+ cl_git_pass(git_thread_join(&th[t], NULL));
}
#endif
}
diff --git a/tests/threads/thread_helpers.c b/tests/threads/thread_helpers.c
index 25370dddb..760a7bd33 100644
--- a/tests/threads/thread_helpers.c
+++ b/tests/threads/thread_helpers.c
@@ -32,7 +32,7 @@ void run_in_parallel(
#ifdef GIT_THREADS
for (t = 0; t < threads; ++t)
- cl_git_pass(git_thread_join(th[t], NULL));
+ cl_git_pass(git_thread_join(&th[t], NULL));
memset(th, 0, threads * sizeof(git_thread));
#endif
diff --git a/tests/transport/register.c b/tests/transport/register.c
new file mode 100644
index 000000000..ea917d5d3
--- /dev/null
+++ b/tests/transport/register.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "git2/sys/transport.h"
+
+static git_transport _transport = GIT_TRANSPORT_INIT;
+
+static int dummy_transport(git_transport **transport, git_remote *owner, void *param)
+{
+ *transport = &_transport;
+ GIT_UNUSED(owner);
+ GIT_UNUSED(param);
+ return 0;
+}
+
+void test_transport_register__custom_transport(void)
+{
+ git_transport *transport;
+
+ cl_git_pass(git_transport_register("something", dummy_transport, NULL));
+
+ cl_git_pass(git_transport_new(&transport, NULL, "something://somepath"));
+
+ cl_assert(transport == &_transport);
+
+ cl_git_pass(git_transport_unregister("something"));
+}
+
+void test_transport_register__custom_transport_error_doubleregister(void)
+{
+ cl_git_pass(git_transport_register("something", dummy_transport, NULL));
+
+ cl_git_fail_with(git_transport_register("something", dummy_transport, NULL), GIT_EEXISTS);
+
+ cl_git_pass(git_transport_unregister("something"));
+}
+
+void test_transport_register__custom_transport_error_remove_non_existing(void)
+{
+ cl_git_fail_with(git_transport_unregister("something"), GIT_ENOTFOUND);
+}
+
+void test_transport_register__custom_transport_ssh(void)
+{
+ git_transport *transport;
+
+#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, "git@somehost:somepath"), -1);
+#else
+ cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
+ transport->free(transport);
+#endif
+
+ cl_git_pass(git_transport_register("ssh", dummy_transport, NULL));
+
+ cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
+
+ cl_assert(transport == &_transport);
+
+ cl_git_pass(git_transport_unregister("ssh"));
+
+#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, "git@somehost:somepath"), -1);
+#else
+ cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
+ transport->free(transport);
+#endif
+}