From a7fbb051ef5e89e866369ad86216eaa415dde58b Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Wed, 18 Apr 2018 22:40:46 +0000 Subject: [PATCH 01/33] repository: being a worktree means we're not really bare We were previously conflating any error into GIT_ENOTFOUND, which might or might not be correct. This fixes the code so a config error is bubbled up, as well as preserving the semantics in the face of worktree-repositories --- src/repository.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/repository.c b/src/repository.c index 90b778e032b..3c898799062 100644 --- a/src/repository.c +++ b/src/repository.c @@ -268,11 +268,15 @@ static int load_config_data(git_repository *repo, const git_config *config) { int is_bare; + int err = git_config_get_bool(&is_bare, config, "core.bare"); + if (err < 0 && err != GIT_ENOTFOUND) + return err; + /* Try to figure out if it's bare, default to non-bare if it's not set */ - if (git_config_get_bool(&is_bare, config, "core.bare") < 0) - repo->is_bare = 0; + if (err != GIT_ENOTFOUND) + repo->is_bare = is_bare && !repo->is_worktree; else - repo->is_bare = is_bare; + repo->is_bare = 0; return 0; } From c62c6379092ba61e6c6d29c191a5b94dbdc8375c Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Fri, 20 Apr 2018 08:38:50 +0000 Subject: [PATCH 02/33] worktree: a worktree can be made from a bare repository --- src/worktree.c | 2 +- tests/worktree/worktree.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/worktree.c b/src/worktree.c index 91527663e6f..6ba1fce33c0 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -139,7 +139,7 @@ static int open_worktree_dir(git_worktree **out, const char *parent, const char if ((wt->name = git__strdup(name)) == NULL || (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL || (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL - || (wt->parent_path = git__strdup(parent)) == NULL) { + || (parent && (wt->parent_path = git__strdup(parent)) == NULL)) { error = -1; goto out; } diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c index 4ac3b8bbab0..8f6bd7c5596 100644 --- a/tests/worktree/worktree.c +++ b/tests/worktree/worktree.c @@ -228,6 +228,26 @@ void test_worktree_worktree__init(void) git_repository_free(repo); } +void test_worktree_worktree__add_from_bare(void) +{ + git_worktree *wt; + git_repository *repo, *wtrepo; + + repo = cl_git_sandbox_init("short_tag.git"); + + cl_assert_equal_i(1, git_repository_is_bare(repo)); + cl_assert_equal_i(0, git_repository_is_worktree(repo)); + + cl_git_pass(git_worktree_add(&wt, repo, "worktree-frombare", "worktree-frombare", NULL)); + cl_git_pass(git_repository_open(&wtrepo, "worktree-frombare")); + cl_assert_equal_i(0, git_repository_is_bare(wtrepo)); + cl_assert_equal_i(1, git_repository_is_worktree(wtrepo)); + + git_worktree_free(wt); + git_repository_free(repo); + git_repository_free(wtrepo); +} + void test_worktree_worktree__add_locked(void) { git_worktree *wt; From a051bce70c44c4ec395a4d9b75fddff6deb105ae Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Mon, 18 Jun 2018 20:37:18 +0000 Subject: [PATCH 03/33] patch_parse: populate line numbers while parsing diffs (cherry picked from commit f9e28026753f7b6c871a160ad584b2dc2639d30f) --- src/patch_parse.c | 7 +++++ tests/diff/parse.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/patch_parse.c b/src/patch_parse.c index acdd45e8237..e02c87cf377 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -563,6 +563,8 @@ static int parse_hunk_body( char c; int origin; int prefix = 1; + int old_lineno = hunk->hunk.old_start + (hunk->hunk.old_lines - oldlines); + int new_lineno = hunk->hunk.new_start + (hunk->hunk.new_lines - newlines); if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') { error = git_parse_err("invalid patch instruction at line %"PRIuZ, @@ -586,11 +588,13 @@ static int parse_hunk_body( case '-': origin = GIT_DIFF_LINE_DELETION; oldlines--; + new_lineno = -1; break; case '+': origin = GIT_DIFF_LINE_ADDITION; newlines--; + old_lineno = -1; break; default: @@ -607,6 +611,9 @@ static int parse_hunk_body( line->content_len = ctx->parse_ctx.line_len - prefix; line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len; line->origin = origin; + line->num_lines = 1; + line->old_lineno = old_lineno; + line->new_lineno = new_lineno; hunk->line_count++; } diff --git a/tests/diff/parse.c b/tests/diff/parse.c index dc2ceefec72..74816f5709a 100644 --- a/tests/diff/parse.c +++ b/tests/diff/parse.c @@ -288,3 +288,74 @@ void test_diff_parse__patch_roundtrip_succeeds(void) git_buf_free(&patchbuf); git_buf_free(&diffbuf); } + +#define cl_assert_equal_i_src(i1,i2,file,line) clar__assert_equal(file,line,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) + +static void cl_git_assert_lineinfo_(int old_lineno, int new_lineno, int num_lines, git_patch *patch, size_t hunk_idx, size_t line_idx, const char *file, int lineno) +{ + const git_diff_line *line; + + cl_git_expect(git_patch_get_line_in_hunk(&line, patch, hunk_idx, line_idx), 0, file, lineno); + cl_assert_equal_i_src(old_lineno, line->old_lineno, file, lineno); + cl_assert_equal_i_src(new_lineno, line->new_lineno, file, lineno); + cl_assert_equal_i_src(num_lines, line->num_lines, file, lineno); +} + +#define cl_git_assert_lineinfo(old, new, num, p, h, l) \ + cl_git_assert_lineinfo_(old,new,num,p,h,l,__FILE__,__LINE__) + + +void test_diff_parse__issue4672(void) +{ + const char *text = "diff --git a/a b/a\n" + "index 7f129fd..af431f2 100644\n" + "--- a/a\n" + "+++ b/a\n" + "@@ -3 +3 @@\n" + "-a contents 2\n" + "+a contents\n"; + + git_diff *diff; + git_patch *patch; + const git_diff_hunk *hunk; + size_t n, l = 0; + + cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text))); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0)); + + cl_git_assert_lineinfo(3, -1, 1, patch, 0, l++); + cl_git_assert_lineinfo(-1, 3, 1, patch, 0, l++); + + cl_assert_equal_i(n, l); + + git_patch_free(patch); + git_diff_free(diff); +} + +void test_diff_parse__lineinfo(void) +{ + const char *text = PATCH_ORIGINAL_TO_CHANGE_MIDDLE; + git_diff *diff; + git_patch *patch; + const git_diff_hunk *hunk; + size_t n, l = 0; + + cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text))); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0)); + + cl_git_assert_lineinfo(3, 3, 1, patch, 0, l++); + cl_git_assert_lineinfo(4, 4, 1, patch, 0, l++); + cl_git_assert_lineinfo(5, 5, 1, patch, 0, l++); + cl_git_assert_lineinfo(6, -1, 1, patch, 0, l++); + cl_git_assert_lineinfo(-1, 6, 1, patch, 0, l++); + cl_git_assert_lineinfo(7, 7, 1, patch, 0, l++); + cl_git_assert_lineinfo(8, 8, 1, patch, 0, l++); + cl_git_assert_lineinfo(9, 9, 1, patch, 0, l++); + + cl_assert_equal_i(n, l); + + git_patch_free(patch); + git_diff_free(diff); +} From c37c6ed84f6133b1fd5d42b3899c7bb4ff880d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 18 Jul 2018 21:04:13 +0200 Subject: [PATCH 04/33] tree: accept null ids in existing trees when updating When we add entries to a treebuilder we validate them. But we validate even those that we're adding because they exist in the base tree. This disables using the normal mechanisms on these trees, even to fix them. Keep track of whether the entry we're appending comes from an existing tree and bypass the name and id validation if it's from existing data. (cherry picked from commit 2dff7e282da77f6b791e843ec267d9ddecabc187) --- src/tree.c | 14 ++++++++------ tests/object/tree/update.c | 15 +++++++++++++++ .../39/6c7f1adb7925f51ba13a75f48252f44c5a14a2 | Bin 0 -> 71 bytes 3 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2 diff --git a/src/tree.c b/src/tree.c index 12622975abb..f52ab0fe6c6 100644 --- a/src/tree.c +++ b/src/tree.c @@ -490,15 +490,16 @@ static int append_entry( git_treebuilder *bld, const char *filename, const git_oid *id, - git_filemode_t filemode) + git_filemode_t filemode, + bool from_tree) { git_tree_entry *entry; int error = 0; - if (!valid_entry_name(bld->repo, filename)) + if (!from_tree && !valid_entry_name(bld->repo, filename)) return tree_error("failed to insert entry: invalid name for a tree entry", filename); - if (git_oid_iszero(id)) + if (!from_tree && git_oid_iszero(id)) return tree_error("failed to insert entry: invalid null OID for a tree entry", filename); entry = alloc_entry(filename, strlen(filename), id); @@ -596,12 +597,12 @@ static int write_tree( last_comp = subdir; } - error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); + error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, false); git__free(subdir); if (error < 0) goto on_error; } else { - error = append_entry(bld, filename, &entry->id, entry->mode); + error = append_entry(bld, filename, &entry->id, entry->mode, false); if (error < 0) goto on_error; } @@ -699,7 +700,8 @@ int git_treebuilder_new( if (append_entry( bld, entry_src->filename, entry_src->oid, - entry_src->attr) < 0) + entry_src->attr, + true) < 0) goto on_error; } } diff --git a/tests/object/tree/update.c b/tests/object/tree/update.c index b76e8612a96..167faecbb08 100644 --- a/tests/object/tree/update.c +++ b/tests/object/tree/update.c @@ -284,3 +284,18 @@ void test_object_tree_update__add_conflict2(void) cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates)); } + +void test_object_tree_update__remove_invalid_submodule(void) +{ + git_tree *baseline; + git_oid updated_tree_id, baseline_id; + git_tree_update updates[] = { + {GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB, "submodule"}, + }; + + cl_git_pass(git_oid_fromstr(&baseline_id, "396c7f1adb7925f51ba13a75f48252f44c5a14a2")); + cl_git_pass(git_tree_lookup(&baseline, g_repo, &baseline_id)); + cl_git_pass(git_tree_create_updated(&updated_tree_id, g_repo, baseline, 1, updates)); + + git_tree_free(baseline); +} diff --git a/tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2 b/tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2 new file mode 100644 index 0000000000000000000000000000000000000000..667704b32101c48fe3606383c5135f87b3f7704f GIT binary patch literal 71 zcmV-N0J#5n0V^p=O;s>AVK6i>Ff%bx2y%6F@paY9O<{O8_tET47q2;ccWbUIkGgT_ dNl)-ZLo)*)P$(`<%FRzH%}Hgz1^|d*7gxXN8 Date: Fri, 27 Jul 2018 12:00:37 +0200 Subject: [PATCH 05/33] tree: rename from_tree to validate and clarify the tree in the test (cherry picked from commit f00db9ed67423b04976f8d20b0de2ee1fb7c3993) --- src/tree.c | 12 ++++++------ tests/object/tree/update.c | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tree.c b/src/tree.c index f52ab0fe6c6..a014ce8071b 100644 --- a/src/tree.c +++ b/src/tree.c @@ -491,15 +491,15 @@ static int append_entry( const char *filename, const git_oid *id, git_filemode_t filemode, - bool from_tree) + bool validate) { git_tree_entry *entry; int error = 0; - if (!from_tree && !valid_entry_name(bld->repo, filename)) + if (validate && !valid_entry_name(bld->repo, filename)) return tree_error("failed to insert entry: invalid name for a tree entry", filename); - if (!from_tree && git_oid_iszero(id)) + if (validate && git_oid_iszero(id)) return tree_error("failed to insert entry: invalid null OID for a tree entry", filename); entry = alloc_entry(filename, strlen(filename), id); @@ -597,12 +597,12 @@ static int write_tree( last_comp = subdir; } - error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, false); + error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, true); git__free(subdir); if (error < 0) goto on_error; } else { - error = append_entry(bld, filename, &entry->id, entry->mode, false); + error = append_entry(bld, filename, &entry->id, entry->mode, true); if (error < 0) goto on_error; } @@ -701,7 +701,7 @@ int git_treebuilder_new( bld, entry_src->filename, entry_src->oid, entry_src->attr, - true) < 0) + false) < 0) goto on_error; } } diff --git a/tests/object/tree/update.c b/tests/object/tree/update.c index 167faecbb08..41b50f3e98c 100644 --- a/tests/object/tree/update.c +++ b/tests/object/tree/update.c @@ -293,6 +293,7 @@ void test_object_tree_update__remove_invalid_submodule(void) {GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB, "submodule"}, }; + /* This tree contains a submodule with an all-zero commit for a submodule named 'submodule' */ cl_git_pass(git_oid_fromstr(&baseline_id, "396c7f1adb7925f51ba13a75f48252f44c5a14a2")); cl_git_pass(git_tree_lookup(&baseline, g_repo, &baseline_id)); cl_git_pass(git_tree_create_updated(&updated_tree_id, g_repo, baseline, 1, updates)); From e129540060d7461679f6ec340d69fb41a4f58118 Mon Sep 17 00:00:00 2001 From: Julian Ganz Date: Sat, 4 Aug 2018 19:30:40 +0200 Subject: [PATCH 06/33] parse: Do not initialize the content in context to NULL String operations in libgit2 are supposed to never receive `NULL`, e.g. they are not `NULL`-save. In the case of `git__linenlen()`, invocation with `NULL` leads to undefined behavior. In a `git_parse_ctx` however, the `content` field used in these operations was initialized to `NULL` if the `git_parse_ctx_init()` was called with `NULL` for `content` or `0` for `content_len`. For the latter case, the initialization function even contained some logic for initializing `content` with `NULL`. This commit mitigates triggering undefined behavior by rewriting the logic. Now `content` is always initialized to a non-null buffer. Instead of a null buffer, an empty string is used for denoting an empty buffer. (cherry picked from commit d1bfe614aa20a0bdaf76c6d418176320ab11baf4) --- src/parse.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/parse.c b/src/parse.c index 6b8902c3585..b04fda36b23 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,12 +8,14 @@ int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len) { - if (content_len) + if (content && content_len) { ctx->content = content; - else - ctx->content = NULL; + ctx->content_len = content_len; + } else { + ctx->content = ""; + ctx->content_len = 0; + } - ctx->content_len = content_len; ctx->remain = ctx->content; ctx->remain_len = ctx->content_len; ctx->line = ctx->remain; @@ -26,6 +28,7 @@ int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_l void git_parse_ctx_clear(git_parse_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); + ctx->content = ""; } void git_parse_advance_line(git_parse_ctx *ctx) From 9023f8f66a4dc03597e75a7527f603cc106c16f7 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sun, 22 Jul 2018 23:31:19 +0000 Subject: [PATCH 07/33] config_file: Don't crash on options without a section (cherry picked from commit c4d7fa951acd066fd80d83954dd6082c1c7e9e1e) --- src/config_file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config_file.c b/src/config_file.c index 0f4eb0e2880..c95ad6705f9 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1075,6 +1075,10 @@ static int read_on_variable( GIT_UNUSED(line); GIT_UNUSED(line_len); + if (!current_section) { + giterr_set(GITERR_CONFIG, "no section for key: %s", var_name); + return -1; + } git__strtolower(var_name); git_buf_printf(&buf, "%s.%s", current_section, var_name); git__free(var_name); From ab3f609981cd43ed09ea4d3522738322d8e81604 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sun, 5 Aug 2018 14:25:22 +0000 Subject: [PATCH 08/33] Don't error on missing section, just continue (cherry picked from commit 019409be004fb73071415750e98db03d33fada47) --- src/config_file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index c95ad6705f9..8808a161cfa 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1075,12 +1075,12 @@ static int read_on_variable( GIT_UNUSED(line); GIT_UNUSED(line_len); - if (!current_section) { - giterr_set(GITERR_CONFIG, "no section for key: %s", var_name); - return -1; + if (current_section) { + git_buf_puts(&buf, current_section); + git_buf_putc(&buf, '.'); } git__strtolower(var_name); - git_buf_printf(&buf, "%s.%s", current_section, var_name); + git_buf_puts(&buf, var_name); git__free(var_name); if (git_buf_oom(&buf)) { From 831a709d61724552bc7d0673e5a51952aa956b36 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sun, 5 Aug 2018 14:37:08 +0000 Subject: [PATCH 09/33] Add a comment (cherry picked from commit ec76a1aa43321db2451e747d7a4408e780991c4a) --- src/config_file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config_file.c b/src/config_file.c index 8808a161cfa..0c05fb51500 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1076,6 +1076,10 @@ static int read_on_variable( GIT_UNUSED(line_len); if (current_section) { + /* TODO: Once warnings lang, we should likely warn + * here. Git appears to warn in most cases if it sees + * un-namespaced config options. + */ git_buf_puts(&buf, current_section); git_buf_putc(&buf, '.'); } From ef52371200e0ff74c71c33f85fdbc21031a1adcd Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Tue, 14 Aug 2018 03:54:01 +0000 Subject: [PATCH 10/33] Write a test. (cherry picked from commit f140950066cf2989912e18ad92ec088f624b8bf2) --- tests/config/read.c | 15 +++++++++++++++ tests/resources/config/config-nosection | 1 + 2 files changed, 16 insertions(+) create mode 100644 tests/resources/config/config-nosection diff --git a/tests/config/read.c b/tests/config/read.c index a34455a0cb6..8a447d3af75 100644 --- a/tests/config/read.c +++ b/tests/config/read.c @@ -748,3 +748,18 @@ void test_config_read__bom(void) git_config_free(cfg); git_buf_free(&buf); } + +/* This would ideally issue a warning, if we had a way to do so. */ +void test_config_read__nosection(void) +{ + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection"))); + + cl_git_pass(git_config_get_string_buf(&buf, cfg, "key")); + cl_assert_equal_s(buf.ptr, "value"); + + git_buf_free(&buf); + git_config_free(cfg); +} diff --git a/tests/resources/config/config-nosection b/tests/resources/config/config-nosection new file mode 100644 index 00000000000..dd2ee085701 --- /dev/null +++ b/tests/resources/config/config-nosection @@ -0,0 +1 @@ +key = value From f4a7652a6fecd2758cb589ff6567254e17aefa41 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Tue, 14 Aug 2018 04:22:14 +0000 Subject: [PATCH 11/33] Fix the test and comment. (cherry picked from commit 6698e0562d0f782903f28c224c879da7c2abf674) --- tests/config/read.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/config/read.c b/tests/config/read.c index 8a447d3af75..64656713463 100644 --- a/tests/config/read.c +++ b/tests/config/read.c @@ -749,16 +749,34 @@ void test_config_read__bom(void) git_buf_free(&buf); } +static int read_nosection_cb(const git_config_entry *entry, void *payload) { + int *seen = (int*)payload; + if (strcmp(entry->name, "key") == 0) { + (*seen)++; + } + return 0; +} + /* This would ideally issue a warning, if we had a way to do so. */ void test_config_read__nosection(void) { git_config *cfg; git_buf buf = GIT_BUF_INIT; + int seen = 0; cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection"))); - cl_git_pass(git_config_get_string_buf(&buf, cfg, "key")); - cl_assert_equal_s(buf.ptr, "value"); + /* + * Given a key with no section, we do not allow reading it, + * but we do include it in an iteration over the config + * store. This appears to match how git's own APIs (and + * git-config(1)) behave. + */ + + cl_git_fail_with(git_config_get_string_buf(&buf, cfg, "key"), GIT_EINVALIDSPEC); + + cl_git_pass(git_config_foreach(cfg, read_nosection_cb, &seen)); + cl_assert_equal_i(seen, 1); git_buf_free(&buf); git_config_free(cfg); From fad596582152112b2fc935fdf4afe98c2890f713 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 9 Aug 2018 12:48:26 +0200 Subject: [PATCH 12/33] diff: fix OOM on AIX when finding similar deltas in empty diff The function `git_diff_find_similar` keeps a function of cache similarity metrics signatures, whose size depends on the number of deltas passed in via the `diff` parameter. In case where the diff is empty and thus doesn't have any deltas at all, we may end up allocating this cache via a call to `git__calloc(0, sizeof(void *))`. At least on AIX, allocating 0 bytes will result in a `NULL` pointer being returned, which causes us to erroneously return an OOM error. Fix this situation by simply returning early in case where we are being passed an empty diff, as we cannot find any similarities in that case anyway. (cherry picked from commit c65568d8c8c1bf4920393190e862819cd263f439) --- src/diff_tform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index bc664dd054c..a9706e00255 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -822,7 +822,7 @@ int git_diff_find_similar( num_deltas = diff->deltas.length; /* TODO: maybe abort if deltas.length > rename_limit ??? */ - if (!git__is_uint32(num_deltas)) + if (!num_deltas || !git__is_uint32(num_deltas)) goto cleanup; /* No flags set; nothing to do */ From 0fefd8994a10163da97b2cacfe79f9d1d29f0e08 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Fri, 17 Aug 2018 15:56:30 +0000 Subject: [PATCH 13/33] util: make the qsort_r check work on macOS This performs a compile-check by using CMake support, to differentiate the GNU version from the BSD version of qsort_r. Module taken from 4f252abea5f1d17c60f6ff115c9c44cc0b6f1df6, which I've checked against CMake 2.8.11. (cherry picked from commit 1a9cc18260b68b149476adb6f39e37ab47d3d21f) --- CMakeLists.txt | 1 + cmake/Modules/CheckPrototypeDefinition.c.in | 29 ++++++ cmake/Modules/CheckPrototypeDefinition.cmake | 96 ++++++++++++++++++++ src/CMakeLists.txt | 17 +++- src/util.c | 6 +- 5 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 cmake/Modules/CheckPrototypeDefinition.c.in create mode 100644 cmake/Modules/CheckPrototypeDefinition.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c159027102..1beb4295da5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ INCLUDE(CheckLibraryExists) INCLUDE(CheckFunctionExists) INCLUDE(CheckSymbolExists) INCLUDE(CheckStructHasMember) +INCLUDE(CheckPrototypeDefinition) # Added in CMake 3.0 INCLUDE(AddCFlagIfSupported) INCLUDE(FindPkgLibraries) INCLUDE(FindThreads) diff --git a/cmake/Modules/CheckPrototypeDefinition.c.in b/cmake/Modules/CheckPrototypeDefinition.c.in new file mode 100644 index 00000000000..a97344ac3e1 --- /dev/null +++ b/cmake/Modules/CheckPrototypeDefinition.c.in @@ -0,0 +1,29 @@ +@CHECK_PROTOTYPE_DEFINITION_HEADER@ + +static void cmakeRequireSymbol(int dummy, ...) { + (void) dummy; +} + +static void checkSymbol(void) { +#ifndef @CHECK_PROTOTYPE_DEFINITION_SYMBOL@ + cmakeRequireSymbol(0, &@CHECK_PROTOTYPE_DEFINITION_SYMBOL@); +#endif +} + +@CHECK_PROTOTYPE_DEFINITION_PROTO@ { + return @CHECK_PROTOTYPE_DEFINITION_RETURN@; +} + +#ifdef __CLASSIC_C__ +int main() { + int ac; + char*av[]; +#else +int main(int ac, char *av[]) { +#endif + checkSymbol(); + if (ac > 1000) { + return *av[0]; + } + return 0; +} diff --git a/cmake/Modules/CheckPrototypeDefinition.cmake b/cmake/Modules/CheckPrototypeDefinition.cmake new file mode 100644 index 00000000000..244b9b53b41 --- /dev/null +++ b/cmake/Modules/CheckPrototypeDefinition.cmake @@ -0,0 +1,96 @@ +# - Check if the protoype we expect is correct. +# check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE) +# FUNCTION - The name of the function (used to check if prototype exists) +# PROTOTYPE- The prototype to check. +# RETURN - The return value of the function. +# HEADER - The header files required. +# VARIABLE - The variable to store the result. +# Example: +# check_prototype_definition(getpwent_r +# "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)" +# "NULL" +# "unistd.h;pwd.h" +# SOLARIS_GETPWENT_R) +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +#============================================================================= +# Copyright 2005-2009 Kitware, Inc. +# Copyright 2010-2011 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +# + +get_filename_component(__check_proto_def_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) + +function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE) + + if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$") + set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n") + + set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS}) + if (CMAKE_REQUIRED_LIBRARIES) + set(CHECK_PROTOTYPE_DEFINITION_LIBS + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + else(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_PROTOTYPE_DEFINITION_LIBS) + endif(CMAKE_REQUIRED_LIBRARIES) + if (CMAKE_REQUIRED_INCLUDES) + set(CMAKE_SYMBOL_EXISTS_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_SYMBOL_EXISTS_INCLUDES) + endif(CMAKE_REQUIRED_INCLUDES) + + foreach(_FILE ${_HEADER}) + set(CHECK_PROTOTYPE_DEFINITION_HEADER + "${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n") + endforeach(_FILE) + + set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION}) + set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE}) + set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN}) + + configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY) + + file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE) + + try_compile(${_VARIABLE} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} + "${CHECK_PROTOTYPE_DEFINITION_LIBS}" + "${CMAKE_SYMBOL_EXISTS_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + + if (${_VARIABLE}) + set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") + message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n" + "${OUTPUT}\n\n") + else (${_VARIABLE}) + message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False") + set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n" + "${OUTPUT}\n\n${_SOURCE}\n\n") + endif (${_VARIABLE}) + endif("${_VARIABLE}" MATCHES "^${_VARIABLE}$") + +endfunction(CHECK_PROTOTYPE_DEFINITION) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cfb2d3117c..2080933f005 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,10 +57,19 @@ IF (HAVE_FUTIMENS) SET(GIT_USE_FUTIMENS 1) ENDIF () -CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R) -IF (HAVE_QSORT_R) - ADD_DEFINITIONS(-DHAVE_QSORT_R) -ENDIF () +CHECK_PROTOTYPE_DEFINITION(qsort_r + "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))" + "" "stdlib.h" HAVE_QSORT_R_BSD) +IF (HAVE_QSORT_R_BSD) + ADD_DEFINITIONS(-DHAVE_QSORT_R_BSD) +ENDIF() + +CHECK_PROTOTYPE_DEFINITION(qsort_r + "void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)" + "" "stdlib.h" HAVE_QSORT_R_GNU) +IF (HAVE_QSORT_R_GNU) + ADD_DEFINITIONS(-DHAVE_QSORT_R_GNU) +ENDIF() CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S) IF (HAVE_QSORT_S) diff --git a/src/util.c b/src/util.c index 48cac852e7f..911921857d6 100644 --- a/src/util.c +++ b/src/util.c @@ -690,7 +690,7 @@ size_t git__unescape(char *str) return (pos - str); } -#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD)) +#if defined(HAVE_QSORT_S) || defined(HAVE_QSORT_R_BSD) typedef struct { git__sort_r_cmp cmp; void *payload; @@ -707,10 +707,10 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(HAVE_QSORT_R) && defined(BSD) +#if defined(HAVE_QSORT_R_BSD) git__qsort_r_glue glue = { cmp, payload }; qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp); -#elif defined(HAVE_QSORT_R) && defined(__GLIBC__) +#elif defined(HAVE_QSORT_R_GNU) qsort_r(els, nel, elsize, cmp, payload); #elif defined(HAVE_QSORT_S) git__qsort_r_glue glue = { cmp, payload }; From a5a0347db2638939ea2f4073301ea91439814fe1 Mon Sep 17 00:00:00 2001 From: abyss7 Date: Thu, 16 Aug 2018 22:45:43 +0300 Subject: [PATCH 14/33] Fix leak in index.c (cherry picked from commit 581d5492f6afdaf31a10e51187466a80ffc9f76f) --- src/index.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index 7a8bb2c6fc4..2f46cb041c1 100644 --- a/src/index.c +++ b/src/index.c @@ -1774,7 +1774,8 @@ int git_index_conflict_add(git_index *index, if (entries[i] && !valid_filemode(entries[i]->mode)) { giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry", i + 1); - return -1; + ret = -1; + goto on_error; } } From ef7d7defd6fdd504f2c34f66080e31a440a0fdfd Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Fri, 17 Aug 2018 00:51:51 +0200 Subject: [PATCH 15/33] worktree: unlock should return 1 when the worktree isn't locked The documentation states that git_worktree_unlock returns 0 on success, and 1 on success if the worktree wasn't locked. Turns out we were returning 0 in any of those cases. (cherry picked from commit 59c2e70eeee8b2bae79d05060599114a5f6d737a) --- src/worktree.c | 2 +- tests/worktree/worktree.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/worktree.c b/src/worktree.c index 6ba1fce33c0..4acf61b8b81 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -417,7 +417,7 @@ int git_worktree_unlock(git_worktree *wt) assert(wt); if (!git_worktree_is_locked(NULL, wt)) - return 0; + return 1; if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0) return -1; diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c index 8f6bd7c5596..9026a7f838c 100644 --- a/tests/worktree/worktree.c +++ b/tests/worktree/worktree.c @@ -455,7 +455,7 @@ void test_worktree_worktree__unlock_unlocked_worktree(void) cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_assert(!git_worktree_is_locked(NULL, wt)); - cl_assert(git_worktree_unlock(wt) == 0); + cl_assert_equal_i(1, git_worktree_unlock(wt)); cl_assert(!wt->locked); git_worktree_free(wt); @@ -468,7 +468,7 @@ void test_worktree_worktree__unlock_locked_worktree(void) cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_git_pass(git_worktree_lock(wt, NULL)); cl_assert(git_worktree_is_locked(NULL, wt)); - cl_git_pass(git_worktree_unlock(wt)); + cl_assert_equal_i(0, git_worktree_unlock(wt)); cl_assert(!wt->locked); git_worktree_free(wt); From c0e038a29b28b43e5bf5e4c246a8fa5fa2bb2cd9 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Tue, 21 Aug 2018 01:12:11 +0200 Subject: [PATCH 16/33] revwalk: The left operand of '<' is a garbage value At line 594, we do this : if (error < 0) return error; but if nothing was pushed in a GIT_SORT_TIME revwalk, we'd return uninitialized stack data. (cherry picked from commit aa8cb5866f1eabd92c8c08f7a8610d42df07375f) --- src/revwalk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/revwalk.c b/src/revwalk.c index eb228a52254..290313a3bcd 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -522,7 +522,7 @@ static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, g static int prepare_walk(git_revwalk *walk) { - int error; + int error = 0; git_commit_list *list, *commits = NULL; git_commit_list_node *next; From 01b8bbf4431f367c04bd7afad632c08f6881f7cf Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Tue, 21 Aug 2018 01:55:56 +0200 Subject: [PATCH 17/33] remote: set the error before cleanup Otherwise we'll return stack data to the caller. (cherry picked from commit 22d013b657c5957fde31641351cb72d08cc192ae) --- src/remote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index d8a6b991de4..b3252b653e8 100644 --- a/src/remote.c +++ b/src/remote.c @@ -428,7 +428,7 @@ static int get_optional_config( int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) { - git_remote *remote; + git_remote *remote = NULL; git_buf buf = GIT_BUF_INIT; const char *val; int error = 0; @@ -510,7 +510,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0) goto cleanup; - if (download_tags_value(remote, config) < 0) + if ((error = download_tags_value(remote, config)) < 0) goto cleanup; if ((error = lookup_remote_prune_config(remote, config, name)) < 0) From 54e7cf7ee0bbed03b0ea64f70346d37ee59eae50 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 24 Aug 2018 09:53:39 +0200 Subject: [PATCH 18/33] cmake: detect and use libc-provided iconv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While most systems provide a separate iconv library against which applications can link, musl based systems do not provide such a library. Instead, iconv functions are directly included in the C library. As our current CMake module to locate the iconv library only checks whether a library exists somewhere in the typical library directories, we will never build libgit2 with libiconv support on such systems. Extend the iconv module to also search whether libc provides iconv functions, which we do by checking whether the `iconv_open` function exists inside of libc. If this is the case, we will default to use the libc provided one instead of trying to use a separate libiconv. While this changes which iconv we use on systems where both libc and an external libiconv exist, to the best of my knowledge common systems only provide either one or the other. Note that libiconv support in musl is held kind of basic. To quote musl libc's page on functional differences from glibc [1]: The iconv implementation musl is very small and oriented towards being unobtrusive to static link. Its character set/encoding coverage is very strong for its size, but not comprehensive like glibc’s. As we assume iconv to be a lot more capable than what musl provides, some of our tests will fail if using iconv on musl-based platforms. [1]: https://wiki.musl-libc.org/functional-differences-from-glibc.html (cherry picked from commit 2e2d8c6493ec4d151c55d7421c93126267ee8e6d) --- cmake/Modules/FindIconv.cmake | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cmake/Modules/FindIconv.cmake b/cmake/Modules/FindIconv.cmake index 95414bda6f6..3c66cdad41c 100644 --- a/cmake/Modules/FindIconv.cmake +++ b/cmake/Modules/FindIconv.cmake @@ -12,14 +12,19 @@ IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) ENDIF() FIND_PATH(ICONV_INCLUDE_DIR iconv.h) +CHECK_FUNCTION_EXISTS(iconv_open libc_has_iconv) FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c) -IF(ICONV_INCLUDE_DIR AND iconv_lib) - SET(ICONV_FOUND TRUE) -ENDIF() - -IF(ICONV_FOUND) - # split iconv into -L and -l linker options, so we can set them for pkg-config +IF(ICONV_INCLUDE_DIR AND libc_has_iconv) + SET(ICONV_FOUND TRUE) + SET(ICONV_LIBRARIES "") + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS "Found Iconv: provided by libc") + ENDIF(NOT ICONV_FIND_QUIETLY) +ELSEIF(ICONV_INCLUDE_DIR AND iconv_lib) + SET(ICONV_FOUND TRUE) + # split iconv into -L and -l linker options, so we can + # set them for pkg-config GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH) GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE) STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name}) From 5fc7d59c79d0e4a86de33c4b9e22713123fb16b7 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Tue, 11 Sep 2018 15:53:35 +0200 Subject: [PATCH 19/33] index: release the snapshot instead of freeing the index Previously we would assert in index_free because the reader incrementation would not be balanced. Release the snapshot normally, so the variable gets decremented before the index is freed. (cherry picked from commit c70713d6e4af563696563e410864eb4a6507757d) --- src/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index 2f46cb041c1..2b47e4dc3a7 100644 --- a/src/index.c +++ b/src/index.c @@ -3510,7 +3510,7 @@ int git_index_snapshot_new(git_vector *snap, git_index *index) error = git_vector_dup(snap, &index->entries, index->entries._cmp); if (error < 0) - git_index_free(index); + git_index_snapshot_release(snap, index); return error; } From ebb0e37ea69c9009c239b7b6d378f57c7802e012 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Wed, 26 Sep 2018 19:15:35 +0000 Subject: [PATCH 20/33] vector: do not malloc 0-length vectors on dup (cherry picked from commit fa48d2ea7d2d5dc9620e5c9f05ba8d788775582b) --- src/vector.c | 18 ++++++++++-------- tests/core/vector.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/vector.c b/src/vector.c index b12fa942db0..aac4863d426 100644 --- a/src/vector.c +++ b/src/vector.c @@ -50,22 +50,24 @@ int git_vector_size_hint(git_vector *v, size_t size_hint) int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp) { - size_t bytes; - assert(v && src); - GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *)); - - v->_alloc_size = src->length; + v->_alloc_size = 0; + v->contents = NULL; v->_cmp = cmp ? cmp : src->_cmp; v->length = src->length; v->flags = src->flags; if (cmp != src->_cmp) git_vector_set_sorted(v, 0); - v->contents = git__malloc(bytes); - GITERR_CHECK_ALLOC(v->contents); - memcpy(v->contents, src->contents, bytes); + if (src->length) { + size_t bytes; + GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *)); + v->contents = git__malloc(bytes); + GITERR_CHECK_ALLOC(v->contents); + v->_alloc_size = src->length; + memcpy(v->contents, src->contents, bytes); + } return 0; } diff --git a/tests/core/vector.c b/tests/core/vector.c index c2e5d3f34c9..371c6816059 100644 --- a/tests/core/vector.c +++ b/tests/core/vector.c @@ -407,3 +407,22 @@ void test_core_vector__reverse(void) git_vector_free(&v); } + +void test_core_vector__dup_empty_vector(void) +{ + git_vector v = GIT_VECTOR_INIT; + git_vector dup = GIT_VECTOR_INIT; + void *dummy = 0xDEAFBEEB; + + cl_assert_equal_i(0, v.length); + + cl_git_pass(git_vector_dup(&dup, &v, v._cmp)); + cl_assert_equal_i(0, dup._alloc_size); + cl_assert_equal_i(0, dup.length); + + cl_git_pass(git_vector_insert(&dup, dummy)); + cl_assert_equal_i(8, dup._alloc_size); + cl_assert_equal_i(1, dup.length); + + git_vector_free(&dup); +} From b133ab9be9b8314e48585e5026698cd8a533bbcd Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Wed, 26 Sep 2018 21:17:39 +0200 Subject: [PATCH 21/33] vector: do not realloc 0-size vectors (cherry picked from commit e0afd1c21c4421cec4f67162021f835e2bbb7df6) --- src/vector.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vector.c b/src/vector.c index aac4863d426..98aa7bb2fd2 100644 --- a/src/vector.c +++ b/src/vector.c @@ -32,6 +32,9 @@ GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size) { void *new_contents; + if (new_size == 0) + return 0; + new_contents = git__reallocarray(v->contents, new_size, sizeof(void *)); GITERR_CHECK_ALLOC(new_contents); From 3cabf8d15e00d7d71aa4df01af07d0dbc974afe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 17 Sep 2018 14:39:58 +0200 Subject: [PATCH 22/33] revwalk: use the max value for a signed integer When porting, we overlooked that the difference between git's and our's time representation and copied their way of getting the max value. Unfortunately git was using unsigned integers, so `~0ll` does correspond to their max value, whereas for us it corresponds to `-1`. This means that we always consider the last date to be smaller than the current commit's and always think commits are interesting. Change the initial value to the macro that gives us the maximum value on each platform so we can accurately consider commits interesting or not. (cherry picked from commit 46f35127b6fcfab87cb80d1b772ac7c662eafd38) --- src/revwalk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/revwalk.c b/src/revwalk.c index 290313a3bcd..7f50e9cd8b6 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -401,7 +401,7 @@ static int still_interesting(git_commit_list *list, int64_t time, int slop) static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits) { int error, slop = SLOP; - int64_t time = ~0ll; + int64_t time = INT64_MAX; git_commit_list *list = commits; git_commit_list *newlist = NULL; git_commit_list **p = &newlist; From b69089fd2aefecb028be2a067b4dda5fa906c0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 17 Sep 2018 14:49:46 +0200 Subject: [PATCH 23/33] revwalk: only check the first commit in the list for an earlier timestamp This is not a big deal, but it does make us match git more closely by checking only the first. The lists are sorted already, so there should be no functional difference other than removing a possible check from every iteration in the loop. (cherry picked from commit 12a1790d8e71087056d2b2de936ddae439e1d94c) --- src/revwalk.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/revwalk.c b/src/revwalk.c index 7f50e9cd8b6..f11dc0630ea 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -384,10 +384,16 @@ static int still_interesting(git_commit_list *list, int64_t time, int slop) if (!list) return 0; + /* + * If the destination list has commits with an earlier date than our + * source, we want to reset the slop counter as we're not done. + */ + if (time <= list->item->time) + return SLOP; + for (; list; list = list->next) { /* - * If the destination list has commits with an earlier date than - * our source or if it still contains interesting commits we + * If the destination list still contains interesting commits we * want to continue looking. */ if (!list->item->uninteresting || list->item->time > time) From e91d6b5ef927b29585879763af46ec986ea756da Mon Sep 17 00:00:00 2001 From: Anders Borum Date: Thu, 27 Sep 2018 11:18:00 +0200 Subject: [PATCH 24/33] fix check if blob is uninteresting when inserting tree to packbuilder Blobs that have been marked as uninteresting should not be inserted into packbuilder when inserting a tree. The check as to whether a blob was uninteresting looked at the status for the tree itself instead of the blob. This could cause significantly larger packfiles. (cherry picked from commit b36cc7a4013a47856dade4226edc657906b82431) --- src/pack-objects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pack-objects.c b/src/pack-objects.c index e9245143ceb..355b9e1e0d5 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -1666,7 +1666,7 @@ int insert_tree(git_packbuilder *pb, git_tree *tree) break; case GIT_OBJ_BLOB: - if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0) + if ((error = retrieve_object(&obj, pb, entry_id)) < 0) return error; if (obj->uninteresting) continue; From 2e7d53ac21b58875ea5b8722ed1c29bca1637ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 17 Sep 2018 20:38:05 +0200 Subject: [PATCH 25/33] cmake: enable -Wformat and -Wformat-security We do not currently have any warnings in this regard, but it's good practice to have them on in case we introduce something. (cherry picked from commit f2c1153d4fa09a36be7c6b87e4f55325f6e5031e) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1beb4295da5..106469377b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,8 @@ ELSE () ENABLE_WARNINGS(shift-count-overflow) DISABLE_WARNINGS(unused-const-variable) DISABLE_WARNINGS(unused-function) + ENABLE_WARNINGS(format) + ENABLE_WARNINGS(format-security) IF (APPLE) # Apple deprecated OpenSSL DISABLE_WARNINGS(deprecated-declarations) From 0952278f5097c5a49fae7ef36d3b291279bce789 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 5 Oct 2018 10:27:33 +0200 Subject: [PATCH 26/33] tests: fix warning for implicit conversion of integer to pointer GCC warns by default when implicitly converting integers to pointers or the other way round, and commit fa48d2ea7 (vector: do not malloc 0-length vectors on dup, 2018-09-26) introduced such an implicit conversion into our vector tests. While this is totally fine in this test, as the pointer's value is never being used in the first place, we can trivially avoid the warning by instead just inserting a pointer for a variable allocated on the stack into the vector. (cherry picked from commit dbb4a5866fcbb121000a705e074f679445d6916b) --- tests/core/vector.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/vector.c b/tests/core/vector.c index 371c6816059..91452404c8b 100644 --- a/tests/core/vector.c +++ b/tests/core/vector.c @@ -412,7 +412,7 @@ void test_core_vector__dup_empty_vector(void) { git_vector v = GIT_VECTOR_INIT; git_vector dup = GIT_VECTOR_INIT; - void *dummy = 0xDEAFBEEB; + int dummy; cl_assert_equal_i(0, v.length); @@ -420,7 +420,7 @@ void test_core_vector__dup_empty_vector(void) cl_assert_equal_i(0, dup._alloc_size); cl_assert_equal_i(0, dup.length); - cl_git_pass(git_vector_insert(&dup, dummy)); + cl_git_pass(git_vector_insert(&dup, &dummy)); cl_assert_equal_i(8, dup._alloc_size); cl_assert_equal_i(1, dup.length); From e78f9f66d829b35ea95376717ea83c4f9f409312 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 5 Oct 2018 10:31:53 +0200 Subject: [PATCH 27/33] cmake: explicitly enable int-conversion warnings While GCC enables int-conversion warnings by default, it will currently only warn about such errors even in case where "-DENABLE_WERROR=ON" has been passed to CMake. Explicitly enable int-conversion warnings by using our `ENABLE_WARNINGS` macro, which will automatically use "-Werror=int-conversions" in case it has been requested by the user. (cherry picked from commit aa0ae59a2a31dc0ee5cc987066903d135a5f9e79) --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 106469377b5..83898c57022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,7 @@ ELSE () DISABLE_WARNINGS(unused-function) ENABLE_WARNINGS(format) ENABLE_WARNINGS(format-security) + ENABLE_WARNINGS(int-conversion) IF (APPLE) # Apple deprecated OpenSSL DISABLE_WARNINGS(deprecated-declarations) From 4ca4c7d1968281954e214d1cd80990c0e3543d70 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Tue, 21 Aug 2018 02:11:32 +0200 Subject: [PATCH 28/33] transport/http: do not return success if we failed to get a scheme Otherwise we return a NULL context, which will get dereferenced in apply_credentials. (cherry picked from commit 1c949ce1483ca22a29e8f523360999cbbe411a50) --- src/transports/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transports/http.c b/src/transports/http.c index e051c8a35bd..575d7c9d7df 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -142,7 +142,7 @@ static int auth_context_match( } if (!scheme) - return 0; + return -1; /* See if authentication has already started for this scheme */ git_vector_foreach(&t->auth_contexts, i, c) { From a77b64a5ebea1bb2edeaa1a3cb55b4b0933fb4e9 Mon Sep 17 00:00:00 2001 From: Anders Borum Date: Sat, 6 Oct 2018 12:58:06 +0200 Subject: [PATCH 29/33] ignore unsupported http authentication schemes auth_context_match returns 0 instead of -1 for unknown schemes to not fail in situations where some authentication schemes are supported and others are not. apply_credentials is adjusted to handle auth_context_match returning 0 without producing authentication context. (cherry picked from commit 475db39bb4c44a2221f340c66c227f555e478d10) --- src/transports/http.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/transports/http.c b/src/transports/http.c index 575d7c9d7df..22b8bb8643b 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -142,7 +142,7 @@ static int auth_context_match( } if (!scheme) - return -1; + return 0; /* See if authentication has already started for this scheme */ git_vector_foreach(&t->auth_contexts, i, c) { @@ -188,6 +188,9 @@ static int apply_credentials(git_buf *buf, http_subtransport *t) if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0) return -1; + if (!context) + return 0; + return context->next_token(buf, context, cred); } From ebdca44a407284305fb701221470ed48145ef586 Mon Sep 17 00:00:00 2001 From: Marcin Krystianc Date: Sun, 2 Sep 2018 11:38:43 +0100 Subject: [PATCH 30/33] git_remote_prune to be O(n * logn) (cherry picked from commit bfec6526e931d7f6ac5ecc38c37e76163092bfda) --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index b3252b653e8..2078dd3e7fa 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1234,7 +1234,7 @@ int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks) goto cleanup; key.name = (char *) git_buf_cstr(&buf); - error = git_vector_search(&pos, &remote_refs, &key); + error = git_vector_bsearch(&pos, &remote_refs, &key); git_buf_free(&buf); if (error < 0 && error != GIT_ENOTFOUND) From 80890b9a354a67aacad66949ba745f5a0073879f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 20 Jul 2018 08:20:48 -0700 Subject: [PATCH 31/33] winhttp: retry erroneously failing requests Early Windows TLS 1.2 implementations have an issue during key exchange with OpenSSL implementations that cause negotiation to fail with the error "the buffer supplied to a function was too small." This is a transient error on the connection, so when that error is received, retry up to 5 times to create a connection to the remote server before actually giving up. (cherry picked from commit dc371e3c5903760cc2334a0acfac9bce04479773) --- src/transports/winhttp.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 4a1eb0490c3..c3c18a80a56 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -846,23 +846,27 @@ static int winhttp_connect( static int do_send_request(winhttp_stream *s, size_t len, int ignore_length) { - if (ignore_length) { - if (!WinHttpSendRequest(s->request, - WINHTTP_NO_ADDITIONAL_HEADERS, 0, - WINHTTP_NO_REQUEST_DATA, 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) { - return -1; - } - } else { - if (!WinHttpSendRequest(s->request, - WINHTTP_NO_ADDITIONAL_HEADERS, 0, - WINHTTP_NO_REQUEST_DATA, 0, - len, 0)) { - return -1; + int attempts; + bool success; + + for (attempts = 0; attempts < 5; attempts++) { + if (ignore_length) { + success = WinHttpSendRequest(s->request, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + WINHTTP_NO_REQUEST_DATA, 0, + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0); + } else { + success = WinHttpSendRequest(s->request, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + WINHTTP_NO_REQUEST_DATA, 0, + len, 0); } + + if (success || GetLastError() != SEC_E_BUFFER_TOO_SMALL) + break; } - return 0; + return success ? 0 : -1; } static int send_request(winhttp_stream *s, size_t len, int ignore_length) From 45d555628e85e1bfa2f5d134add3c24810b23e61 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 19 Oct 2018 14:14:33 +0200 Subject: [PATCH 32/33] CHANGELOG: update for v0.27.7 --- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38db317e795..f7e72d34371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +v0.27.7 +------- + +This is a bugfix release with the following changes or improvements: + +- Our continuous integration environment has switched from Travis and + AppVeyor to Azure Pipelines CI. + +- Fix adding worktrees for bare repositories. + +- Fix parsed patches not computing the old respectively new line + numbers correctly. + +- Fix parsing configuration variables which do not have a section. + +- Fix a zero-byte allocation when trying to detect file renames and + copies of a diff without any hunks. + +- Fix a zero-byte allocation when trying to resize or duplicate + vectors. + +- Fix return value when trying to unlock worktrees which aren't + locked. + +- Fix returning an unitialized error code when preparing a revision + walk without any pushed commits. + +- Fix return value of `git_remote_lookup` when lookup of + "remote.$remote.tagopt" fails. + +- Fix the revision walk always labelling commits as interesting due + to a mishandling of the commit date. + +- Fix the packbuilder inserting uninteresting blobs when adding a + tree containing references to such blobs. + +- Ignore unsupported authentication schemes in HTTP transport. + +- Improve performane of `git_remote_prune`. + +- Fix detection of whether `qsort_r` has a BSD or GNU function + signature. + +- Fix detection of iconv if it is provided by libc. + v0.27.6 ------- From a0c286b5bea67d17016c8a4efc0f73f591c3738b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 19 Oct 2018 14:14:51 +0200 Subject: [PATCH 33/33] version: bump to v0.27.7 --- include/git2/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/version.h b/include/git2/version.h index 8dd6a5e2270..964e4a58b1c 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,10 +7,10 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.27.6" +#define LIBGIT2_VERSION "0.27.7" #define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MINOR 27 -#define LIBGIT2_VER_REVISION 6 +#define LIBGIT2_VER_REVISION 7 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION 27