From 0fb0039b7defd1d6be31150c09990db94370b2a4 Mon Sep 17 00:00:00 2001 From: womax Date: Mon, 3 Jun 2024 19:28:08 +0200 Subject: [PATCH] Command line --- src/commit.c | 68 +++++++++--- src/commit.h | 4 +- src/fs.c | 207 +++++++++++++++++++++++++++++++++---- src/fs.h | 20 +++- src/main.c | 285 +++++++++++++++++++++++++++++++++++++++++++++------ src/tree.c | 33 +++--- src/tree.h | 2 +- 7 files changed, 535 insertions(+), 84 deletions(-) diff --git a/src/commit.c b/src/commit.c index 75df928..d9f6df0 100644 --- a/src/commit.c +++ b/src/commit.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -64,7 +65,10 @@ void free_commit(struct commit *commit) int commit() { struct tree index = {0}; - load_index(&index); + if (load_index(&index) == REPO_NOT_INITIALIZED) + { + return REPO_NOT_INITIALIZED; + } struct object last_commit = {0}; struct commit commit = {0}; @@ -127,10 +131,6 @@ int commit() } 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); @@ -149,10 +149,22 @@ int commit() save_index(&index); } -void diff_commit_with_working_tree(struct commit *commit, int for_print) +int diff_commit_with_working_tree(char *checksum, int for_print) { + struct object commit_obj = {0}; + if (read_object(checksum, &commit_obj) == OBJECT_DOES_NOT_EXIST) + { + return OBJECT_DOES_NOT_EXIST; + } + + if (commit_obj.object_type != COMMIT) + return WRONG_OBJECT_TYPE; + + struct commit commit = {0}; + commit_from_object(&commit, &commit_obj); + struct object obj = {0}; - read_object(commit->tree, &obj); + read_object(commit.tree, &obj); struct tree commit_tree = {0}; get_tree(obj.content, &commit_tree); @@ -177,18 +189,37 @@ void diff_commit_with_working_tree(struct commit *commit, int for_print) pclose(p); free_tree(&commit_tree); + free_commit(&commit); free_object(&obj); + free_object(&commit_obj); } -void diff_commit(struct commit *commit_a, struct commit *commit_b, int for_print) +int diff_commit(char* checksum_a, char* checksum_b, int for_print) { - struct object tree_obj_a, tree_obj_b; - read_object(commit_a->tree, &tree_obj_a); - read_object(commit_b->tree, &tree_obj_b); + struct object commit_a_obj = {0}, commit_b_obj = {0}; + if (read_object(checksum_a, &commit_a_obj) == OBJECT_DOES_NOT_EXIST) + { + errno = 1; + return OBJECT_DOES_NOT_EXIST; + } + if (read_object(checksum_b, &commit_b_obj) == OBJECT_DOES_NOT_EXIST) + { + free_object(&commit_a_obj); + errno = 2; + return OBJECT_DOES_NOT_EXIST; + } + + struct commit commit_a, commit_b; + commit_from_object(&commit_a, &commit_a_obj); + commit_from_object(&commit_b, &commit_b_obj); + + struct object tree_a_obj, tree_b_obj; + read_object(commit_a.tree, &tree_a_obj); + read_object(commit_b.tree, &tree_b_obj); struct tree tree_a, tree_b; - get_tree(tree_obj_a.content, &tree_a); - get_tree(tree_obj_b.content, &tree_b); + get_tree(tree_a_obj.content, &tree_a); + get_tree(tree_b_obj.content, &tree_b); rmdir(TMP"/a"); rmdir(TMP"/b"); @@ -199,11 +230,16 @@ void diff_commit(struct commit *commit_a, struct commit *commit_b, int for_print dump_tree(TMP"/a", &tree_a); dump_tree(TMP"/b", &tree_b); - FILE *f = popen("diff -ruN "TMP"/a "TMP"/b > "LOCAL_REPO"/last.diff", "w"); + FILE *f = popen("diff -ruN "TMP"/a "TMP"/b --color=always> "LOCAL_REPO"/last.diff", "w"); pclose(f); free_tree(&tree_a); free_tree(&tree_b); - free_object(&tree_obj_a); - free_object(&tree_obj_b); + free_commit(&commit_a); + free_commit(&commit_b); + free_object(&tree_a_obj); + free_object(&tree_b_obj); + free_object(&commit_a_obj); + free_object(&commit_b_obj); + return 0; } \ No newline at end of file diff --git a/src/commit.h b/src/commit.h index 910674d..0eb84da 100644 --- a/src/commit.h +++ b/src/commit.h @@ -5,8 +5,8 @@ int commit_from_object(struct commit *commit, struct object *object); void free_commit(struct commit *commit); -void diff_commit(struct commit *commit_a, struct commit *commit_b, int for_print); -void diff_commit_with_working_tree(struct commit *commit, int for_print); +int diff_commit(char* checksum_a, char* checksum_b, int for_print); +int diff_commit_with_working_tree(char *checksum, int for_print); int commit(); #endif // COMMIT_H \ No newline at end of file diff --git a/src/fs.c b/src/fs.c index 01bfcde..ee0c927 100644 --- a/src/fs.c +++ b/src/fs.c @@ -194,6 +194,20 @@ defer: return result; } +int remove_object(char *checksum) +{ + if(!local_repo_exist()) + { + return REPO_NOT_INITIALIZED; + } + int result = FS_OK; + + char path[strlen(OBJECTS_DIR) + strlen(checksum) + 2]; + sprintf(path, "%s/%s", OBJECTS_DIR, checksum); + + remove(path); +} + int create_dir(char *dir) { struct stat buffer = {0}; @@ -361,7 +375,7 @@ int get_head_commit_checksum(char* checksum) return REPO_NOT_INITIALIZED; } - if(head_size == 0) return 0; + if(head_size == 0) return NO_CURRENT_HEAD; FILE *head_file = NULL; head_file = fopen(HEAD_FILE, "r"); @@ -383,7 +397,8 @@ int get_head_commit_checksum(char* checksum) int get_last_commit(struct object *commit) { char commit_checksum[DIGEST_LENGTH * 2 + 1]; - get_head_commit_checksum(commit_checksum); + if (get_head_commit_checksum(commit_checksum) == NO_CURRENT_HEAD) + return FS_OK; int res = read_object(commit_checksum, commit); if (res != 0) return FS_ERROR; @@ -465,24 +480,15 @@ int new_branch(char* branch_name) return FS_OK; } -int revert_to(char* commit_checksum) +int reset_to(char* commit_checksum) { - struct object obj = {0}; - read_object(commit_checksum, &obj); - - if(obj.object_type != COMMIT) - return WRONG_OBJECT_TYPE; - - struct commit commit = {0}; - commit_from_object(&commit, &obj); - - diff_commit_with_working_tree(&commit, 0); + int res = diff_commit_with_working_tree(commit_checksum, 0); + if(res != FS_OK) + return res; FILE *p = popen("patch -p0 -R < "LOCAL_REPO"/last.diff > /dev/null", "w"); pclose(p); - free_commit(&commit); - free_object(&obj); return FS_OK; } @@ -501,15 +507,178 @@ int checkout_branch(char *branch) fread(commit_checksum, DIGEST_LENGTH * 2 + 1, 1, branch_head); fclose(branch_head); - revert_to(commit_checksum); + reset_to(commit_checksum); FILE *head_file = fopen(HEAD_FILE, "w"); fwrite(branch_path, DIGEST_LENGTH * 2 + 1, 1, head_file); fclose(head_file); } -void print_diff() +int is_file_ignored(char *filename) { - FILE *p = popen("cat "LOCAL_REPO"/last.diff | less -R", "w"); - pclose(p); + struct stat buffer = {0}; + if (stat(IGNORE_FILE, &buffer) != 0) + { + return 0; + } + char content[buffer.st_size + 1]; + memset(content, 0, buffer.st_size + 1); + FILE *ignore_file = fopen(IGNORE_FILE, "r"); + fread(content, buffer.st_size, 1, ignore_file); + fclose(ignore_file); + + char *current_line = strtok(content, "\n"); + + while(current_line != NULL) + { + if(strncmp(current_line, filename, strlen(current_line)) == 0) + { + return 1; + } + char alt[strlen(current_line) + 3]; + sprintf(alt, "./%s", current_line); + if(strncmp(alt, filename, strlen(alt)) == 0) + return 1; + current_line = strtok(NULL, "\n"); + } + + return 0; +} + +int add_file_to_index(struct tree *index, char *filename) +{ + if (is_file_ignored(filename)) { + return 0; + } + struct stat st = {0}; + if (stat(filename, &st) != 0) + { + return FILE_NOT_FOUND; + } + if (!S_ISREG(st.st_mode)) + { + DIR *dp; + struct dirent *ep; + dp = opendir(filename); + if (dp != NULL) + { + while ((ep = readdir(dp)) != NULL) + { + if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) + { + char path[strlen(ep->d_name) + strlen(filename) + 2]; + if (strcmp(filename, "./") == 0) + sprintf(path, "%s", ep->d_name); + else if (strcmp(filename, ".") == 0) + sprintf(path, "%s", ep->d_name); + else if(filename[strlen(filename) - 1] == '/') + sprintf(path, "%s%s", filename, ep->d_name); + else + sprintf(path, "%s/%s", filename, ep->d_name); + add_file_to_index(index, path); + } + } + + closedir(dp); + return 0; + } + } else { + if (add_to_index(index, filename) == FILE_NOT_FOUND) + return FILE_NOT_FOUND; + } +} + +int remove_file_from_index(struct tree *index, char *filename) +{ + if (is_file_ignored(filename)) + return 0; + + struct stat st = {0}; + if (stat(filename, &st) != 0) + return FILE_NOT_FOUND; + if (!S_ISREG(st.st_mode)) + { + DIR *dp; + struct dirent *ep; + dp = opendir(filename); + if (dp != NULL) + { + while ((ep = readdir(dp)) != NULL) + { + if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) + { + char path[strlen(ep->d_name) + strlen(filename) + 2]; + if (strcmp(filename, "./") == 0) + sprintf(path, "%s", ep->d_name); + else if(filename[strlen(filename) - 1] == '/') + sprintf(path, "%s%s", filename, ep->d_name); + else + sprintf(path, "%s/%s", filename, ep->d_name); + remove_file_from_index(index, path); + } + } + + closedir(dp); + return 0; + } + } else { + remove_from_index(index, filename, 1); + } +} + +int dump_log() +{ + struct object current_obj = {0}; + get_last_commit(¤t_obj); + struct commit current = {0}; + commit_from_object(¤t, ¤t_obj); + + FILE *log_file = fopen(LOG_FILE, "w"); + char checksum[DIGEST_LENGTH * 2 + 1]; + hash_object(¤t_obj, checksum); + fprintf(log_file, "commit %s\n", checksum); + fprintf(log_file, "Author: %s\n", current.author); + fprintf(log_file, "Tree: %s\n", current.tree); + fprintf(log_file, "\n"); + + while (strcmp(current.parent, " ") != 0) + { + free_object(¤t_obj); + read_object(current.parent, ¤t_obj); + free_commit(¤t); + commit_from_object(¤t, ¤t_obj); + + checksum[DIGEST_LENGTH * 2 + 1]; + hash_object(¤t_obj, checksum); + fprintf(log_file, "commit %s\n", checksum); + fprintf(log_file, "Author: %s\n", current.author); + fprintf(log_file, "Tree: %s\n", current.tree); + fprintf(log_file, "\n"); + } + fclose(log_file); + return 0; +} + +int dump_branches() +{ + if(!heads_dir_exist()) + { + return REPO_NOT_INITIALIZED; + } + + DIR *heads_dir = opendir(HEADS_DIR); + struct dirent *ep; + FILE *dump = fopen(TMP"/branches", "w"); + if (heads_dir != NULL) + { + while ((ep = readdir(heads_dir)) != NULL) + { + if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) + fprintf(dump, "%s\n", ep->d_name); + } + fclose(dump); + closedir(heads_dir); + return 0; + } else + return FS_ERROR; } \ No newline at end of file diff --git a/src/fs.h b/src/fs.h index 78c8cc3..bdb8c1f 100644 --- a/src/fs.h +++ b/src/fs.h @@ -9,6 +9,8 @@ #define REFS_DIR LOCAL_REPO"/refs" #define HEADS_DIR REFS_DIR"/heads" #define HEAD_FILE LOCAL_REPO"/HEAD" +#define IGNORE_FILE ".gitignore" +#define LOG_FILE LOCAL_REPO"/log" #define TMP "/tmp" @@ -26,26 +28,40 @@ #define BRANCH_DOES_NOT_EXIST (-24) #define FILE_NOT_FOUND (-30) #define ENTRY_NOT_FOUND (-31) +#define NO_CURRENT_HEAD (-40) int local_repo_exist(); int index_exist(); int init_repo(); + int blob_from_file(char *filename, struct object *object); + int write_object(struct object *obj); int read_object(char *checksum, struct object *obj); +int remove_object(char *checksum); + int save_index(struct tree *tree); int load_index(struct tree *index); +int add_file_to_index(struct tree *index, char *filename); +int remove_file_from_index(struct tree *index, char *filename); + int load_tree(char* checksum, struct tree *tree); + int update_current_branch_head(char *new_head); int get_last_commit(struct object *commit); + int tmp_dump(struct object *obj, char* filename); int init_tmp_diff_dir(char* dir); + int new_branch(char* branch_name); -void print_diff(); -int revert_to(char* commit_checksum); +int checkout_branch(char *branch); + +int reset_to(char* commit_checksum); int create_dir(char *dir); int dump_tree(char *cwd, struct tree *tree); +int dump_log(); +int dump_branches(); #endif // FS_H diff --git a/src/main.c b/src/main.c index 4ed1f8a..1ca69cc 100644 --- a/src/main.c +++ b/src/main.c @@ -1,46 +1,267 @@ -// Requirements -// commit -// branching -// revert -// pull/push - +#include #include #include +#include +#include +#include #include #include "includes.h" -#include "objects.h" -#include "types.h" +#include "commit.h" #include "fs.h" #include "tree.h" -#include "commit.h" -int main(int argc, char** argv) +#define ARGS_MAX_SIZE 256 + +int print_help() { - // struct tree index = {0}; - // load_index(&index); + printf("Usage: cgit init\n"); + printf(" cgit add [FILES]\n"); + printf(" cgit remove [FILES]\n"); + printf(" cgit commit -m [MESSAGE]\n"); + printf(" cgit diff [COMMIT2]\n"); + printf(" cgit branch [BRANCH]\n"); + printf(" cgit checkout [BRANCH]\n"); + printf(" cgit reset \n"); + printf(" cgit log\n"); + return 0; +} - // add_to_index(&index, "src/commit.c"); - // add_to_index(&index, "src/commit.h"); - // add_to_index(&index, "src/fs.c"); - // add_to_index(&index, "src/fs.h"); +int pop_arg(int *argc, char ***argv, char *buf) +{ + if (*argc == 0) + { + return 1; + } - // save_index(&index); - - // commit(); - - struct object a = {0}, b = {0}; - read_object("083fec39f677b81de863cb0b8575ac76e87b7bff", &a); - read_object("fb5d38457520619b6e2f3b162c2a21a22b531cb4", &b); - - struct commit c_a, c_b; - commit_from_object(&c_a, &a); - commit_from_object(&c_b, &b); - - diff_commit_with_working_tree(&c_b); - - free_object(&a); - free_object(&b); + strcpy(buf, *argv[0]); + *argc -= 1; + *argv += 1; return 0; +} + +int add(int argc, char **argv) +{ + char buf[ARGS_MAX_SIZE]; + int res; + + res = pop_arg(&argc, &argv, buf); + if (res != 0) + { + printf("No file specified\n"); + return 0; + } + + struct tree index = {0}; + if (load_index(&index) == REPO_NOT_INITIALIZED) + { + printf("Not a cgit repository\n"); + return 128; + } + + do { + if (add_file_to_index(&index, buf) == FILE_NOT_FOUND) + { + printf("File %s does not exist\n", buf); + return 1; + } + res = pop_arg(&argc, &argv, buf); + } while (res == 0); + + save_index(&index); + free_tree(&index); + + return 0; +} + +int remove_cmd(int argc, char **argv) +{ + char buf[ARGS_MAX_SIZE]; + int res; + + res = pop_arg(&argc, &argv, buf); + if (res != 0) + { + printf("No file specified\n"); + return 0; + } + + struct tree index = {0}; + if (load_index(&index) == REPO_NOT_INITIALIZED) + { + printf("Not a cgit repository\n"); + return 128; + } + + do { + remove_file_from_index(&index, buf); + res = pop_arg(&argc, &argv, buf); + } while (res == 0); + + save_index(&index); + free_tree(&index); + + return 0; +} + +int commit_cmd(int argc, char **argv) +{ + if (commit() == REPO_NOT_INITIALIZED) + { + printf("Not a cgit repository\n"); + return 128; + } +} + +int diff(int argc, char **argv) +{ + char checksum_a[ARGS_MAX_SIZE]; + char checksum_b[ARGS_MAX_SIZE]; + + if(pop_arg(&argc, &argv, checksum_a) == 1) + { + printf("No commit specified\n"); + return 0; + } + + if(pop_arg(&argc, &argv, checksum_b) == 0) + { + if (diff_commit(checksum_a, checksum_b, 1) == OBJECT_DOES_NOT_EXIST) + { + if(errno == 1) + { + printf("Could not find commit %s\n", checksum_a); + } else if (errno == 2) + { + printf("Could not find commit %s\n", checksum_b); + } + return 0; + } + } else + { + if (diff_commit_with_working_tree(checksum_a, 1) == OBJECT_DOES_NOT_EXIST) + { + printf("Could not find commit %s\n", checksum_a); + return 0; + } + } + + FILE *p = popen("cat "LOCAL_REPO"/last.diff | less -R", "w"); + pclose(p); + return 0; +} + +int checkout(int argc, char **argv) +{ + char buf[ARGS_MAX_SIZE]; + + if(pop_arg(&argc, &argv, buf) == 1) + { + printf("No branch name specified\n"); + return 0; + } + + if (checkout_branch(buf) == BRANCH_DOES_NOT_EXIST) + { + printf("Branch %s does not exist, use cgit branch to create one\n", buf); + } +} + +int reset(int argc, char **argv) +{ + char buf[ARGS_MAX_SIZE]; + + if(pop_arg(&argc, &argv, buf) == 1) + { + printf("You must give a commit to reset to\n"); + return 0; + } + + int res = reset_to(buf); + if (res == OBJECT_DOES_NOT_EXIST) + { + printf("There is no commit named %s\n", buf); + } else if (res == WRONG_OBJECT_TYPE) + { + printf("Object %s is not a commit and thus cannot be reset to\n", buf); + } +} + +int branch(int argc, char **argv) +{ + char buf[ARGS_MAX_SIZE]; + + if (pop_arg(&argc, &argv, buf) == 1) + { + if (dump_branches() == REPO_NOT_INITIALIZED) + { + printf("Not a cgit repository\n"); + return 128; + } + FILE *p = popen("cat "TMP"/branches | less", "w"); + pclose(p); + return 0; + } + + if (new_branch(buf) == BRANCH_ALREADY_EXIST) + { + printf("Branch %s already exist\n", buf); + } + + return 0; +} + +int log_cmd(int argc, char **argv) +{ + dump_log(); + FILE *p = popen("cat "LOG_FILE" | less", "w"); + pclose(p); +} + +int main(int argc, char **argv) +{ + char cmd[ARGS_MAX_SIZE]; + char buf[ARGS_MAX_SIZE]; + + pop_arg(&argc, &argv, cmd); + if(pop_arg(&argc, &argv, buf) == 1) + { + print_help(); + return 0; + } + + if (strcmp(buf, "init") == 0) + { + return init_repo(); + } else if (strcmp(buf, "add") == 0) + { + return add(argc, argv); + } else if (strcmp(buf, "remove") == 0) + { + return remove_cmd(argc, argv); + } else if (strcmp(buf, "commit") == 0) + { + return commit_cmd(argc, argv); + } else if (strcmp(buf, "diff") == 0) + { + return diff(argc, argv); + } else if (strcmp(buf, "branch") == 0) + { + return branch(argc, argv); + } else if (strcmp(buf, "checkout") == 0) + { + return checkout(argc, argv); + } else if (strcmp(buf, "reset") == 0) + { + return reset(argc, argv); + } else if (strcmp(buf, "log") == 0) + { + return log_cmd(argc, argv); + } else if (strcmp(buf, "-h") == 0) { + return print_help(); + } else { + printf("Unknown command %s, try using %s -h\n", buf, cmd); + return 0; + } } \ No newline at end of file diff --git a/src/tree.c b/src/tree.c index d696083..6fa7b00 100644 --- a/src/tree.c +++ b/src/tree.c @@ -155,25 +155,32 @@ int add_to_tree(struct tree *tree, struct object *object, char *filename) int add_to_index(struct tree *index, char *filename) { + int result = FS_OK; struct object object = {0}; if (blob_from_file(filename, &object) != FS_OK) { return FILE_NOT_FOUND; } - add_to_tree(index, &object, filename); - write_object(&object); - free_object(&object); - return FS_OK; -} - -int remove_from_index(struct tree *index, char *filename) -{ - if (!local_repo_exist() || !index_exist()) + result = write_object(&object); + if (result == FS_OK) { - return REPO_NOT_INITIALIZED; + add_to_tree(index, &object, filename); + defer(FS_OK); } + if (result == OBJECT_ALREADY_EXIST) + { + defer(FS_OK); + } + +defer: + free_object(&object); + return result; +} + +int remove_from_index(struct tree *index, char *filename, int delete) +{ struct entry *entry = find_entry(index, filename); if (entry == NULL) { @@ -196,6 +203,8 @@ int remove_from_index(struct tree *index, char *filename) entry->next->previous = entry->previous; } + if (delete) + remove_object(entry->checksum); index->entries_size = index->entries_size - 1; free_entry(entry); free(entry); @@ -239,7 +248,7 @@ int add_object_to_tree(struct tree *tree, char* filename, struct object *source) if(top_folder != NULL) { load_tree(top_folder->checksum, &subtree); - remove_from_index(tree, top_folder->filename); + remove_from_index(tree, top_folder->filename, 0); } add_to_tree(&subtree, source, path_left); @@ -248,7 +257,7 @@ int add_object_to_tree(struct tree *tree, char* filename, struct object *source) tree_to_object(&subtree, &result); write_object(&result); - remove_from_index(tree, filename); + remove_from_index(tree, filename, 0); add_to_tree(tree, &result, top_folder_name); free_object(&result); free_tree(&subtree); diff --git a/src/tree.h b/src/tree.h index 9e4824e..766cc4a 100644 --- a/src/tree.h +++ b/src/tree.h @@ -15,7 +15,7 @@ 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 remove_from_index(struct tree *index, char *filename, int delete); int tree_to_object(struct tree *tree, struct object *object); int add_object_to_tree(struct tree *tree, char* filename, struct object *source);