]> git.proxmox.com Git - libgit2.git/blame - src/refs.c
tree: fix cast warnings
[libgit2.git] / src / refs.c
CommitLineData
9282e921 1/*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "refs.h"
27#include "hash.h"
28#include "repository.h"
29#include "fileops.h"
30
87d3acf4
VM
31#include <git2/tag.h>
32#include <git2/object.h>
33
9282e921 34#define MAX_NESTING_LEVEL 5
35
86194b24
VM
36typedef struct {
37 git_reference ref;
38 git_oid oid;
39 git_oid peel_target;
40} reference_oid;
41
42typedef struct {
43 git_reference ref;
44 char *target;
45} reference_symbolic;
46
9282e921 47static const int default_table_size = 32;
48
fc658755 49static uint32_t reftable_hash(const void *key, int hash_id)
9282e921 50{
fc658755
VM
51 static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
52 2147483647,
53 0x5d20bb23,
932d1baf 54 0x7daaab3c
fc658755 55 };
9282e921 56
fc658755 57 return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
9282e921 58}
59
87d3acf4
VM
60static void reference_free(git_reference *reference);
61static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type);
f79026b4 62static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name);
87d3acf4
VM
63
64/* loose refs */
f79026b4
VM
65static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
66static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content);
7341bf87 67static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic);
87d3acf4 68static int loose_write(git_reference *ref);
7341bf87 69static int loose_update(git_reference *ref);
87d3acf4
VM
70
71/* packed refs */
87d3acf4
VM
72static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end);
73static int packed_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end);
74static int packed_load(git_repository *repo);
75static int packed_loadloose(git_repository *repository);
76static int packed_write_ref(reference_oid *ref, git_filebuf *file);
77static int packed_find_peel(reference_oid *ref);
78static int packed_remove_loose(git_repository *repo, git_vector *packing_list);
79static int packed_sort(const void *a, const void *b);
80static int packed_write(git_repository *repo);
81
95cde17c 82/* internal helpers */
1b6d8163 83static int reference_available(git_repository *repo, const char *ref, const char *old_ref);
95cde17c 84
87d3acf4
VM
85/* name normalization */
86static int check_valid_ref_char(char ch);
3101a3e5 87static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref);
87d3acf4
VM
88
89/*****************************************
90 * Internal methods - Constructor/destructor
91 *****************************************/
2f8a8ab2 92static void reference_free(git_reference *reference)
9282e921 93{
2f8a8ab2
VM
94 if (reference == NULL)
95 return;
9282e921 96
2f8a8ab2
VM
97 if (reference->name)
98 free(reference->name);
9282e921 99
2f8a8ab2 100 if (reference->type == GIT_REF_SYMBOLIC)
86194b24 101 free(((reference_symbolic *)reference)->target);
9282e921 102
2f8a8ab2 103 free(reference);
9282e921 104}
105
87d3acf4
VM
106static int reference_create(
107 git_reference **ref_out,
108 git_repository *repo,
109 const char *name,
110 git_rtype type)
111{
3101a3e5 112 char normalized[GIT_REFNAME_MAX];
86194b24 113 int error = GIT_SUCCESS, size;
9282e921 114 git_reference *reference = NULL;
115
1d8cc731 116 assert(ref_out && repo && name);
117
86194b24
VM
118 if (type == GIT_REF_SYMBOLIC)
119 size = sizeof(reference_symbolic);
120 else if (type == GIT_REF_OID)
121 size = sizeof(reference_oid);
122 else
fa59f18d
VM
123 return git__throw(GIT_EINVALIDARGS,
124 "Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier");
9282e921 125
86194b24 126 reference = git__malloc(size);
9282e921 127 if (reference == NULL)
128 return GIT_ENOMEM;
129
86194b24 130 memset(reference, 0x0, size);
2f8a8ab2 131 reference->owner = repo;
1d8cc731 132 reference->type = type;
133
3101a3e5 134 error = normalize_name(normalized, sizeof(normalized), name, (type & GIT_REF_OID));
1d8cc731 135 if (error < GIT_SUCCESS)
136 goto cleanup;
137
138 reference->name = git__strdup(normalized);
139 if (reference->name == NULL) {
140 error = GIT_ENOMEM;
141 goto cleanup;
142 }
9282e921 143
2f8a8ab2 144 *ref_out = reference;
1d8cc731 145
5bdf7b9f 146 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference");
1d8cc731 147
148cleanup:
149 reference_free(reference);
5bdf7b9f 150 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference");
1d8cc731 151}
152
f79026b4 153static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name)
7341bf87
VM
154{
155 struct stat st;
156 char path[GIT_PATH_MAX];
157
158 /* Determine the full path of the file */
f79026b4 159 git_path_join(path, repo_path, ref_name);
7341bf87 160
f79026b4 161 if (p_stat(path, &st) < 0 || S_ISDIR(st.st_mode))
fa59f18d
VM
162 return git__throw(GIT_ENOTFOUND,
163 "Cannot read reference file '%s'", ref_name);
7341bf87
VM
164
165 if (mtime)
166 *mtime = st.st_mtime;
167
168 if (file_content)
f79026b4 169 return git_futils_readbuffer(file_content, path);
7341bf87
VM
170
171 return GIT_SUCCESS;
172}
173
1d8cc731 174
1d8cc731 175
9282e921 176
87d3acf4
VM
177/*****************************************
178 * Internal methods - Loose references
179 *****************************************/
7341bf87
VM
180static int loose_update(git_reference *ref)
181{
182 int error;
183 time_t ref_time;
f79026b4 184 git_fbuffer ref_file = GIT_FBUFFER_INIT;
7341bf87
VM
185
186 if (ref->type & GIT_REF_PACKED)
187 return packed_load(ref->owner);
188
189 error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name);
190 if (error < GIT_SUCCESS)
191 goto cleanup;
192
193 if (ref_time == ref->mtime)
194 return GIT_SUCCESS;
195
196 error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name);
197 if (error < GIT_SUCCESS)
198 goto cleanup;
199
200 if (ref->type == GIT_REF_SYMBOLIC)
201 error = loose_parse_symbolic(ref, &ref_file);
202 else if (ref->type == GIT_REF_OID)
203 error = loose_parse_oid(ref, &ref_file);
204 else
fa59f18d
VM
205 error = git__throw(GIT_EOBJCORRUPTED,
206 "Invalid reference type (%d) for loose reference", ref->type);
7341bf87 207
f79026b4 208 git_futils_freebuffer(&ref_file);
7341bf87
VM
209
210cleanup:
211 if (error != GIT_SUCCESS) {
212 reference_free(ref);
213 git_hashtable_remove(ref->owner->references.loose_cache, ref->name);
214 }
215
5bdf7b9f 216 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to update loose reference");
7341bf87
VM
217}
218
f79026b4 219static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
2f8a8ab2 220{
ddc9e79a 221 const unsigned int header_len = strlen(GIT_SYMREF);
2f8a8ab2
VM
222 const char *refname_start;
223 char *eol;
86194b24 224 reference_symbolic *ref_sym;
9282e921 225
2f8a8ab2 226 refname_start = (const char *)file_content->data;
86194b24 227 ref_sym = (reference_symbolic *)ref;
9282e921 228
2f8a8ab2 229 if (file_content->len < (header_len + 1))
fa59f18d
VM
230 return git__throw(GIT_EOBJCORRUPTED,
231 "Failed to parse loose reference. Object too short");
9282e921 232
932d1baf 233 /*
2f8a8ab2 234 * Assume we have already checked for the header
932d1baf 235 * before calling this function
2f8a8ab2 236 */
9282e921 237
2f8a8ab2 238 refname_start += header_len;
9282e921 239
7341bf87 240 free(ref_sym->target);
86194b24
VM
241 ref_sym->target = git__strdup(refname_start);
242 if (ref_sym->target == NULL)
32054c24 243 return GIT_ENOMEM;
9282e921 244
2f8a8ab2 245 /* remove newline at the end of file */
86194b24 246 eol = strchr(ref_sym->target, '\n');
ff5873ad 247 if (eol == NULL)
fa59f18d
VM
248 return git__throw(GIT_EOBJCORRUPTED,
249 "Failed to parse loose reference. Missing EOL");
ff5873ad
VM
250
251 *eol = '\0';
252 if (eol[-1] == '\r')
253 eol[-1] = '\0';
9282e921 254
2f8a8ab2 255 return GIT_SUCCESS;
9282e921 256}
257
f79026b4 258static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content)
2f8a8ab2 259{
fa59f18d 260 int error;
86194b24 261 reference_oid *ref_oid;
2f8a8ab2 262 char *buffer;
86194b24 263
2f8a8ab2 264 buffer = (char *)file_content->data;
86194b24 265 ref_oid = (reference_oid *)ref;
9282e921 266
2f8a8ab2 267 /* File format: 40 chars (OID) + newline */
ff5873ad 268 if (file_content->len < GIT_OID_HEXSZ + 1)
fa59f18d
VM
269 return git__throw(GIT_EOBJCORRUPTED,
270 "Failed to parse loose reference. Reference too short");
9282e921 271
fa48608e 272 if ((error = git_oid_fromstr(&ref_oid->oid, buffer)) < GIT_SUCCESS)
fa59f18d 273 return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
9282e921 274
ff5873ad
VM
275 buffer = buffer + GIT_OID_HEXSZ;
276 if (*buffer == '\r')
277 buffer++;
278
279 if (*buffer != '\n')
fa59f18d
VM
280 return git__throw(GIT_EOBJCORRUPTED,
281 "Failed to parse loose reference. Missing EOL");
9282e921 282
2f8a8ab2 283 return GIT_SUCCESS;
9282e921 284}
285
9282e921 286
00571828
VM
287static git_rtype loose_guess_rtype(const char *full_path)
288{
f79026b4 289 git_fbuffer ref_file = GIT_FBUFFER_INIT;
00571828
VM
290 git_rtype type;
291
292 type = GIT_REF_INVALID;
293
f79026b4 294 if (git_futils_readbuffer(&ref_file, full_path) == GIT_SUCCESS) {
00571828
VM
295 if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
296 type = GIT_REF_SYMBOLIC;
297 else
298 type = GIT_REF_OID;
299 }
300
f79026b4 301 git_futils_freebuffer(&ref_file);
00571828
VM
302 return type;
303}
304
87d3acf4 305static int loose_lookup(
932d1baf
KS
306 git_reference **ref_out,
307 git_repository *repo,
87d3acf4
VM
308 const char *name,
309 int skip_symbolic)
9282e921 310{
311 int error = GIT_SUCCESS;
f79026b4 312 git_fbuffer ref_file = GIT_FBUFFER_INIT;
2f8a8ab2 313 git_reference *ref = NULL;
7341bf87 314 time_t ref_time;
9282e921 315
2f8a8ab2 316 *ref_out = NULL;
9282e921 317
7341bf87 318 error = reference_read(&ref_file, &ref_time, repo->path_repository, name);
9282e921 319 if (error < GIT_SUCCESS)
320 goto cleanup;
321
1d8cc731 322 if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
87d3acf4
VM
323 if (skip_symbolic)
324 return GIT_SUCCESS;
325
32054c24 326 error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
1d8cc731 327 if (error < GIT_SUCCESS)
328 goto cleanup;
9282e921 329
87d3acf4 330 error = loose_parse_symbolic(ref, &ref_file);
1d8cc731 331 } else {
32054c24 332 error = reference_create(&ref, repo, name, GIT_REF_OID);
1d8cc731 333 if (error < GIT_SUCCESS)
334 goto cleanup;
335
87d3acf4 336 error = loose_parse_oid(ref, &ref_file);
1d8cc731 337 }
338
ff5873ad
VM
339 if (error < GIT_SUCCESS)
340 goto cleanup;
341
7341bf87 342 ref->mtime = ref_time;
2f8a8ab2 343 *ref_out = ref;
f79026b4 344 git_futils_freebuffer(&ref_file);
2f8a8ab2 345 return GIT_SUCCESS;
9282e921 346
347cleanup:
f79026b4 348 git_futils_freebuffer(&ref_file);
2f8a8ab2 349 reference_free(ref);
5bdf7b9f 350 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to lookup loose reference");
9282e921 351}
352
87d3acf4
VM
353static int loose_write(git_reference *ref)
354{
355 git_filebuf file;
356 char ref_path[GIT_PATH_MAX];
e7e0e20f 357 int error;
7341bf87 358 struct stat st;
87d3acf4 359
f79026b4 360 git_path_join(ref_path, ref->owner->path_repository, ref->name);
87d3acf4 361
55ffebe3 362 if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS)
5bdf7b9f 363 return git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
364
365 if (ref->type & GIT_REF_OID) {
366 reference_oid *ref_oid = (reference_oid *)ref;
e7e0e20f 367 char oid[GIT_OID_HEXSZ + 1];
87d3acf4 368
e7e0e20f 369 memset(oid, 0x0, sizeof(oid));
87d3acf4 370
e7e0e20f
CMN
371 git_oid_fmt(oid, &ref_oid->oid);
372 error = git_filebuf_printf(&file, "%s\n", oid);
373 if (error < GIT_SUCCESS)
374 goto unlock;
87d3acf4
VM
375
376 } else if (ref->type & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */
377 reference_symbolic *ref_sym = (reference_symbolic *)ref;
378
e7e0e20f 379 error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref_sym->target);
87d3acf4 380 } else {
fa59f18d 381 error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type");
87d3acf4
VM
382 goto unlock;
383 }
384
87d3acf4
VM
385 error = git_filebuf_commit(&file);
386
f79026b4 387 if (p_stat(ref_path, &st) == GIT_SUCCESS)
7341bf87
VM
388 ref->mtime = st.st_mtime;
389
5bdf7b9f 390 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
391
392unlock:
393 git_filebuf_cleanup(&file);
5bdf7b9f 394 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
395}
396
397
398
399
400
2f8a8ab2 401
87d3acf4
VM
402/*****************************************
403 * Internal methods - Packed references
404 *****************************************/
9282e921 405
87d3acf4 406static int packed_parse_peel(
86194b24 407 reference_oid *tag_ref,
932d1baf 408 const char **buffer_out,
2f8a8ab2 409 const char *buffer_end)
9282e921 410{
2f8a8ab2
VM
411 const char *buffer = *buffer_out + 1;
412
413 assert(buffer[-1] == '^');
9282e921 414
415 /* Ensure it's not the first entry of the file */
2f8a8ab2 416 if (tag_ref == NULL)
5bdf7b9f 417 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is the first entry of the file");
9282e921 418
419 /* Ensure reference is a tag */
86194b24 420 if (git__prefixcmp(tag_ref->ref.name, GIT_REFS_TAGS_DIR) != 0)
5bdf7b9f 421 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is not a tag");
9282e921 422
2f8a8ab2 423 if (buffer + GIT_OID_HEXSZ >= buffer_end)
5bdf7b9f 424 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer too small");
9282e921 425
2f8a8ab2 426 /* Is this a valid object id? */
fa48608e 427 if (git_oid_fromstr(&tag_ref->peel_target, buffer) < GIT_SUCCESS)
5bdf7b9f 428 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Not a valid object ID");
2f8a8ab2 429
ff5873ad
VM
430 buffer = buffer + GIT_OID_HEXSZ;
431 if (*buffer == '\r')
432 buffer++;
433
434 if (*buffer != '\n')
5bdf7b9f 435 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer not terminated correctly");
ff5873ad
VM
436
437 *buffer_out = buffer + 1;
86194b24 438 tag_ref->ref.type |= GIT_REF_HAS_PEEL;
2f8a8ab2 439
2f8a8ab2 440 return GIT_SUCCESS;
9282e921 441}
442
87d3acf4 443static int packed_parse_oid(
86194b24 444 reference_oid **ref_out,
2f8a8ab2
VM
445 git_repository *repo,
446 const char **buffer_out,
447 const char *buffer_end)
9282e921 448{
e06dd9b6 449 reference_oid *ref = NULL;
2f8a8ab2
VM
450
451 const char *buffer = *buffer_out;
452 const char *refname_begin, *refname_end;
453
9282e921 454 int error = GIT_SUCCESS;
9282e921 455 int refname_len;
3101a3e5 456 char refname[GIT_REFNAME_MAX];
1d8cc731 457 git_oid id;
9282e921 458
2f8a8ab2
VM
459 refname_begin = (buffer + GIT_OID_HEXSZ + 1);
460 if (refname_begin >= buffer_end ||
461 refname_begin[-1] != ' ') {
462 error = GIT_EPACKEDREFSCORRUPTED;
463 goto cleanup;
464 }
9282e921 465
2f8a8ab2 466 /* Is this a valid object id? */
fa48608e 467 if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS)
2f8a8ab2 468 goto cleanup;
9282e921 469
2f8a8ab2
VM
470 refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
471 if (refname_end == NULL) {
472 error = GIT_EPACKEDREFSCORRUPTED;
473 goto cleanup;
474 }
9282e921 475
2f8a8ab2 476 refname_len = refname_end - refname_begin;
9282e921 477
1d8cc731 478 memcpy(refname, refname_begin, refname_len);
479 refname[refname_len] = 0;
9282e921 480
1d8cc731 481 if (refname[refname_len - 1] == '\r')
482 refname[refname_len - 1] = 0;
9282e921 483
86194b24 484 error = reference_create((git_reference **)&ref, repo, refname, GIT_REF_OID);
1d8cc731 485 if (error < GIT_SUCCESS)
486 goto cleanup;
487
86194b24
VM
488 git_oid_cpy(&ref->oid, &id);
489 ref->ref.type |= GIT_REF_PACKED;
9282e921 490
2f8a8ab2 491 *ref_out = ref;
9282e921 492 *buffer_out = refname_end + 1;
493
2f8a8ab2
VM
494 return GIT_SUCCESS;
495
496cleanup:
86194b24 497 reference_free((git_reference *)ref);
5bdf7b9f 498 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse OID of packed reference");
9282e921 499}
500
87d3acf4 501static int packed_load(git_repository *repo)
9282e921 502{
503 int error = GIT_SUCCESS;
f79026b4 504 git_fbuffer packfile = GIT_FBUFFER_INIT;
2f8a8ab2 505 const char *buffer_start, *buffer_end;
87d3acf4
VM
506 git_refcache *ref_cache = &repo->references;
507
508 /* already loaded */
7341bf87
VM
509 if (repo->references.packfile != NULL) {
510 time_t packed_time;
87d3acf4 511
7341bf87
VM
512 /* check if we can read the time of the index;
513 * if we can read it and it matches the time of the
514 * index we had previously loaded, we don't need to do
515 * anything else.
516 *
517 * if we cannot load the time (e.g. the packfile
518 * has disappeared) or the time is different, we
519 * have to reload the packfile */
87d3acf4 520
7341bf87
VM
521 if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) &&
522 packed_time == ref_cache->packfile_time)
523 return GIT_SUCCESS;
87d3acf4 524
7341bf87
VM
525 git_hashtable_clear(repo->references.packfile);
526 } else {
527 ref_cache->packfile = git_hashtable_alloc(
932d1baf 528 default_table_size,
7341bf87
VM
529 reftable_hash,
530 (git_hash_keyeq_ptr)strcmp);
531
532 if (ref_cache->packfile == NULL)
533 return GIT_ENOMEM;
534 }
535
536 /* read the packfile from disk;
537 * store its modification time to check for future reloads */
538 error = reference_read(
539 &packfile,
540 &ref_cache->packfile_time,
541 repo->path_repository,
542 GIT_PACKEDREFS_FILE);
87d3acf4
VM
543
544 /* there is no packfile on disk; that's ok */
545 if (error == GIT_ENOTFOUND)
546 return GIT_SUCCESS;
2f8a8ab2 547
9282e921 548 if (error < GIT_SUCCESS)
549 goto cleanup;
550
2f8a8ab2
VM
551 buffer_start = (const char *)packfile.data;
552 buffer_end = (const char *)(buffer_start) + packfile.len;
9282e921 553
7c8a7b91
VM
554 while (buffer_start < buffer_end && buffer_start[0] == '#') {
555 buffer_start = strchr(buffer_start, '\n');
556 if (buffer_start == NULL) {
557 error = GIT_EPACKEDREFSCORRUPTED;
558 goto cleanup;
559 }
ddc9e79a 560 buffer_start++;
7c8a7b91 561 }
ddc9e79a 562
9282e921 563 while (buffer_start < buffer_end) {
86194b24 564 reference_oid *ref = NULL;
2f8a8ab2 565
87d3acf4 566 error = packed_parse_oid(&ref, repo, &buffer_start, buffer_end);
2f8a8ab2
VM
567 if (error < GIT_SUCCESS)
568 goto cleanup;
569
9282e921 570 if (buffer_start[0] == '^') {
87d3acf4 571 error = packed_parse_peel(ref, &buffer_start, buffer_end);
9282e921 572 if (error < GIT_SUCCESS)
573 goto cleanup;
2f8a8ab2 574 }
9282e921 575
87d3acf4 576 error = git_hashtable_insert(ref_cache->packfile, ref->ref.name, ref);
2f8a8ab2 577 if (error < GIT_SUCCESS) {
86194b24 578 reference_free((git_reference *)ref);
9282e921 579 goto cleanup;
2f8a8ab2
VM
580 }
581 }
9282e921 582
f79026b4 583 git_futils_freebuffer(&packfile);
7341bf87
VM
584 return GIT_SUCCESS;
585
2f8a8ab2 586cleanup:
7341bf87
VM
587 git_hashtable_free(ref_cache->packfile);
588 ref_cache->packfile = NULL;
f79026b4 589 git_futils_freebuffer(&packfile);
5bdf7b9f 590 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load packed references");
2f8a8ab2 591}
9282e921 592
00571828
VM
593
594
595
596struct dirent_list_data {
7ad96e51 597 git_repository *repo;
00571828
VM
598 size_t repo_path_len;
599 unsigned int list_flags;
09e8de0f
VM
600
601 int (*callback)(const char *, void *);
602 void *callback_payload;
00571828
VM
603};
604
605static int _dirent_loose_listall(void *_data, char *full_path)
606{
607 struct dirent_list_data *data = (struct dirent_list_data *)_data;
7ad96e51 608 char *file_path = full_path + data->repo_path_len;
00571828 609
f79026b4
VM
610 if (git_futils_isdir(full_path) == GIT_SUCCESS)
611 return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data);
00571828 612
7ad96e51 613 /* do not add twice a reference that exists already in the packfile */
b5abb881
VM
614 if ((data->list_flags & GIT_REF_PACKED) != 0 &&
615 git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
7ad96e51
VM
616 return GIT_SUCCESS;
617
09e8de0f
VM
618 if (data->list_flags != GIT_REF_LISTALL) {
619 if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
620 return GIT_SUCCESS; /* we are filtering out this reference */
621 }
00571828 622
09e8de0f 623 return data->callback(file_path, data->callback_payload);
00571828
VM
624}
625
87d3acf4 626static int _dirent_loose_load(void *data, char *full_path)
2f8a8ab2 627{
87d3acf4
VM
628 git_repository *repository = (git_repository *)data;
629 git_reference *reference, *old_ref;
630 char *file_path;
631 int error;
86194b24 632
f79026b4
VM
633 if (git_futils_isdir(full_path) == GIT_SUCCESS)
634 return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_load, repository);
9282e921 635
87d3acf4
VM
636 file_path = full_path + strlen(repository->path_repository);
637 error = loose_lookup(&reference, repository, file_path, 1);
638 if (error == GIT_SUCCESS && reference != NULL) {
639 reference->type |= GIT_REF_PACKED;
86194b24 640
87d3acf4
VM
641 if (git_hashtable_insert2(repository->references.packfile, reference->name, reference, (void **)&old_ref) < GIT_SUCCESS) {
642 reference_free(reference);
643 return GIT_ENOMEM;
644 }
645
646 if (old_ref != NULL)
647 reference_free(old_ref);
648 }
9282e921 649
5bdf7b9f 650 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load loose dirent");
2f8a8ab2
VM
651}
652
87d3acf4
VM
653/*
654 * Load all the loose references from the repository
655 * into the in-memory Packfile, and build a vector with
656 * all the references so it can be written back to
657 * disk.
658 */
659static int packed_loadloose(git_repository *repository)
2f8a8ab2 660{
87d3acf4 661 char refs_path[GIT_PATH_MAX];
86194b24 662
87d3acf4
VM
663 /* the packfile must have been previously loaded! */
664 assert(repository->references.packfile);
9282e921 665
f79026b4 666 git_path_join(refs_path, repository->path_repository, GIT_REFS_DIR);
86194b24 667
87d3acf4
VM
668 /* Remove any loose references from the cache */
669 {
402a47a7 670 const void *GIT_UNUSED(_unused);
87d3acf4 671 git_reference *reference;
9282e921 672
87d3acf4
VM
673 GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference,
674 reference_free(reference);
675 );
676 }
677
678 git_hashtable_clear(repository->references.loose_cache);
679
680 /*
681 * Load all the loose files from disk into the Packfile table.
682 * This will overwrite any old packed entries with their
932d1baf 683 * updated loose versions
87d3acf4 684 */
f79026b4 685 return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_load, repository);
2f8a8ab2 686}
9282e921 687
87d3acf4
VM
688/*
689 * Write a single reference into a packfile
690 */
691static int packed_write_ref(reference_oid *ref, git_filebuf *file)
2f8a8ab2 692{
87d3acf4
VM
693 int error;
694 char oid[GIT_OID_HEXSZ + 1];
2f8a8ab2 695
87d3acf4
VM
696 git_oid_fmt(oid, &ref->oid);
697 oid[GIT_OID_HEXSZ] = 0;
2f8a8ab2 698
932d1baf 699 /*
87d3acf4
VM
700 * For references that peel to an object in the repo, we must
701 * write the resulting peel on a separate line, e.g.
702 *
703 * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
704 * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
705 *
706 * This obviously only applies to tags.
707 * The required peels have already been loaded into `ref->peel_target`.
708 */
709 if (ref->ref.type & GIT_REF_HAS_PEEL) {
710 char peel[GIT_OID_HEXSZ + 1];
711 git_oid_fmt(peel, &ref->peel_target);
712 peel[GIT_OID_HEXSZ] = 0;
713
714 error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->ref.name, peel);
715 } else {
716 error = git_filebuf_printf(file, "%s %s\n", oid, ref->ref.name);
717 }
718
5bdf7b9f 719 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference");
9282e921 720}
721
87d3acf4
VM
722/*
723 * Find out what object this reference resolves to.
724 *
932d1baf 725 * For references that point to a 'big' tag (e.g. an
87d3acf4
VM
726 * actual tag object on the repository), we need to
727 * cache on the packfile the OID of the object to
728 * which that 'big tag' is pointing to.
729 */
730static int packed_find_peel(reference_oid *ref)
9282e921 731{
d79f1da6 732 git_object *object;
87d3acf4 733 int error;
32054c24 734
87d3acf4
VM
735 if (ref->ref.type & GIT_REF_HAS_PEEL)
736 return GIT_SUCCESS;
9282e921 737
87d3acf4
VM
738 /*
739 * Only applies to tags, i.e. references
740 * in the /refs/tags folder
741 */
742 if (git__prefixcmp(ref->ref.name, GIT_REFS_TAGS_DIR) != 0)
743 return GIT_SUCCESS;
9282e921 744
87d3acf4 745 /*
d79f1da6 746 * Find the tagged object in the repository
87d3acf4 747 */
d79f1da6 748 error = git_object_lookup(&object, ref->ref.owner, &ref->oid, GIT_OBJ_ANY);
87d3acf4 749 if (error < GIT_SUCCESS)
5bdf7b9f 750 return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference");
86194b24 751
87d3acf4 752 /*
d79f1da6
VM
753 * If the tagged object is a Tag object, we need to resolve it;
754 * if the ref is actually a 'weak' ref, we don't need to resolve
755 * anything.
87d3acf4 756 */
d79f1da6
VM
757 if (git_object_type(object) == GIT_OBJ_TAG) {
758 git_tag *tag = (git_tag *)object;
86194b24 759
d79f1da6
VM
760 /*
761 * Find the object pointed at by this tag
762 */
763 git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag));
764 ref->ref.type |= GIT_REF_HAS_PEEL;
765
766 /*
767 * The reference has now cached the resolved OID, and is
768 * marked at such. When written to the packfile, it'll be
769 * accompanied by this resolved oid
770 */
771 }
87d3acf4 772
1648fbd3
VM
773 git_object_close(object);
774
87d3acf4 775 return GIT_SUCCESS;
2f8a8ab2 776}
9282e921 777
87d3acf4
VM
778/*
779 * Remove all loose references
780 *
781 * Once we have successfully written a packfile,
782 * all the loose references that were packed must be
783 * removed from disk.
784 *
785 * This is a dangerous method; make sure the packfile
786 * is well-written, because we are destructing references
787 * here otherwise.
788 */
789static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
2f8a8ab2 790{
87d3acf4
VM
791 unsigned int i;
792 char full_path[GIT_PATH_MAX];
793 int error = GIT_SUCCESS;
8f90ced5 794 git_reference *reference;
87d3acf4
VM
795
796 for (i = 0; i < packing_list->length; ++i) {
797 git_reference *ref = git_vector_get(packing_list, i);
8f90ced5 798
799 /* Ensure the packed reference doesn't exist
800 * in a (more up-to-date?) state as a loose reference
801 */
802 reference = git_hashtable_lookup(ref->owner->references.loose_cache, ref->name);
803 if (reference != NULL)
804 continue;
805
f79026b4 806 git_path_join(full_path, repo->path_repository, ref->name);
87d3acf4 807
f79026b4
VM
808 if (git_futils_exists(full_path) == GIT_SUCCESS &&
809 p_unlink(full_path) < GIT_SUCCESS)
87d3acf4
VM
810 error = GIT_EOSERR;
811
812 /*
813 * if we fail to remove a single file, this is *not* good,
814 * but we should keep going and remove as many as possible.
815 * After we've removed as many files as possible, we return
816 * the error code anyway.
817 *
818 * TODO: mark this with a very special error code?
819 * GIT_EFAILTORMLOOSE
820 */
821 }
822
5bdf7b9f 823 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to remove loose packed reference");
2f8a8ab2 824}
9282e921 825
87d3acf4 826static int packed_sort(const void *a, const void *b)
2f8a8ab2 827{
de18f276
VM
828 const git_reference *ref_a = (const git_reference *)a;
829 const git_reference *ref_b = (const git_reference *)b;
87d3acf4
VM
830
831 return strcmp(ref_a->name, ref_b->name);
2f8a8ab2
VM
832}
833
87d3acf4
VM
834/*
835 * Write all the contents in the in-memory packfile to disk.
836 */
837static int packed_write(git_repository *repo)
2f8a8ab2 838{
87d3acf4
VM
839 git_filebuf pack_file;
840 int error;
841 unsigned int i;
842 char pack_file_path[GIT_PATH_MAX];
2f8a8ab2 843
87d3acf4
VM
844 git_vector packing_list;
845 size_t total_refs;
2f8a8ab2 846
87d3acf4 847 assert(repo && repo->references.packfile);
9282e921 848
87d3acf4 849 total_refs = repo->references.packfile->key_count;
86d7e1ca 850 if ((error = git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
5bdf7b9f 851 return git__rethrow(error, "Failed to write packed reference");
9282e921 852
87d3acf4
VM
853 /* Load all the packfile into a vector */
854 {
855 git_reference *reference;
402a47a7 856 const void *GIT_UNUSED(_unused);
87d3acf4
VM
857
858 GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
859 git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */
860 );
9282e921 861 }
862
87d3acf4
VM
863 /* sort the vector so the entries appear sorted on the packfile */
864 git_vector_sort(&packing_list);
9282e921 865
87d3acf4 866 /* Now we can open the file! */
f79026b4 867 git_path_join(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
87d3acf4 868 if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS)
5bdf7b9f 869 return git__rethrow(error, "Failed to write packed reference");
9282e921 870
7c8a7b91
VM
871 /* Packfiles have a header... apparently
872 * This is in fact not required, but we might as well print it
873 * just for kicks */
87d3acf4 874 if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS)
5bdf7b9f 875 return git__rethrow(error, "Failed to write packed reference");
2f8a8ab2 876
87d3acf4
VM
877 for (i = 0; i < packing_list.length; ++i) {
878 reference_oid *ref = (reference_oid *)git_vector_get(&packing_list, i);
2f8a8ab2 879
87d3acf4
VM
880 /* only direct references go to the packfile; otherwise
881 * this is a disaster */
882 assert(ref->ref.type & GIT_REF_OID);
2f8a8ab2 883
2b397327
VM
884 if ((error = packed_find_peel(ref)) < GIT_SUCCESS) {
885 error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled");
87d3acf4 886 goto cleanup;
2b397327
VM
887 }
888
2f8a8ab2 889
87d3acf4
VM
890 if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
891 goto cleanup;
892 }
2f8a8ab2 893
87d3acf4
VM
894cleanup:
895 /* if we've written all the references properly, we can commit
896 * the packfile to make the changes effective */
897 if (error == GIT_SUCCESS) {
898 error = git_filebuf_commit(&pack_file);
899
900 /* when and only when the packfile has been properly written,
901 * we can go ahead and remove the loose refs */
7341bf87
VM
902 if (error == GIT_SUCCESS) {
903 struct stat st;
904
87d3acf4 905 error = packed_remove_loose(repo, &packing_list);
7341bf87 906
f79026b4 907 if (p_stat(pack_file_path, &st) == GIT_SUCCESS)
7341bf87
VM
908 repo->references.packfile_time = st.st_mtime;
909 }
9282e921 910 }
87d3acf4 911 else git_filebuf_cleanup(&pack_file);
9282e921 912
87d3acf4 913 git_vector_free(&packing_list);
1d8cc731 914
5bdf7b9f 915 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference");
87d3acf4 916}
2f8a8ab2 917
1b6d8163
MS
918static int _reference_available_cb(const char *ref, void *data)
919{
f120e92b 920 const char *new, *old;
921 git_vector *refs;
922
1b6d8163
MS
923 assert(ref && data);
924
f120e92b 925 refs = (git_vector *)data;
1b6d8163 926
f120e92b 927 new = (const char *)git_vector_get(refs, 0);
928 old = (const char *)git_vector_get(refs, 1);
1b6d8163
MS
929
930 if (!old || strcmp(old, ref)) {
931 int reflen = strlen(ref);
932 int newlen = strlen(new);
933 int cmplen = reflen < newlen ? reflen : newlen;
934 const char *lead = reflen < newlen ? new : ref;
935
936 if (!strncmp(new, ref, cmplen) &&
937 lead[cmplen] == '/')
938 return GIT_EEXISTS;
939 }
940
941 return GIT_SUCCESS;
942}
943
944static int reference_available(git_repository *repo, const char *ref, const char* old_ref)
945{
946 int error;
947 git_vector refs;
948
949 if (git_vector_init(&refs, 2, NULL) < GIT_SUCCESS)
950 return GIT_ENOMEM;
951
952 git_vector_insert(&refs, (void *)ref);
953 git_vector_insert(&refs, (void *)old_ref);
954
43521d06 955 error = git_reference_foreach(repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&refs);
1b6d8163
MS
956
957 git_vector_free(&refs);
958
76b15cb1 959 return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref);
1b6d8163
MS
960}
961
87d3acf4
VM
962/*****************************************
963 * External Library API
964 *****************************************/
965
966/**
967 * Constructors
968 */
5de079b8 969int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name)
2f8a8ab2
VM
970{
971 int error;
3101a3e5 972 char normalized_name[GIT_REFNAME_MAX];
2f8a8ab2
VM
973
974 assert(ref_out && repo && name);
975
976 *ref_out = NULL;
977
3101a3e5 978 error = normalize_name(normalized_name, sizeof(normalized_name), name, 0);
2f8a8ab2 979 if (error < GIT_SUCCESS)
5bdf7b9f 980 return git__rethrow(error, "Failed to lookup reference");
2f8a8ab2 981
87d3acf4
VM
982 /* First, check has been previously loaded and cached */
983 *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name);
2f8a8ab2 984 if (*ref_out != NULL)
7341bf87 985 return loose_update(*ref_out);
2f8a8ab2 986
87d3acf4
VM
987 /* Then check if there is a loose file for that reference.*/
988 error = loose_lookup(ref_out, repo, normalized_name, 0);
2f8a8ab2 989
87d3acf4 990 /* If the file exists, we store it on the cache */
2f8a8ab2 991 if (error == GIT_SUCCESS)
87d3acf4 992 return git_hashtable_insert(repo->references.loose_cache, (*ref_out)->name, (*ref_out));
2f8a8ab2 993
87d3acf4
VM
994 /* The loose lookup has failed, but not because the reference wasn't found;
995 * probably the loose reference is corrupted. this is bad. */
9282e921 996 if (error != GIT_ENOTFOUND)
5bdf7b9f 997 return git__rethrow(error, "Failed to lookup reference");
9282e921 998
87d3acf4
VM
999 /*
1000 * If we cannot find a loose reference, we look into the packfile
932d1baf 1001 * Load the packfile first if it hasn't been loaded
87d3acf4 1002 */
7341bf87
VM
1003 /* load all the packed references */
1004 error = packed_load(repo);
1005 if (error < GIT_SUCCESS)
5bdf7b9f 1006 return git__rethrow(error, "Failed to lookup reference");
9282e921 1007
87d3acf4
VM
1008 /* Look up on the packfile */
1009 *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name);
86194b24
VM
1010 if (*ref_out != NULL)
1011 return GIT_SUCCESS;
1012
2f8a8ab2 1013 /* The reference doesn't exist anywhere */
5bdf7b9f 1014 return git__throw(GIT_ENOTFOUND, "Failed to lookup reference. Reference doesn't exist");
2f8a8ab2 1015}
9282e921 1016
87d3acf4
VM
1017/**
1018 * Getters
1019 */
1020git_rtype git_reference_type(git_reference *ref)
1021{
1022 assert(ref);
1023
1024 if (ref->type & GIT_REF_OID)
1025 return GIT_REF_OID;
1026
1027 if (ref->type & GIT_REF_SYMBOLIC)
1028 return GIT_REF_SYMBOLIC;
1029
1030 return GIT_REF_INVALID;
1031}
1032
1033const char *git_reference_name(git_reference *ref)
1034{
1035 assert(ref);
1036 return ref->name;
1037}
1038
1039git_repository *git_reference_owner(git_reference *ref)
1040{
1041 assert(ref);
1042 return ref->owner;
1043}
1044
1045const git_oid *git_reference_oid(git_reference *ref)
1046{
1047 assert(ref);
1048
1049 if ((ref->type & GIT_REF_OID) == 0)
1050 return NULL;
1051
7341bf87
VM
1052 if (loose_update(ref) < GIT_SUCCESS)
1053 return NULL;
1054
87d3acf4
VM
1055 return &((reference_oid *)ref)->oid;
1056}
1057
1058const char *git_reference_target(git_reference *ref)
1059{
1060 assert(ref);
1061
1062 if ((ref->type & GIT_REF_SYMBOLIC) == 0)
1063 return NULL;
1064
7341bf87
VM
1065 if (loose_update(ref) < GIT_SUCCESS)
1066 return NULL;
1067
87d3acf4
VM
1068 return ((reference_symbolic *)ref)->target;
1069}
1070
d5afc039
VM
1071int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force)
1072{
1073 char normalized[GIT_REFNAME_MAX];
1074 int error = GIT_SUCCESS, updated = 0;
1075 git_reference *ref = NULL, *old_ref = NULL;
1076
1077 if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force)
1078 return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists");
1079
1080 /*
1081 * If they old ref was of the same type, then we can just update
1082 * it (once we've checked that the target is valid). Otherwise we
1083 * need a new reference because we can't make a symbolic ref out
1084 * of an oid one.
1085 * If if didn't exist, then we need to create a new one anyway.
1086 */
1087 if (ref && ref->type & GIT_REF_SYMBOLIC){
1088 updated = 1;
1089 } else {
1090 ref = NULL;
1091 error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
1092 if (error < GIT_SUCCESS)
1093 goto cleanup;
1094 }
1095
1096 /* The target can aither be the name of an object id reference or the name of another symbolic reference */
1097 error = normalize_name(normalized, sizeof(normalized), target, 0);
1098 if (error < GIT_SUCCESS)
1099 goto cleanup;
1100
1101 /* set the target; this will write the reference on disk */
1102 error = git_reference_set_target(ref, normalized);
1103 if (error < GIT_SUCCESS)
1104 goto cleanup;
1105
1106 /*
1107 * If we didn't update the ref, then we need to insert or replace
1108 * it in the loose cache. If we replaced a ref, free it.
1109 */
1110 if (!updated){
1111 error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref);
1112 if (error < GIT_SUCCESS)
1113 goto cleanup;
1114
1115 if(old_ref)
1116 reference_free(old_ref);
1117 }
1118
1119 *ref_out = ref;
1120
1121 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference");
1122
1123cleanup:
1124 reference_free(ref);
1125 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference");
1126}
1127
1128int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force)
1129{
1130 int error = GIT_SUCCESS, updated = 0;
1131 git_reference *ref = NULL, *old_ref = NULL;
1132
1133 if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force)
1134 return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists");
1135
1136 if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
1137 return git__rethrow(error, "Failed to create reference");
1138
1139 /*
1140 * If they old ref was of the same type, then we can just update
1141 * it (once we've checked that the target is valid). Otherwise we
1142 * need a new reference because we can't make a symbolic ref out
1143 * of an oid one.
1144 * If if didn't exist, then we need to create a new one anyway.
1145 */
1146 if (ref && ref-> type & GIT_REF_OID){
1147 updated = 1;
1148 } else {
1149 ref = NULL;
1150 error = reference_create(&ref, repo, name, GIT_REF_OID);
1151 if (error < GIT_SUCCESS)
1152 goto cleanup;
1153 }
1154
1155 /* set the oid; this will write the reference on disk */
1156 error = git_reference_set_oid(ref, id);
1157 if (error < GIT_SUCCESS)
1158 goto cleanup;
1159
1160 if(!updated){
1161 error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref);
1162 if (error < GIT_SUCCESS)
1163 goto cleanup;
1164
1165 if(old_ref)
1166 reference_free(old_ref);
1167 }
1168
1169 *ref_out = ref;
1170
1171 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID");
1172
1173cleanup:
1174 reference_free(ref);
1175 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID");
1176}
1177
87d3acf4
VM
1178/**
1179 * Setters
1180 */
1181
1182/*
1183 * Change the OID target of a reference.
1184 *
1185 * For loose references, just change the oid in memory
1186 * and overwrite the file in disk.
1187 *
1188 * For packed files, this is not pretty:
1189 * For performance reasons, we write the new reference
1190 * loose on disk (it replaces the old on the packfile),
1191 * but we cannot invalidate the pointer to the reference,
1192 * and most importantly, the `packfile` object must stay
1193 * consistent with the representation of the packfile
1194 * on disk. This is what we need to:
1195 *
1196 * 1. Copy the reference
1197 * 2. Change the oid on the original
1198 * 3. Write the original to disk
1199 * 4. Write the original to the loose cache
1200 * 5. Replace the original with the copy (old reference) in the packfile cache
1201 */
1202int git_reference_set_oid(git_reference *ref, const git_oid *id)
1203{
1204 reference_oid *ref_oid;
1205 reference_oid *ref_old = NULL;
1206 int error = GIT_SUCCESS;
1207
1208 if ((ref->type & GIT_REF_OID) == 0)
5bdf7b9f 1209 return git__throw(GIT_EINVALIDREFSTATE, "Failed to set OID target of reference. Not an OID reference");
87d3acf4
VM
1210
1211 ref_oid = (reference_oid *)ref;
1212
9a53df7e
VM
1213 assert(ref->owner);
1214
1215 /* Don't let the user create references to OIDs that
1216 * don't exist in the ODB */
1217 if (!git_odb_exists(git_repository_database(ref->owner), id))
5bdf7b9f 1218 return git__throw(GIT_ENOTFOUND, "Failed to set OID target of reference. OID doesn't exist in ODB");
9a53df7e 1219
87d3acf4
VM
1220 /* duplicate the reference;
1221 * this copy will stay on the packfile cache */
1222 if (ref->type & GIT_REF_PACKED) {
1223 ref_old = git__malloc(sizeof(reference_oid));
1224 if (ref_old == NULL)
1225 return GIT_ENOMEM;
1226
1227 ref_old->ref.name = git__strdup(ref->name);
1228 if (ref_old->ref.name == NULL) {
1229 free(ref_old);
1230 return GIT_ENOMEM;
1231 }
1232 }
1233
1234 git_oid_cpy(&ref_oid->oid, id);
1235 ref->type &= ~GIT_REF_HAS_PEEL;
1236
932d1baf 1237 error = loose_write(ref);
87d3acf4
VM
1238 if (error < GIT_SUCCESS)
1239 goto cleanup;
1240
1241 if (ref->type & GIT_REF_PACKED) {
1242 /* insert the original on the loose cache */
1243 error = git_hashtable_insert(ref->owner->references.loose_cache, ref->name, ref);
1244 if (error < GIT_SUCCESS)
1245 goto cleanup;
1246
1247 ref->type &= ~GIT_REF_PACKED;
1248
1249 /* replace the original in the packfile with the copy */
1250 error = git_hashtable_insert(ref->owner->references.packfile, ref_old->ref.name, ref_old);
1251 if (error < GIT_SUCCESS)
1252 goto cleanup;
1253 }
1254
1255 return GIT_SUCCESS;
1256
1257cleanup:
1258 reference_free((git_reference *)ref_old);
5bdf7b9f 1259 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set OID target of reference");
87d3acf4
VM
1260}
1261
1262/*
1263 * Change the target of a symbolic reference.
1264 *
1265 * This is easy because symrefs cannot be inside
1266 * a pack. We just change the target in memory
1267 * and overwrite the file on disk.
1268 */
1269int git_reference_set_target(git_reference *ref, const char *target)
1270{
1271 reference_symbolic *ref_sym;
1272
1273 if ((ref->type & GIT_REF_SYMBOLIC) == 0)
5bdf7b9f 1274 return git__throw(GIT_EINVALIDREFSTATE, "Failed to set reference target. Not a symbolic reference");
87d3acf4
VM
1275
1276 ref_sym = (reference_symbolic *)ref;
1277
1278 free(ref_sym->target);
1279 ref_sym->target = git__strdup(target);
1280 if (ref_sym->target == NULL)
1281 return GIT_ENOMEM;
1282
1283 return loose_write(ref);
1284}
1285
1286/**
1287 * Other
1288 */
1289
7376ad99
VM
1290int git_reference_rename(git_reference *ref, const char *new_name, int force)
1291{
1292 int error;
ca6f203c 1293 char *old_name = NULL;
858dba58
VM
1294
1295 char aux_path[GIT_PATH_MAX];
0ffcf78a 1296 char normalized[GIT_REFNAME_MAX];
858dba58 1297
0ffcf78a
MS
1298 const char *target_ref = NULL;
1299 const char *head_target = NULL;
1300 const git_oid *target_oid = NULL;
1301 git_reference *new_ref = NULL, *old_ref = NULL, *head = NULL;
7376ad99
VM
1302
1303 assert(ref);
1304
0ffcf78a 1305 error = normalize_name(normalized, sizeof(normalized), new_name, ref->type & GIT_REF_OID);
7376ad99 1306 if (error < GIT_SUCCESS)
0ffcf78a 1307 return git__rethrow(error, "Failed to rename reference. Invalid name");
7376ad99 1308
0ffcf78a 1309 new_name = normalized;
7376ad99 1310
0ffcf78a 1311 error = git_reference_lookup(&new_ref, ref->owner, new_name);
73294339
MS
1312 if (error == GIT_SUCCESS) {
1313 if (!force)
1314 return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists");
1315
1316 error = git_reference_delete(new_ref);
1317 }
7376ad99 1318
0ffcf78a
MS
1319 if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
1320 goto cleanup;
7376ad99
VM
1321
1322 if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
0ffcf78a 1323 return git__rethrow(error, "Failed to rename reference. Reference already exists");
7376ad99 1324
0ffcf78a
MS
1325 /*
1326 * First, we backup the reference targets. Just keeping the old
1327 * reference won't work, since we may have to remove it to create
1328 * the new reference, e.g. when renaming foo/bar -> foo.
1329 */
7376ad99 1330
ca6f203c
MS
1331 old_name = git__strdup(ref->name);
1332
0ffcf78a
MS
1333 if (ref->type & GIT_REF_SYMBOLIC) {
1334 if ((target_ref = git_reference_target(ref)) == NULL)
1335 goto cleanup;
1336 } else {
1337 if ((target_oid = git_reference_oid(ref)) == NULL)
1338 goto cleanup;
7376ad99
VM
1339 }
1340
0ffcf78a
MS
1341 /*
1342 * Now delete the old ref and remove an possibly existing directory
1343 * named `new_name`.
1344 */
7376ad99 1345
0ffcf78a 1346 if (ref->type & GIT_REF_PACKED) {
637edc9c
VM
1347 ref->type &= ~GIT_REF_PACKED;
1348
7376ad99 1349 git_hashtable_remove(ref->owner->references.packfile, old_name);
0ffcf78a
MS
1350 if ((error = packed_write(ref->owner)) < GIT_SUCCESS)
1351 goto rollback;
7376ad99 1352 } else {
858dba58
VM
1353 git_path_join(aux_path, ref->owner->path_repository, old_name);
1354 if ((error = p_unlink(aux_path)) < GIT_SUCCESS)
7376ad99
VM
1355 goto cleanup;
1356
7376ad99
VM
1357 git_hashtable_remove(ref->owner->references.loose_cache, old_name);
1358 }
1359
858dba58
VM
1360 /* build new path */
1361 git_path_join(aux_path, ref->owner->path_repository, new_name);
7376ad99 1362
858dba58
VM
1363 if (git_futils_exists(aux_path) == GIT_SUCCESS) {
1364 if (git_futils_isdir(aux_path) == GIT_SUCCESS) {
1365 if ((error = git_futils_rmdir_r(aux_path, 0)) < GIT_SUCCESS)
0ffcf78a
MS
1366 goto rollback;
1367 } else goto rollback;
1368 }
7376ad99 1369
0ffcf78a
MS
1370 /*
1371 * Crude hack: delete any logs till we support proper reflogs.
1372 * Otherwise git.git will possibly fail and leave a mess. git.git
1373 * writes reflogs by default in any repo with a working directory:
1374 *
1375 * "We only enable reflogs in repositories that have a working directory
1376 * associated with them, as shared/bare repositories do not have
1377 * an easy means to prune away old log entries, or may fail logging
1378 * entirely if the user's gecos information is not valid during a push.
1379 * This heuristic was suggested on the mailing list by Junio."
1380 *
1381 * Shawn O. Pearce - 0bee59186976b1d9e6b2dd77332480c9480131d5
1382 *
1383 * TODO
1384 *
1385 */
1386
858dba58
VM
1387 git_path_join_n(aux_path, 3, ref->owner->path_repository, "logs", old_name);
1388 if (git_futils_isfile(aux_path) == GIT_SUCCESS) {
1389 if ((error = p_unlink(aux_path)) < GIT_SUCCESS)
1390 goto rollback;
0ffcf78a
MS
1391 }
1392
1393 /*
1394 * Finally we can create the new reference.
1395 */
0ffcf78a
MS
1396 if (ref->type & GIT_REF_SYMBOLIC) {
1397 if ((error = git_reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS)
1398 goto rollback;
1399 } else {
1400 if ((error = git_reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS)
1401 goto rollback;
1402 }
7376ad99 1403
7376ad99 1404 free(ref->name);
0ffcf78a
MS
1405 ref->name = new_ref->name;
1406
5c6ae009
KS
1407 /*
1408 * No need in new_ref anymore. We created it to fix the change on disk.
1409 * TODO: Refactoring required.
1410 */
1411 new_ref->name = NULL;
1412 reference_free(new_ref);
1413
1414 if ((error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **)&old_ref)) < GIT_SUCCESS)
0ffcf78a 1415 goto rollback;
7376ad99 1416
0ffcf78a
MS
1417 /*
1418 * Check if we have to update HEAD.
1419 */
7376ad99 1420
0ffcf78a
MS
1421 if ((error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS)
1422 goto cleanup;
7376ad99 1423
0ffcf78a 1424 head_target = git_reference_target(head);
7376ad99 1425
0ffcf78a 1426 if (head_target && !strcmp(head_target, old_name))
5c6ae009 1427 if ((error = git_reference_create_symbolic(&head, ref->owner, "HEAD", ref->name, 1)) < GIT_SUCCESS)
0ffcf78a 1428 goto rollback;
7376ad99 1429
0ffcf78a
MS
1430cleanup:
1431 free(old_name);
7376ad99 1432 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
7376ad99 1433
0ffcf78a
MS
1434rollback:
1435 /*
1436 * Try to create the old reference again.
1437 */
1438 if (ref->type & GIT_REF_SYMBOLIC)
1439 error = git_reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0);
1440 else
1441 error = git_reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0);
1442
1443 ref->name = old_name;
1444
1445 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Failed to rollback");
1446}
7376ad99 1447
87d3acf4
VM
1448/*
1449 * Delete a reference.
1450 *
1451 * If the reference is packed, this is an expensive
1452 * operation. We need to remove the reference from
1453 * the memory cache and then rewrite the whole pack
1454 *
5ad0351d 1455 * If the reference is loose, we remove it on
87d3acf4 1456 * the filesystem and update the in-memory cache
5ad0351d 1457 * accordingly. We also make sure that an older version
1458 * of it doesn't exist as a packed reference. If this
1459 * is the case, this packed reference is removed as well.
87d3acf4
VM
1460 *
1461 * This obviously invalidates the `ref` pointer.
1462 */
1463int git_reference_delete(git_reference *ref)
1464{
1465 int error;
5ad0351d 1466 git_reference *reference;
87d3acf4
VM
1467
1468 assert(ref);
1469
1470 if (ref->type & GIT_REF_PACKED) {
d79f1da6
VM
1471 /* load the existing packfile */
1472 if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
5bdf7b9f 1473 return git__rethrow(error, "Failed to delete reference");
932d1baf 1474
2b397327
VM
1475 if (git_hashtable_remove(ref->owner->references.packfile, ref->name) < GIT_SUCCESS)
1476 return git__throw(GIT_ENOTFOUND, "Reference not found");
1477
87d3acf4
VM
1478 error = packed_write(ref->owner);
1479 } else {
1480 char full_path[GIT_PATH_MAX];
f79026b4 1481 git_path_join(full_path, ref->owner->path_repository, ref->name);
87d3acf4 1482 git_hashtable_remove(ref->owner->references.loose_cache, ref->name);
f79026b4 1483 error = p_unlink(full_path);
5ad0351d 1484 if (error < GIT_SUCCESS)
1485 goto cleanup;
1486
1487 /* When deleting a loose reference, we have to ensure that an older
1488 * packed version of it doesn't exist
1489 */
5de079b8 1490 if (!git_reference_lookup(&reference, ref->owner, ref->name)) {
5ad0351d 1491 assert((reference->type & GIT_REF_PACKED) != 0);
1492 error = git_reference_delete(reference);
1493 }
87d3acf4
VM
1494 }
1495
5ad0351d 1496cleanup:
87d3acf4 1497 reference_free(ref);
5bdf7b9f 1498 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference");
87d3acf4
VM
1499}
1500
87d3acf4
VM
1501int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
1502{
1503 git_repository *repo;
1504 int error, i;
1505
1506 assert(resolved_ref && ref);
1507 *resolved_ref = NULL;
7341bf87
VM
1508
1509 if ((error = loose_update(ref)) < GIT_SUCCESS)
5bdf7b9f 1510 return git__rethrow(error, "Failed to resolve reference");
932d1baf 1511
87d3acf4
VM
1512 repo = ref->owner;
1513
1514 for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
1515 reference_symbolic *ref_sym;
1516
68a146c1
CMN
1517 *resolved_ref = ref;
1518
0d5d5190 1519 if (ref->type & GIT_REF_OID)
87d3acf4 1520 return GIT_SUCCESS;
87d3acf4
VM
1521
1522 ref_sym = (reference_symbolic *)ref;
5de079b8 1523 if ((error = git_reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS)
0d5d5190 1524 return error;
87d3acf4
VM
1525 }
1526
0d5d5190 1527 return git__throw(GIT_ENOMEM, "Failed to resolve reference. Reference is too nested");
87d3acf4
VM
1528}
1529
1530int git_reference_packall(git_repository *repo)
1531{
1532 int error;
1533
1534 /* load the existing packfile */
1535 if ((error = packed_load(repo)) < GIT_SUCCESS)
5bdf7b9f 1536 return git__rethrow(error, "Failed to pack references");
87d3acf4
VM
1537
1538 /* update it in-memory with all the loose references */
1539 if ((error = packed_loadloose(repo)) < GIT_SUCCESS)
5bdf7b9f 1540 return git__rethrow(error, "Failed to pack references");
87d3acf4
VM
1541
1542 /* write it back to disk */
1543 return packed_write(repo);
1544}
1545
43521d06 1546int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload)
00571828
VM
1547{
1548 int error;
1549 struct dirent_list_data data;
1550 char refs_path[GIT_PATH_MAX];
1551
7ad96e51 1552 /* list all the packed references first */
00571828
VM
1553 if (list_flags & GIT_REF_PACKED) {
1554 const char *ref_name;
402a47a7 1555 void *GIT_UNUSED(_unused);
00571828 1556
09e8de0f 1557 if ((error = packed_load(repo)) < GIT_SUCCESS)
5bdf7b9f 1558 return git__rethrow(error, "Failed to list references");
00571828
VM
1559
1560 GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
09e8de0f 1561 if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
0d5d5190 1562 return git__throw(error, "Failed to list references. User callback failed");
00571828
VM
1563 );
1564 }
1565
7ad96e51
VM
1566 /* now list the loose references, trying not to
1567 * duplicate the ref names already in the packed-refs file */
09e8de0f
VM
1568
1569 data.repo_path_len = strlen(repo->path_repository);
1570 data.list_flags = list_flags;
1571 data.repo = repo;
1572 data.callback = callback;
1573 data.callback_payload = payload;
1574
1575
f79026b4
VM
1576 git_path_join(refs_path, repo->path_repository, GIT_REFS_DIR);
1577 return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
09e8de0f
VM
1578}
1579
1580int cb__reflist_add(const char *ref, void *data)
1581{
1582 return git_vector_insert((git_vector *)data, git__strdup(ref));
1583}
1584
1585int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags)
1586{
1587 int error;
1588 git_vector ref_list;
1589
1590 assert(array && repo);
1591
1592 array->strings = NULL;
1593 array->count = 0;
1594
1595 if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
1596 return GIT_ENOMEM;
1597
43521d06 1598 error = git_reference_foreach(repo, list_flags, &cb__reflist_add, (void *)&ref_list);
7ad96e51
VM
1599
1600 if (error < GIT_SUCCESS) {
09e8de0f 1601 git_vector_free(&ref_list);
0d5d5190 1602 return error;
7ad96e51
VM
1603 }
1604
09e8de0f
VM
1605 array->strings = (char **)ref_list.contents;
1606 array->count = ref_list.length;
00571828
VM
1607 return GIT_SUCCESS;
1608}
87d3acf4
VM
1609
1610
1611
1612
1613/*****************************************
1614 * Init/free (repository API)
1615 *****************************************/
2f8a8ab2
VM
1616int git_repository__refcache_init(git_refcache *refs)
1617{
1618 assert(refs);
9282e921 1619
87d3acf4 1620 refs->loose_cache = git_hashtable_alloc(
86194b24
VM
1621 default_table_size,
1622 reftable_hash,
1623 (git_hash_keyeq_ptr)strcmp);
1624
87d3acf4
VM
1625 /* packfile loaded lazily */
1626 refs->packfile = NULL;
9282e921 1627
87d3acf4 1628 return (refs->loose_cache) ? GIT_SUCCESS : GIT_ENOMEM;
2f8a8ab2 1629}
9282e921 1630
2f8a8ab2
VM
1631void git_repository__refcache_free(git_refcache *refs)
1632{
2f8a8ab2 1633 git_reference *reference;
402a47a7 1634 const void *GIT_UNUSED(_unused);
9282e921 1635
2f8a8ab2
VM
1636 assert(refs);
1637
87d3acf4 1638 GIT_HASHTABLE_FOREACH(refs->loose_cache, _unused, reference,
86194b24
VM
1639 reference_free(reference);
1640 );
1641
87d3acf4 1642 git_hashtable_free(refs->loose_cache);
2f8a8ab2 1643
87d3acf4
VM
1644 if (refs->packfile) {
1645 GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference,
1646 reference_free(reference);
1647 );
1648
1649 git_hashtable_free(refs->packfile);
1650 }
9282e921 1651}
2f8a8ab2 1652
87d3acf4
VM
1653
1654
1655/*****************************************
1656 * Name normalization
1657 *****************************************/
aa2120e9 1658static int check_valid_ref_char(char ch)
1659{
1660 if (ch <= ' ')
0d5d5190 1661 return GIT_ERROR;
aa2120e9 1662
1663 switch (ch) {
1664 case '~':
1665 case '^':
1666 case ':':
1667 case '\\':
1668 case '?':
1669 case '[':
e1be1028 1670 case '*':
0d5d5190 1671 return GIT_ERROR;
aa2120e9 1672 break;
1673
1674 default:
1675 return GIT_SUCCESS;
1676 }
1677}
1678
3101a3e5 1679static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref)
aa2120e9 1680{
aa2120e9 1681 const char *name_end, *buffer_out_start;
1682 char *current;
1683 int contains_a_slash = 0;
1684
1685 assert(name && buffer_out);
1686
1687 buffer_out_start = buffer_out;
1688 current = (char *)name;
1689 name_end = name + strlen(name);
1690
3101a3e5
VM
1691 /* Terminating null byte */
1692 out_size--;
1693
aa2120e9 1694 /* A refname can not be empty */
1695 if (name_end == name)
5bdf7b9f 1696 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name is empty");
aa2120e9 1697
1698 /* A refname can not end with a dot or a slash */
1699 if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
5bdf7b9f 1700 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with dot or slash");
aa2120e9 1701
3101a3e5 1702 while (current < name_end && out_size) {
aa2120e9 1703 if (check_valid_ref_char(*current))
0d5d5190 1704 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains invalid characters");
aa2120e9 1705
1706 if (buffer_out > buffer_out_start) {
1707 char prev = *(buffer_out - 1);
1708
1709 /* A refname can not start with a dot nor contain a double dot */
1710 if (*current == '.' && ((prev == '.') || (prev == '/')))
5bdf7b9f 1711 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name starts with a dot or contains a double dot");
aa2120e9 1712
1713 /* '@{' is forbidden within a refname */
1714 if (*current == '{' && prev == '@')
5bdf7b9f 1715 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains '@{'");
aa2120e9 1716
1717 /* Prevent multiple slashes from being added to the output */
1718 if (*current == '/' && prev == '/') {
1719 current++;
1720 continue;
1721 }
1722 }
1723
e1be1028 1724 if (*current == '/')
aa2120e9 1725 contains_a_slash = 1;
aa2120e9 1726
1727 *buffer_out++ = *current++;
3101a3e5 1728 out_size--;
aa2120e9 1729 }
1730
3101a3e5
VM
1731 if (!out_size)
1732 return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long");
1733
83c95128 1734 /* Object id refname have to contain at least one slash, except
df30eac1
JP
1735 * for HEAD in a detached state or MERGE_HEAD if we're in the
1736 * middle of a merge */
1737 if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE)))
0d5d5190 1738 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains no slashes");
aa2120e9 1739
1740 /* A refname can not end with ".lock" */
1741 if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
0d5d5190 1742 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with '.lock'");
aa2120e9 1743
1744 *buffer_out = '\0';
1745
83c95128
CMN
1746 /*
1747 * For object id references, name has to start with refs/. Again,
1748 * we need to allow HEAD to be in a detached state.
1749 */
0d5d5190
VM
1750 if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
1751 strcmp(buffer_out_start, GIT_HEAD_FILE)))
5bdf7b9f 1752 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name does not start with 'refs/'");
aa2120e9 1753
0d5d5190 1754 return GIT_SUCCESS;
aa2120e9 1755}
2f8a8ab2 1756
3101a3e5 1757int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name)
86194b24 1758{
3101a3e5 1759 return normalize_name(buffer_out, out_size, name, 0);
86194b24
VM
1760}
1761
3101a3e5 1762int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name)
86194b24 1763{
3101a3e5 1764 return normalize_name(buffer_out, out_size, name, 1);
86194b24
VM
1765}
1766
1767