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