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