refactor(object, utilities): moved diff saving and reading to the object files
This commit is contained in:
parent
13e3ba70b2
commit
4b2575ab96
4 changed files with 378 additions and 402 deletions
|
|
@ -24,9 +24,12 @@
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "utilities.h"
|
#include "common.h"
|
||||||
|
|
||||||
char* get_object(char*, size_t*);
|
char* get_object(char*, size_t*);
|
||||||
void* parse_object(char*, ObjectType, size_t*);
|
void* parse_object(char*, ObjectType, size_t*);
|
||||||
|
int save_diff(ActionList*, char*, char*, size_t, char*, File*, int);
|
||||||
|
int read_diff(char*, char*, ActionList*);
|
||||||
|
int save_file_diff(char*, char*, size_t, char*, Changes*);
|
||||||
|
|
||||||
#endif // OBJECT_H
|
#endif // OBJECT_H
|
||||||
|
|
@ -9,11 +9,6 @@
|
||||||
#include "myers.h"
|
#include "myers.h"
|
||||||
#include "action_list.h"
|
#include "action_list.h"
|
||||||
|
|
||||||
#define IS_ALPHA(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
|
||||||
#define IS_DIGIT(c) (c >= '0' && c <= '9')
|
|
||||||
#define IS_ALNUM(c) (IS_ALPHA(c) || IS_DIGIT(c))
|
|
||||||
#define IS_PUNCT(c) ((c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126))
|
|
||||||
|
|
||||||
#define RESET "\033[0m"
|
#define RESET "\033[0m"
|
||||||
#define RED_BG "\033[41m"
|
#define RED_BG "\033[41m"
|
||||||
#define GREEN_BG "\033[42m"
|
#define GREEN_BG "\033[42m"
|
||||||
|
|
@ -29,10 +24,6 @@ typedef enum {
|
||||||
|
|
||||||
typedef List StringBuffer;
|
typedef List StringBuffer;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t insertions;
|
|
||||||
uint32_t deletions;
|
|
||||||
} Changes;
|
|
||||||
|
|
||||||
StringBuffer* string_buffer_new();
|
StringBuffer* string_buffer_new();
|
||||||
int string_buffer_push(StringBuffer*, char*);
|
int string_buffer_push(StringBuffer*, char*);
|
||||||
|
|
@ -42,15 +33,12 @@ char* find_root(char*);
|
||||||
void walk(char*, char*, char*, FileInfoBuffer*, int, char*);
|
void walk(char*, char*, char*, FileInfoBuffer*, int, char*);
|
||||||
char* get_repo_path(char*, char*);
|
char* get_repo_path(char*, char*);
|
||||||
int is_in_repo(char*, char*);
|
int is_in_repo(char*, char*);
|
||||||
void visualize_diff(File*, File*, ActionList*);
|
|
||||||
int cut_path(char*, char*);
|
int cut_path(char*, char*);
|
||||||
void combine_path(char*, char*);
|
void combine_path(char*, char*);
|
||||||
PathType get_path_type(const char*);
|
PathType get_path_type(const char*);
|
||||||
char* get_file_content(char*);
|
char* get_file_content(char*);
|
||||||
char* get_file_content_with_size(char*, size_t*);
|
char* get_file_content_with_size(char*, size_t*);
|
||||||
int create_default_config_file(char*);
|
int create_default_config_file(char*);
|
||||||
int save_diff(ActionList*, char*, char*, size_t, char*, File*, int);
|
|
||||||
int read_diff(char*, char*, ActionList*);
|
|
||||||
int save_file_diff(char*, char*, size_t, char*, Changes*);
|
|
||||||
|
|
||||||
#endif // UTILITIES_H
|
#endif // UTILITIES_H
|
||||||
|
|
|
||||||
373
src/object.c
373
src/object.c
|
|
@ -115,3 +115,376 @@ void* parse_object(char* hash, ObjectType type, size_t* size_out) {
|
||||||
default: free(content); return NULL;
|
default: free(content); return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mkdir_recursive(const char *path, mode_t mode) {
|
||||||
|
char *path_copy = strdup(path);
|
||||||
|
if (!path_copy) return -1;
|
||||||
|
|
||||||
|
char *p = path_copy;
|
||||||
|
|
||||||
|
if (*p == '/') p++;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
while (*p && *p != '/') p++;
|
||||||
|
|
||||||
|
char saved = *p;
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
if (mkdir(path_copy, mode) == -1 && errno != EEXIST) {
|
||||||
|
free(path_copy);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = saved;
|
||||||
|
if (*p) p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path_copy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_diff(ActionList* diff, char* path, char* root, size_t diff_id, char* basefile_hash, File* modified_file, int tree) {
|
||||||
|
size_t buffer_size = 41;
|
||||||
|
|
||||||
|
buffer_size += 1 + snprintf(NULL, 0, "%d", diff->len);
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < diff->len; idx++) {
|
||||||
|
Action action = diff->actions[idx];
|
||||||
|
if (action.type == DELETE) {
|
||||||
|
// 4 = OPBIT SPACE ... SPACE ... SPACE
|
||||||
|
buffer_size +=
|
||||||
|
4 +
|
||||||
|
snprintf(NULL, 0, "%d", action.line_original) +
|
||||||
|
snprintf(NULL, 0, "%d", action.line_changed);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 6 = OPBIT SPACE ... SPACE ... SPACE ... SPACE ... SPACE
|
||||||
|
buffer_size +=
|
||||||
|
6 +
|
||||||
|
snprintf(NULL, 0, "%d", action.line_original) +
|
||||||
|
snprintf(NULL, 0, "%d", action.line_changed) +
|
||||||
|
snprintf(NULL, 0, "%zu", strlen(modified_file->content[action.line_changed])) +
|
||||||
|
strlen(modified_file->content[action.line_changed]);
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp[buffer_size];
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
offset += snprintf(tmp, sizeof(tmp), "%s %zu ", basefile_hash, diff->len);
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < diff->len; idx++) {
|
||||||
|
Action action = diff->actions[idx];
|
||||||
|
size_t remaining = sizeof(tmp) - offset;
|
||||||
|
|
||||||
|
if (action.type == DELETE) {
|
||||||
|
offset += snprintf(tmp + offset,
|
||||||
|
remaining,
|
||||||
|
"0 %zu %zu ",
|
||||||
|
action.line_original,
|
||||||
|
action.line_changed
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += snprintf(tmp + offset, remaining,
|
||||||
|
"1 %zu %zu %zu %s ",
|
||||||
|
action.line_original,
|
||||||
|
action.line_changed,
|
||||||
|
strlen(modified_file->content[action.line_changed]),
|
||||||
|
modified_file->content[action.line_changed]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
char id[2+snprintf(NULL, 0, "%d", diff_id)+strlen(path)];
|
||||||
|
snprintf(id, sizeof(id), "%s %d", path, diff_id);
|
||||||
|
char hash[41];
|
||||||
|
if (tree) object_hash(TreeDiffObject, id, hash);
|
||||||
|
else object_hash(FileDiffObject, id, hash);
|
||||||
|
|
||||||
|
char dir_path[PATH_MAX];
|
||||||
|
char file_path[PATH_MAX];
|
||||||
|
snprintf(dir_path, sizeof(dir_path), "%s/.merk/objects/%.2s", root, hash);
|
||||||
|
mkdir(dir_path, 0755);
|
||||||
|
snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, hash+2);
|
||||||
|
|
||||||
|
if (access(file_path, F_OK) == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(file_path, "wb");
|
||||||
|
if (!fp) {
|
||||||
|
perror("ERROR: cannot open path in save_diff!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the directory structure exists
|
||||||
|
if (mkdir_recursive(dir_path, 0755) < 0 && errno != EEXIST) {
|
||||||
|
perror("ERROR: failed to create directory structure in save_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uLong originalLen = strlen(tmp) + 1;
|
||||||
|
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 save_diff!");
|
||||||
|
free(compressed);
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "%lu ", (unsigned long)originalLen);
|
||||||
|
|
||||||
|
fwrite(compressed, 1, compressedLen, fp);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
chmod(file_path, S_IRUSR | S_IRGRP | S_IROTH);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_diff(char* content, char* basefile_hash, ActionList* diff_out) {
|
||||||
|
if (!content || !basefile_hash || !diff_out) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
strncpy(basefile_hash, content, 40);
|
||||||
|
basefile_hash[40] = '\0';
|
||||||
|
idx += 41;
|
||||||
|
|
||||||
|
while (content[idx] && IS_DIGIT(content[idx])) {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == 41) {
|
||||||
|
perror("ERROR: no length found at start of diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* number_of_actions = calloc(idx - 41 + 1, sizeof(char));
|
||||||
|
if (!number_of_actions) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(number_of_actions, content + 41, idx - 41);
|
||||||
|
number_of_actions[idx - 41] = '\0';
|
||||||
|
|
||||||
|
char* end;
|
||||||
|
long actions = strtol(number_of_actions, &end, 10);
|
||||||
|
free(number_of_actions);
|
||||||
|
|
||||||
|
if (end == number_of_actions || *end != '\0' || actions <= 0) {
|
||||||
|
perror("ERROR: invalid number of actions in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t action_idx = 0;
|
||||||
|
while (content[idx] && action_idx < (size_t)actions) {
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
Action action = {0};
|
||||||
|
|
||||||
|
if (content[idx] == '0') {
|
||||||
|
idx++;
|
||||||
|
if (content[idx] != ' ') {
|
||||||
|
perror("ERROR: expected space after opbit in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
char* endptr;
|
||||||
|
action.line_original = strtol(content + idx, &endptr, 10);
|
||||||
|
if (endptr == content + idx || *endptr != ' ') {
|
||||||
|
perror("ERROR: failed to parse line_original in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = endptr - content + 1;
|
||||||
|
|
||||||
|
action.line_changed = strtol(content + idx, &endptr, 10);
|
||||||
|
if (endptr == content + idx || (*endptr != ' ' && *endptr != '\0')) {
|
||||||
|
perror("ERROR: failed to parse line_changed in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = endptr - content;
|
||||||
|
} else if (content[idx] == '1') {
|
||||||
|
idx++;
|
||||||
|
if (content[idx] != ' ') {
|
||||||
|
perror("ERROR: expected space after opbit in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
char* endptr;
|
||||||
|
action.line_original = strtol(content + idx, &endptr, 10);
|
||||||
|
if (endptr == content + idx || *endptr != ' ') {
|
||||||
|
perror("ERROR: failed to parse line_original in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = endptr - content + 1;
|
||||||
|
|
||||||
|
action.line_changed = strtol(content + idx, &endptr, 10);
|
||||||
|
if (endptr == content + idx || *endptr != ' ') {
|
||||||
|
perror("ERROR: failed to parse line_changed in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = endptr - content + 1;
|
||||||
|
|
||||||
|
size_t content_length = strtol(content + idx, &endptr, 10);
|
||||||
|
if (endptr == content + idx || *endptr != ' ') {
|
||||||
|
perror("ERROR: failed to parse content length in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = endptr - content + 1;
|
||||||
|
|
||||||
|
action.content = strndup(content + idx, content_length);
|
||||||
|
if (!action.content) {
|
||||||
|
perror("ERROR: failed to allocate memory for action content in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx += content_length;
|
||||||
|
} else {
|
||||||
|
perror("ERROR: unknown action type in read_diff!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action(diff_out, action);
|
||||||
|
|
||||||
|
action_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int save_file_diff(char* path, char* root, size_t diff_id, char* basefile_hash, Changes* changes) {
|
||||||
|
char basefile_location[2+strlen(root)+strlen("/.merk/objects/")+strlen(basefile_hash)];
|
||||||
|
snprintf(basefile_location, sizeof(basefile_location), "%s/.merk/objects/%.2s/%s", root, basefile_hash, basefile_hash+2);
|
||||||
|
|
||||||
|
size_t compressed_size;
|
||||||
|
unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(basefile_location, &compressed_size);
|
||||||
|
if (!compressed_data) return 0;
|
||||||
|
|
||||||
|
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 save_file_diff!");
|
||||||
|
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 size = strtol(size_str, &end, 10);
|
||||||
|
if (end == size_str || *end != '\0') {
|
||||||
|
perror("ERROR: invalid length in base_file_list");
|
||||||
|
free(size_str);
|
||||||
|
free(compressed_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = strlen(size_str) + 1;
|
||||||
|
free(size_str);
|
||||||
|
|
||||||
|
char* basefile_content = calloc(size+1, sizeof(char));
|
||||||
|
if (!basefile_content) {
|
||||||
|
free(compressed_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uLongf dest_len = (uLongf)size;
|
||||||
|
|
||||||
|
int result = uncompress((unsigned char*)basefile_content, &dest_len, compressed_data+offset, compressed_size);
|
||||||
|
free(compressed_data);
|
||||||
|
|
||||||
|
if (result != Z_OK) {
|
||||||
|
perror("ERROR: decompression of the base file list failed!");
|
||||||
|
free(basefile_content);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
basefile_content[dest_len] = '\0';
|
||||||
|
|
||||||
|
File* basefile = from_string(basefile_content);
|
||||||
|
char* modified_file_path = realpath(path, NULL);
|
||||||
|
File* modified_file = new_file(modified_file_path);
|
||||||
|
|
||||||
|
ActionList* diff = myers_diff(basefile, modified_file, 0, 0);
|
||||||
|
if (diff->len == 0) {
|
||||||
|
free(basefile_content);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < diff->len; idx++) {
|
||||||
|
Action action = diff->actions[idx];
|
||||||
|
if (action.type == INSERT) {
|
||||||
|
changes->insertions += 1;
|
||||||
|
} else if (action.type == DELETE) {
|
||||||
|
changes->deletions += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_diff(diff, path, root, diff_id, basefile_hash, modified_file, 0);
|
||||||
|
|
||||||
|
free(basefile_content);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
File* apply_diff(File* basefile, ActionList* diff) {
|
||||||
|
if (!basefile || !diff) return NULL;
|
||||||
|
|
||||||
|
File* copy = copy_file(basefile);
|
||||||
|
if (!copy) return NULL;
|
||||||
|
|
||||||
|
sort_action_list(diff);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < diff->len; i++) {
|
||||||
|
Action* action = &diff->actions[i];
|
||||||
|
int success = 0;
|
||||||
|
|
||||||
|
switch (action->type) {
|
||||||
|
case INSERT:
|
||||||
|
success = insert_line(copy, action->content, action->line_changed);
|
||||||
|
if (!success) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to insert line at index %zu\n", action->line_changed);
|
||||||
|
free_file(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DELETE:
|
||||||
|
success = delete_line(copy, action->line_original);
|
||||||
|
if (!success) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to delete line at index %zu\n", action->line_original);
|
||||||
|
free_file(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "ERROR: Unknown action type %d\n", action->type);
|
||||||
|
free_file(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
388
src/utilities.c
388
src/utilities.c
|
|
@ -350,48 +350,6 @@ PathType get_path_type(const char* path) {
|
||||||
return PT_OTHER;
|
return PT_OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* get_file_content(char* path) {
|
|
||||||
if (!path) return NULL;
|
|
||||||
|
|
||||||
FILE* file = fopen(path, "rb");
|
|
||||||
if (!file) { perror("ERROR: could not open file in get_file_content!"); return NULL; }
|
|
||||||
if (fseek(file, 0, SEEK_END) != 0) { fclose(file); return NULL; }
|
|
||||||
long file_size = ftell(file);
|
|
||||||
if (file_size < 0) { perror("ERROR: file size is negative in get_file_content!"); fclose(file); return NULL; }
|
|
||||||
if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); return NULL; }
|
|
||||||
size_t n = (size_t)file_size;
|
|
||||||
char* buf = (char*)calloc(n + 1, 1);
|
|
||||||
if (!buf) { fclose(file); return NULL; }
|
|
||||||
|
|
||||||
size_t got = fread(buf, 1, n, file);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
buf[got] = '\0';
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_file_content_with_size(char* path, size_t* size) {
|
|
||||||
if (!path || !size) return NULL;
|
|
||||||
|
|
||||||
FILE* file = fopen(path, "rb");
|
|
||||||
if (!file) { perror("ERROR: could not open file in get_file_content_with_size!"); return NULL; }
|
|
||||||
if (fseek(file, 0, SEEK_END) != 0) { fclose(file); return NULL; }
|
|
||||||
long file_size = ftell(file);
|
|
||||||
if (file_size < 0) { perror("ERROR: file size is negative in get_file_content_with_size!"); fclose(file); return NULL; }
|
|
||||||
if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); return NULL; }
|
|
||||||
size_t n = (size_t)file_size;
|
|
||||||
|
|
||||||
char* buf = (char*)malloc(n);
|
|
||||||
if (!buf) { fclose(file); return NULL; }
|
|
||||||
|
|
||||||
size_t got = fread(buf, 1, n, file);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
*size = got;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create directory recursively
|
// Create directory recursively
|
||||||
static int mkdir_recursive(const char *path, mode_t mode) {
|
static int mkdir_recursive(const char *path, mode_t mode) {
|
||||||
char *path_copy = strdup(path);
|
char *path_copy = strdup(path);
|
||||||
|
|
@ -449,349 +407,3 @@ int create_default_config_file(char* config_path) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int save_diff(ActionList* diff, char* path, char* root, size_t diff_id, char* basefile_hash, File* modified_file, int tree) {
|
|
||||||
size_t buffer_size = 41;
|
|
||||||
|
|
||||||
buffer_size += 1 + snprintf(NULL, 0, "%d", diff->len);
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < diff->len; idx++) {
|
|
||||||
Action action = diff->actions[idx];
|
|
||||||
if (action.type == DELETE) {
|
|
||||||
// 4 = OPBIT SPACE ... SPACE ... SPACE
|
|
||||||
buffer_size +=
|
|
||||||
4 +
|
|
||||||
snprintf(NULL, 0, "%d", action.line_original) +
|
|
||||||
snprintf(NULL, 0, "%d", action.line_changed);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 6 = OPBIT SPACE ... SPACE ... SPACE ... SPACE ... SPACE
|
|
||||||
buffer_size +=
|
|
||||||
6 +
|
|
||||||
snprintf(NULL, 0, "%d", action.line_original) +
|
|
||||||
snprintf(NULL, 0, "%d", action.line_changed) +
|
|
||||||
snprintf(NULL, 0, "%zu", strlen(modified_file->content[action.line_changed])) +
|
|
||||||
strlen(modified_file->content[action.line_changed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char tmp[buffer_size];
|
|
||||||
size_t offset = 0;
|
|
||||||
|
|
||||||
offset += snprintf(tmp, sizeof(tmp), "%s %zu ", basefile_hash, diff->len);
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < diff->len; idx++) {
|
|
||||||
Action action = diff->actions[idx];
|
|
||||||
size_t remaining = sizeof(tmp) - offset;
|
|
||||||
|
|
||||||
if (action.type == DELETE) {
|
|
||||||
offset += snprintf(tmp + offset,
|
|
||||||
remaining,
|
|
||||||
"0 %zu %zu ",
|
|
||||||
action.line_original,
|
|
||||||
action.line_changed
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += snprintf(tmp + offset, remaining,
|
|
||||||
"1 %zu %zu %zu %s ",
|
|
||||||
action.line_original,
|
|
||||||
action.line_changed,
|
|
||||||
strlen(modified_file->content[action.line_changed]),
|
|
||||||
modified_file->content[action.line_changed]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
char id[2+snprintf(NULL, 0, "%d", diff_id)+strlen(path)];
|
|
||||||
snprintf(id, sizeof(id), "%s %d", path, diff_id);
|
|
||||||
char hash[41];
|
|
||||||
if (tree) object_hash(TreeDiffObject, id, hash);
|
|
||||||
else object_hash(FileDiffObject, id, hash);
|
|
||||||
|
|
||||||
char dir_path[PATH_MAX];
|
|
||||||
char file_path[PATH_MAX];
|
|
||||||
snprintf(dir_path, sizeof(dir_path), "%s/.merk/objects/%.2s", root, hash);
|
|
||||||
mkdir(dir_path, 0755);
|
|
||||||
snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, hash+2);
|
|
||||||
|
|
||||||
if (access(file_path, F_OK) == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* fp = fopen(file_path, "wb");
|
|
||||||
if (!fp) {
|
|
||||||
perror("ERROR: cannot open path in save_diff!\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the directory structure exists
|
|
||||||
if (mkdir_recursive(dir_path, 0755) < 0 && errno != EEXIST) {
|
|
||||||
perror("ERROR: failed to create directory structure in save_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uLong originalLen = strlen(tmp) + 1;
|
|
||||||
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 save_diff!");
|
|
||||||
free(compressed);
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(fp, "%lu ", (unsigned long)originalLen);
|
|
||||||
|
|
||||||
fwrite(compressed, 1, compressedLen, fp);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
chmod(file_path, S_IRUSR | S_IRGRP | S_IROTH);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_diff(char* content, char* basefile_hash, ActionList* diff_out) {
|
|
||||||
if (!content || !basefile_hash || !diff_out) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
strncpy(basefile_hash, content, 40);
|
|
||||||
basefile_hash[40] = '\0';
|
|
||||||
idx += 41;
|
|
||||||
|
|
||||||
while (content[idx] && IS_DIGIT(content[idx])) {
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == 41) {
|
|
||||||
perror("ERROR: no length found at start of diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* number_of_actions = calloc(idx - 41 + 1, sizeof(char));
|
|
||||||
if (!number_of_actions) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(number_of_actions, content + 41, idx - 41);
|
|
||||||
number_of_actions[idx - 41] = '\0';
|
|
||||||
|
|
||||||
char* end;
|
|
||||||
long actions = strtol(number_of_actions, &end, 10);
|
|
||||||
free(number_of_actions);
|
|
||||||
|
|
||||||
if (end == number_of_actions || *end != '\0' || actions <= 0) {
|
|
||||||
perror("ERROR: invalid number of actions in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t action_idx = 0;
|
|
||||||
while (content[idx] && action_idx < (size_t)actions) {
|
|
||||||
idx++;
|
|
||||||
|
|
||||||
Action action = {0};
|
|
||||||
|
|
||||||
if (content[idx] == '0') {
|
|
||||||
idx++;
|
|
||||||
if (content[idx] != ' ') {
|
|
||||||
perror("ERROR: expected space after opbit in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
|
|
||||||
char* endptr;
|
|
||||||
action.line_original = strtol(content + idx, &endptr, 10);
|
|
||||||
if (endptr == content + idx || *endptr != ' ') {
|
|
||||||
perror("ERROR: failed to parse line_original in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx = endptr - content + 1;
|
|
||||||
|
|
||||||
action.line_changed = strtol(content + idx, &endptr, 10);
|
|
||||||
if (endptr == content + idx || (*endptr != ' ' && *endptr != '\0')) {
|
|
||||||
perror("ERROR: failed to parse line_changed in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx = endptr - content;
|
|
||||||
} else if (content[idx] == '1') {
|
|
||||||
idx++;
|
|
||||||
if (content[idx] != ' ') {
|
|
||||||
perror("ERROR: expected space after opbit in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
|
|
||||||
char* endptr;
|
|
||||||
action.line_original = strtol(content + idx, &endptr, 10);
|
|
||||||
if (endptr == content + idx || *endptr != ' ') {
|
|
||||||
perror("ERROR: failed to parse line_original in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx = endptr - content + 1;
|
|
||||||
|
|
||||||
action.line_changed = strtol(content + idx, &endptr, 10);
|
|
||||||
if (endptr == content + idx || *endptr != ' ') {
|
|
||||||
perror("ERROR: failed to parse line_changed in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx = endptr - content + 1;
|
|
||||||
|
|
||||||
size_t content_length = strtol(content + idx, &endptr, 10);
|
|
||||||
if (endptr == content + idx || *endptr != ' ') {
|
|
||||||
perror("ERROR: failed to parse content length in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx = endptr - content + 1;
|
|
||||||
|
|
||||||
action.content = strndup(content + idx, content_length);
|
|
||||||
if (!action.content) {
|
|
||||||
perror("ERROR: failed to allocate memory for action content in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
idx += content_length;
|
|
||||||
} else {
|
|
||||||
perror("ERROR: unknown action type in read_diff!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_action(diff_out, action);
|
|
||||||
|
|
||||||
action_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int save_file_diff(char* path, char* root, size_t diff_id, char* basefile_hash, Changes* changes) {
|
|
||||||
char basefile_location[2+strlen(root)+strlen("/.merk/objects/")+strlen(basefile_hash)];
|
|
||||||
snprintf(basefile_location, sizeof(basefile_location), "%s/.merk/objects/%.2s/%s", root, basefile_hash, basefile_hash+2);
|
|
||||||
|
|
||||||
size_t compressed_size;
|
|
||||||
unsigned char* compressed_data = (unsigned char*)get_file_content_with_size(basefile_location, &compressed_size);
|
|
||||||
if (!compressed_data) return 0;
|
|
||||||
|
|
||||||
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 save_file_diff!");
|
|
||||||
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 size = strtol(size_str, &end, 10);
|
|
||||||
if (end == size_str || *end != '\0') {
|
|
||||||
perror("ERROR: invalid length in base_file_list");
|
|
||||||
free(size_str);
|
|
||||||
free(compressed_data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t offset = strlen(size_str) + 1;
|
|
||||||
free(size_str);
|
|
||||||
|
|
||||||
char* basefile_content = calloc(size+1, sizeof(char));
|
|
||||||
if (!basefile_content) {
|
|
||||||
free(compressed_data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uLongf dest_len = (uLongf)size;
|
|
||||||
|
|
||||||
int result = uncompress((unsigned char*)basefile_content, &dest_len, compressed_data+offset, compressed_size);
|
|
||||||
free(compressed_data);
|
|
||||||
|
|
||||||
if (result != Z_OK) {
|
|
||||||
perror("ERROR: decompression of the base file list failed!");
|
|
||||||
free(basefile_content);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
basefile_content[dest_len] = '\0';
|
|
||||||
|
|
||||||
File* basefile = from_string(basefile_content);
|
|
||||||
char* modified_file_path = realpath(path, NULL);
|
|
||||||
File* modified_file = new_file(modified_file_path);
|
|
||||||
|
|
||||||
ActionList* diff = myers_diff(basefile, modified_file, 0, 0);
|
|
||||||
if (diff->len == 0) {
|
|
||||||
free(basefile_content);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < diff->len; idx++) {
|
|
||||||
Action action = diff->actions[idx];
|
|
||||||
if (action.type == INSERT) {
|
|
||||||
changes->insertions += 1;
|
|
||||||
} else if (action.type == DELETE) {
|
|
||||||
changes->deletions += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
save_diff(diff, path, root, diff_id, basefile_hash, modified_file, 0);
|
|
||||||
|
|
||||||
free(basefile_content);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
File* apply_diff(File* basefile, ActionList* diff) {
|
|
||||||
if (!basefile || !diff) return NULL;
|
|
||||||
|
|
||||||
File* copy = copy_file(basefile);
|
|
||||||
if (!copy) return NULL;
|
|
||||||
|
|
||||||
sort_action_list(diff);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < diff->len; i++) {
|
|
||||||
Action* action = &diff->actions[i];
|
|
||||||
int success = 0;
|
|
||||||
|
|
||||||
switch (action->type) {
|
|
||||||
case INSERT:
|
|
||||||
success = insert_line(copy, action->content, action->line_changed);
|
|
||||||
if (!success) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to insert line at index %zu\n", action->line_changed);
|
|
||||||
free_file(copy);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DELETE:
|
|
||||||
success = delete_line(copy, action->line_original);
|
|
||||||
if (!success) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to delete line at index %zu\n", action->line_original);
|
|
||||||
free_file(copy);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "ERROR: Unknown action type %d\n", action->type);
|
|
||||||
free_file(copy);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue