diff options
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | CMakeLists.txt | 16 | ||||
-rw-r--r-- | include/git2/config.h | 18 | ||||
-rw-r--r-- | include/git2/sys/config.h | 14 | ||||
-rw-r--r-- | include/git2/sys/transport.h | 22 | ||||
-rw-r--r-- | include/git2/transport.h | 11 | ||||
-rw-r--r-- | src/config.c | 36 | ||||
-rw-r--r-- | src/config.h | 15 | ||||
-rw-r--r-- | src/config_file.c | 109 | ||||
-rw-r--r-- | src/config_file.h | 10 | ||||
-rw-r--r-- | src/curl_stream.c | 1 | ||||
-rw-r--r-- | src/diff_driver.c | 13 | ||||
-rw-r--r-- | src/path.c | 2 | ||||
-rw-r--r-- | src/remote.c | 14 | ||||
-rw-r--r-- | src/transaction.c | 42 | ||||
-rw-r--r-- | src/transaction.h | 14 | ||||
-rw-r--r-- | src/transports/cred.c | 8 | ||||
-rw-r--r-- | src/transports/http.c | 10 | ||||
-rw-r--r-- | src/transports/smart.c | 14 | ||||
-rw-r--r-- | src/transports/smart_protocol.c | 2 | ||||
-rw-r--r-- | src/util.c | 2 | ||||
-rw-r--r-- | src/win32/w32_buffer.c (renamed from src/win32/buffer.c) | 3 | ||||
-rw-r--r-- | src/win32/w32_buffer.h (renamed from src/win32/buffer.h) | 0 | ||||
-rw-r--r-- | tests/config/write.c | 50 | ||||
-rw-r--r-- | tests/diff/drivers.c | 26 | ||||
-rw-r--r-- | tests/network/fetchlocal.c | 39 |
26 files changed, 457 insertions, 43 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 243b696d7..5dd4b3447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ v0.23 + 1 ### API additions +* `git_config_lock()` has been added, which allow for + transactional/atomic complex updates to the configuration, removing + the opportunity for concurrent operations and not committing any + changes until the unlock. + ### API removals ### Breaking API changes @@ -19,6 +24,10 @@ v0.23 + 1 with the reflog on ref deletion. The file-based backend must delete it, a database-backed one may wish to archive it. +* `git_config_backend` has gained two entries. `lock` and `unlock` + with which to implement the transactional/atomic semantics for the + configuration backend. + v0.23 ------ diff --git a/CMakeLists.txt b/CMakeLists.txt index a15ce7dbd..6c03c718c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,8 +152,18 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$ INCLUDE_DIRECTORIES(src include) IF (SECURITY_FOUND) - MESSAGE("-- Found Security ${SECURITY_DIRS}") - LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") + # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there + CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY) + IF (HAVE_NEWER_SECURITY) + MESSAGE("-- Found Security ${SECURITY_DIRS}") + LIST(APPEND LIBGIT2_PC_LIBS "-framework Security") + ELSE() + MESSAGE("-- Security framework is too old, falling back to OpenSSL") + SET(SECURITY_FOUND "NO") + SET(SECURITY_DIRS "") + SET(SECURITY_DIR "") + SET(USE_OPENSSL "ON") + ENDIF() ENDIF() IF (COREFOUNDATION_FOUND) @@ -286,7 +296,7 @@ IF (LIBSSH2_FOUND) #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}") SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) - CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "" HAVE_LIBSSH2_MEMORY_CREDENTIALS) + CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS) IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS) ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS) ENDIF() diff --git a/include/git2/config.h b/include/git2/config.h index 6d3fdb0c2..05c3ad622 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -689,6 +689,24 @@ GIT_EXTERN(int) git_config_backend_foreach_match( void *payload); +/** + * Lock the backend with the highest priority + * + * Locking disallows anybody else from writing to that backend. Any + * updates made after locking will not be visible to a reader until + * the file is unlocked. + * + * You can apply the changes by calling `git_transaction_commit()` + * before freeing the transaction. Either of these actions will unlock + * the config. + * + * @param tx the resulting transaction, use this to commit or undo the + * changes + * @param cfg the configuration in which to lock + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 044e34417..4dad6da42 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -67,6 +67,20 @@ struct git_config_backend { int (*iterator)(git_config_iterator **, struct git_config_backend *); /** Produce a read-only version of this backend */ int (*snapshot)(struct git_config_backend **, struct git_config_backend *); + /** + * Lock this backend. + * + * Prevent any writes to the data store backing this + * backend. Any updates must not be visible to any other + * readers. + */ + int (*lock)(struct git_config_backend *); + /** + * Unlock the data store backing this backend. If success is + * true, the changes should be committed, otherwise rolled + * back. + */ + int (*unlock)(struct git_config_backend *, int success); void (*free)(struct git_config_backend *); }; #define GIT_CONFIG_BACKEND_VERSION 1 diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index 867fbcbce..4a75b0832 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -211,6 +211,28 @@ GIT_EXTERN(int) git_transport_smart( git_remote *owner, /* (git_smart_subtransport_definition *) */ void *payload); +/** + * Call the certificate check for this transport. + * + * @param transport a smart transport + * @param cert the certificate to pass to the caller + * @param valid whether we believe the certificate is valid + * @param hostname the hostname we connected to + * @return the return value of the callback + */ +GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname); + +/** + * Call the credentials callback for this transport + * + * @param out the pointer where the creds are to be stored + * @param transport a smart transport + * @param user the user we saw on the url (if any) + * @param methods available methods for authentication + * @return the return value of the callback + */ +GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods); + /* *** End of base transport interface *** *** Begin interface for subtransports for the smart transport *** diff --git a/include/git2/transport.h b/include/git2/transport.h index fd55eac0a..0ec241699 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -307,6 +307,17 @@ GIT_EXTERN(int) git_cred_ssh_key_memory_new( const char *privatekey, const char *passphrase); + +/** + * Free a credential. + * + * This is only necessary if you own the object; that is, if you are a + * transport. + * + * @param cred the object to free + */ +GIT_EXTERN(void) git_cred_free(git_cred *cred); + /** * Signature of a function which acquires a credential object. * diff --git a/src/config.c b/src/config.c index 77cf573e6..f0b2c3a61 100644 --- a/src/config.c +++ b/src/config.c @@ -13,6 +13,7 @@ #include "vector.h" #include "buf_text.h" #include "config_file.h" +#include "transaction.h" #if GIT_WIN32 # include <windows.h> #endif @@ -1144,6 +1145,41 @@ int git_config_open_default(git_config **out) return error; } +int git_config_lock(git_transaction **out, git_config *cfg) +{ + int error; + git_config_backend *file; + file_internal *internal; + + internal = git_vector_get(&cfg->files, 0); + if (!internal || !internal->file) { + giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); + return -1; + } + file = internal->file; + + if ((error = file->lock(file)) < 0) + return error; + + return git_transaction_config_new(out, cfg); +} + +int git_config_unlock(git_config *cfg, int commit) +{ + git_config_backend *file; + file_internal *internal; + + internal = git_vector_get(&cfg->files, 0); + if (!internal || !internal->file) { + giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files"); + return -1; + } + + file = internal->file; + + return file->unlock(file, commit); +} + /*********** * Parsers ***********/ diff --git a/src/config.h b/src/config.h index f257cc90f..ba745331a 100644 --- a/src/config.h +++ b/src/config.h @@ -88,4 +88,19 @@ extern int git_config__cvar( */ int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, const git_cvar_map *maps, size_t map_n, int enum_val); + +/** + * Unlock the backend with the highest priority + * + * Unlocking will allow other writers to updat the configuration + * file. Optionally, any changes performed since the lock will be + * applied to the configuration. + * + * @param cfg the configuration + * @param commit boolean which indicates whether to commit any changes + * done since locking + * @return 0 or an error code + */ +GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); + #endif diff --git a/src/config_file.c b/src/config_file.c index 52a5376bd..a3fec1b34 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -105,6 +105,10 @@ typedef struct { git_array_t(struct reader) readers; + bool locked; + git_filebuf locked_buf; + git_buf locked_content; + char *file_path; } diskfile_backend; @@ -685,6 +689,42 @@ static int config_snapshot(git_config_backend **out, git_config_backend *in) return git_config_file__snapshot(out, b); } +static int config_lock(git_config_backend *_cfg) +{ + diskfile_backend *cfg = (diskfile_backend *) _cfg; + int error; + + if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) + return error; + + error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path); + if (error < 0 && error != GIT_ENOTFOUND) { + git_filebuf_cleanup(&cfg->locked_buf); + return error; + } + + cfg->locked = true; + return 0; + +} + +static int config_unlock(git_config_backend *_cfg, int success) +{ + diskfile_backend *cfg = (diskfile_backend *) _cfg; + int error = 0; + + if (success) { + git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size); + error = git_filebuf_commit(&cfg->locked_buf); + } + + git_filebuf_cleanup(&cfg->locked_buf); + git_buf_free(&cfg->locked_content); + cfg->locked = false; + + return error; +} + int git_config_file__ondisk(git_config_backend **out, const char *path) { diskfile_backend *backend; @@ -706,6 +746,8 @@ int git_config_file__ondisk(git_config_backend **out, const char *path) backend->header.parent.del_multivar = config_delete_multivar; backend->header.parent.iterator = config_iterator_new; backend->header.parent.snapshot = config_snapshot; + backend->header.parent.lock = config_lock; + backend->header.parent.unlock = config_unlock; backend->header.parent.free = backend_free; *out = (git_config_backend *)backend; @@ -750,6 +792,21 @@ static int config_delete_readonly(git_config_backend *cfg, const char *name) return config_error_readonly(); } +static int config_lock_readonly(git_config_backend *_cfg) +{ + GIT_UNUSED(_cfg); + + return config_error_readonly(); +} + +static int config_unlock_readonly(git_config_backend *_cfg, int success) +{ + GIT_UNUSED(_cfg); + GIT_UNUSED(success); + + return config_error_readonly(); +} + static void backend_readonly_free(git_config_backend *_backend) { diskfile_backend *backend = (diskfile_backend *)_backend; @@ -803,6 +860,8 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in) 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.lock = config_lock_readonly; + backend->header.parent.unlock = config_unlock_readonly; backend->header.parent.free = backend_readonly_free; *out = (git_config_backend *)backend; @@ -1602,7 +1661,7 @@ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct re return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data); } -static int write_section(git_filebuf *file, const char *key) +static int write_section(git_buf *fbuf, const char *key) { int result; const char *dot; @@ -1626,7 +1685,7 @@ static int write_section(git_filebuf *file, const char *key) if (git_buf_oom(&buf)) return -1; - result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size); + result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size); git_buf_free(&buf); return result; @@ -1651,7 +1710,7 @@ static const char *quotes_for_value(const char *value) } struct write_data { - git_filebuf *file; + git_buf *buf; unsigned int in_section : 1, preg_replaced : 1; const char *section; @@ -1662,10 +1721,10 @@ struct write_data { static int write_line(struct write_data *write_data, const char *line, size_t line_len) { - int result = git_filebuf_write(write_data->file, line, line_len); + int result = git_buf_put(write_data->buf, line, line_len); if (!result && line_len && line[line_len-1] != '\n') - result = git_filebuf_printf(write_data->file, "\n"); + result = git_buf_printf(write_data->buf, "\n"); return result; } @@ -1676,7 +1735,7 @@ static int write_value(struct write_data *write_data) int result; q = quotes_for_value(write_data->value); - result = git_filebuf_printf(write_data->file, + result = git_buf_printf(write_data->buf, "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q); /* If we are updating a single name/value, we're done. Setting `value` @@ -1782,7 +1841,7 @@ static int write_on_eof(struct reader **reader, void *data) * value. */ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) { - if ((result = write_section(write_data->file, write_data->section)) == 0) + if ((result = write_section(write_data->buf, write_data->section)) == 0) result = write_value(write_data); } @@ -1797,18 +1856,23 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p int result; char *section, *name, *ldot; git_filebuf file = GIT_FILEBUF_INIT; + git_buf buf = GIT_BUF_INIT; struct reader *reader = git_array_get(cfg->readers, 0); struct write_data write_data; - /* Lock the file */ - if ((result = git_filebuf_open( - &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { + if (cfg->locked) { + result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content)); + } else { + /* Lock the file */ + if ((result = git_filebuf_open( + &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) { git_buf_free(&reader->buffer); return result; - } + } - /* We need to read in our own config file */ - result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + /* We need to read in our own config file */ + result = git_futils_readbuffer(&reader->buffer, cfg->file_path); + } /* Initialise the reading position */ if (result == GIT_ENOTFOUND) { @@ -1827,7 +1891,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p name = ldot + 1; section = git__strndup(key, ldot - key); - write_data.file = &file; + write_data.buf = &buf; write_data.section = section; write_data.in_section = 0; write_data.preg_replaced = 0; @@ -1843,13 +1907,22 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p goto done; } - /* refresh stats - if this errors, then commit will error too */ - (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); + if (cfg->locked) { + size_t len = buf.asize; + /* Update our copy with the modified contents */ + git_buf_free(&cfg->locked_content); + git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len); + } else { + git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); - result = git_filebuf_commit(&file); - git_buf_free(&reader->buffer); + /* refresh stats - if this errors, then commit will error too */ + (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file); + + result = git_filebuf_commit(&file); + } done: + git_buf_free(&buf); git_buf_free(&reader->buffer); return result; } diff --git a/src/config_file.h b/src/config_file.h index 0d8bf740f..1c52892c3 100644 --- a/src/config_file.h +++ b/src/config_file.h @@ -55,6 +55,16 @@ GIT_INLINE(int) git_config_file_foreach_match( return git_config_backend_foreach_match(cfg, regexp, fn, data); } +GIT_INLINE(int) git_config_file_lock(git_config_backend *cfg) +{ + return cfg->lock(cfg); +} + +GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success) +{ + return cfg->unlock(cfg, success); +} + extern int git_config_file_normalize_section(char *start, char *end); #endif diff --git a/src/curl_stream.c b/src/curl_stream.c index 63421fcf7..798bd5a52 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -220,6 +220,7 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port) curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(handle, CURLOPT_CERTINFO, 1); curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */ diff --git a/src/diff_driver.c b/src/diff_driver.c index 9d1337103..bc3518991 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -97,8 +97,7 @@ static int diff_driver_add_patterns( for (scan = regex_str; scan; scan = end) { /* get pattern to fill in */ if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { - error = -1; - break; + return -1; } pat->flags = regex_flags; @@ -117,10 +116,9 @@ static int diff_driver_add_patterns( break; if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) != 0) { - /* if regex fails to compile, warn? fail? */ - error = giterr_set_regex(&pat->re, error); - regfree(&pat->re); - break; + /* + * TODO: issue a warning + */ } } @@ -128,7 +126,8 @@ static int diff_driver_add_patterns( (void)git_array_pop(drv->fn_patterns); /* release last item */ git_buf_free(&buf); - return error; + /* We want to ignore bad patterns, so return success regardless */ + return 0; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) diff --git a/src/path.c b/src/path.c index 8317aaaa7..9ce5d2978 100644 --- a/src/path.c +++ b/src/path.c @@ -10,7 +10,7 @@ #include "repository.h" #ifdef GIT_WIN32 #include "win32/posix.h" -#include "win32/buffer.h" +#include "win32/w32_buffer.h" #include "win32/w32_util.h" #include "win32/version.h" #else diff --git a/src/remote.c b/src/remote.c index d31e1b89e..7404bf49f 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1334,11 +1334,13 @@ static int update_tips_for_spec( for (; i < refs->length; ++i) { head = git_vector_get(refs, i); autotag = 0; + git_buf_clear(&refname); /* Ignore malformed ref names (which also saves us from tag^{} */ if (!git_reference_is_valid_name(head->name)) continue; + /* If we have a tag, see if the auto-follow rules say to update it */ if (git_refspec_src_matches(&tagspec, head->name)) { if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { @@ -1348,10 +1350,11 @@ static int update_tips_for_spec( git_buf_clear(&refname); if (git_buf_puts(&refname, head->name) < 0) goto on_error; - } else { - continue; } - } else if (git_refspec_src_matches(spec, head->name)) { + } + + /* If we didn't want to auto-follow the tag, check if the refspec matches */ + if (!autotag && git_refspec_src_matches(spec, head->name)) { if (spec->dst) { if (git_refspec_transform(&refname, spec, head->name) < 0) goto on_error; @@ -1365,7 +1368,10 @@ static int update_tips_for_spec( continue; } - } else { + } + + /* If we still don't have a refname, we don't want it */ + if (git_buf_len(&refname) == 0) { continue; } diff --git a/src/transaction.c b/src/transaction.c index e8331891c..e9639bf97 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -12,6 +12,7 @@ #include "pool.h" #include "reflog.h" #include "signature.h" +#include "config.h" #include "git2/transaction.h" #include "git2/signature.h" @@ -20,6 +21,12 @@ GIT__USE_STRMAP +typedef enum { + TRANSACTION_NONE, + TRANSACTION_REFS, + TRANSACTION_CONFIG, +} transaction_t; + typedef struct { const char *name; void *payload; @@ -39,13 +46,29 @@ typedef struct { } transaction_node; struct git_transaction { + transaction_t type; git_repository *repo; git_refdb *db; + git_config *cfg; git_strmap *locks; git_pool pool; }; +int git_transaction_config_new(git_transaction **out, git_config *cfg) +{ + git_transaction *tx; + assert(out && cfg); + + tx = git__calloc(1, sizeof(git_transaction)); + GITERR_CHECK_ALLOC(tx); + + tx->type = TRANSACTION_CONFIG; + tx->cfg = cfg; + *out = tx; + return 0; +} + int git_transaction_new(git_transaction **out, git_repository *repo) { int error; @@ -71,6 +94,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo) if ((error = git_repository_refdb(&tx->db, repo)) < 0) goto on_error; + tx->type = TRANSACTION_REFS; memcpy(&tx->pool, &pool, sizeof(git_pool)); tx->repo = repo; *out = tx; @@ -305,6 +329,14 @@ int git_transaction_commit(git_transaction *tx) assert(tx); + if (tx->type == TRANSACTION_CONFIG) { + error = git_config_unlock(tx->cfg, true); + git_config_free(tx->cfg); + tx->cfg = NULL; + + return error; + } + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { if (!git_strmap_has_data(tx->locks, pos)) continue; @@ -332,6 +364,16 @@ void git_transaction_free(git_transaction *tx) assert(tx); + if (tx->type == TRANSACTION_CONFIG) { + if (tx->cfg) { + git_config_unlock(tx->cfg, false); + git_config_free(tx->cfg); + } + + git__free(tx); + return; + } + /* start by unlocking the ones we've left hanging, if any */ for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { if (!git_strmap_has_data(tx->locks, pos)) diff --git a/src/transaction.h b/src/transaction.h new file mode 100644 index 000000000..780c06830 --- /dev/null +++ b/src/transaction.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_transaction_h__ +#define INCLUDE_transaction_h__ + +#include "common.h" + +int git_transaction_config_new(git_transaction **out, git_config *cfg); + +#endif diff --git a/src/transports/cred.c b/src/transports/cred.c index 044b2a262..49ede48bf 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -378,3 +378,11 @@ int git_cred_username_new(git_cred **cred, const char *username) *cred = (git_cred *) c; return 0; } + +void git_cred_free(git_cred *cred) +{ + if (!cred) + return; + + cred->free(cred); +} diff --git a/src/transports/http.c b/src/transports/http.c index e3d90de11..87f3ee816 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -36,6 +36,8 @@ static const char *post_verb = "POST"; #define PARSE_ERROR_GENERIC -1 #define PARSE_ERROR_REPLAY -2 +/** Look at the user field */ +#define PARSE_ERROR_EXT -3 #define CHUNK_SIZE 4096 @@ -78,6 +80,7 @@ typedef struct { git_vector www_authenticate; enum last_cb last_cb; int parse_error; + int error; unsigned parse_finished : 1; /* Authentication */ @@ -351,7 +354,8 @@ static int on_headers_complete(http_parser *parser) if (error == GIT_PASSTHROUGH) { no_callback = 1; } else if (error < 0) { - return PARSE_ERROR_GENERIC; + t->error = error; + return t->parse_error = PARSE_ERROR_EXT; } else { assert(t->cred); @@ -712,6 +716,10 @@ replay: goto replay; } + if (t->parse_error == PARSE_ERROR_EXT) { + return t->error; + } + if (t->parse_error < 0) return -1; diff --git a/src/transports/smart.c b/src/transports/smart.c index 85a49e543..31a2dec7b 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -372,6 +372,20 @@ static int ref_name_cmp(const void *a, const void *b) return strcmp(ref_a->head.name, ref_b->head.name); } +int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname) +{ + transport_smart *t = (transport_smart *)transport; + + return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload); +} + +int git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods) +{ + transport_smart *t = (transport_smart *)transport; + + return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload); +} + int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 0920f2eef..1d46d4bc9 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -957,7 +957,7 @@ int git_smart__push(git_transport *transport, git_push *push, const git_remote_c packbuilder_payload.pb = push->pb; - if (cbs && cbs->transfer_progress) { + if (cbs && cbs->push_transfer_progress) { packbuilder_payload.cb = cbs->push_transfer_progress; packbuilder_payload.cb_payload = cbs->payload; } diff --git a/src/util.c b/src/util.c index b08b2b884..b3929bca2 100644 --- a/src/util.c +++ b/src/util.c @@ -11,7 +11,7 @@ #include "posix.h" #ifdef GIT_WIN32 -# include "win32/buffer.h" +# include "win32/w32_buffer.h" #endif #ifdef _MSC_VER diff --git a/src/win32/buffer.c b/src/win32/w32_buffer.c index 74950189e..9122baaa6 100644 --- a/src/win32/buffer.c +++ b/src/win32/w32_buffer.c @@ -6,7 +6,7 @@ */ #include "common.h" -#include "buffer.h" +#include "w32_buffer.h" #include "../buffer.h" #include "utf-conv.h" @@ -52,4 +52,3 @@ int git_buf_put_w(git_buf *buf, const wchar_t *string_w, size_t len_w) buf->ptr[buf->size] = '\0'; return 0; } - diff --git a/src/win32/buffer.h b/src/win32/w32_buffer.h index 62243986f..62243986f 100644 --- a/src/win32/buffer.h +++ b/src/win32/w32_buffer.h diff --git a/tests/config/write.c b/tests/config/write.c index 2e7b8182a..3d9b1a16a 100644 --- a/tests/config/write.c +++ b/tests/config/write.c @@ -1,6 +1,9 @@ #include "clar_libgit2.h" #include "buffer.h" #include "fileops.h" +#include "git2/sys/config.h" +#include "config_file.h" +#include "config.h" void test_config_write__initialize(void) { @@ -630,3 +633,50 @@ void test_config_write__to_file_with_only_comment(void) git_buf_free(&result); } +void test_config_write__locking(void) +{ + git_config *cfg, *cfg2; + git_config_entry *entry; + git_transaction *tx; + const char *filename = "locked-file"; + + /* Open the config and lock it */ + cl_git_mkfile(filename, "[section]\n\tname = value\n"); + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_pass(git_config_lock(&tx, cfg)); + + /* Change entries in the locked backend */ + cl_git_pass(git_config_set_string(cfg, "section.name", "other value")); + cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value")); + + /* We can see that the file we read from hasn't changed */ + cl_git_pass(git_config_open_ondisk(&cfg2, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg2, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg2, "section2.name3")); + git_config_free(cfg2); + + /* And we also get the old view when we read from the locked config */ + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); + cl_assert_equal_s("value", entry->value); + git_config_entry_free(entry); + cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3")); + + cl_git_pass(git_transaction_commit(tx)); + git_transaction_free(tx); + + /* Now that we've unlocked it, we should see both updates */ + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); + cl_assert_equal_s("other value", entry->value); + git_config_entry_free(entry); + cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3")); + cl_assert_equal_s("more value", entry->value); + git_config_entry_free(entry); + + git_config_free(cfg); +} diff --git a/tests/diff/drivers.c b/tests/diff/drivers.c index e3a0014db..42af38a9a 100644 --- a/tests/diff/drivers.c +++ b/tests/diff/drivers.c @@ -250,3 +250,29 @@ void test_diff_drivers__builtins(void) git_buf_free(&expected); git_vector_free(&files); } + +void test_diff_drivers__invalid_pattern(void) +{ + git_config *cfg; + git_index *idx; + git_diff *diff; + git_patch *patch; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + + g_repo = cl_git_sandbox_init("userdiff"); + cl_git_mkfile("userdiff/.gitattributes", "*.storyboard diff=storyboard\n"); + + cl_git_pass(git_repository_config__weakptr(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "diff.storyboard.xfuncname", "<!--(.*?)-->")); + + cl_git_mkfile("userdiff/dummy.storyboard", ""); + cl_git_pass(git_repository_index__weakptr(&idx, g_repo)); + cl_git_pass(git_index_add_bypath(idx, "dummy.storyboard")); + cl_git_mkfile("userdiff/dummy.storyboard", "some content\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + + git_patch_free(patch); + git_diff_free(diff); +} diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c index 06ee3dd36..17c8f26e3 100644 --- a/tests/network/fetchlocal.c +++ b/tests/network/fetchlocal.c @@ -369,17 +369,46 @@ void test_network_fetchlocal__clone_into_mirror(void) { git_clone_options opts = GIT_CLONE_OPTIONS_INIT; git_repository *repo; - git_reference *head; + git_reference *ref; 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)); + cl_git_pass(git_reference_lookup(&ref, repo, "HEAD")); + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(ref)); + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(ref)); + + git_reference_free(ref); + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master")); + + git_reference_free(ref); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +} - git_reference_free(head); +void test_network_fetchlocal__all_refs(void) +{ + git_repository *repo; + git_remote *remote; + git_reference *ref; + char *allrefs = "+refs/*:refs/*"; + git_strarray refspecs = { + &allrefs, + 1, + }; + + cl_git_pass(git_repository_init(&repo, "./foo.git", true)); + cl_git_pass(git_remote_create_anonymous(&remote, repo, cl_git_fixture_url("testrepo.git"))); + cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master")); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/test")); + git_reference_free(ref); + + git_remote_free(remote); git_repository_free(repo); cl_fixture_cleanup("./foo.git"); } |