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