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