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.
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.)
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.
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.
28 #include "repository.h"
34 #define GIT_FOLDER "/.git/"
35 #define GIT_OBJECTS_FOLDER "objects/"
36 #define GIT_INDEX_FILE "index"
37 #define GIT_HEAD_FILE "HEAD"
39 static const int default_table_size
= 32;
40 static const double max_load_factor
= 0.65;
42 static const int OBJECT_BASE_SIZE
= 4096;
44 static const size_t object_sizes
[] = {
53 uint32_t git__objtable_hash(const void *key
)
59 memcpy(&r
, id
->id
, sizeof(r
));
63 int git__objtable_haskey(void *object
, const void *key
)
68 obj
= (git_object
*)object
;
71 return (git_oid_cmp(oid
, &obj
->id
) == 0);
76 static int assign_repository_folders(git_repository
*repo
,
78 const char *git_object_directory
,
79 const char *git_index_file
,
80 const char *git_work_tree
)
82 char path_aux
[GIT_PATH_MAX
];
87 if (git_dir
== NULL
|| gitfo_isdir(git_dir
) < 0)
92 path_len
= strlen(git_dir
);
93 strcpy(path_aux
, git_dir
);
95 if (path_aux
[path_len
- 1] != '/') {
96 path_aux
[path_len
] = '/';
97 path_aux
[path_len
+ 1] = 0;
99 path_len
= path_len
+ 1;
102 repo
->path_repository
= git__strdup(path_aux
);
104 /* store GIT_OBJECT_DIRECTORY */
105 if (git_object_directory
== NULL
)
106 strcpy(path_aux
+ path_len
, GIT_OBJECTS_FOLDER
);
108 strcpy(path_aux
, git_object_directory
);
110 if (gitfo_isdir(path_aux
) < 0)
111 return GIT_ENOTFOUND
;
113 repo
->path_odb
= git__strdup(path_aux
);
116 /* store GIT_INDEX_FILE */
117 if (git_index_file
== NULL
)
118 strcpy(path_aux
+ path_len
, GIT_INDEX_FILE
);
120 strcpy(path_aux
, git_index_file
);
122 if (gitfo_exists(path_aux
) < 0)
123 return GIT_ENOTFOUND
;
125 repo
->path_index
= git__strdup(path_aux
);
128 /* store GIT_WORK_TREE */
129 if (git_work_tree
== NULL
)
132 repo
->path_workdir
= git__strdup(git_work_tree
);
137 static int guess_repository_folders(git_repository
*repo
, const char *repository_path
)
139 char path_aux
[GIT_PATH_MAX
], *last_folder
;
142 if (gitfo_isdir(repository_path
) < 0)
143 return GIT_ENOTAREPO
;
145 path_len
= strlen(repository_path
);
146 strcpy(path_aux
, repository_path
);
148 if (path_aux
[path_len
- 1] != '/') {
149 path_aux
[path_len
] = '/';
150 path_aux
[path_len
+ 1] = 0;
152 path_len
= path_len
+ 1;
155 repo
->path_repository
= git__strdup(path_aux
);
157 /* objects database */
158 strcpy(path_aux
+ path_len
, GIT_OBJECTS_FOLDER
);
159 if (gitfo_isdir(path_aux
) < 0)
160 return GIT_ENOTAREPO
;
161 repo
->path_odb
= git__strdup(path_aux
);
164 strcpy(path_aux
+ path_len
, GIT_HEAD_FILE
);
165 if (gitfo_exists(path_aux
) < 0)
166 return GIT_ENOTAREPO
;
168 path_aux
[path_len
] = 0;
170 last_folder
= (path_aux
+ path_len
- 2);
172 while (*last_folder
!= '/')
175 if (strcmp(last_folder
, GIT_FOLDER
) == 0) {
179 strcpy(path_aux
+ path_len
, GIT_INDEX_FILE
);
180 repo
->path_index
= git__strdup(path_aux
);
183 *(last_folder
+ 1) = 0;
184 repo
->path_workdir
= git__strdup(path_aux
);
188 repo
->path_workdir
= NULL
;
194 git_repository
*git_repository__alloc()
196 git_repository
*repo
= git__malloc(sizeof(git_repository
));
200 memset(repo
, 0x0, sizeof(git_repository
));
202 repo
->objects
= git_hashtable_alloc(
205 git__objtable_haskey
);
207 if (repo
->objects
== NULL
) {
215 int git_repository_open2(git_repository
**repo_out
,
217 const char *git_object_directory
,
218 const char *git_index_file
,
219 const char *git_work_tree
)
221 git_repository
*repo
;
222 int error
= GIT_SUCCESS
;
226 repo
= git_repository__alloc();
230 error
= assign_repository_folders(repo
,
232 git_object_directory
,
239 error
= git_odb_open(&repo
->db
, repo
->path_odb
);
247 git_repository_free(repo
);
251 int git_repository_open(git_repository
**repo_out
, const char *path
)
253 git_repository
*repo
;
254 int error
= GIT_SUCCESS
;
256 assert(repo_out
&& path
);
258 repo
= git_repository__alloc();
262 error
= guess_repository_folders(repo
, path
);
266 error
= git_odb_open(&repo
->db
, repo
->path_odb
);
274 git_repository_free(repo
);
278 void git_repository_free(git_repository
*repo
)
280 git_hashtable_iterator it
;
285 free(repo
->path_workdir
);
286 free(repo
->path_index
);
287 free(repo
->path_repository
);
288 free(repo
->path_odb
);
290 git_hashtable_iterator_init(repo
->objects
, &it
);
292 while ((object
= (git_object
*)
293 git_hashtable_iterator_next(&it
)) != NULL
)
294 git_object_free(object
);
296 git_hashtable_free(repo
->objects
);
297 git_odb_close(repo
->db
);
298 git_index_free(repo
->index
);
302 git_index
*git_repository_index(git_repository
*repo
)
304 if (repo
->index
== NULL
) {
305 if (git_index_open_inrepo(&repo
->index
, repo
) < 0)
314 static int source_resize(git_odb_source
*src
)
316 size_t write_offset
, new_size
;
319 write_offset
= (size_t)((char *)src
->write_ptr
- (char *)src
->raw
.data
);
321 new_size
= src
->raw
.len
* 2;
322 if ((new_data
= git__malloc(new_size
)) == NULL
)
325 memcpy(new_data
, src
->raw
.data
, src
->written_bytes
);
328 src
->raw
.data
= new_data
;
329 src
->raw
.len
= new_size
;
330 src
->write_ptr
= (char *)new_data
+ write_offset
;
335 int git__source_printf(git_odb_source
*source
, const char *format
, ...)
338 int len
, did_resize
= 0;
340 assert(source
->open
&& source
->write_ptr
);
342 va_start(arglist
, format
);
344 len
= vsnprintf(source
->write_ptr
, source
->raw
.len
- source
->written_bytes
, format
, arglist
);
346 while (source
->written_bytes
+ len
>= source
->raw
.len
) {
347 if (source_resize(source
) < 0)
354 vsnprintf(source
->write_ptr
, source
->raw
.len
- source
->written_bytes
, format
, arglist
);
356 source
->write_ptr
= (char *)source
->write_ptr
+ len
;
357 source
->written_bytes
+= len
;
362 int git__source_write(git_odb_source
*source
, const void *bytes
, size_t len
)
366 assert(source
->open
&& source
->write_ptr
);
368 while (source
->written_bytes
+ len
>= source
->raw
.len
) {
369 if (source_resize(source
) < 0)
373 memcpy(source
->write_ptr
, bytes
, len
);
374 source
->write_ptr
= (char *)source
->write_ptr
+ len
;
375 source
->written_bytes
+= len
;
380 static void prepare_write(git_object
*object
)
382 if (object
->source
.write_ptr
!= NULL
|| object
->source
.open
)
383 git_object__source_close(object
);
385 /* TODO: proper size calculation */
386 object
->source
.raw
.data
= git__malloc(OBJECT_BASE_SIZE
);
387 object
->source
.raw
.len
= OBJECT_BASE_SIZE
;
389 object
->source
.write_ptr
= object
->source
.raw
.data
;
390 object
->source
.written_bytes
= 0;
392 object
->source
.open
= 1;
395 static int write_back(git_object
*object
)
402 assert(object
->source
.open
);
403 assert(object
->modified
);
405 object
->source
.raw
.len
= object
->source
.written_bytes
;
407 if ((error
= git_odb_write(&new_id
, object
->repo
->db
, &object
->source
.raw
)) < 0)
410 if (!object
->in_memory
)
411 git_hashtable_remove(object
->repo
->objects
, &object
->id
);
413 git_oid_cpy(&object
->id
, &new_id
);
414 git_hashtable_insert(object
->repo
->objects
, &object
->id
, object
);
416 object
->source
.write_ptr
= NULL
;
417 object
->source
.written_bytes
= 0;
419 object
->modified
= 0;
420 object
->in_memory
= 0;
422 git_object__source_close(object
);
426 int git_object__source_open(git_object
*object
)
430 assert(object
&& !object
->in_memory
);
432 if (object
->source
.open
)
433 git_object__source_close(object
);
435 error
= git_odb_read(&object
->source
.raw
, object
->repo
->db
, &object
->id
);
439 object
->source
.open
= 1;
443 void git_object__source_close(git_object
*object
)
447 if (object
->source
.open
) {
448 git_obj_close(&object
->source
.raw
);
449 object
->source
.open
= 0;
453 int git_object_write(git_object
*object
)
456 git_odb_source
*source
;
460 if (object
->modified
== 0)
463 prepare_write(object
);
464 source
= &object
->source
;
466 switch (source
->raw
.type
) {
468 error
= git_commit__writeback((git_commit
*)object
, source
);
472 error
= git_tree__writeback((git_tree
*)object
, source
);
476 error
= git_tag__writeback((git_tag
*)object
, source
);
480 error
= git_blob__writeback((git_blob
*)object
, source
);
489 git_object__source_close(object
);
493 return write_back(object
);
496 void git_object_free(git_object
*object
)
500 git_object__source_close(object
);
501 git_hashtable_remove(object
->repo
->objects
, &object
->id
);
503 switch (object
->source
.raw
.type
) {
505 git_commit__free((git_commit
*)object
);
509 git_tree__free((git_tree
*)object
);
513 git_tag__free((git_tag
*)object
);
517 git_blob__free((git_blob
*)object
);
526 git_odb
*git_repository_database(git_repository
*repo
)
532 const git_oid
*git_object_id(git_object
*obj
)
542 git_otype
git_object_type(git_object
*obj
)
545 return obj
->source
.raw
.type
;
548 git_repository
*git_object_owner(git_object
*obj
)
554 int git_repository_newobject(git_object
**object_out
, git_repository
*repo
, git_otype type
)
556 git_object
*object
= NULL
;
558 assert(object_out
&& repo
);
570 return GIT_EINVALIDTYPE
;
573 object
= git__malloc(object_sizes
[type
]);
578 memset(object
, 0x0, object_sizes
[type
]);
580 object
->in_memory
= 1;
581 object
->modified
= 1;
583 object
->source
.raw
.type
= type
;
585 *object_out
= object
;
589 int git_repository_lookup(git_object
**object_out
, git_repository
*repo
, const git_oid
*id
, git_otype type
)
591 git_object
*object
= NULL
;
595 assert(repo
&& object_out
&& id
);
597 object
= git_hashtable_lookup(repo
->objects
, id
);
598 if (object
!= NULL
) {
599 *object_out
= object
;
603 error
= git_odb_read(&obj_file
, repo
->db
, id
);
607 if (type
!= GIT_OBJ_ANY
&& type
!= obj_file
.type
)
608 return GIT_EINVALIDTYPE
;
610 type
= obj_file
.type
;
612 object
= git__malloc(object_sizes
[type
]);
617 memset(object
, 0x0, object_sizes
[type
]);
619 /* Initialize parent object */
620 git_oid_cpy(&object
->id
, id
);
622 memcpy(&object
->source
.raw
, &obj_file
, sizeof(git_rawobj
));
623 object
->source
.open
= 1;
628 error
= git_commit__parse((git_commit
*)object
);
632 error
= git_tree__parse((git_tree
*)object
);
636 error
= git_tag__parse((git_tag
*)object
);
640 error
= git_blob__parse((git_blob
*)object
);
648 git_object_free(object
);
652 git_object__source_close(object
);
653 git_hashtable_insert(repo
->objects
, &object
->id
, object
);
655 *object_out
= object
;
659 #define GIT_NEWOBJECT_TEMPLATE(obj, tp) \
660 int git_##obj##_new(git_##obj **o, git_repository *repo) {\
661 return git_repository_newobject((git_object **)o, repo, GIT_OBJ_##tp); } \
662 int git_##obj##_lookup(git_##obj **o, git_repository *repo, const git_oid *id) { \
663 return git_repository_lookup((git_object **)o, repo, id, GIT_OBJ_##tp); }
665 GIT_NEWOBJECT_TEMPLATE(commit
, COMMIT
)
666 GIT_NEWOBJECT_TEMPLATE(tag
, TAG
)
667 GIT_NEWOBJECT_TEMPLATE(tree
, TREE
)
668 GIT_NEWOBJECT_TEMPLATE(blob
, BLOB
)