Skip to content

Commit 52f2390

Browse files
author
Vicent Marti
committed
Add external API to access detailed commit attributes
The following new external methods have been added: GIT_EXTERN(const char *) git_commit_message_short(git_commit *commit); GIT_EXTERN(const char *) git_commit_message(git_commit *commit); GIT_EXTERN(time_t) git_commit_time(git_commit *commit); GIT_EXTERN(const git_commit_person *) git_commit_committer(git_commit *commit); GIT_EXTERN(const git_commit_person *) git_commit_author(git_commit *commit); GIT_EXTERN(const git_tree *) git_commit_tree(git_commit *commit); A new structure, git_commit_person has been added to represent a commit's author or committer. The parsing of a commit has been split in two phases. When adding a commit to the revision pool: - the commit's ODB object is opened - its raw contents are parsed for commit TIME, PARENTS and TREE (the minimal amount of data required to traverse the pool) - the commit's ODB object is closed When querying for extended information on a commit: - the commit's ODB object is reopened - its raw contents are parsed for the requested information - the commit's ODB object remains open to handle additional queries New unit tests have been added for the new functionality: In t0401-parse: parse_person_test In t0402-details: query_details_test Signed-off-by: Vicent Marti <tanoku@gmail.com>
1 parent 225fe21 commit 52f2390

File tree

6 files changed

+508
-127
lines changed

6 files changed

+508
-127
lines changed

src/commit.c

Lines changed: 206 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,22 @@
2929
#include "git/odb.h"
3030

3131
#define COMMIT_PRINT(commit) {\
32-
char oid[41]; oid[40] = 0;\
33-
git_oid_fmt(oid, &commit->object.id);\
34-
printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
32+
char oid[41]; oid[40] = 0;\
33+
git_oid_fmt(oid, &commit->object.id);\
34+
printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
3535
}
3636

3737
void git_commit__free(git_commit *commit)
3838
{
3939
git_commit_list_clear(&commit->parents, 0);
40+
41+
if (commit->odb_open)
42+
git_obj_close(&commit->odb_object);
43+
44+
free(commit->author);
45+
free(commit->committer);
46+
free(commit->message);
47+
free(commit->message_short);
4048
free(commit);
4149
}
4250

@@ -69,7 +77,7 @@ git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
6977
if ((commit = git_commit_lookup(pool, id)) == NULL)
7078
return NULL;
7179

72-
if (git_commit_parse_existing(commit) < 0)
80+
if (git_commit__parse_basic(commit) < 0)
7381
goto error_cleanup;
7482

7583
return commit;
@@ -79,30 +87,51 @@ git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
7987
return NULL;
8088
}
8189

82-
int git_commit_parse_existing(git_commit *commit)
90+
int git_commit__parse(git_commit *commit, unsigned int parse_flags, int close_db_object)
8391
{
8492
int error = 0;
85-
git_obj commit_obj;
8693

87-
if (commit->parsed)
88-
return 0;
94+
if (!commit->odb_open) {
95+
error = git_odb_read(&commit->odb_object, commit->object.pool->db, &commit->object.id);
96+
if (error < 0)
97+
return error;
8998

90-
error = git_odb_read(&commit_obj, commit->object.pool->db, &commit->object.id);
91-
if (error < 0)
92-
return error;
99+
if (commit->odb_object.type != GIT_OBJ_COMMIT) {
100+
git_obj_close(&commit->odb_object);
101+
return GIT_EOBJTYPE;
102+
}
93103

94-
if (commit_obj.type != GIT_OBJ_COMMIT) {
95-
error = GIT_EOBJTYPE;
96-
goto cleanup;
104+
commit->odb_open = 1;
97105
}
98106

99-
error = git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len);
107+
error = git_commit__parse_buffer(commit,
108+
commit->odb_object.data, commit->odb_object.len, parse_flags);
109+
110+
if (close_db_object) {
111+
git_obj_close(&commit->odb_object);
112+
commit->odb_open = 0;
113+
}
100114

101-
cleanup:
102-
git_obj_close(&commit_obj);
103115
return error;
104116
}
105117

118+
int git_commit__parse_basic(git_commit *commit)
119+
{
120+
int error;
121+
122+
if (commit->basic_parse)
123+
return 0;
124+
125+
error = git_commit__parse(commit,
126+
(GIT_COMMIT_TREE | GIT_COMMIT_PARENTS | GIT_COMMIT_TIME), 1);
127+
128+
if (error < 0)
129+
return error;
130+
131+
commit->basic_parse = 1;
132+
return 0;
133+
}
134+
106135
git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
107136
{
108137
git_commit *commit = NULL;
@@ -131,35 +160,67 @@ git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
131160
return commit;
132161
}
133162

134-
int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end)
163+
int git_commit__parse_person(git_commit_person *person, char **buffer_out,
164+
const char *buffer_end, const char *header)
135165
{
136-
if (memcmp(buffer, "author ", 7) != 0)
166+
const size_t header_len = strlen(header);
167+
168+
int i;
169+
char *buffer = *buffer_out;
170+
char *line_end, *name, *email;
171+
172+
line_end = memchr(buffer, '\n', buffer_end - buffer);
173+
if (!line_end)
137174
return GIT_EOBJCORRUPTED;
138175

139-
buffer = memchr(buffer, '\n', buffer_end - buffer);
140-
if (!buffer || ++buffer >= buffer_end)
176+
if (buffer + (header_len + 1) > line_end)
141177
return GIT_EOBJCORRUPTED;
142178

143-
if (memcmp(buffer, "committer ", 10) != 0)
179+
if (memcmp(buffer, header, header_len) != 0)
144180
return GIT_EOBJCORRUPTED;
145181

146-
buffer = memchr(buffer, '>', buffer_end - buffer);
147-
if (!buffer || ++buffer >= buffer_end)
182+
buffer += header_len;
183+
184+
185+
/* Parse name field */
186+
for (i = 0, name = person->name;
187+
i < 64 && buffer < line_end && *buffer != '<';
188+
++i)
189+
*name++ = *buffer++;
190+
191+
*(name - 1) = 0;
192+
193+
while (buffer < line_end && *buffer != '<')
194+
buffer++;
195+
196+
if (++buffer >= line_end)
148197
return GIT_EOBJCORRUPTED;
149198

150-
*commit_time = strtol(buffer, &buffer, 10);
199+
/* Parse email field */
200+
for (i = 0, email = person->email;
201+
i < 64 && buffer < line_end && *buffer != '>';
202+
++i)
203+
*email++ = *buffer++;
204+
205+
*email = 0;
206+
207+
while (buffer < line_end && *buffer != '>')
208+
buffer++;
151209

152-
if (*commit_time == 0)
210+
if (++buffer >= line_end)
153211
return GIT_EOBJCORRUPTED;
154212

155-
buffer = memchr(buffer, '\n', buffer_end - buffer);
156-
if (!buffer || ++buffer >= buffer_end)
213+
person->time = strtol(buffer, &buffer, 10);
214+
215+
if (person->time == 0)
157216
return GIT_EOBJCORRUPTED;
158217

159-
return (buffer < buffer_end) ? 0 : -1;
218+
*buffer_out = (line_end + 1);
219+
return 0;
160220
}
161221

162-
int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header)
222+
int git_commit__parse_oid(git_oid *oid, char **buffer_out,
223+
const char *buffer_end, const char *header)
163224
{
164225
const size_t sha_len = GIT_OID_HEXSZ;
165226
const size_t header_len = strlen(header);
@@ -183,28 +244,33 @@ int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_en
183244
return 0;
184245
}
185246

186-
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
247+
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags)
187248
{
188249
char *buffer = (char *)data;
189250
const char *buffer_end = (char *)data + len;
190251

191252
git_oid oid;
192-
193-
if (commit->parsed)
194-
return 0;
253+
git_commit_person person;
195254

196255
if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
197256
return GIT_EOBJCORRUPTED;
198257

199-
commit->tree = git_tree_lookup(commit->object.pool, &oid);
258+
if (parse_flags & GIT_COMMIT_TREE)
259+
commit->tree = git_tree_lookup(commit->object.pool, &oid);
200260

201261
/*
202262
* TODO: commit grafts!
203263
*/
204264

265+
if (parse_flags & GIT_COMMIT_PARENTS)
266+
git_commit_list_clear(&commit->parents, 0);
267+
205268
while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
206269
git_commit *parent;
207270

271+
if ((parse_flags & GIT_COMMIT_PARENTS) == 0)
272+
continue;
273+
208274
if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
209275
return GIT_ENOTFOUND;
210276

@@ -216,14 +282,117 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
216282
return GIT_ENOMEM;
217283
}
218284

219-
if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
285+
if (git_commit__parse_person(&person, &buffer, buffer_end, "author ") < 0)
220286
return GIT_EOBJCORRUPTED;
221287

222-
commit->parsed = 1;
288+
if (parse_flags & GIT_COMMIT_AUTHOR) {
289+
if (commit->author)
290+
free(commit->author);
291+
292+
commit->author = git__malloc(sizeof(git_commit_person));
293+
memcpy(commit->author, &person, sizeof(git_commit_person));
294+
}
295+
296+
if (git_commit__parse_person(&person, &buffer, buffer_end, "committer ") < 0)
297+
return GIT_EOBJCORRUPTED;
298+
299+
if (parse_flags & GIT_COMMIT_TIME)
300+
commit->commit_time = person.time;
301+
302+
if (parse_flags & GIT_COMMIT_COMMITTER) {
303+
if (commit->committer)
304+
free(commit->committer);
305+
306+
commit->committer = git__malloc(sizeof(git_commit_person));
307+
memcpy(commit->committer, &person, sizeof(git_commit_person));
308+
}
309+
310+
/* parse commit message */
311+
while (buffer <= buffer_end && *buffer == '\n')
312+
buffer++;
313+
314+
if (buffer < buffer_end)
315+
{
316+
if (parse_flags & GIT_COMMIT_MESSAGE) {
317+
size_t message_len = buffer_end - buffer;
318+
319+
commit->message = git__malloc(message_len + 1);
320+
memcpy(commit->message, buffer, message_len);
321+
commit->message[message_len] = 0;
322+
}
323+
324+
if (parse_flags & GIT_COMMIT_MESSAGE_SHORT) {
325+
char *line_end;
326+
size_t message_len;
327+
328+
line_end = memchr(buffer, '\n', buffer_end - buffer);
329+
message_len = line_end - buffer;
330+
331+
commit->message_short = git__malloc(message_len + 1);
332+
memcpy(commit->message_short, buffer, message_len);
333+
commit->message_short[message_len] = 0;
334+
}
335+
}
223336

224337
return 0;
225338
}
226339

340+
const git_tree *git_commit_tree(git_commit *commit)
341+
{
342+
if (commit->tree)
343+
return commit->tree;
344+
345+
git_commit__parse(commit, GIT_COMMIT_TREE, 0);
346+
return commit->tree;
347+
}
348+
349+
const git_commit_person *git_commit_author(git_commit *commit)
350+
{
351+
if (commit->author)
352+
return commit->author;
353+
354+
git_commit__parse(commit, GIT_COMMIT_AUTHOR, 0);
355+
return commit->author;
356+
}
357+
358+
const git_commit_person *git_commit_committer(git_commit *commit)
359+
{
360+
if (commit->committer)
361+
return commit->committer;
362+
363+
git_commit__parse(commit, GIT_COMMIT_COMMITTER, 0);
364+
return commit->committer;
365+
}
366+
367+
time_t git_commit_time(git_commit *commit)
368+
{
369+
if (commit->commit_time)
370+
return commit->commit_time;
371+
372+
git_commit__parse(commit, GIT_COMMIT_TIME, 0);
373+
return commit->commit_time;
374+
}
375+
376+
const char *git_commit_message(git_commit *commit)
377+
{
378+
if (commit->message)
379+
return commit->message;
380+
381+
git_commit__parse(commit, GIT_COMMIT_MESSAGE, 0);
382+
return commit->message;
383+
}
384+
385+
const char *git_commit_message_short(git_commit *commit)
386+
{
387+
if (commit->message_short)
388+
return commit->message_short;
389+
390+
git_commit__parse(commit, GIT_COMMIT_MESSAGE_SHORT, 0);
391+
return commit->message_short;
392+
}
393+
394+
395+
227396
int git_commit_list_push_back(git_commit_list *list, git_commit *commit)
228397
{
229398
git_commit_node *node = NULL;

src/commit.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,46 @@ struct git_commit_list {
2323
typedef struct git_commit_list git_commit_list;
2424
typedef struct git_commit_node git_commit_node;
2525

26+
#define GIT_COMMIT_TREE (1 << 1)
27+
#define GIT_COMMIT_PARENTS (1 << 2)
28+
#define GIT_COMMIT_AUTHOR (1 << 3)
29+
#define GIT_COMMIT_COMMITTER (1 << 4)
30+
#define GIT_COMMIT_TIME (1 << 5)
31+
#define GIT_COMMIT_MESSAGE (1 << 6)
32+
#define GIT_COMMIT_MESSAGE_SHORT (1 << 7)
33+
#define GIT_COMMIT_FOOTERS (1 << 8)
2634

2735
struct git_commit {
2836
git_revpool_object object;
37+
git_obj odb_object;
2938

3039
time_t commit_time;
3140
git_commit_list parents;
3241

3342
git_tree *tree;
43+
git_commit_person *author;
44+
git_commit_person *committer;
45+
46+
char *message;
47+
char *message_short;
3448

3549
unsigned short in_degree;
36-
unsigned parsed:1,
50+
unsigned basic_parse:1,
51+
odb_open:1,
3752
seen:1,
3853
uninteresting:1,
3954
topo_delay:1,
40-
flags:26;
55+
flags:25;
4156
};
4257

4358
void git_commit__free(git_commit *c);
59+
int git_commit__parse(git_commit *commit, unsigned int flags, int close_odb);
60+
int git_commit__parse_basic(git_commit *commit);
4461
int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
45-
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len);
46-
int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end);
62+
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags);
63+
int git_commit__parse_person(git_commit_person *person, char **buffer_out, const char *buffer_end, const char *header);
4764
void git_commit__mark_uninteresting(git_commit *commit);
4865

49-
int git_commit_parse_existing(git_commit *commit);
50-
5166

5267
int git_commit_list_push_back(git_commit_list *list, git_commit *commit);
5368
int git_commit_list_push_front(git_commit_list *list, git_commit *commit);

0 commit comments

Comments
 (0)