feat(commit): added structs and functionality for the commit log
This commit is contained in:
parent
bb0ab630c0
commit
f19209ba0a
2 changed files with 497 additions and 0 deletions
31
include/commit.h
Normal file
31
include/commit.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef COMMIT_H
|
||||
#define COMMIT_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
char* email;
|
||||
char* timestamp;
|
||||
} Author;
|
||||
|
||||
typedef struct {
|
||||
char* hash;
|
||||
char* tree_hash;
|
||||
Author* authors;
|
||||
size_t authors_count;
|
||||
Author committer;
|
||||
char* message;
|
||||
} Commit;
|
||||
|
||||
typedef List CommitLog;
|
||||
|
||||
CommitLog* commit_log_new();
|
||||
void commit_log_push(CommitLog*, Commit);
|
||||
void commit_log_free(CommitLog*);
|
||||
int read_commit_log(CommitLog*, char*);
|
||||
int write_commit_log(CommitLog*, char*);
|
||||
|
||||
#endif // COMMIT_H
|
||||
466
src/commit.c
Normal file
466
src/commit.c
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
#include "commit.h"
|
||||
|
||||
CommitLog* commit_log_new() {
|
||||
return list_new(sizeof(Commit));
|
||||
}
|
||||
|
||||
void commit_log_push(CommitLog* log, Commit commit) {
|
||||
if (!log) return;
|
||||
list_push(log, &commit);
|
||||
}
|
||||
|
||||
void commit_log_free(CommitLog* log) {
|
||||
if (log) {
|
||||
for (size_t i = 0; i < log->len; i++) {
|
||||
Commit* commit = (Commit*)log->items + i;
|
||||
free(commit->hash);
|
||||
free(commit->tree_hash);
|
||||
if (commit->authors) {
|
||||
for (size_t j = 0; j < commit->authors_count; j++) {
|
||||
free(commit->authors[j].name);
|
||||
free(commit->authors[j].email);
|
||||
}
|
||||
free(commit->authors);
|
||||
}
|
||||
free(commit->committer.name);
|
||||
free(commit->committer.email);
|
||||
free(commit->message);
|
||||
}
|
||||
free(log->items);
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
|
||||
int read_commit_log(CommitLog* log, char* commit_log_path) {
|
||||
size_t compressed_size;
|
||||
unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(commit_log_path, &compressed_size);
|
||||
|
||||
if (!compressed_data || compressed_size == 0) {
|
||||
free(compressed_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
while (idx < compressed_size && IS_DIGIT(compressed_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
perror("ERROR: no length found at start of commit log!");
|
||||
free(compressed_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* size_str = calloc(idx + 1, sizeof(char));
|
||||
if (!size_str) {
|
||||
free(compressed_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(size_str, compressed_data, idx);
|
||||
size_str[idx] = '\0';
|
||||
|
||||
char* end;
|
||||
long original_size = strtol(size_str, &end, 10);
|
||||
if (end == size_str || *end != '\0') {
|
||||
perror("ERROR: invalid length in read_commit_log!");
|
||||
free(size_str);
|
||||
free(compressed_data);
|
||||
return 0;
|
||||
}
|
||||
free(size_str);
|
||||
|
||||
if (idx < compressed_size && compressed_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
char* commit_log_data = calloc(original_size + 1, sizeof(char));
|
||||
if (!commit_log_data) {
|
||||
free(compressed_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uLongf dest_len = (uLongf)original_size;
|
||||
|
||||
int result = uncompress((unsigned char*)commit_log_data, &dest_len, compressed_data + idx, compressed_size - idx);
|
||||
free(compressed_data);
|
||||
|
||||
if (result != Z_OK) {
|
||||
perror("ERROR: decompression of the commit log failed!");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
commit_log_data[dest_len] = '\0';
|
||||
|
||||
size_t data_len = strlen(commit_log_data);
|
||||
if (data_len == 0) {
|
||||
free(commit_log_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
perror("ERROR: no commit count found at start of commit log!");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* count_str = calloc(idx + 1, sizeof(char));
|
||||
memcpy(count_str, commit_log_data, idx);
|
||||
count_str[idx] = '\0';
|
||||
|
||||
long commit_count = strtol(count_str, &end, 10);
|
||||
if (end == count_str || *end != '\0') {
|
||||
perror("ERROR: invalid commit count in commit log!");
|
||||
free(count_str);
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
free(count_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < commit_count; i++) {
|
||||
Commit commit = {0};
|
||||
|
||||
commit.hash = calloc(41, sizeof(char));
|
||||
memcpy(commit.hash, commit_log_data + idx, 40);
|
||||
commit.hash[40] = '\0';
|
||||
idx += 40;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.tree_hash = calloc(41, sizeof(char));
|
||||
memcpy(commit.tree_hash, commit_log_data + idx, 40);
|
||||
commit.tree_hash[40] = '\0';
|
||||
idx += 40;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t authors_count_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (idx == authors_count_start) {
|
||||
perror("ERROR: no authors count found in commit log!");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t authors_count_size = idx - authors_count_start;
|
||||
char* authors_count_str = calloc(authors_count_size + 1, sizeof(char));
|
||||
memcpy(authors_count_str, commit_log_data + authors_count_start, authors_count_size);
|
||||
authors_count_str[authors_count_size] = '\0';
|
||||
|
||||
long authors_count = strtol(authors_count_str, &end, 10);
|
||||
if (end == authors_count_str || *end != '\0') {
|
||||
perror("ERROR: invalid authors count in commit log!");
|
||||
free(authors_count_str);
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
free(authors_count_str);
|
||||
|
||||
commit.authors_count = authors_count;
|
||||
commit.authors = calloc(authors_count, sizeof(Author));
|
||||
if (!commit.authors) {
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
for (size_t a = 0; a < authors_count; a++) {
|
||||
size_t name_len_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
size_t name_len_size = idx - name_len_start;
|
||||
char* name_len_str = calloc(name_len_size + 1, sizeof(char));
|
||||
memcpy(name_len_str, commit_log_data + name_len_start, name_len_size);
|
||||
long name_len = strtol(name_len_str, &end, 10);
|
||||
free(name_len_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.authors[a].name = calloc(name_len + 1, sizeof(char));
|
||||
memcpy(commit.authors[a].name, commit_log_data + idx, name_len);
|
||||
idx += name_len;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t email_len_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
size_t email_len_size = idx - email_len_start;
|
||||
char* email_len_str = calloc(email_len_size + 1, sizeof(char));
|
||||
memcpy(email_len_str, commit_log_data + email_len_start, email_len_size);
|
||||
long email_len = strtol(email_len_str, &end, 10);
|
||||
free(email_len_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.authors[a].email = calloc(email_len + 1, sizeof(char));
|
||||
memcpy(commit.authors[a].email, commit_log_data + idx, email_len);
|
||||
idx += email_len;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t timestamp_start = idx;
|
||||
while (idx < data_len && commit_log_data[idx] != ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t timestamp_len = idx - timestamp_start;
|
||||
|
||||
if (timestamp_len <= 0 || timestamp_start + timestamp_len > data_len) {
|
||||
fprintf(stderr, "ERROR: Invalid timestamp length or start index in commit log!\n");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
commit.authors[a].timestamp = calloc(timestamp_len + 1, sizeof(char));
|
||||
if (!commit.authors[a].timestamp) {
|
||||
fprintf(stderr, "ERROR: Memory allocation failed for timestamp!\n");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
memcpy(commit.authors[a].timestamp, commit_log_data + timestamp_start, timestamp_len);
|
||||
commit.authors[a].timestamp[timestamp_len] = '\0';
|
||||
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size_t name_len_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t name_len_size = idx - name_len_start;
|
||||
char* name_len_str = calloc(name_len_size + 1, sizeof(char));
|
||||
memcpy(name_len_str, commit_log_data + name_len_start, name_len_size);
|
||||
long name_len = strtol(name_len_str, &end, 10);
|
||||
free(name_len_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.committer.name = calloc(name_len + 1, sizeof(char));
|
||||
memcpy(commit.committer.name, commit_log_data + idx, name_len);
|
||||
idx += name_len;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t email_len_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
size_t email_len_size = idx - email_len_start;
|
||||
char* email_len_str = calloc(email_len_size + 1, sizeof(char));
|
||||
memcpy(email_len_str, commit_log_data + email_len_start, email_len_size);
|
||||
long email_len = strtol(email_len_str, &end, 10);
|
||||
free(email_len_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.committer.email = calloc(email_len + 1, sizeof(char));
|
||||
memcpy(commit.committer.email, commit_log_data + idx, email_len);
|
||||
idx += email_len;
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t timestamp_start = idx;
|
||||
while (idx < data_len && commit_log_data[idx] != ' ') {
|
||||
idx++;
|
||||
}
|
||||
size_t timestamp_len = idx - timestamp_start;
|
||||
|
||||
commit.committer.timestamp = calloc(timestamp_len + 1, sizeof(char));
|
||||
if (!commit.committer.timestamp) {
|
||||
fprintf(stderr, "ERROR: Memory allocation failed for committer timestamp!\n");
|
||||
free(commit_log_data);
|
||||
return 0;
|
||||
}
|
||||
memcpy(commit.committer.timestamp, commit_log_data + timestamp_start, timestamp_len);
|
||||
commit.committer.timestamp[timestamp_len] = '\0';
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t message_len_start = idx;
|
||||
while (idx < data_len && IS_DIGIT(commit_log_data[idx])) {
|
||||
idx++;
|
||||
}
|
||||
|
||||
size_t message_len_size = idx - message_len_start;
|
||||
char* message_len_str = calloc(message_len_size + 1, sizeof(char));
|
||||
memcpy(message_len_str, commit_log_data + message_len_start, message_len_size);
|
||||
long message_len = strtol(message_len_str, &end, 10);
|
||||
free(message_len_str);
|
||||
|
||||
if (idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit.message = calloc(message_len + 1, sizeof(char));
|
||||
memcpy(commit.message, commit_log_data + idx, message_len);
|
||||
commit.message[message_len] = '\0';
|
||||
idx += message_len;
|
||||
|
||||
if (i < commit_count - 1 && idx < data_len && commit_log_data[idx] == ' ') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
commit_log_push(log, commit);
|
||||
}
|
||||
|
||||
free(commit_log_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int write_commit_log(CommitLog* log, char* commit_log_path) {
|
||||
if (!log) return 0;
|
||||
|
||||
size_t buffer_size = 1;
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", log->len);
|
||||
|
||||
for (size_t i = 0; i < log->len; i++) {
|
||||
Commit* commit = (Commit*)log->items + i;
|
||||
|
||||
buffer_size += 40 + 1;
|
||||
buffer_size += 40 + 1;
|
||||
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", commit->authors_count);
|
||||
|
||||
for (size_t a = 0; a < commit->authors_count; a++) {
|
||||
size_t name_len = strlen(commit->authors[a].name);
|
||||
size_t email_len = strlen(commit->authors[a].email);
|
||||
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", name_len);
|
||||
buffer_size += name_len + 1;
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", email_len);
|
||||
buffer_size += email_len + 1;
|
||||
buffer_size += strlen(commit->authors[a].timestamp) + 1;
|
||||
}
|
||||
|
||||
size_t committer_name_len = strlen(commit->committer.name);
|
||||
size_t committer_email_len = strlen(commit->committer.email);
|
||||
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", committer_name_len);
|
||||
buffer_size += committer_name_len + 1;
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", committer_email_len);
|
||||
buffer_size += committer_email_len + 1;
|
||||
buffer_size += strlen(commit->committer.timestamp) + 1;
|
||||
|
||||
size_t message_len = strlen(commit->message);
|
||||
buffer_size += snprintf(NULL, 0, "%zu ", message_len);
|
||||
buffer_size += message_len + 1;
|
||||
}
|
||||
|
||||
char tmp[buffer_size];
|
||||
size_t offset = 0;
|
||||
|
||||
offset += snprintf(tmp + offset, sizeof(tmp) - offset, "%zu ", log->len);
|
||||
|
||||
for (size_t i = 0; i < log->len; i++) {
|
||||
Commit* commit = (Commit*)log->items + i;
|
||||
size_t remaining = sizeof(tmp) - offset;
|
||||
|
||||
offset += snprintf(tmp + offset, remaining, "%s ", commit->hash);
|
||||
|
||||
remaining = sizeof(tmp) - offset;
|
||||
offset += snprintf(tmp + offset, remaining, "%s ", commit->tree_hash);
|
||||
|
||||
remaining = sizeof(tmp) - offset;
|
||||
offset += snprintf(tmp + offset, remaining, "%zu ", commit->authors_count);
|
||||
|
||||
for (size_t a = 0; a < commit->authors_count; a++) {
|
||||
remaining = sizeof(tmp) - offset;
|
||||
size_t name_len = strlen(commit->authors[a].name);
|
||||
size_t email_len = strlen(commit->authors[a].email);
|
||||
|
||||
offset += snprintf(tmp + offset, remaining, "%zu %s %zu %s %s ",
|
||||
name_len, commit->authors[a].name,
|
||||
email_len, commit->authors[a].email,
|
||||
commit->authors[a].timestamp);
|
||||
}
|
||||
|
||||
remaining = sizeof(tmp) - offset;
|
||||
size_t committer_name_len = strlen(commit->committer.name);
|
||||
size_t committer_email_len = strlen(commit->committer.email);
|
||||
|
||||
offset += snprintf(tmp + offset, remaining, "%zu %s %zu %s %s ",
|
||||
committer_name_len, commit->committer.name,
|
||||
committer_email_len, commit->committer.email,
|
||||
commit->committer.timestamp);
|
||||
|
||||
remaining = sizeof(tmp) - offset;
|
||||
size_t message_len = strlen(commit->message);
|
||||
offset += snprintf(tmp + offset, remaining, "%zu %s ", message_len, commit->message);
|
||||
}
|
||||
|
||||
FILE* fp = fopen(commit_log_path, "wb");
|
||||
if (!fp) {
|
||||
perror("ERROR: cannot open path in write_commit_log!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uLong originalLen = strlen(tmp);
|
||||
uLong compressedLen = compressBound(originalLen);
|
||||
Bytef* compressed = malloc(compressedLen);
|
||||
if (!compressed) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compress(compressed, &compressedLen, (const Bytef*)tmp, originalLen) != Z_OK) {
|
||||
perror("ERROR: compression failed in write_commit_log!");
|
||||
free(compressed);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "%lu ", originalLen);
|
||||
fwrite(compressed, sizeof(Bytef), compressedLen, fp);
|
||||
|
||||
fclose(fp);
|
||||
free(compressed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue