diff options
Diffstat (limited to 'src/libgit2/grafts.c')
-rw-r--r-- | src/libgit2/grafts.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/src/libgit2/grafts.c b/src/libgit2/grafts.c new file mode 100644 index 000000000..1d9373a56 --- /dev/null +++ b/src/libgit2/grafts.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "grafts.h" + +#include "futils.h" +#include "oid.h" +#include "oidarray.h" +#include "parse.h" + +struct git_grafts { + /* Map of `git_commit_graft`s */ + git_oidmap *commits; + + /* Type of object IDs */ + git_oid_t oid_type; + + /* File backing the graft. NULL if it's an in-memory graft */ + char *path; + unsigned char path_checksum[GIT_HASH_SHA256_SIZE]; +}; + +int git_grafts_new(git_grafts **out, git_oid_t oid_type) +{ + git_grafts *grafts; + + GIT_ASSERT_ARG(out && oid_type); + + grafts = git__calloc(1, sizeof(*grafts)); + GIT_ERROR_CHECK_ALLOC(grafts); + + if ((git_oidmap_new(&grafts->commits)) < 0) { + git__free(grafts); + return -1; + } + + grafts->oid_type = oid_type; + + *out = grafts; + return 0; +} + +int git_grafts_open( + git_grafts **out, + const char *path, + git_oid_t oid_type) +{ + git_grafts *grafts = NULL; + int error; + + GIT_ASSERT_ARG(out && path && oid_type); + + if ((error = git_grafts_new(&grafts, oid_type)) < 0) + goto error; + + grafts->path = git__strdup(path); + GIT_ERROR_CHECK_ALLOC(grafts->path); + + if ((error = git_grafts_refresh(grafts)) < 0) + goto error; + + *out = grafts; + +error: + if (error < 0) + git_grafts_free(grafts); + + return error; +} + +int git_grafts_open_or_refresh( + git_grafts **out, + const char *path, + git_oid_t oid_type) +{ + GIT_ASSERT_ARG(out && path && oid_type); + + return *out ? git_grafts_refresh(*out) : git_grafts_open(out, path, oid_type); +} + +void git_grafts_free(git_grafts *grafts) +{ + if (!grafts) + return; + git__free(grafts->path); + git_grafts_clear(grafts); + git_oidmap_free(grafts->commits); + git__free(grafts); +} + +void git_grafts_clear(git_grafts *grafts) +{ + git_commit_graft *graft; + + if (!grafts) + return; + + git_oidmap_foreach_value(grafts->commits, graft, { + git__free(graft->parents.ptr); + git__free(graft); + }); + + git_oidmap_clear(grafts->commits); +} + +int git_grafts_refresh(git_grafts *grafts) +{ + git_str contents = GIT_STR_INIT; + int error, updated = 0; + + GIT_ASSERT_ARG(grafts); + + if (!grafts->path) + return 0; + + if ((error = git_futils_readbuffer_updated(&contents, grafts->path, + grafts->path_checksum, &updated)) < 0) { + + if (error == GIT_ENOTFOUND) { + git_grafts_clear(grafts); + error = 0; + } + + goto cleanup; + } + + if (!updated) { + goto cleanup; + } + + if ((error = git_grafts_parse(grafts, contents.ptr, contents.size)) < 0) + goto cleanup; + +cleanup: + git_str_dispose(&contents); + return error; +} + +int git_grafts_parse(git_grafts *grafts, const char *buf, size_t len) +{ + git_array_oid_t parents = GIT_ARRAY_INIT; + git_parse_ctx parser; + int error; + + git_grafts_clear(grafts); + + if ((error = git_parse_ctx_init(&parser, buf, len)) < 0) + goto error; + + for (; parser.remain_len; git_parse_advance_line(&parser)) { + git_oid graft_oid; + + if ((error = git_parse_advance_oid(&graft_oid, &parser, grafts->oid_type)) < 0) { + git_error_set(GIT_ERROR_GRAFTS, "invalid graft OID at line %" PRIuZ, parser.line_num); + goto error; + } + + while (parser.line_len && git_parse_advance_expected(&parser, "\n", 1) != 0) { + git_oid *id = git_array_alloc(parents); + GIT_ERROR_CHECK_ALLOC(id); + + if ((error = git_parse_advance_expected(&parser, " ", 1)) < 0 || + (error = git_parse_advance_oid(id, &parser, grafts->oid_type)) < 0) { + git_error_set(GIT_ERROR_GRAFTS, "invalid parent OID at line %" PRIuZ, parser.line_num); + goto error; + } + } + + if ((error = git_grafts_add(grafts, &graft_oid, parents)) < 0) + goto error; + + git_array_clear(parents); + } + +error: + git_array_clear(parents); + return error; +} + +int git_grafts_add(git_grafts *grafts, const git_oid *oid, git_array_oid_t parents) +{ + git_commit_graft *graft; + git_oid *parent_oid; + int error; + size_t i; + + GIT_ASSERT_ARG(grafts && oid); + + graft = git__calloc(1, sizeof(*graft)); + GIT_ERROR_CHECK_ALLOC(graft); + + git_array_init_to_size(graft->parents, git_array_size(parents)); + git_array_foreach(parents, i, parent_oid) { + git_oid *id = git_array_alloc(graft->parents); + GIT_ERROR_CHECK_ALLOC(id); + + git_oid_cpy(id, parent_oid); + } + git_oid_cpy(&graft->oid, oid); + + if ((error = git_grafts_remove(grafts, &graft->oid)) < 0 && error != GIT_ENOTFOUND) + goto cleanup; + + if ((error = git_oidmap_set(grafts->commits, &graft->oid, graft)) < 0) + goto cleanup; + + return 0; + +cleanup: + git_array_clear(graft->parents); + git__free(graft); + return error; +} + +int git_grafts_remove(git_grafts *grafts, const git_oid *oid) +{ + git_commit_graft *graft; + int error; + + GIT_ASSERT_ARG(grafts && oid); + + if ((graft = git_oidmap_get(grafts->commits, oid)) == NULL) + return GIT_ENOTFOUND; + + if ((error = git_oidmap_delete(grafts->commits, oid)) < 0) + return error; + + git__free(graft->parents.ptr); + git__free(graft); + + return 0; +} + +int git_grafts_get(git_commit_graft **out, git_grafts *grafts, const git_oid *oid) +{ + GIT_ASSERT_ARG(out && grafts && oid); + if ((*out = git_oidmap_get(grafts->commits, oid)) == NULL) + return GIT_ENOTFOUND; + return 0; +} + +int git_grafts_oids(git_oid **out, size_t *out_len, git_grafts *grafts) +{ + git_array_oid_t array = GIT_ARRAY_INIT; + const git_oid *oid; + size_t existing, i = 0; + + GIT_ASSERT_ARG(out && grafts); + + if ((existing = git_oidmap_size(grafts->commits)) > 0) + git_array_init_to_size(array, existing); + + while (git_oidmap_iterate(NULL, grafts->commits, &i, &oid) == 0) { + git_oid *cpy = git_array_alloc(array); + GIT_ERROR_CHECK_ALLOC(cpy); + git_oid_cpy(cpy, oid); + } + + *out = array.ptr; + *out_len = array.size; + + return 0; +} + +size_t git_grafts_size(git_grafts *grafts) +{ + return git_oidmap_size(grafts->commits); +} |