]> git.proxmox.com Git - libgit2.git/blame - src/refs.c
Merge pull request #2092 from libgit2/rb/update-clar
[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
0b28217b 396static int log_signature(git_signature **out, git_repository *repo)
a46ec457 397{
a57dd3b7 398 int error;
0b28217b 399 git_signature *who;
d5afc039 400
0b28217b
CMN
401 if(((error = git_signature_default(&who, repo)) < 0) &&
402 ((error = git_signature_now(&who, "unknown", "unknown")) < 0))
c1281493 403 return error;
4dcd8780 404
0b28217b
CMN
405 *out = who;
406 return 0;
bba25f39 407}
408
0b28217b 409int git_reference_create(
bba25f39 410 git_reference **ref_out,
411 git_repository *repo,
412 const char *name,
413 const git_oid *oid,
414 int force,
415 const git_signature *signature,
416 const char *log_message)
417{
0b28217b
CMN
418 int error;
419 git_signature *who = NULL;
420
421 assert(oid);
bba25f39 422
0b28217b
CMN
423 if (!signature) {
424 if ((error = log_signature(&who, repo)) < 0)
425 return error;
426 else
427 signature = who;
428 }
429
430 error = reference__create(
bba25f39 431 ref_out, repo, name, oid, NULL, force, signature, log_message);
87d3acf4 432
0b28217b
CMN
433 git_signature_free(who);
434 return error;
d00d5464 435}
4dcd8780 436
0b28217b 437int git_reference_symbolic_create(
e5994eb0
AS
438 git_reference **ref_out,
439 git_repository *repo,
440 const char *name,
441 const char *target,
442 int force,
443 const git_signature *signature,
444 const char *log_message)
445{
0b28217b
CMN
446 int error;
447 git_signature *who = NULL;
448
449 assert(target);
450
451 if (!signature) {
452 if ((error = log_signature(&who, repo)) < 0)
453 return error;
454 else
455 signature = who;
456 }
e5994eb0 457
0b28217b 458 error = reference__create(
e5994eb0 459 ref_out, repo, name, NULL, target, force, signature, log_message);
0b28217b
CMN
460
461 git_signature_free(who);
462 return error;
e5994eb0
AS
463}
464
14ab0e10 465static int ensure_is_an_updatable_direct_reference(git_reference *ref)
466{
467 if (ref->type == GIT_REF_OID)
468 return 0;
9a53df7e 469
14ab0e10 470 giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
471 return -1;
d00d5464 472}
9462c471 473
d00d5464 474int git_reference_set_target(
14ab0e10 475 git_reference **out,
476 git_reference *ref,
477 const git_oid *id,
478 const git_signature *signature,
479 const char *log_message)
480{
481 int error;
482
483 assert(out && ref && id);
14ab0e10 484
485 if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
486 return error;
487
0b28217b 488 return git_reference_create(
14ab0e10 489 out, ref->db->repo, ref->name, id, 1, signature, log_message);
490}
491
ca84e058 492static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
493{
494 if (ref->type == GIT_REF_SYMBOLIC)
495 return 0;
496
497 giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference");
498 return -1;
499}
500
d00d5464
ET
501int git_reference_symbolic_set_target(
502 git_reference **out,
503 git_reference *ref,
0b28217b
CMN
504 const char *target,
505 const git_signature *signature,
506 const char *log_message)
87d3acf4 507{
ca84e058 508 int error;
509
d00d5464 510 assert(out && ref && target);
4dcd8780 511
ca84e058 512 if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
513 return error;
4dcd8780 514
0b28217b
CMN
515 return git_reference_symbolic_create(
516 out, ref->db->repo, ref->name, target, 1, signature, log_message);
87d3acf4
VM
517}
518
a57dd3b7
CMN
519static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
520 const git_signature *signature, const char *message)
7376ad99 521{
2e0c8816 522 unsigned int normalization_flags;
0ffcf78a 523 char normalized[GIT_REFNAME_MAX];
f3cc7834 524 bool should_head_be_updated = false;
d00d5464 525 int error = 0;
a57dd3b7
CMN
526
527 assert(ref && new_name && signature);
4dcd8780 528
d00d5464
ET
529 normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
530 GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
531
3be933b1 532 if ((error = git_reference_normalize_name(
4e6e2ff2 533 normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
d00d5464 534 return error;
7376ad99 535
a57dd3b7 536
d00d5464 537 /* Check if we have to update HEAD. */
10c06114 538 if ((error = git_branch_is_head(ref)) < 0)
4e6e2ff2 539 return error;
d00d5464 540
10c06114
AS
541 should_head_be_updated = (error > 0);
542
a57dd3b7 543 if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0)
4e6e2ff2 544 return error;
4dcd8780 545
96869a4e 546 /* Update HEAD it was pointing to the reference being renamed */
4e6e2ff2 547 if (should_head_be_updated &&
94f263f5 548 (error = git_repository_set_head(ref->db->repo, new_name, signature, message)) < 0) {
d00d5464 549 giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
4e6e2ff2 550 return error;
d4a0b124
VM
551 }
552
a57dd3b7
CMN
553 return 0;
554}
555
7376ad99 556
a57dd3b7
CMN
557int git_reference_rename(
558 git_reference **out,
559 git_reference *ref,
560 const char *new_name,
ccf6ce5c
BS
561 int force,
562 const git_signature *signature,
563 const char *log_message)
a57dd3b7 564{
ccf6ce5c 565 git_signature *who = (git_signature*)signature;
a57dd3b7 566 int error;
7376ad99 567
a57dd3b7 568 /* Should we return an error if there is no default? */
ccf6ce5c
BS
569 if (!who &&
570 ((error = git_signature_default(&who, ref->db->repo)) < 0) &&
a57dd3b7 571 ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) {
4e6e2ff2 572 return error;
a57dd3b7 573 }
64093ce5 574
ccf6ce5c 575 error = reference__rename(out, ref, new_name, force, who, log_message);
a57dd3b7 576
a1710a28
BS
577 if (!signature)
578 git_signature_free(who);
a57dd3b7
CMN
579
580 return error;
581}
582
2508cc66 583int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
87d3acf4 584{
3be933b1
VM
585 switch (git_reference_type(ref)) {
586 case GIT_REF_OID:
d00d5464 587 return git_reference_lookup(ref_out, ref->db->repo, ref->name);
4dcd8780 588
3be933b1
VM
589 case GIT_REF_SYMBOLIC:
590 return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
591
592 default:
593 giterr_set(GITERR_REFERENCE, "Invalid reference");
594 return -1;
595 }
87d3acf4
VM
596}
597
d4a0b124
VM
598int git_reference_foreach(
599 git_repository *repo,
eecc8050 600 git_reference_foreach_cb callback,
1a481123 601 void *payload)
00571828 602{
95727245 603 git_reference_iterator *iter;
ec24e542 604 git_reference *ref;
95727245
CMN
605 int error;
606
dab89f9b
RB
607 if ((error = git_reference_iterator_new(&iter, repo)) < 0)
608 return error;
00571828 609
f10d7a36
RB
610 while (!(error = git_reference_next(&ref, iter))) {
611 if ((error = callback(ref, payload)) != 0) {
26c1cb91 612 giterr_set_after_callback(error);
f10d7a36
RB
613 break;
614 }
615 }
ec24e542
VM
616
617 if (error == GIT_ITEROVER)
618 error = 0;
619
ec24e542
VM
620 git_reference_iterator_free(iter);
621 return error;
622}
623
624int git_reference_foreach_name(
625 git_repository *repo,
626 git_reference_foreach_name_cb callback,
627 void *payload)
628{
629 git_reference_iterator *iter;
630 const char *refname;
95727245
CMN
631 int error;
632
dab89f9b
RB
633 if ((error = git_reference_iterator_new(&iter, repo)) < 0)
634 return error;
00571828 635
f10d7a36
RB
636 while (!(error = git_reference_next_name(&refname, iter))) {
637 if ((error = callback(refname, payload)) != 0) {
26c1cb91 638 giterr_set_after_callback(error);
f10d7a36
RB
639 break;
640 }
641 }
ec24e542
VM
642
643 if (error == GIT_ITEROVER)
644 error = 0;
645
ec24e542
VM
646 git_reference_iterator_free(iter);
647 return error;
648}
649
650int git_reference_foreach_glob(
651 git_repository *repo,
652 const char *glob,
653 git_reference_foreach_name_cb callback,
654 void *payload)
655{
656 git_reference_iterator *iter;
657 const char *refname;
658 int error;
659
dab89f9b
RB
660 if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0)
661 return error;
ec24e542 662
f10d7a36
RB
663 while (!(error = git_reference_next_name(&refname, iter))) {
664 if ((error = callback(refname, payload)) != 0) {
26c1cb91 665 giterr_set_after_callback(error);
f10d7a36
RB
666 break;
667 }
668 }
95727245
CMN
669
670 if (error == GIT_ITEROVER)
671 error = 0;
672
95727245
CMN
673 git_reference_iterator_free(iter);
674 return error;
09e8de0f
VM
675}
676
4def7035
CMN
677int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
678{
679 git_refdb *refdb;
680
681 if (git_repository_refdb__weakptr(&refdb, repo) < 0)
682 return -1;
683
ec24e542 684 return git_refdb_iterator(out, refdb, NULL);
4def7035
CMN
685}
686
ec24e542
VM
687int git_reference_iterator_glob_new(
688 git_reference_iterator **out, git_repository *repo, const char *glob)
00571828 689{
d00d5464 690 git_refdb *refdb;
00571828 691
c58cac12
CMN
692 if (git_repository_refdb__weakptr(&refdb, repo) < 0)
693 return -1;
694
ec24e542 695 return git_refdb_iterator(out, refdb, glob);
c58cac12
CMN
696}
697
56960b83 698int git_reference_next(git_reference **out, git_reference_iterator *iter)
4def7035 699{
ec24e542
VM
700 return git_refdb_iterator_next(out, iter);
701}
702
703int git_reference_next_name(const char **out, git_reference_iterator *iter)
704{
705 return git_refdb_iterator_next_name(out, iter);
4def7035
CMN
706}
707
708void git_reference_iterator_free(git_reference_iterator *iter)
709{
2ad45213
BR
710 if (iter == NULL)
711 return;
712
4def7035 713 git_refdb_iterator_free(iter);
09e8de0f
VM
714}
715
d568d585 716static int cb__reflist_add(const char *ref, void *data)
09e8de0f 717{
25e0b157
RB
718 char *name = git__strdup(ref);
719 GITERR_CHECK_ALLOC(name);
720 return git_vector_insert((git_vector *)data, name);
09e8de0f
VM
721}
722
4fbd1c00 723int git_reference_list(
d4a0b124 724 git_strarray *array,
2b562c3a 725 git_repository *repo)
09e8de0f 726{
09e8de0f
VM
727 git_vector ref_list;
728
729 assert(array && repo);
730
731 array->strings = NULL;
732 array->count = 0;
733
0d0fa7c3 734 if (git_vector_init(&ref_list, 8, NULL) < 0)
45d387ac 735 return -1;
7ad96e51 736
ec24e542 737 if (git_reference_foreach_name(
2b562c3a 738 repo, &cb__reflist_add, (void *)&ref_list) < 0) {
09e8de0f 739 git_vector_free(&ref_list);
45d387ac 740 return -1;
7ad96e51
VM
741 }
742
25e0b157
RB
743 array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list);
744
1a481123 745 return 0;
00571828 746}
87d3acf4 747
d4a0b124 748static int is_valid_ref_char(char ch)
aa2120e9 749{
50a8fd03 750 if ((unsigned) ch <= ' ')
d4a0b124 751 return 0;
aa2120e9 752
753 switch (ch) {
754 case '~':
755 case '^':
756 case ':':
757 case '\\':
758 case '?':
759 case '[':
e1be1028 760 case '*':
d4a0b124 761 return 0;
aa2120e9 762 default:
d4a0b124 763 return 1;
aa2120e9 764 }
765}
766
c030ada7 767static int ensure_segment_validity(const char *name)
aa2120e9 768{
c030ada7 769 const char *current = name;
770 char prev = '\0';
0d1b094b
RB
771 const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
772 int segment_len;
aa2120e9 773
c030ada7 774 if (*current == '.')
775 return -1; /* Refname starts with "." */
aa2120e9 776
c030ada7 777 for (current = name; ; current++) {
778 if (*current == '\0' || *current == '/')
779 break;
2e0c8816 780
c030ada7 781 if (!is_valid_ref_char(*current))
782 return -1; /* Illegal character in refname */
aa2120e9 783
c030ada7 784 if (prev == '.' && *current == '.')
785 return -1; /* Refname contains ".." */
3101a3e5 786
c030ada7 787 if (prev == '@' && *current == '{')
788 return -1; /* Refname contains "@{" */
aa2120e9 789
c030ada7 790 prev = *current;
791 }
aa2120e9 792
0d1b094b
RB
793 segment_len = (int)(current - name);
794
4d811c3b 795 /* A refname component can not end with ".lock" */
0d1b094b 796 if (segment_len >= lock_len &&
2bca5b67 797 !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
4d811c3b 798 return -1;
799
0d1b094b 800 return segment_len;
c030ada7 801}
aa2120e9 802
7c411fd9 803static bool is_all_caps_and_underscore(const char *name, size_t len)
77e06d7e 804{
7c411fd9 805 size_t i;
77e06d7e 806 char c;
807
808 assert(name && len > 0);
809
810 for (i = 0; i < len; i++)
811 {
812 c = name[i];
813 if ((c < 'A' || c > 'Z') && c != '_')
814 return false;
815 }
816
817 if (*name == '_' || name[len - 1] == '_')
818 return false;
819
820 return true;
821}
822
92dac975 823/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
c030ada7 824int git_reference__normalize_name(
825 git_buf *buf,
826 const char *name,
827 unsigned int flags)
828{
c030ada7 829 char *current;
80d9d1df 830 int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
77e06d7e 831 unsigned int process_flags;
832 bool normalize = (buf != NULL);
0bfa7323
VM
833
834#ifdef GIT_USE_ICONV
92dac975 835 git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
0bfa7323 836#endif
92dac975 837
77e06d7e 838 assert(name);
c030ada7 839
77e06d7e 840 process_flags = flags;
c030ada7 841 current = (char *)name;
842
bb45c57f
CMN
843 if (*current == '/')
844 goto cleanup;
845
77e06d7e 846 if (normalize)
847 git_buf_clear(buf);
c030ada7 848
0bfa7323 849#ifdef GIT_USE_ICONV
92dac975
RB
850 if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
851 size_t namelen = strlen(current);
852 if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
853 (error = git_path_iconv(&ic, &current, &namelen)) < 0)
854 goto cleanup;
74353137 855 error = GIT_EINVALIDSPEC;
92dac975 856 }
0bfa7323 857#endif
92dac975 858
c030ada7 859 while (true) {
860 segment_len = ensure_segment_validity(current);
861 if (segment_len < 0) {
77e06d7e 862 if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
c030ada7 863 current[0] == '*' &&
864 (current[1] == '\0' || current[1] == '/')) {
865 /* Accept one wildcard as a full refname component. */
77e06d7e 866 process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
c030ada7 867 segment_len = 1;
868 } else
869 goto cleanup;
870 }
aa2120e9 871
c030ada7 872 if (segment_len > 0) {
77e06d7e 873 if (normalize) {
7c411fd9 874 size_t cur_len = git_buf_len(buf);
aa2120e9 875
77e06d7e 876 git_buf_joinpath(buf, git_buf_cstr(buf), current);
7c411fd9 877 git_buf_truncate(buf,
77e06d7e 878 cur_len + segment_len + (segments_count ? 1 : 0));
aa2120e9 879
80d9d1df 880 if (git_buf_oom(buf)) {
881 error = -1;
77e06d7e 882 goto cleanup;
80d9d1df 883 }
77e06d7e 884 }
aa2120e9 885
77e06d7e 886 segments_count++;
0844ed06 887 }
2e0c8816 888
2bca5b67 889 /* No empty segment is allowed when not normalizing */
890 if (segment_len == 0 && !normalize)
e5ef0f18 891 goto cleanup;
0d1b094b 892
c030ada7 893 if (current[segment_len] == '\0')
894 break;
aa2120e9 895
c030ada7 896 current += segment_len + 1;
2e0c8816 897 }
3101a3e5 898
c030ada7 899 /* A refname can not be empty */
77e06d7e 900 if (segment_len == 0 && segments_count == 0)
c030ada7 901 goto cleanup;
902
903 /* A refname can not end with "." */
904 if (current[segment_len - 1] == '.')
905 goto cleanup;
906
907 /* A refname can not end with "/" */
908 if (current[segment_len - 1] == '/')
909 goto cleanup;
910
77e06d7e 911 if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
912 goto cleanup;
913
914 if ((segments_count == 1 ) &&
528a4e24 915 !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) &&
7c411fd9 916 !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
77e06d7e 917 ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
918 goto cleanup;
919
920 if ((segments_count > 1)
921 && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
922 goto cleanup;
aa2120e9 923
c030ada7 924 error = 0;
aa2120e9 925
c030ada7 926cleanup:
80d9d1df 927 if (error == GIT_EINVALIDSPEC)
c030ada7 928 giterr_set(
929 GITERR_REFERENCE,
930 "The given reference name '%s' is not valid", name);
aa2120e9 931
3865f7f6
RB
932 if (error && normalize)
933 git_buf_free(buf);
934
0bfa7323 935#ifdef GIT_USE_ICONV
92dac975 936 git_path_iconv_clear(&ic);
0bfa7323 937#endif
92dac975 938
c030ada7 939 return error;
940}
1a481123 941
c030ada7 942int git_reference_normalize_name(
943 char *buffer_out,
944 size_t buffer_size,
945 const char *name,
946 unsigned int flags)
947{
948 git_buf buf = GIT_BUF_INIT;
949 int error;
950
951 if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
952 goto cleanup;
953
954 if (git_buf_len(&buf) > buffer_size - 1) {
955 giterr_set(
2e0c8816 956 GITERR_REFERENCE,
c030ada7 957 "The provided buffer is too short to hold the normalization of '%s'", name);
958 error = GIT_EBUFS;
959 goto cleanup;
960 }
961
962 git_buf_copy_cstr(buffer_out, buffer_size, &buf);
963
964 error = 0;
965
966cleanup:
967 git_buf_free(&buf);
968 return error;
aa2120e9 969}
2f8a8ab2 970
c030ada7 971int git_reference__normalize_name_lax(
d4a0b124
VM
972 char *buffer_out,
973 size_t out_size,
974 const char *name)
86194b24 975{
2e0c8816 976 return git_reference_normalize_name(
977 buffer_out,
978 out_size,
979 name,
980 GIT_REF_FORMAT_ALLOW_ONELEVEL);
86194b24 981}
f201d613
RB
982#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
983
984int git_reference_cmp(git_reference *ref1, git_reference *ref2)
985{
3be933b1 986 git_ref_t type1, type2;
f201d613
RB
987 assert(ref1 && ref2);
988
3be933b1
VM
989 type1 = git_reference_type(ref1);
990 type2 = git_reference_type(ref2);
991
f201d613 992 /* let's put symbolic refs before OIDs */
3be933b1
VM
993 if (type1 != type2)
994 return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1;
f201d613 995
3be933b1 996 if (type1 == GIT_REF_SYMBOLIC)
f201d613
RB
997 return strcmp(ref1->target.symbolic, ref2->target.symbolic);
998
b7f167da 999 return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
f201d613
RB
1000}
1001
d00d5464
ET
1002static int reference__update_terminal(
1003 git_repository *repo,
1004 const char *ref_name,
1005 const git_oid *oid,
0b28217b
CMN
1006 int nesting,
1007 const git_signature *signature,
1008 const char *log_message)
edebceff 1009{
1010 git_reference *ref;
d00d5464 1011 int error = 0;
edebceff 1012
41e93563
RB
1013 if (nesting > MAX_NESTING_LEVEL) {
1014 giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting);
d00d5464 1015 return GIT_ENOTFOUND;
41e93563 1016 }
4dcd8780 1017
d00d5464 1018 error = git_reference_lookup(&ref, repo, ref_name);
edebceff 1019
d00d5464
ET
1020 /* If we haven't found the reference at all, create a new reference. */
1021 if (error == GIT_ENOTFOUND) {
edebceff 1022 giterr_clear();
0b28217b 1023 return git_reference_create(NULL, repo, ref_name, oid, 0, signature, log_message);
edebceff 1024 }
4dcd8780 1025
d00d5464
ET
1026 if (error < 0)
1027 return error;
4dcd8780 1028
d00d5464 1029 /* If the ref is a symbolic reference, follow its target. */
edebceff 1030 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
d00d5464 1031 error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid,
0b28217b 1032 nesting+1, signature, log_message);
edebceff 1033 git_reference_free(ref);
d00d5464
ET
1034 } else {
1035 git_reference_free(ref);
0b28217b 1036 error = git_reference_create(NULL, repo, ref_name, oid, 1, signature, log_message);
edebceff 1037 }
4dcd8780 1038
d00d5464 1039 return error;
edebceff 1040}
527ed554 1041
d00d5464
ET
1042/*
1043 * Starting with the reference given by `ref_name`, follows symbolic
1044 * references until a direct reference is found and updated the OID
1045 * on that direct reference to `oid`.
1046 */
1047int git_reference__update_terminal(
1048 git_repository *repo,
1049 const char *ref_name,
0b28217b
CMN
1050 const git_oid *oid,
1051 const git_signature *signature,
1052 const char *log_message)
527ed554 1053{
0b28217b 1054 return reference__update_terminal(repo, ref_name, oid, 0, signature, log_message);
527ed554 1055}
1056
f2105129 1057int git_reference_has_log(git_repository *repo, const char *refname)
75261421 1058{
f2105129
CMN
1059 int error;
1060 git_refdb *refdb;
75261421 1061
f2105129 1062 assert(repo && refname);
75261421 1063
f2105129
CMN
1064 if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
1065 return error;
75261421 1066
f2105129 1067 return git_refdb_has_log(refdb, refname);
75261421 1068}
84f18e35 1069
8d5ec910
CMN
1070int git_reference_ensure_log(git_repository *repo, const char *refname)
1071{
1072 int error;
1073 git_refdb *refdb;
1074
1075 assert(repo && refname);
1076
1077 if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
1078 return error;
75261421 1079
8d5ec910 1080 return git_refdb_ensure_log(refdb, refname);
75261421 1081}
84f18e35 1082
bf031581 1083int git_reference__is_branch(const char *ref_name)
1084{
1085 return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
1086}
1087
88bcd515 1088int git_reference_is_branch(git_reference *ref)
1089{
1090 assert(ref);
bf031581 1091 return git_reference__is_branch(ref->name);
88bcd515 1092}
1c947daa 1093
c1b5e8c4 1094int git_reference__is_remote(const char *ref_name)
1095{
1096 return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
1097}
1098
1c947daa
VM
1099int git_reference_is_remote(git_reference *ref)
1100{
1101 assert(ref);
c1b5e8c4 1102 return git_reference__is_remote(ref->name);
1c947daa 1103}
31665948 1104
504850cd
NV
1105int git_reference__is_tag(const char *ref_name)
1106{
1107 return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
1108}
1109
1110int git_reference_is_tag(git_reference *ref)
1111{
1112 assert(ref);
1113 return git_reference__is_tag(ref->name);
1114}
1115
31665948 1116static int peel_error(int error, git_reference *ref, const char* msg)
1117{
1118 giterr_set(
1119 GITERR_INVALID,
1120 "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
1121 return error;
1122}
1123
31665948 1124int git_reference_peel(
92dac975
RB
1125 git_object **peeled,
1126 git_reference *ref,
1127 git_otype target_type)
31665948 1128{
1129 git_reference *resolved = NULL;
1130 git_object *target = NULL;
1131 int error;
1132
1133 assert(ref);
1134
3be933b1
VM
1135 if (ref->type == GIT_REF_OID) {
1136 resolved = ref;
1137 } else {
1138 if ((error = git_reference_resolve(&resolved, ref)) < 0)
1139 return peel_error(error, ref, "Cannot resolve reference");
1140 }
1141
fedd0f9e 1142 if (!git_oid_iszero(&resolved->peel)) {
3be933b1 1143 error = git_object_lookup(&target,
fedd0f9e 1144 git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
3be933b1
VM
1145 } else {
1146 error = git_object_lookup(&target,
fedd0f9e 1147 git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY);
3be933b1 1148 }
31665948 1149
3be933b1 1150 if (error < 0) {
31665948 1151 peel_error(error, ref, "Cannot retrieve reference target");
1152 goto cleanup;
1153 }
b90500f0 1154
31665948 1155 if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
575a54db 1156 error = git_object_dup(peeled, target);
b90500f0 1157 else
31665948 1158 error = git_object_peel(peeled, target, target_type);
1159
1160cleanup:
1161 git_object_free(target);
3be933b1
VM
1162
1163 if (resolved != ref)
1164 git_reference_free(resolved);
1165
31665948 1166 return error;
1167}
77e06d7e 1168
92dac975 1169int git_reference__is_valid_name(const char *refname, unsigned int flags)
0adfa20a 1170{
92dac975
RB
1171 if (git_reference__normalize_name(NULL, refname, flags) < 0) {
1172 giterr_clear();
1173 return false;
1174 }
83458bb7 1175
92dac975 1176 return true;
0adfa20a 1177}
1178
92dac975 1179int git_reference_is_valid_name(const char *refname)
77e06d7e 1180{
92dac975 1181 return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
77e06d7e 1182}
4f2eb2b7
CMN
1183
1184const char *git_reference_shorthand(git_reference *ref)
1185{
1186 const char *name = ref->name;
1187
1188 if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
1189 return name + strlen(GIT_REFS_HEADS_DIR);
1190 else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR))
1191 return name + strlen(GIT_REFS_TAGS_DIR);
1192 else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR))
1193 return name + strlen(GIT_REFS_REMOTES_DIR);
1194 else if (!git__prefixcmp(name, GIT_REFS_DIR))
1195 return name + strlen(GIT_REFS_DIR);
1196
1197 /* No shorthands are avaiable, so just return the name */
1198 return name;
1199}