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.
27 #include "git2/object.h"
30 #include "repository.h"
36 #define GIT_FOLDER "/.git/"
37 #define GIT_OBJECTS_FOLDER "objects/"
38 #define GIT_REFS_FOLDER "refs/"
39 #define GIT_INDEX_FILE "index"
40 #define GIT_HEAD_FILE "HEAD"
42 static const int default_table_size
= 32;
43 static const double max_load_factor
= 0.65;
45 static const int OBJECT_BASE_SIZE
= 4096;
48 const char *str
; /* type name string */
49 int loose
; /* valid loose object type flag */
50 size_t size
; /* size in bytes of the object structure */
51 } git_objects_table
[] = {
52 { "", 0, 0 }, /* 0 = GIT_OBJ__EXT1 */
53 { "commit", 1, sizeof(git_commit
)}, /* 1 = GIT_OBJ_COMMIT */
54 { "tree", 1, sizeof(git_tree
) }, /* 2 = GIT_OBJ_TREE */
55 { "blob", 1, sizeof(git_blob
) }, /* 3 = GIT_OBJ_BLOB */
56 { "tag", 1, sizeof(git_tag
) }, /* 4 = GIT_OBJ_TAG */
57 { "", 0, 0 }, /* 5 = GIT_OBJ__EXT2 */
58 { "OFS_DELTA", 0, 0 }, /* 6 = GIT_OBJ_OFS_DELTA */
59 { "REF_DELTA", 0, 0 } /* 7 = GIT_OBJ_REF_DELTA */
62 typedef struct git_repository_init_results
{
63 char *path_repository
;
66 unsigned has_been_reinit
:1;
67 } git_repository_init_results
;
69 /***********************************************************
71 * MISCELANEOUS HELPER FUNCTIONS
73 ***********************************************************/
77 uint32_t git__objtable_hash(const void *key
)
83 memcpy(&r
, id
->id
, sizeof(r
));
87 int git__objtable_haskey(void *object
, const void *key
)
92 obj
= (git_object
*)object
;
95 return (git_oid_cmp(oid
, &obj
->id
) == 0);
100 static int assign_repository_folders(git_repository
*repo
,
102 const char *git_object_directory
,
103 const char *git_index_file
,
104 const char *git_work_tree
)
106 char path_aux
[GIT_PATH_MAX
];
111 if (git_dir
== NULL
|| gitfo_isdir(git_dir
) < GIT_SUCCESS
)
112 return GIT_ENOTFOUND
;
115 path_len
= strlen(git_dir
);
116 strcpy(path_aux
, git_dir
);
118 if (path_aux
[path_len
- 1] != '/') {
119 path_aux
[path_len
] = '/';
120 path_aux
[path_len
+ 1] = 0;
122 path_len
= path_len
+ 1;
125 repo
->path_repository
= git__strdup(path_aux
);
127 /* store GIT_OBJECT_DIRECTORY */
128 if (git_object_directory
== NULL
)
129 strcpy(path_aux
+ path_len
, GIT_OBJECTS_FOLDER
);
131 strcpy(path_aux
, git_object_directory
);
133 if (gitfo_isdir(path_aux
) < GIT_SUCCESS
)
134 return GIT_ENOTFOUND
;
136 repo
->path_odb
= git__strdup(path_aux
);
139 /* store GIT_INDEX_FILE */
140 if (git_index_file
== NULL
)
141 strcpy(path_aux
+ path_len
, GIT_INDEX_FILE
);
143 strcpy(path_aux
, git_index_file
);
145 if (gitfo_exists(path_aux
) < 0)
146 return GIT_ENOTFOUND
;
148 repo
->path_index
= git__strdup(path_aux
);
151 /* store GIT_WORK_TREE */
152 if (git_work_tree
== NULL
)
155 repo
->path_workdir
= git__strdup(git_work_tree
);
160 static int guess_repository_folders(git_repository
*repo
, const char *repository_path
)
162 char path_aux
[GIT_PATH_MAX
], *last_folder
;
165 if (gitfo_isdir(repository_path
) < GIT_SUCCESS
)
166 return GIT_ENOTAREPO
;
168 path_len
= strlen(repository_path
);
169 strcpy(path_aux
, repository_path
);
171 if (path_aux
[path_len
- 1] != '/') {
172 path_aux
[path_len
] = '/';
173 path_aux
[path_len
+ 1] = 0;
175 path_len
= path_len
+ 1;
178 repo
->path_repository
= git__strdup(path_aux
);
180 /* objects database */
181 strcpy(path_aux
+ path_len
, GIT_OBJECTS_FOLDER
);
182 if (gitfo_isdir(path_aux
) < GIT_SUCCESS
)
183 return GIT_ENOTAREPO
;
184 repo
->path_odb
= git__strdup(path_aux
);
187 strcpy(path_aux
+ path_len
, GIT_HEAD_FILE
);
188 if (gitfo_exists(path_aux
) < 0)
189 return GIT_ENOTAREPO
;
191 path_aux
[path_len
] = 0;
193 last_folder
= (path_aux
+ path_len
- 2);
195 while (*last_folder
!= '/')
198 if (strcmp(last_folder
, GIT_FOLDER
) == 0) {
202 strcpy(path_aux
+ path_len
, GIT_INDEX_FILE
);
203 repo
->path_index
= git__strdup(path_aux
);
206 *(last_folder
+ 1) = 0;
207 repo
->path_workdir
= git__strdup(path_aux
);
211 repo
->path_workdir
= NULL
;
217 git_repository
*git_repository__alloc()
219 git_repository
*repo
= git__malloc(sizeof(git_repository
));
223 memset(repo
, 0x0, sizeof(git_repository
));
225 repo
->objects
= git_hashtable_alloc(
228 git__objtable_haskey
);
230 if (repo
->objects
== NULL
) {
238 int git_repository_open2(git_repository
**repo_out
,
240 const char *git_object_directory
,
241 const char *git_index_file
,
242 const char *git_work_tree
)
244 git_repository
*repo
;
245 int error
= GIT_SUCCESS
;
249 repo
= git_repository__alloc();
253 error
= assign_repository_folders(repo
,
255 git_object_directory
,
259 if (error
< GIT_SUCCESS
)
262 error
= git_odb_open(&repo
->db
, repo
->path_odb
);
263 if (error
< GIT_SUCCESS
)
270 git_repository_free(repo
);
274 int git_repository_open(git_repository
**repo_out
, const char *path
)
276 git_repository
*repo
;
277 int error
= GIT_SUCCESS
;
279 assert(repo_out
&& path
);
281 repo
= git_repository__alloc();
285 error
= guess_repository_folders(repo
, path
);
286 if (error
< GIT_SUCCESS
)
290 error
= git_odb_open(&repo
->db
, repo
->path_odb
);
291 if (error
< GIT_SUCCESS
)
298 git_repository_free(repo
);
302 void git_repository_free(git_repository
*repo
)
304 git_hashtable_iterator it
;
310 free(repo
->path_workdir
);
311 free(repo
->path_index
);
312 free(repo
->path_repository
);
313 free(repo
->path_odb
);
315 git_hashtable_iterator_init(repo
->objects
, &it
);
317 while ((object
= (git_object
*)
318 git_hashtable_iterator_next(&it
)) != NULL
)
319 git_object_free(object
);
321 git_hashtable_free(repo
->objects
);
323 if (repo
->db
!= NULL
)
324 git_odb_close(repo
->db
);
326 if (repo
->index
!= NULL
)
327 git_index_free(repo
->index
);
332 git_index
*git_repository_index(git_repository
*repo
)
334 if (repo
->index
== NULL
) {
335 if (git_index_open_inrepo(&repo
->index
, repo
) < GIT_SUCCESS
)
344 static int source_resize(git_odb_source
*src
)
346 size_t write_offset
, new_size
;
349 write_offset
= (size_t)((char *)src
->write_ptr
- (char *)src
->raw
.data
);
351 new_size
= src
->raw
.len
* 2;
352 if ((new_data
= git__malloc(new_size
)) == NULL
)
355 memcpy(new_data
, src
->raw
.data
, src
->written_bytes
);
358 src
->raw
.data
= new_data
;
359 src
->raw
.len
= new_size
;
360 src
->write_ptr
= (char *)new_data
+ write_offset
;
365 int git__source_printf(git_odb_source
*source
, const char *format
, ...)
368 int len
, did_resize
= 0;
370 assert(source
->open
&& source
->write_ptr
);
372 va_start(arglist
, format
);
374 len
= vsnprintf(source
->write_ptr
, source
->raw
.len
- source
->written_bytes
, format
, arglist
);
376 while (source
->written_bytes
+ len
>= source
->raw
.len
) {
377 if (source_resize(source
) < GIT_SUCCESS
)
384 vsnprintf(source
->write_ptr
, source
->raw
.len
- source
->written_bytes
, format
, arglist
);
386 source
->write_ptr
= (char *)source
->write_ptr
+ len
;
387 source
->written_bytes
+= len
;
392 int git__source_write(git_odb_source
*source
, const void *bytes
, size_t len
)
396 assert(source
->open
&& source
->write_ptr
);
398 while (source
->written_bytes
+ len
>= source
->raw
.len
) {
399 if (source_resize(source
) < GIT_SUCCESS
)
403 memcpy(source
->write_ptr
, bytes
, len
);
404 source
->write_ptr
= (char *)source
->write_ptr
+ len
;
405 source
->written_bytes
+= len
;
410 static void prepare_write(git_object
*object
)
412 if (object
->source
.write_ptr
!= NULL
|| object
->source
.open
)
413 git_object__source_close(object
);
415 /* TODO: proper size calculation */
416 object
->source
.raw
.data
= git__malloc(OBJECT_BASE_SIZE
);
417 object
->source
.raw
.len
= OBJECT_BASE_SIZE
;
419 object
->source
.write_ptr
= object
->source
.raw
.data
;
420 object
->source
.written_bytes
= 0;
422 object
->source
.open
= 1;
425 static int write_back(git_object
*object
)
432 assert(object
->source
.open
);
433 assert(object
->modified
);
435 object
->source
.raw
.len
= object
->source
.written_bytes
;
437 if ((error
= git_odb_write(&new_id
, object
->repo
->db
, &object
->source
.raw
)) < GIT_SUCCESS
)
440 if (!object
->in_memory
)
441 git_hashtable_remove(object
->repo
->objects
, &object
->id
);
443 git_oid_cpy(&object
->id
, &new_id
);
444 git_hashtable_insert(object
->repo
->objects
, &object
->id
, object
);
446 object
->source
.write_ptr
= NULL
;
447 object
->source
.written_bytes
= 0;
449 object
->modified
= 0;
450 object
->in_memory
= 0;
452 git_object__source_close(object
);
456 int git_object__source_open(git_object
*object
)
460 assert(object
&& !object
->in_memory
);
462 if (object
->source
.open
)
463 git_object__source_close(object
);
465 error
= git_odb_read(&object
->source
.raw
, object
->repo
->db
, &object
->id
);
466 if (error
< GIT_SUCCESS
)
469 object
->source
.open
= 1;
473 void git_object__source_close(git_object
*object
)
477 if (object
->source
.open
) {
478 git_rawobj_close(&object
->source
.raw
);
479 object
->source
.open
= 0;
483 int git_object_write(git_object
*object
)
486 git_odb_source
*source
;
490 if (object
->modified
== 0)
493 prepare_write(object
);
494 source
= &object
->source
;
496 switch (source
->raw
.type
) {
498 error
= git_commit__writeback((git_commit
*)object
, source
);
502 error
= git_tree__writeback((git_tree
*)object
, source
);
506 error
= git_tag__writeback((git_tag
*)object
, source
);
510 error
= git_blob__writeback((git_blob
*)object
, source
);
518 if (error
< GIT_SUCCESS
) {
519 git_object__source_close(object
);
523 return write_back(object
);
526 void git_object_free(git_object
*object
)
531 git_object__source_close(object
);
532 git_hashtable_remove(object
->repo
->objects
, &object
->id
);
534 switch (object
->source
.raw
.type
) {
536 git_commit__free((git_commit
*)object
);
540 git_tree__free((git_tree
*)object
);
544 git_tag__free((git_tag
*)object
);
548 git_blob__free((git_blob
*)object
);
557 git_odb
*git_repository_database(git_repository
*repo
)
563 const git_oid
*git_object_id(git_object
*obj
)
573 git_otype
git_object_type(git_object
*obj
)
576 return obj
->source
.raw
.type
;
579 git_repository
*git_object_owner(git_object
*obj
)
585 int git_repository_newobject(git_object
**object_out
, git_repository
*repo
, git_otype type
)
587 git_object
*object
= NULL
;
589 assert(object_out
&& repo
);
601 return GIT_EINVALIDTYPE
;
604 object
= git__malloc(git_objects_table
[type
].size
);
609 memset(object
, 0x0, git_objects_table
[type
].size
);
611 object
->in_memory
= 1;
612 object
->modified
= 1;
614 object
->source
.raw
.type
= type
;
616 *object_out
= object
;
620 int git_repository_lookup(git_object
**object_out
, git_repository
*repo
, const git_oid
*id
, git_otype type
)
622 git_object
*object
= NULL
;
624 int error
= GIT_SUCCESS
;
626 assert(repo
&& object_out
&& id
);
628 object
= git_hashtable_lookup(repo
->objects
, id
);
629 if (object
!= NULL
) {
630 *object_out
= object
;
634 error
= git_odb_read(&obj_file
, repo
->db
, id
);
635 if (error
< GIT_SUCCESS
)
638 if (type
!= GIT_OBJ_ANY
&& type
!= obj_file
.type
) {
639 git_rawobj_close(&obj_file
);
640 return GIT_EINVALIDTYPE
;
643 type
= obj_file
.type
;
645 object
= git__malloc(git_objects_table
[type
].size
);
650 memset(object
, 0x0, git_objects_table
[type
].size
);
652 /* Initialize parent object */
653 git_oid_cpy(&object
->id
, id
);
655 memcpy(&object
->source
.raw
, &obj_file
, sizeof(git_rawobj
));
656 object
->source
.open
= 1;
661 error
= git_commit__parse((git_commit
*)object
);
665 error
= git_tree__parse((git_tree
*)object
);
669 error
= git_tag__parse((git_tag
*)object
);
673 error
= git_blob__parse((git_blob
*)object
);
680 if (error
< GIT_SUCCESS
) {
681 git_object_free(object
);
685 git_object__source_close(object
);
686 git_hashtable_insert(repo
->objects
, &object
->id
, object
);
688 *object_out
= object
;
692 #define GIT_NEWOBJECT_TEMPLATE(obj, tp) \
693 int git_##obj##_new(git_##obj **o, git_repository *repo) {\
694 return git_repository_newobject((git_object **)o, repo, GIT_OBJ_##tp); } \
695 int git_##obj##_lookup(git_##obj **o, git_repository *repo, const git_oid *id) { \
696 return git_repository_lookup((git_object **)o, repo, id, GIT_OBJ_##tp); }
698 GIT_NEWOBJECT_TEMPLATE(commit
, COMMIT
)
699 GIT_NEWOBJECT_TEMPLATE(tag
, TAG
)
700 GIT_NEWOBJECT_TEMPLATE(tree
, TREE
)
701 GIT_NEWOBJECT_TEMPLATE(blob
, BLOB
)
704 const char *git_object_type2string(git_otype type
)
706 if (type
< 0 || ((size_t) type
) >= ARRAY_SIZE(git_objects_table
))
709 return git_objects_table
[type
].str
;
712 git_otype
git_object_string2type(const char *str
)
719 for (i
= 0; i
< ARRAY_SIZE(git_objects_table
); i
++)
720 if (!strcmp(str
, git_objects_table
[i
].str
))
726 int git_object_typeisloose(git_otype type
)
728 if (type
< 0 || ((size_t) type
) >= ARRAY_SIZE(git_objects_table
))
731 return git_objects_table
[type
].loose
;
734 int git_repository_init__reinit(git_repository_init_results
* results
)
736 /* To be implemented */
738 results
->has_been_reinit
= 1;
742 int git_repository_init__create_structure(git_repository_init_results
* results
)
744 char temp_path
[GIT_PATH_MAX
];
746 const int mode
= 0755; /* or 0777 ? */
748 char* git_dir
= results
->path_repository
;
750 //TODO : Ensure the parent tree structure already exists by recursively building it through gitfo_mkdir_recurs().
751 if (gitfo_mkdir_recurs(git_dir
, mode
))
754 path_len
= strlen(git_dir
);
755 strcpy(temp_path
, git_dir
);
757 /* Does HEAD file already exist ? */
758 strcpy(temp_path
+ path_len
, GIT_HEAD_FILE
);
759 if (!gitfo_exists(temp_path
))
761 return git_repository_init__reinit(results
);
764 strcpy(temp_path
+ path_len
, GIT_OBJECTS_FOLDER
);
765 if (gitfo_mkdir(temp_path
, mode
))
768 strcpy(temp_path
+ path_len
, GIT_REFS_FOLDER
);
769 if (gitfo_mkdir(temp_path
, mode
))
773 /* To be implemented */
778 int git_repository_init__assign_git_directory(git_repository_init_results
* results
, const char* path
)
780 const int MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH
= 66; // TODO: How many ?
781 char temp_path
[GIT_PATH_MAX
];
784 path_len
= strlen(path
);
785 strcpy(temp_path
, path
);
787 /* Ensure path has a trailing slash */
788 if (temp_path
[path_len
- 1] != '/') {
789 temp_path
[path_len
] = '/';
790 temp_path
[path_len
+ 1] = 0;
792 path_len
= path_len
+ 1;
795 assert(path_len
< GIT_PATH_MAX
- MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH
);
797 if (!results
->is_bare
)
799 strcpy(temp_path
+ path_len
- 1, GIT_FOLDER
);
800 path_len
= path_len
+ strlen(GIT_FOLDER
) - 1; /* Skip the leading slash from the constant */
803 results
->path_repository
= git__strdup(temp_path
);
808 int git_repository_init(git_repository
** repo_out
, const char* path
, unsigned is_bare
)
810 git_repository_init_results results
;
811 int error
= GIT_SUCCESS
;
813 assert(repo_out
&& path
);
815 results
.is_bare
= is_bare
;
817 error
= git_repository_init__assign_git_directory(&results
, path
);
818 if (error
< GIT_SUCCESS
)
821 error
= git_repository_init__create_structure(&results
);
822 if (error
< GIT_SUCCESS
)
825 //TODO: Uncomment when the structure has been properly created
826 //error = git_repository_open(repo_out, results->path_repository);
827 //if (error < GIT_SUCCESS)
831 free(results
.path_repository
);