Commit 7c9f77b43a82ae62664f7b5fa76aee130d8e9752

Miguel Arroz 2022-01-07T19:25:55

Merge branch 'libgit2:main' into fix/apple-static-lib

diff --git a/deps/ntlmclient/crypt_openssl.c b/deps/ntlmclient/crypt_openssl.c
index 463eae4..bab8276 100644
--- a/deps/ntlmclient/crypt_openssl.c
+++ b/deps/ntlmclient/crypt_openssl.c
@@ -44,7 +44,7 @@ static inline void HMAC_CTX_free(HMAC_CTX *ctx)
 
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(CRYPT_OPENSSL_DYNAMIC)
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !LIBRESSL_VERSION_NUMBER) || defined(CRYPT_OPENSSL_DYNAMIC)
 
 static inline void HMAC_CTX_cleanup(HMAC_CTX *ctx)
 {
diff --git a/include/git2/status.h b/include/git2/status.h
index 526df63..d8f9663 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -250,6 +250,12 @@ typedef struct {
 	 * working directory and index; defaults to HEAD.
 	 */
 	git_tree          *baseline;
+
+	/**
+	 * Threshold above which similar files will be considered renames.
+	 * This is equivalent to the -M option. Defaults to 50.
+	 */
+	uint16_t          rename_threshold;
 } git_status_options;
 
 #define GIT_STATUS_OPTIONS_VERSION 1
diff --git a/src/config_file.c b/src/config_file.c
index 11b4440..43a6ba4 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -724,14 +724,25 @@ static const struct {
 static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file)
 {
 	char *condition;
-	size_t i;
+	size_t section_len, i;
 	int error = 0, matches;
 
 	if (!parse_data->repo || !file)
 		return 0;
 
-	condition = git__substrdup(section + strlen("includeIf."),
-				   strlen(section) - strlen("includeIf.") - strlen(".path"));
+	section_len = strlen(section);
+
+	/*
+	 * We checked that the string starts with `includeIf.` and ends
+	 * in `.path` to get here.  Make sure it consists of more.
+	 */
+	if (section_len < CONST_STRLEN("includeIf.") + CONST_STRLEN(".path"))
+		return 0;
+
+	condition = git__substrdup(section + CONST_STRLEN("includeIf."),
+		section_len - CONST_STRLEN("includeIf.") - CONST_STRLEN(".path"));
+
+	GIT_ERROR_CHECK_ALLOC(condition);
 
 	for (i = 0; i < ARRAY_SIZE(conditions); i++) {
 		if (git__prefixcmp(condition, conditions[i].prefix))
diff --git a/src/diff_tform.c b/src/diff_tform.c
index d141340..f9836d2 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -375,7 +375,7 @@ static int apply_splits_and_deletes(
 	size_t i;
 	git_diff_delta *delta;
 
-	if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
+	if (git_vector_init(&onto, expected_size, diff->deltas._cmp) < 0)
 		return -1;
 
 	/* build new delta list without TO_DELETE and splitting TO_SPLIT */
diff --git a/src/status.c b/src/status.c
index c985646..b321e0e 100644
--- a/src/status.c
+++ b/src/status.c
@@ -336,6 +336,9 @@ int git_status_list_new(
 			GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
 			GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
 
+	if (opts != NULL && opts->rename_threshold != 0)
+		findopt.rename_threshold = opts->rename_threshold;
+
 	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
 		if ((error = git_diff_tree_to_index(
 				&status->head2idx, repo, head, index, &diffopt)) < 0)
diff --git a/tests/config/conditionals.c b/tests/config/conditionals.c
index 6249dbd..564719d 100644
--- a/tests/config/conditionals.c
+++ b/tests/config/conditionals.c
@@ -148,3 +148,28 @@ void test_config_conditionals__onbranch(void)
 	assert_condition_includes("onbranch", "dir*", false);
 	assert_condition_includes("onbranch", "dir/*", false);
 }
+
+void test_config_conditionals__empty(void)
+{
+	git_buf value = GIT_BUF_INIT;
+	git_str buf = GIT_STR_INIT;
+	git_config *cfg;
+
+	cl_git_pass(git_str_puts(&buf, "[includeIf]\n"));
+	cl_git_pass(git_str_puts(&buf, "path = other\n"));
+
+	cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
+	cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
+	_repo = cl_git_sandbox_reopen();
+
+	git_str_dispose(&buf);
+
+	cl_git_pass(git_repository_config(&cfg, _repo));
+
+	cl_git_fail_with(GIT_ENOTFOUND,
+		git_config_get_string_buf(&value, cfg, "foo.bar"));
+
+	git_str_dispose(&buf);
+	git_buf_dispose(&value);
+	git_config_free(cfg);
+}
diff --git a/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02 b/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02
new file mode 100644
index 0000000..4bd7592
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02
@@ -0,0 +1 @@
+xKN0DYGD#!CK=8pzZ[ԫ{`g4:3`N?E Bp8=wL,	`ubΜh#VSᣬop}%smǃc}2nNKDo%xE'I׃kO]J]A0 ƃ	ZʟƆ_[
\ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156 b/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156
new file mode 100644
index 0000000..c61d650
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156 differ
diff --git a/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550 b/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550
new file mode 100644
index 0000000..d6deb18
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550 differ
diff --git a/tests/resources/renames/.gitted/refs/heads/case-insensitive-status b/tests/resources/renames/.gitted/refs/heads/case-insensitive-status
new file mode 100644
index 0000000..5a92127
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/case-insensitive-status
@@ -0,0 +1 @@
+41a766bb079e18ff6a24571831bd751168799a02
diff --git a/tests/status/renames.c b/tests/status/renames.c
index e69dbcc..d5cf87d 100644
--- a/tests/status/renames.c
+++ b/tests/status/renames.c
@@ -718,3 +718,127 @@ void test_status_renames__precomposed_unicode_toggle_is_rename(void)
 #endif
 }
 
+void test_status_renames__rename_threshold(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+	_rename_helper(g_repo, "ikeepsix.txt", "newname.txt",
+		"Line 1\n" \
+		"Line 2\n" \
+		"Line 3\n" \
+		"Line 4\n" \
+		"Line 5\n" \
+		"Line 6\n" \
+		"Line 7\n" \
+		"Line 8\n" \
+		"Line 9\n"
+	);
+
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	/* Default threshold */
+	{
+		struct status_entry expected[] = {
+			{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
+		};
+
+		cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+		check_status(statuslist, expected, 1);
+		git_status_list_free(statuslist);
+	}
+
+	/* Threshold set to 90 */
+	{
+		struct status_entry expected[] = {
+			{ GIT_STATUS_WT_DELETED, "ikeepsix.txt", NULL },
+			{ GIT_STATUS_WT_NEW, "newname.txt", NULL }
+		};
+
+		opts.rename_threshold = 90;
+
+		cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+		check_status(statuslist, expected, 2);
+		git_status_list_free(statuslist);
+	}
+
+	/* Threshold set to 25 */
+	{
+		struct status_entry expected[] = {
+			{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
+		};
+
+		opts.rename_threshold = 25;
+
+		cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+		check_status(statuslist, expected, 1);
+		git_status_list_free(statuslist);
+	}
+
+	git_index_free(index);
+}
+
+void test_status_renames__case_insensitive_h2i_and_i2wc(void)
+{
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	git_reference *head, *test_branch;
+	git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+	git_str path_to_delete = GIT_STR_INIT;
+	git_str path_to_edit = GIT_STR_INIT;
+	git_index *index;
+	git_strarray paths = { NULL, 0 };
+
+	struct status_entry expected[] = {
+		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED, "sixserving.txt", "sixserving-renamed.txt" },
+		{ GIT_STATUS_INDEX_DELETED, "Wow.txt", "Wow.txt" }
+	};
+
+
+	/* Checkout the correct branch */
+	checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+	cl_git_pass(git_reference_symbolic_set_target(
+		&test_branch, head, "refs/heads/case-insensitive-status", NULL));
+	cl_git_pass(git_checkout_head(g_repo, &checkout_opts));
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+
+	/* Rename sixserving.txt, delete Wow.txt, and stage those changes */
+	rename_file(g_repo, "sixserving.txt", "sixserving-renamed.txt");
+	cl_git_pass(git_str_joinpath(
+		&path_to_delete, git_repository_workdir(g_repo), "Wow.txt"));
+	cl_git_rmfile(path_to_delete.ptr);
+
+	cl_git_pass(git_index_add_all(index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
+	cl_git_pass(git_index_write(index));
+
+
+	/* Change content of sixserving-renamed.txt */
+	cl_git_pass(git_str_joinpath(
+		&path_to_edit, git_repository_workdir(g_repo), "sixserving-renamed.txt"));
+	cl_git_append2file(path_to_edit.ptr, "New content\n");
+
+	/* Run status */
+	opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+	opts.flags |= GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	check_status(statuslist, expected, 2);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+
+	git_str_dispose(&path_to_delete);
+	git_str_dispose(&path_to_edit);
+
+	git_reference_free(head);
+	git_reference_free(test_branch);
+}