diff options
-rw-r--r-- | include/git2/repository.h | 17 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/repository.c | 100 | ||||
-rw-r--r-- | tests-clar/clar_libgit2.c | 3 | ||||
-rw-r--r-- | tests-clar/diff/diff_helpers.c | 43 | ||||
-rw-r--r-- | tests-clar/diff/diffiter.c | 10 | ||||
-rw-r--r-- | tests-clar/diff/drivers.c | 7 | ||||
-rw-r--r-- | tests-clar/diff/patch.c | 8 | ||||
-rw-r--r-- | tests-clar/index/filemodes.c | 30 |
9 files changed, 167 insertions, 53 deletions
diff --git a/include/git2/repository.h b/include/git2/repository.h index b4d561992..74ab65bf7 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -288,6 +288,23 @@ GIT_EXTERN(int) git_repository_init_ext( git_repository_init_options *opts); /** + * Update the filesystem config settings for an open repository + * + * When a repository is initialized, config values are set based on the + * properties of the filesystem that the repository is on, such as + * "core.ignorecase", "core.filemode", "core.symlinks", etc. If the + * repository is moved to a new filesystem, these properties may no + * longer be correct and API calls may not behave as expected. This + * call reruns the phase of repository initialization that sets those + * properties to compensate for the current filesystem of the repo. + * + * @param repo A repository object + * @returrn 0 on success, < 0 on error + */ +GIT_EXTERN(int) git_repository_reset_filesystem( + git_repository *repo); + +/** * Retrieve and resolve the reference pointed at by HEAD. * * The returned `git_reference` will be owned by caller and diff --git a/src/config.h b/src/config.h index 85db5e3e1..01e8465cc 100644 --- a/src/config.h +++ b/src/config.h @@ -47,7 +47,7 @@ extern int git_config_rename_section( * @param out the new backend * @param path where the config file is located */ -extern int git_config_file__ondisk(struct git_config_backend **out, const char *path); +extern int git_config_file__ondisk(git_config_backend **out, const char *path); extern int git_config__normalize_name(const char *in, char **out); diff --git a/src/repository.c b/src/repository.c index f609e5779..52509ffc1 100644 --- a/src/repository.c +++ b/src/repository.c @@ -962,34 +962,46 @@ static int create_empty_file(const char *path, mode_t mode) } static int repo_init_config( + git_config *parent, const char *repo_dir, const char *work_dir, - git_repository_init_options *opts) + uint32_t flags, + uint32_t mode) { int error = 0; - git_buf cfg_path = GIT_BUF_INIT; + git_buf buf = GIT_BUF_INIT; + const char *cfg_path = NULL; git_config *config = NULL; - bool is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0); + bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0); #define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \ goto cleanup; } while (0) - if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0) + if (git_buf_joinpath(&buf, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0) return -1; + cfg_path = git_buf_cstr(&buf); - if (!git_path_isfile(git_buf_cstr(&cfg_path)) && - create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) { - git_buf_free(&cfg_path); - return -1; - } + if (!git_path_isfile(cfg_path) && + (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0) + goto cleanup; - if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) { - git_buf_free(&cfg_path); - return -1; + if (!parent) + error = git_config_open_ondisk(&config, cfg_path); + else if ((error = git_config_open_level( + &config, parent, GIT_CONFIG_LEVEL_LOCAL)) < 0) + { + giterr_clear(); + + if (!(error = git_config_add_file_ondisk( + parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false))) + error = git_config_open_level( + &config, parent, GIT_CONFIG_LEVEL_LOCAL); } + if (error < 0) + goto cleanup; - if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 && + if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 && (error = check_repositoryformatversion(config)) < 0) goto cleanup; @@ -998,7 +1010,7 @@ static int repo_init_config( SET_REPO_CONFIG( int32, "core.repositoryformatversion", GIT_REPO_VERSION); SET_REPO_CONFIG( - bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path))); + bool, "core.filemode", is_chmod_supported(cfg_path)); #ifdef GIT_USE_ICONV SET_REPO_CONFIG( @@ -1009,38 +1021,70 @@ static int repo_init_config( if (!are_symlinks_supported(is_bare ? repo_dir : work_dir)) SET_REPO_CONFIG(bool, "core.symlinks", false); + /* core git does not do this on a reinit, but it is a property of + * the filesystem, so I think we should... + */ + if (!(flags & GIT_REPOSITORY_INIT__IS_REINIT) && + is_filesystem_case_insensitive(repo_dir)) + SET_REPO_CONFIG(bool, "core.ignorecase", true); + if (!is_bare) { SET_REPO_CONFIG(bool, "core.logallrefupdates", true); - if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { + if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { SET_REPO_CONFIG(string, "core.worktree", work_dir); } - else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) { + else if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) { if (git_config_delete_entry(config, "core.worktree") < 0) giterr_clear(); } } - if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) && - is_filesystem_case_insensitive(repo_dir)) - SET_REPO_CONFIG(bool, "core.ignorecase", true); - - if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) { + if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) { SET_REPO_CONFIG(int32, "core.sharedrepository", 1); SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); } - else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) { + else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) { SET_REPO_CONFIG(int32, "core.sharedrepository", 2); SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); } cleanup: - git_buf_free(&cfg_path); + git_buf_free(&buf); git_config_free(config); return error; } +int git_repository_reset_filesystem(git_repository *repo) +{ + int error = 0; + uint32_t flags = 0; + const char *repo_dir, *work_dir; + git_config *cfg; + + assert(repo); + + repo_dir = git_repository_path(repo); + work_dir = git_repository_workdir(repo); + + if (git_repository_is_bare(repo)) + flags |= GIT_REPOSITORY_INIT_BARE; + else if (!git__prefixcmp(repo_dir, work_dir) && + !strcmp(repo_dir + strlen(work_dir), DOT_GIT "/")) + flags |= GIT_REPOSITORY_INIT__NATURAL_WD; + + if ((error = git_repository_config(&cfg, repo)) < 0) + return error; + + error = repo_init_config(cfg, repo_dir, work_dir, flags, 0); + + git_config_free(cfg); + git_repository__cvar_cache_clear(repo); + + return error; +} + static int repo_write_template( const char *git_dir, bool allow_overwrite, @@ -1429,22 +1473,22 @@ int git_repository_init_ext( opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT; error = repo_init_config( - git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts); + NULL, repo_path.ptr, wd_path.ptr, opts->flags, opts->mode); /* TODO: reinitialize the templates */ } else { if (!(error = repo_init_structure( - git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) && + repo_path.ptr, wd_path.ptr, opts)) && !(error = repo_init_config( - git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts))) + NULL, repo_path.ptr, wd_path.ptr, opts->flags, opts->mode))) error = repo_init_create_head( - git_buf_cstr(&repo_path), opts->initial_head); + repo_path.ptr, opts->initial_head); } if (error < 0) goto cleanup; - error = git_repository_open(out, git_buf_cstr(&repo_path)); + error = git_repository_open(out, repo_path.ptr); if (!error && opts->origin_url) error = repo_init_create_origin(*out, opts->origin_url); diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c index 0f8f26efb..c3abc1f95 100644 --- a/tests-clar/clar_libgit2.c +++ b/tests-clar/clar_libgit2.c @@ -190,6 +190,9 @@ git_repository *cl_git_sandbox_init(const char *sandbox) /* Now open the sandbox repository and make it available for tests */ cl_git_pass(git_repository_open(&_cl_repo, sandbox)); + /* Adjust configs after copying to new filesystem */ + cl_git_pass(git_repository_reset_filesystem(_cl_repo)); + return _cl_repo; } diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index a5c322b4d..3452f231d 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -21,6 +21,35 @@ git_tree *resolve_commit_oid_to_tree( return tree; } +static char diff_pick_suffix(int mode) +{ + if (S_ISDIR(mode)) + return '/'; + else if (GIT_PERMS_IS_EXEC(mode)) + return '*'; + else + return ' '; +} + +static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress) +{ + char code = git_diff_status_char(delta->status); + char old_suffix = diff_pick_suffix(delta->old_file.mode); + char new_suffix = diff_pick_suffix(delta->new_file.mode); + + fprintf(fp, "%c\t%s", code, delta->old_file.path); + + if ((delta->old_file.path != delta->new_file.path && + strcmp(delta->old_file.path, delta->new_file.path) != 0) || + (delta->old_file.mode != delta->new_file.mode && + delta->old_file.mode != 0 && delta->new_file.mode != 0)) + fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix); + else if (old_suffix != ' ') + fprintf(fp, "%c", old_suffix); + + fprintf(fp, "\t[%.2f]\n", progress); +} + int diff_file_cb( const git_diff_delta *delta, float progress, @@ -29,9 +58,7 @@ int diff_file_cb( diff_expects *e = payload; if (e->debug) - fprintf(stderr, "%c %s (%.3f)\n", - git_diff_status_char(delta->status), - delta->old_file.path, progress); + fprintf_delta(stderr, delta, progress); if (e->names) cl_assert_equal_s(e->names[e->files], delta->old_file.path); @@ -55,8 +82,14 @@ int diff_print_file_cb( float progress, void *payload) { - fprintf(stderr, "%c %s\n", - git_diff_status_char(delta->status), delta->old_file.path); + if (!payload) { + fprintf_delta(stderr, delta, progress); + return 0; + } + + if (!((diff_expects *)payload)->debug) + fprintf_delta(stderr, delta, progress); + return diff_file_cb(delta, progress, payload); } diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c index 932d720f2..ea5908475 100644 --- a/tests-clar/diff/diffiter.c +++ b/tests-clar/diff/diffiter.c @@ -27,25 +27,25 @@ void test_diff_diffiter__create(void) git_diff_list_free(diff); } -void test_diff_diffiter__iterate_files(void) +void test_diff_diffiter__iterate_files_1(void) { git_repository *repo = cl_git_sandbox_init("attr"); git_diff_list *diff; size_t d, num_d; - int count = 0; + diff_expects exp = { 0 }; cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL)); num_d = git_diff_num_deltas(diff); - cl_assert_equal_i(6, (int)num_d); for (d = 0; d < num_d; ++d) { const git_diff_delta *delta; cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d)); cl_assert(delta != NULL); - count++; + + diff_file_cb(delta, (float)d / (float)num_d, &exp); } - cl_assert_equal_i(6, count); + cl_assert_equal_sz(6, exp.files); git_diff_list_free(diff); } diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c index e02dd5c68..719d229fc 100644 --- a/tests-clar/diff/drivers.c +++ b/tests-clar/diff/drivers.c @@ -147,6 +147,13 @@ void test_diff_drivers__long_lines(void) cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); cl_git_pass(git_diff_patch_to_str(&actual, patch)); + /* if chmod not supported, overwrite mode bits since anything is possible */ + if (!cl_is_chmod_supported()) { + size_t actual_len = strlen(actual); + if (actual_len > 72 && memcmp(&actual[66], "100644", 6) != 0) + memcpy(&actual[66], "100644", 6); + } + cl_assert_equal_s(expected, actual); free(actual); diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 6a33fa990..7aab8f409 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -238,6 +238,9 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) cl_git_pass(git_config_new(&cfg)); git_repository_set_config(g_repo, cfg); + git_config_free(cfg); + + git_repository_reset_filesystem(g_repo); cl_git_pass( git_futils_readbuffer(&old_content, "renames/songof7cities.txt")); @@ -408,7 +411,6 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) git_buf_free(&actual); git_buf_free(&old_content); git_tree_free(head); - git_config_free(cfg); } static void check_single_patch_stats( @@ -520,6 +522,9 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_pass(git_config_new(&cfg)); git_repository_set_config(g_repo, cfg); + git_config_free(cfg); + + git_repository_reset_filesystem(g_repo); cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt")); @@ -574,5 +579,4 @@ void test_diff_patch__line_counts_with_eofnl(void) g_repo, 1, 1, 1, 6, expected_sizes, expected); git_buf_free(&content); - git_config_free(cfg); } diff --git a/tests-clar/index/filemodes.c b/tests-clar/index/filemodes.c index 02f83efba..8cbd6ff3a 100644 --- a/tests-clar/index/filemodes.c +++ b/tests-clar/index/filemodes.c @@ -51,18 +51,24 @@ static void replace_file_with_mode( git_buf_free(&content); } -static void add_and_check_mode( - git_index *index, const char *filename, unsigned int expect_mode) +#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__) + +static void add_and_check_mode_( + git_index *index, const char *filename, unsigned int expect_mode, + const char *file, int line) { size_t pos; const git_index_entry *entry; cl_git_pass(git_index_add_bypath(index, filename)); - cl_assert(!git_index_find(&pos, index, filename)); + clar__assert(!git_index_find(&pos, index, filename), + file, line, "Cannot find index entry", NULL, 1); entry = git_index_get_byindex(index, pos); - cl_assert(entry->mode == expect_mode); + + clar__assert_equal(file, line, "Expected mode does not match index", + 1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode); } void test_index_filemodes__untrusted(void) @@ -91,16 +97,16 @@ void test_index_filemodes__untrusted(void) replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755); add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE); - /* 5 - add new 0644 -> expect 0644 */ - cl_git_write2file("filemodes/new_off", "blah", 0, - O_WRONLY | O_CREAT | O_TRUNC, 0644); - add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB); - - /* this test won't give predictable results on a platform - * that doesn't support filemodes correctly, so skip it. + /* these tests of newly added files won't give predictable results on + * filesystems without actual filemode support, so skip them. */ if (can_filemode) { - /* 6 - add 0755 -> expect 0755 */ + /* 5 - add new 0644 -> expect 0644 */ + cl_git_write2file("filemodes/new_off", "blah", 0, + O_WRONLY | O_CREAT | O_TRUNC, 0644); + add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB); + + /* 6 - add new 0755 -> expect 0755 */ cl_git_write2file("filemodes/new_on", "blah", 0, O_WRONLY | O_CREAT | O_TRUNC, 0755); add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE); |