lookup.c 16.3 KB
Newer Older
Russell Belfer committed
1 2
#include "clar_libgit2.h"
#include "submodule_helpers.h"
3
#include "git2/sys/repository.h"
4
#include "repository.h"
5
#include "futils.h"
6
#include "index.h"
Russell Belfer committed
7 8 9 10 11

static git_repository *g_repo = NULL;

void test_submodule_lookup__initialize(void)
{
12
	g_repo = setup_fixture_submod2();
Russell Belfer committed
13 14
}

15 16 17 18 19
void test_submodule_lookup__cleanup(void)
{
	cl_git_sandbox_cleanup();
}

Russell Belfer committed
20 21
void test_submodule_lookup__simple_lookup(void)
{
22
	assert_submodule_exists(g_repo, "sm_unchanged");
Russell Belfer committed
23 24

	/* lookup pending change in .gitmodules that is not in HEAD */
25
	assert_submodule_exists(g_repo, "sm_added_and_uncommited");
Russell Belfer committed
26

27 28
	/* lookup pending change in .gitmodules that is not in HEAD nor index */
	assert_submodule_exists(g_repo, "sm_gitmodules_only");
29

Russell Belfer committed
30
	/* lookup git repo subdir that is not added as submodule */
31
	refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
Russell Belfer committed
32 33

	/* lookup existing directory that is not a submodule */
34
	refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
Russell Belfer committed
35 36

	/* lookup existing file that is not a submodule */
37
	refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
Russell Belfer committed
38 39

	/* lookup non-existent item */
40
	refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
41 42 43

	/* lookup a submodule by path with a trailing slash */
	assert_submodule_exists(g_repo, "sm_added_and_uncommited/");
Russell Belfer committed
44 45
}

46 47 48 49 50 51
void test_submodule_lookup__can_be_dupped(void)
{
	git_submodule *sm;
	git_submodule *sm_duplicate;
	const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";

52
	/* Check original */
53 54 55 56 57 58 59 60 61 62 63 64 65
	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
	cl_assert(git_submodule_owner(sm) == g_repo);
	cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
	cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
	cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);

	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);

	cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
	cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);

66
	/* Duplicate and free original */
67 68 69
	cl_assert(git_submodule_dup(&sm_duplicate, sm) == 0);
	git_submodule_free(sm);

70
	/* Check duplicate */
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	cl_assert(git_submodule_owner(sm_duplicate) == g_repo);
	cl_assert_equal_s("sm_unchanged", git_submodule_name(sm_duplicate));
	cl_assert(git__suffixcmp(git_submodule_path(sm_duplicate), "sm_unchanged") == 0);
	cl_assert(git__suffixcmp(git_submodule_url(sm_duplicate), "/submod2_target") == 0);

	cl_assert(git_oid_streq(git_submodule_index_id(sm_duplicate), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_head_id(sm_duplicate), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm_duplicate), oid) == 0);

	cl_assert(git_submodule_ignore(sm_duplicate) == GIT_SUBMODULE_IGNORE_NONE);
	cl_assert(git_submodule_update_strategy(sm_duplicate) == GIT_SUBMODULE_UPDATE_CHECKOUT);

	git_submodule_free(sm_duplicate);
}

Russell Belfer committed
86 87 88 89 90 91 92 93 94 95 96
void test_submodule_lookup__accessors(void)
{
	git_submodule *sm;
	const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";

	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
	cl_assert(git_submodule_owner(sm) == g_repo);
	cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
	cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
	cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);

97 98 99
	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
Russell Belfer committed
100 101

	cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
102
	cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
Russell Belfer committed
103

104 105 106
	git_submodule_free(sm);


Russell Belfer committed
107 108 109
	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
	cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));

110 111 112
	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
Russell Belfer committed
113 114
		"3d9386c507f6b093471a3e324085657a3c2b4247") == 0);

115 116 117
	git_submodule_free(sm);


Russell Belfer committed
118 119 120
	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
	cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));

121 122 123
	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
	cl_assert(git_submodule_head_id(sm) == NULL);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
Russell Belfer committed
124

125 126 127
	git_submodule_free(sm);


Russell Belfer committed
128 129 130
	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
	cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));

131 132 133
	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
Russell Belfer committed
134
		"5e4963595a9774b90524d35a807169049de8ccad") == 0);
135 136

	git_submodule_free(sm);
Russell Belfer committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
}

typedef struct {
	int count;
} sm_lookup_data;

static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
{
	sm_lookup_data *data = payload;
	data->count += 1;
	cl_assert_equal_s(git_submodule_name(sm), name);
	return 0;
}

void test_submodule_lookup__foreach(void)
{
153
	git_config *cfg;
Russell Belfer committed
154
	sm_lookup_data data;
155 156 157 158 159

	memset(&data, 0, sizeof(data));
	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
	cl_assert_equal_i(8, data.count);

Russell Belfer committed
160
	memset(&data, 0, sizeof(data));
161 162 163 164 165 166 167 168 169 170 171

	/* Change the path for a submodule so it doesn't match the name */
	cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));

	cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
	cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
	cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
	cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));

	git_config_free(cfg);

Russell Belfer committed
172
	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
173
	cl_assert_equal_i(8, data.count);
Russell Belfer committed
174
}
175

176
static int foreach_cb(git_submodule *sm, const char *name, void *payload)
177 178 179 180 181 182 183 184 185
{
	GIT_UNUSED(sm);
	GIT_UNUSED(name);
	GIT_UNUSED(payload);
	return 0;
}

void test_submodule_lookup__duplicated_path(void)
{
186 187 188 189 190 191 192
	cl_git_rewritefile("submod2/.gitmodules",
			   "[submodule \"sm1\"]\n"
			   "    path = duplicated-path\n"
			   "    url = sm1\n"
			   "[submodule \"sm2\"]\n"
			   "    path = duplicated-path\n"
			   "    url = sm2\n");
193

194
	cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
195 196
}

197
void test_submodule_lookup__lookup_even_with_unborn_head(void)
198
{
199
	git_reference *head;
200

201
	/* put us on an unborn branch */
202
	cl_git_pass(git_reference_symbolic_create(
203
		&head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL));
204
	git_reference_free(head);
205

206
	test_submodule_lookup__simple_lookup(); /* baseline should still pass */
207 208 209 210 211 212 213
}

void test_submodule_lookup__lookup_even_with_missing_index(void)
{
	git_index *idx;

	/* give the repo an empty index */
214
	cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
215 216 217
	git_repository_set_index(g_repo, idx);
	git_index_free(idx);

218
	test_submodule_lookup__simple_lookup(); /* baseline should still pass */
219
}
220

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
void test_submodule_lookup__backslashes(void)
{
	git_config *cfg;
	git_submodule *sm;
	git_repository *subrepo;
	git_buf buf = GIT_BUF_INIT;
	const char *backslashed_path = "..\\submod2_target";

	cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
	cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.url", backslashed_path));
	git_config_free(cfg);

	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
	cl_assert_equal_s(backslashed_path, git_submodule_url(sm));
	cl_git_pass(git_submodule_open(&subrepo, sm));

	cl_git_pass(git_submodule_resolve_url(&buf, g_repo, backslashed_path));

239
	git_buf_dispose(&buf);
240 241 242 243
	git_submodule_free(sm);
	git_repository_free(subrepo);
}

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
static void baseline_tests(void)
{
	/* small baseline that should work even if we change the index or make
	 * commits from the index
	 */
	assert_submodule_exists(g_repo, "sm_unchanged");
	assert_submodule_exists(g_repo, "sm_gitmodules_only");
	refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
}

static void add_submodule_with_commit(const char *name)
{
	git_submodule *sm;
	git_repository *smrepo;
	git_index *idx;
259
	git_str p = GIT_STR_INIT;
260 261 262 263 264 265 266 267 268

	cl_git_pass(git_submodule_add_setup(&sm, g_repo,
		"https://github.com/libgit2/libgit2.git", name, 1));

	assert_submodule_exists(g_repo, name);

	cl_git_pass(git_submodule_open(&smrepo, sm));
	cl_git_pass(git_repository_index(&idx, smrepo));

269
	cl_git_pass(git_str_joinpath(&p, git_repository_workdir(smrepo), "file"));
270
	cl_git_mkfile(p.ptr, "new file");
271
	git_str_dispose(&p);
272 273 274 275 276 277 278 279 280 281 282 283 284

	cl_git_pass(git_index_add_bypath(idx, "file"));
	cl_git_pass(git_index_write(idx));
	git_index_free(idx);

	cl_repo_commit_from_index(NULL, smrepo, NULL, 0, "initial commit");
	git_repository_free(smrepo);

	cl_git_pass(git_submodule_add_finalize(sm));

	git_submodule_free(sm);
}

285 286 287
void test_submodule_lookup__just_added(void)
{
	git_submodule *sm;
288
	git_str snap1 = GIT_STR_INIT, snap2 = GIT_STR_INIT;
289
	git_reference *original_head = NULL;
290 291 292

	refute_submodule_exists(g_repo, "sm_just_added", GIT_ENOTFOUND);
	refute_submodule_exists(g_repo, "sm_just_added_2", GIT_ENOTFOUND);
293 294
	refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_ENOTFOUND);
	refute_submodule_exists(g_repo, "sm_just_added_head", GIT_ENOTFOUND);
295 296
	refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
	refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
297
	baseline_tests();
298

299
	cl_git_pass(git_futils_readbuffer(&snap1, "submod2/.gitmodules"));
300
	cl_git_pass(git_repository_head(&original_head, g_repo));
301 302 303

	cl_git_pass(git_submodule_add_setup(&sm, g_repo,
		"https://github.com/libgit2/libgit2.git", "sm_just_added", 1));
304 305 306
	git_submodule_free(sm);
	assert_submodule_exists(g_repo, "sm_just_added");

307 308
	cl_git_pass(git_submodule_add_setup(&sm, g_repo,
		"https://github.com/libgit2/libgit2.git", "sm_just_added_2", 1));
309
	assert_submodule_exists(g_repo, "sm_just_added_2");
310
	cl_git_fail(git_submodule_add_finalize(sm)); /* fails if no HEAD */
311 312
	git_submodule_free(sm);

313 314 315 316 317 318 319
	add_submodule_with_commit("sm_just_added_head");
	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit new sm to head");
	assert_submodule_exists(g_repo, "sm_just_added_head");

	add_submodule_with_commit("sm_just_added_idx");
	assert_submodule_exists(g_repo, "sm_just_added_idx");

320
	cl_git_pass(git_futils_readbuffer(&snap2, "submod2/.gitmodules"));
321

322 323 324 325 326
	cl_git_append2file(
		"submod2/.gitmodules",
		"\n[submodule \"mismatch_name\"]\n"
		"\tpath = mismatch_path\n"
		"\turl = https://example.com/example.git\n\n");
327 328 329

	assert_submodule_exists(g_repo, "mismatch_name");
	assert_submodule_exists(g_repo, "mismatch_path");
330 331
	assert_submodule_exists(g_repo, "sm_just_added");
	assert_submodule_exists(g_repo, "sm_just_added_2");
332 333 334
	assert_submodule_exists(g_repo, "sm_just_added_idx");
	assert_submodule_exists(g_repo, "sm_just_added_head");
	baseline_tests();
335 336

	cl_git_rewritefile("submod2/.gitmodules", snap2.ptr);
337
	git_str_dispose(&snap2);
338

339 340
	refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
	refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
341 342
	assert_submodule_exists(g_repo, "sm_just_added");
	assert_submodule_exists(g_repo, "sm_just_added_2");
343 344 345
	assert_submodule_exists(g_repo, "sm_just_added_idx");
	assert_submodule_exists(g_repo, "sm_just_added_head");
	baseline_tests();
346

347
	cl_git_rewritefile("submod2/.gitmodules", snap1.ptr);
348
	git_str_dispose(&snap1);
349

350 351 352 353 354
	refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
	refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
	/* note error code change, because add_setup made a repo in the workdir */
	refute_submodule_exists(g_repo, "sm_just_added", GIT_EEXISTS);
	refute_submodule_exists(g_repo, "sm_just_added_2", GIT_EEXISTS);
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
	/* these still exist in index and head respectively */
	assert_submodule_exists(g_repo, "sm_just_added_idx");
	assert_submodule_exists(g_repo, "sm_just_added_head");
	baseline_tests();

	{
		git_index *idx;
		cl_git_pass(git_repository_index(&idx, g_repo));
		cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_idx"));
		cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_head"));
		cl_git_pass(git_index_write(idx));
		git_index_free(idx);
	}

	refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_EEXISTS);
	assert_submodule_exists(g_repo, "sm_just_added_head");

	{
373
		cl_git_pass(git_reference_create(NULL, g_repo, "refs/heads/master", git_reference_target(original_head), 1, "move head back"));
374 375 376 377
		git_reference_free(original_head);
	}

	refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS);
378
}
379

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
/* Test_App and Test_App2 are fairly similar names, make sure we load the right one */
void test_submodule_lookup__prefix_name(void)
{
	git_submodule *sm;

	cl_git_rewritefile("submod2/.gitmodules",
			   "[submodule \"Test_App\"]\n"
			   "    path = Test_App\n"
			   "    url = ../Test_App\n"
			   "[submodule \"Test_App2\"]\n"
			   "    path = Test_App2\n"
			   "    url = ../Test_App\n");

	cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App"));
	cl_assert_equal_s("Test_App", git_submodule_name(sm));

	git_submodule_free(sm);

	cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App2"));
	cl_assert_equal_s("Test_App2", git_submodule_name(sm));

	git_submodule_free(sm);
}
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426

void test_submodule_lookup__renamed(void)
{
	const char *newpath = "sm_actually_changed";
	git_index *idx;
	sm_lookup_data data;

	cl_git_pass(git_repository_index__weakptr(&idx, g_repo));

	/* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */

	cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed"));

	/* Change the path in .gitmodules and stage it*/
	{
		git_config *cfg;

		cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
		cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.path", newpath));
		git_config_free(cfg);

		cl_git_pass(git_index_add_bypath(idx, ".gitmodules"));
	}

Dmitriy Olshevskiy committed
427
	/* Change the worktree info in the submodule's config */
428 429 430 431 432 433 434 435 436 437 438
	{
		git_config *cfg;

		cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.git/modules/sm_unchanged/config"));
		cl_git_pass(git_config_set_string(cfg, "core.worktree", "../../../sm_actually_changed"));
		git_config_free(cfg);
	}

	/* Rename the entry in the index */
	{
		const git_index_entry *e;
439
		git_index_entry entry = {{ 0 }};
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457

		e = git_index_get_bypath(idx, "sm_unchanged", 0);
		cl_assert(e);
		cl_assert_equal_i(GIT_FILEMODE_COMMIT, e->mode);

		entry.path = newpath;
		entry.mode = GIT_FILEMODE_COMMIT;
		git_oid_cpy(&entry.id, &e->id);

		cl_git_pass(git_index_remove(idx, "sm_unchanged", 0));
		cl_git_pass(git_index_add(idx, &entry));
		cl_git_pass(git_index_write(idx));
	}

	memset(&data, 0, sizeof(data));
	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
	cl_assert_equal_i(8, data.count);
}
458

459 460
void test_submodule_lookup__cached(void)
{
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
	git_submodule *sm;
	git_submodule *sm2;
	/* See that the simple tests still pass. */

	git_repository_submodule_cache_all(g_repo);
	test_submodule_lookup__simple_lookup();
	git_repository_submodule_cache_clear(g_repo);
	test_submodule_lookup__simple_lookup();

	/* Check that subsequent calls return different objects when cached. */
	git_repository_submodule_cache_all(g_repo);
	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
	cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
	cl_assert_equal_p(sm, sm2);
	git_submodule_free(sm2);

	/* and that we get new objects again after clearing the cache. */
	git_repository_submodule_cache_clear(g_repo);
	cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
	cl_assert(sm != sm2);
	git_submodule_free(sm);
	git_submodule_free(sm2);
}
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

void test_submodule_lookup__lookup_in_bare_repository_fails(void)
{
	git_submodule *sm;

	cl_git_sandbox_cleanup();
	g_repo = cl_git_sandbox_init("submodules.git");

	cl_git_fail(git_submodule_lookup(&sm, g_repo, "nonexisting"));
}

void test_submodule_lookup__foreach_in_bare_repository_fails(void)
{
	cl_git_sandbox_cleanup();
	g_repo = cl_git_sandbox_init("submodules.git");

	cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
}
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

void test_submodule_lookup__fail_invalid_gitmodules(void)
{
	git_submodule *sm;
	sm_lookup_data data;
	memset(&data, 0, sizeof(data));

	cl_git_rewritefile("submod2/.gitmodules",
			   "[submodule \"Test_App\"\n"
			   "    path = Test_App\n"
			   "    url = ../Test_App\n");

	cl_git_fail(git_submodule_lookup(&sm, g_repo, "Test_App"));

	cl_git_fail(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
}