template.c 7.81 KB
Newer Older
1 2
#include "clar_libgit2.h"

3
#include "futils.h"
4 5 6 7 8 9 10 11
#include "repo/repo_helpers.h"

#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)

static git_repository *_repo = NULL;
static mode_t g_umask = 0;
static git_buf _global_path = GIT_BUF_INIT;

12 13 14
static const char *fixture_repo;
static const char *fixture_templates;

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
void test_repo_template__initialize(void)
{
	_repo = NULL;

	/* load umask if not already loaded */
	if (!g_umask) {
		g_umask = p_umask(022);
		(void)p_umask(g_umask);
	}
}

void test_repo_template__cleanup(void)
{
	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
		_global_path.ptr);
	git_buf_dispose(&_global_path);

	cl_fixture_cleanup("tmp_global_path");

34 35 36 37 38 39 40 41 42 43
	if (fixture_repo) {
		cl_fixture_cleanup(fixture_repo);
		fixture_repo = NULL;
	}

	if (fixture_templates) {
		cl_fixture_cleanup(fixture_templates);
		fixture_templates = NULL;
	}

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	git_repository_free(_repo);
	_repo = NULL;
}

static void assert_hooks_match(
	const char *template_dir,
	const char *repo_dir,
	const char *hook_path,
	bool core_filemode)
{
	git_buf expected = GIT_BUF_INIT;
	git_buf actual = GIT_BUF_INIT;
	struct stat expected_st, st;

	cl_git_pass(git_buf_joinpath(&expected, template_dir, hook_path));
	cl_git_pass(git_path_lstat(expected.ptr, &expected_st));

	cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path));
	cl_git_pass(git_path_lstat(actual.ptr, &st));

	cl_assert(expected_st.st_size == st.st_size);

	if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
		mode_t expected_mode =
			GIT_MODE_TYPE(expected_st.st_mode) |
			(GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);

		if (!core_filemode) {
			CLEAR_FOR_CORE_FILEMODE(expected_mode);
			CLEAR_FOR_CORE_FILEMODE(st.st_mode);
		}

		cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
	}

	git_buf_dispose(&expected);
	git_buf_dispose(&actual);
}

static void assert_mode_seems_okay(
	const char *base, const char *path,
	git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
{
	git_buf full = GIT_BUF_INIT;
	struct stat st;

	cl_git_pass(git_buf_joinpath(&full, base, path));
	cl_git_pass(git_path_lstat(full.ptr, &st));
	git_buf_dispose(&full);

	if (!core_filemode) {
		CLEAR_FOR_CORE_FILEMODE(expect_mode);
		CLEAR_FOR_CORE_FILEMODE(st.st_mode);
		expect_setgid = false;
	}

	if (S_ISGID != 0)
		cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);

	cl_assert_equal_b(
		GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));

	cl_assert_equal_i_fmt(
		GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
}

110 111 112 113 114 115 116
static void setup_repo(const char *name, git_repository_init_options *opts)
{
	cl_git_pass(git_repository_init_ext(&_repo, name, opts));
	fixture_repo = name;
}

static void setup_templates(const char *name, bool setup_globally)
117
{
118
	git_buf path = GIT_BUF_INIT;
119

120 121 122 123 124
	cl_fixture_sandbox("template");
	if (strcmp(name, "template"))
		cl_must_pass(p_rename("template", name));

	fixture_templates = name;
125

126 127
	/*
	 * Create a symlink from link.sample to update.sample if the filesystem
128 129
	 * supports it.
	 */
130
	cl_git_pass(git_buf_join3(&path, '/', name, "hooks", "link.sample"));
131
#ifdef GIT_WIN32
132
	cl_git_mkfile(path.ptr, "#!/bin/sh\necho hello, world\n");
133
#else
134
	cl_must_pass(p_symlink("update.sample", path.ptr));
135 136
#endif

137
	git_buf_clear(&path);
138

139 140 141
	/* Create a file starting with a dot */
	cl_git_pass(git_buf_join3(&path, '/', name, "hooks", ".dotfile"));
	cl_git_mkfile(path.ptr, "something\n");
142

143
	git_buf_clear(&path);
144

145 146 147 148 149 150
	if (setup_globally) {
		cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), name));
		create_tmp_global_config("tmp_global_path", "init.templatedir", path.ptr);
	}

	git_buf_dispose(&path);
151 152 153 154
}

static void validate_templates(git_repository *repo, const char *template_path)
{
155
	git_buf path = GIT_BUF_INIT, expected = GIT_BUF_INIT, actual = GIT_BUF_INIT;
156 157
	int filemode;

158 159
	cl_git_pass(git_buf_joinpath(&path, template_path, "description"));
	cl_git_pass(git_futils_readbuffer(&expected, path.ptr));
160

161 162 163 164
	git_buf_clear(&path);

	cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "description"));
	cl_git_pass(git_futils_readbuffer(&actual, path.ptr));
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

	cl_assert_equal_s(expected.ptr, actual.ptr);

	filemode = cl_repo_get_bool(repo, "core.filemode");

	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/update.sample", filemode);
	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/link.sample", filemode);
	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/.dotfile", filemode);

	git_buf_dispose(&expected);
	git_buf_dispose(&actual);
182
	git_buf_dispose(&path);
183 184 185 186 187 188 189 190 191 192
}

void test_repo_template__external_templates_specified_in_options(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
	opts.template_path = "template";

193 194
	setup_templates("template", false);
	setup_repo("templated.git", &opts);
195 196 197 198 199 200 201 202 203 204 205

	validate_templates(_repo, "template");
}

void test_repo_template__external_templates_specified_in_config(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;

206 207
	setup_templates("template", true);
	setup_repo("templated.git", &opts);
208 209 210 211 212 213 214 215 216 217 218

	validate_templates(_repo, "template");
}

void test_repo_template__external_templates_with_leading_dot(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;

219 220
	setup_templates(".template_with_leading_dot", true);
	setup_repo("templated.git", &opts);
221 222 223 224 225 226 227

	validate_templates(_repo, ".template_with_leading_dot");
}

void test_repo_template__extended_with_template_and_shared_mode(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
228 229
	const char *repo_path;
	int filemode;
230 231 232 233 234 235

	opts.flags = GIT_REPOSITORY_INIT_MKPATH |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
	opts.template_path = "template";
	opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;

236 237
	setup_templates("template", false);
	setup_repo("init_shared_from_tpl", &opts);
238 239 240 241 242 243 244 245 246 247 248 249 250 251

	filemode = cl_repo_get_bool(_repo, "core.filemode");

	repo_path = git_repository_path(_repo);
	assert_mode_seems_okay(repo_path, "hooks",
		GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
	assert_mode_seems_okay(repo_path, "info",
		GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
	assert_mode_seems_okay(repo_path, "description",
		GIT_FILEMODE_BLOB, false, filemode);

	validate_templates(_repo, "template");
}

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
void test_repo_template__templated_head_is_used(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
	git_buf head = GIT_BUF_INIT;

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;

	setup_templates("template", true);
	cl_git_mkfile("template/HEAD", "foobar\n");
	setup_repo("repo", &opts);

	cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD"));
	cl_assert_equal_s("foobar\n", head.ptr);

	git_buf_dispose(&head);
}

void test_repo_template__initial_head_option_overrides_template_head(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
	git_buf head = GIT_BUF_INIT;

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
	opts.initial_head = "manual";

	setup_templates("template", true);
	cl_git_mkfile("template/HEAD", "foobar\n");
	setup_repo("repo", &opts);

	cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD"));
	cl_assert_equal_s("ref: refs/heads/manual\n", head.ptr);

	git_buf_dispose(&head);
}

287 288 289 290
void test_repo_template__empty_template_path(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

291 292
	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
	opts.template_path = "";
293

294
	setup_repo("foo", &opts);
295
}