const char *repo_path,
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.
*
* @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);
}
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;
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(
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,
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);
/* 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;
}
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,
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);
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);
}
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);
}
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);
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"));
git_buf_free(&actual);
git_buf_free(&old_content);
git_tree_free(head);
- git_config_free(cfg);
}
static void check_single_patch_stats(
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"));
g_repo, 1, 1, 1, 6, expected_sizes, expected);
git_buf_free(&content);
- git_config_free(cfg);
}
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)
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);