git-rev-list: add "--bisect" flag to find the "halfway" point
[git/git.git] / commit.c
CommitLineData
000182ea 1#include <ctype.h>
961784ee 2#include "tag.h"
175785e5
DB
3#include "commit.h"
4#include "cache.h"
175785e5
DB
5
6const char *commit_type = "commit";
7
5d6ccf5c 8static struct commit *check_commit(struct object *obj, const unsigned char *sha1)
961784ee
LT
9{
10 if (obj->type != commit_type) {
11 error("Object %s is a %s, not a commit",
12 sha1_to_hex(sha1), obj->type);
13 return NULL;
14 }
15 return (struct commit *) obj;
16}
17
5d6ccf5c 18struct commit *lookup_commit_reference(const unsigned char *sha1)
961784ee
LT
19{
20 struct object *obj = parse_object(sha1);
21
22 if (!obj)
23 return NULL;
24 if (obj->type == tag_type)
25 obj = ((struct tag *)obj)->tagged;
26 return check_commit(obj, sha1);
27}
28
5d6ccf5c 29struct commit *lookup_commit(const unsigned char *sha1)
175785e5
DB
30{
31 struct object *obj = lookup_object(sha1);
32 if (!obj) {
812666c8 33 struct commit *ret = xmalloc(sizeof(struct commit));
175785e5
DB
34 memset(ret, 0, sizeof(struct commit));
35 created_object(sha1, &ret->object);
d32987be 36 ret->object.type = commit_type;
175785e5
DB
37 return ret;
38 }
d1af002d
NP
39 if (!obj->type)
40 obj->type = commit_type;
961784ee 41 return check_commit(obj, sha1);
175785e5
DB
42}
43
44static unsigned long parse_commit_date(const char *buf)
45{
46 unsigned long date;
47
48 if (memcmp(buf, "author", 6))
49 return 0;
50 while (*buf++ != '\n')
51 /* nada */;
52 if (memcmp(buf, "committer", 9))
53 return 0;
54 while (*buf++ != '>')
55 /* nada */;
56 date = strtoul(buf, NULL, 10);
57 if (date == ULONG_MAX)
58 date = 0;
59 return date;
60}
61
bd2c39f5 62int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
175785e5 63{
bd2c39f5 64 void *bufptr = buffer;
175785e5 65 unsigned char parent[20];
bd2c39f5 66
175785e5
DB
67 if (item->object.parsed)
68 return 0;
69 item->object.parsed = 1;
175785e5
DB
70 get_sha1_hex(bufptr + 5, parent);
71 item->tree = lookup_tree(parent);
235ac407
LT
72 if (item->tree)
73 add_ref(&item->object, &item->tree->object);
175785e5
DB
74 bufptr += 46; /* "tree " + "hex sha1" + "\n" */
75 while (!memcmp(bufptr, "parent ", 7) &&
76 !get_sha1_hex(bufptr + 7, parent)) {
dd97f850 77 struct commit *new_parent = lookup_commit(parent);
235ac407
LT
78 if (new_parent) {
79 commit_list_insert(new_parent, &item->parents);
80 add_ref(&item->object, &new_parent->object);
81 }
175785e5
DB
82 bufptr += 48;
83 }
84 item->date = parse_commit_date(bufptr);
175785e5
DB
85 return 0;
86}
87
bd2c39f5
NP
88int parse_commit(struct commit *item)
89{
90 char type[20];
91 void *buffer;
92 unsigned long size;
93 int ret;
94
95 if (item->object.parsed)
96 return 0;
97 buffer = read_sha1_file(item->object.sha1, type, &size);
98 if (!buffer)
99 return error("Could not read %s",
100 sha1_to_hex(item->object.sha1));
101 if (strcmp(type, commit_type)) {
102 free(buffer);
103 return error("Object %s not a commit",
104 sha1_to_hex(item->object.sha1));
105 }
106 ret = parse_commit_buffer(item, buffer, size);
3ff1fbbb
LT
107 if (!ret) {
108 item->buffer = buffer;
109 return 0;
110 }
bd2c39f5
NP
111 free(buffer);
112 return ret;
113}
114
ac5155ef 115struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
dd97f850 116{
812666c8 117 struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
dd97f850
DB
118 new_list->item = item;
119 new_list->next = *list_p;
120 *list_p = new_list;
ac5155ef 121 return new_list;
dd97f850
DB
122}
123
175785e5
DB
124void free_commit_list(struct commit_list *list)
125{
126 while (list) {
127 struct commit_list *temp = list;
128 list = temp->next;
129 free(temp);
130 }
131}
dd97f850 132
a3437b8c 133void insert_by_date(struct commit_list **list, struct commit *item)
dd97f850
DB
134{
135 struct commit_list **pp = list;
136 struct commit_list *p;
137 while ((p = *pp) != NULL) {
138 if (p->item->date < item->date) {
139 break;
140 }
141 pp = &p->next;
142 }
143 commit_list_insert(item, pp);
144}
145
146
147void sort_by_date(struct commit_list **list)
148{
149 struct commit_list *ret = NULL;
150 while (*list) {
151 insert_by_date(&ret, (*list)->item);
152 *list = (*list)->next;
153 }
154 *list = ret;
155}
156
58e28af6
DB
157struct commit *pop_most_recent_commit(struct commit_list **list,
158 unsigned int mark)
dd97f850
DB
159{
160 struct commit *ret = (*list)->item;
161 struct commit_list *parents = ret->parents;
162 struct commit_list *old = *list;
163
164 *list = (*list)->next;
165 free(old);
166
167 while (parents) {
4056c091 168 struct commit *commit = parents->item;
58e28af6
DB
169 parse_commit(commit);
170 if (!(commit->object.flags & mark)) {
171 commit->object.flags |= mark;
4056c091
LT
172 insert_by_date(list, commit);
173 }
dd97f850
DB
174 parents = parents->next;
175 }
176 return ret;
177}
e3bc7a3b
LT
178
179/*
180 * Generic support for pretty-printing the header
181 */
182static int get_one_line(const char *msg, unsigned long len)
183{
184 int ret = 0;
185
186 while (len--) {
187 char c = *msg++;
188 ret++;
189 if (c == '\n')
190 break;
191 if (!c)
192 return 0;
193 }
194 return ret;
195}
196
000182ea 197static int add_author_info(enum cmit_fmt fmt, char *buf, const char *line, int len)
e3bc7a3b
LT
198{
199 char *date;
200 unsigned int namelen;
201 unsigned long time;
000182ea 202 int tz, ret;
e3bc7a3b
LT
203
204 line += strlen("author ");
205 date = strchr(line, '>');
206 if (!date)
207 return 0;
208 namelen = ++date - line;
209 time = strtoul(date, &date, 10);
210 tz = strtol(date, NULL, 10);
211
000182ea
LT
212 ret = sprintf(buf, "Author: %.*s\n", namelen, line);
213 if (fmt == CMIT_FMT_MEDIUM)
214 ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
215 return ret;
216}
217
218static int is_empty_line(const char *line, int len)
219{
220 while (len && isspace(line[len-1]))
221 len--;
222 return !len;
e3bc7a3b
LT
223}
224
000182ea 225unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
e3bc7a3b 226{
000182ea 227 int hdr = 1, body = 0;
e3bc7a3b
LT
228 unsigned long offset = 0;
229
230 for (;;) {
231 const char *line = msg;
232 int linelen = get_one_line(msg, len);
233
234 if (!linelen)
235 break;
236
237 /*
238 * We want some slop for indentation and a possible
239 * final "...". Thus the "+ 20".
240 */
241 if (offset + linelen + 20 > space) {
242 memcpy(buf + offset, " ...\n", 8);
243 offset += 8;
244 break;
245 }
246
247 msg += linelen;
248 len -= linelen;
e3bc7a3b 249 if (hdr) {
000182ea
LT
250 if (linelen == 1) {
251 hdr = 0;
252 buf[offset++] = '\n';
253 continue;
254 }
255 if (fmt == CMIT_FMT_RAW) {
256 memcpy(buf + offset, line, linelen);
257 offset += linelen;
258 continue;
259 }
e3bc7a3b 260 if (!memcmp(line, "author ", 7))
000182ea 261 offset += add_author_info(fmt, buf + offset, line, linelen);
e3bc7a3b
LT
262 continue;
263 }
000182ea
LT
264
265 if (is_empty_line(line, linelen)) {
266 if (!body)
267 continue;
268 if (fmt == CMIT_FMT_SHORT)
269 break;
270 } else {
271 body = 1;
272 }
e3bc7a3b
LT
273 memset(buf + offset, ' ', 4);
274 memcpy(buf + offset + 4, line, linelen);
275 offset += linelen + 4;
276 }
277 /* Make sure there is an EOLN */
278 if (buf[offset - 1] != '\n')
279 buf[offset++] = '\n';
280 buf[offset] = '\0';
281 return offset;
282}
a3437b8c
JS
283
284struct commit *pop_commit(struct commit_list **stack)
285{
286 struct commit_list *top = *stack;
287 struct commit *item = top ? top->item : NULL;
288
289 if (top) {
290 *stack = top->next;
291 free(top);
292 }
293 return item;
294}
295
296int count_parents(struct commit * commit)
297{
298 int count = 0;
299 struct commit_list * parents = commit->parents;
300 for (count=0;parents; parents=parents->next,count++)
301 ;
302 return count;
303}
304