summaryrefslogtreecommitdiff
path: root/src/commit.c
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2014-05-12 14:38:39 -0700
committerRussell Belfer <rb@github.com>2014-05-15 14:11:19 -0700
commit575f107704255254f52d197240d55f2030af0454 (patch)
treeaf5149788297f3e9ec17992b342a3005eab36e31 /src/commit.c
parent88b1b36dfcc6b406f2b6f21e0e042071984b3b90 (diff)
downloadlibgit2-rb/object-parse-flexibility.tar.gz
Add lax parsing for commit and tag objectsrb/object-parse-flexibility
This changes the behavior of object parsing for commits and tags so that even when bad data is found inside the object, we will continue to try to parse as much of the object as we can. The existing functions (`git_object_lookup` for example) will still delete the partially parsed object before returning an error, but this also adds a new function `git_object_lookup_lax` that will still return the error, but will also return the object with the partial data (if we got far enough along in the parsing process to even create the base object).
Diffstat (limited to 'src/commit.c')
-rw-r--r--src/commit.c92
1 files changed, 23 insertions, 69 deletions
diff --git a/src/commit.c b/src/commit.c
index 227d5c4a5..1388cb3f5 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -328,87 +328,41 @@ int git_commit_amend(
return error;
}
-int git_commit__parse(void *_commit, git_odb_object *odb_obj)
+int git_commit__parse(void *obj, git_odb_object *odb_obj)
{
- git_commit *commit = _commit;
- const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
- const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
- git_oid parent_id;
- size_t header_len;
-
- buffer = buffer_start;
+ int error = 0;
+ const char *start = git_odb_object_data(odb_obj);
+ const char *end = start + git_odb_object_size(odb_obj);
+ const char *body = NULL;
+ git_commit *commit = obj;
+ git_object_parse_t parser[] = {
+ { "tree", 4, GIT_PARSE_OID, { .id = &commit->tree_id } },
+ { "parent", 6, GIT_PARSE_OID_ARRAY, { .ids = &commit->parent_ids } },
+ { "author", 6, GIT_PARSE_SIGNATURE, { .sig = &commit->author } },
+ { "committer", 9, GIT_PARSE_SIGNATURE, { .sig = &commit->committer } },
+ { NULL, 0, GIT_PARSE_MODE_OPTIONAL },
+ { "encoding", 8, GIT_PARSE_TO_EOL, { .text = &commit->message_encoding } },
+ { NULL, 0, GIT_PARSE_BODY, { .body = &body } },
+ };
/* Allocate for one, which will allow not to realloc 90% of the time */
git_array_init_to_size(commit->parent_ids, 1);
GITERR_CHECK_ARRAY(commit->parent_ids);
- /* The tree is always the first field */
- if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
- goto bad_buffer;
-
- /*
- * TODO: commit grafts!
- */
-
- while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
- git_oid *new_id = git_array_alloc(commit->parent_ids);
- GITERR_CHECK_ALLOC(new_id);
-
- git_oid_cpy(new_id, &parent_id);
- }
-
- commit->author = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(commit->author);
-
- if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
- return -1;
-
- /* Always parse the committer; we need the commit time */
- commit->committer = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(commit->committer);
-
- if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
- return -1;
-
- /* Parse add'l header entries */
- while (buffer < buffer_end) {
- const char *eoln = buffer;
- if (buffer[-1] == '\n' && buffer[0] == '\n')
- break;
-
- while (eoln < buffer_end && *eoln != '\n')
- ++eoln;
+ error = git_object__parse_lines(GIT_OBJ_COMMIT, parser, start, end);
- if (git__prefixcmp(buffer, "encoding ") == 0) {
- buffer += strlen("encoding ");
-
- commit->message_encoding = git__strndup(buffer, eoln - buffer);
- GITERR_CHECK_ALLOC(commit->message_encoding);
+ /* strdup raw version of header data and commit message */
+ if (body != NULL) {
+ if (body > start) {
+ commit->raw_header = git__strndup(start, (body - 1) - start);
+ GITERR_CHECK_ALLOC(commit->raw_header);
}
- if (eoln < buffer_end && *eoln == '\n')
- ++eoln;
- buffer = eoln;
- }
-
- header_len = buffer - buffer_start;
- commit->raw_header = git__strndup(buffer_start, header_len);
- GITERR_CHECK_ALLOC(commit->raw_header);
-
- /* point "buffer" to data after header, +1 for the final LF */
- buffer = buffer_start + header_len + 1;
-
- /* extract commit message */
- if (buffer <= buffer_end) {
- commit->raw_message = git__strndup(buffer, buffer_end - buffer);
+ commit->raw_message = git__strndup(body, end - body);
GITERR_CHECK_ALLOC(commit->raw_message);
}
- return 0;
-
-bad_buffer:
- giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
- return -1;
+ return error;
}
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \