summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md7
-rw-r--r--CONTRIBUTING.md64
-rw-r--r--PROJECTS.md44
-rw-r--r--include/git2/clone.h78
-rw-r--r--src/filebuf.c2
-rw-r--r--src/global.c67
-rw-r--r--src/global.h5
-rw-r--r--src/indexer.c9
-rw-r--r--src/mwindow.c124
-rw-r--r--src/mwindow.h10
-rw-r--r--src/netops.c24
-rw-r--r--src/netops.h1
-rw-r--r--src/odb_pack.c9
-rw-r--r--src/pack.c19
-rw-r--r--src/pack.h3
-rw-r--r--src/revwalk.c4
-rw-r--r--src/transports/http.c2
-rw-r--r--src/win32/posix_w32.c30
-rw-r--r--tests/online/clone.c12
-rw-r--r--tests/pack/sharing.c42
20 files changed, 459 insertions, 97 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..4ae64627e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+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.
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 d17214471..5164d95b6 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
@@ -67,19 +68,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/include/git2/clone.h b/include/git2/clone.h
index b2c944a78..05b7522ce 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -23,10 +23,31 @@
*/
GIT_BEGIN_DECL
+/**
+ * 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;
@@ -36,37 +57,58 @@ typedef enum {
* Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this:
*
* git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
- *
- * - `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.
- *
- * ** "origin" remote options: **
- *
- * - `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.
*/
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.
+ */
git_remote_callbacks remote_callbacks;
+ /**
+ * Set to zero (false) to create a standard repo, or non-zero
+ * for a bare repo
+ */
int bare;
+
+ /**
+ * Set to 1 if errors validating the remote host's certificate
+ * should be ignored.
+ */
int ignore_cert_errors;
+
+ /**
+ * Whether to use a fetch or copy the object database.
+ */
git_clone_local_t local;
+
+ /**
+ * The name to be given to the remote that will be
+ * created. The default is "origin".
+ */
const char *remote_name;
+
+ /**
+ * 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;
} git_clone_options;
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/global.c b/src/global.c
index 1c4a7a1a9..03a4bcedf 100644
--- a/src/global.c
+++ b/src/global.c
@@ -16,6 +16,12 @@ git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+SSL_CTX *git__ssl_ctx;
+static git_mutex *openssl_locks;
+#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;
@@ -39,6 +45,62 @@ static void git__shutdown(void)
}
+#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
+}
+
/**
* Handle the global state with TLS
*
@@ -168,10 +230,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;
}
@@ -225,6 +291,7 @@ static git_global_st __state;
int git_threads_init(void)
{
+ init_ssl();
git_atomic_inc(&git__n_inits);
return 0;
}
diff --git a/src/global.h b/src/global.h
index 778250376..745df3e4a 100644
--- a/src/global.h
+++ b/src/global.h
@@ -15,6 +15,11 @@ typedef struct {
git_error error_t;
} 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/indexer.c b/src/indexer.c
index 25c3d0537..eb8b23e92 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 {
@@ -1044,6 +1046,11 @@ void git_indexer_free(git_indexer *idx)
}
git_vector_free_deep(&idx->deltas);
- git_packfile_free(idx->pack);
+
+ 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/mwindow.c b/src/mwindow.c
index 7e5fcdfbc..0d6535056 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,126 @@ 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)
+ return -1;
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ 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)
+ return -1;
+ *out = pack;
+ return 0;
+}
+
+int git_mwindow_put_pack(struct git_pack_file *pack)
+{
+ int count;
+ git_strmap_iter pos;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0)
+ return -1;
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return -1;
+ }
+
+ pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name);
+ if (!git_strmap_valid_index(git__pack_cache, pos)) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return GIT_ENOTFOUND;
+ }
+
+ 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 0;
+}
+
+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 +177,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..57fabae70 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);
+int git_mwindow_put_pack(struct git_pack_file *pack);
+
#endif
diff --git a/src/netops.c b/src/netops.c
index a0193a022..965e4775d 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -33,6 +33,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 +158,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,7 +203,6 @@ static int gitno_ssl_teardown(gitno_ssl *ssl)
ret = 0;
SSL_free(ssl->ssl);
- SSL_CTX_free(ssl->ctx);
return ret;
}
@@ -390,18 +390,12 @@ static int ssl_setup(gitno_socket *socket, const char *host, int flags)
{
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);
@@ -538,7 +532,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
@@ -559,7 +553,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
diff --git a/src/netops.h b/src/netops.h
index 8e3a2524f..dfb4ab7b4 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;
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/pack.c b/src/pack.c
index ace7abb58..767efb6c3 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -968,7 +968,7 @@ void git_packfile_free(struct git_pack_file *p)
cache_free(&p->bases);
- git_mwindow_free_all(&p->mwf);
+ git_mwindow_free_all_locked(&p->mwf);
if (p->mwf.fd >= 0)
p_close(p->mwf.fd);
@@ -1063,6 +1063,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;
diff --git a/src/pack.h b/src/pack.h
index 610e70c18..34d37d907 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -90,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;
@@ -123,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/revwalk.c b/src/revwalk.c
index 7aedd1f44..530c9705e 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -48,7 +48,7 @@ static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
assert(commit);
- git_array_alloc(pending);
+ git_array_init_to_size(pending, 2);
GITERR_CHECK_ARRAY(pending);
do {
@@ -67,7 +67,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/transports/http.c b/src/transports/http.c
index a7eff7365..ae608ab3d 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -260,7 +260,7 @@ static int on_headers_complete(http_parser *parser)
if (parser->status_code == 401 &&
get_verb == s->verb) {
- if (!t->owner->cred_acquire_payload) {
+ if (!t->owner->cred_acquire_cb) {
no_callback = 1;
} else {
int allowed_types = 0;
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 34938431a..fbadb1c9e 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -591,6 +591,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;
@@ -602,12 +627,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/tests/online/clone.c b/tests/online/clone.c
index 8a2a64f95..4f4312a8c 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -8,10 +8,9 @@
#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"
static git_repository *g_repo;
static git_clone_options g_options;
@@ -290,11 +289,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);
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));
+}