diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | CONTRIBUTING.md | 64 | ||||
-rw-r--r-- | PROJECTS.md | 44 | ||||
-rw-r--r-- | include/git2/clone.h | 78 | ||||
-rw-r--r-- | src/filebuf.c | 2 | ||||
-rw-r--r-- | src/global.c | 67 | ||||
-rw-r--r-- | src/global.h | 5 | ||||
-rw-r--r-- | src/indexer.c | 9 | ||||
-rw-r--r-- | src/mwindow.c | 124 | ||||
-rw-r--r-- | src/mwindow.h | 10 | ||||
-rw-r--r-- | src/netops.c | 24 | ||||
-rw-r--r-- | src/netops.h | 1 | ||||
-rw-r--r-- | src/odb_pack.c | 9 | ||||
-rw-r--r-- | src/pack.c | 19 | ||||
-rw-r--r-- | src/pack.h | 3 | ||||
-rw-r--r-- | src/revwalk.c | 4 | ||||
-rw-r--r-- | src/transports/http.c | 2 | ||||
-rw-r--r-- | src/win32/posix_w32.c | 30 | ||||
-rw-r--r-- | tests/online/clone.c | 12 | ||||
-rw-r--r-- | tests/pack/sharing.c | 42 |
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)); +} |