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