summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-08-27 15:09:07 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-08-27 15:09:07 +0200
commit6a0d2b43ee4a32b2894d3c2b7c798c4eb4853534 (patch)
treea7fedcd3bffe2d021e31491b4f1f020c1bea8f88 /src
parent8733993599d9d9a8fe1b042a1e18d6ecb9b0a0b2 (diff)
parentcb92467bc284b87c38cdf32f8803a528846d094b (diff)
downloadlibgit2-6a0d2b43ee4a32b2894d3c2b7c798c4eb4853534.tar.gz
Merge remote-tracking branch 'upstream/master' into cmn/ssh-retry
Diffstat (limited to 'src')
-rw-r--r--src/array.h2
-rw-r--r--src/attr_file.c15
-rw-r--r--src/buf_text.c14
-rw-r--r--src/buf_text.h5
-rw-r--r--src/buffer.c77
-rw-r--r--src/buffer.h6
-rw-r--r--src/cache.c18
-rw-r--r--src/cache.h2
-rw-r--r--src/checkout.c141
-rw-r--r--src/cherrypick.c85
-rw-r--r--src/clone.c101
-rw-r--r--src/config_file.c8
-rw-r--r--src/crlf.c3
-rw-r--r--src/diff.c21
-rw-r--r--src/diff_file.c1
-rw-r--r--src/diff_print.c20
-rw-r--r--src/diff_tform.c4
-rw-r--r--src/errors.c16
-rw-r--r--src/filebuf.c2
-rw-r--r--src/fileops.c8
-rw-r--r--src/global.c75
-rw-r--r--src/global.h6
-rw-r--r--src/indexer.c16
-rw-r--r--src/merge.c188
-rw-r--r--src/merge.h2
-rw-r--r--src/mwindow.c125
-rw-r--r--src/mwindow.h10
-rw-r--r--src/netops.c28
-rw-r--r--src/netops.h1
-rw-r--r--src/odb_pack.c9
-rw-r--r--src/oid.c8
-rw-r--r--src/oid.h11
-rw-r--r--src/pack.c25
-rw-r--r--src/pack.h3
-rw-r--r--src/path.c71
-rw-r--r--src/path.h10
-rw-r--r--src/pool.c2
-rw-r--r--src/posix.h74
-rw-r--r--src/refs.h4
-rw-r--r--src/refspec.c72
-rw-r--r--src/remote.c33
-rw-r--r--src/remote.h3
-rw-r--r--src/repository.c6
-rw-r--r--src/revert.c10
-rw-r--r--src/revwalk.c3
-rw-r--r--src/status.c13
-rw-r--r--src/strnlen.h3
-rw-r--r--src/thread-utils.h7
-rw-r--r--src/trace.h10
-rw-r--r--src/transport.c153
-rw-r--r--src/transports/auth.c71
-rw-r--r--src/transports/auth.h63
-rw-r--r--src/transports/auth_negotiate.c275
-rw-r--r--src/transports/auth_negotiate.h27
-rw-r--r--src/transports/git.c1
-rw-r--r--src/transports/http.c246
-rw-r--r--src/transports/smart.h1
-rw-r--r--src/transports/smart_protocol.c4
-rw-r--r--src/transports/ssh.c75
-rw-r--r--src/transports/winhttp.c10
-rw-r--r--src/tree.c117
-rw-r--r--src/tree.h5
-rw-r--r--src/unix/posix.h32
-rw-r--r--src/unix/realpath.c15
-rw-r--r--src/util.c3
-rw-r--r--src/util.h6
-rw-r--r--src/win32/mingw-compat.h17
-rw-r--r--src/win32/msvc-compat.h35
-rw-r--r--src/win32/posix.h51
-rw-r--r--src/win32/posix_w32.c67
70 files changed, 1895 insertions, 756 deletions
diff --git a/src/array.h b/src/array.h
index f8a48722a..af9eafa43 100644
--- a/src/array.h
+++ b/src/array.h
@@ -44,7 +44,7 @@ typedef git_array_t(char) git_array_generic_t;
/* use a generic array for growth so this can return the new item */
GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
{
- git_array_generic_t *a = _a;
+ volatile git_array_generic_t *a = _a;
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
char *new_array = git__realloc(a->ptr, new_size * item_size);
if (!new_array) {
diff --git a/src/attr_file.c b/src/attr_file.c
index 3e95a2134..07ffacbaf 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -378,6 +378,18 @@ bool git_attr_fnmatch__match(
return (matchval != FNM_NOMATCH);
}
+ /* if path is a directory prefix of a negated pattern, then match */
+ if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
+ size_t pathlen = strlen(path->path);
+ bool prefixed = (pathlen <= match->length) &&
+ ((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
+ !strncasecmp(match->pattern, path->path, pathlen) :
+ !strncmp(match->pattern, path->path, pathlen));
+
+ if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
+ return true;
+ }
+
return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
}
@@ -522,7 +534,8 @@ int git_attr_fnmatch__parse(
}
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
+ spec->flags = spec->flags |
+ GIT_ATTR_FNMATCH_NEGATIVE | GIT_ATTR_FNMATCH_LEADINGDIR;
pattern++;
}
diff --git a/src/buf_text.c b/src/buf_text.c
index 631feb3f8..8d2b141b2 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -123,9 +123,13 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
size_t copylen = next - scan;
- /* don't convert existing \r\n to \r\r\n */
- size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
- size_t needsize = tgt->size + copylen + extralen + 1;
+ size_t needsize = tgt->size + copylen + 2 + 1;
+
+ /* if we find mixed line endings, bail */
+ if (next > start && next[-1] == '\r') {
+ git_buf_free(tgt);
+ return GIT_PASSTHROUGH;
+ }
if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
return -1;
@@ -134,8 +138,8 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
memcpy(tgt->ptr + tgt->size, scan, copylen);
tgt->size += copylen;
}
- if (extralen == 2)
- tgt->ptr[tgt->size++] = '\r';
+
+ tgt->ptr[tgt->size++] = '\r';
tgt->ptr[tgt->size++] = '\n';
}
diff --git a/src/buf_text.h b/src/buf_text.h
index 3ac9d1443..e753a0244 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,9 +56,10 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf);
/**
- * Replace all \r\n with \n. Does not modify \r without trailing \n.
+ * Replace all \r\n with \n.
*
- * @return 0 on success, -1 on memory error
+ * @return 0 on success, -1 on memory error, GIT_PASSTHROUGH if the
+ * source buffer has mixed line endings.
*/
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
diff --git a/src/buffer.c b/src/buffer.c
index b8f8660ed..e9c420e16 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -7,6 +7,7 @@
#include "buffer.h"
#include "posix.h"
#include "git2/buffer.h"
+#include "buf_text.h"
#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
@@ -141,6 +142,16 @@ int git_buf_set(git_buf *buf, const void *data, size_t len)
return 0;
}
+int git_buf_is_binary(const git_buf *buf)
+{
+ return git_buf_text_is_binary(buf);
+}
+
+int git_buf_contains_nul(const git_buf *buf)
+{
+ return git_buf_text_contains_nul(buf);
+}
+
int git_buf_sets(git_buf *buf, const char *string)
{
return git_buf_set(buf, string, string ? strlen(string) : 0);
@@ -178,10 +189,10 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
-static const char b64str[] =
+static const char base64_encode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
+int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
{
size_t extra = len % 3;
uint8_t *write, a, b, c;
@@ -196,19 +207,19 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
b = *read++;
c = *read++;
- *write++ = b64str[a >> 2];
- *write++ = b64str[(a & 0x03) << 4 | b >> 4];
- *write++ = b64str[(b & 0x0f) << 2 | c >> 6];
- *write++ = b64str[c & 0x3f];
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
+ *write++ = base64_encode[c & 0x3f];
}
if (extra > 0) {
a = *read++;
b = (extra > 1) ? *read++ : 0;
- *write++ = b64str[a >> 2];
- *write++ = b64str[(a & 0x03) << 4 | b >> 4];
- *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
*write++ = '=';
}
@@ -218,10 +229,56 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
+/* The inverse of base64_encode, offset by '+' == 43. */
+static const int8_t base64_decode[] = {
+ 62,
+ -1, -1, -1,
+ 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ -1, -1, -1, 0, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ -1, -1, -1, -1, -1, -1,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
+
+int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
+{
+ size_t i;
+ int8_t a, b, c, d;
+ size_t orig_size = buf->size;
+
+ assert(len % 4 == 0);
+ ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
+
+ for (i = 0; i < len; i += 4) {
+ if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
+ (b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
+ (c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
+ (d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
+ buf->size = orig_size;
+ buf->ptr[buf->size] = '\0';
+
+ giterr_set(GITERR_INVALID, "Invalid base64 input");
+ return -1;
+ }
+
+ buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
+ buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
+ buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
+ }
+
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
static const char b85str[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
-int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
+int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
diff --git a/src/buffer.h b/src/buffer.h
index 70d6d73b3..8ee4b532c 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -156,10 +156,12 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */
-int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
+int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
+/* Decode the given bas64 and write the result to the buffer */
+int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
/* Write data as "base85" encoded in buffer */
-int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
+int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
/*
* Insert, remove or replace a portion of the buffer.
diff --git a/src/cache.c b/src/cache.c
index 36ce66570..8dc9cbf9c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -68,8 +68,8 @@ int git_cache_init(git_cache *cache)
{
memset(cache, 0, sizeof(*cache));
cache->map = git_oidmap_alloc();
- if (git_mutex_init(&cache->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize cache mutex");
+ if (git_rwlock_init(&cache->lock)) {
+ giterr_set(GITERR_OS, "Failed to initialize cache rwlock");
return -1;
}
return 0;
@@ -94,19 +94,19 @@ static void clear_cache(git_cache *cache)
void git_cache_clear(git_cache *cache)
{
- if (git_mutex_lock(&cache->lock) < 0)
+ if (git_rwlock_wrlock(&cache->lock) < 0)
return;
clear_cache(cache);
- git_mutex_unlock(&cache->lock);
+ git_rwlock_wrunlock(&cache->lock);
}
void git_cache_free(git_cache *cache)
{
git_cache_clear(cache);
git_oidmap_free(cache->map);
- git_mutex_free(&cache->lock);
+ git_rwlock_free(&cache->lock);
git__memzero(cache, sizeof(*cache));
}
@@ -152,7 +152,7 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
khiter_t pos;
git_cached_obj *entry = NULL;
- if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0)
+ if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
return NULL;
pos = kh_get(oid, cache->map, oid);
@@ -166,7 +166,7 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
}
}
- git_mutex_unlock(&cache->lock);
+ git_rwlock_rdunlock(&cache->lock);
return entry;
}
@@ -185,7 +185,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
if (!cache_should_store(entry->type, entry->size))
return entry;
- if (git_mutex_lock(&cache->lock) < 0)
+ if (git_rwlock_wrlock(&cache->lock) < 0)
return entry;
/* soften the load on the cache */
@@ -227,7 +227,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
}
}
- git_mutex_unlock(&cache->lock);
+ git_rwlock_wrunlock(&cache->lock);
return entry;
}
diff --git a/src/cache.h b/src/cache.h
index 53fbcf4e9..697123739 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -30,7 +30,7 @@ typedef struct {
typedef struct {
git_oidmap *map;
- git_mutex lock;
+ git_rwlock lock;
ssize_t used_memory;
} git_cache;
diff --git a/src/checkout.c b/src/checkout.c
index 20763fd35..f25a6eff0 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -46,6 +46,7 @@ enum {
typedef struct {
git_repository *repo;
+ git_iterator *target;
git_diff *diff;
git_checkout_options opts;
bool opts_free_baseline;
@@ -54,6 +55,8 @@ typedef struct {
git_pool pool;
git_vector removes;
git_vector conflicts;
+ git_vector *reuc;
+ git_vector *names;
git_buf path;
size_t workdir_len;
git_buf tmp;
@@ -116,6 +119,7 @@ static int checkout_notify(
case GIT_DELTA_ADDED:
case GIT_DELTA_IGNORED:
case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
target = &delta->new_file;
break;
case GIT_DELTA_DELETED:
@@ -138,6 +142,7 @@ static int checkout_notify(
static bool checkout_is_workdir_modified(
checkout_data *data,
const git_diff_file *baseitem,
+ const git_diff_file *newitem,
const git_index_entry *wditem)
{
git_oid oid;
@@ -169,13 +174,16 @@ static bool checkout_is_workdir_modified(
/* Look at the cache to decide if the workdir is modified. If not,
* we can simply compare the oid in the cache to the baseitem instead
- * of hashing the file.
+ * of hashing the file. If so, we allow the checkout to proceed if the
+ * oid is identical (ie, the staged item is what we're trying to check
+ * out.)
*/
if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
if (wditem->mtime.seconds == ie->mtime.seconds &&
wditem->mtime.nanoseconds == ie->mtime.nanoseconds &&
wditem->file_size == ie->file_size)
- return (git_oid__cmp(&baseitem->id, &ie->id) != 0);
+ return (git_oid__cmp(&baseitem->id, &ie->id) != 0 &&
+ git_oid_cmp(&newitem->id, &ie->id) != 0);
}
/* depending on where base is coming from, we may or may not know
@@ -401,7 +409,7 @@ static int checkout_action_with_wd(
switch (delta->status) {
case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd)) {
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
GITERR_CHECK_ERROR(
checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
@@ -414,13 +422,13 @@ static int checkout_action_with_wd(
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
break;
case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
break;
case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
@@ -443,7 +451,7 @@ static int checkout_action_with_wd(
} else
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
}
- else if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+ else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
*action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
else
*action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
@@ -788,11 +796,16 @@ done:
static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
{
git_index_conflict_iterator *iterator = NULL;
+ git_index *index;
const git_index_entry *ancestor, *ours, *theirs;
checkout_conflictdata *conflict;
int error = 0;
- if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
+ /* Only write conficts from sources that have them: indexes. */
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
+ if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0)
goto done;
data->conflicts._cmp = checkout_conflictdata_cmp;
@@ -819,6 +832,10 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g
git_vector_insert(&data->conflicts, conflict);
}
+ /* Collect the REUC and NAME entries */
+ data->reuc = &index->reuc;
+ data->names = &index->names;
+
if (error == GIT_ITEROVER)
error = 0;
@@ -957,16 +974,20 @@ done:
static int checkout_conflicts_coalesce_renames(
checkout_data *data)
{
+ git_index *index;
const git_index_name_entry *name_entry;
checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
size_t i, names;
int error = 0;
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
/* Juggle entries based on renames */
- names = git_index_name_entrycount(data->index);
+ names = git_index_name_entrycount(index);
for (i = 0; i < names; i++) {
- name_entry = git_index_name_get_byindex(data->index, i);
+ name_entry = git_index_name_get_byindex(index, i);
if ((error = checkout_conflicts_load_byname_entry(
&ancestor_conflict, &our_conflict, &their_conflict,
@@ -1010,13 +1031,17 @@ done:
static int checkout_conflicts_mark_directoryfile(
checkout_data *data)
{
+ git_index *index;
checkout_conflictdata *conflict;
const git_index_entry *entry;
size_t i, j, len;
const char *path;
int prefixed, error = 0;
- len = git_index_entrycount(data->index);
+ if ((index = git_iterator_get_index(data->target)) == NULL)
+ return 0;
+
+ len = git_index_entrycount(index);
/* Find d/f conflicts */
git_vector_foreach(&data->conflicts, i, conflict) {
@@ -1027,7 +1052,7 @@ static int checkout_conflicts_mark_directoryfile(
path = conflict->ours ?
conflict->ours->path : conflict->theirs->path;
- if ((error = git_index_find(&j, data->index, path)) < 0) {
+ if ((error = git_index_find(&j, index, path)) < 0) {
if (error == GIT_ENOTFOUND)
giterr_set(GITERR_INDEX,
"Index inconsistency, could not find entry for expected conflict '%s'", path);
@@ -1036,7 +1061,7 @@ static int checkout_conflicts_mark_directoryfile(
}
for (; j < len; j++) {
- if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
+ if ((entry = git_index_get_byindex(index, j)) == NULL) {
giterr_set(GITERR_INDEX,
"Index inconsistency, truncated index while loading expected conflict '%s'", path);
error = -1;
@@ -1802,6 +1827,24 @@ done:
return error;
}
+static int checkout_conflict_update_index(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if (conflict->ancestor)
+ error = git_index_add(data->index, conflict->ancestor);
+
+ if (!error && conflict->ours)
+ error = git_index_add(data->index, conflict->ours);
+
+ if (!error && conflict->theirs)
+ error = git_index_add(data->index, conflict->theirs);
+
+ return error;
+}
+
static int checkout_create_conflicts(checkout_data *data)
{
checkout_conflictdata *conflict;
@@ -1864,6 +1907,12 @@ static int checkout_create_conflicts(checkout_data *data)
else if (!error)
error = checkout_write_merge(data, conflict);
+ /* Update the index extensions (REUC and NAME) if we're checking
+ * out a different index. (Otherwise just leave them there.)
+ */
+ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ error = checkout_conflict_update_index(data, conflict);
+
if (error)
break;
@@ -1876,6 +1925,37 @@ static int checkout_create_conflicts(checkout_data *data)
return error;
}
+static int checkout_extensions_update_index(checkout_data *data)
+{
+ const git_index_reuc_entry *reuc_entry;
+ const git_index_name_entry *name_entry;
+ size_t i;
+ int error = 0;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ return 0;
+
+ if (data->reuc) {
+ git_vector_foreach(data->reuc, i, reuc_entry) {
+ if ((error = git_index_reuc_add(data->index, reuc_entry->path,
+ reuc_entry->mode[0], &reuc_entry->oid[0],
+ reuc_entry->mode[1], &reuc_entry->oid[1],
+ reuc_entry->mode[2], &reuc_entry->oid[2])) < 0)
+ goto done;
+ }
+ }
+
+ if (data->names) {
+ git_vector_foreach(data->names, i, name_entry) {
+ if ((error = git_index_name_add(data->index, name_entry->ancestor,
+ name_entry->ours, name_entry->theirs)) < 0)
+ goto done;
+ }
+ }
+
+done:
+ return error;
+}
static void checkout_data_clear(checkout_data *data)
{
@@ -1919,6 +1999,7 @@ static int checkout_data_init(
return error;
data->repo = repo;
+ data->target = target;
GITERR_CHECK_VERSION(
proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
@@ -1943,15 +2024,15 @@ static int checkout_data_init(
(error = git_config_refresh(cfg)) < 0)
goto cleanup;
- /* if we are checking out the index, don't reload,
- * otherwise get index and force reload
+ /* Get the repository index and reload it (unless we're checking
+ * out the index; then it has the changes we're trying to check
+ * out and those should not be overwritten.)
*/
- if ((data->index = git_iterator_get_index(target)) != NULL) {
- GIT_REFCOUNT_INC(data->index);
- } else {
- /* otherwise, grab and reload the index */
- if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
- (error = git_index_read(data->index, true)) < 0)
+ if ((error = git_repository_index(&data->index, data->repo)) < 0)
+ goto cleanup;
+
+ if (data->index != git_iterator_get_index(target)) {
+ if ((error = git_index_read(data->index, true)) < 0)
goto cleanup;
/* cannot checkout if unresolved conflicts exist */
@@ -1963,7 +2044,7 @@ static int checkout_data_init(
goto cleanup;
}
- /* clean conflict data when doing a tree or commit checkout */
+ /* clean conflict data in the current index */
git_index_name_clear(data->index);
git_index_reuc_clear(data->index);
}
@@ -2063,6 +2144,7 @@ int git_checkout_iterator(
diff_opts.flags =
GIT_DIFF_INCLUDE_UNMODIFIED |
+ GIT_DIFF_INCLUDE_UNREADABLE |
GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
GIT_DIFF_INCLUDE_IGNORED |
@@ -2132,6 +2214,10 @@ int git_checkout_iterator(
(error = checkout_create_conflicts(&data)) < 0)
goto cleanup;
+ if (data.index != git_iterator_get_index(target) &&
+ (error = checkout_extensions_update_index(&data)) < 0)
+ goto cleanup;
+
assert(data.completed_steps == data.total_steps);
cleanup:
@@ -2154,7 +2240,7 @@ int git_checkout_index(
git_index *index,
const git_checkout_options *opts)
{
- int error;
+ int error, owned = 0;
git_iterator *index_i;
if (!index && !repo) {
@@ -2162,10 +2248,16 @@ int git_checkout_index(
"Must provide either repository or index to checkout");
return -1;
}
- if (index && repo && git_index_owner(index) != repo) {
+
+ if (index && repo &&
+ git_index_owner(index) &&
+ git_index_owner(index) != repo) {
giterr_set(GITERR_CHECKOUT,
"Index to checkout does not match repository");
return -1;
+ } else if(index && repo && !git_index_owner(index)) {
+ GIT_REFCOUNT_OWN(index, repo);
+ owned = 1;
}
if (!repo)
@@ -2178,6 +2270,9 @@ int git_checkout_index(
if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
error = git_checkout_iterator(index_i, opts);
+ if (owned)
+ GIT_REFCOUNT_OWN(index, NULL);
+
git_iterator_free(index_i);
git_index_free(index);
diff --git a/src/cherrypick.c b/src/cherrypick.c
index e02348a03..e58d0ab4c 100644
--- a/src/cherrypick.c
+++ b/src/cherrypick.c
@@ -17,9 +17,9 @@
#include "git2/commit.h"
#include "git2/sys/commit.h"
-#define GIT_CHERRY_PICK_FILE_MODE 0666
+#define GIT_CHERRYPICK_FILE_MODE 0666
-static int write_cherry_pick_head(
+static int write_cherrypick_head(
git_repository *repo,
const char *commit_oidstr)
{
@@ -27,8 +27,8 @@ static int write_cherry_pick_head(
git_buf file_path = GIT_BUF_INIT;
int error = 0;
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRY_PICK_HEAD_FILE)) >= 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) >= 0 &&
+ if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 &&
(error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
error = git_filebuf_commit(&file);
@@ -49,7 +49,7 @@ static int write_merge_msg(
int error = 0;
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRY_PICK_FILE_MODE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 ||
(error = git_filebuf_printf(&file, "%s", commit_msg)) < 0)
goto cleanup;
@@ -64,10 +64,10 @@ cleanup:
return error;
}
-static int cherry_pick_normalize_opts(
+static int cherrypick_normalize_opts(
git_repository *repo,
- git_cherry_pick_options *opts,
- const git_cherry_pick_options *given,
+ git_cherrypick_options *opts,
+ const git_cherrypick_options *given,
const char *their_label)
{
int error = 0;
@@ -77,10 +77,10 @@ static int cherry_pick_normalize_opts(
GIT_UNUSED(repo);
if (given != NULL)
- memcpy(opts, given, sizeof(git_cherry_pick_options));
+ memcpy(opts, given, sizeof(git_cherrypick_options));
else {
- git_cherry_pick_options default_opts = GIT_CHERRY_PICK_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_cherry_pick_options));
+ git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_cherrypick_options));
}
if (!opts->checkout_opts.checkout_strategy)
@@ -95,14 +95,14 @@ static int cherry_pick_normalize_opts(
return error;
}
-static int cherry_pick_state_cleanup(git_repository *repo)
+static int cherrypick_state_cleanup(git_repository *repo)
{
- const char *state_files[] = { GIT_CHERRY_PICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
+ const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
-static int cherry_pick_seterr(git_commit *commit, const char *fmt)
+static int cherrypick_seterr(git_commit *commit, const char *fmt)
{
char commit_oidstr[GIT_OID_HEXSZ + 1];
@@ -112,71 +112,71 @@ static int cherry_pick_seterr(git_commit *commit, const char *fmt)
return -1;
}
-int git_cherry_pick_commit(
+int git_cherrypick_commit(
git_index **out,
git_repository *repo,
- git_commit *cherry_pick_commit,
+ git_commit *cherrypick_commit,
git_commit *our_commit,
unsigned int mainline,
const git_merge_options *merge_opts)
{
git_commit *parent_commit = NULL;
- git_tree *parent_tree = NULL, *our_tree = NULL, *cherry_pick_tree = NULL;
+ git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL;
int parent = 0, error = 0;
- assert(out && repo && cherry_pick_commit && our_commit);
+ assert(out && repo && cherrypick_commit && our_commit);
- if (git_commit_parentcount(cherry_pick_commit) > 1) {
+ if (git_commit_parentcount(cherrypick_commit) > 1) {
if (!mainline)
- return cherry_pick_seterr(cherry_pick_commit,
+ return cherrypick_seterr(cherrypick_commit,
"Mainline branch is not specified but %s is a merge commit");
parent = mainline;
} else {
if (mainline)
- return cherry_pick_seterr(cherry_pick_commit,
+ return cherrypick_seterr(cherrypick_commit,
"Mainline branch specified but %s is not a merge commit");
- parent = git_commit_parentcount(cherry_pick_commit);
+ parent = git_commit_parentcount(cherrypick_commit);
}
if (parent &&
- ((error = git_commit_parent(&parent_commit, cherry_pick_commit, (parent - 1))) < 0 ||
+ ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 ||
(error = git_commit_tree(&parent_tree, parent_commit)) < 0))
goto done;
- if ((error = git_commit_tree(&cherry_pick_tree, cherry_pick_commit)) < 0 ||
+ if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 ||
(error = git_commit_tree(&our_tree, our_commit)) < 0)
goto done;
- error = git_merge_trees(out, repo, parent_tree, our_tree, cherry_pick_tree, merge_opts);
+ error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts);
done:
git_tree_free(parent_tree);
git_tree_free(our_tree);
- git_tree_free(cherry_pick_tree);
+ git_tree_free(cherrypick_tree);
git_commit_free(parent_commit);
return error;
}
-int git_cherry_pick(
+int git_cherrypick(
git_repository *repo,
git_commit *commit,
- const git_cherry_pick_options *given_opts)
+ const git_cherrypick_options *given_opts)
{
- git_cherry_pick_options opts;
+ git_cherrypick_options opts;
git_reference *our_ref = NULL;
git_commit *our_commit = NULL;
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg, *commit_summary;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
int error = 0;
assert(repo && commit);
- GITERR_CHECK_VERSION(given_opts, GIT_CHERRY_PICK_OPTIONS_VERSION, "git_cherry_pick_options");
+ GITERR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options");
if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0)
return error;
@@ -191,25 +191,22 @@ int git_cherry_pick(
if ((error = write_merge_msg(repo, commit_msg)) < 0 ||
(error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 ||
- (error = cherry_pick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
- (error = write_cherry_pick_head(repo, commit_oidstr)) < 0 ||
+ (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
+ (error = write_cherrypick_head(repo, commit_oidstr)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_cherry_pick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ (error = git_cherrypick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
goto on_error;
-
goto done;
on_error:
- cherry_pick_state_cleanup(repo);
+ cherrypick_state_cleanup(repo);
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
@@ -217,10 +214,10 @@ done:
return error;
}
-int git_cherry_pick_init_options(
- git_cherry_pick_options *opts, unsigned int version)
+int git_cherrypick_init_options(
+ git_cherrypick_options *opts, unsigned int version)
{
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_cherry_pick_options, GIT_CHERRY_PICK_OPTIONS_INIT);
+ opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT);
return 0;
}
diff --git a/src/clone.c b/src/clone.c
index 6c4fb6727..7835be1b1 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -24,6 +24,8 @@
#include "repository.h"
#include "odb.h"
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature);
+
static int create_branch(
git_reference **branch,
git_repository *repo,
@@ -229,6 +231,29 @@ cleanup:
return retcode;
}
+static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return git_repository_init(out, path, bare);
+}
+
+static int default_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+ git_remote_callbacks *callbacks = payload;
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ return git_remote_set_callbacks(*out, callbacks);
+}
+
/*
* submodules?
*/
@@ -241,8 +266,9 @@ static int create_and_configure_origin(
{
int error;
git_remote *origin = NULL;
- const char *name;
char buf[GIT_PATH_MAX];
+ git_remote_create_cb remote_create = options->remote_cb;
+ void *payload = options->remote_cb_payload;
/* If the path exists and is a dir, the url should be the absolute path */
if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
@@ -252,14 +278,12 @@ static int create_and_configure_origin(
url = buf;
}
- name = options->remote_name ? options->remote_name : "origin";
- if ((error = git_remote_create(&origin, repo, name, url)) < 0)
- goto on_error;
-
- if (options->ignore_cert_errors)
- git_remote_check_cert(origin, 0);
+ if (!remote_create) {
+ remote_create = default_remote_create;
+ payload = (void *)&options->remote_callbacks;
+ }
- if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
+ if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
goto on_error;
if ((error = git_remote_save(origin)) < 0)
@@ -307,7 +331,7 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
return error;
}
-int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
+static int clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
int error;
git_buf reflog_message = GIT_BUF_INIT;
@@ -347,27 +371,30 @@ cleanup:
return error;
}
-int git_clone__should_clone_local(const char *url, git_clone_local_t local)
+int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
{
- const char *path;
- int is_url;
+ git_buf fromurl = GIT_BUF_INIT;
+ const char *path = url_or_path;
+ bool is_url, is_local;
if (local == GIT_CLONE_NO_LOCAL)
- return false;
-
- is_url = !git__prefixcmp(url, "file://");
+ return 0;
- if (is_url && local != GIT_CLONE_LOCAL && local != GIT_CLONE_LOCAL_NO_LINKS )
- return false;
+ if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) {
+ if (git_path_fromurl(&fromurl, url_or_path) < 0) {
+ is_local = -1;
+ goto done;
+ }
- path = url;
- if (is_url)
- path = url + strlen("file://");
+ path = fromurl.ptr;
+ }
- if ((git_path_exists(path) && git_path_isdir(path)) && local != GIT_CLONE_NO_LOCAL)
- return true;
+ is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
+ git_path_isdir(path);
- return false;
+done:
+ git_buf_free(&fromurl);
+ return is_local;
}
int git_clone(
@@ -381,6 +408,7 @@ int git_clone(
git_remote *origin;
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+ git_repository_create_cb repository_cb;
assert(out && url && local_path);
@@ -400,20 +428,28 @@ int git_clone(
if (git_path_exists(local_path))
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
- if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
+ if (options.repository_cb)
+ repository_cb = options.repository_cb;
+ else
+ repository_cb = default_repository_create;
+
+ if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
return error;
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
- if (git_clone__should_clone_local(url, options.local)) {
- int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
- error = git_clone_local_into(
+ int clone_local = git_clone__should_clone_local(url, options.local);
+ int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
+
+ if (clone_local == 1)
+ error = clone_local_into(
repo, origin, &options.checkout_opts,
options.checkout_branch, link, options.signature);
- } else {
- error = git_clone_into(
+ else if (clone_local == 0)
+ error = clone_into(
repo, origin, &options.checkout_opts,
options.checkout_branch, options.signature);
- }
+ else
+ error = -1;
git_remote_free(origin);
}
@@ -452,6 +488,9 @@ static const char *repository_base(git_repository *repo)
static bool can_link(const char *src, const char *dst, int link)
{
#ifdef GIT_WIN32
+ GIT_UNUSED(src);
+ GIT_UNUSED(dst);
+ GIT_UNUSED(link);
return false;
#else
@@ -470,7 +509,7 @@ static bool can_link(const char *src, const char *dst, int link)
#endif
}
-int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
{
int error, flags;
git_repository *src;
diff --git a/src/config_file.c b/src/config_file.c
index 56271144b..7106f18db 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1522,6 +1522,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
+ if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
+ git_filebuf_write(&file, "\n", 1);
+
/* And now if we just need to add a variable */
if (!section_matches && write_section(&file, section) < 0)
goto rewrite_fail;
@@ -1536,9 +1539,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
}
/* If we are here, there is at least a section line */
- if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
- git_filebuf_write(&file, "\n", 1);
-
q = quotes_for_value(value);
git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
@@ -1649,7 +1649,7 @@ static int is_multiline_var(const char *str)
}
/* An odd number means last backslash wasn't escaped, so it's multiline */
- return (end > str) && (count & 1);
+ return count & 1;
}
static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
diff --git a/src/crlf.c b/src/crlf.c
index 821e04eb2..93448760d 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -286,7 +286,8 @@ static int crlf_check(
if (error < 0)
return error;
- if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+ if (ca.crlf_action == GIT_CRLF_GUESS &&
+ ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
return GIT_PASSTHROUGH;
if (ca.auto_crlf == GIT_AUTO_CRLF_INPUT &&
diff --git a/src/diff.c b/src/diff.c
index 325c599e0..2691d7ca0 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -92,6 +92,10 @@ static int diff_delta__from_one(
if (status == GIT_DELTA_UNTRACKED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0;
+
+ if (status == GIT_DELTA_UNREADABLE &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
+ return 0;
if (!git_pathspec__match(
&diff->pathspec, entry->path,
@@ -196,6 +200,7 @@ static git_diff_delta *diff_delta__last_for_item(
if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
return delta;
break;
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_UNTRACKED:
if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
git_oid__cmp(&delta->new_file.id, &item->id) == 0)
@@ -293,6 +298,10 @@ bool git_diff_delta__should_skip(
(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
return true;
+ if (delta->status == GIT_DELTA_UNREADABLE &&
+ (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
+ return true;
+
return false;
}
@@ -734,6 +743,11 @@ static int maybe_modified(
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
status = GIT_DELTA_TYPECHANGE;
+ else if (nmode == GIT_FILEMODE_UNREADABLE) {
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
+ error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem);
+ return error;
+ }
else {
if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem)))
error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem);
@@ -954,6 +968,13 @@ static int handle_unmatched_new_item(
}
}
+ else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
+ delta_type = GIT_DELTA_UNTRACKED;
+ else
+ delta_type = GIT_DELTA_UNREADABLE;
+ }
+
/* Actually create the record for this item if necessary */
if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0)
return error;
diff --git a/src/diff_file.c b/src/diff_file.c
index f2a1d5099..96be0942b 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -112,6 +112,7 @@ int git_diff_file_content__init_from_diff(
has_data = !use_old &&
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
diff --git a/src/diff_print.c b/src/diff_print.c
index bb925ef98..43a90b3d8 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -82,14 +82,15 @@ char git_diff_status_char(git_delta_t status)
char code;
switch (status) {
- case GIT_DELTA_ADDED: code = 'A'; break;
- case GIT_DELTA_DELETED: code = 'D'; break;
- case GIT_DELTA_MODIFIED: code = 'M'; break;
- case GIT_DELTA_RENAMED: code = 'R'; break;
- case GIT_DELTA_COPIED: code = 'C'; break;
- case GIT_DELTA_IGNORED: code = 'I'; break;
- case GIT_DELTA_UNTRACKED: code = '?'; break;
- default: code = ' '; break;
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ case GIT_DELTA_UNREADABLE: code = 'X'; break;
+ default: code = ' '; break;
}
return code;
@@ -351,7 +352,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
else
git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
- git_buf_put_base85(pi->buf, scan, chunk_len);
+ git_buf_encode_base85(pi->buf, scan, chunk_len);
git_buf_putc(pi->buf, '\n');
if (git_buf_oom(pi->buf)) {
@@ -441,6 +442,7 @@ static int diff_print_patch_file(
if (S_ISDIR(delta->new_file.mode) ||
delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
(delta->status == GIT_DELTA_UNTRACKED &&
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
diff --git a/src/diff_tform.c b/src/diff_tform.c
index a2dab0ae2..423a0ca33 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -114,7 +114,7 @@ static git_diff_delta *diff_delta__merge_like_cgit_reversed(
if ((dup = diff_delta__dup(a, pool)) == NULL)
return NULL;
- if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
+ if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED || b->status == GIT_DELTA_UNREADABLE)
return dup;
if (dup->status == GIT_DELTA_DELETED) {
@@ -732,6 +732,7 @@ static bool is_rename_source(
switch (delta->status) {
case GIT_DELTA_ADDED:
case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
case GIT_DELTA_IGNORED:
return false;
@@ -786,6 +787,7 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
{
return (delta->status == GIT_DELTA_ADDED ||
delta->status == GIT_DELTA_UNTRACKED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
delta->status == GIT_DELTA_IGNORED);
}
diff --git a/src/errors.c b/src/errors.c
index 393a7875f..7a2600586 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -45,15 +45,19 @@ void giterr_set(int error_class, const char *string, ...)
#endif
int error_code = (error_class == GITERR_OS) ? errno : 0;
- va_start(arglist, string);
- git_buf_vprintf(&buf, string, arglist);
- va_end(arglist);
+ if (string) {
+ va_start(arglist, string);
+ git_buf_vprintf(&buf, string, arglist);
+ va_end(arglist);
+
+ if (error_class == GITERR_OS)
+ git_buf_PUTS(&buf, ": ");
+ }
if (error_class == GITERR_OS) {
#ifdef GIT_WIN32
char * win32_error = git_win32_get_error_message(win32_error_code);
if (win32_error) {
- git_buf_PUTS(&buf, ": ");
git_buf_puts(&buf, win32_error);
git__free(win32_error);
@@ -61,10 +65,8 @@ void giterr_set(int error_class, const char *string, ...)
}
else
#endif
- if (error_code) {
- git_buf_PUTS(&buf, ": ");
+ if (error_code)
git_buf_puts(&buf, strerror(error_code));
- }
if (error_code)
errno = 0;
diff --git a/src/filebuf.c b/src/filebuf.c
index d23bcc11c..25f6e52ef 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -334,8 +334,6 @@ int git_filebuf_commit(git_filebuf *file)
file->fd = -1;
- p_unlink(file->path_original);
-
if (p_rename(file->path_lock, file->path_original) < 0) {
giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
goto on_error;
diff --git a/src/fileops.c b/src/fileops.c
index bebbae4f9..34659ad3b 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -355,8 +355,9 @@ int git_futils_mkdir(
if (p_mkdir(make_path.ptr, mode) < 0) {
int tmp_errno = giterr_system_last();
- /* ignore error if directory already exists */
- if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ /* ignore error if not at end or if directory already exists */
+ if (lastch == '\0' &&
+ (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
giterr_system_set(tmp_errno);
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
goto done;
@@ -374,7 +375,8 @@ int git_futils_mkdir(
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
st.st_mode != mode &&
- (error = p_chmod(make_path.ptr, mode)) < 0) {
+ (error = p_chmod(make_path.ptr, mode)) < 0 &&
+ lastch == '\0') {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
goto done;
}
diff --git a/src/global.c b/src/global.c
index 1c4a7a1a9..f89c73256 100644
--- a/src/global.c
+++ b/src/global.c
@@ -16,6 +16,14 @@ git_mutex git__mwindow_mutex;
#define MAX_SHUTDOWN_CB 8
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+SSL_CTX *git__ssl_ctx;
+# ifdef GIT_THREADS
+static git_mutex *openssl_locks;
+# endif
+#endif
+
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
@@ -39,6 +47,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 +232,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 +293,13 @@ static git_global_st __state;
int git_threads_init(void)
{
+ static int ssl_inited = 0;
+
+ if (!ssl_inited) {
+ init_ssl();
+ ssl_inited = 1;
+ }
+
git_atomic_inc(&git__n_inits);
return 0;
}
diff --git a/src/global.h b/src/global.h
index 778250376..106504628 100644
--- a/src/global.h
+++ b/src/global.h
@@ -13,8 +13,14 @@
typedef struct {
git_error *last_error;
git_error error_t;
+ char oid_fmt[41];
} 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..010de329b 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,8 @@
#include "oidmap.h"
#include "zstream.h"
+extern git_mutex git__mwindow_mutex;
+
#define UINT31_MAX (0x7FFFFFFF)
struct entry {
@@ -433,6 +435,8 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t
git_map map;
int error;
+ assert(data && size);
+
/* the offset needs to be at the beginning of the a page boundary */
page_start = (offset / page_size) * page_size;
page_offset = offset - page_start;
@@ -451,9 +455,12 @@ static int append_to_pack(git_indexer *idx, const void *data, size_t size)
{
git_off_t current_size = idx->pack->mwf.size;
+ if (!size)
+ return 0;
+
/* add the extra space we need at the end */
if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) {
- giterr_system_set(errno);
+ giterr_set(GITERR_OS, "Failed to increase size of pack file '%s'", idx->pack->pack_name);
return -1;
}
@@ -1044,6 +1051,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/merge.c b/src/merge.c
index a279d31d4..add7eab64 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -228,8 +228,11 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
return -1;
git_vector_foreach(twos, i, two) {
- git_commit_list_parse(walk, two);
+ if (git_commit_list_parse(walk, two) < 0)
+ return -1;
+
two->flags |= PARENT2;
+
if (git_pqueue_insert(&list, two) < 0)
return -1;
}
@@ -2193,8 +2196,7 @@ static int merge_normalize_checkout_opts(
memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options));
else {
git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
- default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_ALLOW_CONFLICTS;
+ default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options));
}
@@ -2226,64 +2228,6 @@ static int merge_normalize_checkout_opts(
return error;
}
-static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new)
-{
- git_tree *head_tree = NULL;
- git_iterator *iter_head = NULL, *iter_new = NULL;
- git_diff *merged_list = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_delta *delta;
- size_t i;
- const git_index_entry *e;
- char *path;
- int error = 0;
-
- if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
- (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
- (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
- (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
- goto done;
-
- git_vector_foreach(&merged_list->deltas, i, delta) {
- path = git__strdup(delta->new_file.path);
- GITERR_CHECK_ALLOC(path);
-
- if ((error = git_vector_insert(paths, path)) < 0)
- goto on_error;
- }
-
- for (i = 0; i < git_index_entrycount(index_new); i++) {
- e = git_index_get_byindex(index_new, i);
-
- if (git_index_entry_stage(e) != 0 &&
- (git_vector_last(paths) == NULL ||
- strcmp(git_vector_last(paths), e->path) != 0)) {
-
- path = git__strdup(e->path);
- GITERR_CHECK_ALLOC(path);
-
- if ((error = git_vector_insert(paths, path)) < 0)
- goto on_error;
- }
- }
-
- goto done;
-
-on_error:
- git_vector_foreach(paths, i, path)
- git__free(path);
-
- git_vector_clear(paths);
-
-done:
- git_tree_free(head_tree);
- git_iterator_free(iter_head);
- git_iterator_free(iter_new);
- git_diff_free(merged_list);
-
- return error;
-}
-
static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
{
git_tree *head_tree = NULL;
@@ -2372,99 +2316,58 @@ done:
return error;
}
-int git_merge__indexes(git_repository *repo, git_index *index_new)
+int git_merge__check_result(git_repository *repo, git_index *index_new)
{
- git_index *index_repo = NULL;
- int index_repo_caps = 0;
+ git_tree *head_tree = NULL;
+ git_iterator *iter_head = NULL, *iter_new = NULL;
+ git_diff *merged_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_delta *delta;
git_vector paths = GIT_VECTOR_INIT;
- size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i;
- char *path;
+ size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts;
const git_index_entry *e;
- const git_index_name_entry *name;
- const git_index_reuc_entry *reuc;
int error = 0;
- if ((error = git_repository_index(&index_repo, repo)) < 0)
- goto done;
-
- /* Set the index to case sensitive to handle the merge */
- index_repo_caps = git_index_caps(index_repo);
-
- if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0)
- goto done;
-
- /* Make sure the index and workdir state do not prevent merging */
- if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 ||
- (error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
- (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
- goto done;
-
- if ((conflicts = index_conflicts + wd_conflicts) > 0) {
- giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
- conflicts, (conflicts != 1) ? "s" : "");
- error = GIT_EMERGECONFLICT;
-
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
goto done;
- }
-
- /* Remove removed items from the index */
- git_vector_foreach(&paths, i, path) {
- if (git_index_get_bypath(index_new, path, 0) == NULL) {
- if ((error = git_index_remove(index_repo, path, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto done;
- }
- }
- /* Add updated items to the index */
- git_vector_foreach(&paths, i, path) {
- if ((e = git_index_get_bypath(index_new, path, 0)) != NULL) {
- if ((error = git_index_add(index_repo, e)) < 0)
- goto done;
- }
+ git_vector_foreach(&merged_list->deltas, i, delta) {
+ if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0)
+ goto done;
}
- /* Add conflicts */
- git_index_conflict_cleanup(index_repo);
-
for (i = 0; i < git_index_entrycount(index_new); i++) {
e = git_index_get_byindex(index_new, i);
if (git_index_entry_stage(e) != 0 &&
- (error = git_index_add(index_repo, e)) < 0)
- goto done;
- }
-
- /* Add name entries */
- git_index_name_clear(index_repo);
-
- for (i = 0; i < git_index_name_entrycount(index_new); i++) {
- name = git_index_name_get_byindex(index_new, i);
+ (git_vector_last(&paths) == NULL ||
+ strcmp(git_vector_last(&paths), e->path) != 0)) {
- if ((error = git_index_name_add(index_repo,
- name->ancestor, name->ours, name->theirs)) < 0)
- goto done;
+ if ((error = git_vector_insert(&paths, (char *)e->path)) < 0)
+ goto done;
+ }
}
- /* Add the reuc */
- git_index_reuc_clear(index_repo);
-
- for (i = 0; i < git_index_reuc_entrycount(index_new); i++) {
- reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i);
+ /* Make sure the index and workdir state do not prevent merging */
+ if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
+ (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
+ goto done;
- if ((error = git_index_reuc_add(index_repo, reuc->path,
- reuc->mode[0], &reuc->oid[0],
- reuc->mode[1], &reuc->oid[1],
- reuc->mode[2], &reuc->oid[2])) < 0)
- goto done;
+ if ((conflicts = index_conflicts + wd_conflicts) > 0) {
+ giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+ conflicts, (conflicts != 1) ? "s" : "");
+ error = GIT_EMERGECONFLICT;
}
done:
- if (index_repo != NULL)
- git_index_set_caps(index_repo, index_repo_caps);
-
- git_index_free(index_repo);
- git_vector_free_deep(&paths);
+ git_vector_free(&paths);
+ git_tree_free(head_tree);
+ git_iterator_free(iter_head);
+ git_iterator_free(iter_new);
+ git_diff_free(merged_list);
return error;
}
@@ -2479,12 +2382,14 @@ int git_merge__append_conflicts_to_merge_msg(
size_t i;
int error;
+ if (!git_index_has_conflicts(index))
+ return 0;
+
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
goto cleanup;
- if (git_index_has_conflicts(index))
- git_filebuf_printf(&file, "\nConflicts:\n");
+ git_filebuf_printf(&file, "\nConflicts:\n");
for (i = 0; i < git_index_entrycount(index); i++) {
const git_index_entry *e = git_index_get_byindex(index, i);
@@ -2509,7 +2414,6 @@ cleanup:
return error;
}
-
static int merge_state_cleanup(git_repository *repo)
{
const char *state_files[] = {
@@ -2657,7 +2561,7 @@ int git_merge(
git_checkout_options checkout_opts;
git_merge_head *ancestor_head = NULL, *our_head = NULL;
git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
size_t i;
int error = 0;
@@ -2697,10 +2601,9 @@ int git_merge(
/* TODO: recursive, octopus, etc... */
if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &checkout_opts)) < 0)
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &checkout_opts)) < 0)
goto on_error;
goto done;
@@ -2710,7 +2613,6 @@ on_error:
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_tree_free(ancestor_tree);
git_tree_free(our_tree);
diff --git a/src/merge.h b/src/merge.h
index 00f6197bf..cc17389ab 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -149,7 +149,7 @@ int git_merge__setup(
const git_merge_head *heads[],
size_t heads_len);
-int git_merge__indexes(git_repository *repo, git_index *index_new);
+int git_merge__check_result(git_repository *repo, git_index *index_new);
int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index);
diff --git a/src/mwindow.c b/src/mwindow.c
index 7e5fcdfbc..1d64d26a4 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -11,6 +11,10 @@
#include "fileops.h"
#include "map.h"
#include "global.h"
+#include "strmap.h"
+#include "pack.h"
+
+GIT__USE_STRMAP;
#define DEFAULT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
@@ -26,20 +30,127 @@ size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
/* Whenever you want to read or modify this, grab git__mwindow_mutex */
static git_mwindow_ctl mem_ctl;
-/*
- * Free all the windows in a sequence, typically because we're done
- * with the file
+/* Global list of mwindow files, to open packs once across repos */
+git_strmap *git__pack_cache = NULL;
+
+/**
+ * Run under mwindow lock
*/
-void git_mwindow_free_all(git_mwindow_file *mwf)
+int git_mwindow_files_init(void)
{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t i;
+ if (git__pack_cache)
+ return 0;
+
+ return git_strmap_alloc(&git__pack_cache);
+}
+
+void git_mwindow_files_free(void)
+{
+ git_strmap *tmp = git__pack_cache;
+
+ git__pack_cache = NULL;
+ git_strmap_free(tmp);
+}
+
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
+{
+ int error;
+ char *packname;
+ git_strmap_iter pos;
+ struct git_pack_file *pack;
+
+ if ((error = git_packfile__name(&packname, path)) < 0)
+ return error;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0) {
+ giterr_set(GITERR_OS, "failed to lock mwindow mutex");
+ return -1;
+ }
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ git__free(packname);
+ return -1;
+ }
+
+ pos = git_strmap_lookup_index(git__pack_cache, packname);
+ git__free(packname);
+
+ if (git_strmap_valid_index(git__pack_cache, pos)) {
+ pack = git_strmap_value_at(git__pack_cache, pos);
+ git_atomic_inc(&pack->refcount);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ *out = pack;
+ return 0;
+ }
+
+ /* If we didn't find it, we need to create it */
+ if ((error = git_packfile_alloc(&pack, path)) < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return error;
+ }
+
+ git_atomic_inc(&pack->refcount);
+
+ git_strmap_insert(git__pack_cache, pack->pack_name, pack, error);
+ git_mutex_unlock(&git__mwindow_mutex);
+
+ if (error < 0) {
+ git_packfile_free(pack);
+ return -1;
+ }
+
+ *out = pack;
+ return 0;
+}
+
+void git_mwindow_put_pack(struct git_pack_file *pack)
+{
+ int count;
+ git_strmap_iter pos;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0)
+ return;
+
+ /* put before get would be a corrupted state */
+ assert(git__pack_cache);
+
+ pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name);
+ /* if we cannot find it, the state is corrupted */
+ assert(git_strmap_valid_index(git__pack_cache, pos));
+
+ count = git_atomic_dec(&pack->refcount);
+ if (count == 0) {
+ git_strmap_delete_at(git__pack_cache, pos);
+ git_packfile_free(pack);
+ }
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ return;
+}
+void git_mwindow_free_all(git_mwindow_file *mwf)
+{
if (git_mutex_lock(&git__mwindow_mutex)) {
giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
return;
}
+ git_mwindow_free_all_locked(mwf);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+}
+
+/*
+ * Free all the windows in a sequence, typically because we're done
+ * with the file
+ */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf)
+{
+ git_mwindow_ctl *ctl = &mem_ctl;
+ size_t i;
+
/*
* Remove these windows from the global list
*/
@@ -67,8 +178,6 @@ void git_mwindow_free_all(git_mwindow_file *mwf)
mwf->windows = w->next;
git__free(w);
}
-
- git_mutex_unlock(&git__mwindow_mutex);
}
/*
diff --git a/src/mwindow.h b/src/mwindow.h
index 0018ebbf0..63418e458 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -36,10 +36,18 @@ typedef struct git_mwindow_ctl {
} git_mwindow_ctl;
int git_mwindow_contains(git_mwindow *win, git_off_t offset);
-void git_mwindow_free_all(git_mwindow_file *mwf);
+void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
int git_mwindow_file_register(git_mwindow_file *mwf);
void git_mwindow_file_deregister(git_mwindow_file *mwf);
void git_mwindow_close(git_mwindow **w_cursor);
+int git_mwindow_files_init(void);
+void git_mwindow_files_free(void);
+
+struct git_pack_file; /* just declaration to avoid cyclical includes */
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
+void git_mwindow_put_pack(struct git_pack_file *pack);
+
#endif
diff --git a/src/netops.c b/src/netops.c
index a0193a022..fceb4fb74 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -13,6 +13,7 @@
# include <netinet/in.h>
# include <arpa/inet.h>
#else
+# include <winsock2.h>
# include <ws2tcpip.h>
# ifdef _MSC_VER
# pragma comment(lib, "ws2_32")
@@ -33,6 +34,7 @@
#include "posix.h"
#include "buffer.h"
#include "http_parser.h"
+#include "global.h"
#ifdef GIT_WIN32
static void net_set_error(const char *str)
@@ -157,7 +159,7 @@ void gitno_buffer_setup_callback(
void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
{
#ifdef GIT_SSL
- if (socket->ssl.ctx) {
+ if (socket->ssl.ssl) {
gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
return;
}
@@ -202,7 +204,6 @@ static int gitno_ssl_teardown(gitno_ssl *ssl)
ret = 0;
SSL_free(ssl->ssl);
- SSL_CTX_free(ssl->ctx);
return ret;
}
@@ -390,18 +391,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 +533,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 +554,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
@@ -723,6 +718,9 @@ int gitno_extract_url_parts(
if (u.field_set & (1 << UF_PATH)) {
*path = git__substrdup(_path, u.field_data[UF_PATH].len);
GITERR_CHECK_ALLOC(*path);
+ } else {
+ giterr_set(GITERR_NET, "invalid url, missing path");
+ return GIT_EINVALIDSPEC;
}
if (u.field_set & (1 << UF_USERINFO)) {
diff --git a/src/netops.h b/src/netops.h
index 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/oid.c b/src/oid.c
index b640cadd1..969931d04 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -8,6 +8,7 @@
#include "common.h"
#include "git2/oid.h"
#include "repository.h"
+#include "global.h"
#include <string.h>
#include <limits.h>
@@ -99,6 +100,13 @@ void git_oid_pathfmt(char *str, const git_oid *oid)
str = fmt_one(str, oid->id[i]);
}
+char *git_oid_tostr_s(const git_oid *oid)
+{
+ char *str = GIT_GLOBAL->oid_fmt;
+ git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
+ return str;
+}
+
char *git_oid_allocfmt(const git_oid *oid)
{
char *str = git__malloc(GIT_OID_HEXSZ + 1);
diff --git a/src/oid.h b/src/oid.h
index cfe7ca1b2..aa1f0bfdc 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -9,6 +9,17 @@
#include "git2/oid.h"
+/**
+ * Format a git_oid into a newly allocated c-string.
+ *
+ * The c-string is owned by the caller and needs to be manually freed.
+ *
+ * @param id the oid structure to format
+ * @return the c-string; NULL if memory is exhausted. Caller must
+ * deallocate the string with git__free().
+ */
+char *git_oid_allocfmt(const git_oid *id);
+
GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
int i;
diff --git a/src/pack.c b/src/pack.c
index ace7abb58..b05aa91f1 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -744,7 +744,7 @@ cleanup:
git__free(obj->data);
if (elem)
- *obj_offset = elem->offset;
+ *obj_offset = curpos;
git_array_clear(chain);
return error;
@@ -968,10 +968,10 @@ void git_packfile_free(struct git_pack_file *p)
cache_free(&p->bases);
- git_mwindow_free_all(&p->mwf);
-
- if (p->mwf.fd >= 0)
+ if (p->mwf.fd >= 0) {
+ git_mwindow_free_all_locked(&p->mwf);
p_close(p->mwf.fd);
+ }
pack_index_free(p);
@@ -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/path.c b/src/path.c
index 5beab97ed..77f8d8858 100644
--- a/src/path.c
+++ b/src/path.c
@@ -377,26 +377,33 @@ static int error_invalid_local_file_uri(const char *uri)
return -1;
}
-int git_path_fromurl(git_buf *local_path_out, const char *file_url)
+static int local_file_url_prefixlen(const char *file_url)
{
- int offset = 0, len;
+ int len = -1;
- assert(local_path_out && file_url);
+ if (git__prefixcmp(file_url, "file://") == 0) {
+ if (file_url[7] == '/')
+ len = 8;
+ else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
+ len = 17;
+ }
- if (git__prefixcmp(file_url, "file://") != 0)
- return error_invalid_local_file_uri(file_url);
+ return len;
+}
+
+bool git_path_is_local_file_url(const char *file_url)
+{
+ return (local_file_url_prefixlen(file_url) > 0);
+}
- offset += 7;
- len = (int)strlen(file_url);
+int git_path_fromurl(git_buf *local_path_out, const char *file_url)
+{
+ int offset;
- if (offset < len && file_url[offset] == '/')
- offset++;
- else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)
- offset += 10;
- else
- return error_invalid_local_file_uri(file_url);
+ assert(local_path_out && file_url);
- if (offset >= len || file_url[offset] == '/')
+ if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
+ file_url[offset] == '\0' || file_url[offset] == '/')
return error_invalid_local_file_uri(file_url);
#ifndef GIT_WIN32
@@ -404,7 +411,6 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url)
#endif
git_buf_clear(local_path_out);
-
return git__percent_decode(local_path_out, file_url + offset);
}
@@ -1104,20 +1110,29 @@ int git_path_dirload_with_stat(
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
(error = git_path_lstat(full.ptr, &ps->st)) < 0) {
+
if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
+ /* file was removed between readdir and lstat */
git_vector_remove(contents, i--);
- continue;
+ } else {
+ /* Treat the file as unreadable if we get any other error */
+ memset(&ps->st, 0, sizeof(ps->st));
+ ps->st.st_mode = GIT_FILEMODE_UNREADABLE;
}
- break;
+ giterr_clear();
+ error = 0;
+ continue;
}
if (S_ISDIR(ps->st.st_mode)) {
ps->path[ps->path_len++] = '/';
ps->path[ps->path_len] = '\0';
}
+ else if (!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) {
+ /* skip everything but dirs, plain files, and symlinks */
+ git_vector_remove(contents, i--);
+ }
}
/* sort now that directory suffix is added */
@@ -1130,18 +1145,8 @@ int git_path_dirload_with_stat(
int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
{
- int error;
-
- /* If url_or_path begins with file:// treat it as a URL */
- if (!git__prefixcmp(url_or_path, "file://")) {
- if ((error = git_path_fromurl(local_path_out, url_or_path)) < 0) {
- return error;
- }
- } else { /* We assume url_or_path is already a path */
- if ((error = git_buf_sets(local_path_out, url_or_path)) < 0) {
- return error;
- }
- }
-
- return 0;
+ if (git_path_is_local_file_url(url_or_path))
+ return git_path_fromurl(local_path_out, url_or_path);
+ else
+ return git_buf_sets(local_path_out, url_or_path);
}
diff --git a/src/path.h b/src/path.h
index 3e6efe3de..46d6efe93 100644
--- a/src/path.h
+++ b/src/path.h
@@ -8,6 +8,7 @@
#define INCLUDE_path_h__
#include "common.h"
+#include "posix.h"
#include "buffer.h"
#include "vector.h"
@@ -127,6 +128,14 @@ GIT_INLINE(int) git_path_is_relative(const char *p)
return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
}
+/**
+ * Check if string is at end of path segment (i.e. looking at '/' or '\0')
+ */
+GIT_INLINE(int) git_path_at_end_of_segment(const char *p)
+{
+ return !*p || *p == '/';
+}
+
extern int git__percent_decode(git_buf *decoded_out, const char *input);
/**
@@ -439,6 +448,7 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
extern bool git_path_does_fs_decompose_unicode(const char *root);
/* Used for paths to repositories on the filesystem */
+extern bool git_path_is_local_file_url(const char *file_url);
extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
#endif
diff --git a/src/pool.c b/src/pool.c
index 146f118b4..a516ff9eb 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -146,7 +146,7 @@ GIT_INLINE(void) pool_remove_page(
void *git_pool_malloc(git_pool *pool, uint32_t items)
{
git_pool_page *scan = pool->open, *prev;
- uint32_t size = items * pool->item_size;
+ uint32_t size = ((items * pool->item_size) + 7) & ~7;
void *ptr = NULL;
pool->has_string_alloc = 0;
diff --git a/src/posix.h b/src/posix.h
index 965cd98d5..9ef348739 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -12,23 +12,61 @@
#include <time.h>
#include "fnmatch.h"
+/* stat: file mode type testing macros */
#ifndef S_IFGITLINK
#define S_IFGITLINK 0160000
#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
#endif
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#undef _S_IFLNK
+#define _S_IFLNK S_IFLNK
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 00100
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
+#ifndef S_ISFIFO
+#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+#endif
+
/* if S_ISGID is not defined, then don't try to set it */
#ifndef S_ISGID
#define S_ISGID 0
#endif
-#if !defined(O_BINARY)
+#ifndef O_BINARY
#define O_BINARY 0
#endif
-#if !defined(O_CLOEXEC)
+#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
+/* access() mode parameter #defines */
+#ifndef F_OK
+#define F_OK 0 /* existence check */
+#endif
+#ifndef W_OK
+#define W_OK 2 /* write mode check */
+#endif
+#ifndef R_OK
+#define R_OK 4 /* read mode check */
+#endif
+
/* Determine whether an errno value indicates that a read or write failed
* because the descriptor is blocked.
*/
@@ -38,6 +76,12 @@
#define GIT_ISBLOCKED(e) ((e) == EAGAIN)
#endif
+/* define some standard errnos that the runtime may be missing. for example,
+ * mingw lacks EAFNOSUPPORT. */
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT (INT_MAX-1)
+#endif
+
typedef int git_file;
/**
@@ -56,8 +100,6 @@ typedef int git_file;
extern int p_read(git_file fd, void *buf, size_t cnt);
extern int p_write(git_file fd, const void *buf, size_t cnt);
-#define p_fstat(f,b) fstat(f, b)
-#define p_lseek(f,n,w) lseek(f, n, w)
#define p_close(fd) close(fd)
#define p_umask(m) umask(m)
@@ -66,30 +108,6 @@ extern int p_creat(const char *path, mode_t mode);
extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
-#ifndef GIT_WIN32
-
-#define p_stat(p,b) stat(p, b)
-#define p_chdir(p) chdir(p)
-#define p_rmdir(p) rmdir(p)
-#define p_chmod(p,m) chmod(p, m)
-#define p_access(p,m) access(p,m)
-#define p_ftruncate(fd, sz) ftruncate(fd, sz)
-#define p_recv(s,b,l,f) recv(s,b,l,f)
-#define p_send(s,b,l,f) send(s,b,l,f)
-typedef int GIT_SOCKET;
-#define INVALID_SOCKET -1
-
-#define p_localtime_r localtime_r
-#define p_gmtime_r gmtime_r
-
-#else
-
-typedef SOCKET GIT_SOCKET;
-extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
-extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
-
-#endif
-
/**
* Platform-dependent methods
*/
diff --git a/src/refs.h b/src/refs.h
index f75a4bf7e..2e79bdfca 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -35,7 +35,7 @@
#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
-#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD"
+#define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD"
#define GIT_BISECT_LOG_FILE "BISECT_LOG"
#define GIT_REBASE_MERGE_DIR "rebase-merge/"
#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
@@ -63,7 +63,7 @@ struct git_reference {
} target;
git_oid peel;
- char name[0];
+ char name[GIT_FLEX_ARRAY];
};
git_reference *git_reference__set_name(git_reference *ref, const char *name);
diff --git a/src/refspec.c b/src/refspec.c
index fa60aa7aa..9f0df35a7 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -181,39 +181,75 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
static int refspec_transform(
git_buf *out, const char *from, const char *to, const char *name)
{
- size_t to_len = to ? strlen(to) : 0;
- size_t from_len = from ? strlen(from) : 0;
- size_t name_len = name ? strlen(name) : 0;
+ const char *from_star, *to_star;
+ const char *name_slash, *from_slash;
+ size_t replacement_len, star_offset;
git_buf_sanitize(out);
+ git_buf_clear(out);
- if (git_buf_set(out, to, to_len) < 0)
- return -1;
+ /*
+ * There are two parts to each side of a refspec, the bit
+ * before the star and the bit after it. The star can be in
+ * the middle of the pattern, so we need to look at each bit
+ * individually.
+ */
+ from_star = strchr(from, '*');
+ to_star = strchr(to, '*');
- if (to_len > 0) {
- /* No '*' at the end of 'to' means that refspec is mapped to one
- * specific branch, so no actual transformation is needed.
- */
- if (out->ptr[to_len - 1] != '*')
- return 0;
- git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */
- }
+ assert(from_star && to_star);
+
+ /* star offset, both in 'from' and in 'name' */
+ star_offset = from_star - from;
- if (from_len > 0) /* ignore trailing '*' from 'from' */
- from_len--;
- if (from_len > name_len)
- from_len = name_len;
+ /* the first half is copied over */
+ git_buf_put(out, to, to_star - to);
- return git_buf_put(out, name + from_len, name_len - from_len);
+ /* then we copy over the replacement, from the star's offset to the next slash in 'name' */
+ name_slash = strchr(name + star_offset, '/');
+ if (!name_slash)
+ name_slash = strrchr(name, '\0');
+
+ /* if there is no slash after the star in 'from', we want to copy everything over */
+ from_slash = strchr(from + star_offset, '/');
+ if (!from_slash)
+ name_slash = strrchr(name, '\0');
+
+ replacement_len = (name_slash - name) - star_offset;
+ git_buf_put(out, name + star_offset, replacement_len);
+
+ return git_buf_puts(out, to_star + 1);
}
int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name)
{
+ assert(out && spec && name);
+ git_buf_sanitize(out);
+
+ if (!git_refspec_src_matches(spec, name)) {
+ giterr_set(GITERR_INVALID, "ref '%s' doesn't match the source", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_buf_puts(out, spec->dst);
+
return refspec_transform(out, spec->src, spec->dst, name);
}
int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name)
{
+ assert(out && spec && name);
+ git_buf_sanitize(out);
+
+ if (!git_refspec_dst_matches(spec, name)) {
+ giterr_set(GITERR_INVALID, "ref '%s' doesn't match the destination", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_buf_puts(out, spec->src);
+
return refspec_transform(out, spec->dst, spec->src, name);
}
diff --git a/src/remote.c b/src/remote.c
index 47b61b1b1..433015f60 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -267,9 +267,11 @@ int git_remote_dup(git_remote **dest, git_remote *source)
if (source->pushurl != NULL) {
remote->pushurl = git__strdup(source->pushurl);
- GITERR_CHECK_ALLOC(remote->pushurl);
+ GITERR_CHECK_ALLOC(remote->pushurl);
}
+ remote->transport_cb = source->transport_cb;
+ remote->transport_cb_payload = source->transport_cb_payload;
remote->repo = source->repo;
remote->download_tags = source->download_tags;
remote->check_cert = source->check_cert;
@@ -659,8 +661,14 @@ int git_remote_connect(git_remote *remote, git_direction direction)
return -1;
}
- /* A transport could have been supplied in advance with
- * git_remote_set_transport */
+ /* If we don't have a transport object yet, and the caller specified a
+ * custom transport factory, use that */
+ if (!t && remote->transport_cb &&
+ (error = remote->transport_cb(&t, remote, remote->transport_cb_payload)) < 0)
+ return error;
+
+ /* If we still don't have a transport, then use the global
+ * transport registrations which map URI schemes to transport factories */
if (!t && (error = git_transport_new(&t, remote, url)) < 0)
return error;
@@ -691,6 +699,11 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote
{
assert(remote);
+ if (!remote->transport) {
+ giterr_set(GITERR_NET, "No transport bound to this remote");
+ return -1;
+ }
+
return remote->transport->ls(out, size, remote->transport);
}
@@ -1262,18 +1275,20 @@ const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote)
return &remote->callbacks;
}
-int git_remote_set_transport(git_remote *remote, git_transport *transport)
+int git_remote_set_transport(
+ git_remote *remote,
+ git_transport_cb transport_cb,
+ void *payload)
{
- assert(remote && transport);
-
- GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+ assert(remote);
if (remote->transport) {
giterr_set(GITERR_NET, "A transport is already bound to this remote");
return -1;
}
- remote->transport = transport;
+ remote->transport_cb = transport_cb;
+ remote->transport_cb_payload = payload;
return 0;
}
@@ -1932,6 +1947,8 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
size_t heads_len, i;
int error;
+ assert(out);
+
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
return error;
diff --git a/src/remote.h b/src/remote.h
index 4164a14b3..c471756b8 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -9,6 +9,7 @@
#include "git2/remote.h"
#include "git2/transport.h"
+#include "git2/sys/transport.h"
#include "refspec.h"
#include "vector.h"
@@ -22,6 +23,8 @@ struct git_remote {
git_vector refs;
git_vector refspecs;
git_vector active_refspecs;
+ git_transport_cb transport_cb;
+ void *transport_cb_payload;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
diff --git a/src/repository.c b/src/repository.c
index e8d50aed3..4f7a5feab 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1915,8 +1915,8 @@ int git_repository_state(git_repository *repo)
state = GIT_REPOSITORY_STATE_MERGE;
else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE))
state = GIT_REPOSITORY_STATE_REVERT;
- else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE))
- state = GIT_REPOSITORY_STATE_CHERRY_PICK;
+ else if(git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_CHERRYPICK;
else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
state = GIT_REPOSITORY_STATE_BISECT;
@@ -1958,7 +1958,7 @@ static const char *state_files[] = {
GIT_MERGE_MODE_FILE,
GIT_MERGE_MSG_FILE,
GIT_REVERT_HEAD_FILE,
- GIT_CHERRY_PICK_HEAD_FILE,
+ GIT_CHERRYPICK_HEAD_FILE,
GIT_BISECT_LOG_FILE,
GIT_REBASE_MERGE_DIR,
GIT_REBASE_APPLY_DIR,
diff --git a/src/revert.c b/src/revert.c
index 9c587724b..36560a77c 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -174,7 +174,7 @@ int git_revert(
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL, *index_repo = NULL;
+ git_index *index_new = NULL;
int error;
assert(repo && commit);
@@ -199,10 +199,9 @@ int git_revert(
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
(error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__indexes(repo, index_new)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ (error = git_merge__check_result(repo, index_new)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
+ (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
goto on_error;
goto done;
@@ -212,7 +211,6 @@ on_error:
done:
git_index_free(index_new);
- git_index_free(index_repo);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
diff --git a/src/revwalk.c b/src/revwalk.c
index 530c9705e..bd07d02cb 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -48,9 +48,6 @@ static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit)
assert(commit);
- git_array_init_to_size(pending, 2);
- GITERR_CHECK_ARRAY(pending);
-
do {
commit->uninteresting = 1;
diff --git a/src/status.c b/src/status.c
index 8d7612f72..cb2490042 100644
--- a/src/status.c
+++ b/src/status.c
@@ -62,6 +62,9 @@ static unsigned int workdir_delta2status(
case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW;
break;
+ case GIT_DELTA_UNREADABLE:
+ st = GIT_STATUS_WT_UNREADABLE;
+ break;
case GIT_DELTA_DELETED:
st = GIT_STATUS_WT_DELETED;
break;
@@ -310,6 +313,10 @@ int git_status_list_new(
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED;
if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
findopt.flags = findopt.flags |
@@ -329,8 +336,9 @@ int git_status_list_new(
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
if ((error = git_diff_index_to_workdir(
- &status->idx2wd, repo, index, &diffopt)) < 0)
+ &status->idx2wd, repo, index, &diffopt)) < 0) {
goto done;
+ }
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
@@ -407,8 +415,9 @@ int git_status_foreach_ext(
size_t i;
int error = 0;
- if ((error = git_status_list_new(&status, repo, opts)) < 0)
+ if ((error = git_status_list_new(&status, repo, opts)) < 0) {
return error;
+ }
git_vector_foreach(&status->paired, i, status_entry) {
const char *path = status_entry->head_to_index ?
diff --git a/src/strnlen.h b/src/strnlen.h
index fdd7fe39c..eecfe3c02 100644
--- a/src/strnlen.h
+++ b/src/strnlen.h
@@ -7,7 +7,8 @@
#ifndef INCLUDE_strlen_h__
#define INCLUDE_strlen_h__
-#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__)
+#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\
+ (defined(_MSC_VER) && _MSC_VER < 1500)
# define NO_STRNLEN
#endif
diff --git a/src/thread-utils.h b/src/thread-utils.h
index daec14eeb..5511a5117 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -53,12 +53,6 @@ typedef struct {
#endif
-#if defined(GIT_WIN32)
-#define git_thread_yield() Sleep(0)
-#else
-#define git_thread_yield() sched_yield()
-#endif
-
/* Pthreads Mutex */
#define git_mutex pthread_mutex_t
#define git_mutex_init(a) pthread_mutex_init(a, NULL)
@@ -186,7 +180,6 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#define git_thread unsigned int
#define git_thread_create(thread, attr, start_routine, arg) 0
#define git_thread_join(id, status) (void)0
-#define git_thread_yield() (void)0
/* Pthreads Mutex */
#define git_mutex unsigned int
diff --git a/src/trace.h b/src/trace.h
index 4d4e3bf53..486084d01 100644
--- a/src/trace.h
+++ b/src/trace.h
@@ -46,8 +46,16 @@ GIT_INLINE(void) git_trace__write_fmt(
#else
+GIT_INLINE(void) git_trace__null(
+ git_trace_level_t level,
+ const char *fmt, ...)
+{
+ GIT_UNUSED(level);
+ GIT_UNUSED(fmt);
+}
+
#define git_trace_level() ((void)0)
-#define git_trace(lvl, ...) ((void)0)
+#define git_trace git_trace__null
#endif
diff --git a/src/transport.c b/src/transport.c
index 2194b1864..c0311fa90 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -9,11 +9,11 @@
#include "git2/remote.h"
#include "git2/net.h"
#include "git2/transport.h"
+#include "git2/sys/transport.h"
#include "path.h"
typedef struct transport_definition {
char *prefix;
- unsigned priority;
git_transport_cb fn;
void *param;
} transport_definition;
@@ -24,51 +24,54 @@ static git_smart_subtransport_definition git_subtransport_definition = { git_sma
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 };
#endif
-static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
+static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
#ifdef GIT_SSH
-static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition };
+static transport_definition ssh_transport_definition = { "ssh://", git_transport_smart, &ssh_subtransport_definition };
#else
-static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
+static transport_definition dummy_transport_definition = { NULL, git_transport_dummy, NULL };
#endif
static transport_definition transports[] = {
- {"git://", 1, git_transport_smart, &git_subtransport_definition},
- {"http://", 1, git_transport_smart, &http_subtransport_definition},
- {"https://", 1, git_transport_smart, &http_subtransport_definition},
- {"file://", 1, git_transport_local, NULL},
+ { "git://", git_transport_smart, &git_subtransport_definition },
+ { "http://", git_transport_smart, &http_subtransport_definition },
+ { "https://", git_transport_smart, &http_subtransport_definition },
+ { "file://", git_transport_local, NULL },
#ifdef GIT_SSH
- {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition},
+ { "ssh://", git_transport_smart, &ssh_subtransport_definition },
#endif
- {NULL, 0, 0}
+ { NULL, 0, 0 }
};
-static git_vector additional_transports = GIT_VECTOR_INIT;
+static git_vector custom_transports = GIT_VECTOR_INIT;
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
-static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
+static int transport_find_fn(
+ git_transport_cb *out,
+ const char *url,
+ void **param)
{
size_t i = 0;
- unsigned priority = 0;
- transport_definition *definition = NULL, *definition_iter;
+ transport_definition *d, *definition = NULL;
- // First, check to see if it's an obvious URL, which a URL scheme
- for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
- definition_iter = &transports[i];
-
- if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
- continue;
-
- if (definition_iter->priority > priority)
- definition = definition_iter;
+ /* Find a user transport who wants to deal with this URI */
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ definition = d;
+ break;
+ }
}
- git_vector_foreach(&additional_transports, i, definition_iter) {
- if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
- continue;
+ /* Find a system transport for this URI */
+ if (!definition) {
+ for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
+ d = &transports[i];
- if (definition_iter->priority > priority)
- definition = definition_iter;
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ definition = d;
+ break;
+ }
+ }
}
#ifdef GIT_WIN32
@@ -90,7 +93,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
#ifdef GIT_SSH
definition = &ssh_transport_definition;
#else
- definition = &dummy_transport_definition;
+ definition = &dummy_transport_definition;
#endif
#ifndef GIT_WIN32
@@ -100,9 +103,9 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
#endif
if (!definition)
- return -1;
+ return GIT_ENOTFOUND;
- *callback = definition->fn;
+ *out = definition->fn;
*param = definition->param;
return 0;
@@ -128,74 +131,96 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
void *param;
int error;
- if (transport_find_fn(url, &fn, &param) < 0) {
+ if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
giterr_set(GITERR_NET, "Unsupported URL protocol");
return -1;
- }
+ } else if (error < 0)
+ return error;
- error = fn(&transport, owner, param);
- if (error < 0)
+ if ((error = fn(&transport, owner, param)) < 0)
return error;
+ GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+
*out = transport;
return 0;
}
int git_transport_register(
- const char *prefix,
- unsigned priority,
+ const char *scheme,
git_transport_cb cb,
void *param)
{
- transport_definition *d;
+ git_buf prefix = GIT_BUF_INIT;
+ transport_definition *d, *definition = NULL;
+ size_t i;
+ int error = 0;
- d = git__calloc(sizeof(transport_definition), 1);
- GITERR_CHECK_ALLOC(d);
+ assert(scheme);
+ assert(cb);
- d->prefix = git__strdup(prefix);
-
- if (!d->prefix)
+ if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
goto on_error;
- d->priority = priority;
- d->fn = cb;
- d->param = param;
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ error = GIT_EEXISTS;
+ goto on_error;
+ }
+ }
+
+ definition = git__calloc(1, sizeof(transport_definition));
+ GITERR_CHECK_ALLOC(definition);
+
+ definition->prefix = git_buf_detach(&prefix);
+ definition->fn = cb;
+ definition->param = param;
- if (git_vector_insert(&additional_transports, d) < 0)
+ if (git_vector_insert(&custom_transports, definition) < 0)
goto on_error;
return 0;
on_error:
- git__free(d->prefix);
- git__free(d);
- return -1;
+ git_buf_free(&prefix);
+ git__free(definition);
+ return error;
}
-int git_transport_unregister(
- const char *prefix,
- unsigned priority)
+int git_transport_unregister(const char *scheme)
{
+ git_buf prefix = GIT_BUF_INIT;
transport_definition *d;
- unsigned i;
+ size_t i;
+ int error = 0;
+
+ assert(scheme);
- git_vector_foreach(&additional_transports, i, d) {
- if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
- if (git_vector_remove(&additional_transports, i) < 0)
- return -1;
+ if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
+ goto done;
+
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ if ((error = git_vector_remove(&custom_transports, i)) < 0)
+ goto done;
git__free(d->prefix);
git__free(d);
- if (!additional_transports.length)
- git_vector_free(&additional_transports);
+ if (!custom_transports.length)
+ git_vector_free(&custom_transports);
- return 0;
+ error = 0;
+ goto done;
}
}
- return GIT_ENOTFOUND;
+ error = GIT_ENOTFOUND;
+
+done:
+ git_buf_free(&prefix);
+ return error;
}
/* from remote.h */
@@ -204,7 +229,7 @@ int git_remote_valid_url(const char *url)
git_transport_cb fn;
void *param;
- return !transport_find_fn(url, &fn, &param);
+ return !transport_find_fn(&fn, url, &param);
}
int git_remote_supported_url(const char* url)
@@ -212,7 +237,7 @@ int git_remote_supported_url(const char* url)
git_transport_cb fn;
void *param;
- if (transport_find_fn(url, &fn, &param) < 0)
+ if (transport_find_fn(&fn, url, &param) < 0)
return 0;
return fn != &git_transport_dummy;
diff --git a/src/transports/auth.c b/src/transports/auth.c
new file mode 100644
index 000000000..c1154db34
--- /dev/null
+++ b/src/transports/auth.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+#include "buffer.h"
+#include "auth.h"
+
+static int basic_next_token(
+ git_buf *out, git_http_auth_context *ctx, git_cred *c)
+{
+ git_cred_userpass_plaintext *cred;
+ git_buf raw = GIT_BUF_INIT;
+ int error = -1;
+
+ GIT_UNUSED(ctx);
+
+ if (c->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+ giterr_set(GITERR_INVALID, "invalid credential type for basic auth");
+ goto on_error;
+ }
+
+ cred = (git_cred_userpass_plaintext *)c;
+
+ git_buf_printf(&raw, "%s:%s", cred->username, cred->password);
+
+ if (git_buf_oom(&raw) ||
+ git_buf_puts(out, "Authorization: Basic ") < 0 ||
+ git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 ||
+ git_buf_puts(out, "\r\n") < 0)
+ goto on_error;
+
+ error = 0;
+
+on_error:
+ if (raw.size)
+ git__memzero(raw.ptr, raw.size);
+
+ git_buf_free(&raw);
+ return error;
+}
+
+static git_http_auth_context basic_context = {
+ GIT_AUTHTYPE_BASIC,
+ GIT_CREDTYPE_USERPASS_PLAINTEXT,
+ NULL,
+ basic_next_token,
+ NULL
+};
+
+int git_http_auth_basic(
+ git_http_auth_context **out, const gitno_connection_data *connection_data)
+{
+ GIT_UNUSED(connection_data);
+
+ *out = &basic_context;
+ return 0;
+}
+
+int git_http_auth_dummy(
+ git_http_auth_context **out, const gitno_connection_data *connection_data)
+{
+ GIT_UNUSED(connection_data);
+
+ *out = NULL;
+ return 0;
+}
+
diff --git a/src/transports/auth.h b/src/transports/auth.h
new file mode 100644
index 000000000..52138cf8f
--- /dev/null
+++ b/src/transports/auth.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_http_auth_h__
+#define INCLUDE_http_auth_h__
+
+#include "git2.h"
+#include "netops.h"
+
+typedef enum {
+ GIT_AUTHTYPE_BASIC = 1,
+ GIT_AUTHTYPE_NEGOTIATE = 2,
+} git_http_authtype_t;
+
+typedef struct git_http_auth_context git_http_auth_context;
+
+struct git_http_auth_context {
+ /** Type of scheme */
+ git_http_authtype_t type;
+
+ /** Supported credentials */
+ git_credtype_t credtypes;
+
+ /** Sets the challenge on the authentication context */
+ int (*set_challenge)(git_http_auth_context *ctx, const char *challenge);
+
+ /** Gets the next authentication token from the context */
+ int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_cred *cred);
+
+ /** Frees the authentication context */
+ void (*free)(git_http_auth_context *ctx);
+};
+
+typedef struct {
+ /** Type of scheme */
+ git_http_authtype_t type;
+
+ /** Name of the scheme (as used in the Authorization header) */
+ const char *name;
+
+ /** Credential types this scheme supports */
+ git_credtype_t credtypes;
+
+ /** Function to initialize an authentication context */
+ int (*init_context)(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+} git_http_auth_scheme;
+
+int git_http_auth_dummy(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+int git_http_auth_basic(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+#endif
+
diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c
new file mode 100644
index 000000000..8b99fc735
--- /dev/null
+++ b/src/transports/auth_negotiate.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifdef GIT_GSSAPI
+
+#include "git2.h"
+#include "common.h"
+#include "buffer.h"
+#include "auth.h"
+
+#include <gssapi.h>
+#include <krb5.h>
+
+static gss_OID_desc negotiate_oid_spnego =
+ { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+static gss_OID_desc negotiate_oid_krb5 =
+ { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+static gss_OID negotiate_oids[] =
+ { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
+
+typedef struct {
+ git_http_auth_context parent;
+ unsigned configured : 1,
+ complete : 1;
+ git_buf target;
+ char *challenge;
+ gss_ctx_id_t gss_context;
+ gss_OID oid;
+} http_auth_negotiate_context;
+
+static void negotiate_err_set(
+ OM_uint32 status_major,
+ OM_uint32 status_minor,
+ const char *message)
+{
+ gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+ OM_uint32 status_display, context = 0;
+
+ if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
+ GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
+ giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
+ message, (int)buffer.length, (const char *)buffer.value,
+ status_major, status_minor);
+ gss_release_buffer(&status_minor, &buffer);
+ } else {
+ giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
+ message, status_major, status_minor);
+ }
+}
+
+static int negotiate_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+
+ assert(ctx && ctx->configured && challenge);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GITERR_CHECK_ALLOC(ctx->challenge);
+
+ return 0;
+}
+
+static int negotiate_next_token(
+ git_buf *buf,
+ git_http_auth_context *c,
+ git_cred *cred)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_major, status_minor;
+ gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
+ input_token = GSS_C_EMPTY_BUFFER,
+ output_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
+ git_buf input_buf = GIT_BUF_INIT;
+ gss_name_t server = NULL;
+ gss_OID mech;
+ size_t challenge_len;
+ int error = 0;
+
+ assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
+
+ if (ctx->complete)
+ return 0;
+
+ target_buffer.value = (void *)ctx->target.ptr;
+ target_buffer.length = ctx->target.size;
+
+ status_major = gss_import_name(&status_minor, &target_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE, &server);
+
+ if (GSS_ERROR(status_major)) {
+ negotiate_err_set(status_major, status_minor,
+ "Could not parse principal");
+ error = -1;
+ goto done;
+ }
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (challenge_len < 9) {
+ giterr_set(GITERR_NET, "No negotiate challenge sent from server");
+ error = -1;
+ goto done;
+ } else if (challenge_len > 9) {
+ if (git_buf_decode_base64(&input_buf,
+ ctx->challenge + 10, challenge_len - 10) < 0) {
+ giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
+ error = -1;
+ goto done;
+ }
+
+ input_token.value = input_buf.ptr;
+ input_token.length = input_buf.size;
+ input_token_ptr = &input_token;
+ } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ giterr_set(GITERR_NET, "Could not restart authentication");
+ error = -1;
+ goto done;
+ }
+
+ mech = &negotiate_oid_spnego;
+
+ if (GSS_ERROR(status_major = gss_init_sec_context(
+ &status_minor,
+ GSS_C_NO_CREDENTIAL,
+ &ctx->gss_context,
+ server,
+ mech,
+ GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ input_token_ptr,
+ NULL,
+ &output_token,
+ NULL,
+ NULL))) {
+ negotiate_err_set(status_major, status_minor, "Negotiate failure");
+ error = -1;
+ goto done;
+ }
+
+ /* This message merely told us auth was complete; we do not respond. */
+ if (status_major == GSS_S_COMPLETE) {
+ ctx->complete = 1;
+ goto done;
+ }
+
+ git_buf_puts(buf, "Authorization: Negotiate ");
+ git_buf_encode_base64(buf, output_token.value, output_token.length);
+ git_buf_puts(buf, "\r\n");
+
+ if (git_buf_oom(buf))
+ error = -1;
+
+done:
+ gss_release_name(&status_minor, &server);
+ gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
+ git_buf_free(&input_buf);
+ return error;
+}
+
+static void negotiate_context_free(git_http_auth_context *c)
+{
+ http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ OM_uint32 status_minor;
+
+ if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(
+ &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ }
+
+ git_buf_free(&ctx->target);
+
+ git__free(ctx->challenge);
+
+ ctx->configured = 0;
+ ctx->complete = 0;
+ ctx->oid = NULL;
+
+ git__free(ctx);
+}
+
+static int negotiate_init_context(
+ http_auth_negotiate_context *ctx,
+ const gitno_connection_data *connection_data)
+{
+ OM_uint32 status_major, status_minor;
+ gss_OID item, *oid;
+ gss_OID_set mechanism_list;
+ size_t i;
+
+ /* Query supported mechanisms looking for SPNEGO) */
+ if (GSS_ERROR(status_major =
+ gss_indicate_mechs(&status_minor, &mechanism_list))) {
+ negotiate_err_set(status_major, status_minor,
+ "could not query mechanisms");
+ return -1;
+ }
+
+ if (mechanism_list) {
+ for (oid = negotiate_oids; *oid; oid++) {
+ for (i = 0; i < mechanism_list->count; i++) {
+ item = &mechanism_list->elements[i];
+
+ if (item->length == (*oid)->length &&
+ memcmp(item->elements, (*oid)->elements, item->length) == 0) {
+ ctx->oid = *oid;
+ break;
+ }
+
+ }
+
+ if (ctx->oid)
+ break;
+ }
+ }
+
+ gss_release_oid_set(&status_minor, &mechanism_list);
+
+ if (!ctx->oid) {
+ giterr_set(GITERR_NET, "Negotiate authentication is not supported");
+ return -1;
+ }
+
+ git_buf_puts(&ctx->target, "HTTP@");
+ git_buf_puts(&ctx->target, connection_data->host);
+
+ if (git_buf_oom(&ctx->target))
+ return -1;
+
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ ctx->configured = 1;
+
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data)
+{
+ http_auth_negotiate_context *ctx;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
+ GITERR_CHECK_ALLOC(ctx);
+
+ if (negotiate_init_context(ctx, connection_data) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
+ ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
+ ctx->parent.set_challenge = negotiate_set_challenge;
+ ctx->parent.next_token = negotiate_next_token;
+ ctx->parent.free = negotiate_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_GSSAPI */
+
diff --git a/src/transports/auth_negotiate.h b/src/transports/auth_negotiate.h
new file mode 100644
index 000000000..d7270b7ab
--- /dev/null
+++ b/src/transports/auth_negotiate.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_auth_negotiate_h__
+#define INCLUDE_auth_negotiate_h__
+
+#include "git2.h"
+#include "auth.h"
+
+#ifdef GIT_GSSAPI
+
+extern int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const gitno_connection_data *connection_data);
+
+#else
+
+#define git_http_auth_negotiate git_http_auth_dummy
+
+#endif /* GIT_GSSAPI */
+
+#endif
+
diff --git a/src/transports/git.c b/src/transports/git.c
index 21507c1c7..e2690fe36 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -8,6 +8,7 @@
#include "git2.h"
#include "buffer.h"
#include "netops.h"
+#include "git2/sys/transport.h"
#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
diff --git a/src/transports/http.c b/src/transports/http.c
index a7eff7365..f9df53b71 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -11,6 +11,13 @@
#include "buffer.h"
#include "netops.h"
#include "smart.h"
+#include "auth.h"
+#include "auth_negotiate.h"
+
+git_http_auth_scheme auth_schemes[] = {
+ { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
+ { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
+};
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
@@ -20,7 +27,6 @@ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive
static const char *receive_pack_service_url = "/git-receive-pack";
static const char *get_verb = "GET";
static const char *post_verb = "POST";
-static const char *basic_authtype = "Basic";
#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
@@ -35,10 +41,6 @@ enum last_cb {
VALUE
};
-typedef enum {
- GIT_HTTP_AUTH_BASIC = 1,
-} http_authmechanism_t;
-
typedef struct {
git_smart_subtransport_stream parent;
const char *service;
@@ -58,9 +60,6 @@ typedef struct {
transport_smart *owner;
gitno_socket socket;
gitno_connection_data connection_data;
- git_cred *cred;
- git_cred *url_cred;
- http_authmechanism_t auth_mechanism;
bool connected;
/* Parser structures */
@@ -76,6 +75,11 @@ typedef struct {
enum last_cb last_cb;
int parse_error;
unsigned parse_finished : 1;
+
+ /* Authentication */
+ git_cred *cred;
+ git_cred *url_cred;
+ git_vector auth_contexts;
} http_subtransport;
typedef struct {
@@ -88,28 +92,91 @@ typedef struct {
size_t *bytes_read;
} parser_context;
-static int apply_basic_credential(git_buf *buf, git_cred *cred)
+static bool credtype_match(git_http_auth_scheme *scheme, void *data)
{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- git_buf raw = GIT_BUF_INIT;
- int error = -1;
+ unsigned int credtype = *(unsigned int *)data;
- git_buf_printf(&raw, "%s:%s", c->username, c->password);
+ return !!(scheme->credtypes & credtype);
+}
- if (git_buf_oom(&raw) ||
- git_buf_puts(buf, "Authorization: Basic ") < 0 ||
- git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
- git_buf_puts(buf, "\r\n") < 0)
- goto on_error;
+static bool challenge_match(git_http_auth_scheme *scheme, void *data)
+{
+ const char *scheme_name = scheme->name;
+ const char *challenge = (const char *)data;
+ size_t scheme_len;
- error = 0;
+ scheme_len = strlen(scheme_name);
+ return (strncmp(challenge, scheme_name, scheme_len) == 0 &&
+ (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
+}
-on_error:
- if (raw.size)
- memset(raw.ptr, 0x0, raw.size);
+static int auth_context_match(
+ git_http_auth_context **out,
+ http_subtransport *t,
+ bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
+ void *data)
+{
+ git_http_auth_scheme *scheme = NULL;
+ git_http_auth_context *context = NULL, *c;
+ size_t i;
+
+ *out = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
+ if (scheme_match(&auth_schemes[i], data)) {
+ scheme = &auth_schemes[i];
+ break;
+ }
+ }
+
+ if (!scheme)
+ return 0;
+
+ /* See if authentication has already started for this scheme */
+ git_vector_foreach(&t->auth_contexts, i, c) {
+ if (c->type == scheme->type) {
+ context = c;
+ break;
+ }
+ }
+
+ if (!context) {
+ if (scheme->init_context(&context, &t->connection_data) < 0)
+ return -1;
+ else if (!context)
+ return 0;
+ else if (git_vector_insert(&t->auth_contexts, context) < 0)
+ return -1;
+ }
+
+ *out = context;
+
+ return 0;
+}
+
+static int apply_credentials(git_buf *buf, http_subtransport *t)
+{
+ git_cred *cred = t->cred;
+ git_http_auth_context *context;
+
+ /* Apply the credentials given to us in the URL */
+ if (!cred && t->connection_data.user && t->connection_data.pass) {
+ if (!t->url_cred &&
+ git_cred_userpass_plaintext_new(&t->url_cred,
+ t->connection_data.user, t->connection_data.pass) < 0)
+ return -1;
+
+ cred = t->url_cred;
+ }
+
+ if (!cred)
+ return 0;
- git_buf_free(&raw);
- return error;
+ /* Get or create a context for the best scheme for this cred type */
+ if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
+ return -1;
+
+ return context->next_token(buf, context, cred);
}
static int gen_request(
@@ -137,19 +204,9 @@ static int gen_request(
git_buf_puts(buf, "Accept: */*\r\n");
/* Apply credentials to the request */
- if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
- t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
- apply_basic_credential(buf, t->cred) < 0)
+ if (apply_credentials(buf, t) < 0)
return -1;
- /* Use url-parsed basic auth if username and password are both provided */
- if (!t->cred && t->connection_data.user && t->connection_data.pass) {
- if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
- t->connection_data.user, t->connection_data.pass) < 0)
- return -1;
- if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
- }
-
git_buf_puts(buf, "\r\n");
if (git_buf_oom(buf))
@@ -158,20 +215,26 @@ static int gen_request(
return 0;
}
-static int parse_unauthorized_response(
+static int parse_authenticate_response(
git_vector *www_authenticate,
- int *allowed_types,
- http_authmechanism_t *auth_mechanism)
+ http_subtransport *t,
+ int *allowed_types)
{
- unsigned i;
- char *entry;
-
- git_vector_foreach(www_authenticate, i, entry) {
- if (!strncmp(entry, basic_authtype, 5) &&
- (entry[5] == '\0' || entry[5] == ' ')) {
- *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
- *auth_mechanism = GIT_HTTP_AUTH_BASIC;
- }
+ git_http_auth_context *context;
+ char *challenge;
+ size_t i;
+
+ git_vector_foreach(www_authenticate, i, challenge) {
+ if (auth_context_match(&context, t, challenge_match, challenge) < 0)
+ return -1;
+ else if (!context)
+ continue;
+
+ if (context->set_challenge &&
+ context->set_challenge(context, challenge) < 0)
+ return -1;
+
+ *allowed_types |= context->credtypes;
}
return 0;
@@ -248,7 +311,7 @@ static int on_headers_complete(http_parser *parser)
http_subtransport *t = ctx->t;
http_stream *s = ctx->s;
git_buf buf = GIT_BUF_INIT;
- int error = 0, no_callback = 0;
+ int error = 0, no_callback = 0, allowed_auth_types = 0;
/* Both parse_header_name and parse_header_value are populated
* and ready for consumption. */
@@ -256,26 +319,26 @@ static int on_headers_complete(http_parser *parser)
if (on_header_ready(t) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
- /* Check for an authentication failure. */
+ /* Capture authentication headers which may be a 401 (authentication
+ * is not complete) or a 200 (simply informing us that auth *is*
+ * complete.)
+ */
+ if (parse_authenticate_response(&t->www_authenticate, t,
+ &allowed_auth_types) < 0)
+ return t->parse_error = PARSE_ERROR_GENERIC;
- if (parser->status_code == 401 &&
- get_verb == s->verb) {
- if (!t->owner->cred_acquire_payload) {
+ /* Check for an authentication failure. */
+ if (parser->status_code == 401 && get_verb == s->verb) {
+ if (!t->owner->cred_acquire_cb) {
no_callback = 1;
} else {
- int allowed_types = 0;
-
- if (parse_unauthorized_response(&t->www_authenticate,
- &allowed_types, &t->auth_mechanism) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- if (allowed_types &&
- (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+ if (allowed_auth_types &&
+ (!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) {
error = t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
t->connection_data.user,
- allowed_types,
+ allowed_auth_types,
t->owner->cred_acquire_payload);
if (error == GIT_PASSTHROUGH) {
@@ -286,7 +349,8 @@ static int on_headers_complete(http_parser *parser)
assert(t->cred);
/* Successfully acquired a credential. */
- return t->parse_error = PARSE_ERROR_REPLAY;
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
}
}
}
@@ -324,7 +388,8 @@ static int on_headers_complete(http_parser *parser)
t->connected = 0;
s->redirect_count++;
- return t->parse_error = PARSE_ERROR_REPLAY;
+ t->parse_error = PARSE_ERROR_REPLAY;
+ return 0;
}
/* Check for a 200 HTTP status code. */
@@ -382,6 +447,13 @@ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
parser_context *ctx = (parser_context *) parser->data;
http_subtransport *t = ctx->t;
+ /* If our goal is to replay the request (either an auth failure or
+ * a redirect) then don't bother buffering since we're ignoring the
+ * content anyway.
+ */
+ if (t->parse_error == PARSE_ERROR_REPLAY)
+ return 0;
+
if (ctx->buf_size < len) {
giterr_set(GITERR_NET, "Can't fit data in the buffer");
return t->parse_error = PARSE_ERROR_GENERIC;
@@ -456,7 +528,7 @@ static int http_connect(http_subtransport *t)
if (t->connected &&
http_should_keep_alive(&t->parser) &&
- http_body_is_final(&t->parser))
+ t->parse_finished)
return 0;
if (t->socket.socket)
@@ -502,10 +574,8 @@ replay:
clear_parser_state(t);
- if (gen_request(&request, s, 0) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, 0) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request);
@@ -537,7 +607,23 @@ replay:
}
while (!*bytes_read && !t->parse_finished) {
- t->parse_buffer.offset = 0;
+ size_t data_offset;
+
+ /*
+ * Make the parse_buffer think it's as full of data as
+ * the buffer, so it won't try to recv more data than
+ * we can put into it.
+ *
+ * data_offset is the actual data offset from which we
+ * should tell the parser to start reading.
+ */
+ if (buf_size >= t->parse_buffer.len) {
+ t->parse_buffer.offset = 0;
+ } else {
+ t->parse_buffer.offset = t->parse_buffer.len - buf_size;
+ }
+
+ data_offset = t->parse_buffer.offset;
if (gitno_recv(&t->parse_buffer) < 0)
return -1;
@@ -558,8 +644,8 @@ replay:
bytes_parsed = http_parser_execute(&t->parser,
&t->settings,
- t->parse_buffer.data,
- t->parse_buffer.offset);
+ t->parse_buffer.data + data_offset,
+ t->parse_buffer.offset - data_offset);
t->parser.data = NULL;
@@ -577,7 +663,7 @@ replay:
if (t->parse_error < 0)
return -1;
- if (bytes_parsed != t->parse_buffer.offset) {
+ if (bytes_parsed != t->parse_buffer.offset - data_offset) {
giterr_set(GITERR_NET,
"HTTP parser error: %s",
http_errno_description((enum http_errno)t->parser.http_errno));
@@ -604,10 +690,8 @@ static int http_stream_write_chunked(
clear_parser_state(t);
- if (gen_request(&request, s, 0) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, 0) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
git_buf_free(&request);
@@ -679,10 +763,8 @@ static int http_stream_write_single(
clear_parser_state(t);
- if (gen_request(&request, s, len) < 0) {
- giterr_set(GITERR_NET, "Failed to generate request");
+ if (gen_request(&request, s, len) < 0)
return -1;
- }
if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
goto on_error;
@@ -849,6 +931,8 @@ static int http_action(
static int http_close(git_smart_subtransport *subtransport)
{
http_subtransport *t = (http_subtransport *) subtransport;
+ git_http_auth_context *context;
+ size_t i;
clear_parser_state(t);
@@ -867,6 +951,13 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
+ git_vector_foreach(&t->auth_contexts, i, context) {
+ if (context->free)
+ context->free(context);
+ }
+
+ git_vector_clear(&t->auth_contexts);
+
gitno_connection_data_free_ptrs(&t->connection_data);
return 0;
@@ -878,6 +969,7 @@ static void http_free(git_smart_subtransport *subtransport)
http_close(subtransport);
+ git_vector_free(&t->auth_contexts);
git__free(t);
}
diff --git a/src/transports/smart.h b/src/transports/smart.h
index f1fc29520..b29faae44 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -9,6 +9,7 @@
#include "netops.h"
#include "buffer.h"
#include "push.h"
+#include "git2/sys/transport.h"
#define GIT_SIDE_BAND_DATA 1
#define GIT_SIDE_BAND_PROGRESS 2
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index a52aacc60..82891165f 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -592,7 +592,9 @@ int git_smart__download_pack(
}
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
- error = writepack->append(writepack, p->data, p->len, stats);
+
+ if (p->len)
+ error = writepack->append(writepack, p->data, p->len, stats);
} else if (pkt->type == GIT_PKT_FLUSH) {
/* A flush indicates the end of the packfile */
git__free(pkt);
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 6a7f67e99..fff81661a 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -5,6 +5,10 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
+#ifdef GIT_SSH
+#include <libssh2.h>
+#endif
+
#include "git2.h"
#include "buffer.h"
#include "netops.h"
@@ -13,8 +17,6 @@
#ifdef GIT_SSH
-#include <libssh2.h>
-
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
static const char prefix_ssh[] = "ssh://";
@@ -35,6 +37,9 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
ssh_stream *current_stream;
+ git_cred *cred;
+ char *cmd_uploadpack;
+ char *cmd_receivepack;
} ssh_subtransport;
static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
@@ -134,11 +139,22 @@ static int ssh_stream_write(
size_t len)
{
ssh_stream *s = (ssh_stream *)stream;
+ size_t off = 0;
+ ssize_t ret = 0;
if (!s->sent_command && send_command(s) < 0)
return -1;
- if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
+ do {
+ ret = libssh2_channel_write(s->channel, buffer + off, len - off);
+ if (ret < 0)
+ break;
+
+ off += ret;
+
+ } while (off < len);
+
+ if (ret < 0) {
ssh_error(s->session, "SSH could not write data");
return -1;
}
@@ -552,7 +568,9 @@ static int ssh_uploadpack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- return _git_ssh_setup_conn(t, url, cmd_uploadpack, stream);
+ const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
}
static int ssh_uploadpack(
@@ -576,7 +594,9 @@ static int ssh_receivepack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0)
+ const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
+
+ if (_git_ssh_setup_conn(t, url, cmd, stream) < 0)
return -1;
return 0;
@@ -641,6 +661,8 @@ static void _ssh_free(git_smart_subtransport *subtransport)
assert(!t->current_stream);
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
git__free(t);
}
@@ -720,3 +742,46 @@ int git_smart_subtransport_ssh(
return -1;
#endif
}
+
+int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
+{
+#ifdef GIT_SSH
+ git_strarray *paths = (git_strarray *) payload;
+ git_transport *transport;
+ transport_smart *smart;
+ ssh_subtransport *t;
+ int error;
+ git_smart_subtransport_definition ssh_definition = {
+ git_smart_subtransport_ssh,
+ 0, /* no RPC */
+ };
+
+ if (paths->count != 2) {
+ giterr_set(GITERR_SSH, "invalid ssh paths, must be two strings");
+ return GIT_EINVALIDSPEC;
+ }
+
+ if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
+ return error;
+
+ smart = (transport_smart *) transport;
+ t = (ssh_subtransport *) smart->wrapped;
+
+ t->cmd_uploadpack = git__strdup(paths->strings[0]);
+ GITERR_CHECK_ALLOC(t->cmd_uploadpack);
+ t->cmd_receivepack = git__strdup(paths->strings[1]);
+ GITERR_CHECK_ALLOC(t->cmd_receivepack);
+
+ *out = transport;
+ return 0;
+#else
+ GIT_UNUSED(owner);
+ GIT_UNUSED(payload);
+
+ assert(out);
+ *out = NULL;
+
+ giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+ return -1;
+#endif
+}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index bd9509cd4..1e46dfaee 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -36,6 +36,10 @@
#define CACHED_POST_BODY_BUF_SIZE 4096
#define UUID_LENGTH_CCH 32
+#ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH
+#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
+#endif
+
static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
@@ -97,7 +101,7 @@ static int apply_basic_credential(HINTERNET request, git_cred *cred)
if (git_buf_oom(&raw) ||
git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
- git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
+ git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
goto on_error;
if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
@@ -745,9 +749,9 @@ replay:
/* Verify that we got the correct content-type back */
if (post_verb == s->verb)
- snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
else
- snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters");
diff --git a/src/tree.c b/src/tree.c
index b64efe460..28190d6da 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -17,6 +17,8 @@
#define DEFAULT_TREE_SIZE 16
#define MAX_FILEMODE_BYTES 6
+GIT__USE_STRMAP;
+
static bool valid_filemode(const int filemode)
{
return (filemode == GIT_FILEMODE_TREE
@@ -365,7 +367,8 @@ size_t git_tree_entrycount(const git_tree *tree)
unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
{
assert(bld);
- return (unsigned int)bld->entrycount;
+
+ return git_strmap_num_entries(bld->map);
}
static int tree_error(const char *str, const char *path)
@@ -450,6 +453,7 @@ static int append_entry(
git_filemode_t filemode)
{
git_tree_entry *entry;
+ int error = 0;
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
@@ -460,12 +464,13 @@ static int append_entry(
git_oid_cpy(&entry->oid, id);
entry->attr = (uint16_t)filemode;
- if (git_vector_insert_sorted(&bld->entries, entry, NULL) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+ if (error < 0) {
+ git_tree_entry_free(entry);
+ giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
return -1;
}
- bld->entrycount++;
return 0;
}
@@ -610,18 +615,17 @@ int git_tree__write_index(
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
{
git_treebuilder *bld;
- size_t i, source_entries = DEFAULT_TREE_SIZE;
+ size_t i;
assert(builder_p);
bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld);
- if (source != NULL)
- source_entries = source->entries.length;
-
- if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
- goto on_error;
+ if (git_strmap_alloc(&bld->map) < 0) {
+ git__free(bld);
+ return -1;
+ }
if (source != NULL) {
git_tree_entry *entry_src;
@@ -651,7 +655,8 @@ int git_treebuilder_insert(
git_filemode_t filemode)
{
git_tree_entry *entry;
- size_t pos;
+ int error;
+ git_strmap_iter pos;
assert(bld && id && filename);
@@ -661,22 +666,20 @@ int git_treebuilder_insert(
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
- if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) {
- entry = git_vector_get(&bld->entries, pos);
- if (entry->removed) {
- entry->removed = 0;
- bld->entrycount++;
- }
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos)) {
+ entry = git_strmap_value_at(bld->map, pos);
} else {
entry = alloc_entry(filename);
GITERR_CHECK_ALLOC(entry);
- if (git_vector_insert_sorted(&bld->entries, entry, NULL) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+
+ if (error < 0) {
+ git_tree_entry_free(entry);
+ giterr_set(GITERR_TREE, "failed to insert %s", filename);
return -1;
}
-
- bld->entrycount++;
}
git_oid_cpy(&entry->oid, id);
@@ -690,17 +693,14 @@ int git_treebuilder_insert(
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
{
- size_t idx;
- git_tree_entry *entry;
+ git_tree_entry *entry = NULL;
+ git_strmap_iter pos;
assert(bld && filename);
- if (tree_key_search(&idx, &bld->entries, filename, strlen(filename)) < 0)
- return NULL;
-
- entry = git_vector_get(&bld->entries, idx);
- if (entry->removed)
- return NULL;
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos))
+ entry = git_strmap_value_at(bld->map, pos);
return entry;
}
@@ -712,35 +712,44 @@ const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *file
int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
{
- git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
+ git_tree_entry *entry = treebuilder_get(bld, filename);
- if (remove_ptr == NULL || remove_ptr->removed)
+ if (entry == NULL)
return tree_error("Failed to remove entry. File isn't in the tree", filename);
- remove_ptr->removed = 1;
- bld->entrycount--;
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+
return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
int error = 0;
- size_t i;
+ size_t i, entrycount;
git_buf tree = GIT_BUF_INIT;
git_odb *odb;
+ git_tree_entry *entry;
+ git_vector entries;
assert(bld);
- git_vector_sort(&bld->entries);
+ entrycount = git_strmap_num_entries(bld->map);
+ if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
+ return -1;
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, bld->entries.length * 72);
+ git_strmap_foreach_value(bld->map, entry, {
+ if (git_vector_insert(&entries, entry) < 0)
+ return -1;
+ });
- for (i = 0; i < bld->entries.length && !error; ++i) {
- git_tree_entry *entry = git_vector_get(&bld->entries, i);
+ git_vector_sort(&entries);
+
+ /* Grow the buffer beforehand to an estimated size */
+ error = git_buf_grow(&tree, entrycount * 72);
- if (entry->removed)
- continue;
+ for (i = 0; i < entries.length && !error; ++i) {
+ git_tree_entry *entry = git_vector_get(&entries, i);
git_buf_printf(&tree, "%o ", entry->attr);
git_buf_put(&tree, entry->filename, entry->filename_len + 1);
@@ -750,6 +759,8 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
error = -1;
}
+ git_vector_free(&entries);
+
if (!error &&
!(error = git_repository_odb__weakptr(&odb, repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
@@ -763,31 +774,27 @@ void git_treebuilder_filter(
git_treebuilder_filter_cb filter,
void *payload)
{
- size_t i;
+ const char *filename;
git_tree_entry *entry;
assert(bld && filter);
- git_vector_foreach(&bld->entries, i, entry) {
- if (!entry->removed && filter(entry, payload)) {
- entry->removed = 1;
- bld->entrycount--;
- }
- }
+ git_strmap_foreach(bld->map, filename, entry, {
+ if (filter(entry, payload)) {
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+ }
+ });
}
void git_treebuilder_clear(git_treebuilder *bld)
{
- size_t i;
git_tree_entry *e;
assert(bld);
- git_vector_foreach(&bld->entries, i, e)
- git_tree_entry_free(e);
-
- git_vector_clear(&bld->entries);
- bld->entrycount = 0;
+ git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
+ git_strmap_clear(bld->map);
}
void git_treebuilder_free(git_treebuilder *bld)
@@ -796,7 +803,7 @@ void git_treebuilder_free(git_treebuilder *bld)
return;
git_treebuilder_clear(bld);
- git_vector_free(&bld->entries);
+ git_strmap_free(bld->map);
git__free(bld);
}
diff --git a/src/tree.h b/src/tree.h
index f07039a07..5d27eb7c9 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -11,9 +11,9 @@
#include "repository.h"
#include "odb.h"
#include "vector.h"
+#include "strmap.h"
struct git_tree_entry {
- uint16_t removed;
uint16_t attr;
git_oid oid;
size_t filename_len;
@@ -26,8 +26,7 @@ struct git_tree {
};
struct git_treebuilder {
- git_vector entries;
- size_t entrycount; /* vector may contain "removed" entries */
+ git_strmap *map;
};
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 1e41bcf18..e4f3ac67a 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -4,33 +4,47 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_posix__w32_h__
-#define INCLUDE_posix__w32_h__
+#ifndef INCLUDE_posix__unix_h__
+#define INCLUDE_posix__unix_h__
#include <stdio.h>
#include <sys/param.h>
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#define p_lseek(f,n,w) lseek(f, n, w)
+#define p_fstat(f,b) fstat(f, b)
#define p_lstat(p,b) lstat(p,b)
+#define p_stat(p,b) stat(p, b)
+
#define p_readlink(a, b, c) readlink(a, b, c)
#define p_symlink(o,n) symlink(o, n)
#define p_link(o,n) link(o, n)
#define p_unlink(p) unlink(p)
#define p_mkdir(p,m) mkdir(p, m)
#define p_fsync(fd) fsync(fd)
+extern char *p_realpath(const char *, char *);
-/* The OpenBSD realpath function behaves differently */
-#if !defined(__OpenBSD__)
-# define p_realpath(p, po) realpath(p, po)
-#else
-char *p_realpath(const char *, char *);
-#endif
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+#define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
+#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p)
-#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+#define p_chdir(p) chdir(p)
+#define p_chmod(p,m) chmod(p, m)
+#define p_rmdir(p) rmdir(p)
+#define p_access(p,m) access(p,m)
+#define p_ftruncate(fd, sz) ftruncate(fd, sz)
/* see win32/posix.h for explanation about why this exists */
#define p_lstat_posixly(p,b) lstat(p,b)
+#define p_localtime_r(c, r) localtime_r(c, r)
+#define p_gmtime_r(c, r) gmtime_r(c, r)
+
#endif
diff --git a/src/unix/realpath.c b/src/unix/realpath.c
index 15601bd22..2e49150c2 100644
--- a/src/unix/realpath.c
+++ b/src/unix/realpath.c
@@ -6,7 +6,7 @@
*/
#include <git2/common.h>
-#ifdef __OpenBSD__
+#ifndef GIT_WIN32
#include <limits.h>
#include <stdlib.h>
@@ -16,15 +16,16 @@
char *p_realpath(const char *pathname, char *resolved)
{
char *ret;
-
if ((ret = realpath(pathname, resolved)) == NULL)
return NULL;
- /* Figure out if the file exists */
- if (!access(ret, F_OK))
- return ret;
-
- return NULL;
+#ifdef __OpenBSD__
+ /* The OpenBSD realpath function behaves differently,
+ * figure out if the file exists */
+ if (access(ret, F_OK) < 0)
+ ret = NULL;
+#endif
+ return ret;
}
#endif
diff --git a/src/util.c b/src/util.c
index f9d37e4f4..5c305950f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -613,7 +613,8 @@ void git__qsort_r(
defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__gnu_hurd__) || defined(__ANDROID_API__) || \
defined(__sun) || defined(__CYGWIN__) || \
- (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)
+ (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \
+ (defined(_MSC_VER) && _MSC_VER < 1500)
git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
#elif defined(GIT_WIN32)
git__qsort_r_glue glue = { cmp, payload };
diff --git a/src/util.h b/src/util.h
index ca676c039..6e57ad8c3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -419,7 +419,7 @@ GIT_INLINE(double) git__timer(void)
scaling_factor = (double)info.numer / (double)info.denom;
}
- return (double)time * scaling_factor / 1.0E-9;
+ return (double)time * scaling_factor / 1.0E9;
}
#else
@@ -431,13 +431,13 @@ GIT_INLINE(double) git__timer(void)
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
- return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
+ return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9;
} else {
/* Fall back to using gettimeofday */
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
}
}
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 059e39cbc..83ee28765 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -9,18 +9,11 @@
#if defined(__MINGW32__)
-/* use a 64-bit file offset type */
-# undef lseek
-# define lseek _lseeki64
-# undef stat
-# define stat _stati64
-# undef fstat
-# define fstat _fstati64
-
-/* stat: file mode type testing macros */
-# define _S_IFLNK 0120000
-# define S_IFLNK _S_IFLNK
-# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#if _WIN32_WINNT >= 0x0601
+#define stat __stat64
+#else
+#define stat _stati64
+#endif
#endif
diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h
index 50865ed17..4789d63df 100644
--- a/src/win32/msvc-compat.h
+++ b/src/win32/msvc-compat.h
@@ -9,41 +9,12 @@
#if defined(_MSC_VER)
-/* access() mode parameter #defines */
-# define F_OK 0 /* existence check */
-# define W_OK 2 /* write mode check */
-# define R_OK 4 /* read mode check */
+/* 64-bit stat information, regardless of USE_32BIT_TIME_T define */
+#define stat __stat64
-# define lseek _lseeki64
-# define stat _stat64
-# define fstat _fstat64
-
-/* stat: file mode type testing macros */
-# define _S_IFLNK 0120000
-# define S_IFLNK _S_IFLNK
-# define S_IXUSR 00100
-
-# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
-# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
-# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
-# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
-
-# define mode_t unsigned short
-
-/* case-insensitive string comparison */
-# define strcasecmp _stricmp
-# define strncasecmp _strnicmp
-
-/* MSVC doesn't define ssize_t at all */
+typedef unsigned short mode_t;
typedef SSIZE_T ssize_t;
-/* define snprintf using variadic macro support if available */
-#if _MSC_VER >= 1400
-# define snprintf(BUF, SZ, FMT, ...) _snprintf_s(BUF, SZ, _TRUNCATE, FMT, __VA_ARGS__)
-#else
-# define snprintf _snprintf
-#endif
-
#endif
#define GIT_STDLIB_CALL __cdecl
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 2cbea1807..e055a77d0 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -12,49 +12,35 @@
#include "utf-conv.h"
#include "dir.h"
-/* define some standard errnos that the runtime may be missing. for example,
- * mingw lacks EAFNOSUPPORT. */
+typedef SOCKET GIT_SOCKET;
-#ifndef EAFNOSUPPORT
-# define EAFNOSUPPORT (INT_MAX-1)
-#endif
-
-#ifdef _MSC_VER
-# define p_ftruncate(fd, sz) _chsize_s(fd, sz)
-#else /* MinGW */
-# define p_ftruncate(fd, sz) _chsize(fd, sz)
-#endif
-
-GIT_INLINE(int) p_link(const char *old, const char *new)
-{
- GIT_UNUSED(old);
- GIT_UNUSED(new);
- errno = ENOSYS;
- return -1;
-}
-
-extern int p_mkdir(const char *path, mode_t mode);
-extern int p_unlink(const char *path);
+#define p_lseek(f,n,w) _lseeki64(f, n, w)
+#define p_fstat(f,b) _fstat64(f, b)
extern int p_lstat(const char *file_name, struct stat *buf);
+extern int p_stat(const char* path, struct stat* buf);
+
extern int p_readlink(const char *path, char *buf, size_t bufsiz);
extern int p_symlink(const char *old, const char *new);
+extern int p_link(const char *old, const char *new);
+extern int p_unlink(const char *path);
+extern int p_mkdir(const char *path, mode_t mode);
+extern int p_fsync(int fd);
extern char *p_realpath(const char *orig_path, char *buffer);
+
+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
+extern int p_inet_pton(int af, const char* src, void* dst);
+
+#define strcasecmp(s1, s2) _stricmp(s1, s2)
+#define strncasecmp(s1, s2, c) _strnicmp(s1, s2, c)
extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
extern int p_mkstemp(char *tmp_path);
-extern int p_stat(const char* path, struct stat* buf);
extern int p_chdir(const char* path);
extern int p_chmod(const char* path, mode_t mode);
extern int p_rmdir(const char* path);
extern int p_access(const char* path, mode_t mode);
-extern int p_fsync(int fd);
-extern int p_open(const char *path, int flags, ...);
-extern int p_creat(const char *path, mode_t mode);
-extern int p_getcwd(char *buffer_out, size_t size);
-extern int p_rename(const char *from, const char *to);
-extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
-extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
-extern int p_inet_pton(int af, const char* src, void* dst);
+extern int p_ftruncate(int fd, long size);
/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
* ENOTDIR is wrong, in that it does not mean precisely that a non-directory
@@ -64,4 +50,7 @@ extern int p_inet_pton(int af, const char* src, void* dst);
*/
extern int p_lstat_posixly(const char *filename, struct stat *buf);
+extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
+extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
+
#endif
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 34938431a..0023f95ff 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -19,6 +19,10 @@
# define FILE_NAME_NORMALIZED 0
#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+
/* Options which we always provide to _wopen.
*
* _O_BINARY - Raw access; no translation of CR or LF characters
@@ -47,6 +51,15 @@ static int utf8_to_16_with_errno(git_win32_path dest, const char *src)
return len;
}
+int p_ftruncate(int fd, long size)
+{
+#if defined(_MSC_VER) && _MSC_VER >= 1500
+ return _chsize_s(fd, size);
+#else
+ return _chsize(fd, size);
+#endif
+}
+
int p_mkdir(const char *path, mode_t mode)
{
git_win32_path buf;
@@ -59,6 +72,14 @@ int p_mkdir(const char *path, mode_t mode)
return _wmkdir(buf);
}
+int p_link(const char *old, const char *new)
+{
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
+ errno = ENOSYS;
+ return -1;
+}
+
int p_unlink(const char *path)
{
git_win32_path buf;
@@ -543,11 +564,19 @@ char *p_realpath(const char *orig_path, char *buffer)
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
int len;
- if (count == 0 ||
- (len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr)) < 0)
+ if (count == 0)
+ return _vscprintf(format, argptr);
+
+ #if _MSC_VER >= 1500
+ len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
+ #else
+ len = _vsnprintf(buffer, count, format, argptr);
+ #endif
+
+ if (len < 0)
return _vscprintf(format, argptr);
return len;
@@ -570,7 +599,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...)
int p_mkstemp(char *tmp_path)
{
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && _MSC_VER >= 1500
if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
return -1;
#else
@@ -591,6 +620,31 @@ int p_access(const char* path, mode_t mode)
return _waccess(buf, mode);
}
+static int ensure_writable(wchar_t *fpath)
+{
+ DWORD attrs;
+
+ attrs = GetFileAttributesW(fpath);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ return 0;
+
+ giterr_set(GITERR_OS, "failed to get attributes");
+ return -1;
+ }
+
+ if (!(attrs & FILE_ATTRIBUTE_READONLY))
+ return 0;
+
+ attrs &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(fpath, attrs)) {
+ giterr_set(GITERR_OS, "failed to set attributes");
+ return -1;
+ }
+
+ return 0;
+}
+
int p_rename(const char *from, const char *to)
{
git_win32_path wfrom;
@@ -602,12 +656,13 @@ int p_rename(const char *from, const char *to)
if (utf8_to_16_with_errno(wfrom, from) < 0 ||
utf8_to_16_with_errno(wto, to) < 0)
return -1;
-
+
/* wait up to 50ms if file is locked by another thread or process */
rename_tries = 0;
rename_succeeded = 0;
while (rename_tries < 10) {
- if (MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
+ if (ensure_writable(wto) == 0 &&
+ MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
rename_succeeded = 1;
break;
}