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