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