]> git.proxmox.com Git - libgit2.git/commitdiff
New upstream version 1.0.1+dfsg.1
authorUtkarsh Gupta <utkarsh@debian.org>
Fri, 9 Oct 2020 15:08:27 +0000 (20:38 +0530)
committerUtkarsh Gupta <utkarsh@debian.org>
Fri, 9 Oct 2020 15:08:27 +0000 (20:38 +0530)
14 files changed:
docs/changelog.md
include/git2/version.h
package.json
script/release.py
src/config_file.c
src/index.c
src/merge.c
src/stash.c
src/transports/httpclient.c
src/worktree.c
tests/config/read.c
tests/index/version.c
tests/merge/trees/renames.c
tests/online/clone.c

index 97cf94c6bf0779a2b61905810740cb968f154cd3..c2423c33884a3d8668cf2e721e759fe887f18f45 100644 (file)
@@ -1,3 +1,33 @@
+v1.0.1
+------
+
+This is a bugfix release with the following changes:
+
+- Calculating information about renamed files during merges is more
+  efficient because dissimilarity about files is now being cached and
+  no longer needs to be recomputed.
+  
+- The `git_worktree_prune_init_options` has been correctly restored for
+  backward compatibility.  In v1.0 it was incorrectly deprecated with a
+  typo.
+
+- The optional ntlmclient dependency now supports NetBSD.
+
+- A bug where attempting to stash on a bare repository may have failed
+  has been fixed.
+
+- Configuration files that are unreadable due to permissions are now
+  silently ignored, and treated as if they do not exist.  This matches
+  git's behavior; previously this case would have been an error.
+
+- v4 index files are now correctly written; previously we would read
+  them correctly but would not write the prefix-compression accurately,
+  causing corruption.
+
+- A bug where the smart HTTP transport could not read large data packets
+  has been fixed.  Previously, fetching from servers like Gerrit, that
+  sent large data packets, would error.
+
 v1.0
 ----
 
index b078e1ea25745b6bbcca424ae9cbc49cb56ca56e..2d87086227d531558c4c6d54d8521454756a4592 100644 (file)
@@ -7,10 +7,10 @@
 #ifndef INCLUDE_git_version_h__
 #define INCLUDE_git_version_h__
 
-#define LIBGIT2_VERSION "1.0.0"
+#define LIBGIT2_VERSION "1.0.1"
 #define LIBGIT2_VER_MAJOR 1
 #define LIBGIT2_VER_MINOR 0
-#define LIBGIT2_VER_REVISION 0
+#define LIBGIT2_VER_REVISION 1
 #define LIBGIT2_VER_PATCH 0
 
 #define LIBGIT2_SOVERSION "1.0"
index d33f31c303663dbdbb4baed08ec3cd6c83116367..79cddbee749fe861f9d244fb89f2ab29b115cd50 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "libgit2",
-  "version": "0.27.0",
+  "version": "1.0.1",
   "repo": "https://github.com/libgit2/libgit2",
   "description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
   "install": "mkdir build && cd build && cmake .. && cmake --build ."
index e0f29538e7b605598950d79808fb36b09154cc27..3d8e9b806fc5fc26a398a733f2abe490daf1031c 100755 (executable)
@@ -56,6 +56,17 @@ def verify_version(version):
         if v[0] != v[1]:
             raise Error("version.h: define '{}' does not match (got '{}', expected '{}')".format(k, v[0], v[1]))
 
+    with open('package.json') as f:
+        pkg = json.load(f)
+
+    try:
+        pkg_version = Version(pkg["version"])
+    except KeyError as err:
+        raise Error("package.json: missing the field {}".format(err))
+
+    if pkg_version != version:
+        raise Error("package.json: version does not match (got '{}', expected '{}')".format(pkg_version, version))
+
 def generate_relnotes(tree, version):
     with open('docs/changelog.md') as f:
         lines = f.readlines()
index c9e36493ea63dc831c475ad1dd0d39945c37f656..b1e0028369ac64af2d245c4132281dd172367b63 100644 (file)
@@ -111,6 +111,15 @@ static int config_file_open(git_config_backend *cfg, git_config_level_t level, c
        if (!git_path_exists(b->file.path))
                return 0;
 
+       /*
+        * git silently ignores configuration files that are not
+        * readable.  We emulate that behavior.  This is particularly
+        * important for sandboxed applications on macOS where the
+        * git configuration files may not be readable.
+        */
+       if (p_access(b->file.path, R_OK) < 0)
+               return GIT_ENOTFOUND;
+
        if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) {
                git_config_entries_free(b->entries);
                b->entries = NULL;
index 907bd6d93556d9e57facf203a4b936b16a707bd5..36a8bdb7bc1db567390d994cfceaf91630283af7 100644 (file)
@@ -2744,7 +2744,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
                        ++same_len;
                }
                path_len -= same_len;
-               varint_len = git_encode_varint(NULL, 0, same_len);
+               varint_len = git_encode_varint(NULL, 0, strlen(last) - same_len);
        }
 
        disk_size = index_entry_size(path_len, varint_len, entry->flags);
@@ -2795,7 +2795,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
 
        if (last) {
                varint_len = git_encode_varint((unsigned char *) path,
-                                         disk_size, same_len);
+                                         disk_size, strlen(last) - same_len);
                assert(varint_len > 0);
                path += varint_len;
                disk_size -= varint_len;
index 05a776e4573918e8911e45406db107dab86b5347..afe69e5648babbc8db50d6e714551ca6b6407881 100644 (file)
@@ -68,6 +68,16 @@ struct merge_diff_df_data {
        git_merge_diff *prev_conflict;
 };
 
+/*
+ * This acts as a negative cache entry marker. In case we've tried to calculate
+ * similarity metrics for a given blob already but `git_hashsig` determined
+ * that it's too small in order to have a meaningful hash signature, we will
+ * insert the address of this marker instead of `NULL`. Like this, we can
+ * easily check whether we have checked a gien entry already and skip doing the
+ * calculation again and again.
+ */
+static int cache_invalid_marker;
+
 /* Merge base computation */
 
 int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
@@ -1027,6 +1037,9 @@ static int index_entry_similarity_calc(
        git_object_size_t blobsize;
        int error;
 
+       if (*out || *out == &cache_invalid_marker)
+               return 0;
+
        *out = NULL;
 
        if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
@@ -1047,6 +1060,8 @@ static int index_entry_similarity_calc(
        error = opts->metric->buffer_signature(out, &diff_file,
                git_blob_rawcontent(blob), (size_t)blobsize,
                opts->metric->payload);
+       if (error == GIT_EBUFS)
+               *out = &cache_invalid_marker;
 
        git_blob_free(blob);
 
@@ -1069,18 +1084,16 @@ static int index_entry_similarity_inexact(
                return 0;
 
        /* update signature cache if needed */
-       if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0)
-               return error;
-       if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
+       if ((error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0 ||
+           (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
                return error;
 
        /* some metrics may not wish to process this file (too big / too small) */
-       if (!cache[a_idx] || !cache[b_idx])
+       if (cache[a_idx] == &cache_invalid_marker || cache[b_idx] == &cache_invalid_marker)
                return 0;
 
        /* compare signatures */
-       if (opts->metric->similarity(
-               &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
+       if (opts->metric->similarity(&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
                return -1;
 
        /* clip score */
@@ -1550,7 +1563,7 @@ int git_merge_diff_list__find_renames(
 done:
        if (cache != NULL) {
                for (i = 0; i < cache_size; ++i) {
-                       if (cache[i] != NULL)
+                       if (cache[i] != NULL && cache[i] != &cache_invalid_marker)
                                opts->metric->free_signature(cache[i], opts->metric->payload);
                }
 
index 4a13d0530f6b9cf4d5eae14642d845b9ac3bff63..790f56fddc79f2fc20facbe9bf977e98aab7384c 100644 (file)
@@ -173,7 +173,7 @@ static int stash_to_index(
        git_index *index,
        const char *path)
 {
-       git_index *repo_index;
+       git_index *repo_index = NULL;
        git_index_entry entry = {{0}};
        struct stat st;
        int error;
@@ -187,7 +187,7 @@ static int stash_to_index(
                return error;
 
        git_index_entry__init_from_stat(&entry, &st,
-               (repo_index != NULL || !repo_index->distrust_filemode));
+               (repo_index == NULL || !repo_index->distrust_filemode));
 
        entry.path = path;
 
index bde67ca9fd9b5df687bed6a447d74f77ce33a167..010baa60477e3e31c268336990729363e270843b 100644 (file)
@@ -1038,6 +1038,7 @@ on_error:
 
 GIT_INLINE(int) client_read(git_http_client *client)
 {
+       http_parser_context *parser_context = client->parser.data;
        git_stream *stream;
        char *buf = client->read_buf.ptr + client->read_buf.size;
        size_t max_len;
@@ -1054,6 +1055,9 @@ GIT_INLINE(int) client_read(git_http_client *client)
        max_len = client->read_buf.asize - client->read_buf.size;
        max_len = min(max_len, INT_MAX);
 
+       if (parser_context->output_size)
+               max_len = min(max_len, parser_context->output_size);
+
        if (max_len == 0) {
                git_error_set(GIT_ERROR_HTTP, "no room in output buffer");
                return -1;
@@ -1191,7 +1195,7 @@ static void complete_response_body(git_http_client *client)
        /* If we're not keeping alive, don't bother. */
        if (!client->keepalive) {
                client->connected = 0;
-               return;
+               goto done;
        }
 
        parser_context.client = client;
@@ -1205,6 +1209,9 @@ static void complete_response_body(git_http_client *client)
                git_error_clear();
                client->connected = 0;
        }
+
+done:
+       git_buf_clear(&client->read_buf);
 }
 
 int git_http_client_send_request(
@@ -1419,15 +1426,20 @@ int git_http_client_read_body(
        client->parser.data = &parser_context;
 
        /*
-        * Clients expect to get a non-zero amount of data from us.
-        * With a sufficiently small buffer, one might only read a chunk
-        * length.  Loop until we actually have data to return.
+        * Clients expect to get a non-zero amount of data from us,
+        * so we either block until we have data to return, until we
+        * hit EOF or there's an error.  Do this in a loop, since we
+        * may end up reading only some stream metadata (like chunk
+        * information).
         */
        while (!parser_context.output_written) {
                error = client_read_and_parse(client);
 
                if (error <= 0)
                        goto done;
+
+               if (client->state == DONE)
+                       break;
        }
 
        assert(parser_context.output_written <= INT_MAX);
index e171afbb224a77d19566845cadfb47562dd9fca0..74b32f97cbab50646ca3860b419877700f3b3944 100644 (file)
@@ -506,7 +506,7 @@ int git_worktree_prune_options_init(
        return 0;
 }
 
-int git_worktree_pruneinit_options(git_worktree_prune_options *opts,
+int git_worktree_prune_init_options(git_worktree_prune_options *opts,
        unsigned int version)
 {
        return git_worktree_prune_options_init(opts, version);
index 008dfd9fc5b9f824d084c4ce3d226346369e3ed7..ba97302f7f8a1c5abd1cf7a67a0420bd4fcda65e 100644 (file)
@@ -849,6 +849,23 @@ void test_config_read__invalid_quoted_third_section(void)
        git_config_free(cfg);
 }
 
+void test_config_read__unreadable_file_ignored(void)
+{
+       git_buf buf = GIT_BUF_INIT;
+       git_config *cfg;
+       int ret;
+
+       cl_set_cleanup(&clean_test_config, NULL);
+       cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
+       cl_git_pass(p_chmod("./testconfig", 0));
+
+       ret = git_config_open_ondisk(&cfg, "./test/config");
+       cl_assert(ret == 0 || ret == GIT_ENOTFOUND);
+
+       git_config_free(cfg);
+       git_buf_dispose(&buf);
+}
+
 void test_config_read__single_line(void)
 {
        git_buf buf = GIT_BUF_INIT;
index 3827df8617fa3afd13a7b856b8db476e48f6263f..b6c0b7918dd8e6e7ebf9009e50f2c72ba7bd3e40 100644 (file)
@@ -43,6 +43,7 @@ void test_index_version__can_write_v4(void)
            "xz",
            "xyzzyx"
        };
+       git_repository *repo;
        git_index_entry entry;
        git_index *index;
        size_t i;
@@ -63,7 +64,8 @@ void test_index_version__can_write_v4(void)
        cl_git_pass(git_index_write(index));
        git_index_free(index);
 
-       cl_git_pass(git_repository_index(&index, g_repo));
+       cl_git_pass(git_repository_open(&repo, git_repository_path(g_repo)));
+       cl_git_pass(git_repository_index(&index, repo));
        cl_assert(git_index_version(index) == 4);
 
        for (i = 0; i < ARRAY_SIZE(paths); i++) {
@@ -74,6 +76,7 @@ void test_index_version__can_write_v4(void)
        }
 
        git_index_free(index);
+       git_repository_free(repo);
 }
 
 void test_index_version__v4_uses_path_compression(void)
index e0b12af3da69a4094e01466bdc8411bd0c46d1de..c515aaf1b16e327b5b3303e536488395e56451e3 100644 (file)
@@ -274,3 +274,80 @@ void test_merge_trees_renames__submodules(void)
        cl_assert(merge_test_index(index, merge_index_entries, 7));
        git_index_free(index);
 }
+
+void test_merge_trees_renames__cache_recomputation(void)
+{
+       git_oid blob, binary, ancestor_oid, theirs_oid, ours_oid;
+       git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+       git_buf path = GIT_BUF_INIT;
+       git_treebuilder *builder;
+       git_tree *ancestor_tree, *their_tree, *our_tree;
+       git_index *index;
+       size_t blob_size;
+       void *data;
+       size_t i;
+
+       cl_git_pass(git_oid_fromstr(&blob, "a2d8d1824c68541cca94ffb90f79291eba495921"));
+
+       /*
+        * Create a 50MB blob that consists of NUL bytes only. It is important
+        * that this blob is of a special format, most importantly it cannot
+        * contain more than four non-consecutive newlines or NUL bytes. This
+        * is because of git_hashsig's inner workings where all files with less
+        * than four "lines" are deemed to small.
+        */
+       blob_size = 50 * 1024 * 1024;
+       cl_assert(data = git__calloc(blob_size, 1));
+       cl_git_pass(git_blob_create_from_buffer(&binary, repo, data, blob_size));
+
+       /*
+        * Create the common ancestor, which has 1000 dummy blobs and the binary
+        * blob. The dummy blobs serve as potential rename targets for the
+        * dummy blob.
+        */
+       cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+       for (i = 0; i < 1000; i++) {
+               cl_git_pass(git_buf_printf(&path, "%"PRIuMAX".txt", i));
+               cl_git_pass(git_treebuilder_insert(NULL, builder, path.ptr, &blob, GIT_FILEMODE_BLOB));
+               git_buf_clear(&path);
+       }
+       cl_git_pass(git_treebuilder_insert(NULL, builder, "original.bin", &binary, GIT_FILEMODE_BLOB));
+       cl_git_pass(git_treebuilder_write(&ancestor_oid, builder));
+
+       /* We now the binary blob in our tree. */
+       cl_git_pass(git_treebuilder_remove(builder, "original.bin"));
+       cl_git_pass(git_treebuilder_insert(NULL, builder, "renamed.bin", &binary, GIT_FILEMODE_BLOB));
+       cl_git_pass(git_treebuilder_write(&ours_oid, builder));
+
+       git_treebuilder_free(builder);
+
+       /* And move everything into a subdirectory in their tree. */
+       cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+       cl_git_pass(git_treebuilder_insert(NULL, builder, "subdir", &ancestor_oid, GIT_FILEMODE_TREE));
+       cl_git_pass(git_treebuilder_write(&theirs_oid, builder));
+
+       /*
+        * Now merge ancestor, ours and theirs. As `git_hashsig` refuses to
+        * create a hash signature for the 50MB binary file, we historically
+        * didn't cache the hashsig computation for it. As a result, we now
+        * started looking up the 50MB blob and scanning it at least 1000
+        * times, which takes a long time.
+        *
+        * The number of 1000 blobs is chosen in such a way that it's
+        * noticeable when the bug creeps in again, as it takes around 12
+        * minutes on my machine to compute the following merge.
+        */
+       opts.target_limit = 5000;
+       cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid));
+       cl_git_pass(git_tree_lookup(&their_tree, repo, &theirs_oid));
+       cl_git_pass(git_tree_lookup(&our_tree, repo, &ours_oid));
+       cl_git_pass(git_merge_trees(&index, repo, ancestor_tree, our_tree, their_tree, &opts));
+
+       git_treebuilder_free(builder);
+       git_buf_dispose(&path);
+       git_index_free(index);
+       git_tree_free(ancestor_tree);
+       git_tree_free(their_tree);
+       git_tree_free(our_tree);
+       git__free(data);
+}
index 034d0c2e81cede0e3af96ab05813d6bbd8cdaa87..9107956bd1bd2e19d964b7efa090ed9065d90219 100644 (file)
@@ -11,6 +11,7 @@
 #define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git"
 #define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
 #define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git"
+#define GOOGLESOURCE_REPO_URL "https://chromium.googlesource.com/external/github.com/sergi/go-diff"
 
 #define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
 
@@ -463,6 +464,13 @@ void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
        cl_fixture_cleanup("./foo");
 }
 
+void test_online_clone__googlesource(void)
+{
+       cl_git_pass(git_clone(&g_repo, GOOGLESOURCE_REPO_URL, "./foo", &g_options));
+       git_repository_free(g_repo); g_repo = NULL;
+       cl_fixture_cleanup("./foo");
+}
+
 static int cancel_at_half(const git_indexer_progress *stats, void *payload)
 {
        GIT_UNUSED(payload);