summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rw-r--r--CMakeLists.txt16
-rw-r--r--include/git2/config.h18
-rw-r--r--include/git2/sys/config.h14
-rw-r--r--include/git2/sys/transport.h22
-rw-r--r--include/git2/transport.h11
-rw-r--r--src/config.c36
-rw-r--r--src/config.h15
-rw-r--r--src/config_file.c109
-rw-r--r--src/config_file.h10
-rw-r--r--src/curl_stream.c1
-rw-r--r--src/diff_driver.c13
-rw-r--r--src/path.c2
-rw-r--r--src/remote.c14
-rw-r--r--src/transaction.c42
-rw-r--r--src/transaction.h14
-rw-r--r--src/transports/cred.c8
-rw-r--r--src/transports/http.c10
-rw-r--r--src/transports/smart.c14
-rw-r--r--src/transports/smart_protocol.c2
-rw-r--r--src/util.c2
-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.c50
-rw-r--r--tests/diff/drivers.c26
-rw-r--r--tests/network/fetchlocal.c39
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");
}