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