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