From d055f4ea129d89d06b22f99a1e0a6ffe1417441a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 3 Oct 2014 19:34:37 -0400 Subject: [PATCH 01/13] repository_head_unborn: clear error when HEAD is unborn --- src/repository.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index e8d50aed342..d52c0c60f31 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1551,8 +1551,10 @@ int git_repository_head_unborn(git_repository *repo) error = git_repository_head(&ref, repo); git_reference_free(ref); - if (error == GIT_EUNBORNBRANCH) + if (error == GIT_EUNBORNBRANCH) { + giterr_clear(); return 1; + } if (error < 0) return -1; From 6e41c27f1b458d0db7b4d6114fb3b9b8744c4e84 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 11 Oct 2014 20:56:50 -0400 Subject: [PATCH 02/13] Introduce failing test for conflict filtering in index --- tests/checkout/index.c | 69 ++++++++++++++++++ .../2b/d0a343aeef7a2cf0d158478966a6e587ff3863 | Bin 0 -> 56 bytes .../4e/886e602529caa9ab11d71f86634bd1b6e0de10 | Bin 0 -> 56 bytes .../d4/27e0b2e138501a3d15cc376077a3631e15bd46 | Bin 0 -> 38 bytes 4 files changed, 69 insertions(+) create mode 100644 tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 create mode 100644 tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 create mode 100644 tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 diff --git a/tests/checkout/index.c b/tests/checkout/index.c index 7f641b32979..3c01e241135 100644 --- a/tests/checkout/index.c +++ b/tests/checkout/index.c @@ -618,3 +618,72 @@ void test_checkout_index__can_get_repo_from_index(void) git_index_free(index); } + +static void add_conflict(void) +{ + git_index *index; + git_index_entry entry; + + memset(&entry, 0, sizeof(git_index_entry)); + + cl_git_pass(git_repository_index(&index, g_repo)); + + entry.mode = 0100644; + entry.path = "conflicting.txt"; + + git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); + entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10"); + entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); + entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + cl_git_pass(git_index_write(index)); + git_index_free(index); +} + +void test_checkout_index__writes_conflict_file(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + add_conflict(); + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, + "<<<<<<< ours\n" + "this file is changed in master and branch\n" + "=======\n" + "this file is changed in branch and master\n" + ">>>>>>> theirs\n") == 0); + git_buf_free(&conflicting_buf); +} + +void test_checkout_index__conflicts_honor_coreautocrlf(void) +{ +#ifdef GIT_WIN32 + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + cl_git_pass(p_unlink("./testrepo/.gitattributes")); + cl_repo_set_bool(g_repo, "core.autocrlf", true); + + add_conflict(); + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, + "<<<<<<< ours\r\n" + "this file is changed in master and branch\r\n" + "=======\r\n" + "this file is changed in branch and master\r\n" + ">>>>>>> theirs\r\n") == 0); + git_buf_free(&conflicting_buf); +#endif +} diff --git a/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 new file mode 100644 index 0000000000000000000000000000000000000000..d10ca636b6bf5f66bce633362d871d17d9a292c6 GIT binary patch literal 56 zcmV-80LTA$0ZYosPf{>3VkpVTELKR%%t=)M(#aW#dFiPs3YmEdNkxfy$r%cXc_|9H OiNz(UMO*-@y%7?c&=$P_ literal 0 HcmV?d00001 diff --git a/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 new file mode 100644 index 0000000000000000000000000000000000000000..53168a038b77edb9bb0073f155cf7e2315c07f59 GIT binary patch literal 56 zcmV-80LTA$0ZYosPf{>3VkpVTELKR%%t=)M(#aW#dFiPs3YmEdxrxOksYMEjc_|7> OMTvRI8C(FZ2N4pV?iL9E literal 0 HcmV?d00001 diff --git a/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 new file mode 100644 index 0000000000000000000000000000000000000000..0b3611ae4c9dc635dafd5dc9560cbc2ff5e92198 GIT binary patch literal 38 ucmb Date: Fri, 10 Oct 2014 13:22:11 +0200 Subject: [PATCH 03/13] Ensure filters (i.e. CRLF) are applied when checking out conflict content --- src/checkout.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 20763fd35b0..c265c3feaf8 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1736,10 +1736,12 @@ static int checkout_write_merge( checkout_conflictdata *conflict) { git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT, - path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT; + path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT, + in_data = GIT_BUF_INIT, out_data = GIT_BUF_INIT; git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; git_filebuf output = GIT_FILEBUF_INIT; + git_filter_list *fl = NULL; int error = 0; if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) @@ -1785,13 +1787,27 @@ static int checkout_write_merge( (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0) goto done; + if (!data->opts.disable_filters) { + if ((error = git_buf_put(&in_data, result.ptr, result.len)) < 0 || + (error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir), + GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT)) < 0 || + (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0) + goto done; + } else if ((error = git_buf_put(&out_data, result.ptr, result.len)) < 0) + goto done; + + if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 || - (error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || - (error = git_filebuf_write(&output, result.ptr, result.len)) < 0 || + (error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || + (error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 || (error = git_filebuf_commit(&output)) < 0) goto done; done: + git_filter_list_free(fl); + + git_buf_free(&out_data); + git_buf_free(&in_data); git_buf_free(&our_label); git_buf_free(&their_label); From 01a1be3fb1323fdd65e231d5af37f4dcc9531b89 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 11 Oct 2014 21:38:22 -0400 Subject: [PATCH 04/13] Don't copy buffer in checkout unless needed --- src/checkout.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index c265c3feaf8..1c5fdd34024 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1788,14 +1788,17 @@ static int checkout_write_merge( goto done; if (!data->opts.disable_filters) { - if ((error = git_buf_put(&in_data, result.ptr, result.len)) < 0 || - (error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir), + in_data.ptr = (char *)result.ptr; + in_data.size = result.len; + + if ((error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir), GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT)) < 0 || (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0) goto done; - } else if ((error = git_buf_put(&out_data, result.ptr, result.len)) < 0) - goto done; - + } else { + out_data.ptr = (char *)result.ptr; + out_data.size = result.len; + } if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 || (error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || @@ -1807,7 +1810,6 @@ static int checkout_write_merge( git_filter_list_free(fl); git_buf_free(&out_data); - git_buf_free(&in_data); git_buf_free(&our_label); git_buf_free(&their_label); From b2fa95a0828eba77ba3dbdc6fff021871d9185ec Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 11 Oct 2014 14:34:24 -0400 Subject: [PATCH 05/13] p_lstat win32: don't canonicalize volume mounts A reparse point that is an IO_REPARSE_TAG_MOUNT_POINT could be a junction or an actual filesystem mount point. (Who knew?) If it's the latter, its reparse point will report the actual volume information \??\Volume{GUID}\ and we should not attempt to dereference that further, instead readlink should report EINVAL since it's not a symlink / junction and its original path was canonical. Yes, really. --- src/win32/posix_w32.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 34938431a98..c6169e816fe 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -110,6 +110,11 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) return (time_t)winTime; } +static bool path_is_volume(wchar_t *target, size_t target_len) +{ + return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0); +} + /* On success, returns the length, in characters, of the path stored in dest. * On failure, returns a negative value. */ static int readlink_w( @@ -156,7 +161,13 @@ static int readlink_w( goto on_error; } - if (target_len) { + if (path_is_volume(target, target_len)) { + /* This path is a reparse point that represents another volume mounted + * at this location, it is not a symbolic link our input was canonical. + */ + errno = EINVAL; + error = -1; + } else if (target_len) { /* The path may need to have a prefix removed. */ target_len = git_win32__canonicalize_path(target, target_len); From 635ba11894691dbfaaa6a2f470b2de6462702c4b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 11 Oct 2014 11:23:34 -0400 Subject: [PATCH 06/13] is_empty_dir (wi32): cope with empty mount points FindFirstFile will fail with INVALID_HANDLE_VALUE if there are no children to the given path, which can happen if the given path is a file (and obviously has no children) or if the given path is an empty mount point. (Most directories have at least directory entries '.' and '..', but ridiculously another volume mounted in another drive letter's path space do not, and thus have nothing to enumerate.) If FindFirstFile fails, check if this is a directory-like thing (a mount point). --- src/path.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/path.c b/src/path.c index 5beab97edaf..f7d05dc916b 100644 --- a/src/path.c +++ b/src/path.c @@ -494,23 +494,33 @@ bool git_path_is_empty_dir(const char *path) WIN32_FIND_DATAW findData; HANDLE hFind = FindFirstFileW(filter_w, &findData); + /* FindFirstFile will fail if there are no children to the given + * path, which can happen if the given path is a file (and obviously + * has no children) or if the given path is an empty mount point. + * (Most directories have at least directory entries '.' and '..', + * but ridiculously another volume mounted in another drive letter's + * path space do not, and thus have nothing to enumerate.) If + * FindFirstFile fails, check if this is a directory-like thing + * (a mount point). + */ + if (hFind == INVALID_HANDLE_VALUE) + return git_path_isdir(path); + /* If the find handle was created successfully, then it's a directory */ - if (hFind != INVALID_HANDLE_VALUE) { - empty = true; - - do { - /* Allow the enumeration to return . and .. and still be considered - * empty. In the special case of drive roots (i.e. C:\) where . and - * .. do not occur, we can still consider the path to be an empty - * directory if there's nothing there. */ - if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { - empty = false; - break; - } - } while (FindNextFileW(hFind, &findData)); - - FindClose(hFind); - } + empty = true; + + do { + /* Allow the enumeration to return . and .. and still be considered + * empty. In the special case of drive roots (i.e. C:\) where . and + * .. do not occur, we can still consider the path to be an empty + * directory if there's nothing there. */ + if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { + empty = false; + break; + } + } while (FindNextFileW(hFind, &findData)); + + FindClose(hFind); } return empty; From acbfce9f838e4c313718d5c1fb3929f9b906ab62 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Thu, 16 Oct 2014 13:52:55 +1100 Subject: [PATCH 07/13] Add a test to make sure a new snapshot has the new value. --- tests/config/snapshot.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/config/snapshot.c b/tests/config/snapshot.c index c9f15921a6e..d8e6f4e3bfd 100644 --- a/tests/config/snapshot.c +++ b/tests/config/snapshot.c @@ -3,7 +3,7 @@ void test_config_snapshot__create_snapshot(void) { int32_t tmp; - git_config *cfg, *snapshot; + git_config *cfg, *snapshot, *new_snapshot; const char *filename = "config-ext-change"; cl_git_mkfile(filename, "[old]\nvalue = 5\n"); @@ -23,6 +23,19 @@ void test_config_snapshot__create_snapshot(void) cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); cl_assert_equal_i(5, tmp); + + /* Change the value on the file itself (simulate external process) */ + cl_git_mkfile(filename, "[old]\nvalue = 99\n"); + + cl_git_pass(git_config_snapshot(&new_snapshot, cfg)); + + /* New snapshot should see new value */ + cl_git_pass(git_config_get_int32(&tmp, new_snapshot, "old.value")); + cl_assert_equal_i(99, tmp); + + /* Old snapshot should still have the old value */ + cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); + cl_assert_equal_i(5, tmp); git_config_free(snapshot); git_config_free(cfg); From 1ebb8e947fa21ecc6a7a145b832a2738d56c77d3 Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 21 Oct 2014 09:29:17 +1100 Subject: [PATCH 08/13] Change the length of the file so that the change is picked up. --- tests/config/snapshot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/config/snapshot.c b/tests/config/snapshot.c index d8e6f4e3bfd..e47e252ac4f 100644 --- a/tests/config/snapshot.c +++ b/tests/config/snapshot.c @@ -25,13 +25,13 @@ void test_config_snapshot__create_snapshot(void) cl_assert_equal_i(5, tmp); /* Change the value on the file itself (simulate external process) */ - cl_git_mkfile(filename, "[old]\nvalue = 99\n"); + cl_git_mkfile(filename, "[old]\nvalue = 999\n"); cl_git_pass(git_config_snapshot(&new_snapshot, cfg)); /* New snapshot should see new value */ cl_git_pass(git_config_get_int32(&tmp, new_snapshot, "old.value")); - cl_assert_equal_i(99, tmp); + cl_assert_equal_i(999, tmp); /* Old snapshot should still have the old value */ cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); From 7881bab2eb176a5e055221d3f2c1f0665871c1fe Mon Sep 17 00:00:00 2001 From: Alan Rogers Date: Tue, 21 Oct 2014 09:29:45 +1100 Subject: [PATCH 09/13] Patch from @carlosmn to refresh the parent config before snapshotting. --- src/config_file.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config_file.c b/src/config_file.c index 8f55c42f348..1f73e7e11f8 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -767,12 +767,17 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve { diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg; diskfile_backend *src = b->snapshot_from; + diskfile_header *src_header = &src->header; refcounted_strmap *src_map; + int error; + + if (!src_header->readonly && (error = config_refresh(&src_header->parent)) < 0) + return error; /* We're just copying data, don't care about the level */ GIT_UNUSED(level); - src_map = refcounted_strmap_take(&src->header); + src_map = refcounted_strmap_take(src_header); b->header.values = src_map; return 0; From 559528734fe8a6be148670ba34d79e58c1f48d3d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Fri, 24 Oct 2014 08:23:14 -0700 Subject: [PATCH 10/13] Fixed memory leak in git_tag_delete() --- src/tag.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tag.c b/src/tag.c index d7b531d3408..ca2ed531bbf 100644 --- a/src/tag.c +++ b/src/tag.c @@ -406,8 +406,9 @@ int git_tag_delete(git_repository *repo, const char *tag_name) if (error < 0) return error; - if ((error = git_reference_delete(tag_ref)) == 0) - git_reference_free(tag_ref); + error = git_reference_delete(tag_ref); + + git_reference_free(tag_ref); return error; } From f4cb227c4e391d055916f03b70ad0700cb0b9003 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 25 Oct 2014 15:30:29 -0400 Subject: [PATCH 11/13] travis ci: build maint branches and development' --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bab02bb44ab..f93a9214ca0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,10 +46,11 @@ after_success: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install valgrind; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi -# Only watch the development branch +# Watch development and stable branches branches: only: - development + - /^maint\/.*$/ # Notify development list when needed notifications: From cb93013aa6cd1f17d79e04ff38236a37ded2a86c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 25 Oct 2014 17:17:15 -0400 Subject: [PATCH 12/13] config test: clean up memory leak --- tests/config/snapshot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/config/snapshot.c b/tests/config/snapshot.c index e47e252ac4f..3ea07c11841 100644 --- a/tests/config/snapshot.c +++ b/tests/config/snapshot.c @@ -37,6 +37,7 @@ void test_config_snapshot__create_snapshot(void) cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); cl_assert_equal_i(5, tmp); + git_config_free(new_snapshot); git_config_free(snapshot); git_config_free(cfg); } From cc71348db9dbfa348199c357235fa30e61f1009a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 25 Oct 2014 19:52:11 -0400 Subject: [PATCH 13/13] global: clean up openssl_locks on shutdown --- src/global.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/global.c b/src/global.c index f8b387676b4..5238af9d2b8 100644 --- a/src/global.c +++ b/src/global.c @@ -61,8 +61,12 @@ void openssl_locking_function(int mode, int n, const char *file, int line) git_mutex_unlock(&openssl_locks[n]); } } -#endif +static void shutdown_ssl(void) +{ + git__free(openssl_locks); +} +#endif static void init_ssl(void) { @@ -110,6 +114,8 @@ static void init_ssl(void) CRYPTO_set_locking_callback(openssl_locking_function); } + + git__on_shutdown(shutdown_ssl); # endif #endif }