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