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