summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/array.h11
-rw-r--r--src/attr_file.c18
-rw-r--r--src/attr_file.h6
-rw-r--r--src/blob.c89
-rw-r--r--src/blob.h9
-rw-r--r--src/buf_text.c2
-rw-r--r--src/config_file.c14
-rw-r--r--src/diff.c77
-rw-r--r--src/diff.h36
-rw-r--r--src/diff_file.c17
-rw-r--r--src/diff_patch.c87
-rw-r--r--src/diff_print.c106
-rw-r--r--src/diff_tform.c356
-rw-r--r--src/fileops.c30
-rw-r--r--src/fileops.h8
-rw-r--r--src/hashsig.c214
-rw-r--r--src/ignore.c42
-rw-r--r--src/ignore.h5
-rw-r--r--src/index.c53
-rw-r--r--src/indexer.c2
-rw-r--r--src/iterator.c2
-rw-r--r--src/merge.c4
-rw-r--r--src/odb.c5
-rw-r--r--src/odb_pack.c41
-rw-r--r--src/oid.h23
-rw-r--r--src/pack-objects.c4
-rw-r--r--src/pack.c14
-rw-r--r--src/path.c9
-rw-r--r--src/path.h2
-rw-r--r--src/pathspec.c12
-rw-r--r--src/posix.h4
-rw-r--r--src/pqueue.h6
-rw-r--r--src/refdb_fs.c71
-rw-r--r--src/remote.c41
-rw-r--r--src/repository.c6
-rw-r--r--src/revparse.c23
-rw-r--r--src/sha1_lookup.c24
-rw-r--r--src/sha1_lookup.h5
-rw-r--r--src/signature.c19
-rw-r--r--src/status.c29
-rw-r--r--src/submodule.c8
-rw-r--r--src/transports/cred.c39
-rw-r--r--src/transports/ssh.c21
-rw-r--r--src/transports/winhttp.c6
-rw-r--r--src/util.h8
-rw-r--r--src/win32/dir.c38
-rw-r--r--src/win32/dir.h4
-rw-r--r--src/win32/findfile.c14
-rw-r--r--src/win32/mingw-compat.h5
-rw-r--r--src/win32/posix.h6
-rw-r--r--src/win32/posix_w32.c80
-rw-r--r--src/win32/precompiled.h3
-rw-r--r--src/win32/utf-conv.c8
-rw-r--r--src/win32/utf-conv.h31
54 files changed, 1140 insertions, 657 deletions
diff --git a/src/array.h b/src/array.h
index 248010425..c25a1b29e 100644
--- a/src/array.h
+++ b/src/array.h
@@ -39,25 +39,26 @@
#define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr)
-typedef git_array_t(void) git_array_generic_t;
+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(git_array_generic_t *a, size_t item_size)
+GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
{
+ git_array_generic_t *a = _a;
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
- void *new_array = git__realloc(a->ptr, new_size * item_size);
+ char *new_array = git__realloc(a->ptr, new_size * item_size);
if (!new_array) {
git_array_clear(*a);
return NULL;
} else {
a->ptr = new_array; a->asize = new_size; a->size++;
- return (((char *)a->ptr) + (a->size - 1) * item_size);
+ return a->ptr + (a->size - 1) * item_size;
}
}
#define git_array_alloc(a) \
((a).size >= (a).asize) ? \
- git_array_grow((git_array_generic_t *)&(a), sizeof(*(a).ptr)) : \
+ git_array_grow(&(a), sizeof(*(a).ptr)) : \
(a).ptr ? &(a).ptr[(a).size++] : NULL
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
diff --git a/src/attr_file.c b/src/attr_file.c
index d880398e8..92702df98 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -79,9 +79,13 @@ int git_attr_file__parse_buffer(
while (!error && *scan) {
/* allocate rule if needed */
- if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
- error = -1;
- break;
+ if (!rule) {
+ if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+ error = -1;
+ break;
+ }
+ rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
+ GIT_ATTR_FNMATCH_ALLOWMACRO;
}
/* parse the next "pattern attr attr attr" line */
@@ -351,8 +355,8 @@ int git_attr_fnmatch__parse(
if (parse_optimized_patterns(spec, pool, *base))
return 0;
- spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
- allow_space = (spec->flags != 0);
+ spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
+ allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
pattern = *base;
@@ -362,7 +366,7 @@ int git_attr_fnmatch__parse(
return GIT_ENOTFOUND;
}
- if (*pattern == '[') {
+ if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
if (strncmp(pattern, "[attr]", 6) == 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
pattern += 6;
@@ -370,7 +374,7 @@ int git_attr_fnmatch__parse(
/* else a character range like [a-e]* which is accepted */
}
- if (*pattern == '!') {
+ if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
pattern++;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index 15bba1c6a..3bc7c6cb8 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -28,6 +28,12 @@
#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
+#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
+#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
+
+#define GIT_ATTR_FNMATCH__INCOMING \
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | \
+ GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
extern const char *git_attr__true;
extern const char *git_attr__false;
diff --git a/src/blob.c b/src/blob.c
index 2e4d5f479..5bb51f7cf 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -105,6 +105,7 @@ static int write_file_stream(
static int write_file_filtered(
git_oid *oid,
+ git_off_t *size,
git_odb *odb,
const char *full_path,
git_vector *filters)
@@ -123,8 +124,11 @@ static int write_file_filtered(
git_buf_free(&source);
/* Write the file to disk if it was properly filtered */
- if (!error)
+ if (!error) {
+ *size = dest.size;
+
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+ }
git_buf_free(&dest);
return error;
@@ -152,21 +156,46 @@ static int write_symlink(
return error;
}
-static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
+int git_blob__create_from_paths(
+ git_oid *oid,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *content_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool try_load_filters)
{
int error;
struct stat st;
git_odb *odb = NULL;
git_off_t size;
+ mode_t mode;
+ git_buf path = GIT_BUF_INIT;
assert(hint_path || !try_load_filters);
- if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
- return error;
+ if (!content_path) {
+ if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
+ return GIT_EBAREREPO;
+
+ if (git_buf_joinpath(
+ &path, git_repository_workdir(repo), hint_path) < 0)
+ return -1;
+
+ content_path = path.ptr;
+ }
+
+ if ((error = git_path_lstat(content_path, &st)) < 0 ||
+ (error = git_repository_odb(&odb, repo)) < 0)
+ goto done;
+
+ if (out_st)
+ memcpy(out_st, &st, sizeof(st));
size = st.st_size;
+ mode = hint_mode ? hint_mode : st.st_mode;
- if (S_ISLNK(st.st_mode)) {
+ if (S_ISLNK(mode)) {
error = write_symlink(oid, odb, content_path, (size_t)size);
} else {
git_vector write_filters = GIT_VECTOR_INIT;
@@ -187,7 +216,8 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
error = write_file_stream(oid, odb, content_path, size);
} else {
/* We need to apply one or more filters */
- error = write_file_filtered(oid, odb, content_path, &write_filters);
+ error = write_file_filtered(
+ oid, &size, odb, content_path, &write_filters);
}
git_filters_free(&write_filters);
@@ -207,34 +237,21 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
*/
}
+done:
+ git_odb_free(odb);
+ git_buf_free(&path);
+
return error;
}
-int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromworkdir(
+ git_oid *oid, git_repository *repo, const char *path)
{
- git_buf full_path = GIT_BUF_INIT;
- const char *workdir;
- int error;
-
- if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
- return error;
-
- workdir = git_repository_workdir(repo);
-
- if (git_buf_joinpath(&full_path, workdir, path) < 0) {
- git_buf_free(&full_path);
- return -1;
- }
-
- error = blob_create_internal(
- oid, repo, git_buf_cstr(&full_path),
- git_buf_cstr(&full_path) + strlen(workdir), true);
-
- git_buf_free(&full_path);
- return error;
+ return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true);
}
-int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromdisk(
+ git_oid *oid, git_repository *repo, const char *path)
{
int error;
git_buf full_path = GIT_BUF_INIT;
@@ -251,8 +268,8 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
if (workdir && !git__prefixcmp(hintpath, workdir))
hintpath += strlen(workdir);
- error = blob_create_internal(
- oid, repo, git_buf_cstr(&full_path), hintpath, true);
+ error = git_blob__create_from_paths(
+ oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
git_buf_free(&full_path);
return error;
@@ -272,12 +289,9 @@ int git_blob_create_fromchunks(
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
- if (git_buf_join_n(
- &path, '/', 3,
- git_repository_path(repo),
- GIT_OBJECTS_DIR,
- "streamed") < 0)
- goto cleanup;
+ if (git_buf_joinpath(
+ &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0)
+ goto cleanup;
content = git__malloc(BUFFER_SIZE);
GITERR_CHECK_ALLOC(content);
@@ -303,7 +317,8 @@ int git_blob_create_fromchunks(
if (git_filebuf_flush(&file) < 0)
goto cleanup;
- error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
+ error = git_blob__create_from_paths(
+ oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
cleanup:
git_buf_free(&path);
diff --git a/src/blob.h b/src/blob.h
index 22e37cc3a..4cd9f1e0c 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -21,4 +21,13 @@ void git_blob__free(void *blob);
int git_blob__parse(void *blob, git_odb_object *obj);
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
+extern int git_blob__create_from_paths(
+ git_oid *out_oid,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *full_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool apply_filters);
+
#endif
diff --git a/src/buf_text.c b/src/buf_text.c
index 443454b5f..472339def 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -262,7 +262,7 @@ bool git_buf_text_gather_stats(
while (scan < end) {
unsigned char c = *scan++;
- if ((c > 0x1F && c < 0x7F) || c > 0x9f)
+ if (c > 0x1F && c != 0x7F)
stats->printable++;
else switch (c) {
case '\0':
diff --git a/src/config_file.c b/src/config_file.c
index 2b0732a13..1d7b4fb38 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -792,6 +792,11 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
}
switch (c) {
+ case 0:
+ set_parse_error(cfg, 0, "Unexpected end-of-line in section header");
+ git_buf_free(&buf);
+ return -1;
+
case '"':
++quote_marks;
continue;
@@ -801,6 +806,12 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
switch (c) {
case '"':
+ if (&line[rpos-1] == last_quote) {
+ set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ git_buf_free(&buf);
+ return -1;
+ }
+
case '\\':
break;
@@ -1293,6 +1304,9 @@ static char *escape_value(const char *ptr)
assert(ptr);
len = strlen(ptr);
+ if (!len)
+ return git__calloc(1, sizeof(char));
+
git_buf_grow(&buf, len);
while (*ptr != '\0') {
diff --git a/src/diff.c b/src/diff.c
index e875d09b3..77dbbd8bc 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -258,6 +258,26 @@ int git_diff_delta__casecmp(const void *a, const void *b)
return val ? val : ((int)da->status - (int)db->status);
}
+GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
+{
+ return delta->old_file.path ?
+ delta->old_file.path : delta->new_file.path;
+}
+
+int git_diff_delta__i2w_cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+int git_diff_delta__i2w_casecmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta)
{
@@ -1276,7 +1296,7 @@ int git_diff__paired_foreach(
git_diff_delta *h2i, *i2w;
size_t i, j, i_max, j_max;
int (*strcomp)(const char *, const char *) = git__strcmp;
- bool icase_mismatch;
+ bool h2i_icase, i2w_icase, icase_mismatch;
i_max = head2idx ? head2idx->deltas.length : 0;
j_max = idx2wd ? idx2wd->deltas.length : 0;
@@ -1291,24 +1311,35 @@ int git_diff__paired_foreach(
* Therefore the main thing we need to do here is make sure the diffs
* are traversed in a compatible order. To do this, we temporarily
* resort a mismatched diff to get the order correct.
+ *
+ * In order to traverse renames in the index->workdir, we need to
+ * ensure that we compare the index name on both sides, so we
+ * always sort by the old name in the i2w list.
*/
+ h2i_icase = head2idx != NULL &&
+ (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
+
+ i2w_icase = idx2wd != NULL &&
+ (idx2wd->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
+
icase_mismatch =
- (head2idx != NULL && idx2wd != NULL &&
- ((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
-
- /* force case-sensitive delta sort */
- if (icase_mismatch) {
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
- git_vector_sort(&head2idx->deltas);
- } else {
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
- git_vector_sort(&idx2wd->deltas);
- }
+ (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
+
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
+ git_vector_sort(&head2idx->deltas);
}
- else if (head2idx != NULL && head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
+
+ if (i2w_icase && !icase_mismatch) {
strcomp = git__strcasecmp;
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
+ git_vector_sort(&idx2wd->deltas);
+ } else if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
+ git_vector_sort(&idx2wd->deltas);
+ }
+
for (i = 0, j = 0; i < i_max || j < j_max; ) {
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
@@ -1332,14 +1363,16 @@ int git_diff__paired_foreach(
}
/* restore case-insensitive delta sort */
- if (icase_mismatch) {
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
- git_vector_sort(&head2idx->deltas);
- } else {
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
- git_vector_sort(&idx2wd->deltas);
- }
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
+ git_vector_sort(&head2idx->deltas);
+ }
+
+ /* restore idx2wd sort by new path */
+ if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas,
+ i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
+ git_vector_sort(&idx2wd->deltas);
}
return 0;
diff --git a/src/diff.h b/src/diff.h
index d09a130bc..bec7e27d7 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -16,6 +16,7 @@
#include "iterator.h"
#include "repository.h"
#include "pool.h"
+#include "odb.h"
#define DIFF_OLD_PREFIX_DEFAULT "a/"
#define DIFF_NEW_PREFIX_DEFAULT "b/"
@@ -81,6 +82,13 @@ extern const char *git_diff_delta__path(const git_diff_delta *delta);
extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta);
+extern int git_diff_delta__format_file_header(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int oid_strlen);
+
extern int git_diff__oid_for_file(
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
@@ -108,5 +116,33 @@ extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
+/*
+ * Sometimes a git_diff_file will have a zero size; this attempts to
+ * fill in the size without loading the blob if possible. If that is
+ * not possible, then it will return the git_odb_object that had to be
+ * loaded and the caller can use it or dispose of it as needed.
+ */
+GIT_INLINE(int) git_diff_file__resolve_zero_size(
+ git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
+{
+ int error;
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ return error;
+
+ error = git_odb__read_header_or_object(
+ odb_obj, &len, &type, odb, &file->oid);
+
+ git_odb_free(odb);
+
+ if (!error)
+ file->size = (git_off_t)len;
+
+ return error;
+}
+
#endif
diff --git a/src/diff_file.c b/src/diff_file.c
index 9d06daafa..bcfef13cd 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -241,19 +241,9 @@ static int diff_file_content_load_blob(git_diff_file_content *fc)
/* if we don't know size, try to peek at object header first */
if (!fc->file->size) {
- git_odb *odb;
- size_t len;
- git_otype type;
-
- if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
- error = git_odb__read_header_or_object(
- &odb_obj, &len, &type, odb, &fc->file->oid);
- git_odb_free(odb);
- }
- if (error)
+ if ((error = git_diff_file__resolve_zero_size(
+ fc->file, &odb_obj, fc->repo)) < 0)
return error;
-
- fc->file->size = len;
}
if (diff_file_content_binary_by_size(fc))
@@ -417,6 +407,9 @@ int git_diff_file_content__load(git_diff_file_content *fc)
void git_diff_file_content__unload(git_diff_file_content *fc)
{
+ if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
+ return;
+
if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
git__free(fc->map.data);
fc->map.data = "";
diff --git a/src/diff_patch.c b/src/diff_patch.c
index 1b4adac03..cc45b6ddb 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -42,7 +42,7 @@ struct git_diff_patch {
git_array_t(diff_patch_hunk) hunks;
git_array_t(diff_patch_line) lines;
size_t oldno, newno;
- size_t content_size;
+ size_t content_size, context_size, header_size;
git_pool flattened;
};
@@ -230,6 +230,10 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
return 0;
+ /* if we are not looking at the hunks and lines, don't do the diff */
+ if (!output->hunk_cb && !output->data_cb)
+ return 0;
+
if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
(error = diff_patch_load(patch, output)) < 0)
return error;
@@ -284,8 +288,8 @@ int git_diff_foreach(
if (diff_required(diff, "git_diff_foreach") < 0)
return -1;
- diff_output_init((git_diff_output *)&xo,
- &diff->opts, file_cb, hunk_cb, data_cb, payload);
+ diff_output_init(
+ &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, &diff->opts);
git_vector_foreach(&diff->deltas, idx, patch.delta) {
@@ -296,10 +300,10 @@ int git_diff_foreach(
if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) {
- error = diff_patch_file_callback(&patch, (git_diff_output *)&xo);
+ error = diff_patch_file_callback(&patch, &xo.output);
if (!error)
- error = diff_patch_generate(&patch, (git_diff_output *)&xo);
+ error = diff_patch_generate(&patch, &xo.output);
git_diff_patch_free(&patch);
}
@@ -437,7 +441,7 @@ int git_diff_blobs(
memset(&xo, 0, sizeof(xo));
diff_output_init(
- (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && new_path)
@@ -448,7 +452,7 @@ int git_diff_blobs(
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
- git_diff_patch_free((git_diff_patch *)&pd);
+ git_diff_patch_free(&pd.patch);
return error;
}
@@ -473,7 +477,7 @@ int git_diff_patch_from_blobs(
memset(&xo, 0, sizeof(xo));
- diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blobs(
@@ -549,7 +553,7 @@ int git_diff_blob_to_buffer(
memset(&xo, 0, sizeof(xo));
diff_output_init(
- (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && buf_path)
@@ -560,7 +564,7 @@ int git_diff_blob_to_buffer(
error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
- git_diff_patch_free((git_diff_patch *)&pd);
+ git_diff_patch_free(&pd.patch);
return error;
}
@@ -586,7 +590,7 @@ int git_diff_patch_from_blob_and_buffer(
memset(&xo, 0, sizeof(xo));
- diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blob_and_buffer(
@@ -638,13 +642,13 @@ int git_diff_get_patch(
if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
return error;
- diff_output_to_patch((git_diff_output *)&xo, patch);
+ diff_output_to_patch(&xo.output, patch);
git_xdiff_init(&xo, &diff->opts);
- error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
+ error = diff_patch_file_callback(patch, &xo.output);
if (!error)
- error = diff_patch_generate(patch, (git_diff_output *)&xo);
+ error = diff_patch_generate(patch, &xo.output);
if (!error) {
/* if cumulative diff size is < 0.5 total size, flatten the patch */
@@ -806,6 +810,39 @@ notfound:
return diff_error_outofrange(thing);
}
+size_t git_diff_patch_size(
+ git_diff_patch *patch,
+ int include_context,
+ int include_hunk_headers,
+ int include_file_headers)
+{
+ size_t out;
+
+ assert(patch);
+
+ out = patch->content_size;
+
+ if (!include_context)
+ out -= patch->context_size;
+
+ if (include_hunk_headers)
+ out += patch->header_size;
+
+ if (include_file_headers) {
+ git_buf file_header = GIT_BUF_INIT;
+
+ if (git_diff_delta__format_file_header(
+ &file_header, patch->delta, NULL, NULL, 0) < 0)
+ giterr_clear();
+ else
+ out += git_buf_len(&file_header);
+
+ git_buf_free(&file_header);
+ }
+
+ return out;
+}
+
git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
{
return patch->diff;
@@ -900,6 +937,8 @@ static int diff_patch_hunk_cb(
hunk->header[header_len] = '\0';
hunk->header_len = header_len;
+ patch->header_size += header_len;
+
hunk->line_start = git_array_size(patch->lines);
hunk->line_count = 0;
@@ -920,6 +959,7 @@ static int diff_patch_line_cb(
git_diff_patch *patch = payload;
diff_patch_hunk *hunk;
diff_patch_line *line;
+ const char *content_end = content + content_len;
GIT_UNUSED(delta);
GIT_UNUSED(range);
@@ -934,34 +974,43 @@ static int diff_patch_line_cb(
line->len = content_len;
line->origin = line_origin;
- patch->content_size += content_len;
-
/* do some bookkeeping so we can provide old/new line numbers */
- for (line->lines = 0; content_len > 0; --content_len) {
+ line->lines = 0;
+ while (content < content_end)
if (*content++ == '\n')
++line->lines;
- }
+
+ patch->content_size += content_len;
switch (line_origin) {
case GIT_DIFF_LINE_ADDITION:
+ patch->content_size += 1;
case GIT_DIFF_LINE_DEL_EOFNL:
line->oldno = -1;
line->newno = patch->newno;
patch->newno += line->lines;
break;
case GIT_DIFF_LINE_DELETION:
+ patch->content_size += 1;
case GIT_DIFF_LINE_ADD_EOFNL:
line->oldno = patch->oldno;
line->newno = -1;
patch->oldno += line->lines;
break;
- default:
+ case GIT_DIFF_LINE_CONTEXT:
+ patch->content_size += 1;
+ patch->context_size += 1;
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ patch->context_size += content_len;
line->oldno = patch->oldno;
line->newno = patch->newno;
patch->oldno += line->lines;
patch->newno += line->lines;
break;
+ default:
+ assert(false);
+ break;
}
hunk->line_count++;
diff --git a/src/diff_print.c b/src/diff_print.c
index 0de548813..4ddd72443 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -98,12 +98,12 @@ static int diff_print_one_compact(
if (delta->old_file.path != delta->new_file.path &&
strcomp(delta->old_file.path,delta->new_file.path) != 0)
- git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
+ git_buf_printf(out, "%c\t%s%c %s%c\n", code,
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (delta->old_file.mode != delta->new_file.mode &&
delta->old_file.mode != 0 && delta->new_file.mode != 0)
- git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
- delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+ git_buf_printf(out, "%c\t%s%c %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (old_suffix != ' ')
git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
else
@@ -198,13 +198,13 @@ int git_diff_print_raw(
return error;
}
-static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
+static int diff_print_oid_range(
+ git_buf *out, const git_diff_delta *delta, int oid_strlen)
{
- git_buf *out = pi->buf;
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
- git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
- git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
+ git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid);
+ git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid);
/* TODO: Match git diff more closely */
if (delta->old_file.mode == delta->new_file.mode) {
@@ -228,52 +228,78 @@ static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta
return 0;
}
-static int diff_print_patch_file(
- const git_diff_delta *delta, float progress, void *data)
+static int diff_delta_format_with_paths(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ const char *template)
{
- diff_print_info *pi = data;
- const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
const char *oldpath = delta->old_file.path;
- const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
const char *newpath = delta->new_file.path;
- uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
- GIT_UNUSED(progress);
+ if (git_oid_iszero(&delta->old_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+ if (git_oid_iszero(&delta->new_file.oid)) {
+ newpfx = "";
+ newpath = "/dev/null";
+ }
- if (S_ISDIR(delta->new_file.mode) ||
- delta->status == GIT_DELTA_UNMODIFIED ||
- delta->status == GIT_DELTA_IGNORED ||
- (delta->status == GIT_DELTA_UNTRACKED &&
- (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
- return 0;
+ return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
+}
+int git_diff_delta__format_file_header(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int oid_strlen)
+{
if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
if (!newpfx)
newpfx = DIFF_NEW_PREFIX_DEFAULT;
+ if (!oid_strlen)
+ oid_strlen = GIT_ABBREV_DEFAULT + 1;
- git_buf_clear(pi->buf);
- git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
+ git_buf_clear(out);
+
+ git_buf_printf(out, "diff --git %s%s %s%s\n",
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
- if (diff_print_oid_range(pi, delta) < 0)
+ if (diff_print_oid_range(out, delta, oid_strlen) < 0)
return -1;
- if (git_oid_iszero(&delta->old_file.oid)) {
- oldpfx = "";
- oldpath = "/dev/null";
- }
- if (git_oid_iszero(&delta->new_file.oid)) {
- newpfx = "";
- newpath = "/dev/null";
- }
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ diff_delta_format_with_paths(
+ out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
- git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
- git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
- }
+ return git_buf_oom(out) ? -1 : 0;
+}
- if (git_buf_oom(pi->buf))
+static int diff_print_patch_file(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ const char *oldpfx =
+ pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ const char *newpfx =
+ pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+ uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
+
+ GIT_UNUSED(progress);
+
+ if (S_ISDIR(delta->new_file.mode) ||
+ delta->status == GIT_DELTA_UNMODIFIED ||
+ delta->status == GIT_DELTA_IGNORED ||
+ (delta->status == GIT_DELTA_UNTRACKED &&
+ (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
+ return 0;
+
+ if (git_diff_delta__format_file_header(
+ pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
return -1;
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
@@ -284,10 +310,10 @@ static int diff_print_patch_file(
return 0;
git_buf_clear(pi->buf);
- git_buf_printf(
- pi->buf, "Binary files %s%s and %s%s differ\n",
- oldpfx, oldpath, newpfx, newpath);
- if (git_buf_oom(pi->buf))
+
+ if (diff_delta_format_with_paths(
+ pi->buf, delta, oldpfx, newpfx,
+ "Binary files %s%s and %s%s differ\n") < 0)
return -1;
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
diff --git a/src/diff_tform.c b/src/diff_tform.c
index b137bd319..ba35d3c14 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -408,57 +408,99 @@ GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
return (idx & 1) ? &delta->new_file : &delta->old_file;
}
-static int similarity_calc(
- git_diff_list *diff,
+typedef struct {
+ size_t idx;
+ git_iterator_type_t src;
+ git_repository *repo;
+ git_diff_file *file;
+ git_buf data;
+ git_odb_object *odb_obj;
+ git_blob *blob;
+} similarity_info;
+
+static int similarity_init(
+ similarity_info *info, git_diff_list *diff, size_t file_idx)
+{
+ info->idx = file_idx;
+ info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
+ info->repo = diff->repo;
+ info->file = similarity_get_file(diff, file_idx);
+ info->odb_obj = NULL;
+ info->blob = NULL;
+ git_buf_init(&info->data, 0);
+
+ if (info->file->size > 0)
+ return 0;
+
+ return git_diff_file__resolve_zero_size(
+ info->file, &info->odb_obj, info->repo);
+}
+
+static int similarity_sig(
+ similarity_info *info,
const git_diff_find_options *opts,
- size_t file_idx,
void **cache)
{
int error = 0;
- git_diff_file *file = similarity_get_file(diff, file_idx);
- git_iterator_type_t src = (file_idx & 1) ? diff->new_src : diff->old_src;
-
- if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
- git_buf path = GIT_BUF_INIT;
-
- /* TODO: apply wd-to-odb filters to file data if necessary */
+ git_diff_file *file = info->file;
+ if (info->src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_buf_joinpath(
- &path, git_repository_workdir(diff->repo), file->path)) < 0)
+ &info->data, git_repository_workdir(info->repo), file->path)) < 0)
return error;
/* if path is not a regular file, just skip this item */
- if (git_path_isfile(path.ptr))
- error = opts->metric->file_signature(
- &cache[file_idx], file, path.ptr, opts->metric->payload);
+ if (!git_path_isfile(info->data.ptr))
+ return 0;
- git_buf_free(&path);
- } else { /* compute hashsig from blob buffer */
- git_blob *blob = NULL;
- git_off_t blobsize;
+ /* TODO: apply wd-to-odb filters to file data if necessary */
- /* TODO: add max size threshold a la diff? */
+ error = opts->metric->file_signature(
+ &cache[info->idx], info->file,
+ info->data.ptr, opts->metric->payload);
+ } else {
+ /* if we didn't initially know the size, we might have an odb_obj
+ * around from earlier, so convert that, otherwise load the blob now
+ */
+ if (info->odb_obj != NULL)
+ error = git_object__from_odb_object(
+ (git_object **)&info->blob, info->repo,
+ info->odb_obj, GIT_OBJ_BLOB);
+ else
+ error = git_blob_lookup(&info->blob, info->repo, &file->oid);
- if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
+ if (error < 0) {
/* if lookup fails, just skip this item in similarity calc */
giterr_clear();
- return 0;
- }
+ } else {
+ size_t sz;
- blobsize = git_blob_rawsize(blob);
- if (!git__is_sizet(blobsize)) /* ? what to do ? */
- blobsize = (size_t)-1;
+ /* index size may not be actual blob size if filtered */
+ if (file->size != git_blob_rawsize(info->blob))
+ file->size = git_blob_rawsize(info->blob);
- error = opts->metric->buffer_signature(
- &cache[file_idx], file, git_blob_rawcontent(blob),
- (size_t)blobsize, opts->metric->payload);
+ sz = (size_t)(git__is_sizet(file->size) ? file->size : -1);
- git_blob_free(blob);
+ error = opts->metric->buffer_signature(
+ &cache[info->idx], info->file,
+ git_blob_rawcontent(info->blob), sz, opts->metric->payload);
+ }
}
return error;
}
+static void similarity_unload(similarity_info *info)
+{
+ if (info->odb_obj)
+ git_odb_object_free(info->odb_obj);
+
+ if (info->blob)
+ git_blob_free(info->blob);
+ else
+ git_buf_free(&info->data);
+}
+
#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
/* - score < 0 means files cannot be compared
@@ -476,6 +518,8 @@ static int similarity_measure(
git_diff_file *a_file = similarity_get_file(diff, a_idx);
git_diff_file *b_file = similarity_get_file(diff, b_idx);
bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
+ int error = 0;
+ similarity_info a_info, b_info;
*score = -1;
@@ -510,19 +554,44 @@ static int similarity_measure(
return 0;
}
+ memset(&a_info, 0, sizeof(a_info));
+ memset(&b_info, 0, sizeof(b_info));
+
+ /* set up similarity data (will try to update missing file sizes) */
+ if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
+ return error;
+ if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
+ goto cleanup;
+
+ /* check if file sizes are nowhere near each other */
+ if (a_file->size > 127 &&
+ b_file->size > 127 &&
+ (a_file->size > (b_file->size << 3) ||
+ b_file->size > (a_file->size << 3)))
+ goto cleanup;
+
/* update signature cache if needed */
- if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
- return -1;
- if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
- return -1;
+ if (!cache[a_idx]) {
+ if ((error = similarity_sig(&a_info, opts, cache)) < 0)
+ goto cleanup;
+ }
+ if (!cache[b_idx]) {
+ if ((error = similarity_sig(&b_info, opts, cache)) < 0)
+ goto cleanup;
+ }
- /* some metrics may not wish to process this file (too big / too small) */
- if (!cache[a_idx] || !cache[b_idx])
- return 0;
+ /* calculate similarity provided that the metric choose to process
+ * both the a and b files (some may not if file is too big, etc).
+ */
+ if (cache[a_idx] && cache[b_idx])
+ error = opts->metric->similarity(
+ score, cache[a_idx], cache[b_idx], opts->metric->payload);
- /* compare signatures */
- return opts->metric->similarity(
- score, cache[a_idx], cache[b_idx], opts->metric->payload);
+cleanup:
+ similarity_unload(&a_info);
+ similarity_unload(&b_info);
+
+ return error;
}
static int calc_self_similarity(
@@ -590,11 +659,13 @@ static bool is_rename_target(
return false;
case GIT_DELTA_UNTRACKED:
- case GIT_DELTA_IGNORED:
if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
return false;
break;
+ case GIT_DELTA_IGNORED:
+ return false;
+
default: /* all other status values should be checked */
break;
}
@@ -691,25 +762,30 @@ int git_diff_find_similar(
git_diff_list *diff,
git_diff_find_options *given_opts)
{
- size_t i, j, sigcache_size;
+ size_t s, t;
int error = 0, similarity;
- git_diff_delta *from, *to;
+ git_diff_delta *src, *tgt;
git_diff_find_options opts;
- size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0;
+ size_t num_deltas, num_srcs = 0, num_tgts = 0;
+ size_t tried_srcs = 0, tried_tgts = 0;
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
void **sigcache; /* cache of similarity metric file signatures */
- diff_find_match *match_srcs = NULL, *match_tgts = NULL, *best_match;
+ diff_find_match *tgt2src = NULL;
+ diff_find_match *src2tgt = NULL;
+ diff_find_match *tgt2src_copy = NULL;
+ diff_find_match *best_match;
git_diff_file swap;
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
return error;
+ num_deltas = diff->deltas.length;
+
/* TODO: maybe abort if deltas.length > rename_limit ??? */
- if (!git__is_uint32(diff->deltas.length))
+ if (!git__is_uint32(num_deltas))
return 0;
- sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */
- sigcache = git__calloc(sigcache_size, sizeof(void *));
+ sigcache = git__calloc(num_deltas * 2, sizeof(void *));
GITERR_CHECK_ALLOC(sigcache);
/* Label rename sources and targets
@@ -717,11 +793,11 @@ int git_diff_find_similar(
* This will also set self-similarity scores for MODIFIED files and
* mark them for splitting if break-rewrites is enabled
*/
- git_vector_foreach(&diff->deltas, i, to) {
- if (is_rename_source(diff, &opts, i, sigcache))
+ git_vector_foreach(&diff->deltas, t, tgt) {
+ if (is_rename_source(diff, &opts, t, sigcache))
++num_srcs;
- if (is_rename_target(diff, &opts, i, sigcache))
+ if (is_rename_target(diff, &opts, t, sigcache))
++num_tgts;
}
@@ -729,10 +805,15 @@ int git_diff_find_similar(
if (!num_srcs || !num_tgts)
goto cleanup;
- match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(match_tgts);
- match_srcs = git__calloc(diff->deltas.length, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(match_srcs);
+ src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(src2tgt);
+ tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(tgt2src);
+
+ if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
+ tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(tgt2src_copy);
+ }
/*
* Find best-fit matches for rename / copy candidates
@@ -741,47 +822,61 @@ int git_diff_find_similar(
find_best_matches:
tried_tgts = num_bumped = 0;
- git_vector_foreach(&diff->deltas, i, to) {
+ git_vector_foreach(&diff->deltas, t, tgt) {
/* skip things that are not rename targets */
- if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
continue;
tried_srcs = 0;
- git_vector_foreach(&diff->deltas, j, from) {
+ git_vector_foreach(&diff->deltas, s, src) {
/* skip things that are not rename sources */
- if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
+ if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
continue;
/* calculate similarity for this pair and find best match */
- if (i == j)
+ if (s == t)
similarity = -1; /* don't measure self-similarity here */
else if ((error = similarity_measure(
- &similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0)
+ &similarity, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
goto cleanup;
- /* if this pairing is better for the src and the tgt, keep it */
- if (similarity > 0 &&
- match_tgts[i].similarity < (uint32_t)similarity &&
- match_srcs[j].similarity < (uint32_t)similarity)
+ if (similarity < 0)
+ continue;
+
+ /* is this a better rename? */
+ if (tgt2src[t].similarity < (uint32_t)similarity &&
+ src2tgt[s].similarity < (uint32_t)similarity)
{
- if (match_tgts[i].similarity > 0) {
- match_tgts[match_srcs[j].idx].similarity = 0;
- match_srcs[match_tgts[i].idx].similarity = 0;
- ++num_bumped;
+ /* eject old mapping */
+ if (src2tgt[s].similarity > 0) {
+ tgt2src[src2tgt[s].idx].similarity = 0;
+ num_bumped++;
+ }
+ if (tgt2src[t].similarity > 0) {
+ src2tgt[tgt2src[t].idx].similarity = 0;
+ num_bumped++;
}
- match_tgts[i].similarity = (uint32_t)similarity;
- match_tgts[i].idx = (uint32_t)j;
+ /* write new mapping */
+ tgt2src[t].idx = s;
+ tgt2src[t].similarity = (uint32_t)similarity;
+ src2tgt[s].idx = t;
+ src2tgt[s].similarity = (uint32_t)similarity;
+ }
- match_srcs[j].similarity = (uint32_t)similarity;
- match_srcs[j].idx = (uint32_t)i;
+ /* keep best absolute match for copies */
+ if (tgt2src_copy != NULL &&
+ tgt2src_copy[t].similarity < (uint32_t)similarity)
+ {
+ tgt2src_copy[t].idx = s;
+ tgt2src_copy[t].similarity = (uint32_t)similarity;
}
if (++tried_srcs >= num_srcs)
break;
- /* cap on maximum targets we'll examine (per "to" file) */
+ /* cap on maximum targets we'll examine (per "tgt" file) */
if (tried_srcs > opts.rename_limit)
break;
}
@@ -799,18 +894,21 @@ find_best_matches:
tried_tgts = 0;
- git_vector_foreach(&diff->deltas, i, to) {
+ git_vector_foreach(&diff->deltas, t, tgt) {
/* skip things that are not rename targets */
- if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
continue;
/* check if this delta was the target of a similarity */
- best_match = &match_tgts[i];
- if (!best_match->similarity)
+ if (tgt2src[t].similarity)
+ best_match = &tgt2src[t];
+ else if (tgt2src_copy && tgt2src_copy[t].similarity)
+ best_match = &tgt2src_copy[t];
+ else
continue;
- j = best_match->idx;
- from = GIT_VECTOR_GET(&diff->deltas, j);
+ s = best_match->idx;
+ src = GIT_VECTOR_GET(&diff->deltas, s);
/* possible scenarios:
* 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
@@ -820,101 +918,112 @@ find_best_matches:
* 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
*/
- if (from->status == GIT_DELTA_DELETED) {
+ if (src->status == GIT_DELTA_DELETED) {
- if (delta_is_new_only(to)) {
+ if (delta_is_new_only(tgt)) {
if (best_match->similarity < opts.rename_threshold)
continue;
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
- from->flags |= GIT_DIFF_FLAG__TO_DELETE;
+ src->flags |= GIT_DIFF_FLAG__TO_DELETE;
num_rewrites++;
} else {
- assert(delta_is_split(to));
+ assert(delta_is_split(tgt));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
- memcpy(&swap, &to->old_file, sizeof(swap));
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
- from->status = GIT_DELTA_DELETED;
- memcpy(&from->old_file, &swap, sizeof(from->old_file));
- memset(&from->new_file, 0, sizeof(from->new_file));
- from->new_file.path = from->old_file.path;
- from->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ src->status = GIT_DELTA_DELETED;
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
+ memset(&src->new_file, 0, sizeof(src->new_file));
+ src->new_file.path = src->old_file.path;
+ src->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
num_updates++;
+
+ if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
+ }
}
}
- else if (delta_is_split(from)) {
+ else if (delta_is_split(src)) {
- if (delta_is_new_only(to)) {
+ if (delta_is_new_only(tgt)) {
if (best_match->similarity < opts.rename_threshold)
continue;
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
- from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
+ src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
- memset(&from->old_file, 0, sizeof(from->old_file));
- from->old_file.path = from->new_file.path;
- from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ memset(&src->old_file, 0, sizeof(src->old_file));
+ src->old_file.path = src->new_file.path;
+ src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
- from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
num_updates++;
} else {
- assert(delta_is_split(from));
+ assert(delta_is_split(src));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
- memcpy(&swap, &to->old_file, sizeof(swap));
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
num_updates++;
- memcpy(&from->old_file, &swap, sizeof(from->old_file));
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
/* if we've just swapped the new element into the correct
* place, clear the SPLIT flag
*/
- if (match_tgts[j].idx == i &&
- match_tgts[j].similarity >
+ if (tgt2src[s].idx == t &&
+ tgt2src[s].similarity >
opts.rename_from_rewrite_threshold) {
-
- from->status = GIT_DELTA_RENAMED;
- from->similarity = match_tgts[j].similarity;
- match_tgts[j].similarity = 0;
- from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ src->status = GIT_DELTA_RENAMED;
+ src->similarity = tgt2src[s].similarity;
+ tgt2src[s].similarity = 0;
+ src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
}
/* otherwise, if we just overwrote a source, update mapping */
- else if (j > i && match_srcs[i].similarity > 0) {
- match_tgts[match_srcs[i].idx].idx = (uint32_t)j;
+ else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
}
num_updates++;
}
}
- else if (delta_is_new_only(to)) {
- if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
- best_match->similarity < opts.copy_threshold)
+ else if (delta_is_new_only(tgt)) {
+ if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES))
continue;
- to->status = GIT_DELTA_COPIED;
- to->similarity = best_match->similarity;
- memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+ if (tgt2src_copy[t].similarity < opts.copy_threshold)
+ continue;
+
+ /* always use best possible source for copy */
+ best_match = &tgt2src_copy[t];
+ src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
+
+ tgt->status = GIT_DELTA_COPIED;
+ tgt->similarity = best_match->similarity;
+ memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
num_updates++;
}
@@ -930,12 +1039,13 @@ find_best_matches:
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
cleanup:
- git__free(match_srcs);
- git__free(match_tgts);
+ git__free(tgt2src);
+ git__free(src2tgt);
+ git__free(tgt2src_copy);
- for (i = 0; i < sigcache_size; ++i) {
- if (sigcache[i] != NULL)
- opts.metric->free_signature(sigcache[i], opts.metric->payload);
+ for (t = 0; t < num_deltas * 2; ++t) {
+ if (sigcache[t] != NULL)
+ opts.metric->free_signature(sigcache[t], opts.metric->payload);
}
git__free(sigcache);
diff --git a/src/fileops.c b/src/fileops.c
index db53d4fce..3a5a53074 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -58,9 +58,9 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
int fd;
#ifdef GIT_WIN32
- wchar_t buf[GIT_WIN_PATH];
+ git_win32_path buf;
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path_from_c(buf, path);
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
O_EXCL | O_BINARY | O_CLOEXEC, mode);
#else
@@ -147,6 +147,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
{
+ int error = 0;
git_file fd;
struct stat st;
bool changed = false;
@@ -156,11 +157,15 @@ int git_futils_readbuffer_updated(
if (updated != NULL)
*updated = 0;
- if ((fd = git_futils_open_ro(path)) < 0)
- return fd;
+ if (p_stat(path, &st) < 0) {
+ error = errno;
+ giterr_set(GITERR_OS, "Failed to stat '%s'", path);
+ if (error == ENOENT || error == ENOTDIR)
+ return GIT_ENOTFOUND;
+ return -1;
+ }
- if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
- p_close(fd);
+ if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
return -1;
}
@@ -177,7 +182,6 @@ int git_futils_readbuffer_updated(
changed = true;
if (!changed) {
- p_close(fd);
return 0;
}
@@ -186,6 +190,9 @@ int git_futils_readbuffer_updated(
if (size != NULL)
*size = (size_t)st.st_size;
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
+
if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
p_close(fd);
return -1;
@@ -222,6 +229,7 @@ int git_futils_writebuffer(
if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
giterr_set(GITERR_OS, "Could not write to '%s'", path);
(void)p_close(fd);
+ return error;
}
if ((error = p_close(fd)) < 0)
@@ -630,13 +638,11 @@ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
int git_futils_dirs_global_init(void)
{
git_futils_dir_t i;
- git_buf *path;
+ const git_buf *path;
int error = 0;
- for (i = 0; i < GIT_FUTILS_DIR__MAX; i++) {
- if ((error = git_futils_dirs_get(&path, i)) < 0)
- break;
- }
+ for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
+ error = git_futils_dirs_get(&path, i);
return error;
}
diff --git a/src/fileops.h b/src/fileops.h
index d23ebaffb..5adedfc57 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -244,7 +244,7 @@ extern mode_t git_futils_canonical_mode(mode_t raw_mode);
* @param out buffer to populate with the mapping information.
* @param fd open descriptor to configure the mapping from.
* @param begin first byte to map, this should be page aligned.
- * @param end number of bytes to map.
+ * @param len number of bytes to map.
* @return
* - 0 on success;
* - -1 on error.
@@ -278,7 +278,7 @@ extern void git_futils_mmap_free(git_map *map);
/**
* Find a "global" file (i.e. one in a user's home directory).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
@@ -287,7 +287,7 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
/**
* Find an "XDG" file (i.e. one in user's XDG config path).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
@@ -296,7 +296,7 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
/**
* Find a "system" file (i.e. one shared for all users of the system).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
diff --git a/src/hashsig.c b/src/hashsig.c
index ab8d8b3f0..109f966ba 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -13,12 +13,15 @@ typedef uint64_t hashsig_state;
#define HASHSIG_SCALE 100
-#define HASHSIG_HASH_WINDOW 32
-#define HASHSIG_HASH_START 0
+#define HASHSIG_MAX_RUN 80
+#define HASHSIG_HASH_START 0x012345678ABCDEF0LL
#define HASHSIG_HASH_SHIFT 5
-#define HASHSIG_HASH_MASK 0x7FFFFFFF
+
+#define HASHSIG_HASH_MIX(S,CH) \
+ (S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
+#define HASHSIG_HEAP_MIN_SIZE 4
typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
@@ -28,14 +31,6 @@ typedef struct {
hashsig_t values[HASHSIG_HEAP_SIZE];
} hashsig_heap;
-typedef struct {
- hashsig_state state, shift_n;
- char window[HASHSIG_HASH_WINDOW];
- int win_len, win_pos, saw_lf;
-} hashsig_in_progress;
-
-#define HASHSIG_IN_PROGRESS_INIT { HASHSIG_HASH_START, 1, {0}, 0, 0, 1 }
-
struct git_hashsig {
hashsig_heap mins;
hashsig_heap maxs;
@@ -43,8 +38,8 @@ struct git_hashsig {
int considered;
};
-#define HEAP_LCHILD_OF(I) (((I)*2)+1)
-#define HEAP_RCHILD_OF(I) (((I)*2)+2)
+#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
+#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
#define HEAP_PARENT_OF(I) (((I)-1)>>1)
static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
@@ -115,134 +110,109 @@ static void hashsig_heap_sort(hashsig_heap *h)
static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
{
- /* if heap is full, pop top if new element should replace it */
- if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
- h->size--;
- h->values[0] = h->values[h->size];
- hashsig_heap_down(h, 0);
- }
-
/* if heap is not full, insert new element */
if (h->size < h->asize) {
h->values[h->size++] = val;
hashsig_heap_up(h, h->size - 1);
}
-}
-
-GIT_INLINE(bool) hashsig_include_char(
- char ch, git_hashsig_option_t opt, int *saw_lf)
-{
- if ((opt & GIT_HASHSIG_IGNORE_WHITESPACE) && git__isspace(ch))
- return false;
-
- if (opt & GIT_HASHSIG_SMART_WHITESPACE) {
- if (ch == '\r' || (*saw_lf && git__isspace(ch)))
- return false;
- *saw_lf = (ch == '\n');
+ /* if heap is full, pop top if new element should replace it */
+ else if (h->cmp(&val, &h->values[0], NULL) > 0) {
+ h->size--;
+ h->values[0] = h->values[h->size];
+ hashsig_heap_down(h, 0);
}
- return true;
}
-static void hashsig_initial_window(
- git_hashsig *sig,
- const char **data,
- size_t size,
- hashsig_in_progress *prog)
-{
- hashsig_state state, shift_n;
- int win_len;
- const char *scan, *end;
-
- /* init until we have processed at least HASHSIG_HASH_WINDOW data */
-
- if (prog->win_len >= HASHSIG_HASH_WINDOW)
- return;
-
- state = prog->state;
- win_len = prog->win_len;
- shift_n = prog->shift_n;
-
- scan = *data;
- end = scan + size;
-
- while (scan < end && win_len < HASHSIG_HASH_WINDOW) {
- char ch = *scan++;
-
- if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
- continue;
-
- state = (state * HASHSIG_HASH_SHIFT + ch) & HASHSIG_HASH_MASK;
-
- if (!win_len)
- shift_n = 1;
- else
- shift_n = (shift_n * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
-
- prog->window[win_len++] = ch;
- }
-
- /* insert initial hash if we just finished */
+typedef struct {
+ int use_ignores;
+ uint8_t ignore_ch[256];
+} hashsig_in_progress;
- if (win_len == HASHSIG_HASH_WINDOW) {
- hashsig_heap_insert(&sig->mins, (hashsig_t)state);
- hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- sig->considered = 1;
+static void hashsig_in_progress_init(
+ hashsig_in_progress *prog, git_hashsig *sig)
+{
+ int i;
+
+ switch (sig->opt) {
+ case GIT_HASHSIG_IGNORE_WHITESPACE:
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace_nonlf(i);
+ prog->use_ignores = 1;
+ break;
+ case GIT_HASHSIG_SMART_WHITESPACE:
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace(i);
+ prog->use_ignores = 1;
+ break;
+ default:
+ memset(prog, 0, sizeof(*prog));
+ break;
}
-
- prog->state = state;
- prog->win_len = win_len;
- prog->shift_n = shift_n;
-
- *data = scan;
}
+#define HASHSIG_IN_PROGRESS_INIT { 1 }
+
static int hashsig_add_hashes(
git_hashsig *sig,
- const char *data,
+ const uint8_t *data,
size_t size,
hashsig_in_progress *prog)
{
- const char *scan = data, *end = data + size;
- hashsig_state state, shift_n, rmv;
-
- if (prog->win_len < HASHSIG_HASH_WINDOW)
- hashsig_initial_window(sig, &scan, size, prog);
-
- state = prog->state;
- shift_n = prog->shift_n;
-
- /* advance window, adding new chars and removing old */
-
- for (; scan < end; ++scan) {
- char ch = *scan;
-
- if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
- continue;
-
- rmv = shift_n * prog->window[prog->win_pos];
+ const uint8_t *scan = data, *end = data + size;
+ hashsig_state state = HASHSIG_HASH_START;
+ int use_ignores = prog->use_ignores, len;
+ uint8_t ch;
+
+ while (scan < end) {
+ state = HASHSIG_HASH_START;
+
+ for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
+ ch = *scan;
+
+ if (use_ignores)
+ for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
+ ++scan;
+ else if (sig->opt != GIT_HASHSIG_NORMAL)
+ for (; scan < end && ch == '\r'; ch = *scan)
+ ++scan;
+
+ /* peek at next character to decide what to do next */
+ if (sig->opt == GIT_HASHSIG_SMART_WHITESPACE)
+ use_ignores = (ch == '\n');
+
+ if (scan >= end)
+ break;
+ ++scan;
+
+ /* check run terminator */
+ if (ch == '\n' || ch == '\0')
+ break;
+
+ ++len;
+ HASHSIG_HASH_MIX(state, ch);
+ }
- state = (state - rmv) & HASHSIG_HASH_MASK;
- state = (state * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
- state = (state + ch) & HASHSIG_HASH_MASK;
+ if (len > 0) {
+ hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+ hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- hashsig_heap_insert(&sig->mins, (hashsig_t)state);
- hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- sig->considered++;
+ sig->considered++;
- prog->window[prog->win_pos] = ch;
- prog->win_pos = (prog->win_pos + 1) % HASHSIG_HASH_WINDOW;
+ while (scan < end && (*scan == '\n' || !*scan))
+ ++scan;
+ }
}
- prog->state = state;
+ prog->use_ignores = use_ignores;
return 0;
}
static int hashsig_finalize_hashes(git_hashsig *sig)
{
- if (sig->mins.size < HASHSIG_HEAP_SIZE) {
+ if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE) {
giterr_set(GITERR_INVALID,
"File too small for similarity signature calculation");
return GIT_EBUFS;
@@ -274,11 +244,13 @@ int git_hashsig_create(
git_hashsig_option_t opts)
{
int error;
- hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+ hashsig_in_progress prog;
git_hashsig *sig = hashsig_alloc(opts);
GITERR_CHECK_ALLOC(sig);
- error = hashsig_add_hashes(sig, buf, buflen, &prog);
+ hashsig_in_progress_init(&prog, sig);
+
+ error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
if (!error)
error = hashsig_finalize_hashes(sig);
@@ -296,10 +268,10 @@ int git_hashsig_create_fromfile(
const char *path,
git_hashsig_option_t opts)
{
- char buf[4096];
+ uint8_t buf[0x1000];
ssize_t buflen = 0;
int error = 0, fd;
- hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+ hashsig_in_progress prog;
git_hashsig *sig = hashsig_alloc(opts);
GITERR_CHECK_ALLOC(sig);
@@ -308,6 +280,8 @@ int git_hashsig_create_fromfile(
return fd;
}
+ hashsig_in_progress_init(&prog, sig);
+
while (!error) {
if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
if ((error = (int)buflen) < 0)
@@ -362,6 +336,12 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
{
- return (hashsig_heap_compare(&a->mins, &b->mins) +
- hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
+ /* if we have fewer than the maximum number of elements, then just use
+ * one array since the two arrays will be the same
+ */
+ if (a->mins.size < HASHSIG_HEAP_SIZE)
+ return hashsig_heap_compare(&a->mins, &b->mins);
+ else
+ return (hashsig_heap_compare(&a->mins, &b->mins) +
+ hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
}
diff --git a/src/ignore.c b/src/ignore.c
index cc90b0c61..0c35d0431 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -37,7 +37,7 @@ static int parse_ignore_file(
GITERR_CHECK_ALLOC(match);
}
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan)))
@@ -159,17 +159,36 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
{
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
return -1;
- else
- return push_ignore_file(
- ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+
+ return push_ignore_file(
+ ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
}
int git_ignore__pop_dir(git_ignores *ign)
{
if (ign->ign_path.length > 0) {
git_attr_file *file = git_vector_last(&ign->ign_path);
- if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
+ const char *start, *end, *scan;
+ size_t keylen;
+
+ /* - ign->dir looks something like "a/b" (or "a/b/c/d")
+ * - file->key looks something like "0#a/b/.gitignore
+ *
+ * We are popping the last directory off ign->dir. We also want to
+ * remove the file from the vector if the directory part of the key
+ * matches the ign->dir path. We need to test if the "a/b" part of
+ * the file key matches the path we are about to pop.
+ */
+
+ for (start = end = scan = &file->key[2]; *scan; ++scan)
+ if (*scan == '/')
+ end = scan; /* point 'end' to last '/' in key */
+ keylen = (end - start) + 1;
+
+ if (ign->dir.size >= keylen &&
+ !memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen))
git_vector_pop(&ign->ign_path);
+
git_buf_rtruncate_at_char(&ign->dir, '/');
}
return 0;
@@ -298,12 +317,9 @@ int git_ignore_path_is_ignored(
path.full.size = (tail - path.full.ptr);
path.is_dir = (tail == end) ? full_is_dir : true;
- /* update ignores for new path fragment */
- if (path.basename == path.path)
- error = git_ignore__for_path(repo, path.path, &ignores);
- else
- error = git_ignore__push_dir(&ignores, path.basename);
- if (error < 0)
+ /* initialize ignores the first time through */
+ if (path.basename == path.path &&
+ (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
break;
/* first process builtins - success means path was found */
@@ -327,6 +343,10 @@ int git_ignore_path_is_ignored(
if (tail == end)
break;
+ /* now add this directory to list of ignores */
+ if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
+ break;
+
/* reinstate divider in path */
*tail = '/';
while (*tail == '/') tail++;
diff --git a/src/ignore.h b/src/ignore.h
index cc114b001..851c824bf 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -24,14 +24,15 @@
*/
typedef struct {
git_repository *repo;
- git_buf dir;
+ git_buf dir; /* current directory reflected in ign_path */
git_attr_file *ign_internal;
git_vector ign_path;
git_vector ign_global;
int ignore_case;
} git_ignores;
-extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
+extern int git_ignore__for_path(
+ git_repository *repo, const char *path, git_ignores *ign);
extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
diff --git a/src/index.c b/src/index.c
index bd5e192f3..5f53f1e2f 100644
--- a/src/index.c
+++ b/src/index.c
@@ -16,6 +16,7 @@
#include "iterator.h"
#include "pathspec.h"
#include "ignore.h"
+#include "blob.h"
#include "git2/odb.h"
#include "git2/oid.h"
@@ -604,42 +605,23 @@ int git_index_entry__cmp_icase(const void *a, const void *b)
return strcasecmp(entry_a->path, entry_b->path);
}
-static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
+static int index_entry_init(
+ git_index_entry **entry_out, git_index *index, const char *rel_path)
{
+ int error = 0;
git_index_entry *entry = NULL;
struct stat st;
git_oid oid;
- const char *workdir;
- git_buf full_path = GIT_BUF_INIT;
- int error;
if (INDEX_OWNER(index) == NULL)
return create_index_error(-1,
"Could not initialize index entry. "
"Index is not backed up by an existing repository.");
- workdir = git_repository_workdir(INDEX_OWNER(index));
-
- if (!workdir)
- return create_index_error(GIT_EBAREREPO,
- "Could not initialize index entry. Repository is bare");
-
- if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
- return error;
-
- if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
- git_buf_free(&full_path);
- return error;
- }
-
- git_buf_free(&full_path); /* done with full path */
-
- /* There is no need to validate the rel_path here, since it will be
- * immediately validated by the call to git_blob_create_fromfile.
- */
-
- /* write the blob to disk and get the oid */
- if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
+ /* write the blob to disk and get the oid and stat info */
+ error = git_blob__create_from_paths(
+ &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
+ if (error < 0)
return error;
entry = git__calloc(1, sizeof(git_index_entry));
@@ -1389,7 +1371,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
while (size) {
git_index_reuc_entry *lost;
- len = strlen(buffer) + 1;
+ len = p_strnlen(buffer, size) + 1;
if (size <= len)
return index_error_invalid("reading reuc entries");
@@ -1409,14 +1391,18 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
!endptr || endptr == buffer || *endptr ||
- (unsigned)tmp > UINT_MAX)
+ (unsigned)tmp > UINT_MAX) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry stage");
+ }
lost->mode[i] = tmp;
len = (endptr + 1) - buffer;
- if (size <= len)
+ if (size <= len) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry stage");
+ }
size -= len;
buffer += len;
@@ -1426,8 +1412,10 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
for (i = 0; i < 3; i++) {
if (!lost->mode[i])
continue;
- if (size < 20)
+ if (size < 20) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry oid");
+ }
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
size -= 20;
@@ -1456,7 +1444,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
return -1;
#define read_conflict_name(ptr) \
- len = strlen(buffer) + 1; \
+ len = p_strnlen(buffer, size) + 1; \
if (size < len) \
return index_error_invalid("reading conflict name entries"); \
\
@@ -1583,7 +1571,8 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
total_size = dest.extension_size + sizeof(struct index_extension);
- if (buffer_size < total_size ||
+ if (dest.extension_size > total_size ||
+ buffer_size < total_size ||
buffer_size - total_size < INDEX_FOOTER_SIZE)
return 0;
diff --git a/src/indexer.c b/src/indexer.c
index 1b638cd8a..09f962934 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -325,7 +325,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
/* FIXME: Parse the object instead of hashing it */
if (git_odb__hashobj(&oid, obj) < 0) {
giterr_set(GITERR_INDEXER, "Failed to hash object");
- return -1;
+ goto on_error;
}
pentry = git__calloc(1, sizeof(struct git_pack_entry));
diff --git a/src/iterator.c b/src/iterator.c
index 5917f63fd..bdc98d22b 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1350,7 +1350,7 @@ int git_iterator_for_workdir_ext(
wi->fi.update_entry_cb = workdir_iterator__update_entry;
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
- (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
+ (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
{
git_iterator_free((git_iterator *)wi);
return error;
diff --git a/src/merge.c b/src/merge.c
index 82d2e6f37..2e94ce1cd 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -1902,8 +1902,10 @@ static int write_merge_msg(
entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
GITERR_CHECK_ALLOC(entries);
- if (git_vector_init(&matching, heads_len, NULL) < 0)
+ if (git_vector_init(&matching, heads_len, NULL) < 0) {
+ git__free(entries);
return -1;
+ }
for (i = 0; i < heads_len; i++)
entries[i].merge_head = heads[i];
diff --git a/src/odb.c b/src/odb.c
index 8e62efd00..6969cf772 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -232,6 +232,7 @@ int git_odb__hashlink(git_oid *out, const char *path)
link_data[size] = '\0';
if (read_len != (ssize_t)size) {
giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+ git__free(link_data);
return -1;
}
@@ -785,8 +786,10 @@ attempt_lookup:
git__free(data);
data = raw.data;
- if (found && git_oid__cmp(&full_oid, &found_full_oid))
+ if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
+ git__free(raw.data);
return git_odb__error_ambiguous("multiple matches for prefix");
+ }
found_full_oid = full_oid;
found = true;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index eec79259b..43880612a 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -259,23 +259,26 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
return git_odb__error_notfound("failed to find pack entry", oid);
}
-static unsigned pack_entry_find_prefix_inner(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len,
- struct git_pack_file *last_found)
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ size_t len)
{
int error;
size_t i;
- unsigned found = 0;
+ git_oid found_full_oid = {{0}};
+ bool found = false;
+ struct git_pack_file *last_found = backend->last_found;
if (last_found) {
error = git_pack_entry_find(e, last_found, short_oid, len);
if (error == GIT_EAMBIGUOUS)
return error;
- if (!error)
- found = 1;
+ if (!error) {
+ git_oid_cpy(&found_full_oid, &e->sha1);
+ found = true;
+ }
}
for (i = 0; i < backend->packs.length; ++i) {
@@ -289,28 +292,16 @@ static unsigned pack_entry_find_prefix_inner(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (++found > 1)
- break;
+ if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ return git_odb__error_ambiguous("found multiple pack entries");
+ git_oid_cpy(&found_full_oid, &e->sha1);
+ found = true;
backend->last_found = p;
}
}
- return found;
-}
-
-static int pack_entry_find_prefix(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- struct git_pack_file *last_found = backend->last_found;
- unsigned int found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
-
if (!found)
return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
- else if (found > 1)
- return git_odb__error_ambiguous("found multiple pack entries");
else
return 0;
}
diff --git a/src/oid.h b/src/oid.h
index 077d0a4c8..cfe7ca1b2 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -9,17 +9,8 @@
#include "git2/oid.h"
-/*
- * Compare two oid structures.
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @return <0, 0, >0 if a < b, a == b, a > b.
- */
-GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
+GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
- const unsigned char *sha1 = a->id;
- const unsigned char *sha2 = b->id;
int i;
for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
@@ -30,4 +21,16 @@ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
return 0;
}
+/*
+ * Compare two oid structures.
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return <0, 0, >0 if a < b, a == b, a > b.
+ */
+GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
+{
+ return git_oid__hashcmp(a->id, b->id);
+}
+
#endif
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 500104c55..7f427e3bd 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -505,8 +505,10 @@ static git_pobject **compute_write_order(git_packbuilder *pb)
/*
* Mark objects that are at the tip of tags.
*/
- if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0)
+ if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
+ git__free(wo);
return NULL;
+ }
/*
* Give the objects in the original recency order until
diff --git a/src/pack.c b/src/pack.c
index 7ce7099e0..e7fb9f1ae 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -329,8 +329,10 @@ static int pack_index_open(struct git_pack_file *p)
memcpy(idx_name, p->pack_name, base_len);
memcpy(idx_name + base_len, ".idx", sizeof(".idx"));
- if ((error = git_mutex_lock(&p->lock)) < 0)
+ if ((error = git_mutex_lock(&p->lock)) < 0) {
+ git__free(idx_name);
return error;
+ }
if (p->index_version == -1)
error = pack_index_check(idx_name, p);
@@ -820,7 +822,7 @@ void git_packfile_free(struct git_pack_file *p)
git_mwindow_free_all(&p->mwf);
- if (p->mwf.fd != -1)
+ if (p->mwf.fd >= 0)
p_close(p->mwf.fd);
pack_index_free(p);
@@ -903,7 +905,8 @@ static int packfile_open(struct git_pack_file *p)
cleanup:
giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
- p_close(p->mwf.fd);
+ if (p->mwf.fd >= 0)
+ p_close(p->mwf.fd);
p->mwf.fd = -1;
git_mutex_unlock(&p->lock);
@@ -1107,8 +1110,11 @@ static int pack_entry_find_offset(
short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
- /* Use git.git lookup code */
+#ifdef GIT_USE_LOOKUP
pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
+#else
+ pos = sha1_position(index, stride, lo, hi, short_oid->id);
+#endif
if (pos >= 0) {
/* An object matching exactly the oid was found */
diff --git a/src/path.c b/src/path.c
index 6437979d5..a753a734d 100644
--- a/src/path.c
+++ b/src/path.c
@@ -8,7 +8,6 @@
#include "path.h"
#include "posix.h"
#ifdef GIT_WIN32
-#include "win32/dir.h"
#include "win32/posix.h"
#else
#include <dirent.h>
@@ -486,24 +485,26 @@ bool git_path_is_empty_dir(const char *path)
{
git_buf pathbuf = GIT_BUF_INIT;
HANDLE hFind = INVALID_HANDLE_VALUE;
- wchar_t wbuf[GIT_WIN_PATH];
+ git_win32_path wbuf;
WIN32_FIND_DATAW ffd;
bool retval = true;
if (!git_path_isdir(path)) return false;
git_buf_printf(&pathbuf, "%s\\*", path);
- git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf));
+ git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf));
hFind = FindFirstFileW(wbuf, &ffd);
if (INVALID_HANDLE_VALUE == hFind) {
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+ git_buf_free(&pathbuf);
return false;
}
do {
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
retval = false;
+ break;
}
} while (FindNextFileW(hFind, &ffd) != 0);
@@ -603,7 +604,7 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base)
}
/* call dirname if this is not a directory */
- if (!error && git_path_isdir(dir->ptr) == false)
+ if (!error) /* && git_path_isdir(dir->ptr) == false) */
error = git_path_dirname_r(dir, dir->ptr);
if (!error)
diff --git a/src/path.h b/src/path.h
index ead4fa338..b2899e97f 100644
--- a/src/path.h
+++ b/src/path.h
@@ -175,7 +175,6 @@ extern bool git_path_contains(git_buf *dir, const char *item);
*
* @param parent Directory path that might contain subdir
* @param subdir Subdirectory name to look for in parent
- * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
* @return true if subdirectory exists, false otherwise.
*/
extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
@@ -185,7 +184,6 @@ extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
*
* @param dir Directory path that might contain file
* @param file File name to look for in parent
- * @param append_if_exists If true, then file will be appended to the path if it does exist
* @return true if file exists, false otherwise.
*/
extern bool git_path_contains_file(git_buf *dir, const char *file);
diff --git a/src/pathspec.c b/src/pathspec.c
index 625726e0b..d56d03918 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -83,7 +83,7 @@ int git_pathspec__vinit(
if (!match)
return -1;
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
@@ -160,6 +160,16 @@ static int pathspec_match_one(
path[match->length] == '/')
result = 0;
+ /* if we didn't match and this is a negative match, check for exact
+ * match of filename with leading '!'
+ */
+ if (result == FNM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
+ *path == '!' &&
+ ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
+ (!path[match->length + 1] || path[match->length + 1] == '/'))
+ return 1;
+
if (result == 0)
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
return -1;
diff --git a/src/posix.h b/src/posix.h
index 40bcc1ab0..ea97a1349 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -93,6 +93,10 @@ extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
# include "unix/posix.h"
#endif
+#ifndef __MINGW32__
+# define p_strnlen strnlen
+#endif
+
#ifdef NO_READDIR_R
# include <dirent.h>
GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
diff --git a/src/pqueue.h b/src/pqueue.h
index ed7139285..9061f8279 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -48,7 +48,7 @@ typedef struct {
* should be preallocated
* @param cmppri the callback function to compare two nodes of the queue
*
- * @Return the handle or NULL for insufficent memory
+ * @return the handle or NULL for insufficent memory
*/
int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);
@@ -83,8 +83,7 @@ int git_pqueue_insert(git_pqueue *q, void *d);
/**
* pop the highest-ranking item from the queue.
- * @param p the queue
- * @param d where to copy the entry to
+ * @param q the queue
* @return NULL on error, otherwise the entry
*/
void *git_pqueue_pop(git_pqueue *q);
@@ -93,7 +92,6 @@ void *git_pqueue_pop(git_pqueue *q);
/**
* access highest-ranking item without removing it.
* @param q the queue
- * @param d the entry
* @return NULL on error, otherwise the entry
*/
void *git_pqueue_peek(git_pqueue *q);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index b9e283ac5..acd82594b 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -452,6 +452,9 @@ static int loose_lookup(
git_buf ref_file = GIT_BUF_INIT;
int error = 0;
+ if (out)
+ *out = NULL;
+
error = reference_read(&ref_file, NULL, backend->path, ref_name, NULL);
if (error < 0)
@@ -465,15 +468,17 @@ static int loose_lookup(
goto done;
}
- *out = git_reference__alloc_symbolic(ref_name, target);
+ if (out)
+ *out = git_reference__alloc_symbolic(ref_name, target);
} else {
if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0)
goto done;
- *out = git_reference__alloc(ref_name, &oid, NULL);
+ if (out)
+ *out = git_reference__alloc(ref_name, &oid, NULL);
}
- if (*out == NULL)
+ if (out && *out == NULL)
error = -1;
done:
@@ -555,7 +560,10 @@ typedef struct {
git_reference_iterator parent;
char *glob;
+
+ git_pool pool;
git_vector loose;
+
unsigned int loose_pos;
khiter_t packed_pos;
} refdb_fs_iter;
@@ -563,24 +571,18 @@ typedef struct {
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
{
refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
- char *loose_path;
- size_t i;
-
- git_vector_foreach(&iter->loose, i, loose_path) {
- git__free(loose_path);
- }
git_vector_free(&iter->loose);
-
- git__free(iter->glob);
+ git_pool_clear(&iter->pool);
git__free(iter);
}
static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
{
+ int error = 0;
git_strmap *packfile = backend->refcache.packfile;
git_buf path = GIT_BUF_INIT;
- git_iterator *fsit;
+ git_iterator *fsit = NULL;
const git_index_entry *entry = NULL;
if (!backend->path) /* do nothing if no path for loose refs */
@@ -589,15 +591,16 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
if (git_buf_printf(&path, "%s/refs", backend->path) < 0)
return -1;
- if (git_iterator_for_filesystem(&fsit, git_buf_cstr(&path), 0, NULL, NULL) < 0)
- return -1;
-
- git_vector_init(&iter->loose, 8, NULL);
- git_buf_sets(&path, GIT_REFS_DIR);
+ if ((error = git_iterator_for_filesystem(
+ &fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0 ||
+ (error = git_vector_init(&iter->loose, 8, NULL)) < 0 ||
+ (error = git_buf_sets(&path, GIT_REFS_DIR)) < 0)
+ goto cleanup;
while (!git_iterator_advance(&entry, fsit)) {
const char *ref_name;
khiter_t pos;
+ char *ref_dup;
git_buf_truncate(&path, strlen(GIT_REFS_DIR));
git_buf_puts(&path, entry->path);
@@ -613,9 +616,16 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
ref->flags |= PACKREF_SHADOWED;
}
- git_vector_insert(&iter->loose, git__strdup(ref_name));
+ if (!(ref_dup = git_pool_strdup(&iter->pool, ref_name))) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_vector_insert(&iter->loose, ref_dup)) < 0)
+ goto cleanup;
}
+cleanup:
git_iterator_free(fsit);
git_buf_free(&path);
@@ -679,6 +689,11 @@ static int refdb_fs_backend__iterator_next_name(
if (git_strmap_exists(packfile, path))
continue;
+ if (loose_lookup(NULL, backend, path) != 0) {
+ giterr_clear();
+ continue;
+ }
+
*out = path;
return 0;
}
@@ -717,20 +732,26 @@ static int refdb_fs_backend__iterator(
iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter);
- if (glob != NULL)
- iter->glob = git__strdup(glob);
+ if (git_pool_init(&iter->pool, 1, 0) < 0)
+ goto fail;
+
+ if (glob != NULL &&
+ (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL)
+ goto fail;
iter->parent.next = refdb_fs_backend__iterator_next;
iter->parent.next_name = refdb_fs_backend__iterator_next_name;
iter->parent.free = refdb_fs_backend__iterator_free;
- if (iter_load_loose_paths(backend, iter) < 0) {
- refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
- return -1;
- }
+ if (iter_load_loose_paths(backend, iter) < 0)
+ goto fail;
*out = (git_reference_iterator *)iter;
return 0;
+
+fail:
+ refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
+ return -1;
}
static bool ref_is_available(
@@ -782,7 +803,7 @@ static int reference_path_available(
return -1;
}
});
-
+
return 0;
}
diff --git a/src/remote.c b/src/remote.c
index 0e8354a11..7677e56b2 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -81,6 +81,31 @@ static int ensure_remote_name_is_valid(const char *name)
return error;
}
+static int get_check_cert(git_repository *repo)
+{
+ git_config *cfg;
+ const char *val;
+ int check_cert;
+
+ assert(repo);
+
+ /* Go through the possible sources for SSL verification settings, from
+ * most specific to least specific. */
+
+ /* GIT_SSL_NO_VERIFY environment variable */
+ if ((val = getenv("GIT_SSL_NO_VERIFY")) &&
+ !git_config_parse_bool(&check_cert, val))
+ return !check_cert;
+
+ /* http.sslVerify config setting */
+ if (!git_repository_config__weakptr(&cfg, repo) &&
+ !git_config_get_bool(&check_cert, cfg, "http.sslVerify"))
+ return check_cert;
+
+ /* By default, we *DO* want to verify the certificate. */
+ return 1;
+}
+
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
git_remote *remote;
@@ -94,7 +119,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
GITERR_CHECK_ALLOC(remote);
remote->repo = repo;
- remote->check_cert = 1;
+ remote->check_cert = (unsigned)get_check_cert(repo);
remote->update_fetchhead = 1;
if (git_vector_init(&remote->refs, 32, NULL) < 0)
@@ -253,7 +278,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
- remote->check_cert = 1;
+ remote->check_cert = (unsigned)get_check_cert(repo);
remote->update_fetchhead = 1;
remote->name = git__strdup(name);
GITERR_CHECK_ALLOC(remote->name);
@@ -272,12 +297,6 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
goto cleanup;
- if (strlen(val) == 0) {
- giterr_set(GITERR_INVALID, "Malformed remote '%s' - missing URL", name);
- error = -1;
- goto cleanup;
- }
-
remote->repo = repo;
remote->url = git__strdup(val);
GITERR_CHECK_ALLOC(remote->url);
@@ -467,6 +486,12 @@ const char *git_remote_name(const git_remote *remote)
return remote->name;
}
+git_repository *git_remote_owner(const git_remote *remote)
+{
+ assert(remote);
+ return remote->repo;
+}
+
const char *git_remote_url(const git_remote *remote)
{
assert(remote);
diff --git a/src/repository.c b/src/repository.c
index bd7ef5476..99ac56ef9 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1500,12 +1500,12 @@ int git_repository_is_empty(git_repository *repo)
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
return -1;
- if (!(error = git_reference_type(head) == GIT_REF_SYMBOLIC))
+ if (!((error = git_reference_type(head)) == GIT_REF_SYMBOLIC))
goto cleanup;
- if (!(error = strcmp(
+ if (!(error = (strcmp(
git_reference_symbolic_target(head),
- GIT_REFS_HEADS_DIR "master") == 0))
+ GIT_REFS_HEADS_DIR "master") == 0)))
goto cleanup;
error = repo_contains_no_reference(repo);
diff --git a/src/revparse.c b/src/revparse.c
index bcfb0843f..b84f0037f 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -685,6 +685,8 @@ int revparse__ext(
git_reference *reference = NULL;
git_object *base_rev = NULL;
+ bool should_return_reference = true;
+
assert(object_out && reference_out && repo && spec);
*object_out = NULL;
@@ -693,6 +695,8 @@ int revparse__ext(
while (spec[pos]) {
switch (spec[pos]) {
case '^':
+ should_return_reference = false;
+
if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
goto cleanup;
@@ -725,6 +729,8 @@ int revparse__ext(
{
git_object *temp_object = NULL;
+ should_return_reference = false;
+
if ((error = extract_how_many(&n, spec, &pos)) < 0)
goto cleanup;
@@ -743,6 +749,8 @@ int revparse__ext(
{
git_object *temp_object = NULL;
+ should_return_reference = false;
+
if ((error = extract_path(&buf, spec, &pos)) < 0)
goto cleanup;
@@ -807,6 +815,11 @@ int revparse__ext(
if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
goto cleanup;
+ if (!should_return_reference) {
+ git_reference_free(reference);
+ reference = NULL;
+ }
+
*object_out = base_rev;
*reference_out = reference;
*identifier_len_out = identifier_len;
@@ -899,13 +912,9 @@ int git_revparse(
rstr++;
}
- if ((error = git_revparse_single(&revspec->from, repo, lstr)) < 0) {
- return error;
- }
-
- if ((error = git_revparse_single(&revspec->to, repo, rstr)) < 0) {
- return error;
- }
+ error = git_revparse_single(&revspec->from, repo, lstr);
+ if (!error)
+ error = git_revparse_single(&revspec->to, repo, rstr);
git__free((void*)lstr);
} else {
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index b7e66cc69..cdcadfaa9 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -9,6 +9,7 @@
#include "sha1_lookup.h"
#include "common.h"
+#include "oid.h"
/*
* Conventional binary search loop looks like this:
@@ -176,3 +177,26 @@ int sha1_entry_pos(const void *table,
} while (lo < hi);
return -((int)lo)-1;
}
+
+int sha1_position(const void *table,
+ size_t stride,
+ unsigned lo, unsigned hi,
+ const unsigned char *key)
+{
+ const unsigned char *base = table;
+
+ do {
+ unsigned mi = (lo + hi) / 2;
+ int cmp = git_oid__hashcmp(base + mi * stride, key);
+
+ if (!cmp)
+ return mi;
+
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi+1;
+ } while (lo < hi);
+
+ return -((int)lo)-1;
+}
diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h
index 9a3537273..3799620c7 100644
--- a/src/sha1_lookup.h
+++ b/src/sha1_lookup.h
@@ -15,4 +15,9 @@ int sha1_entry_pos(const void *table,
unsigned lo, unsigned hi, unsigned nr,
const unsigned char *key);
+int sha1_position(const void *table,
+ size_t stride,
+ unsigned lo, unsigned hi,
+ const unsigned char *key);
+
#endif
diff --git a/src/signature.c b/src/signature.c
index 0a34ccfaa..52ca2b375 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -74,7 +74,7 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
git_signature_free(p);
return signature_error("Signature cannot have an empty name");
}
-
+
p->when.time = time;
p->when.offset = offset;
@@ -129,6 +129,23 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
return 0;
}
+int git_signature_default(git_signature **out, git_repository *repo)
+{
+ int error;
+ git_config *cfg;
+ const char *user_name, *user_email;
+
+ if ((error = git_repository_config(&cfg, repo)) < 0)
+ return error;
+
+ if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
+ !(error = git_config_get_string(&user_email, cfg, "user.email")))
+ error = git_signature_now(out, user_name, user_email);
+
+ git_config_free(cfg);
+ return error;
+}
+
int git_signature__parse(git_signature *sig, const char **buffer_out,
const char *buffer_end, const char *header, char ender)
{
diff --git a/src/status.c b/src/status.c
index ccb8d37da..b2353258b 100644
--- a/src/status.c
+++ b/src/status.c
@@ -225,24 +225,6 @@ static git_status_list *git_status_list_alloc(git_index *index)
return status;
}
-/*
-static int newfile_cmp(const void *a, const void *b)
-{
- const git_diff_delta *delta_a = a;
- const git_diff_delta *delta_b = b;
-
- return git__strcmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-
-static int newfile_casecmp(const void *a, const void *b)
-{
- const git_diff_delta *delta_a = a;
- const git_diff_delta *delta_b = b;
-
- return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-*/
-
int git_status_list_new(
git_status_list **out,
git_repository *repo,
@@ -251,7 +233,7 @@ int git_status_list_new(
git_index *index = NULL;
git_status_list *status = NULL;
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
git_tree *head = NULL;
git_status_show_t show =
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
@@ -284,6 +266,7 @@ int git_status_list_new(
}
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
@@ -300,7 +283,9 @@ int git_status_list_new(
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
- findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED;
+ if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
+ findopt.flags = findopt.flags | GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
if ((error = git_diff_tree_to_index(
@@ -308,7 +293,7 @@ int git_status_list_new(
goto done;
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
- (error = git_diff_find_similar(status->head2idx, NULL)) < 0)
+ (error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
goto done;
}
@@ -318,7 +303,7 @@ int git_status_list_new(
goto done;
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
- (error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0)
+ (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
goto done;
}
diff --git a/src/submodule.c b/src/submodule.c
index b5dacc42e..40bda9a41 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -798,7 +798,7 @@ static void submodule_update_from_head_data(
static int submodule_update_head(git_submodule *submodule)
{
git_tree *head = NULL;
- git_tree_entry *te;
+ git_tree_entry *te = NULL;
submodule->flags = submodule->flags &
~(GIT_SUBMODULE_STATUS_IN_HEAD |
@@ -811,6 +811,7 @@ static int submodule_update_head(git_submodule *submodule)
else
submodule_update_from_head_data(submodule, te->attr, &te->oid);
+ git_tree_entry_free(te);
git_tree_free(head);
return 0;
}
@@ -1024,6 +1025,7 @@ static int submodule_get(
if (!git_strmap_valid_index(smcfg, pos)) {
sm = submodule_alloc(repo, name);
+ GITERR_CHECK_ALLOC(sm);
/* insert value at name - if another thread beats us to it, then use
* their record and release our own.
@@ -1100,8 +1102,10 @@ static int submodule_load_from_config(
namestart = key + strlen("submodule.");
property = strrchr(namestart, '.');
- if (property == NULL)
+
+ if (!property || (property == namestart))
return 0;
+
property++;
is_path = (strcasecmp(property, "path") == 0);
diff --git a/src/transports/cred.c b/src/transports/cred.c
index a6727e902..35aaf4f91 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -9,6 +9,31 @@
#include "smart.h"
#include "git2/cred_helpers.h"
+int git_cred_has_username(git_cred *cred)
+{
+ int ret = 0;
+
+ switch (cred->credtype) {
+ case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+ ret = !!c->username;
+ break;
+ }
+ case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
+ git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+ ret = !!c->username;
+ break;
+ }
+ case GIT_CREDTYPE_SSH_PUBLICKEY: {
+ git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ ret = !!c->username;
+ break;
+ }
+ }
+
+ return ret;
+}
+
static void plaintext_free(struct git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -64,6 +89,7 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
git_cred_ssh_keyfile_passphrase *c =
(git_cred_ssh_keyfile_passphrase *)cred;
+ git__free(c->username);
git__free(c->publickey);
git__free(c->privatekey);
@@ -82,6 +108,7 @@ static void ssh_publickey_free(struct git_cred *cred)
{
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ git__free(c->username);
git__free(c->publickey);
git__memzero(c, sizeof(*c));
@@ -90,6 +117,7 @@ static void ssh_publickey_free(struct git_cred *cred)
int git_cred_ssh_keyfile_passphrase_new(
git_cred **cred,
+ const char *username,
const char *publickey,
const char *privatekey,
const char *passphrase)
@@ -104,6 +132,11 @@ int git_cred_ssh_keyfile_passphrase_new(
c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
c->parent.free = ssh_keyfile_passphrase_free;
+ if (username) {
+ c->username = git__strdup(username);
+ GITERR_CHECK_ALLOC(c->username);
+ }
+
c->privatekey = git__strdup(privatekey);
GITERR_CHECK_ALLOC(c->privatekey);
@@ -123,6 +156,7 @@ int git_cred_ssh_keyfile_passphrase_new(
int git_cred_ssh_publickey_new(
git_cred **cred,
+ const char *username,
const char *publickey,
size_t publickey_len,
git_cred_sign_callback sign_callback,
@@ -138,6 +172,11 @@ int git_cred_ssh_publickey_new(
c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
c->parent.free = ssh_publickey_free;
+ if (username) {
+ c->username = git__strdup(username);
+ GITERR_CHECK_ALLOC(c->username);
+ }
+
if (publickey_len > 0) {
c->publickey = git__malloc(publickey_len);
GITERR_CHECK_ALLOC(c->publickey);
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 7fb53bc3c..e0126a8fb 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -17,7 +17,6 @@
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
static const char prefix_ssh[] = "ssh://";
-static const char default_user[] = "git";
static const char cmd_uploadpack[] = "git-upload-pack";
static const char cmd_receivepack[] = "git-receive-pack";
@@ -214,11 +213,11 @@ static int git_ssh_extract_url_parts(
if (at) {
start = at+1;
*username = git__substrdup(url, at - url);
+ GITERR_CHECK_ALLOC(*username);
} else {
start = url;
- *username = git__strdup(default_user);
+ *username = NULL;
}
- GITERR_CHECK_ALLOC(*username);
*host = git__substrdup(start, colon - start);
GITERR_CHECK_ALLOC(*host);
@@ -237,19 +236,23 @@ static int _git_ssh_authenticate_session(
switch (cred->credtype) {
case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- rc = libssh2_userauth_password(session, c->username, c->password);
+ user = c->username ? c->username : user;
+ rc = libssh2_userauth_password(session, user, c->password);
break;
}
case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+ user = c->username ? c->username : user;
rc = libssh2_userauth_publickey_fromfile(
- session, user, c->publickey, c->privatekey, c->passphrase);
+ session, c->username, c->publickey, c->privatekey, c->passphrase);
break;
}
case GIT_CREDTYPE_SSH_PUBLICKEY: {
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+
+ user = c->username ? c->username : user;
rc = libssh2_userauth_publickey(
- session, user, (const unsigned char *)c->publickey,
+ session, c->username, (const unsigned char *)c->publickey,
c->publickey_len, c->sign_callback, &c->sign_data);
break;
}
@@ -351,9 +354,9 @@ static int _git_ssh_setup_conn(
}
assert(t->cred);
- if (!user) {
- user = git__strdup(default_user);
- GITERR_CHECK_ALLOC(user);
+ if (!user && !git_cred_has_username(t->cred)) {
+ giterr_set_str(GITERR_NET, "Cannot authenticate without a username");
+ goto on_error;
}
if (_git_ssh_session_create(&session, s->socket) < 0)
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 95e422dc0..8decd8d51 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -893,7 +893,7 @@ static int winhttp_connect(
const char *url)
{
wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
- wchar_t host[GIT_WIN_PATH];
+ git_win32_path host;
int32_t port;
const char *default_port = "80";
int ret;
@@ -920,7 +920,7 @@ static int winhttp_connect(
return -1;
/* Prepare host */
- git__utf8_to_16(host, GIT_WIN_PATH, t->host);
+ git_win32_path_from_c(host, t->host);
/* Establish session */
t->session = WinHttpOpen(
@@ -934,7 +934,7 @@ static int winhttp_connect(
giterr_set(GITERR_OS, "Failed to init WinHTTP");
return -1;
}
-
+
/* Establish connection */
t->connection = WinHttpConnect(
t->session,
diff --git a/src/util.h b/src/util.h
index a97c9bf39..a784390c1 100644
--- a/src/util.h
+++ b/src/util.h
@@ -55,6 +55,9 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
ptr = (char*)git__malloc(length + 1);
+ if (!ptr)
+ return NULL;
+
if (length)
memcpy(ptr, str, length);
@@ -294,6 +297,11 @@ GIT_INLINE(bool) git__isspace(int c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
}
+GIT_INLINE(bool) git__isspace_nonlf(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
+}
+
GIT_INLINE(bool) git__iswildcard(int c)
{
return (c == '*' || c == '?' || c == '[');
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 8c51d8378..f7859b73f 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -5,8 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#define GIT__WIN32_NO_WRAP_DIR
-#include "dir.h"
-#include "utf-conv.h"
+#include "posix.h"
static int init_filter(char *filter, size_t n, const char *dir)
{
@@ -25,36 +24,32 @@ static int init_filter(char *filter, size_t n, const char *dir)
git__DIR *git__opendir(const char *dir)
{
- char filter[GIT_WIN_PATH];
- wchar_t filter_w[GIT_WIN_PATH];
+ git_win32_path_as_utf8 filter;
+ git_win32_path filter_w;
git__DIR *new = NULL;
+ size_t dirlen;
if (!dir || !init_filter(filter, sizeof(filter), dir))
return NULL;
- new = git__calloc(1, sizeof(*new));
+ dirlen = strlen(dir);
+
+ new = git__calloc(sizeof(*new) + dirlen + 1, 1);
if (!new)
return NULL;
+ memcpy(new->dir, dir, dirlen);
- new->dir = git__strdup(dir);
- if (!new->dir)
- goto fail;
-
- git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+ git_win32_path_from_c(filter_w, filter);
new->h = FindFirstFileW(filter_w, &new->f);
if (new->h == INVALID_HANDLE_VALUE) {
giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
- goto fail;
+ git__free(new);
+ return NULL;
}
new->first = 1;
return new;
-
-fail:
- git__free(new->dir);
- git__free(new);
- return NULL;
}
int git__readdir_ext(
@@ -80,7 +75,7 @@ int git__readdir_ext(
if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
return -1;
- git__utf16_to_8(entry->d_name, d->f.cFileName);
+ git_win32_path_to_c(entry->d_name, d->f.cFileName);
entry->d_ino = 0;
*result = entry;
@@ -101,8 +96,8 @@ struct git__dirent *git__readdir(git__DIR *d)
void git__rewinddir(git__DIR *d)
{
- char filter[GIT_WIN_PATH];
- wchar_t filter_w[GIT_WIN_PATH];
+ git_win32_path_as_utf8 filter;
+ git_win32_path filter_w;
if (!d)
return;
@@ -116,7 +111,7 @@ void git__rewinddir(git__DIR *d)
if (!init_filter(filter, sizeof(filter), d->dir))
return;
- git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+ git_win32_path_from_c(filter_w, filter);
d->h = FindFirstFileW(filter_w, &d->f);
if (d->h == INVALID_HANDLE_VALUE)
@@ -134,8 +129,7 @@ int git__closedir(git__DIR *d)
FindClose(d->h);
d->h = INVALID_HANDLE_VALUE;
}
- git__free(d->dir);
- d->dir = NULL;
+
git__free(d);
return 0;
}
diff --git a/src/win32/dir.h b/src/win32/dir.h
index 7696d468e..24d48f6ba 100644
--- a/src/win32/dir.h
+++ b/src/win32/dir.h
@@ -11,15 +11,15 @@
struct git__dirent {
int d_ino;
- char d_name[261];
+ git_win32_path_as_utf8 d_name;
};
typedef struct {
HANDLE h;
WIN32_FIND_DATAW f;
struct git__dirent entry;
- char *dir;
int first;
+ char dir[GIT_FLEX_ARRAY];
} git__DIR;
extern git__DIR *git__opendir(const char *);
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index 9d9051bff..a1c11fcfb 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -23,11 +23,11 @@ int git_win32__expand_path(struct git_win32__path *s_root, const wchar_t *templ)
return s_root->len ? 0 : -1;
}
-static int win32_path_utf16_to_8(git_buf *path_utf8, const wchar_t *path_utf16)
+static int win32_path_to_8(git_buf *path_utf8, const wchar_t *path)
{
char temp_utf8[GIT_PATH_MAX];
- git__utf16_to_8(temp_utf8, path_utf16);
+ git__utf16_to_8(temp_utf8, GIT_PATH_MAX, path);
git_path_mkposix(temp_utf8);
return git_buf_sets(path_utf8, temp_utf8);
@@ -53,7 +53,7 @@ int git_win32__find_file(
if (*filename == '/' || *filename == '\\')
filename++;
- git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename);
+ git__utf8_to_16(file_utf16 + root->len - 1, alloc_len - root->len, filename);
/* check access */
if (_waccess(file_utf16, F_OK) < 0) {
@@ -61,7 +61,7 @@ int git_win32__find_file(
return GIT_ENOTFOUND;
}
- win32_path_utf16_to_8(path, file_utf16);
+ win32_path_to_8(path, file_utf16);
git__free(file_utf16);
return 0;
@@ -113,7 +113,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
/* replace "bin\\" or "cmd\\" with "etc\\" */
wcscpy(&root.path[root.len - 4], L"etc\\");
- win32_path_utf16_to_8(buf, root.path);
+ win32_path_to_8(buf, root.path);
return 0;
}
}
@@ -146,7 +146,7 @@ static int win32_find_git_in_registry(
wcscat(path16.path, L"etc\\");
path16.len += 4;
- win32_path_utf16_to_8(buf, path16.path);
+ win32_path_to_8(buf, path16.path);
}
RegCloseKey(hKey);
@@ -168,7 +168,7 @@ static int win32_find_existing_dirs(
path16.path[0] != L'%' &&
!_waccess(path16.path, F_OK))
{
- win32_path_utf16_to_8(&buf, path16.path);
+ win32_path_to_8(&buf, path16.path);
if (buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 7b97b48db..97b1cb71b 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -19,6 +19,11 @@
# define S_IFLNK _S_IFLNK
# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
+ const char *end = memchr(s, 0, maxlen);
+ return end ? (end - s) : maxlen;
+}
+
#endif
#endif /* INCLUDE_mingw_compat__ */
diff --git a/src/win32/posix.h b/src/win32/posix.h
index c49c2175c..5f924a026 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -8,7 +8,9 @@
#define INCLUDE_posix__w32_h__
#include "common.h"
+#include "../posix.h"
#include "utf-conv.h"
+#include "dir.h"
GIT_INLINE(int) p_link(const char *old, const char *new)
{
@@ -20,9 +22,9 @@ GIT_INLINE(int) p_link(const char *old, const char *new)
GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
+ git_win32_path buf;
GIT_UNUSED(mode);
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path_from_c(buf, path);
return _wmkdir(buf);
}
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 036632e2a..57ebaa12e 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -16,8 +16,8 @@
int p_unlink(const char *path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
_wchmod(buf, 0666);
return _wunlink(buf);
}
@@ -59,10 +59,11 @@ static int do_lstat(
const char *file_name, struct stat *buf, int posix_enotdir)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
- wchar_t fbuf[GIT_WIN_PATH], lastch;
+ git_win32_path fbuf;
+ wchar_t lastch;
int flen;
- flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name);
+ flen = git_win32_path_from_c(fbuf, file_name);
/* truncate trailing slashes */
for (; flen > 0; --flen) {
@@ -108,10 +109,10 @@ static int do_lstat(
* the length of the path pointed to, which we expect everywhere else
*/
if (S_ISLNK(fMode)) {
- char target[GIT_WIN_PATH];
+ git_win32_path_as_utf8 target;
int readlink_result;
- readlink_result = p_readlink(file_name, target, GIT_WIN_PATH);
+ readlink_result = p_readlink(file_name, target, sizeof(target));
if (readlink_result == -1)
return -1;
@@ -159,13 +160,22 @@ int p_lstat_posixly(const char *filename, struct stat *buf)
return do_lstat(filename, buf, 1);
}
+
+/*
+ * Parts of the The p_readlink function are heavily inspired by the php
+ * readlink function in link_win32.c
+ *
+ * Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
+ *
+ * For details of the PHP license see http://www.php.net/license/3_01.txt
+ */
int p_readlink(const char *link, char *target, size_t target_len)
{
typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);
static fpath_func pGetFinalPath = NULL;
HANDLE hFile;
DWORD dwRet;
- wchar_t link_w[GIT_WIN_PATH];
+ git_win32_path link_w;
wchar_t* target_w;
int error = 0;
@@ -188,7 +198,7 @@ int p_readlink(const char *link, char *target, size_t target_len)
}
}
- git__utf8_to_16(link_w, GIT_WIN_PATH, link);
+ git_win32_path_from_c(link_w, link);
hFile = CreateFileW(link_w, // file to open
GENERIC_READ, // open for reading
@@ -254,10 +264,10 @@ int p_symlink(const char *old, const char *new)
int p_open(const char *path, int flags, ...)
{
- wchar_t buf[GIT_WIN_PATH];
+ git_win32_path buf;
mode_t mode = 0;
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path_from_c(buf, path);
if (flags & O_CREAT) {
va_list arg_list;
@@ -272,8 +282,8 @@ int p_open(const char *path, int flags, ...)
int p_creat(const char *path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
}
@@ -299,7 +309,7 @@ int p_getcwd(char *buffer_out, size_t size)
int p_stat(const char* path, struct stat* buf)
{
- char target[GIT_WIN_PATH];
+ git_win32_path_as_utf8 target;
int error = 0;
error = do_lstat(path, buf, 0);
@@ -307,7 +317,7 @@ int p_stat(const char* path, struct stat* buf)
/* We need not do this in a loop to unwind chains of symlinks since
* p_readlink calls GetFinalPathNameByHandle which does it for us. */
if (error >= 0 && S_ISLNK(buf->st_mode) &&
- (error = p_readlink(path, target, GIT_WIN_PATH)) >= 0)
+ (error = p_readlink(path, target, sizeof(target))) >= 0)
error = do_lstat(target, buf, 0);
return error;
@@ -315,23 +325,23 @@ int p_stat(const char* path, struct stat* buf)
int p_chdir(const char* path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wchdir(buf);
}
int p_chmod(const char* path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wchmod(buf, mode);
}
int p_rmdir(const char* path)
{
int error;
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
error = _wrmdir(buf);
@@ -347,24 +357,24 @@ int p_rmdir(const char* path)
int p_hide_directory__w32(const char *path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1;
}
char *p_realpath(const char *orig_path, char *buffer)
{
int ret;
- wchar_t orig_path_w[GIT_WIN_PATH];
- wchar_t buffer_w[GIT_WIN_PATH];
+ git_win32_path orig_path_w;
+ git_win32_path buffer_w;
- git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path);
+ git_win32_path_from_c(orig_path_w, orig_path);
/* Implicitly use GetCurrentDirectory which can be a threading issue */
- ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL);
+ ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL);
/* According to MSDN, a return value equals to zero means a failure. */
- if (ret == 0 || ret > GIT_WIN_PATH)
+ if (ret == 0 || ret > GIT_WIN_PATH_UTF16)
buffer = NULL;
else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
@@ -448,18 +458,18 @@ int p_setenv(const char* name, const char* value, int overwrite)
int p_access(const char* path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _waccess(buf, mode);
}
int p_rename(const char *from, const char *to)
{
- wchar_t wfrom[GIT_WIN_PATH];
- wchar_t wto[GIT_WIN_PATH];
+ git_win32_path wfrom;
+ git_win32_path wto;
- git__utf8_to_16(wfrom, GIT_WIN_PATH, from);
- git__utf8_to_16(wto, GIT_WIN_PATH, to);
+ git_win32_path_from_c(wfrom, from);
+ git_win32_path_from_c(wto, to);
return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
}
@@ -513,10 +523,10 @@ p_gmtime_r (const time_t *timer, struct tm *result)
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
-
+
#ifndef _TIMEZONE_DEFINED
#define _TIMEZONE_DEFINED
-struct timezone
+struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h
index 5de7e6f34..cbfe98812 100644
--- a/src/win32/precompiled.h
+++ b/src/win32/precompiled.h
@@ -1,4 +1,5 @@
#include "git2.h"
+#include "common.h"
#include <assert.h>
#include <errno.h>
@@ -6,6 +7,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <fcntl.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index c06f3a8c2..d4dbfbab9 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -70,12 +70,12 @@ void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
}
#endif
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src)
{
- return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length);
+ return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)dest_size);
}
-int git__utf16_to_8(char *out, const wchar_t *input)
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
{
- return WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL);
+ return WideCharToMultiByte(CP_UTF8, 0, src, -1, dest, (int)dest_size, NULL, NULL);
}
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index 6cc9205f7..3af77580e 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -4,16 +4,35 @@
* 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_git_utfconv_h__
+#define INCLUDE_git_utfconv_h__
#include <wchar.h>
+#include "common.h"
-#ifndef INCLUDE_git_utfconv_h__
-#define INCLUDE_git_utfconv_h__
+/* Maximum characters in a Windows path plus one for NUL byte */
+#define GIT_WIN_PATH_UTF16 (260 + 1)
-#define GIT_WIN_PATH (260 + 1)
+/* Maximum bytes necessary to convert a full-length UTF16 path to UTF8 */
+#define GIT_WIN_PATH_UTF8 (260 * 4 + 1)
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src);
-int git__utf16_to_8(char *dest, const wchar_t *src);
+typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
-#endif
+typedef char git_win32_path_as_utf8[GIT_WIN_PATH_UTF8];
+/* dest_size is the size of dest in wchar_t's */
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src);
+/* dest_size is the size of dest in char's */
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
+
+GIT_INLINE(int) git_win32_path_from_c(git_win32_path dest, const char *src)
+{
+ return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src);
+}
+
+GIT_INLINE(int) git_win32_path_to_c(git_win32_path_as_utf8 dest, const wchar_t *src)
+{
+ return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src);
+}
+
+#endif