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