diff --git a/Makefile b/Makefile index 41a4190..16bdfcc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SRC := $(wildcard src/*.c) CFLAGS := -lcrypto -lm -lz -DEBUG = false +DEBUG ?= false ifeq ($(DEBUG), true) DEBUG_FLAG = -DDEBUG -ggdb endif diff --git a/src/cgit.c b/src/cgit.c deleted file mode 100644 index 6cd387d..0000000 --- a/src/cgit.c +++ /dev/null @@ -1,12 +0,0 @@ -// Requirements -// commit -// branching -// revert -// pull/push - -#include - -int main(int argc, char** argv) { - printf("%s", "Hello World!\n"); - return 0; -} \ No newline at end of file diff --git a/src/commit.c b/src/commit.c new file mode 100644 index 0000000..c33c812 --- /dev/null +++ b/src/commit.c @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "commit.h" +#include "fs.h" +#include "includes.h" +#include "objects.h" +#include "tree.h" +#include "types.h" + +int commit_from_object(struct commit *commit, struct object *object) +{ + char tmp[object->size + 1]; + memcpy(tmp, object->content, object->size); + tmp[object->size] = '\0'; + char *current_line = strtok(tmp, "\n"); + commit->tree = malloc(strlen(current_line) + 1); + sprintf(commit->tree, "%s", current_line); + + current_line = strtok(NULL, "\n"); + commit->parent = malloc(strlen(current_line) + 1); + sprintf(commit->parent, "%s", current_line); + + current_line = strtok(NULL, "\n"); + commit->author = malloc(strlen(current_line) + 1); + sprintf(commit->author, "%s", current_line); + return 0; +} + +int commit_to_object(struct commit *commit, struct object *object) +{ + object->object_type = COMMIT; + object->size = strlen(commit->tree) + 2; + object->content = malloc(object->size); + sprintf(object->content, "%s\n", commit->tree); + + object->size += strlen(commit->parent) + 1; + object->content = realloc(object->content, object->size); + strncat(object->content, commit->parent, strlen(commit->parent)); + strcat(object->content, "\n"); + + object->size += strlen(commit->author); + object->content = realloc(object->content, object->size); + strncat(object->content, commit->author, strlen(commit->author)); + + object->size --; +} + +void free_commit(struct commit *commit) +{ + if (commit->author != NULL) + free(commit->author); + + if (commit->parent != NULL) + free(commit->parent); + + if (commit->tree != NULL) + free(commit->tree); +} + +int commit() +{ + struct tree index = {0}; + load_index(&index); + + struct object last_commit = {0}; + struct commit commit = {0}; + char last_commit_checksum[DIGEST_LENGTH * 2 + 1] = {0}; + struct tree commit_tree = {0}; + get_last_commit(&last_commit); + if (last_commit.size != 0) { + hash_object(&last_commit, last_commit_checksum); + commit_from_object(&commit, &last_commit); + + struct object last_commit_tree = {0}; + read_object(commit.tree, &last_commit_tree); + get_tree(last_commit_tree.content, &commit_tree); + free_object(&last_commit_tree); + } + free_object(&last_commit); + + struct entry *current = index.first_entry; + while(current != NULL) + { + size_t entry_size = DIGEST_LENGTH * 2 + 2 + strlen(current->filename); + char tmp[entry_size + 1]; + sprintf(tmp, "%s %s\n", current->checksum, current->filename); + + struct object object = {0}; + blob_from_file(current->filename, &object); + + add_object_to_tree(&commit_tree, current->filename, &object); + + free_object(&object); + current = current->next; + } + + if (commit.author == NULL) + { + char* author = "Antonin"; + commit.author = malloc(strlen(author) + 1); + sprintf(commit.author, "%s", author); + } + + + commit.parent = realloc(commit.parent, DIGEST_LENGTH * 2 + 1); + if (last_commit.size == 0) + { + sprintf(commit.parent, "%s", " "); + } else + { + sprintf(commit.parent, "%s", last_commit_checksum); + } + + struct object commit_tree_obj = {0}; + tree_to_object(&commit_tree, &commit_tree_obj); + char *commit_tree_checksum = malloc(DIGEST_LENGTH * 2 + 1); + hash_object(&commit_tree_obj, commit_tree_checksum); + write_object(&commit_tree_obj); + + if(commit.tree != NULL) + { + free(commit.tree); + } + commit.tree = commit_tree_checksum; + + debug_print("Author: %s", commit.author); + debug_print("Parent: %s", commit.parent); + debug_print("Tree: %s", commit.tree); + + struct object commit_obj = {0}; + commit_to_object(&commit, &commit_obj); + write_object(&commit_obj); + char commit_checksum[DIGEST_LENGTH * 2 + 1]; + hash_object(&commit_obj, commit_checksum); + + update_head(commit_checksum); + + free_commit(&commit); + free_object(&commit_obj); + free_tree(&commit_tree); + free_object(&commit_tree_obj); + free_tree(&index); + + memset(&index, 0, sizeof(struct tree)); + save_index(&index); +} \ No newline at end of file diff --git a/src/commit.h b/src/commit.h new file mode 100644 index 0000000..aa1fa46 --- /dev/null +++ b/src/commit.h @@ -0,0 +1,6 @@ +#ifndef COMMIT_H +#define COMMIT_H 1 + +int commit(); + +#endif // COMMIT_H \ No newline at end of file diff --git a/src/fs.c b/src/fs.c index a7f83c0..325a1bf 100644 --- a/src/fs.c +++ b/src/fs.c @@ -11,7 +11,7 @@ #include "fs.h" #include "includes.h" -#include "index.h" +#include "tree.h" #include "objects.h" #include "utils.h" @@ -27,99 +27,41 @@ int index_exist() return stat(INDEX_FILE, &buffer) == 0; } -void dump_index(struct index *index) +int heads_dir_exist() { - printf("index:\n"); - printf("\tentries_size: %ld\n", index->entries_size); - printf("\tentries:\n"); - struct entry *current = index->first_entry; - for(int i = 0; i < index->entries_size; i ++) - { - printf("\t\tfilename: %s, checksum: %s\n", current->filename, current->checksum); - current = current->next; - } + struct stat buffer; + return stat(HEADS_DIR, &buffer) == 0; } -int load_index(struct index *index) +int head_file_exist(size_t *head_size) { - if(!local_repo_exist() || !index_exist()) - { - return REPO_NOT_INITIALIZED; - } - - FILE* index_file = fopen(INDEX_FILE, "r"); struct stat buffer; - stat(INDEX_FILE, &buffer); + int result = stat(HEAD_FILE, &buffer) == 0; + *head_size = buffer.st_size; + return result; +} - char* file_content = calloc(sizeof(char), buffer.st_size + 1); - fread(file_content, buffer.st_size, 1, index_file); - fclose(index_file); - - char* current_line; - current_line = strtok(file_content, "\n"); - index->entries_size = strtol(current_line, NULL, 10); - - for(int i = 0; i < index->entries_size; i++) - { - current_line = strtok(NULL, "\n"); - int j = 0; - while(current_line[j] != ' ') - { - j++; - } - - char* checksum = calloc(sizeof(char), j + 1); - char* filename = calloc(sizeof(char), strlen(current_line) - j); - strncat(checksum, current_line, j); - strncat(filename, current_line + j + 1, strlen(current_line) - j); - - struct entry *entry = malloc(sizeof(struct entry)); - entry->checksum = checksum; - entry->filename = filename; - entry->next = NULL; - - if(i == 0) - { - index->first_entry = entry; - } else - { - entry->previous = index->last_entry; - index->last_entry->next = entry; - } - index->last_entry = entry; - debug_print("checksum: %s, filename: %s", entry->checksum, entry->filename); +int blob_from_file(char *filename, struct object *object) +{ + FILE* file = fopen(filename, "r"); + if (file == NULL) { + error_print("File %s not found", filename); + return FILE_NOT_FOUND; } - free(file_content); + struct stat file_info; + if (stat(filename, &file_info) != 0) + return -1; + + object->object_type = str_to_object_type("blob"); + object->size = file_info.st_size; + object->content = realloc(object->content, object->size); + fread(object->content, 1, object->size, file); + fclose(file); return FS_OK; } -int save_index(struct index *index) -{ - if(!local_repo_exist()) - { - return REPO_NOT_INITIALIZED; - } - - FILE *index_file = fopen(INDEX_FILE, "w"); - if(index_exist == NULL) - { - return FS_ERROR; - } - - fprintf(index_file, "%li\n", index->entries_size); - - struct entry *current = index->first_entry; - for(int i = 0; i < index->entries_size; i++) - { - fprintf(index_file, "%s %s\n", current->checksum, current->filename); - current = current->next; - } - - fclose(index_file); -} - int write_object(struct object *obj) { if(!local_repo_exist()) @@ -129,12 +71,12 @@ int write_object(struct object *obj) int result = FS_OK; struct stat buffer; - if (stat(OBJECTS_REPO, &buffer) != 0) + if (stat(OBJECTS_DIR, &buffer) != 0) { - mkdir(OBJECTS_REPO, DEFAULT_DIR_MODE); + mkdir(OBJECTS_DIR, DEFAULT_DIR_MODE); } - DIR *objects_dir = opendir(OBJECTS_REPO); + DIR *objects_dir = opendir(OBJECTS_DIR); int objects_dir_fd = dirfd(objects_dir); char checksum[DIGEST_LENGTH * 2]; @@ -178,15 +120,17 @@ int read_object(char *checksum, struct object *obj) int result = FS_OK; struct stat buffer; - if (stat(OBJECTS_REPO, &buffer) != 0) + if (stat(OBJECTS_DIR, &buffer) != 0) { error_print("Object dir does not exist"); return OBJECT_DOES_NOT_EXIST; } - DIR *objects_dir = opendir(OBJECTS_REPO); + DIR *objects_dir = opendir(OBJECTS_DIR); int objects_dir_fd = dirfd(objects_dir); + fstatat(objects_dir_fd, checksum, &buffer, 0); + char file_content[buffer.st_size]; int save_file_fd = openat(objects_dir_fd, checksum, O_RDONLY, DEFAULT_FILE_MODE); if (save_file_fd == -1) { @@ -195,55 +139,141 @@ int read_object(char *checksum, struct object *obj) error_print("Object %s does not exist", checksum); defer(OBJECT_DOES_NOT_EXIST); } + error_print("Cannot open file %s", checksum); defer(FS_ERROR); } - fstatat(objects_dir_fd, checksum, &buffer, 0); - char* file_content = malloc(buffer.st_size); FILE *save_file = fdopen(save_file_fd, "r"); fread(file_content, 1, buffer.st_size, save_file); fclose(save_file); - int res = uncompress_object(obj, file_content, buffer.st_size); - if(res != Z_OK) - { - defer(COMPRESSION_ERROR); - } - - free(file_content); + result = uncompress_object(obj, file_content, buffer.st_size); defer: closedir(objects_dir); return result; } -int main(void) +int load_tree(char* checksum, struct tree *tree) { - struct index index = {0}; - load_index(&index); + struct object object; + int res = read_object(checksum, &object); + if (res != FS_OK) + return res; - // struct object obj = {0}; - // obj.content = "Hello, world!\n"; - // obj.size = strlen(obj.content); - // obj.object_type = "blob"; + if (object.object_type != TREE) + { + error_print("Object %s is not a tree", checksum); + return WRONG_OBJECT_TYPE; + } - // return write_object(&obj); - - // read_object("af5626b4a114abcb82d63db7c8082c3c4756e51b", &obj); - - // debug_print("Object type is \"%s\"", obj.object_type); - // debug_print("Content size is %li", obj.size); - // debug_print("Content is %.*s", (int)obj.size, obj.content); - - // free_object(&obj); - - add_to_index(&index, "src/fs.c"); - - dump_index(&index); - - save_index(&index); - - free_index(&index); + get_tree(object.content, tree); + free_object(&object); + return 0; +} + +int load_index(struct tree *index) +{ + if(!local_repo_exist() || !index_exist()) + { + return REPO_NOT_INITIALIZED; + } + + FILE* index_file = fopen(INDEX_FILE, "r"); + struct stat buffer; + stat(INDEX_FILE, &buffer); + + char* file_content = calloc(buffer.st_size + 1, sizeof(char)); + fread(file_content, buffer.st_size, 1, index_file); + fclose(index_file); + + get_tree(file_content, index); + + free(file_content); + + return FS_OK; +} + +int save_index(struct tree *tree) +{ + if(!local_repo_exist()) + { + return REPO_NOT_INITIALIZED; + } + + FILE *index_file = fopen(INDEX_FILE"_cpy", "w"); + if(index_exist == NULL) + { + return FS_ERROR; + } + + struct object object; + tree_to_object(tree, &object); + + fwrite(object.content, object.size, 1, index_file); + fclose(index_file); + free_object(&object); +} + +int get_last_commit(struct object *commit) +{ + size_t head_size = 0; + if(!local_repo_exist() || !head_file_exist(&head_size) || !heads_dir_exist) + { + return REPO_NOT_INITIALIZED; + } + + if(head_size == 0) return 0; + + FILE *head_file = NULL; + head_file = fopen(HEAD_FILE, "r"); + char head_path[head_size]; + fread(head_path, head_size, 1, head_file); + fclose(head_file); + + struct stat buffer = {0}; + if (stat(head_path, &buffer) != 0) return 0; + + char commit_checksum[buffer.st_size + 1]; + memset(commit_checksum, 0, buffer.st_size + 1); + head_file = fopen(head_path, "r"); + fread(commit_checksum, buffer.st_size, 1, head_file); + fclose(head_file); + + int res = read_object(commit_checksum, commit); + if (res != 0) return FS_ERROR; + + if (commit->object_type != COMMIT) return WRONG_OBJECT_TYPE; + + return 0; +} + +int update_head(char *new_head) +{ + size_t head_size = 0; + if(!local_repo_exist() || !head_file_exist(&head_size) || !heads_dir_exist) + { + return REPO_NOT_INITIALIZED; + } + + FILE *file; + + if(head_size != 0) { + FILE *head_file = fopen(HEAD_FILE, "r"); + char branch[head_size]; + fread(branch, head_size, 1, head_file); + fclose(head_file); + file = fopen(branch, "w"); + } else + { + FILE *head_file = fopen(HEAD_FILE, "w"); + fprintf(head_file, "%s/master", HEADS_DIR); + fclose(head_file); + file = fopen(HEADS_DIR"/master", "w"); + } + + fwrite(new_head, strlen(new_head), 1, file); + fclose(file); return 0; } \ No newline at end of file diff --git a/src/fs.h b/src/fs.h index d28898a..f170f11 100644 --- a/src/fs.h +++ b/src/fs.h @@ -1,11 +1,14 @@ #ifndef FS_H #define FS_H 1 -#include "objects.h" +#include "types.h" #define LOCAL_REPO ".cgit" #define INDEX_FILE LOCAL_REPO"/index" -#define OBJECTS_REPO LOCAL_REPO"/objects" +#define OBJECTS_DIR LOCAL_REPO"/objects" +#define REFS_DIR LOCAL_REPO"/refs" +#define HEADS_DIR REFS_DIR"/heads" +#define HEAD_FILE LOCAL_REPO"/HEAD" #define DEFAULT_DIR_MODE 0755 #define DEFAULT_FILE_MODE 0444 @@ -15,13 +18,20 @@ #define REPO_NOT_INITIALIZED (-10) #define OBJECT_ALREADY_EXIST (-20) #define OBJECT_DOES_NOT_EXIST (-21) -#define FILE_NOT_FOUND (-22) -#define ENTRY_NOT_FOUND (-23) +#define WRONG_OBJECT_TYPE (-22) +#define FILE_NOT_FOUND (-30) +#define ENTRY_NOT_FOUND (-31) int local_repo_exist(); int index_exist(); +int blob_from_file(char *filename, struct object *object); int write_object(struct object *obj); int read_object(char *checksum, struct object *obj); +int save_index(struct tree *tree); +int load_index(struct tree *index); +int load_tree(char* checksum, struct tree *tree); +int update_head(char *new_head); +int get_last_commit(struct object *commit); #endif // FS_H diff --git a/src/includes.h b/src/includes.h index c5eb143..a584971 100644 --- a/src/includes.h +++ b/src/includes.h @@ -1,6 +1,8 @@ #ifndef INCLUDES_H #define INCLUDES_H +#define DIGEST_LENGTH 20 + #ifdef DEBUG #define debug_print(X, ...) \ printf("[DEBUG] %s:%d: ", __FILE__, __LINE__); \ diff --git a/src/index.c b/src/index.c deleted file mode 100644 index 42c266f..0000000 --- a/src/index.c +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include - -#include "index.h" -#include "includes.h" -#include "fs.h" - -void free_entry(struct entry *entry) -{ - if (entry->checksum != NULL) - free(entry->checksum); - - if (entry->filename != NULL) - free(entry->filename); - -} - -void free_index(struct index *index) -{ - struct entry *current = index->first_entry; - struct entry *next; - while(current != NULL) - { - free_entry(current); - next = current->next; - free(current); - current = next; - } -} - -struct entry *find_entry(struct index *index, char* filename) -{ - struct entry *current = index->first_entry; - for (int i = 0; i < index->entries_size; i ++) - { - if (strncmp(filename, current->filename, strlen(filename)) == 0) { - return current; - } - current = current->next; - } - - return NULL; -} - -int add_to_index(struct index *index, char *filename) -{ - if(!local_repo_exist() || !index_exist()) - { - return REPO_NOT_INITIALIZED; - } - - int result = FS_OK; - int new = 0; - - struct entry *entry = find_entry(index, filename); - if (entry == NULL) - { - new = 1; - entry = calloc(sizeof(struct entry), 1); - } else { - free_entry(entry); - } - - struct object object = {0}; - if (blob_from_file(filename, &object) != 0) - { - return FILE_NOT_FOUND; - } - - entry->filename = calloc(sizeof(char), strlen(filename) + 1); - strncat(entry->filename, filename, strlen(filename)); - entry->checksum = calloc(sizeof(char), DIGEST_LENGTH * 2 + 1); - hash_object(&object, entry->checksum); - int res = write_object(&object); - if (res != FS_OK && res != OBJECT_ALREADY_EXIST) - { - defer(FS_ERROR); - } - - if(new) - { - if(index->entries_size == 0) - { - index->first_entry = entry; - index->last_entry = entry; - } else - { - entry->previous = index->last_entry; - index->last_entry->next = entry; - index->last_entry = entry; - } - index->entries_size = index->entries_size + 1; - } - -defer: - free_object(&object); - return result; -} - -int remove_from_index(struct index *index, char *filename) -{ - if (!local_repo_exist() || !index_exist()) - { - return REPO_NOT_INITIALIZED; - } - - struct entry *entry = find_entry(index, filename); - if (entry == NULL) - { - return ENTRY_NOT_FOUND; - } - - if(index->first_entry == entry) - { - index->first_entry = entry->next; - } else - { - entry->previous->next = entry->next; - } - - if (index->last_entry == entry) - { - index->last_entry = entry->previous; - } else - { - entry->next->previous = entry->previous; - } - - index->entries_size = index->entries_size - 1; - free_entry(entry); - free(entry); - return FS_OK; -} \ No newline at end of file diff --git a/src/index.h b/src/index.h deleted file mode 100644 index 74f632e..0000000 --- a/src/index.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef INDEX_H -#define INDEX_H - -#include - -// Index file should follow the format -// number of entries\n -// entry1\n -// entry2\n -// ... -struct entry { - char *checksum; - char *filename; - struct entry *previous; - struct entry *next; -}; - -struct index { - size_t entries_size; - struct entry *first_entry; - struct entry *last_entry; -}; - -void free_index(struct index *index); -int add_to_index(struct index *index, char *filename); -int remove_from_index(struct index *index, char *filename); - -#endif // INDEX_H \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..8230ccb --- /dev/null +++ b/src/main.c @@ -0,0 +1,37 @@ +// Requirements +// commit +// branching +// revert +// pull/push + +#include + +#include "commit.h" + +int main(int argc, char** argv) { + // struct object obj = {0}; + // obj.content = "Hello, world!\n"; + // obj.size = strlen(obj.content); + // obj.object_type = "blob"; + + // return write_object(&obj); + + // read_object("af5626b4a114abcb82d63db7c8082c3c4756e51b", &obj); + + // debug_print("Object type is \"%s\"", obj.object_type); + // debug_print("Content size is %li", obj.size); + // debug_print("Content is %.*s", (int)obj.size, obj.content); + + // free_object(&obj); + + // add_to_index(&index, "src/fs.c"); + // add_to_index(&index, "src/test/foo"); + // add_to_index(&index, ".gitignore"); + // add_to_index(&index, "src/fs.h"); + + // dump_index(&tree); + + commit(); + + return 0; +} \ No newline at end of file diff --git a/src/objects.c b/src/objects.c index afb35fe..54bd223 100644 --- a/src/objects.c +++ b/src/objects.c @@ -11,9 +11,28 @@ #include "utils.h" #include "objects.h" +char *object_type_str[3] = { + "blob", + "tree", + "commit", +}; + +enum object_type str_to_object_type(char* str) +{ + if(strncmp("tree", str, 4) == 0) + { + return TREE; + } else if (strncmp("commit", str, 5) == 0) + { + return COMMIT; + } + + return BLOB; +} + size_t header_size(struct object *obj) { - return strlen(obj->object_type) + 2 + decimal_len(obj->size); + return strlen(object_type_str[obj->object_type]) + 2 + decimal_len(obj->size); } size_t object_size(struct object *obj) @@ -30,8 +49,9 @@ int full_object(struct object *obj, char *buffer, size_t buffer_size) assert(data_size <= buffer_size); char *writing = buffer; - memcpy(writing, obj->object_type, strlen(obj->object_type)); - writing += strlen(obj->object_type); + char *type = object_type_str[obj->object_type]; + memcpy(writing, type, strlen(type)); + writing += strlen(type); memcpy(writing, " ", 1); writing ++; memcpy(writing, hr_size, strlen(hr_size)); @@ -86,8 +106,7 @@ int uncompress_object(struct object *obj, char* compressed, uLongf comp_size) char *content_type_tmp = strtok(deflated, " "); obj->size = content_size; - obj->object_type = malloc(strlen(content_type_tmp) + 1); - strncpy(obj->object_type, content_type_tmp, strlen(content_type_tmp) + 1); + obj->object_type = str_to_object_type(content_type_tmp); obj->content = malloc(obj->size); memcpy(obj->content, deflated + header_size, obj->size); free(deflated); @@ -107,25 +126,6 @@ int compress_object(struct object *obj, char* compressed, uLongf *comp_size) return res; } -int blob_from_file(char *filename, struct object *object) -{ - FILE* file = fopen(filename, "r"); - if (file == NULL) - return -1; - - struct stat file_info; - if (stat(filename, &file_info) != 0) - return -1; - - object->object_type = "blob"; - object->size = file_info.st_size; - object->content = realloc(object->content, object->size); - fread(object->content, 1, object->size, file); - fclose(file); - - return 0; -} - void free_object(struct object *obj) { if (obj->content != NULL) diff --git a/src/objects.h b/src/objects.h index 5023e53..42fe12f 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4,22 +4,16 @@ #include #include -#define DIGEST_LENGTH 20 +#include "types.h" + #define HEADER_MAX_SIZE 20 -struct object -{ - char* content; - size_t size; - char* object_type; -}; - +enum object_type str_to_object_type(char* str); size_t object_size(struct object *obj); int full_object(struct object *obj, char* buffer, size_t buffer_size); int uncompress_object(struct object *obj, char* compressed, uLongf comp_size); int compress_object(struct object *obj, char* compressed, uLongf *comp_size); void hash_object(struct object *obj, char* result); -int blob_from_file(char *filename, struct object *object); void free_object(struct object *obj); #endif // OBJECTS_H diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..4912459 --- /dev/null +++ b/src/tree.c @@ -0,0 +1,248 @@ +#include +#include +#include + +#include "tree.h" +#include "includes.h" +#include "fs.h" +#include "objects.h" +#include "utils.h" + +void free_entry(struct entry *entry) +{ + if (entry->checksum != NULL) + free(entry->checksum); + + if (entry->filename != NULL) + free(entry->filename); + +} + +void free_tree(struct tree *tree) +{ + struct entry *current = tree->first_entry; + struct entry *next; + while(current != NULL) + { + free_entry(current); + next = current->next; + free(current); + current = next; + } +} + +struct entry *find_entry(struct tree *tree, char* filename) +{ + struct entry *current = tree->first_entry; + for (int i = 0; i < tree->entries_size; ++i) + { + if (strcmp(filename, current->filename) == 0) { + return current; + } + current = current->next; + } + + return NULL; +} + +void get_tree(char* content, struct tree *tree) +{ + char* current_line; + current_line = strtok(content, "\n"); + tree->entries_size = strtol(current_line, NULL, 10); + tree->first_entry = NULL; + tree->last_entry = NULL; + + for(int i = 0; i < tree->entries_size; i++) + { + current_line = strtok(NULL, "\n"); + int j = 0; + while(current_line[j] != ' ') + { + j++; + } + + char* checksum = calloc(j + 1, sizeof(char)); + char* filename = calloc(strlen(current_line) - j, sizeof(char)); + strncat(checksum, current_line, j); + strncat(filename, current_line + j + 1, strlen(current_line) - j); + + struct entry *entry = calloc(1, sizeof(struct entry)); + entry->checksum = checksum; + entry->filename = filename; + entry->next = NULL; + + if(i == 0) + { + tree->first_entry = entry; + tree->last_entry = entry; + } else + { + entry->previous = tree->last_entry; + tree->last_entry->next = entry; + } + tree->last_entry = entry; + } +} + +int add_to_tree(struct tree *tree, struct object *object, char *filename) +{ + + int new = 0; + struct entry *entry = find_entry(tree, filename); + if (entry == NULL) + { + new = 1; + entry = calloc(sizeof(struct entry), 1); + } else { + free_entry(entry); + } + + entry->filename = calloc(sizeof(char), strlen(filename) + 1); + strncat(entry->filename, filename, strlen(filename)); + entry->checksum = calloc(sizeof(char), DIGEST_LENGTH * 2 + 1); + hash_object(object, entry->checksum); + // int res = write_object(object); + // if (res != FS_OK && res != OBJECT_ALREADY_EXIST) + // { + // return res; + // } + + if(new) + { + if (tree->entries_size == 0) + { + tree->first_entry = entry; + tree->last_entry = entry; + } else + { + struct entry *current = tree->first_entry; + while (current != NULL && (strncmp(current->filename, entry->filename, strlen(entry->filename)) < 0)) + { + current = current->next; + } + + if (current == NULL) + { + tree->last_entry->next = entry; + entry->previous = tree->last_entry; + tree->last_entry = entry; + } else { + if(current == tree->first_entry) + { + tree->first_entry = entry; + } else { + current->previous->next = entry; + entry->previous = current->previous; + } + + entry->next = current; + current->previous = entry; + } + } + tree->entries_size ++; + } +} + +int add_to_index(struct tree *index, char *filename) +{ + struct object object = {0}; + if (blob_from_file(filename, &object) != FS_OK) + { + return FILE_NOT_FOUND; + } + + add_to_tree(index, &object, filename); + + free_object(&object); + return FS_OK; +} + +int remove_from_index(struct tree *index, char *filename) +{ + if (!local_repo_exist() || !index_exist()) + { + return REPO_NOT_INITIALIZED; + } + + struct entry *entry = find_entry(index, filename); + if (entry == NULL) + { + return ENTRY_NOT_FOUND; + } + + if(index->first_entry == entry) + { + index->first_entry = entry->next; + } else + { + entry->previous->next = entry->next; + } + + if (index->last_entry == entry) + { + index->last_entry = entry->previous; + } else + { + entry->next->previous = entry->previous; + } + + index->entries_size = index->entries_size - 1; + free_entry(entry); + free(entry); + return FS_OK; +} + +int tree_to_object(struct tree *tree, struct object *object) +{ + object->object_type = TREE; + object->content = calloc(1, decimal_len(tree->entries_size) + 2); + object->size = decimal_len(tree->entries_size) + 2; + sprintf(object->content, "%li\n", tree->entries_size); + + struct entry *current = tree->first_entry; + for(int i = 0; i < tree->entries_size; i++) + { + size_t entry_size = DIGEST_LENGTH * 2 + 2 + strlen(current->filename); + char tmp[entry_size + 1]; + object->size = object->size + entry_size; + object->content = realloc(object->content, object->size); + sprintf(tmp, "%s %s\n", current->checksum, current->filename); + strncat(object->content, tmp, entry_size); + current = current->next; + } + + object->size --; +} + +int add_object_to_tree(struct tree *tree, char* filename, struct object *source) +{ + char top_folder_name[strlen(filename)]; + char path_left[strlen(filename)]; + int res = get_top_folder(filename, top_folder_name, path_left); + if (res > 0) + { + struct tree subtree = {0}; + struct object result = {0}; + struct entry *top_folder = find_entry(tree, top_folder_name); + if(top_folder != NULL) + { + load_tree(top_folder->checksum, &subtree); + remove_from_index(tree, top_folder->filename); + } + + add_to_tree(&subtree, source, path_left); + + add_object_to_tree(&subtree, path_left, source); + + tree_to_object(&subtree, &result); + write_object(&result); + remove_from_index(tree, filename); + add_to_tree(tree, &result, top_folder_name); + free_object(&result); + free_tree(&subtree); + } else { + add_to_tree(tree, source, filename); + write_object(source); + } +} diff --git a/src/tree.h b/src/tree.h new file mode 100644 index 0000000..9e4824e --- /dev/null +++ b/src/tree.h @@ -0,0 +1,22 @@ +#ifndef INDEX_H +#define INDEX_H + +#include +#include "types.h" + +// Index file should follow the format +// number of entries\n +// entry1\n +// entry2\n +// ... + +void free_tree(struct tree *index); +struct entry *find_entry(struct tree *index, char* filename); +void get_tree(char* content, struct tree *tree); +int add_to_tree(struct tree *tree, struct object *object, char *filename); +int add_to_index(struct tree *index, char *filename); +int remove_from_index(struct tree *index, char *filename); +int tree_to_object(struct tree *tree, struct object *object); +int add_object_to_tree(struct tree *tree, char* filename, struct object *source); + +#endif // INDEX_H \ No newline at end of file diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..1cb3b98 --- /dev/null +++ b/src/types.h @@ -0,0 +1,40 @@ +#ifndef TYPES_H +#define TYPES_H 1 + +#include + +enum object_type +{ + BLOB, + TREE, + COMMIT +}; + +struct object +{ + char* content; + size_t size; + enum object_type object_type; +}; + +struct entry { + char *checksum; + char *filename; + struct entry *previous; + struct entry *next; +}; + +struct tree { + size_t entries_size; + struct entry *first_entry; + struct entry *last_entry; +}; + +struct commit +{ + char *tree; + char *parent; + char *author; +}; + +#endif // TYPES_H \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 83b58af..780606d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include +#include #include "utils.h" @@ -9,3 +10,15 @@ int decimal_len(size_t size) return floor(log10(size)) + 1; } + +int get_top_folder(char* path, char* top_folder, char* left) +{ + int i = 0; + for (;path[i] != '/' && path[i] != '\0'; i++); + if (path[i] == '\0') return 0; + path[i] = '\0'; + sprintf(top_folder, "%s", path); + sprintf(left, "%s", path + i + 1); + path[i] = '/'; + return 1; +} diff --git a/src/utils.h b/src/utils.h index e4ef1e2..5ec5913 100644 --- a/src/utils.h +++ b/src/utils.h @@ -4,5 +4,6 @@ #include int decimal_len(size_t size); +int get_top_folder(char* path, char* top_folder, char* left); #endif // UTILS_H \ No newline at end of file