]> git.proxmox.com Git - libgit2.git/blame - src/refs.c
What are the chances, really
[libgit2.git] / src / refs.c
CommitLineData
9282e921 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
9282e921 3 *
bb742ede
VM
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.
9282e921 6 */
7
8#include "refs.h"
9#include "hash.h"
10#include "repository.h"
11#include "fileops.h"
01ad7b3a 12#include "pack.h"
a5cd086d 13#include "reflog.h"
d00d5464 14#include "refdb.h"
9282e921 15
87d3acf4
VM
16#include <git2/tag.h>
17#include <git2/object.h>
c07d9c95 18#include <git2/oid.h>
4ba23be1 19#include <git2/branch.h>
d00d5464
ET
20#include <git2/refs.h>
21#include <git2/refdb.h>
21ca0451 22#include <git2/sys/refs.h>
87d3acf4 23
c2b67043 24GIT__USE_STRMAP;
01fed0a8 25
f201d613
RB
26#define DEFAULT_NESTING_LEVEL 5
27#define MAX_NESTING_LEVEL 10
9282e921 28
d4a0b124
VM
29enum {
30 GIT_PACKREF_HAS_PEEL = 1,
31 GIT_PACKREF_WAS_LOOSE = 2
32};
86194b24 33
4e4eab52 34static git_reference *alloc_ref(const char *name)
3be933b1
VM
35{
36 git_reference *ref;
37 size_t namelen = strlen(name);
86194b24 38
3be933b1
VM
39 if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL)
40 return NULL;
41
3be933b1
VM
42 memcpy(ref->name, name, namelen + 1);
43
44 return ref;
45}
46
47git_reference *git_reference__alloc_symbolic(
21ca0451 48 const char *name, const char *target)
2f8a8ab2 49{
d00d5464 50 git_reference *ref;
2f8a8ab2 51
4e4eab52 52 assert(name && target);
55e0f53d 53
4e4eab52 54 ref = alloc_ref(name);
3be933b1 55 if (!ref)
d00d5464 56 return NULL;
87d3acf4 57
3be933b1 58 ref->type = GIT_REF_SYMBOLIC;
87d3acf4 59
3be933b1
VM
60 if ((ref->target.symbolic = git__strdup(target)) == NULL) {
61 git__free(ref);
62 return NULL;
d79f1da6 63 }
55e0f53d 64
3be933b1
VM
65 return ref;
66}
67
68git_reference *git_reference__alloc(
3be933b1
VM
69 const char *name,
70 const git_oid *oid,
71 const git_oid *peel)
72{
73 git_reference *ref;
74
4e4eab52 75 assert(name && oid);
3be933b1 76
4e4eab52 77 ref = alloc_ref(name);
3be933b1
VM
78 if (!ref)
79 return NULL;
80
81 ref->type = GIT_REF_OID;
fedd0f9e 82 git_oid_cpy(&ref->target.oid, oid);
3be933b1
VM
83
84 if (peel != NULL)
fedd0f9e 85 git_oid_cpy(&ref->peel, peel);
55e0f53d 86
d00d5464 87 return ref;
2f8a8ab2 88}
9282e921 89
d00d5464 90void git_reference_free(git_reference *reference)
2f8a8ab2 91{
d00d5464
ET
92 if (reference == NULL)
93 return;
2b397327 94
3be933b1 95 if (reference->type == GIT_REF_SYMBOLIC)
d00d5464 96 git__free(reference->target.symbolic);
2f8a8ab2 97
d00d5464 98 git__free(reference);
87d3acf4 99}
2f8a8ab2 100
45d387ac
VM
101struct reference_available_t {
102 const char *new_ref;
103 const char *old_ref;
104 int available;
105};
106
ec24e542 107static int _reference_available_cb(const char *refname, void *data)
1b6d8163 108{
45d387ac 109 struct reference_available_t *d;
f120e92b 110
ec24e542 111 assert(refname && data);
1a481123 112 d = (struct reference_available_t *)data;
1b6d8163 113
ec24e542
VM
114 if (!d->old_ref || strcmp(d->old_ref, refname)) {
115 size_t reflen = strlen(refname);
44ef8b1b
RB
116 size_t newlen = strlen(d->new_ref);
117 size_t cmplen = reflen < newlen ? reflen : newlen;
ec24e542 118 const char *lead = reflen < newlen ? d->new_ref : refname;
1b6d8163 119
ec24e542 120 if (!strncmp(d->new_ref, refname, cmplen) && lead[cmplen] == '/') {
45d387ac
VM
121 d->available = 0;
122 return -1;
123 }
1b6d8163
MS
124 }
125
45d387ac 126 return 0;
1b6d8163
MS
127}
128
ec24e542
VM
129/**
130 * TODO: this should be part of the FS backend
131 */
1a481123 132static int reference_path_available(
d4a0b124
VM
133 git_repository *repo,
134 const char *ref,
1a481123 135 const char* old_ref)
1b6d8163 136{
5dca2010 137 int error;
45d387ac 138 struct reference_available_t data;
1b6d8163 139
45d387ac
VM
140 data.new_ref = ref;
141 data.old_ref = old_ref;
142 data.available = 1;
1b6d8163 143
ec24e542 144 error = git_reference_foreach_name(repo, _reference_available_cb, (void *)&data);
5dca2010
RB
145 if (error < 0)
146 return error;
1a481123
VM
147
148 if (!data.available) {
149 giterr_set(GITERR_REFERENCE,
d00d5464 150 "The path to reference '%s' collides with an existing one", ref);
1a481123 151 return -1;
d4a0b124 152 }
2f8a8ab2 153
1a481123
VM
154 return 0;
155}
97769280 156
1a481123
VM
157/*
158 * Check if a reference could be written to disk, based on:
159 *
160 * - Whether a reference with the same name already exists,
161 * and we are allowing or disallowing overwrites
162 *
163 * - Whether the name of the reference would collide with
164 * an existing path
165 */
166static int reference_can_write(
167 git_repository *repo,
168 const char *refname,
169 const char *previous_name,
170 int force)
171{
d00d5464
ET
172 git_refdb *refdb;
173
174 if (git_repository_refdb__weakptr(&refdb, repo) < 0)
175 return -1;
176
1a481123
VM
177 /* see if the reference shares a path with an existing reference;
178 * if a path is shared, we cannot create the reference, even when forcing */
179 if (reference_path_available(repo, refname, previous_name) < 0)
180 return -1;
181
182 /* check if the reference actually exists, but only if we are not forcing
183 * the rename. If we are forcing, it's OK to overwrite */
184 if (!force) {
185 int exists;
186
d00d5464 187 if (git_refdb_exists(&exists, refdb, refname) < 0)
1a481123
VM
188 return -1;
189
190 /* We cannot proceed if the reference already exists and we're not forcing
191 * the rename; the existing one would be overwritten */
192 if (exists) {
193 giterr_set(GITERR_REFERENCE,
e1de726c 194 "A reference with that name (%s) already exists", refname);
904b67e6 195 return GIT_EEXISTS;
1a481123
VM
196 }
197 }
198
199 /* FIXME: if the reference exists and we are forcing, do we really need to
200 * remove the reference first?
201 *
202 * Two cases:
203 *
204 * - the reference already exists and is loose: not a problem, the file
205 * gets overwritten on disk
206 *
207 * - the reference already exists and is packed: we write a new one as
208 * loose, which by all means renders the packed one useless
209 */
210
211 return 0;
d4a0b124 212}
2f8a8ab2 213
1a481123 214int git_reference_delete(git_reference *ref)
a46ec457 215{
d00d5464 216 return git_refdb_delete(ref->db, ref);
a46ec457
MS
217}
218
d4a0b124 219int git_reference_lookup(git_reference **ref_out,
1a481123 220 git_repository *repo, const char *name)
a46ec457 221{
f201d613
RB
222 return git_reference_lookup_resolved(ref_out, repo, name, 0);
223}
224
2508cc66 225int git_reference_name_to_id(
f201d613
RB
226 git_oid *out, git_repository *repo, const char *name)
227{
228 int error;
229 git_reference *ref;
230
231 if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
232 return error;
233
2508cc66 234 git_oid_cpy(out, git_reference_target(ref));
f201d613
RB
235 git_reference_free(ref);
236 return 0;
237}
238
239int git_reference_lookup_resolved(
240 git_reference **ref_out,
241 git_repository *repo,
242 const char *name,
243 int max_nesting)
244{
d00d5464
ET
245 char scan_name[GIT_REFNAME_MAX];
246 git_ref_t scan_type;
247 int error = 0, nesting;
248 git_reference *ref = NULL;
249 git_refdb *refdb;
d4a0b124
VM
250
251 assert(ref_out && repo && name);
f201d613 252
1a481123 253 *ref_out = NULL;
d4a0b124 254
f201d613
RB
255 if (max_nesting > MAX_NESTING_LEVEL)
256 max_nesting = MAX_NESTING_LEVEL;
257 else if (max_nesting < 0)
258 max_nesting = DEFAULT_NESTING_LEVEL;
4dcd8780 259
d00d5464
ET
260 strncpy(scan_name, name, GIT_REFNAME_MAX);
261 scan_type = GIT_REF_SYMBOLIC;
4dcd8780 262
d00d5464
ET
263 if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
264 return -1;
a46ec457 265
d00d5464
ET
266 if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
267 return error;
f201d613
RB
268
269 for (nesting = max_nesting;
d00d5464 270 nesting >= 0 && scan_type == GIT_REF_SYMBOLIC;
f201d613
RB
271 nesting--)
272 {
d00d5464
ET
273 if (nesting != max_nesting) {
274 strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
275 git_reference_free(ref);
276 }
f201d613 277
d00d5464
ET
278 if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
279 return error;
4dcd8780 280
d00d5464 281 scan_type = ref->type;
f201d613
RB
282 }
283
d00d5464 284 if (scan_type != GIT_REF_OID && max_nesting != 0) {
f201d613
RB
285 giterr_set(GITERR_REFERENCE,
286 "Cannot resolve reference (>%u levels deep)", max_nesting);
d00d5464 287 git_reference_free(ref);
f201d613
RB
288 return -1;
289 }
290
d00d5464 291 *ref_out = ref;
f201d613 292 return 0;
a46ec457
MS
293}
294
98d633cc
CMN
295int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
296{
297 int error = 0, i;
298 bool fallbackmode = true, foundvalid = false;
299 git_reference *ref;
300 git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
301
302 static const char* formatters[] = {
303 "%s",
304 GIT_REFS_DIR "%s",
305 GIT_REFS_TAGS_DIR "%s",
306 GIT_REFS_HEADS_DIR "%s",
307 GIT_REFS_REMOTES_DIR "%s",
308 GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
309 NULL
310 };
311
312 if (*refname)
313 git_buf_puts(&name, refname);
314 else {
315 git_buf_puts(&name, GIT_HEAD_FILE);
316 fallbackmode = false;
317 }
318
319 for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
320
321 git_buf_clear(&refnamebuf);
322
323 if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
324 goto cleanup;
325
326 if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
327 error = GIT_EINVALIDSPEC;
328 continue;
329 }
330 foundvalid = true;
331
332 error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);
333
334 if (!error) {
335 *out = ref;
336 error = 0;
337 goto cleanup;
338 }
339
340 if (error != GIT_ENOTFOUND)
341 goto cleanup;
342 }
343
344cleanup:
345 if (error && !foundvalid) {
346 /* never found a valid reference name */
347 giterr_set(GITERR_REFERENCE,
348 "Could not use '%s' as valid reference name", git_buf_cstr(&name));
349 }
350
351 git_buf_free(&name);
352 git_buf_free(&refnamebuf);
353 return error;
354}
355
d4a0b124
VM
356/**
357 * Getters
358 */
2508cc66 359git_ref_t git_reference_type(const git_reference *ref)
87d3acf4
VM
360{
361 assert(ref);
d00d5464 362 return ref->type;
87d3acf4
VM
363}
364
2508cc66 365const char *git_reference_name(const git_reference *ref)
87d3acf4
VM
366{
367 assert(ref);
d4a0b124 368 return ref->name;
87d3acf4
VM
369}
370
2508cc66 371git_repository *git_reference_owner(const git_reference *ref)
a46ec457 372{
d4a0b124 373 assert(ref);
d00d5464 374 return ref->db->repo;
a46ec457
MS
375}
376
2508cc66 377const git_oid *git_reference_target(const git_reference *ref)
87d3acf4
VM
378{
379 assert(ref);
380
d00d5464 381 if (ref->type != GIT_REF_OID)
87d3acf4
VM
382 return NULL;
383
fedd0f9e 384 return &ref->target.oid;
3be933b1
VM
385}
386
387const git_oid *git_reference_target_peel(const git_reference *ref)
388{
389 assert(ref);
390
fedd0f9e 391 if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->peel))
3be933b1
VM
392 return NULL;
393
fedd0f9e 394 return &ref->peel;
87d3acf4
VM
395}
396
2508cc66 397const char *git_reference_symbolic_target(const git_reference *ref)
a46ec457 398{
d4a0b124 399 assert(ref);
a46ec457 400
d00d5464 401 if (ref->type != GIT_REF_SYMBOLIC)
a46ec457
MS
402 return NULL;
403
d4a0b124 404 return ref->target.symbolic;
a46ec457
MS
405}
406
d00d5464 407static int reference__create(
d4a0b124
VM
408 git_reference **ref_out,
409 git_repository *repo,
410 const char *name,
d00d5464
ET
411 const git_oid *oid,
412 const char *symbolic,
1a481123 413 int force)
d5afc039
VM
414{
415 char normalized[GIT_REFNAME_MAX];
d00d5464 416 git_refdb *refdb;
d4a0b124 417 git_reference *ref = NULL;
d00d5464 418 int error = 0;
4dcd8780 419
d00d5464
ET
420 if (ref_out)
421 *ref_out = NULL;
422
423 if ((error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name)) < 0 ||
424 (error = reference_can_write(repo, normalized, NULL, force)) < 0 ||
425 (error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
c1281493 426 return error;
3be933b1
VM
427
428 if (oid != NULL) {
429 assert(symbolic == NULL);
4e4eab52 430 ref = git_reference__alloc(name, oid, NULL);
3be933b1 431 } else {
4e4eab52 432 ref = git_reference__alloc_symbolic(name, symbolic);
3be933b1 433 }
4dcd8780 434
ec24e542 435 /* TODO: this needs to be written more explicitly */
13421eee 436 GITERR_CHECK_ALLOC(ref);
4e4eab52 437 ref->db = refdb;
d5afc039 438
d00d5464 439 if ((error = git_refdb_write(refdb, ref)) < 0) {
45d387ac 440 git_reference_free(ref);
d00d5464 441 return error;
45d387ac 442 }
4dcd8780 443
d00d5464 444 if (ref_out == NULL)
d4a0b124 445 git_reference_free(ref);
d00d5464 446 else
d4a0b124 447 *ref_out = ref;
d5afc039 448
45d387ac 449 return 0;
d5afc039
VM
450}
451
2508cc66 452int git_reference_create(
d4a0b124
VM
453 git_reference **ref_out,
454 git_repository *repo,
455 const char *name,
d00d5464 456 const git_oid *oid,
1a481123 457 int force)
a46ec457 458{
d00d5464
ET
459 git_odb *odb;
460 int error = 0;
d5afc039 461
d00d5464 462 assert(repo && name && oid);
4dcd8780 463
d00d5464
ET
464 /* Sanity check the reference being created - target must exist. */
465 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
c1281493 466 return error;
4dcd8780 467
d00d5464
ET
468 if (!git_odb_exists(odb, oid)) {
469 giterr_set(GITERR_REFERENCE,
470 "Target OID for the reference doesn't exist on the repository");
45d387ac
VM
471 return -1;
472 }
4dcd8780 473
d00d5464 474 return reference__create(ref_out, repo, name, oid, NULL, force);
a46ec457 475}
87d3acf4 476
d00d5464
ET
477int git_reference_symbolic_create(
478 git_reference **ref_out,
479 git_repository *repo,
480 const char *name,
481 const char *target,
482 int force)
483{
484 char normalized[GIT_REFNAME_MAX];
485 int error = 0;
87d3acf4 486
d00d5464 487 assert(repo && name && target);
4dcd8780 488
d00d5464
ET
489 if ((error = git_reference__normalize_name_lax(
490 normalized, sizeof(normalized), target)) < 0)
491 return error;
9a53df7e 492
d00d5464
ET
493 return reference__create(ref_out, repo, name, NULL, normalized, force);
494}
9462c471 495
d00d5464
ET
496int git_reference_set_target(
497 git_reference **out,
498 git_reference *ref,
499 const git_oid *id)
500{
501 assert(out && ref && id);
4dcd8780 502
d00d5464
ET
503 if (ref->type != GIT_REF_OID) {
504 giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
45d387ac
VM
505 return -1;
506 }
87d3acf4 507
d00d5464 508 return git_reference_create(out, ref->db->repo, ref->name, id, 1);
a46ec457
MS
509}
510
d00d5464
ET
511int git_reference_symbolic_set_target(
512 git_reference **out,
513 git_reference *ref,
514 const char *target)
87d3acf4 515{
d00d5464 516 assert(out && ref && target);
4dcd8780 517
d00d5464 518 if (ref->type != GIT_REF_SYMBOLIC) {
1a481123 519 giterr_set(GITERR_REFERENCE,
45d387ac
VM
520 "Cannot set symbolic target on a direct reference");
521 return -1;
522 }
4dcd8780 523
d00d5464 524 return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1);
87d3acf4
VM
525}
526
d00d5464
ET
527int git_reference_rename(
528 git_reference **out,
529 git_reference *ref,
530 const char *new_name,
531 int force)
7376ad99 532{
2e0c8816 533 unsigned int normalization_flags;
0ffcf78a 534 char normalized[GIT_REFNAME_MAX];
f3cc7834 535 bool should_head_be_updated = false;
d00d5464 536 git_reference *result = NULL;
d00d5464 537 int error = 0;
10c06114 538 int reference_has_log;
4dcd8780 539
d00d5464
ET
540 *out = NULL;
541
542 normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
543 GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
544
3be933b1
VM
545 if ((error = git_reference_normalize_name(
546 normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
d00d5464
ET
547 (error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0)
548 return error;
7376ad99 549
0ffcf78a 550 /*
d00d5464 551 * Create the new reference.
0ffcf78a 552 */
d00d5464 553 if (ref->type == GIT_REF_OID) {
4dcd8780 554 result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel);
3be933b1 555 } else if (ref->type == GIT_REF_SYMBOLIC) {
4e4eab52 556 result = git_reference__alloc_symbolic(new_name, ref->target.symbolic);
0ffcf78a 557 } else {
3be933b1 558 assert(0);
0ffcf78a 559 }
3be933b1
VM
560
561 if (result == NULL)
d00d5464 562 return -1;
7376ad99 563
ec24e542 564 /* TODO: this is bad */
4e4eab52
ET
565 result->db = ref->db;
566
d00d5464 567 /* Check if we have to update HEAD. */
10c06114 568 if ((error = git_branch_is_head(ref)) < 0)
d00d5464
ET
569 goto on_error;
570
10c06114
AS
571 should_head_be_updated = (error > 0);
572
d00d5464 573 /* Now delete the old ref and save the new one. */
10c06114 574 if ((error = git_refdb_delete(ref->db, ref)) < 0)
d00d5464 575 goto on_error;
4dcd8780 576
d00d5464
ET
577 /* Save the new reference. */
578 if ((error = git_refdb_write(ref->db, result)) < 0)
64093ce5 579 goto rollback;
4dcd8780 580
d00d5464 581 /* Update HEAD it was poiting to the reference being renamed. */
10c06114 582 if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
d00d5464
ET
583 giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
584 goto on_error;
d4a0b124
VM
585 }
586
d00d5464 587 /* Rename the reflog file, if it exists. */
10c06114
AS
588 reference_has_log = git_reference_has_log(ref);
589 if (reference_has_log < 0) {
590 error = reference_has_log;
591 goto on_error;
592 }
593 if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
d00d5464 594 goto on_error;
7376ad99 595
d00d5464 596 *out = result;
45d387ac 597
d00d5464 598 return error;
7376ad99 599
0ffcf78a 600rollback:
d00d5464 601 git_refdb_write(ref->db, ref);
0ffcf78a 602
d00d5464
ET
603on_error:
604 git_reference_free(result);
64093ce5 605
d00d5464 606 return error;
0ffcf78a 607}
7376ad99 608
2508cc66 609int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
87d3acf4 610{
3be933b1
VM
611 switch (git_reference_type(ref)) {
612 case GIT_REF_OID:
d00d5464 613 return git_reference_lookup(ref_out, ref->db->repo, ref->name);
4dcd8780 614
3be933b1
VM
615 case GIT_REF_SYMBOLIC:
616 return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
617
618 default:
619 giterr_set(GITERR_REFERENCE, "Invalid reference");
620 return -1;
621 }
87d3acf4
VM
622}
623
d4a0b124
VM
624int git_reference_foreach(
625 git_repository *repo,
eecc8050 626 git_reference_foreach_cb callback,
1a481123 627 void *payload)
00571828 628{
95727245 629 git_reference_iterator *iter;
ec24e542 630 git_reference *ref;
95727245
CMN
631 int error;
632
95727245
CMN
633 if (git_reference_iterator_new(&iter, repo) < 0)
634 return -1;
00571828 635
ec24e542
VM
636 while ((error = git_reference_next(&ref, iter)) == 0) {
637 if (callback(ref, payload)) {
638 error = GIT_EUSER;
639 goto out;
640 }
641 }
642
643 if (error == GIT_ITEROVER)
644 error = 0;
645
646out:
647 git_reference_iterator_free(iter);
648 return error;
649}
650
651int git_reference_foreach_name(
652 git_repository *repo,
653 git_reference_foreach_name_cb callback,
654 void *payload)
655{
656 git_reference_iterator *iter;
657 const char *refname;
658 int error;
659
660 if (git_reference_iterator_new(&iter, repo) < 0)
661 return -1;
662
663 while ((error = git_reference_next_name(&refname, iter)) == 0) {
664 if (callback(refname, payload)) {
665 error = GIT_EUSER;
666 goto out;
667 }
668 }
669
670 if (error == GIT_ITEROVER)
671 error = 0;
672
673out:
674 git_reference_iterator_free(iter);
675 return error;
676}
677
678int git_reference_foreach_glob(
679 git_repository *repo,
680 const char *glob,
681 git_reference_foreach_name_cb callback,
682 void *payload)
683{
684 git_reference_iterator *iter;
685 const char *refname;
686 int error;
687
688 if (git_reference_iterator_glob_new(&iter, repo, glob) < 0)
689 return -1;
690
691 while ((error = git_reference_next_name(&refname, iter)) == 0) {
692 if (callback(refname, payload)) {
95727245
CMN
693 error = GIT_EUSER;
694 goto out;
695 }
696 }
697
698 if (error == GIT_ITEROVER)
699 error = 0;
700
701out:
702 git_reference_iterator_free(iter);
703 return error;
09e8de0f
VM
704}
705
4def7035
CMN
706int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
707{
708 git_refdb *refdb;
709
710 if (git_repository_refdb__weakptr(&refdb, repo) < 0)
711 return -1;
712
ec24e542 713 return git_refdb_iterator(out, refdb, NULL);
4def7035
CMN
714}
715
ec24e542
VM
716int git_reference_iterator_glob_new(
717 git_reference_iterator **out, git_repository *repo, const char *glob)
00571828 718{
d00d5464 719 git_refdb *refdb;
00571828 720
c58cac12
CMN
721 if (git_repository_refdb__weakptr(&refdb, repo) < 0)
722 return -1;
723
ec24e542 724 return git_refdb_iterator(out, refdb, glob);
c58cac12
CMN
725}
726
56960b83 727int git_reference_next(git_reference **out, git_reference_iterator *iter)
4def7035 728{
ec24e542
VM
729 return git_refdb_iterator_next(out, iter);
730}
731
732int git_reference_next_name(const char **out, git_reference_iterator *iter)
733{
734 return git_refdb_iterator_next_name(out, iter);
4def7035
CMN
735}
736
737void git_reference_iterator_free(git_reference_iterator *iter)
738{
739 git_refdb_iterator_free(iter);
09e8de0f
VM
740}
741
d568d585 742static int cb__reflist_add(const char *ref, void *data)
09e8de0f
VM
743{
744 return git_vector_insert((git_vector *)data, git__strdup(ref));
745}
746
4fbd1c00 747int git_reference_list(
d4a0b124 748 git_strarray *array,
2b562c3a 749 git_repository *repo)
09e8de0f 750{
09e8de0f
VM
751 git_vector ref_list;
752
753 assert(array && repo);
754
755 array->strings = NULL;
756 array->count = 0;
757
0d0fa7c3 758 if (git_vector_init(&ref_list, 8, NULL) < 0)
45d387ac 759 return -1;
7ad96e51 760
ec24e542 761 if (git_reference_foreach_name(
2b562c3a 762 repo, &cb__reflist_add, (void *)&ref_list) < 0) {
09e8de0f 763 git_vector_free(&ref_list);
45d387ac 764 return -1;
7ad96e51
VM
765 }
766
09e8de0f
VM
767 array->strings = (char **)ref_list.contents;
768 array->count = ref_list.length;
1a481123 769 return 0;
00571828 770}
87d3acf4 771
d4a0b124 772static int is_valid_ref_char(char ch)
aa2120e9 773{
50a8fd03 774 if ((unsigned) ch <= ' ')
d4a0b124 775 return 0;
aa2120e9 776
777 switch (ch) {
778 case '~':
779 case '^':
780 case ':':
781 case '\\':
782 case '?':
783 case '[':
e1be1028 784 case '*':
d4a0b124 785 return 0;
aa2120e9 786 default:
d4a0b124 787 return 1;
aa2120e9 788 }
789}
790
c030ada7 791static int ensure_segment_validity(const char *name)
aa2120e9 792{
c030ada7 793 const char *current = name;
794 char prev = '\0';
0d1b094b
RB
795 const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
796 int segment_len;
aa2120e9 797
c030ada7 798 if (*current == '.')
799 return -1; /* Refname starts with "." */
aa2120e9 800
c030ada7 801 for (current = name; ; current++) {
802 if (*current == '\0' || *current == '/')
803 break;
2e0c8816 804
c030ada7 805 if (!is_valid_ref_char(*current))
806 return -1; /* Illegal character in refname */
aa2120e9 807
c030ada7 808 if (prev == '.' && *current == '.')
809 return -1; /* Refname contains ".." */
3101a3e5 810
c030ada7 811 if (prev == '@' && *current == '{')
812 return -1; /* Refname contains "@{" */
aa2120e9 813
c030ada7 814 prev = *current;
815 }
aa2120e9 816
0d1b094b
RB
817 segment_len = (int)(current - name);
818
4d811c3b 819 /* A refname component can not end with ".lock" */
0d1b094b 820 if (segment_len >= lock_len &&
2bca5b67 821 !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
4d811c3b 822 return -1;
823
0d1b094b 824 return segment_len;
c030ada7 825}
aa2120e9 826
7c411fd9 827static bool is_all_caps_and_underscore(const char *name, size_t len)
77e06d7e 828{
7c411fd9 829 size_t i;
77e06d7e 830 char c;
831
832 assert(name && len > 0);
833
834 for (i = 0; i < len; i++)
835 {
836 c = name[i];
837 if ((c < 'A' || c > 'Z') && c != '_')
838 return false;
839 }
840
841 if (*name == '_' || name[len - 1] == '_')
842 return false;
843
844 return true;
845}
846
c030ada7 847int git_reference__normalize_name(
848 git_buf *buf,
849 const char *name,
850 unsigned int flags)
851{
852 // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
853
854 char *current;
80d9d1df 855 int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
77e06d7e 856 unsigned int process_flags;
857 bool normalize = (buf != NULL);
858 assert(name);
c030ada7 859
77e06d7e 860 process_flags = flags;
c030ada7 861 current = (char *)name;
862
bb45c57f
CMN
863 if (*current == '/')
864 goto cleanup;
865
77e06d7e 866 if (normalize)
867 git_buf_clear(buf);
c030ada7 868
869 while (true) {
870 segment_len = ensure_segment_validity(current);
871 if (segment_len < 0) {
77e06d7e 872 if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
c030ada7 873 current[0] == '*' &&
874 (current[1] == '\0' || current[1] == '/')) {
875 /* Accept one wildcard as a full refname component. */
77e06d7e 876 process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
c030ada7 877 segment_len = 1;
878 } else
879 goto cleanup;
880 }
aa2120e9 881
c030ada7 882 if (segment_len > 0) {
77e06d7e 883 if (normalize) {
7c411fd9 884 size_t cur_len = git_buf_len(buf);
aa2120e9 885
77e06d7e 886 git_buf_joinpath(buf, git_buf_cstr(buf), current);
7c411fd9 887 git_buf_truncate(buf,
77e06d7e 888 cur_len + segment_len + (segments_count ? 1 : 0));
aa2120e9 889
80d9d1df 890 if (git_buf_oom(buf)) {
891 error = -1;
77e06d7e 892 goto cleanup;
80d9d1df 893 }
77e06d7e 894 }
aa2120e9 895
77e06d7e 896 segments_count++;
0844ed06 897 }
2e0c8816 898
2bca5b67 899 /* No empty segment is allowed when not normalizing */
900 if (segment_len == 0 && !normalize)
e5ef0f18 901 goto cleanup;
0d1b094b 902
c030ada7 903 if (current[segment_len] == '\0')
904 break;
aa2120e9 905
c030ada7 906 current += segment_len + 1;
2e0c8816 907 }
3101a3e5 908
c030ada7 909 /* A refname can not be empty */
77e06d7e 910 if (segment_len == 0 && segments_count == 0)
c030ada7 911 goto cleanup;
912
913 /* A refname can not end with "." */
914 if (current[segment_len - 1] == '.')
915 goto cleanup;
916
917 /* A refname can not end with "/" */
918 if (current[segment_len - 1] == '/')
919 goto cleanup;
920
77e06d7e 921 if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
922 goto cleanup;
923
924 if ((segments_count == 1 ) &&
528a4e24 925 !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) &&
7c411fd9 926 !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
77e06d7e 927 ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
928 goto cleanup;
929
930 if ((segments_count > 1)
931 && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
932 goto cleanup;
aa2120e9 933
c030ada7 934 error = 0;
aa2120e9 935
c030ada7 936cleanup:
80d9d1df 937 if (error == GIT_EINVALIDSPEC)
c030ada7 938 giterr_set(
939 GITERR_REFERENCE,
940 "The given reference name '%s' is not valid", name);
aa2120e9 941
3865f7f6
RB
942 if (error && normalize)
943 git_buf_free(buf);
944
c030ada7 945 return error;
946}
1a481123 947
c030ada7 948int git_reference_normalize_name(
949 char *buffer_out,
950 size_t buffer_size,
951 const char *name,
952 unsigned int flags)
953{
954 git_buf buf = GIT_BUF_INIT;
955 int error;
956
957 if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
958 goto cleanup;
959
960 if (git_buf_len(&buf) > buffer_size - 1) {
961 giterr_set(
2e0c8816 962 GITERR_REFERENCE,
c030ada7 963 "The provided buffer is too short to hold the normalization of '%s'", name);
964 error = GIT_EBUFS;
965 goto cleanup;
966 }
967
968 git_buf_copy_cstr(buffer_out, buffer_size, &buf);
969
970 error = 0;
971
972cleanup:
973 git_buf_free(&buf);
974 return error;
aa2120e9 975}
2f8a8ab2 976
c030ada7 977int git_reference__normalize_name_lax(
d4a0b124
VM
978 char *buffer_out,
979 size_t out_size,
980 const char *name)
86194b24 981{
2e0c8816 982 return git_reference_normalize_name(
983 buffer_out,
984 out_size,
985 name,
986 GIT_REF_FORMAT_ALLOW_ONELEVEL);
86194b24 987}
f201d613
RB
988#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
989
990int git_reference_cmp(git_reference *ref1, git_reference *ref2)
991{
3be933b1 992 git_ref_t type1, type2;
f201d613
RB
993 assert(ref1 && ref2);
994
3be933b1
VM
995 type1 = git_reference_type(ref1);
996 type2 = git_reference_type(ref2);
997
f201d613 998 /* let's put symbolic refs before OIDs */
3be933b1
VM
999 if (type1 != type2)
1000 return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1;
f201d613 1001
3be933b1 1002 if (type1 == GIT_REF_SYMBOLIC)
f201d613
RB
1003 return strcmp(ref1->target.symbolic, ref2->target.symbolic);
1004
b7f167da 1005 return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
f201d613
RB
1006}
1007
d00d5464
ET
1008static int reference__update_terminal(
1009 git_repository *repo,
1010 const char *ref_name,
1011 const git_oid *oid,
1012 int nesting)
edebceff 1013{
1014 git_reference *ref;
d00d5464 1015 int error = 0;
edebceff 1016
41e93563
RB
1017 if (nesting > MAX_NESTING_LEVEL) {
1018 giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting);
d00d5464 1019 return GIT_ENOTFOUND;
41e93563 1020 }
4dcd8780 1021
d00d5464 1022 error = git_reference_lookup(&ref, repo, ref_name);
edebceff 1023
d00d5464
ET
1024 /* If we haven't found the reference at all, create a new reference. */
1025 if (error == GIT_ENOTFOUND) {
edebceff 1026 giterr_clear();
d00d5464 1027 return git_reference_create(NULL, repo, ref_name, oid, 0);
edebceff 1028 }
4dcd8780 1029
d00d5464
ET
1030 if (error < 0)
1031 return error;
4dcd8780 1032
d00d5464 1033 /* If the ref is a symbolic reference, follow its target. */
edebceff 1034 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
d00d5464
ET
1035 error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid,
1036 nesting+1);
edebceff 1037 git_reference_free(ref);
d00d5464
ET
1038 } else {
1039 git_reference_free(ref);
1040 error = git_reference_create(NULL, repo, ref_name, oid, 1);
edebceff 1041 }
4dcd8780 1042
d00d5464 1043 return error;
edebceff 1044}
527ed554 1045
d00d5464
ET
1046/*
1047 * Starting with the reference given by `ref_name`, follows symbolic
1048 * references until a direct reference is found and updated the OID
1049 * on that direct reference to `oid`.
1050 */
1051int git_reference__update_terminal(
1052 git_repository *repo,
1053 const char *ref_name,
1054 const git_oid *oid)
527ed554 1055{
d00d5464 1056 return reference__update_terminal(repo, ref_name, oid, 0);
527ed554 1057}
1058
75261421 1059int git_reference_has_log(
1060 git_reference *ref)
1061{
1062 git_buf path = GIT_BUF_INIT;
1063 int result;
1064
1065 assert(ref);
1066
d00d5464
ET
1067 if (git_buf_join_n(&path, '/', 3, ref->db->repo->path_repository,
1068 GIT_REFLOG_DIR, ref->name) < 0)
75261421 1069 return -1;
1070
1071 result = git_path_isfile(git_buf_cstr(&path));
1072 git_buf_free(&path);
1073
1074 return result;
1075}
84f18e35 1076
bf031581 1077int git_reference__is_branch(const char *ref_name)
1078{
1079 return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
1080}
1081
88bcd515 1082int git_reference_is_branch(git_reference *ref)
1083{
1084 assert(ref);
bf031581 1085 return git_reference__is_branch(ref->name);
88bcd515 1086}
1c947daa 1087
c1b5e8c4 1088int git_reference__is_remote(const char *ref_name)
1089{
1090 return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
1091}
1092
1c947daa
VM
1093int git_reference_is_remote(git_reference *ref)
1094{
1095 assert(ref);
c1b5e8c4 1096 return git_reference__is_remote(ref->name);
1c947daa 1097}
31665948 1098
1099static int peel_error(int error, git_reference *ref, const char* msg)
1100{
1101 giterr_set(
1102 GITERR_INVALID,
1103 "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
1104 return error;
1105}
1106
31665948 1107int git_reference_peel(
1108 git_object **peeled,
1109 git_reference *ref,
1110 git_otype target_type)
1111{
1112 git_reference *resolved = NULL;
1113 git_object *target = NULL;
1114 int error;
1115
1116 assert(ref);
1117
3be933b1
VM
1118 if (ref->type == GIT_REF_OID) {
1119 resolved = ref;
1120 } else {
1121 if ((error = git_reference_resolve(&resolved, ref)) < 0)
1122 return peel_error(error, ref, "Cannot resolve reference");
1123 }
1124
fedd0f9e 1125 if (!git_oid_iszero(&resolved->peel)) {
3be933b1 1126 error = git_object_lookup(&target,
fedd0f9e 1127 git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
3be933b1
VM
1128 } else {
1129 error = git_object_lookup(&target,
fedd0f9e 1130 git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY);
3be933b1 1131 }
31665948 1132
3be933b1 1133 if (error < 0) {
31665948 1134 peel_error(error, ref, "Cannot retrieve reference target");
1135 goto cleanup;
1136 }
b90500f0 1137
31665948 1138 if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
575a54db 1139 error = git_object_dup(peeled, target);
b90500f0 1140 else
31665948 1141 error = git_object_peel(peeled, target, target_type);
1142
1143cleanup:
1144 git_object_free(target);
3be933b1
VM
1145
1146 if (resolved != ref)
1147 git_reference_free(resolved);
1148
31665948 1149 return error;
1150}
77e06d7e 1151
0adfa20a 1152int git_reference__is_valid_name(
1153 const char *refname,
1154 unsigned int flags)
1155{
83458bb7 1156 int error;
1157
1158 error = git_reference__normalize_name(NULL, refname, flags) == 0;
0adfa20a 1159 giterr_clear();
83458bb7 1160
1161 return error;
0adfa20a 1162}
1163
77e06d7e 1164int git_reference_is_valid_name(
1165 const char *refname)
1166{
0adfa20a 1167 return git_reference__is_valid_name(
77e06d7e 1168 refname,
0adfa20a 1169 GIT_REF_FORMAT_ALLOW_ONELEVEL);
77e06d7e 1170}