]> git.proxmox.com Git - libgit2.git/blob - src/repository.c
Split object methods from repository.c
[libgit2.git] / src / repository.c
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 #include <stdarg.h>
26
27 #include "git2/object.h"
28
29 #include "common.h"
30 #include "repository.h"
31 #include "commit.h"
32 #include "tag.h"
33 #include "blob.h"
34 #include "fileops.h"
35
36 #define GIT_DIR "/.git/"
37 #define GIT_OBJECTS_DIR "objects/"
38 #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
39 #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
40 #define GIT_REFS_DIR "refs/"
41 #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
42 #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
43
44 #define GIT_INDEX_FILE "index"
45 #define GIT_HEAD_FILE "HEAD"
46
47 #define GIT_SYMREF "ref: "
48 #define GIT_BRANCH_MASTER "master"
49
50 static const int OBJECT_TABLE_SIZE = 32;
51
52 typedef struct {
53 char *path_repository;
54 unsigned is_bare:1, has_been_reinit:1;
55 } repo_init;
56
57 /*
58 * Hash table methods
59 *
60 * Callbacks for the ODB cache, implemented
61 * as a hash table
62 */
63 uint32_t object_table_hash(const void *key)
64 {
65 uint32_t r;
66 git_oid *id;
67
68 id = (git_oid *)key;
69 memcpy(&r, id->id, sizeof(r));
70 return r;
71 }
72
73 int object_table_hashkey(void *object, const void *key)
74 {
75 git_object *obj;
76 git_oid *oid;
77
78 obj = (git_object *)object;
79 oid = (git_oid *)key;
80
81 return (git_oid_cmp(oid, &obj->id) == 0);
82 }
83
84
85 /*
86 * Git repository open methods
87 *
88 * Open a repository object from its path
89 */
90 static int assign_repository_DIRs(git_repository *repo,
91 const char *git_dir,
92 const char *git_object_directory,
93 const char *git_index_file,
94 const char *git_work_tree)
95 {
96 char path_aux[GIT_PATH_MAX];
97 size_t path_len;
98
99 assert(repo);
100
101 if (git_dir == NULL || gitfo_isdir(git_dir) < GIT_SUCCESS)
102 return GIT_ENOTFOUND;
103
104 /* store GIT_DIR */
105 path_len = strlen(git_dir);
106 strcpy(path_aux, git_dir);
107
108 if (path_aux[path_len - 1] != '/') {
109 path_aux[path_len] = '/';
110 path_aux[path_len + 1] = 0;
111
112 path_len = path_len + 1;
113 }
114
115 repo->path_repository = git__strdup(path_aux);
116
117 /* store GIT_OBJECT_DIRECTORY */
118 if (git_object_directory == NULL)
119 strcpy(path_aux + path_len, GIT_OBJECTS_DIR);
120 else
121 strcpy(path_aux, git_object_directory);
122
123 if (gitfo_isdir(path_aux) < GIT_SUCCESS)
124 return GIT_ENOTFOUND;
125
126 repo->path_odb = git__strdup(path_aux);
127
128
129 /* store GIT_INDEX_FILE */
130 if (git_index_file == NULL)
131 strcpy(path_aux + path_len, GIT_INDEX_FILE);
132 else
133 strcpy(path_aux, git_index_file);
134
135 if (gitfo_exists(path_aux) < 0)
136 return GIT_ENOTFOUND;
137
138 repo->path_index = git__strdup(path_aux);
139
140
141 /* store GIT_WORK_TREE */
142 if (git_work_tree == NULL)
143 repo->is_bare = 1;
144 else
145 repo->path_workdir = git__strdup(git_work_tree);
146
147 return GIT_SUCCESS;
148 }
149
150 static int guess_repository_DIRs(git_repository *repo, const char *repository_path)
151 {
152 char path_aux[GIT_PATH_MAX], *last_DIR;
153 int path_len;
154
155 if (gitfo_isdir(repository_path) < GIT_SUCCESS)
156 return GIT_ENOTAREPO;
157
158 path_len = strlen(repository_path);
159 strcpy(path_aux, repository_path);
160
161 if (path_aux[path_len - 1] != '/') {
162 path_aux[path_len] = '/';
163 path_aux[path_len + 1] = 0;
164
165 path_len = path_len + 1;
166 }
167
168 repo->path_repository = git__strdup(path_aux);
169
170 /* objects database */
171 strcpy(path_aux + path_len, GIT_OBJECTS_DIR);
172 if (gitfo_isdir(path_aux) < GIT_SUCCESS)
173 return GIT_ENOTAREPO;
174 repo->path_odb = git__strdup(path_aux);
175
176 /* HEAD file */
177 strcpy(path_aux + path_len, GIT_HEAD_FILE);
178 if (gitfo_exists(path_aux) < 0)
179 return GIT_ENOTAREPO;
180
181 path_aux[path_len] = 0;
182
183 last_DIR = (path_aux + path_len - 2);
184
185 while (*last_DIR != '/')
186 last_DIR--;
187
188 if (strcmp(last_DIR, GIT_DIR) == 0) {
189 repo->is_bare = 0;
190
191 /* index file */
192 strcpy(path_aux + path_len, GIT_INDEX_FILE);
193 repo->path_index = git__strdup(path_aux);
194
195 /* working dir */
196 *(last_DIR + 1) = 0;
197 repo->path_workdir = git__strdup(path_aux);
198
199 } else {
200 repo->is_bare = 1;
201 repo->path_workdir = NULL;
202 }
203
204 return GIT_SUCCESS;
205 }
206
207 static git_repository *repository_alloc()
208 {
209 git_repository *repo = git__malloc(sizeof(git_repository));
210 if (!repo)
211 return NULL;
212
213 memset(repo, 0x0, sizeof(git_repository));
214
215 repo->objects = git_hashtable_alloc(
216 OBJECT_TABLE_SIZE,
217 object_table_hash,
218 object_table_hashkey);
219
220 if (repo->objects == NULL) {
221 free(repo);
222 return NULL;
223 }
224
225 return repo;
226 }
227
228 int git_repository_open2(git_repository **repo_out,
229 const char *git_dir,
230 const char *git_object_directory,
231 const char *git_index_file,
232 const char *git_work_tree)
233 {
234 git_repository *repo;
235 int error = GIT_SUCCESS;
236
237 assert(repo_out);
238
239 repo = repository_alloc();
240 if (repo == NULL)
241 return GIT_ENOMEM;
242
243 error = assign_repository_DIRs(repo,
244 git_dir,
245 git_object_directory,
246 git_index_file,
247 git_work_tree);
248
249 if (error < GIT_SUCCESS)
250 goto cleanup;
251
252 error = git_odb_open(&repo->db, repo->path_odb);
253 if (error < GIT_SUCCESS)
254 goto cleanup;
255
256 *repo_out = repo;
257 return GIT_SUCCESS;
258
259 cleanup:
260 git_repository_free(repo);
261 return error;
262 }
263
264 int git_repository_open(git_repository **repo_out, const char *path)
265 {
266 git_repository *repo;
267 int error = GIT_SUCCESS;
268
269 assert(repo_out && path);
270
271 repo = repository_alloc();
272 if (repo == NULL)
273 return GIT_ENOMEM;
274
275 error = guess_repository_DIRs(repo, path);
276 if (error < GIT_SUCCESS)
277 goto cleanup;
278
279
280 error = git_odb_open(&repo->db, repo->path_odb);
281 if (error < GIT_SUCCESS)
282 goto cleanup;
283
284 *repo_out = repo;
285 return GIT_SUCCESS;
286
287 cleanup:
288 git_repository_free(repo);
289 return error;
290 }
291
292 void git_repository_free(git_repository *repo)
293 {
294 git_hashtable_iterator it;
295 git_object *object;
296
297 if (repo == NULL)
298 return;
299
300 free(repo->path_workdir);
301 free(repo->path_index);
302 free(repo->path_repository);
303 free(repo->path_odb);
304
305 git_hashtable_iterator_init(repo->objects, &it);
306
307 while ((object = (git_object *)
308 git_hashtable_iterator_next(&it)) != NULL)
309 git_object_free(object);
310
311 git_hashtable_free(repo->objects);
312
313 if (repo->db != NULL)
314 git_odb_close(repo->db);
315
316 if (repo->index != NULL)
317 git_index_free(repo->index);
318
319 free(repo);
320 }
321
322 git_index *git_repository_index(git_repository *repo)
323 {
324 if (repo->index == NULL) {
325 if (git_index_open_inrepo(&repo->index, repo) < GIT_SUCCESS)
326 return NULL;
327
328 assert(repo->index);
329 }
330
331 return repo->index;
332 }
333
334 git_odb *git_repository_database(git_repository *repo)
335 {
336 assert(repo);
337 return repo->db;
338 }
339
340 int git_repository_newobject(git_object **object_out, git_repository *repo, git_otype type)
341 {
342 git_object *object = NULL;
343
344 assert(object_out && repo);
345
346 *object_out = NULL;
347
348 switch (type) {
349 case GIT_OBJ_COMMIT:
350 case GIT_OBJ_TAG:
351 case GIT_OBJ_TREE:
352 case GIT_OBJ_BLOB:
353 break;
354
355 default:
356 return GIT_EINVALIDTYPE;
357 }
358
359 object = git__malloc(git_object__size(type));
360
361 if (object == NULL)
362 return GIT_ENOMEM;
363
364 memset(object, 0x0, git_object__size(type));
365 object->repo = repo;
366 object->in_memory = 1;
367 object->modified = 1;
368
369 object->source.raw.type = type;
370
371 *object_out = object;
372 return GIT_SUCCESS;
373 }
374
375 int git_repository_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
376 {
377 git_object *object = NULL;
378 git_rawobj obj_file;
379 int error = GIT_SUCCESS;
380
381 assert(repo && object_out && id);
382
383 object = git_hashtable_lookup(repo->objects, id);
384 if (object != NULL) {
385 *object_out = object;
386 return GIT_SUCCESS;
387 }
388
389 error = git_odb_read(&obj_file, repo->db, id);
390 if (error < GIT_SUCCESS)
391 return error;
392
393 if (type != GIT_OBJ_ANY && type != obj_file.type) {
394 git_rawobj_close(&obj_file);
395 return GIT_EINVALIDTYPE;
396 }
397
398 type = obj_file.type;
399
400 object = git__malloc(git_object__size(type));
401
402 if (object == NULL)
403 return GIT_ENOMEM;
404
405 memset(object, 0x0, git_object__size(type));
406
407 /* Initialize parent object */
408 git_oid_cpy(&object->id, id);
409 object->repo = repo;
410 memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
411 object->source.open = 1;
412
413 switch (type) {
414
415 case GIT_OBJ_COMMIT:
416 error = git_commit__parse((git_commit *)object);
417 break;
418
419 case GIT_OBJ_TREE:
420 error = git_tree__parse((git_tree *)object);
421 break;
422
423 case GIT_OBJ_TAG:
424 error = git_tag__parse((git_tag *)object);
425 break;
426
427 case GIT_OBJ_BLOB:
428 error = git_blob__parse((git_blob *)object);
429 break;
430
431 default:
432 break;
433 }
434
435 if (error < GIT_SUCCESS) {
436 git_object_free(object);
437 return error;
438 }
439
440 git_object__source_close(object);
441 git_hashtable_insert(repo->objects, &object->id, object);
442
443 *object_out = object;
444 return GIT_SUCCESS;
445 }
446
447 static int repo_init_reinit(repo_init *results)
448 {
449 /* TODO: reinit the repository */
450 results->has_been_reinit = 1;
451 return GIT_SUCCESS;
452 }
453
454 static int repo_init_createhead(const char *head_path)
455 {
456 git_file fd;
457 int error = GIT_SUCCESS;
458 char head_symlink[50];
459 int len;
460
461 len = sprintf(head_symlink, "%s %s%s\n", GIT_SYMREF, GIT_REFS_HEADS_DIR, GIT_BRANCH_MASTER);
462
463 if ((fd = gitfo_creat(head_path, S_IREAD | S_IWRITE)) < GIT_SUCCESS)
464 return GIT_ERROR;
465
466 error = gitfo_write(fd, (void*)head_symlink, strlen(head_symlink));
467
468 gitfo_close(fd);
469 return error;
470 }
471
472 static int repo_init_structure(repo_init *results)
473 {
474 const int mode = 0755; /* or 0777 ? */
475
476 char temp_path[GIT_PATH_MAX];
477 int path_len;
478 char *git_dir = results->path_repository;
479
480 if (gitfo_mkdir_recurs(git_dir, mode))
481 return GIT_ERROR;
482
483 path_len = strlen(git_dir);
484 strcpy(temp_path, git_dir);
485
486 /* Does HEAD file already exist ? */
487 strcpy(temp_path + path_len, GIT_HEAD_FILE);
488
489 if (gitfo_exists(temp_path) == GIT_SUCCESS)
490 return repo_init_reinit(results);
491
492 if (repo_init_createhead(temp_path) < GIT_SUCCESS)
493 return GIT_ERROR;
494
495 /* Creates the '/objects/info/' directory */
496 strcpy(temp_path + path_len, GIT_OBJECTS_INFO_DIR);
497 if (gitfo_mkdir_recurs(temp_path, mode))
498 return GIT_ERROR;
499
500 /* Creates the '/objects/pack/' directory */
501 strcpy(temp_path + path_len, GIT_OBJECTS_PACK_DIR);
502 if (gitfo_mkdir(temp_path, mode))
503 return GIT_ERROR;
504
505 /* Creates the '/refs/heads/' directory */
506 strcpy(temp_path + path_len, GIT_REFS_HEADS_DIR);
507 if (gitfo_mkdir_recurs(temp_path, mode))
508 return GIT_ERROR;
509
510 /* Creates the '/refs/tags/' directory */
511 strcpy(temp_path + path_len, GIT_REFS_TAGS_DIR);
512 if (gitfo_mkdir(temp_path, mode))
513 return GIT_ERROR;
514
515 /* TODO: what's left? templates? */
516
517 return GIT_SUCCESS;
518 }
519
520 static int repo_init_find_dir(repo_init *results, const char* path)
521 {
522 const int MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH = 66;
523
524 char temp_path[GIT_PATH_MAX];
525 int path_len;
526
527 path_len = strlen(path);
528 strcpy(temp_path, path);
529
530 /* Ensure path has a trailing slash */
531 if (temp_path[path_len - 1] != '/') {
532 temp_path[path_len] = '/';
533 temp_path[path_len + 1] = 0;
534
535 path_len = path_len + 1;
536 }
537
538 if (!results->is_bare) {
539 strcpy(temp_path + path_len - 1, GIT_DIR);
540 path_len = path_len + strlen(GIT_DIR) - 1; /* Skip the leading slash from the constant */
541 }
542
543 if (path_len >= GIT_PATH_MAX - MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH)
544 return GIT_ENOTAREPO;
545
546 results->path_repository = git__strdup(temp_path);
547
548 return GIT_SUCCESS;
549 }
550
551 int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
552 {
553 int error = GIT_SUCCESS;
554 repo_init results;
555
556 assert(repo_out && path);
557
558 results.path_repository = NULL;
559 results.is_bare = is_bare;
560
561 error = repo_init_find_dir(&results, path);
562 if (error < GIT_SUCCESS)
563 goto cleanup;
564
565 error = repo_init_structure(&results);
566 if (error < GIT_SUCCESS)
567 goto cleanup;
568
569 error = git_repository_open(repo_out, results.path_repository);
570
571 cleanup:
572 free(results.path_repository);
573 return error;
574 }