Unverified Commit 6cd094ef by Edward Thomson Committed by GitHub

Merge pull request #5540 from libgit2/ethomson/1_0_1

Release v1.0.1
parents 7d3c7057 11dca3ca
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
/* See man page endian(3) */ /* See man page endian(3) */
# include <endian.h> # include <endian.h>
# define htonll htobe64 # define htonll htobe64
#elif defined(__OpenBSD__) #elif defined(__NetBSD__) || defined(__OpenBSD__)
/* See man page htobe64(3) */ /* See man page htobe64(3) */
# include <endian.h> # include <endian.h>
# define htonll htobe64 # define htonll htobe64
......
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 v1.0
---- ----
......
...@@ -56,6 +56,17 @@ def verify_version(version): ...@@ -56,6 +56,17 @@ def verify_version(version):
if v[0] != v[1]: if v[0] != v[1]:
raise Error("version.h: define '{}' does not match (got '{}', expected '{}')".format(k, 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): def generate_relnotes(tree, version):
with open('docs/changelog.md') as f: with open('docs/changelog.md') as f:
lines = f.readlines() lines = f.readlines()
......
...@@ -111,6 +111,15 @@ static int config_file_open(git_config_backend *cfg, git_config_level_t level, c ...@@ -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)) if (!git_path_exists(b->file.path))
return 0; 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) { if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) {
git_config_entries_free(b->entries); git_config_entries_free(b->entries);
b->entries = NULL; b->entries = NULL;
......
...@@ -2744,7 +2744,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ...@@ -2744,7 +2744,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
++same_len; ++same_len;
} }
path_len -= 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); 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 ...@@ -2795,7 +2795,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
if (last) { if (last) {
varint_len = git_encode_varint((unsigned char *) path, varint_len = git_encode_varint((unsigned char *) path,
disk_size, same_len); disk_size, strlen(last) - same_len);
assert(varint_len > 0); assert(varint_len > 0);
path += varint_len; path += varint_len;
disk_size -= varint_len; disk_size -= varint_len;
......
...@@ -68,6 +68,16 @@ struct merge_diff_df_data { ...@@ -68,6 +68,16 @@ struct merge_diff_df_data {
git_merge_diff *prev_conflict; 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 */ /* 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[]) 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( ...@@ -1027,6 +1037,9 @@ static int index_entry_similarity_calc(
git_object_size_t blobsize; git_object_size_t blobsize;
int error; int error;
if (*out || *out == &cache_invalid_marker)
return 0;
*out = NULL; *out = NULL;
if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0) if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
...@@ -1047,6 +1060,8 @@ static int index_entry_similarity_calc( ...@@ -1047,6 +1060,8 @@ static int index_entry_similarity_calc(
error = opts->metric->buffer_signature(out, &diff_file, error = opts->metric->buffer_signature(out, &diff_file,
git_blob_rawcontent(blob), (size_t)blobsize, git_blob_rawcontent(blob), (size_t)blobsize,
opts->metric->payload); opts->metric->payload);
if (error == GIT_EBUFS)
*out = &cache_invalid_marker;
git_blob_free(blob); git_blob_free(blob);
...@@ -1069,18 +1084,16 @@ static int index_entry_similarity_inexact( ...@@ -1069,18 +1084,16 @@ static int index_entry_similarity_inexact(
return 0; return 0;
/* update signature cache if needed */ /* update signature cache if needed */
if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0) if ((error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0 ||
return error; (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
return error; return error;
/* some metrics may not wish to process this file (too big / too small) */ /* 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; return 0;
/* compare signatures */ /* compare signatures */
if (opts->metric->similarity( if (opts->metric->similarity(&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
return -1; return -1;
/* clip score */ /* clip score */
...@@ -1550,7 +1563,7 @@ int git_merge_diff_list__find_renames( ...@@ -1550,7 +1563,7 @@ int git_merge_diff_list__find_renames(
done: done:
if (cache != NULL) { if (cache != NULL) {
for (i = 0; i < cache_size; ++i) { 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); opts->metric->free_signature(cache[i], opts->metric->payload);
} }
......
...@@ -173,7 +173,7 @@ static int stash_to_index( ...@@ -173,7 +173,7 @@ static int stash_to_index(
git_index *index, git_index *index,
const char *path) const char *path)
{ {
git_index *repo_index; git_index *repo_index = NULL;
git_index_entry entry = {{0}}; git_index_entry entry = {{0}};
struct stat st; struct stat st;
int error; int error;
...@@ -187,7 +187,7 @@ static int stash_to_index( ...@@ -187,7 +187,7 @@ static int stash_to_index(
return error; return error;
git_index_entry__init_from_stat(&entry, &st, 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; entry.path = path;
......
...@@ -1038,6 +1038,7 @@ on_error: ...@@ -1038,6 +1038,7 @@ on_error:
GIT_INLINE(int) client_read(git_http_client *client) GIT_INLINE(int) client_read(git_http_client *client)
{ {
http_parser_context *parser_context = client->parser.data;
git_stream *stream; git_stream *stream;
char *buf = client->read_buf.ptr + client->read_buf.size; char *buf = client->read_buf.ptr + client->read_buf.size;
size_t max_len; size_t max_len;
...@@ -1054,6 +1055,9 @@ GIT_INLINE(int) client_read(git_http_client *client) ...@@ -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 = client->read_buf.asize - client->read_buf.size;
max_len = min(max_len, INT_MAX); 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) { if (max_len == 0) {
git_error_set(GIT_ERROR_HTTP, "no room in output buffer"); git_error_set(GIT_ERROR_HTTP, "no room in output buffer");
return -1; return -1;
...@@ -1191,7 +1195,7 @@ static void complete_response_body(git_http_client *client) ...@@ -1191,7 +1195,7 @@ static void complete_response_body(git_http_client *client)
/* If we're not keeping alive, don't bother. */ /* If we're not keeping alive, don't bother. */
if (!client->keepalive) { if (!client->keepalive) {
client->connected = 0; client->connected = 0;
return; goto done;
} }
parser_context.client = client; parser_context.client = client;
...@@ -1205,6 +1209,9 @@ static void complete_response_body(git_http_client *client) ...@@ -1205,6 +1209,9 @@ static void complete_response_body(git_http_client *client)
git_error_clear(); git_error_clear();
client->connected = 0; client->connected = 0;
} }
done:
git_buf_clear(&client->read_buf);
} }
int git_http_client_send_request( int git_http_client_send_request(
...@@ -1419,15 +1426,20 @@ int git_http_client_read_body( ...@@ -1419,15 +1426,20 @@ int git_http_client_read_body(
client->parser.data = &parser_context; client->parser.data = &parser_context;
/* /*
* Clients expect to get a non-zero amount of data from us. * Clients expect to get a non-zero amount of data from us,
* With a sufficiently small buffer, one might only read a chunk * so we either block until we have data to return, until we
* length. Loop until we actually have data to return. * 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) { while (!parser_context.output_written) {
error = client_read_and_parse(client); error = client_read_and_parse(client);
if (error <= 0) if (error <= 0)
goto done; goto done;
if (client->state == DONE)
break;
} }
assert(parser_context.output_written <= INT_MAX); assert(parser_context.output_written <= INT_MAX);
......
...@@ -506,7 +506,7 @@ int git_worktree_prune_options_init( ...@@ -506,7 +506,7 @@ int git_worktree_prune_options_init(
return 0; 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) unsigned int version)
{ {
return git_worktree_prune_options_init(opts, version); return git_worktree_prune_options_init(opts, version);
......
...@@ -849,6 +849,23 @@ void test_config_read__invalid_quoted_third_section(void) ...@@ -849,6 +849,23 @@ void test_config_read__invalid_quoted_third_section(void)
git_config_free(cfg); 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) void test_config_read__single_line(void)
{ {
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
......
...@@ -43,6 +43,7 @@ void test_index_version__can_write_v4(void) ...@@ -43,6 +43,7 @@ void test_index_version__can_write_v4(void)
"xz", "xz",
"xyzzyx" "xyzzyx"
}; };
git_repository *repo;
git_index_entry entry; git_index_entry entry;
git_index *index; git_index *index;
size_t i; size_t i;
...@@ -63,7 +64,8 @@ void test_index_version__can_write_v4(void) ...@@ -63,7 +64,8 @@ void test_index_version__can_write_v4(void)
cl_git_pass(git_index_write(index)); cl_git_pass(git_index_write(index));
git_index_free(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); cl_assert(git_index_version(index) == 4);
for (i = 0; i < ARRAY_SIZE(paths); i++) { for (i = 0; i < ARRAY_SIZE(paths); i++) {
...@@ -74,6 +76,7 @@ void test_index_version__can_write_v4(void) ...@@ -74,6 +76,7 @@ void test_index_version__can_write_v4(void)
} }
git_index_free(index); git_index_free(index);
git_repository_free(repo);
} }
void test_index_version__v4_uses_path_compression(void) void test_index_version__v4_uses_path_compression(void)
......
...@@ -274,3 +274,80 @@ void test_merge_trees_renames__submodules(void) ...@@ -274,3 +274,80 @@ void test_merge_trees_renames__submodules(void)
cl_assert(merge_test_index(index, merge_index_entries, 7)); cl_assert(merge_test_index(index, merge_index_entries, 7));
git_index_free(index); 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);
}
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git" #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_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 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" #define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
...@@ -463,6 +464,13 @@ void test_online_clone__bitbucket_falls_back_to_specified_creds(void) ...@@ -463,6 +464,13 @@ void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
cl_fixture_cleanup("./foo"); 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) static int cancel_at_half(const git_indexer_progress *stats, void *payload)
{ {
GIT_UNUSED(payload); GIT_UNUSED(payload);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment