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
3737void 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+
106135git_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+
227396int git_commit_list_push_back (git_commit_list * list , git_commit * commit )
228397{
229398 git_commit_node * node = NULL ;
0 commit comments