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