revwalk: relax the parsing of the commit time
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
diff --git a/src/revwalk.c b/src/revwalk.c
index 7bcdc4a..9dff283 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -188,7 +188,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
unsigned char *buffer = raw->data;
unsigned char *buffer_end = buffer + raw->len;
- unsigned char *parents_start;
+ unsigned char *parents_start, *committer_start;
int i, parents = 0;
int commit_time;
@@ -219,17 +219,34 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
commit->out_degree = (unsigned short)parents;
+ if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
+ return commit_error(commit, "object is corrupted");
+
+ buffer++;
+
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
return commit_error(commit, "object is corrupted");
- if ((buffer = memchr(buffer, '<', buffer_end - buffer)) == NULL ||
- (buffer = memchr(buffer, '>', buffer_end - buffer)) == NULL)
- return commit_error(commit, "malformed author information");
+ /* Skip trailing spaces */
+ while (buffer > committer_start && git__isspace(*buffer))
+ buffer--;
+
+ /* Seek for the begining of the pack of digits */
+ while (buffer > committer_start && git__isdigit(*buffer))
+ buffer--;
- while (*buffer == '>' || git__isspace(*buffer))
- buffer++;
+ /* Skip potential timezone offset */
+ if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) {
+ buffer--;
+
+ while (buffer > committer_start && git__isspace(*buffer))
+ buffer--;
+
+ while (buffer > committer_start && git__isdigit(*buffer))
+ buffer--;
+ }
- if (git__strtol32(&commit_time, (char *)buffer, NULL, 10) < 0)
+ if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
return commit_error(commit, "cannot parse commit time");
commit->time = (time_t)commit_time;
diff --git a/tests-clar/revwalk/signatureparsing.c b/tests-clar/revwalk/signatureparsing.c
new file mode 100644
index 0000000..94de1a3
--- /dev/null
+++ b/tests-clar/revwalk/signatureparsing.c
@@ -0,0 +1,44 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_revwalk *_walk;
+
+void test_revwalk_signatureparsing__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_signatureparsing__cleanup(void)
+{
+ git_revwalk_free(_walk);
+ git_repository_free(_repo);
+}
+
+void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_brackets(void)
+{
+ git_reference *ref;
+ git_oid commit_oid;
+ git_commit *commit;
+ const git_signature *signature;
+
+ /*
+ * The branch below points at a commit with angle brackets in the committer/author name
+ * committer <Yu V. Bin Haacked> <foo@example.com> 1323847743 +0100
+ */
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/haacked"));
+
+ git_revwalk_push(_walk, git_reference_oid(ref));
+ cl_git_pass(git_revwalk_next(&commit_oid, _walk));
+
+ cl_git_pass(git_commit_lookup(&commit, _repo, git_reference_oid(ref)));
+
+ signature = git_commit_committer(commit);
+ cl_assert_equal_s("Yu V. Bin Haacked", signature->email);
+ cl_assert_equal_s("", signature->name);
+ cl_assert_equal_i(1323847743, (int)signature->when.time);
+ cl_assert_equal_i(60, signature->when.offset);
+
+ git_commit_free(commit);
+ git_reference_free(ref);
+}