t10-refs.c 28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2,
 * as published by the Free Software Foundation.
 *
 * In addition to the permissions in the GNU General Public License,
 * the authors give you unlimited permission to link the compiled
 * version of this file into combinations with other programs,
 * and to distribute those combinations without any restriction
 * coming from the use of this file.  (The General Public License
 * restrictions do apply in other respects; for example, they cover
 * modification of the file, and distribution when not linked into
 * a combined executable.)
 *
 * This file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include "test_lib.h"
#include "test_helpers.h"

28
#include "repository.h"
29 30 31 32

static const char *loose_tag_ref_name = "refs/tags/test";
static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";

33
BEGIN_TEST(readtag0, "lookup a loose tag reference")
34 35 36 37 38 39
	git_repository *repo;
	git_reference *reference;
	git_object *object;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

40
	must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
41 42
	must_be_true(reference->type & GIT_REF_OID);
	must_be_true((reference->type & GIT_REF_PACKED) == 0);
43 44
	must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);

45
	must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
46 47 48 49 50 51
	must_be_true(object != NULL);
	must_be_true(git_object_type(object) == GIT_OBJ_TAG);

	git_repository_free(repo);
END_TEST

52
BEGIN_TEST(readtag1, "lookup a loose tag reference that doesn't exist")
53 54 55 56
	git_repository *repo;
	git_reference *reference;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
57
	must_fail(git_reference_lookup(&reference, repo, non_existing_tag_ref_name));
58 59 60 61

	git_repository_free(repo);
END_TEST

62
static const char *head_tracker_sym_ref_name = "head-tracker";
63 64 65
static const char *current_head_target = "refs/heads/master";
static const char *current_master_tip = "be3563ae3f795b2b4353bcce3a527ad0a4f7f644";

66
BEGIN_TEST(readsym0, "lookup a symbolic reference")
67 68 69 70 71 72 73
	git_repository *repo;
	git_reference *reference, *resolved_ref;
	git_object *object;
	git_oid id;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

74
	must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
75 76
	must_be_true(reference->type & GIT_REF_SYMBOLIC);
	must_be_true((reference->type & GIT_REF_PACKED) == 0);
77
	must_be_true(strcmp(reference->name, GIT_HEAD_FILE) == 0);
78 79 80 81

	must_pass(git_reference_resolve(&resolved_ref, reference));
	must_be_true(resolved_ref->type == GIT_REF_OID);

82
	must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY));
83 84 85 86 87 88 89 90 91
	must_be_true(object != NULL);
	must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);

	git_oid_mkstr(&id, current_master_tip);
	must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);

	git_repository_free(repo);
END_TEST

92
BEGIN_TEST(readsym1, "lookup a nested symbolic reference")
93 94 95 96 97 98 99
	git_repository *repo;
	git_reference *reference, *resolved_ref;
	git_object *object;
	git_oid id;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

100
	must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
101 102
	must_be_true(reference->type & GIT_REF_SYMBOLIC);
	must_be_true((reference->type & GIT_REF_PACKED) == 0);
103 104 105 106 107
	must_be_true(strcmp(reference->name, head_tracker_sym_ref_name) == 0);

	must_pass(git_reference_resolve(&resolved_ref, reference));
	must_be_true(resolved_ref->type == GIT_REF_OID);

108
	must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY));
109 110 111 112
	must_be_true(object != NULL);
	must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);

	git_oid_mkstr(&id, current_master_tip);
113 114 115 116 117
	must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);

	git_repository_free(repo);
END_TEST

118
BEGIN_TEST(readsym2, "lookup the HEAD and resolve the master branch")
119
	git_repository *repo;
120
	git_reference *reference, *resolved_ref, *comp_base_ref;
121 122

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
123

124
	must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
125 126 127
	must_pass(git_reference_resolve(&resolved_ref, reference));
	comp_base_ref = resolved_ref;

128
	must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
129
	must_pass(git_reference_resolve(&resolved_ref, reference));
130 131
	must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));

132
	must_pass(git_reference_lookup(&reference, repo, current_head_target));
133 134
	must_pass(git_reference_resolve(&resolved_ref, reference));
	must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
135 136 137 138

	git_repository_free(repo);
END_TEST

139
BEGIN_TEST(readsym3, "lookup the master branch and then the HEAD")
140
	git_repository *repo;
141
	git_reference *reference, *master_ref, *resolved_ref;
142 143

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
144

145 146
	must_pass(git_reference_lookup(&master_ref, repo, current_head_target));
	must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
147

148
	must_pass(git_reference_resolve(&resolved_ref, reference));
149
	must_pass(git_oid_cmp(git_reference_oid(master_ref), git_reference_oid(resolved_ref)));
150

151 152 153 154 155 156
	git_repository_free(repo);
END_TEST

static const char *packed_head_name = "refs/heads/packed";
static const char *packed_test_head_name = "refs/heads/packed-test";

157
BEGIN_TEST(readpacked0, "lookup a packed reference")
158 159 160 161 162 163
	git_repository *repo;
	git_reference *reference;
	git_object *object;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

164
	must_pass(git_reference_lookup(&reference, repo, packed_head_name));
165 166
	must_be_true(reference->type & GIT_REF_OID);
	must_be_true((reference->type & GIT_REF_PACKED) != 0);
167 168
	must_be_true(strcmp(reference->name, packed_head_name) == 0);

169
	must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
170 171 172 173 174 175
	must_be_true(object != NULL);
	must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);

	git_repository_free(repo);
END_TEST

176
BEGIN_TEST(readpacked1, "assure that a loose reference is looked up before a packed reference")
177 178 179 180
	git_repository *repo;
	git_reference *reference;

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
181 182
	must_pass(git_reference_lookup(&reference, repo, packed_head_name));
	must_pass(git_reference_lookup(&reference, repo, packed_test_head_name));
183 184
	must_be_true(reference->type & GIT_REF_OID);
	must_be_true((reference->type & GIT_REF_PACKED) == 0);
185 186 187 188 189
	must_be_true(strcmp(reference->name, packed_test_head_name) == 0);

	git_repository_free(repo);
END_TEST

190
BEGIN_TEST(create0, "create a new symbolic reference")
191 192 193
	git_reference *new_reference, *looked_up_ref, *resolved_ref;
	git_repository *repo;
	git_oid id;
194
	char ref_path[GIT_PATH_MAX];
195 196 197

	const char *new_head_tracker = "another-head-tracker";

198
	git_oid_mkstr(&id, current_master_tip);
199 200 201 202

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

	/* Retrieve the physical path to the symbolic ref for further cleaning */
203
	git__joinpath(ref_path, repo->path_repository, new_head_tracker);
204 205

	/* Create and write the new symbolic reference */
206
	must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target));
207 208

	/* Ensure the reference can be looked-up... */
209
	must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
210 211
	must_be_true(looked_up_ref->type & GIT_REF_SYMBOLIC);
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);
212 213 214 215 216 217 218
	must_be_true(strcmp(looked_up_ref->name, new_head_tracker) == 0);

	/* ...peeled.. */
	must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
	must_be_true(resolved_ref->type == GIT_REF_OID);

	/* ...and that it points to the current master tip */
219 220 221 222 223 224 225
	must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);

	git_repository_free(repo);

	/* Similar test with a fresh new repository */
	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

226
	must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
227
	must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
228 229 230 231 232 233 234
	must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);

	git_repository_free(repo);

	must_pass(gitfo_unlink(ref_path));	/* TODO: replace with git_reference_delete() when available */
END_TEST

235
BEGIN_TEST(create1, "create a deep symbolic reference")
236 237 238 239 240 241 242 243 244 245 246 247 248
	git_reference *new_reference, *looked_up_ref, *resolved_ref;
	git_repository *repo;
	git_oid id;
	char ref_path[GIT_PATH_MAX];

	const char *new_head_tracker = "deep/rooted/tracker";

	git_oid_mkstr(&id, current_master_tip);

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

	git__joinpath(ref_path, repo->path_repository, new_head_tracker);
	must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target));
249
	must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
250 251 252 253 254 255 256 257
	must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
	must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);

	git_repository_free(repo);

	must_pass(gitfo_unlink(ref_path));	/* TODO: replace with git_reference_delete() when available */
END_TEST

258
BEGIN_TEST(create2, "create a new OID reference")
259 260 261
	git_reference *new_reference, *looked_up_ref;
	git_repository *repo;
	git_oid id;
262
	char ref_path[GIT_PATH_MAX];
263 264 265

	const char *new_head = "refs/heads/new-head";

266
	git_oid_mkstr(&id, current_master_tip);
267 268 269 270

	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

	/* Retrieve the physical path to the symbolic ref for further cleaning */
271
	git__joinpath(ref_path, repo->path_repository, new_head);
272 273

	/* Create and write the new object id reference */
274
	must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id));
275 276

	/* Ensure the reference can be looked-up... */
277
	must_pass(git_reference_lookup(&looked_up_ref, repo, new_head));
278 279
	must_be_true(looked_up_ref->type & GIT_REF_OID);
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);
280 281 282
	must_be_true(strcmp(looked_up_ref->name, new_head) == 0);

	/* ...and that it points to the current master tip */
283 284 285 286 287 288 289
	must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);

	git_repository_free(repo);

	/* Similar test with a fresh new repository */
	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));

290
	must_pass(git_reference_lookup(&looked_up_ref, repo, new_head));
291 292 293 294 295 296 297
	must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);

	git_repository_free(repo);

	must_pass(gitfo_unlink(ref_path));	/* TODO: replace with git_reference_delete() when available */
END_TEST

298
BEGIN_TEST(pack0, "create a packfile for an empty folder")
Vicent Marti committed
299
	git_repository *repo;
300 301
	char temp_path[GIT_PATH_MAX];
	const int mode = 0755; /* or 0777 ? */
302

303
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
304 305 306 307
	
	git__joinpath_n(temp_path, 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir");
	must_pass(gitfo_mkdir_recurs(temp_path, mode));

Vicent Marti committed
308
	must_pass(git_reference_packall(repo));
309

310
	close_temp_repo(repo);
311 312
END_TEST

313
BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
314 315 316 317
	git_repository *repo;
	git_reference *reference;
	char temp_path[GIT_PATH_MAX];

318
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
319 320
	
	/* Ensure a known loose ref can be looked up */
321
	must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
322 323 324 325 326 327 328 329 330 331
	must_be_true((reference->type & GIT_REF_PACKED) == 0);
	must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
	
	must_pass(git_reference_packall(repo));

	/* Ensure the packed-refs file exists */
	git__joinpath(temp_path, repo->path_repository, GIT_PACKEDREFS_FILE);
	must_pass(gitfo_exists(temp_path));

	/* Ensure the known ref can still be looked up but is now packed */
332
	must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
333 334 335 336 337 338 339
	must_be_true((reference->type & GIT_REF_PACKED) != 0);
	must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);

	/* Ensure the known ref has been removed from the loose folder structure */
	git__joinpath(temp_path, repo->path_repository, loose_tag_ref_name);
	must_pass(!gitfo_exists(temp_path));

340
	close_temp_repo(repo);
Vicent Marti committed
341 342
END_TEST

343
BEGIN_TEST(rename0, "rename a loose reference")
344 345 346 347 348
	git_reference *looked_up_ref, *another_looked_up_ref;
	git_repository *repo;
	char temp_path[GIT_PATH_MAX];
	const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";

349
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
350 351 352 353 354 355

	/* Ensure the ref doesn't exist on the file system */
	git__joinpath(temp_path, repo->path_repository, new_name);
	must_pass(!gitfo_exists(temp_path));

	/* Retrieval of the reference to rename */
356
	must_pass(git_reference_lookup(&looked_up_ref, repo, loose_tag_ref_name));
357 358 359 360 361 362 363 364 365

	/* ... which is indeed loose */
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* Now that the reference is renamed... */
	must_pass(git_reference_rename(looked_up_ref, new_name));
	must_be_true(!strcmp(looked_up_ref->name, new_name));

	/* ...It can't be looked-up with the old name... */
366
	must_fail(git_reference_lookup(&another_looked_up_ref, repo, loose_tag_ref_name));
367 368

	/* ...but the new name works ok... */
369
	must_pass(git_reference_lookup(&another_looked_up_ref, repo, new_name));
370 371 372 373 374 375 376 377 378 379
	must_be_true(!strcmp(another_looked_up_ref->name, new_name));

	/* .. the ref is still loose... */
	must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0);
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* ...and the ref can be found in the file system */
	git__joinpath(temp_path, repo->path_repository, new_name);
	must_pass(gitfo_exists(temp_path));

380
	close_temp_repo(repo);
381 382
END_TEST

383
BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
384 385 386 387 388
	git_reference *looked_up_ref, *another_looked_up_ref;
	git_repository *repo;
	char temp_path[GIT_PATH_MAX];
	const char *brand_new_name = "refs/heads/brand_new_name";

389
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
390 391 392 393 394 395

	/* Ensure the ref doesn't exist on the file system */
	git__joinpath(temp_path, repo->path_repository, packed_head_name);
	must_pass(!gitfo_exists(temp_path));

	/* The reference can however be looked-up... */
396
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
397 398 399 400 401 402 403 404 405

	/* .. and it's packed */
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);

	/* Now that the reference is renamed... */
	must_pass(git_reference_rename(looked_up_ref, brand_new_name));
	must_be_true(!strcmp(looked_up_ref->name, brand_new_name));

	/* ...It can't be looked-up with the old name... */
406
	must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_head_name));
407 408

	/* ...but the new name works ok... */
409
	must_pass(git_reference_lookup(&another_looked_up_ref, repo, brand_new_name));
410 411 412 413 414 415 416 417 418 419
	must_be_true(!strcmp(another_looked_up_ref->name, brand_new_name));

	/* .. the ref is no longer packed... */
	must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0);
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* ...and the ref now happily lives in the file system */
	git__joinpath(temp_path, repo->path_repository, brand_new_name);
	must_pass(gitfo_exists(temp_path));

420
	close_temp_repo(repo);
421 422
END_TEST

423
BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference which happens to be in both loose and pack state")
424 425 426 427 428
	git_reference *looked_up_ref, *another_looked_up_ref;
	git_repository *repo;
	char temp_path[GIT_PATH_MAX];
	const char *brand_new_name = "refs/heads/brand_new_name";

429
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
430 431 432 433 434 435

	/* Ensure the other reference exists on the file system */
	git__joinpath(temp_path, repo->path_repository, packed_test_head_name);
	must_pass(gitfo_exists(temp_path));

	/* Lookup the other reference */
436
	must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
437 438 439 440 441

	/* Ensure it's loose */
	must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* Lookup the reference to rename */
442
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
443 444 445 446 447 448 449 450

	/* Ensure it's packed */
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0);

	/* Now that the reference is renamed... */
	must_pass(git_reference_rename(looked_up_ref, brand_new_name));

	/* Lookup the other reference */
451
	must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
452 453 454 455 456 457 458

	/* Ensure it's loose */
	must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* Ensure the other ref still exists on the file system */
	must_pass(gitfo_exists(temp_path));

459
	close_temp_repo(repo);
460 461
END_TEST

462
BEGIN_TEST(rename3, "can not rename a reference with the name of an existing reference")
463 464 465
	git_reference *looked_up_ref;
	git_repository *repo;

466
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
467 468

	/* An existing reference... */
469
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
470 471 472 473 474

	/* Can not be renamed to the name of another existing reference. */
	must_fail(git_reference_rename(looked_up_ref, packed_test_head_name));

	/* Failure to rename it hasn't corrupted its state */
475
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
476 477
	must_be_true(!strcmp(looked_up_ref->name, packed_head_name));

478
	close_temp_repo(repo);
479 480
END_TEST

481
BEGIN_TEST(rename4, "can not rename a reference with an invalid name")
482 483 484
	git_reference *looked_up_ref;
	git_repository *repo;

485
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
486 487

	/* An existing oid reference... */
488
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
489 490 491 492 493 494 495 496

	/* Can not be renamed with an invalid name. */
	must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name."));

	/* Can not be renamed outside of the refs hierarchy. */
	must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you"));

	/* Failure to rename it hasn't corrupted its state */
497
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
498 499
	must_be_true(!strcmp(looked_up_ref->name, packed_test_head_name));

500
	close_temp_repo(repo);
501 502
END_TEST

503
BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove both tracks in the filesystem")
nulltoken committed
504 505 506 507
	git_reference *looked_up_ref, *another_looked_up_ref;
	git_repository *repo;
	char temp_path[GIT_PATH_MAX];

508
	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
nulltoken committed
509 510 511 512 513 514

	/* Ensure the loose reference exists on the file system */
	git__joinpath(temp_path, repo->path_repository, packed_test_head_name);
	must_pass(gitfo_exists(temp_path));

	/* Lookup the reference */
515
	must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
nulltoken committed
516 517 518 519 520 521 522 523

	/* Ensure it's the loose version that has been found */
	must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0);

	/* Now that the reference is deleted... */
	must_pass(git_reference_delete(looked_up_ref));

	/* Looking up the reference once again should not retrieve it */
524
	must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
nulltoken committed
525 526 527 528

	/* Ensure the loose reference doesn't exist any longer on the file system */
	must_pass(!gitfo_exists(temp_path));

529
	close_temp_repo(repo);
nulltoken committed
530 531
END_TEST

532
static int ensure_refname_normalized(int is_oid_ref, const char *input_refname, const char *expected_refname)
533 534 535 536
{
	int error = GIT_SUCCESS;
	char buffer_out[GIT_PATH_MAX];

537 538 539 540 541
	if (is_oid_ref)
		error = git_reference__normalize_name_oid(buffer_out, input_refname);
	else
		error = git_reference__normalize_name(buffer_out, input_refname);

542 543 544 545 546 547 548 549 550 551 552 553
	if (error < GIT_SUCCESS)
		return error;

	if (expected_refname == NULL)
		return error;

	if (strcmp(buffer_out, expected_refname))
		error = GIT_ERROR;

	return error;
}

554 555 556
#define OID_REF 1
#define SYM_REF 0

557
BEGIN_TEST(normalize0, "normalize a direct (OID) reference name")
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
	must_fail(ensure_refname_normalized(OID_REF, "a", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a/", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.lock", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/dummy/a", NULL));
	must_pass(ensure_refname_normalized(OID_REF, "refs/tags/a", "refs/tags/a"));
	must_pass(ensure_refname_normalized(OID_REF, "refs/heads/a/b", "refs/heads/a/b"));
	must_pass(ensure_refname_normalized(OID_REF, "refs/heads/a./b", "refs/heads/a./b"));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo?bar", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads\foo", NULL));
	must_pass(ensure_refname_normalized(OID_REF, "refs/heads/v@ation", "refs/heads/v@ation"));
	must_pass(ensure_refname_normalized(OID_REF, "refs///heads///a", "refs/heads/a"));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/.a/b", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo/../bar", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo..bar", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/./foo", NULL));
	must_fail(ensure_refname_normalized(OID_REF, "refs/heads/v@{ation", NULL));
576 577
END_TEST

578
BEGIN_TEST(normalize1, "normalize a symbolic reference name")
579 580 581 582 583
	must_pass(ensure_refname_normalized(SYM_REF, "a", "a"));
	must_pass(ensure_refname_normalized(SYM_REF, "a/b", "a/b"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs///heads///a", "refs/heads/a"));
	must_fail(ensure_refname_normalized(SYM_REF, "", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "heads\foo", NULL));
584 585
END_TEST

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
/* Ported from JGit, BSD licence.
 * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */
BEGIN_TEST(normalize2, "tests borrowed from JGit")

/* EmptyString */
	must_fail(ensure_refname_normalized(SYM_REF, "", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "/", NULL));

/* MustHaveTwoComponents */
	must_fail(ensure_refname_normalized(OID_REF, "master", NULL));
	must_pass(ensure_refname_normalized(SYM_REF, "heads/master", "heads/master"));

/* ValidHead */

	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/master", "refs/heads/master"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/pu", "refs/heads/pu"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/z", "refs/heads/z"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/FoO", "refs/heads/FoO"));

/* ValidTag */
	must_pass(ensure_refname_normalized(SYM_REF, "refs/tags/v1.0", "refs/tags/v1.0"));

/* NoLockSuffix */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master.lock", NULL));

/* NoDirectorySuffix */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master/", NULL));

/* NoSpace */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/i haz space", NULL));

/* NoAsciiControlCharacters */
	{
		char c;
		char buffer[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH];
		for (c = '\1'; c < ' '; c++) {
			strncpy(buffer, "refs/heads/mast", 15);
			strncpy(buffer + 15, (const char *)&c, 1);
			strncpy(buffer + 16, "er", 2);
			buffer[18 - 1] = '\0';
			must_fail(ensure_refname_normalized(SYM_REF, buffer, NULL));
		}
	}

/* NoBareDot */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/./master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/../master", NULL));

/* NoLeadingOrTrailingDot */
	must_fail(ensure_refname_normalized(SYM_REF, ".", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.bar", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..bar", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/bar.", NULL));

/* ContainsDot */
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r"));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master..pu", NULL));

/* NoMagicRefCharacters */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master^", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/^master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "^refs/heads/master", NULL));

	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master~", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/~master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "~refs/heads/master", NULL));

	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master:", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/:master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, ":refs/heads/master", NULL));

/* ShellGlob */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master?", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/?master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "?refs/heads/master", NULL));

	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master[", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/[master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "[refs/heads/master", NULL));

	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master*", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/*master", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "*refs/heads/master", NULL));

/* ValidSpecialCharacters */
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/!", "refs/heads/!"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\"", "refs/heads/\""));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/#", "refs/heads/#"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/$", "refs/heads/$"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/%", "refs/heads/%"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/&", "refs/heads/&"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/'", "refs/heads/'"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/(", "refs/heads/("));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/)", "refs/heads/)"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/+", "refs/heads/+"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/,", "refs/heads/,"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/-", "refs/heads/-"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/;", "refs/heads/;"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/<", "refs/heads/<"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/=", "refs/heads/="));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/>", "refs/heads/>"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/@", "refs/heads/@"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/]", "refs/heads/]"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/_", "refs/heads/_"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/`", "refs/heads/`"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/{", "refs/heads/{"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/|", "refs/heads/|"));
	must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/}", "refs/heads/}"));

	// This is valid on UNIX, but not on Windows
	// hence we make in invalid due to non-portability
	//
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/\\", NULL));

/* UnicodeNames */
	/*
	 * Currently this fails.
	 * must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m"));
	 */

/* RefLogQueryIsValidRef */
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1}", NULL));
	must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1.hour.ago}", NULL));
711 712
END_TEST

713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

BEGIN_SUITE(refs)
	ADD_TEST(readtag0);
	ADD_TEST(readtag1);

	ADD_TEST(readsym0);
	ADD_TEST(readsym1);
	ADD_TEST(readsym2);
	ADD_TEST(readsym3);

	ADD_TEST(readpacked0);
	ADD_TEST(readpacked1);

	ADD_TEST(create0);
	ADD_TEST(create1);
	ADD_TEST(create2);

	ADD_TEST(normalize0);
	ADD_TEST(normalize1);
	ADD_TEST(normalize2);

	ADD_TEST(pack0);
	ADD_TEST(pack1);

	ADD_TEST(rename0);
	ADD_TEST(rename1);
	ADD_TEST(rename2);
	ADD_TEST(rename3);
	ADD_TEST(rename4);

	ADD_TEST(delete0);
END_SUITE