feat(commit): added structs and functionality for the commit log

This commit is contained in:
lisk77 2025-09-15 03:23:49 +02:00
parent bb0ab630c0
commit f19209ba0a
2 changed files with 497 additions and 0 deletions

466
src/commit.c Normal file
View 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;
}