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