PageRenderTime 22ms CodeModel.GetById 11ms app.highlight 113ms RepoModel.GetById 1ms app.codeStats 1ms

/src/refdb_fs.c

http://github.com/libgit2/libgit2
C | 2196 lines | 1536 code | 465 blank | 195 comment | 412 complexity | 7d6548a63f9b791a35ef48bc6225a580 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) the libgit2 contributors. All rights reserved.
   3 *
   4 * This file is part of libgit2, distributed under the GNU GPL v2 with
   5 * a Linking Exception. For full terms see the included COPYING file.
   6 */
   7
   8#include "refs.h"
   9#include "hash.h"
  10#include "repository.h"
  11#include "futils.h"
  12#include "filebuf.h"
  13#include "pack.h"
  14#include "parse.h"
  15#include "reflog.h"
  16#include "refdb.h"
  17#include "iterator.h"
  18#include "sortedcache.h"
  19#include "signature.h"
  20#include "wildmatch.h"
  21
  22#include <git2/tag.h>
  23#include <git2/object.h>
  24#include <git2/refdb.h>
  25#include <git2/branch.h>
  26#include <git2/sys/refdb_backend.h>
  27#include <git2/sys/refs.h>
  28#include <git2/sys/reflog.h>
  29
  30#define DEFAULT_NESTING_LEVEL	5
  31#define MAX_NESTING_LEVEL		10
  32
  33enum {
  34	PACKREF_HAS_PEEL = 1,
  35	PACKREF_WAS_LOOSE = 2,
  36	PACKREF_CANNOT_PEEL = 4,
  37	PACKREF_SHADOWED = 8,
  38};
  39
  40enum {
  41	PEELING_NONE = 0,
  42	PEELING_STANDARD,
  43	PEELING_FULL
  44};
  45
  46struct packref {
  47	git_oid oid;
  48	git_oid peel;
  49	char flags;
  50	char name[GIT_FLEX_ARRAY];
  51};
  52
  53typedef struct refdb_fs_backend {
  54	git_refdb_backend parent;
  55
  56	git_repository *repo;
  57	/* path to git directory */
  58	char *gitpath;
  59	/* path to common objects' directory */
  60	char *commonpath;
  61
  62	git_sortedcache *refcache;
  63	int peeling_mode;
  64	git_iterator_flag_t iterator_flags;
  65	uint32_t direach_flags;
  66	int fsync;
  67} refdb_fs_backend;
  68
  69static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
  70
  71static int packref_cmp(const void *a_, const void *b_)
  72{
  73	const struct packref *a = a_, *b = b_;
  74	return strcmp(a->name, b->name);
  75}
  76
  77static int packed_reload(refdb_fs_backend *backend)
  78{
  79	int error;
  80	git_buf packedrefs = GIT_BUF_INIT;
  81	char *scan, *eof, *eol;
  82
  83	if (!backend->gitpath)
  84		return 0;
  85
  86	error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
  87
  88	/*
  89	 * If we can't find the packed-refs, clear table and return.
  90	 * Any other error just gets passed through.
  91	 * If no error, and file wasn't changed, just return.
  92	 * Anything else means we need to refresh the packed refs.
  93	 */
  94	if (error <= 0) {
  95		if (error == GIT_ENOTFOUND) {
  96			git_sortedcache_clear(backend->refcache, true);
  97			git_error_clear();
  98			error = 0;
  99		}
 100		return error;
 101	}
 102
 103	/* At this point, refresh the packed refs from the loaded buffer. */
 104
 105	git_sortedcache_clear(backend->refcache, false);
 106
 107	scan = (char *)packedrefs.ptr;
 108	eof  = scan + packedrefs.size;
 109
 110	backend->peeling_mode = PEELING_NONE;
 111
 112	if (*scan == '#') {
 113		static const char *traits_header = "# pack-refs with: ";
 114
 115		if (git__prefixcmp(scan, traits_header) == 0) {
 116			scan += strlen(traits_header);
 117			eol = strchr(scan, '\n');
 118
 119			if (!eol)
 120				goto parse_failed;
 121			*eol = '\0';
 122
 123			if (strstr(scan, " fully-peeled ") != NULL) {
 124				backend->peeling_mode = PEELING_FULL;
 125			} else if (strstr(scan, " peeled ") != NULL) {
 126				backend->peeling_mode = PEELING_STANDARD;
 127			}
 128
 129			scan = eol + 1;
 130		}
 131	}
 132
 133	while (scan < eof && *scan == '#') {
 134		if (!(eol = strchr(scan, '\n')))
 135			goto parse_failed;
 136		scan = eol + 1;
 137	}
 138
 139	while (scan < eof) {
 140		struct packref *ref;
 141		git_oid oid;
 142
 143		/* parse "<OID> <refname>\n" */
 144
 145		if (git_oid_fromstr(&oid, scan) < 0)
 146			goto parse_failed;
 147		scan += GIT_OID_HEXSZ;
 148
 149		if (*scan++ != ' ')
 150			goto parse_failed;
 151		if (!(eol = strchr(scan, '\n')))
 152			goto parse_failed;
 153		*eol = '\0';
 154		if (eol[-1] == '\r')
 155			eol[-1] = '\0';
 156
 157		if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
 158			goto parse_failed;
 159		scan = eol + 1;
 160
 161		git_oid_cpy(&ref->oid, &oid);
 162
 163		/* look for optional "^<OID>\n" */
 164
 165		if (*scan == '^') {
 166			if (git_oid_fromstr(&oid, scan + 1) < 0)
 167				goto parse_failed;
 168			scan += GIT_OID_HEXSZ + 1;
 169
 170			if (scan < eof) {
 171				if (!(eol = strchr(scan, '\n')))
 172					goto parse_failed;
 173				scan = eol + 1;
 174			}
 175
 176			git_oid_cpy(&ref->peel, &oid);
 177			ref->flags |= PACKREF_HAS_PEEL;
 178		}
 179		else if (backend->peeling_mode == PEELING_FULL ||
 180				(backend->peeling_mode == PEELING_STANDARD &&
 181				 git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
 182			ref->flags |= PACKREF_CANNOT_PEEL;
 183	}
 184
 185	git_sortedcache_wunlock(backend->refcache);
 186	git_buf_dispose(&packedrefs);
 187
 188	return 0;
 189
 190parse_failed:
 191	git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
 192
 193	git_sortedcache_clear(backend->refcache, false);
 194	git_sortedcache_wunlock(backend->refcache);
 195	git_buf_dispose(&packedrefs);
 196
 197	return -1;
 198}
 199
 200static int loose_parse_oid(
 201	git_oid *oid, const char *filename, git_buf *file_content)
 202{
 203	const char *str = git_buf_cstr(file_content);
 204
 205	if (git_buf_len(file_content) < GIT_OID_HEXSZ)
 206		goto corrupted;
 207
 208	/* we need to get 40 OID characters from the file */
 209	if (git_oid_fromstr(oid, str) < 0)
 210		goto corrupted;
 211
 212	/* If the file is longer than 40 chars, the 41st must be a space */
 213	str += GIT_OID_HEXSZ;
 214	if (*str == '\0' || git__isspace(*str))
 215		return 0;
 216
 217corrupted:
 218	git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file: %s", filename);
 219	return -1;
 220}
 221
 222static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
 223{
 224	int error;
 225
 226	/* build full path to file */
 227	if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
 228		(error = git_futils_readbuffer(buf, buf->ptr)) < 0)
 229		git_buf_dispose(buf);
 230
 231	return error;
 232}
 233
 234static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
 235{
 236	int error = 0;
 237	git_buf ref_file = GIT_BUF_INIT;
 238	struct packref *ref = NULL;
 239	git_oid oid;
 240
 241	/* if we fail to load the loose reference, assume someone changed
 242	 * the filesystem under us and skip it...
 243	 */
 244	if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) {
 245		git_error_clear();
 246		goto done;
 247	}
 248
 249	/* skip symbolic refs */
 250	if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
 251		goto done;
 252
 253	/* parse OID from file */
 254	if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
 255		goto done;
 256
 257	git_sortedcache_wlock(backend->refcache);
 258
 259	if (!(error = git_sortedcache_upsert(
 260			(void **)&ref, backend->refcache, name))) {
 261
 262		git_oid_cpy(&ref->oid, &oid);
 263		ref->flags = PACKREF_WAS_LOOSE;
 264	}
 265
 266	git_sortedcache_wunlock(backend->refcache);
 267
 268done:
 269	git_buf_dispose(&ref_file);
 270	return error;
 271}
 272
 273static int _dirent_loose_load(void *payload, git_buf *full_path)
 274{
 275	refdb_fs_backend *backend = payload;
 276	const char *file_path;
 277
 278	if (git__suffixcmp(full_path->ptr, ".lock") == 0)
 279		return 0;
 280
 281	if (git_path_isdir(full_path->ptr)) {
 282		int error = git_path_direach(
 283			full_path, backend->direach_flags, _dirent_loose_load, backend);
 284		/* Race with the filesystem, ignore it */
 285		if (error == GIT_ENOTFOUND) {
 286			git_error_clear();
 287			return 0;
 288		}
 289
 290		return error;
 291	}
 292
 293	file_path = full_path->ptr + strlen(backend->gitpath);
 294
 295	return loose_lookup_to_packfile(backend, file_path);
 296}
 297
 298/*
 299 * Load all the loose references from the repository
 300 * into the in-memory Packfile, and build a vector with
 301 * all the references so it can be written back to
 302 * disk.
 303 */
 304static int packed_loadloose(refdb_fs_backend *backend)
 305{
 306	int error;
 307	git_buf refs_path = GIT_BUF_INIT;
 308
 309	if (git_buf_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0)
 310		return -1;
 311
 312	/*
 313	 * Load all the loose files from disk into the Packfile table.
 314	 * This will overwrite any old packed entries with their
 315	 * updated loose versions
 316	 */
 317	error = git_path_direach(
 318		&refs_path, backend->direach_flags, _dirent_loose_load, backend);
 319
 320	git_buf_dispose(&refs_path);
 321
 322	return error;
 323}
 324
 325static int refdb_fs_backend__exists(
 326	int *exists,
 327	git_refdb_backend *_backend,
 328	const char *ref_name)
 329{
 330	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 331	git_buf ref_path = GIT_BUF_INIT;
 332	int error;
 333
 334	assert(backend);
 335
 336	*exists = 0;
 337
 338	if ((error = git_buf_joinpath(&ref_path, backend->gitpath, ref_name)) < 0)
 339		goto out;
 340
 341	if (git_path_isfile(ref_path.ptr)) {
 342		*exists = 1;
 343		goto out;
 344	}
 345
 346	if ((error = packed_reload(backend)) < 0)
 347		goto out;
 348
 349	if (git_sortedcache_lookup(backend->refcache, ref_name) != NULL) {
 350		*exists = 1;
 351		goto out;
 352	}
 353
 354out:
 355	git_buf_dispose(&ref_path);
 356	return error;
 357}
 358
 359static const char *loose_parse_symbolic(git_buf *file_content)
 360{
 361	const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
 362	const char *refname_start;
 363
 364	refname_start = (const char *)file_content->ptr;
 365
 366	if (git_buf_len(file_content) < header_len + 1) {
 367		git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file");
 368		return NULL;
 369	}
 370
 371	/*
 372	 * Assume we have already checked for the header
 373	 * before calling this function
 374	 */
 375	refname_start += header_len;
 376
 377	return refname_start;
 378}
 379
 380/*
 381 * Returns whether a reference is stored per worktree or not.
 382 * Per-worktree references are:
 383 *
 384 * - all pseudorefs, e.g. HEAD and MERGE_HEAD
 385 * - all references stored inside of "refs/bisect/"
 386 */
 387static bool is_per_worktree_ref(const char *ref_name)
 388{
 389	return git__prefixcmp(ref_name, "refs/") != 0 ||
 390	    git__prefixcmp(ref_name, "refs/bisect/") == 0;
 391}
 392
 393static int loose_lookup(
 394	git_reference **out,
 395	refdb_fs_backend *backend,
 396	const char *ref_name)
 397{
 398	git_buf ref_file = GIT_BUF_INIT;
 399	int error = 0;
 400	const char *ref_dir;
 401
 402	if (out)
 403		*out = NULL;
 404
 405	if (is_per_worktree_ref(ref_name))
 406		ref_dir = backend->gitpath;
 407	else
 408		ref_dir = backend->commonpath;
 409
 410	if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0)
 411		/* cannot read loose ref file - gah */;
 412	else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
 413		const char *target;
 414
 415		git_buf_rtrim(&ref_file);
 416
 417		if (!(target = loose_parse_symbolic(&ref_file)))
 418			error = -1;
 419		else if (out != NULL)
 420			*out = git_reference__alloc_symbolic(ref_name, target);
 421	} else {
 422		git_oid oid;
 423
 424		if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
 425			out != NULL)
 426			*out = git_reference__alloc(ref_name, &oid, NULL);
 427	}
 428
 429	git_buf_dispose(&ref_file);
 430	return error;
 431}
 432
 433static int ref_error_notfound(const char *name)
 434{
 435	git_error_set(GIT_ERROR_REFERENCE, "reference '%s' not found", name);
 436	return GIT_ENOTFOUND;
 437}
 438
 439static int packed_lookup(
 440	git_reference **out,
 441	refdb_fs_backend *backend,
 442	const char *ref_name)
 443{
 444	int error = 0;
 445	struct packref *entry;
 446
 447	if ((error = packed_reload(backend)) < 0)
 448		return error;
 449
 450	if (git_sortedcache_rlock(backend->refcache) < 0)
 451		return -1;
 452
 453	entry = git_sortedcache_lookup(backend->refcache, ref_name);
 454	if (!entry) {
 455		error = ref_error_notfound(ref_name);
 456	} else {
 457		*out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
 458		if (!*out)
 459			error = -1;
 460	}
 461
 462	git_sortedcache_runlock(backend->refcache);
 463
 464	return error;
 465}
 466
 467static int refdb_fs_backend__lookup(
 468	git_reference **out,
 469	git_refdb_backend *_backend,
 470	const char *ref_name)
 471{
 472	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 473	int error;
 474
 475	assert(backend);
 476
 477	if (!(error = loose_lookup(out, backend, ref_name)))
 478		return 0;
 479
 480	/* only try to lookup this reference on the packfile if it
 481	 * wasn't found on the loose refs; not if there was a critical error */
 482	if (error == GIT_ENOTFOUND) {
 483		git_error_clear();
 484		error = packed_lookup(out, backend, ref_name);
 485	}
 486
 487	return error;
 488}
 489
 490typedef struct {
 491	git_reference_iterator parent;
 492
 493	char *glob;
 494
 495	git_pool pool;
 496	git_vector loose;
 497
 498	git_sortedcache *cache;
 499	size_t loose_pos;
 500	size_t packed_pos;
 501} refdb_fs_iter;
 502
 503static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
 504{
 505	refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
 506
 507	git_vector_free(&iter->loose);
 508	git_pool_clear(&iter->pool);
 509	git_sortedcache_free(iter->cache);
 510	git__free(iter);
 511}
 512
 513static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
 514{
 515	int error = 0;
 516	git_buf path = GIT_BUF_INIT;
 517	git_iterator *fsit = NULL;
 518	git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
 519	const git_index_entry *entry = NULL;
 520	const char *ref_prefix = GIT_REFS_DIR;
 521	size_t ref_prefix_len = strlen(ref_prefix);
 522
 523	if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
 524		return 0;
 525
 526	fsit_opts.flags = backend->iterator_flags;
 527
 528	if (iter->glob) {
 529		const char *last_sep = NULL;
 530		const char *pos;
 531		for (pos = iter->glob; *pos; ++pos) {
 532			switch (*pos) {
 533			case '?':
 534			case '*':
 535			case '[':
 536			case '\\':
 537				break;
 538			case '/':
 539				last_sep = pos;
 540				/* FALLTHROUGH */
 541			default:
 542				continue;
 543			}
 544			break;
 545		}
 546		if (last_sep) {
 547			ref_prefix = iter->glob;
 548			ref_prefix_len = (last_sep - ref_prefix) + 1;
 549		}
 550	}
 551
 552	if ((error = git_buf_printf(&path, "%s/", backend->commonpath)) < 0 ||
 553		(error = git_buf_put(&path, ref_prefix, ref_prefix_len)) < 0) {
 554		git_buf_dispose(&path);
 555		return error;
 556	}
 557
 558	if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
 559		git_buf_dispose(&path);
 560		return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
 561	}
 562
 563	error = git_buf_sets(&path, ref_prefix);
 564
 565	while (!error && !git_iterator_advance(&entry, fsit)) {
 566		const char *ref_name;
 567		char *ref_dup;
 568
 569		git_buf_truncate(&path, ref_prefix_len);
 570		git_buf_puts(&path, entry->path);
 571		ref_name = git_buf_cstr(&path);
 572
 573		if (git__suffixcmp(ref_name, ".lock") == 0 ||
 574			(iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
 575			continue;
 576
 577		ref_dup = git_pool_strdup(&iter->pool, ref_name);
 578		if (!ref_dup)
 579			error = -1;
 580		else
 581			error = git_vector_insert(&iter->loose, ref_dup);
 582	}
 583
 584	git_iterator_free(fsit);
 585	git_buf_dispose(&path);
 586
 587	return error;
 588}
 589
 590static int refdb_fs_backend__iterator_next(
 591	git_reference **out, git_reference_iterator *_iter)
 592{
 593	int error = GIT_ITEROVER;
 594	refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
 595	refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
 596	struct packref *ref;
 597
 598	while (iter->loose_pos < iter->loose.length) {
 599		const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
 600
 601		if (loose_lookup(out, backend, path) == 0) {
 602			ref = git_sortedcache_lookup(iter->cache, path);
 603			if (ref)
 604				ref->flags |= PACKREF_SHADOWED;
 605
 606			return 0;
 607		}
 608
 609		git_error_clear();
 610	}
 611
 612	error = GIT_ITEROVER;
 613	while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
 614		ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
 615		if (!ref) /* stop now if another thread deleted refs and we past end */
 616			break;
 617
 618		if (ref->flags & PACKREF_SHADOWED)
 619			continue;
 620		if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
 621			continue;
 622
 623		*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
 624		error = (*out != NULL) ? 0 : -1;
 625		break;
 626	}
 627
 628	return error;
 629}
 630
 631static int refdb_fs_backend__iterator_next_name(
 632	const char **out, git_reference_iterator *_iter)
 633{
 634	int error = GIT_ITEROVER;
 635	refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
 636	refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
 637	struct packref *ref;
 638
 639	while (iter->loose_pos < iter->loose.length) {
 640		const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
 641		struct packref *ref;
 642
 643		if (loose_lookup(NULL, backend, path) == 0) {
 644			ref = git_sortedcache_lookup(iter->cache, path);
 645			if (ref)
 646				ref->flags |= PACKREF_SHADOWED;
 647
 648			*out = path;
 649			return 0;
 650		}
 651
 652		git_error_clear();
 653	}
 654
 655	error = GIT_ITEROVER;
 656	while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
 657		ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
 658		if (!ref) /* stop now if another thread deleted refs and we past end */
 659			break;
 660
 661		if (ref->flags & PACKREF_SHADOWED)
 662			continue;
 663		if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
 664			continue;
 665
 666		*out = ref->name;
 667		error = 0;
 668		break;
 669	}
 670
 671	return error;
 672}
 673
 674static int refdb_fs_backend__iterator(
 675	git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
 676{
 677	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 678	refdb_fs_iter *iter = NULL;
 679	int error;
 680
 681	assert(backend);
 682
 683	iter = git__calloc(1, sizeof(refdb_fs_iter));
 684	GIT_ERROR_CHECK_ALLOC(iter);
 685
 686	git_pool_init(&iter->pool, 1);
 687
 688	if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0)
 689		goto out;
 690
 691	if (glob != NULL &&
 692	    (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) {
 693		error = GIT_ERROR_NOMEMORY;
 694		goto out;
 695	}
 696
 697	if ((error = iter_load_loose_paths(backend, iter)) < 0)
 698		goto out;
 699
 700	if ((error = packed_reload(backend)) < 0)
 701		goto out;
 702
 703	if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
 704		goto out;
 705
 706	iter->parent.next = refdb_fs_backend__iterator_next;
 707	iter->parent.next_name = refdb_fs_backend__iterator_next_name;
 708	iter->parent.free = refdb_fs_backend__iterator_free;
 709
 710	*out = (git_reference_iterator *)iter;
 711out:
 712	if (error)
 713		refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
 714	return error;
 715}
 716
 717static bool ref_is_available(
 718	const char *old_ref, const char *new_ref, const char *this_ref)
 719{
 720	if (old_ref == NULL || strcmp(old_ref, this_ref)) {
 721		size_t reflen = strlen(this_ref);
 722		size_t newlen = strlen(new_ref);
 723		size_t cmplen = reflen < newlen ? reflen : newlen;
 724		const char *lead = reflen < newlen ? new_ref : this_ref;
 725
 726		if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
 727			return false;
 728		}
 729	}
 730
 731	return true;
 732}
 733
 734static int reference_path_available(
 735	refdb_fs_backend *backend,
 736	const char *new_ref,
 737	const char* old_ref,
 738	int force)
 739{
 740	size_t i;
 741	int error;
 742
 743	if ((error = packed_reload(backend)) < 0)
 744		return error;
 745
 746	if (!force) {
 747		int exists;
 748
 749		if ((error = refdb_fs_backend__exists(
 750			&exists, (git_refdb_backend *)backend, new_ref)) < 0) {
 751			return error;
 752		}
 753
 754		if (exists) {
 755			git_error_set(GIT_ERROR_REFERENCE,
 756				"failed to write reference '%s': a reference with "
 757				"that name already exists.", new_ref);
 758			return GIT_EEXISTS;
 759		}
 760	}
 761
 762	git_sortedcache_rlock(backend->refcache);
 763
 764	for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
 765		struct packref *ref = git_sortedcache_entry(backend->refcache, i);
 766
 767		if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
 768			git_sortedcache_runlock(backend->refcache);
 769			git_error_set(GIT_ERROR_REFERENCE,
 770				"path to reference '%s' collides with existing one", new_ref);
 771			return -1;
 772		}
 773	}
 774
 775	git_sortedcache_runlock(backend->refcache);
 776	return 0;
 777}
 778
 779static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
 780{
 781	int error, filebuf_flags;
 782	git_buf ref_path = GIT_BUF_INIT;
 783	const char *basedir;
 784
 785	assert(file && backend && name);
 786
 787	if (!git_path_isvalid(backend->repo, name, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
 788		git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name);
 789		return GIT_EINVALIDSPEC;
 790	}
 791
 792	if (is_per_worktree_ref(name))
 793		basedir = backend->gitpath;
 794	else
 795		basedir = backend->commonpath;
 796
 797	/* Remove a possibly existing empty directory hierarchy
 798	 * which name would collide with the reference name
 799	 */
 800	if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
 801		return error;
 802
 803	if (git_buf_joinpath(&ref_path, basedir, name) < 0)
 804		return -1;
 805
 806	filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
 807	if (backend->fsync)
 808		filebuf_flags |= GIT_FILEBUF_FSYNC;
 809
 810	error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE);
 811
 812	if (error == GIT_EDIRECTORY)
 813		git_error_set(GIT_ERROR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
 814
 815	git_buf_dispose(&ref_path);
 816	return error;
 817}
 818
 819static int loose_commit(git_filebuf *file, const git_reference *ref)
 820{
 821	assert(file && ref);
 822
 823	if (ref->type == GIT_REFERENCE_DIRECT) {
 824		char oid[GIT_OID_HEXSZ + 1];
 825		git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
 826
 827		git_filebuf_printf(file, "%s\n", oid);
 828	} else if (ref->type == GIT_REFERENCE_SYMBOLIC) {
 829		git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
 830	} else {
 831		assert(0); /* don't let this happen */
 832	}
 833
 834	return git_filebuf_commit(file);
 835}
 836
 837static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
 838{
 839	int error;
 840	git_filebuf *lock;
 841	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
 842
 843	lock = git__calloc(1, sizeof(git_filebuf));
 844	GIT_ERROR_CHECK_ALLOC(lock);
 845
 846	if ((error = loose_lock(lock, backend, refname)) < 0) {
 847		git__free(lock);
 848		return error;
 849	}
 850
 851	*out = lock;
 852	return 0;
 853}
 854
 855static int refdb_fs_backend__write_tail(
 856	git_refdb_backend *_backend,
 857	const git_reference *ref,
 858	git_filebuf *file,
 859	int update_reflog,
 860	const git_oid *old_id,
 861	const char *old_target,
 862	const git_signature *who,
 863	const char *message);
 864
 865static int refdb_fs_backend__delete_tail(
 866	git_refdb_backend *_backend,
 867	git_filebuf *file,
 868	const char *ref_name,
 869	const git_oid *old_id,
 870	const char *old_target);
 871
 872static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
 873				    const git_reference *ref, const git_signature *sig, const char *message)
 874{
 875	git_filebuf *lock = (git_filebuf *) payload;
 876	int error = 0;
 877
 878	if (success == 2)
 879		error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
 880	else if (success)
 881		error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message);
 882	else
 883		git_filebuf_cleanup(lock);
 884
 885	git__free(lock);
 886	return error;
 887}
 888
 889/*
 890 * Find out what object this reference resolves to.
 891 *
 892 * For references that point to a 'big' tag (e.g. an
 893 * actual tag object on the repository), we need to
 894 * cache on the packfile the OID of the object to
 895 * which that 'big tag' is pointing to.
 896 */
 897static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
 898{
 899	git_object *object;
 900
 901	if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
 902		return 0;
 903
 904	/*
 905	 * Find the tagged object in the repository
 906	 */
 907	if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJECT_ANY) < 0)
 908		return -1;
 909
 910	/*
 911	 * If the tagged object is a Tag object, we need to resolve it;
 912	 * if the ref is actually a 'weak' ref, we don't need to resolve
 913	 * anything.
 914	 */
 915	if (git_object_type(object) == GIT_OBJECT_TAG) {
 916		git_tag *tag = (git_tag *)object;
 917
 918		/*
 919		 * Find the object pointed at by this tag
 920		 */
 921		git_oid_cpy(&ref->peel, git_tag_target_id(tag));
 922		ref->flags |= PACKREF_HAS_PEEL;
 923
 924		/*
 925		 * The reference has now cached the resolved OID, and is
 926		 * marked at such. When written to the packfile, it'll be
 927		 * accompanied by this resolved oid
 928		 */
 929	}
 930
 931	git_object_free(object);
 932	return 0;
 933}
 934
 935/*
 936 * Write a single reference into a packfile
 937 */
 938static int packed_write_ref(struct packref *ref, git_filebuf *file)
 939{
 940	char oid[GIT_OID_HEXSZ + 1];
 941	git_oid_nfmt(oid, sizeof(oid), &ref->oid);
 942
 943	/*
 944	 * For references that peel to an object in the repo, we must
 945	 * write the resulting peel on a separate line, e.g.
 946	 *
 947	 *	6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
 948	 *	^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
 949	 *
 950	 * This obviously only applies to tags.
 951	 * The required peels have already been loaded into `ref->peel_target`.
 952	 */
 953	if (ref->flags & PACKREF_HAS_PEEL) {
 954		char peel[GIT_OID_HEXSZ + 1];
 955		git_oid_nfmt(peel, sizeof(peel), &ref->peel);
 956
 957		if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
 958			return -1;
 959	} else {
 960		if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
 961			return -1;
 962	}
 963
 964	return 0;
 965}
 966
 967/*
 968 * Remove all loose references
 969 *
 970 * Once we have successfully written a packfile,
 971 * all the loose references that were packed must be
 972 * removed from disk.
 973 *
 974 * This is a dangerous method; make sure the packfile
 975 * is well-written, because we are destructing references
 976 * here otherwise.
 977 */
 978static int packed_remove_loose(refdb_fs_backend *backend)
 979{
 980	size_t i;
 981	git_filebuf lock = GIT_FILEBUF_INIT;
 982	git_buf ref_content = GIT_BUF_INIT;
 983	int error = 0;
 984
 985	/* backend->refcache is already locked when this is called */
 986
 987	for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
 988		struct packref *ref = git_sortedcache_entry(backend->refcache, i);
 989		git_oid current_id;
 990
 991		if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
 992			continue;
 993
 994		git_filebuf_cleanup(&lock);
 995
 996		/* We need to stop anybody from updating the ref while we try to do a safe delete */
 997		error = loose_lock(&lock, backend, ref->name);
 998		/* If someone else is updating it, let them do it */
 999		if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
1000			continue;
1001
1002		if (error < 0) {
1003			git_buf_dispose(&ref_content);
1004			git_error_set(GIT_ERROR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
1005			return error;
1006		}
1007
1008		error = git_futils_readbuffer(&ref_content, lock.path_original);
1009		/* Someone else beat us to cleaning up the ref, let's simply continue */
1010		if (error == GIT_ENOTFOUND)
1011			continue;
1012
1013		/* This became a symref between us packing and trying to delete it, so ignore it */
1014		if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
1015			continue;
1016
1017		/* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
1018		if (loose_parse_oid(&current_id, lock.path_original, &ref_content) < 0)
1019			continue;
1020
1021		/* If the ref moved since we packed it, we must not delete it */
1022		if (!git_oid_equal(&current_id, &ref->oid))
1023			continue;
1024
1025		/*
1026		 * if we fail to remove a single file, this is *not* good,
1027		 * but we should keep going and remove as many as possible.
1028		 * If we fail to remove, the ref is still in the old state, so
1029		 * we haven't lost information.
1030		 */
1031		p_unlink(lock.path_original);
1032	}
1033
1034	git_buf_dispose(&ref_content);
1035	git_filebuf_cleanup(&lock);
1036	return 0;
1037}
1038
1039/*
1040 * Write all the contents in the in-memory packfile to disk.
1041 */
1042static int packed_write(refdb_fs_backend *backend)
1043{
1044	git_sortedcache *refcache = backend->refcache;
1045	git_filebuf pack_file = GIT_FILEBUF_INIT;
1046	int error, open_flags = 0;
1047	size_t i;
1048
1049	/* lock the cache to updates while we do this */
1050	if ((error = git_sortedcache_wlock(refcache)) < 0)
1051		return error;
1052
1053	if (backend->fsync)
1054		open_flags = GIT_FILEBUF_FSYNC;
1055
1056	/* Open the file! */
1057	if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0)
1058		goto fail;
1059
1060	/* Packfiles have a header... apparently
1061	 * This is in fact not required, but we might as well print it
1062	 * just for kicks */
1063	if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
1064		goto fail;
1065
1066	for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
1067		struct packref *ref = git_sortedcache_entry(refcache, i);
1068		assert(ref);
1069
1070		if ((error = packed_find_peel(backend, ref)) < 0)
1071			goto fail;
1072
1073		if ((error = packed_write_ref(ref, &pack_file)) < 0)
1074			goto fail;
1075	}
1076
1077	/* if we've written all the references properly, we can commit
1078	 * the packfile to make the changes effective */
1079	if ((error = git_filebuf_commit(&pack_file)) < 0)
1080		goto fail;
1081
1082	/* when and only when the packfile has been properly written,
1083	 * we can go ahead and remove the loose refs */
1084	if ((error = packed_remove_loose(backend)) < 0)
1085		goto fail;
1086
1087	git_sortedcache_updated(refcache);
1088	git_sortedcache_wunlock(refcache);
1089
1090	/* we're good now */
1091	return 0;
1092
1093fail:
1094	git_filebuf_cleanup(&pack_file);
1095	git_sortedcache_wunlock(refcache);
1096
1097	return error;
1098}
1099
1100static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
1101{
1102	size_t pack_pos;
1103	int error, found = 0;
1104
1105	if ((error = packed_reload(backend)) < 0)
1106		goto cleanup;
1107
1108	if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
1109		goto cleanup;
1110
1111	/* If a packed reference exists, remove it from the packfile and repack if necessary */
1112	error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name);
1113	if (error == 0) {
1114		error = git_sortedcache_remove(backend->refcache, pack_pos);
1115		found = 1;
1116	}
1117	if (error == GIT_ENOTFOUND)
1118		error = 0;
1119
1120	git_sortedcache_wunlock(backend->refcache);
1121
1122	if (found)
1123		error = packed_write(backend);
1124
1125cleanup:
1126	return error;
1127}
1128
1129static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
1130static int has_reflog(git_repository *repo, const char *name);
1131
1132static int should_write_reflog(int *write, git_repository *repo, const char *name)
1133{
1134	int error, logall;
1135
1136	error = git_repository__configmap_lookup(&logall, repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
1137	if (error < 0)
1138		return error;
1139
1140	/* Defaults to the opposite of the repo being bare */
1141	if (logall == GIT_LOGALLREFUPDATES_UNSET)
1142		logall = !git_repository_is_bare(repo);
1143
1144	*write = 0;
1145	switch (logall) {
1146	case GIT_LOGALLREFUPDATES_FALSE:
1147		*write = 0;
1148		break;
1149
1150	case GIT_LOGALLREFUPDATES_TRUE:
1151		/* Only write if it already has a log,
1152		 * or if it's under heads/, remotes/ or notes/
1153		 */
1154		*write = has_reflog(repo, name) ||
1155			!git__prefixcmp(name, GIT_REFS_HEADS_DIR) ||
1156			!git__strcmp(name, GIT_HEAD_FILE) ||
1157			!git__prefixcmp(name, GIT_REFS_REMOTES_DIR) ||
1158			!git__prefixcmp(name, GIT_REFS_NOTES_DIR);
1159		break;
1160
1161	case GIT_LOGALLREFUPDATES_ALWAYS:
1162		*write = 1;
1163		break;
1164	}
1165
1166	return 0;
1167}
1168
1169static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
1170	const git_oid *old_id, const char *old_target)
1171{
1172	int error = 0;
1173	git_reference *old_ref = NULL;
1174
1175	*cmp = 0;
1176	/* It "matches" if there is no old value to compare against */
1177	if (!old_id && !old_target)
1178		return 0;
1179
1180	if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
1181		goto out;
1182
1183	/* If the types don't match, there's no way the values do */
1184	if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
1185		*cmp = -1;
1186		goto out;
1187	}
1188	if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) {
1189		*cmp = 1;
1190		goto out;
1191	}
1192
1193	if (old_id && old_ref->type == GIT_REFERENCE_DIRECT)
1194		*cmp = git_oid_cmp(old_id, &old_ref->target.oid);
1195
1196	if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC)
1197		*cmp = git__strcmp(old_target, old_ref->target.symbolic);
1198
1199out:
1200	git_reference_free(old_ref);
1201
1202	return error;
1203}
1204
1205/*
1206 * The git.git comment regarding this, for your viewing pleasure:
1207 *
1208 * Special hack: If a branch is updated directly and HEAD
1209 * points to it (may happen on the remote side of a push
1210 * for example) then logically the HEAD reflog should be
1211 * updated too.
1212 * A generic solution implies reverse symref information,
1213 * but finding all symrefs pointing to the given branch
1214 * would be rather costly for this rare event (the direct
1215 * update of a branch) to be worth it.  So let's cheat and
1216 * check with HEAD only which should cover 99% of all usage
1217 * scenarios (even 100% of the default ones).
1218 */
1219static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
1220{
1221	int error;
1222	git_oid old_id;
1223	git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
1224	const char *name;
1225
1226	if (ref->type == GIT_REFERENCE_SYMBOLIC)
1227		return 0;
1228
1229	/* if we can't resolve, we use {0}*40 as old id */
1230	if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
1231		memset(&old_id, 0, sizeof(old_id));
1232
1233	if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
1234		return error;
1235
1236	if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
1237		goto cleanup;
1238
1239	if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0)
1240		goto cleanup;
1241
1242	/* Go down the symref chain until we find the branch */
1243	while (git_reference_type(tmp) == GIT_REFERENCE_SYMBOLIC) {
1244		error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp));
1245		if (error < 0)
1246			break;
1247
1248		git_reference_free(tmp);
1249		tmp = peeled;
1250	}
1251
1252	if (error == GIT_ENOTFOUND) {
1253		error = 0;
1254		name = git_reference_symbolic_target(tmp);
1255	} else if (error < 0) {
1256		goto cleanup;
1257	} else {
1258		name = git_reference_name(tmp);
1259	}
1260
1261	if (strcmp(name, ref->name))
1262		goto cleanup;
1263
1264	error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message);
1265
1266cleanup:
1267	git_reference_free(tmp);
1268	git_reference_free(head);
1269	return error;
1270}
1271
1272static int refdb_fs_backend__write(
1273	git_refdb_backend *_backend,
1274	const git_reference *ref,
1275	int force,
1276	const git_signature *who,
1277	const char *message,
1278	const git_oid *old_id,
1279	const char *old_target)
1280{
1281	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1282	git_filebuf file = GIT_FILEBUF_INIT;
1283	int error = 0;
1284
1285	assert(backend);
1286
1287	if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
1288		return error;
1289
1290	/* We need to perform the reflog append and old value check under the ref's lock */
1291	if ((error = loose_lock(&file, backend, ref->name)) < 0)
1292		return error;
1293
1294	return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
1295}
1296
1297static int refdb_fs_backend__write_tail(
1298	git_refdb_backend *_backend,
1299	const git_reference *ref,
1300	git_filebuf *file,
1301	int update_reflog,
1302	const git_oid *old_id,
1303	const char *old_target,
1304	const git_signature *who,
1305	const char *message)
1306{
1307	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1308	int error = 0, cmp = 0, should_write;
1309	const char *new_target = NULL;
1310	const git_oid *new_id = NULL;
1311
1312	if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
1313		goto on_error;
1314
1315	if (cmp) {
1316		git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1317		error = GIT_EMODIFIED;
1318		goto on_error;
1319	}
1320
1321	if (ref->type == GIT_REFERENCE_SYMBOLIC)
1322		new_target = ref->target.symbolic;
1323	else
1324		new_id = &ref->target.oid;
1325
1326	error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
1327	if (error < 0 && error != GIT_ENOTFOUND)
1328		goto on_error;
1329
1330	/* Don't update if we have the same value */
1331	if (!error && !cmp) {
1332		error = 0;
1333		goto on_error; /* not really error */
1334	}
1335
1336	if (update_reflog) {
1337		if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
1338			goto on_error;
1339
1340		if (should_write) {
1341			if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
1342				goto on_error;
1343			if ((error = maybe_append_head(backend, ref, who, message)) < 0)
1344				goto on_error;
1345		}
1346	}
1347
1348	return loose_commit(file, ref);
1349
1350on_error:
1351        git_filebuf_cleanup(file);
1352        return error;
1353}
1354
1355static void refdb_fs_backend__prune_refs(
1356	refdb_fs_backend *backend,
1357	const char *ref_name,
1358	const char *prefix)
1359{
1360	git_buf relative_path = GIT_BUF_INIT;
1361	git_buf base_path = GIT_BUF_INIT;
1362	size_t commonlen;
1363
1364	assert(backend && ref_name);
1365
1366	if (git_buf_sets(&relative_path, ref_name) < 0)
1367		goto cleanup;
1368
1369	git_path_squash_slashes(&relative_path);
1370	if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") ||
1371		(commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") ||
1372		(commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) {
1373
1374		git_buf_truncate(&relative_path, commonlen);
1375
1376		if (prefix) {
1377			if (git_buf_join3(&base_path, '/', backend->commonpath, prefix, git_buf_cstr(&relative_path)) < 0)
1378				goto cleanup;
1379		} else {
1380			if (git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)) < 0)
1381				goto cleanup;
1382		}
1383
1384		git_futils_rmdir_r(ref_name + commonlen, git_buf_cstr(&base_path), GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
1385	}
1386
1387cleanup:
1388	git_buf_dispose(&relative_path);
1389	git_buf_dispose(&base_path);
1390}
1391
1392static int refdb_fs_backend__delete(
1393	git_refdb_backend *_backend,
1394	const char *ref_name,
1395	const git_oid *old_id, const char *old_target)
1396{
1397	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1398	git_filebuf file = GIT_FILEBUF_INIT;
1399	int error = 0;
1400
1401	assert(backend && ref_name);
1402
1403	if ((error = loose_lock(&file, backend, ref_name)) < 0)
1404		return error;
1405
1406	if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
1407		git_filebuf_cleanup(&file);
1408		return error;
1409	}
1410
1411	return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
1412}
1413
1414static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
1415{
1416	git_buf loose_path = GIT_BUF_INIT;
1417	int error = 0;
1418
1419	if (git_buf_joinpath(&loose_path, backend->commonpath, ref_name) < 0)
1420		return -1;
1421
1422	error = p_unlink(loose_path.ptr);
1423	if (error < 0 && errno == ENOENT)
1424		error = GIT_ENOTFOUND;
1425	else if (error != 0)
1426		error = -1;
1427
1428	git_buf_dispose(&loose_path);
1429
1430	return error;
1431}
1432
1433static int refdb_fs_backend__delete_tail(
1434	git_refdb_backend *_backend,
1435	git_filebuf *file,
1436	const char *ref_name,
1437	const git_oid *old_id, const char *old_target)
1438{
1439	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1440	int error = 0, cmp = 0;
1441	bool packed_deleted = 0;
1442
1443	error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
1444	if (error < 0)
1445		goto cleanup;
1446
1447	if (cmp) {
1448		git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
1449		error = GIT_EMODIFIED;
1450		goto cleanup;
1451	}
1452
1453	/*
1454	 * To ensure that an external observer will see either the current ref value
1455	 * (because the loose ref still exists), or a missing ref (after the packed-file is
1456	 * unlocked, there will be nothing left), we must ensure things happen in the
1457	 * following order:
1458	 *
1459	 * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
1460	 * - we optimistically delete a packed ref, keeping track of whether it existed
1461	 * - we delete the loose ref, note that we have its .lock
1462	 * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
1463	 * - we should prune the path components if a loose ref was deleted
1464	 *
1465	 * Note that, because our packed backend doesn't expose its filesystem lock,
1466	 * we might not be able to guarantee that this is what actually happens (ie.
1467	 * as our current code never write packed-refs.lock, nothing stops observers
1468	 * from grabbing a "stale" value from there).
1469	 */
1470	if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1471		goto cleanup;
1472
1473	if (error == 0)
1474		packed_deleted = 1;
1475
1476	if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
1477		goto cleanup;
1478
1479	if (error == GIT_ENOTFOUND) {
1480		error = packed_deleted ? 0 : ref_error_notfound(ref_name);
1481		goto cleanup;
1482	}
1483
1484cleanup:
1485	git_filebuf_cleanup(file);
1486	if (error == 0)
1487		refdb_fs_backend__prune_refs(backend, ref_name, "");
1488	return error;
1489}
1490
1491static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
1492
1493static int refdb_fs_backend__rename(
1494	git_reference **out,
1495	git_refdb_backend *_backend,
1496	const char *old_name,
1497	const char *new_name,
1498	int force,
1499	const git_signature *who,
1500	const char *message)
1501{
1502	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1503	git_reference *old, *new = NULL;
1504	git_filebuf file = GIT_FILEBUF_INIT;
1505	int error;
1506
1507	assert(backend);
1508
1509	if ((error = reference_path_available(
1510			backend, new_name, old_name, force)) < 0 ||
1511		(error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
1512		return error;
1513
1514	if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
1515		git_reference_free(old);
1516		return error;
1517	}
1518
1519	new = git_reference__realloc(&old, new_name);
1520	if (!new) {
1521		git_reference_free(old);
1522		return -1;
1523	}
1524
1525	if ((error = loose_lock(&file, backend, new->name)) < 0) {
1526		git_reference_free(new);
1527		return error;
1528	}
1529
1530	/* Try to rename the refog; it's ok if the old doesn't exist */
1531	error = refdb_reflog_fs__rename(_backend, old_name, new_name);
1532	if (((error == 0) || (error == GIT_ENOTFOUND)) &&
1533	    ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
1534		git_reference_free(new);
1535		git_filebuf_cleanup(&file);
1536		return error;
1537	}
1538
1539	if (error < 0) {
1540		git_reference_free(new);
1541		git_filebuf_cleanup(&file);
1542		return error;
1543	}
1544
1545
1546	if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
1547		git_reference_free(new);
1548		return error;
1549	}
1550
1551	*out = new;
1552	return 0;
1553}
1554
1555static int refdb_fs_backend__compress(git_refdb_backend *_backend)
1556{
1557	int error;
1558	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1559
1560	assert(backend);
1561
1562	if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
1563	    (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
1564	    (error = packed_write(backend)) < 0) /* write back to disk */
1565		return error;
1566
1567	return 0;
1568}
1569
1570static void refdb_fs_backend__free(git_refdb_backend *_backend)
1571{
1572	refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1573
1574	assert(backend);
1575
1576	git_sortedcache_free(backend->refcache);
1577	git__free(backend->gitpath);
1578	git__free(backend->commonpath);
1579	git__free(backend);
1580}
1581
1582static char *setup_namespace(git_repository *repo, const char *in)
1583{
1584	git_buf path = GIT_BUF_INIT;
1585	char *parts, *start, *end, *out = NULL;
1586
1587	if (!in)
1588		goto done;
1589
1590	git_buf_puts(&path, in);
1591
1592	/* if the repo is not namespaced, nothing else to do */
1593	if (repo->namespace == NULL) {
1594		out = git_buf_detach(&path);
1595		goto done;
1596	}
1597
1598	parts = end = git__strdup(repo->namespace);
1599	if (parts == NULL)
1600		goto done;
1601
1602	/*
1603	 * From `man gitnamespaces`:
1604	 *  namespaces which include a / will expand to a hierarchy
1605	 *  of namespaces; for example, GIT_NAMESPACE=foo/bar will store
1606	 *  refs under refs/namespaces/foo/refs/namespaces/bar/
1607	 */
1608	while ((start = git__strsep(&end, "/")) != NULL)
1609		git_buf_printf(&path, "refs/namespaces/%s/", start);
1610
1611	git_buf_printf(&path, "refs/namespaces/%s/refs", end);
1612	git__free(parts);
1613
1614	/* Make sure that the folder with the namespace exists */
1615	if (git_futils_mkdir_relative(git_buf_cstr(&path), in, 0777,
1616			GIT_MKDIR_PATH, NULL) < 0)
1617		goto done;
1618
1619	/* Return root of the namespaced gitpath, i.e. without the trailing '/refs' */
1620	git_buf_rtruncate_at_char(&path, '/');
1621	out = git_buf_detach(&path);
1622
1623done:
1624	git_buf_dispose(&path);
1625	return out;
1626}
1627
1628static int reflog_alloc(git_reflog **reflog, const char *name)
1629{
1630	git_reflog *log;
1631
1632	*reflog = NULL;
1633
1634	log = git__calloc(1, sizeof(git_reflog));
1635	GIT_ERROR_CHECK_ALLOC(log);
1636
1637	log->ref_name = git__strdup(name);
1638	GIT_ERROR_CHECK_ALLOC(log->ref_name);
1639
1640	if (git_vector_init(&log->entries, 0, NULL) < 0) {
1641		git__free(log->ref_name);
1642		git__free(log);
1643		return -1;
1644	}
1645
1646	*reflog = log;
1647
1648	return 0;
1649}
1650
1651static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
1652{
1653	git_parse_ctx parser = GIT_PARSE_CTX_INIT;
1654
1655	if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
1656		return -1;
1657
1658	for (; parser.remain_len; git_parse_advance_line(&parser)) {
1659		git_reflog_entry *entry;
1660		const char *sig;
1661		char c;
1662
1663		entry = git__calloc(1, sizeof(*entry));
1664		GIT_ERROR_CHECK_ALLOC(entry);
1665		entry->committer = git__calloc(1, sizeof(*entry->committer));
1666		GIT_ERROR_CHECK_ALLOC(entry->committer);
1667
1668		if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
1669		    git_parse_advance_expected(&parser, " ", 1) < 0 ||
1670		    git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
1671			goto next;
1672
1673		sig = parser.line;
1674		while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
1675			git_parse_advance_chars(&parser, 1);
1676
1677		if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
1678			goto next;
1679
1680		if (c == '\t') {
1681			size_t len;
1682			git_parse_advance_chars(&parser, 1);
1683
1684			len = parser.line_len;
1685			if (parser.line[len - 1] == '\n')
1686				len--;
1687
1688			entry->msg = git__strndup(parser.line, len);
1689			GIT_ERROR_CHECK_ALLOC(entry->msg);
1690		}
1691
1692		if ((git_vector_insert(&log->entries, entry)) < 0) {
1693			git_reflog_entry__free(entry);
1694			return -1;
1695		}
1696
1697		continue;
1698
1699next:
1700		git_reflog_entry__free(entry);
1701	}
1702
1703	return 0;
1704}
1705
1706static int create_new_reflog_file(const char *filepath)
1707{
1708	int fd, error;
1709
1710	if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
1711		return error;
1712
1713	if ((fd = p_open(filepath,
1714			O_WRONLY | O_CREAT,
1715			GIT_REFLOG_FILE_MODE)) < 0)
1716		return -1;
1717
1718	return p_close(fd);
1719}
1720
1721GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
1722{
1723	if (strcmp(name, GIT_HEAD_FILE) == 0)
1724		return git_buf_join3(path, '/', repo->gitdir, GIT_REFLOG_DIR, name);
1725	return git_buf_join3(path, '/', repo->commondir, GIT_REFLOG_DIR, name);
1726}
1727
1728static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
1729{
1730	refdb_fs_backend *backend;
1731	git_repository *repo;
1732	git_buf path = GIT_BUF_INIT;
1733	int error;
1734
1735	assert(_backend && name);
1736
1737	backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1738	repo = backend->repo;
1739
1740	if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
1741		return error;
1742
1743	error = create_new_reflog_file(git_buf_cstr(&path));
1744	git_buf_dispose(&path);
1745
1746	return error;
1747}
1748
1749static int has_reflog(git_repository *repo, const char *name)
1750{
1751	int ret = 0;
1752	git_buf path = GIT_BUF_INIT;
1753
1754	if (retrieve_reflog_path(&path, repo, name) < 0)
1755		goto cleanup;
1756
1757	ret = git_path_isfile(git_buf_cstr(&path));
1758
1759cleanup:
1760	git_buf_dispose(&path);
1761	return ret;
1762}
1763
1764static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
1765{
1766	refdb_fs_backend *backend;
1767
1768	assert(_backend && name);
1769
1770	backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1771
1772	return has_reflog(backend->repo, name);
1773}
1774
1775static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
1776{
1777	int error = -1;
1778	git_buf log_path = GIT_BUF_INIT;
1779	git_buf log_file = GIT_BUF_INIT;
1780	git_reflog *log = NULL;
1781	git_repository *repo;
1782	refdb_fs_backend *backend;
1783
1784	assert(out && _backend && name);
1785
1786	backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1787	repo = backend->repo;
1788
1789	if (reflog_alloc(&log, name) < 0)
1790		return -1;
1791
1792	if (retrieve_reflog_path(&log_path, repo, name) < 0)
1793		goto cleanup;
1794
1795	error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
1796	if (error < 0 && error != GIT_ENOTFOUND)
1797		goto cleanup;
1798
1799	if ((error == GIT_ENOTFOUND) &&
1800		((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
1801		goto cleanup;
1802
1803	if ((error = reflog_parse(log,
1804		git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
1805		goto cleanup;
1806
1807	*out = log;
1808	goto success;
1809
1810cleanup:
1811	git_reflog_free(log);
1812
1813success:
1814	git_buf_dispose(&log_file);
1815	git_buf_dispose(&log_path);
1816
1817	return error;
1818}
1819
1820static int serialize_reflog_entry(
1821	git_buf *buf,
1822	const git_oid *oid_old,
1823	const git_oid *oid_new,
1824	const git_signature *committer,
1825	const char *msg)
1826{
1827	char raw_old[GIT_OID_HEXSZ+1];
1828	char raw_new[GIT_OID_HEXSZ+1];
1829
1830	git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
1831	git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
1832
1833	git_buf_clear(buf);
1834
1835	git_buf_puts(buf, raw_old);
1836	git_buf_putc(buf, ' ');
1837	git_buf_puts(buf, raw_new);
1838
1839	git_signature__writebuf(buf, " ", committer);
1840
1841	/* drop trailing LF */
1842	git_buf_rtrim(buf);
1843
1844	if (msg) {
1845		size_t i;
1846
1847		git_buf_putc(buf, '\t');
1848		git_buf_puts(buf, msg);
1849
1850		for (i = 0; i < buf->size - 2; i++)
1851			if (buf->ptr[i] == '\n')
1852				buf->ptr[i] = ' ';
1853		git_buf_rtrim(buf);
1854	}
1855
1856	git_buf_putc(buf, '\n');
1857
1858	return git_buf_oom(buf);
1859}
1860
1861static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
1862{
1863	git_repository *repo;
1864	git_buf log_path = GIT_BUF_INIT;
1865	int error;
1866
1867	repo = backend->repo;
1868
1869	if (!git_path_isvalid(backend->repo, refname, 0, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
1870		git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
1871		return GIT_EINVALIDSPEC;
1872	}
1873
1874	if (retrieve_reflog_path(&log_path, repo, refname) < 0)
1875		return -1;
1876
1877	if (!git_path_isfile(git_buf_cstr(&log_path))) {
1878		git_error_set(GIT_ERROR_INVALID,
1879			"log file for reference '%s' doesn't exist", refname);
1880		error = -1;
1881		goto cleanup;
1882	}
1883
1884	error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
1885
1886cleanup:
1887	git_buf_dispose(&log_path);
1888
1889	return error;
1890}
1891
1892static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
1893{
1894	int error = -1;
1895	unsigned int i;
1896	git_reflog_entry *entry;
1897	refdb_fs_backend *backend;
1898	git_buf log = GIT_BUF_INIT;
1899	git_filebuf fbuf = GIT_FILEBUF_INIT;
1900
1901	assert(_backend && reflog);
1902
1903	backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
1904
1905	if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
1906		return -1;
1907
1908	git_vector_foreach(&reflog->entries, i, entry) {
1909		if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
1910			goto cleanup;
1911
1912		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
1913			goto cleanup;
1914	}
1915
1916	error = git_filebuf_commit(&fbuf);
1917	goto success;
1918
1919cleanup:
1920	git_filebuf_cleanup(&fbuf);
1921
1922success:
1923	git_buf_dispose(&log);
1924
1925	return error;
1926}
1927
1928/* Append to the reflog, must be called under reference lock */
1929static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
1930{
1931	int error, is_symbolic, open_flags;
1932	git_oid old_id = {{0}}, new_id = {{0}};
1933	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
1934	git_repository *repo = backend->repo;
1935
1936	is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC;
1937
1938	/* "normal" symbolic updates do not write */
1939	if (is_symbolic &&
1940	    strcmp(ref->name, GIT_HEAD_FILE) &&
1941	    !(old && new))
1942		return 0;
1943
1944	/* From here on is_symbolic also means that it's HEAD */
1945
1946	if (old) {
1947		git_oid_cpy(&old_id, old);
1948	} else {
1949		error = git_reference_name_to_id(&old_id, repo, ref->name);
1950		if (error < 0 && error != GIT_ENOTFOUND)
1951			return error;
1952	}
1953
1954	if (new) {
1955		git_oid_cpy(&new_id, new);
1956	} else {
1957		if (!is_symbolic) {
1958			git_oid_cpy(&new_id, git_reference_target(ref));
1959		} else {
1960			error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
1961			if (error < 0 && error != GIT_ENOTFOUND)
1962				return error;
1963			/* detaching HEAD does not create an entry */
1964			if (error == GIT_ENOTFOUND)
1965				return 0;
1966
1967			git_error_clear();
1968		}
1969	}
1970
1971	if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
1972		goto cleanup;
1973
1974	if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0)
1975		goto cleanup;
1976
1977	if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
1978	    (error != GIT_EEXISTS)) {
1979		goto cleanup;
1980	}
1981
1982	/* If the new branch matches part of the namespace of a previously deleted branch,
1983	 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
1984	 */
1985	if (git_path_isdir(git_buf_cstr(&path))) {
1986		if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
1987			if (error 

Large files files are truncated, but you can click here to view the full file