[PATCH] Unify usage strings declaration
[git.git] / commit.c
1 #include <ctype.h>
2 #include "tag.h"
3 #include "commit.h"
4 #include "cache.h"
5
6 struct sort_node
7 {
8         /*
9          * the number of children of the associated commit
10          * that also occur in the list being sorted.
11          */
12         unsigned int indegree;
13
14         /*
15          * reference to original list item that we will re-use
16          * on output.
17          */
18         struct commit_list * list_item;
19
20 };
21
22 const char *commit_type = "commit";
23
24 enum cmit_fmt get_commit_format(const char *arg)
25 {
26         if (!*arg)
27                 return CMIT_FMT_DEFAULT;
28         if (!strcmp(arg, "=raw"))
29                 return CMIT_FMT_RAW;
30         if (!strcmp(arg, "=medium"))
31                 return CMIT_FMT_MEDIUM;
32         if (!strcmp(arg, "=short"))
33                 return CMIT_FMT_SHORT;
34         if (!strcmp(arg, "=full"))
35                 return CMIT_FMT_FULL;
36         die("invalid --pretty format");
37 }
38
39 static struct commit *check_commit(struct object *obj, const unsigned char *sha1)
40 {
41         if (obj->type != commit_type) {
42                 error("Object %s is a %s, not a commit", 
43                       sha1_to_hex(sha1), obj->type);
44                 return NULL;
45         }
46         return (struct commit *) obj;
47 }
48
49 struct commit *lookup_commit_reference(const unsigned char *sha1)
50 {
51         struct object *obj = parse_object(sha1);
52
53         if (!obj)
54                 return NULL;
55         while (obj->type == tag_type)
56                 obj = parse_object(((struct tag *)obj)->tagged->sha1);
57
58         return check_commit(obj, sha1);
59 }
60
61 struct commit *lookup_commit(const unsigned char *sha1)
62 {
63         struct object *obj = lookup_object(sha1);
64         if (!obj) {
65                 struct commit *ret = xmalloc(sizeof(struct commit));
66                 memset(ret, 0, sizeof(struct commit));
67                 created_object(sha1, &ret->object);
68                 ret->object.type = commit_type;
69                 return ret;
70         }
71         if (!obj->type)
72                 obj->type = commit_type;
73         return check_commit(obj, sha1);
74 }
75
76 static unsigned long parse_commit_date(const char *buf)
77 {
78         unsigned long date;
79
80         if (memcmp(buf, "author", 6))
81                 return 0;
82         while (*buf++ != '\n')
83                 /* nada */;
84         if (memcmp(buf, "committer", 9))
85                 return 0;
86         while (*buf++ != '>')
87                 /* nada */;
88         date = strtoul(buf, NULL, 10);
89         if (date == ULONG_MAX)
90                 date = 0;
91         return date;
92 }
93
94 int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
95 {
96         char *bufptr = buffer;
97         unsigned char parent[20];
98         struct commit_list **pptr;
99
100         if (item->object.parsed)
101                 return 0;
102         item->object.parsed = 1;
103         if (memcmp(bufptr, "tree ", 5))
104                 return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
105         if (get_sha1_hex(bufptr + 5, parent) < 0)
106                 return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1));
107         item->tree = lookup_tree(parent);
108         if (item->tree)
109                 add_ref(&item->object, &item->tree->object);
110         bufptr += 46; /* "tree " + "hex sha1" + "\n" */
111         pptr = &item->parents;
112         while (!memcmp(bufptr, "parent ", 7)) {
113                 struct commit *new_parent;
114
115                 if (get_sha1_hex(bufptr + 7, parent) || bufptr[47] != '\n')
116                         return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
117                 new_parent = lookup_commit(parent);
118                 if (new_parent) {
119                         pptr = &commit_list_insert(new_parent, pptr)->next;
120                         add_ref(&item->object, &new_parent->object);
121                 }
122                 bufptr += 48;
123         }
124         item->date = parse_commit_date(bufptr);
125         return 0;
126 }
127
128 int parse_commit(struct commit *item)
129 {
130         char type[20];
131         void *buffer;
132         unsigned long size;
133         int ret;
134
135         if (item->object.parsed)
136                 return 0;
137         buffer = read_sha1_file(item->object.sha1, type, &size);
138         if (!buffer)
139                 return error("Could not read %s",
140                              sha1_to_hex(item->object.sha1));
141         if (strcmp(type, commit_type)) {
142                 free(buffer);
143                 return error("Object %s not a commit",
144                              sha1_to_hex(item->object.sha1));
145         }
146         ret = parse_commit_buffer(item, buffer, size);
147         if (!ret) {
148                 item->buffer = buffer;
149                 return 0;
150         }
151         free(buffer);
152         return ret;
153 }
154
155 struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
156 {
157         struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
158         new_list->item = item;
159         new_list->next = *list_p;
160         *list_p = new_list;
161         return new_list;
162 }
163
164 void free_commit_list(struct commit_list *list)
165 {
166         while (list) {
167                 struct commit_list *temp = list;
168                 list = temp->next;
169                 free(temp);
170         }
171 }
172
173 struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
174 {
175         struct commit_list **pp = list;
176         struct commit_list *p;
177         while ((p = *pp) != NULL) {
178                 if (p->item->date < item->date) {
179                         break;
180                 }
181                 pp = &p->next;
182         }
183         return commit_list_insert(item, pp);
184 }
185
186         
187 void sort_by_date(struct commit_list **list)
188 {
189         struct commit_list *ret = NULL;
190         while (*list) {
191                 insert_by_date((*list)->item, &ret);
192                 *list = (*list)->next;
193         }
194         *list = ret;
195 }
196
197 struct commit *pop_most_recent_commit(struct commit_list **list,
198                                       unsigned int mark)
199 {
200         struct commit *ret = (*list)->item;
201         struct commit_list *parents = ret->parents;
202         struct commit_list *old = *list;
203
204         *list = (*list)->next;
205         free(old);
206
207         while (parents) {
208                 struct commit *commit = parents->item;
209                 parse_commit(commit);
210                 if (!(commit->object.flags & mark)) {
211                         commit->object.flags |= mark;
212                         insert_by_date(commit, list);
213                 }
214                 parents = parents->next;
215         }
216         return ret;
217 }
218
219 /*
220  * Generic support for pretty-printing the header
221  */
222 static int get_one_line(const char *msg, unsigned long len)
223 {
224         int ret = 0;
225
226         while (len--) {
227                 char c = *msg++;
228                 ret++;
229                 if (c == '\n')
230                         break;
231                 if (!c)
232                         return 0;
233         }
234         return ret;
235 }
236
237 static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
238 {
239         char *date;
240         unsigned int namelen;
241         unsigned long time;
242         int tz, ret;
243
244         date = strchr(line, '>');
245         if (!date)
246                 return 0;
247         namelen = ++date - line;
248         time = strtoul(date, &date, 10);
249         tz = strtol(date, NULL, 10);
250
251         ret = sprintf(buf, "%s: %.*s\n", what, namelen, line);
252         if (fmt == CMIT_FMT_MEDIUM)
253                 ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
254         return ret;
255 }
256
257 static int is_empty_line(const char *line, int len)
258 {
259         while (len && isspace(line[len-1]))
260                 len--;
261         return !len;
262 }
263
264 static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents)
265 {
266         int offset = 0;
267         switch (parents) {
268         case 1:
269                 break;
270         case 2:
271                 /* Go back to the previous line: 40 characters of previous parent, and one '\n' */
272                 offset = sprintf(buf, "Merge: %.40s\n", line-41);
273                 /* Fallthrough */
274         default:
275                 /* Replace the previous '\n' with a space */
276                 buf[offset-1] = ' ';
277                 offset += sprintf(buf + offset, "%.40s\n", line+7);
278         }
279         return offset;
280 }
281
282 unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
283 {
284         int hdr = 1, body = 0;
285         unsigned long offset = 0;
286         int parents = 0;
287
288         for (;;) {
289                 const char *line = msg;
290                 int linelen = get_one_line(msg, len);
291
292                 if (!linelen)
293                         break;
294
295                 /*
296                  * We want some slop for indentation and a possible
297                  * final "...". Thus the "+ 20".
298                  */
299                 if (offset + linelen + 20 > space) {
300                         memcpy(buf + offset, "    ...\n", 8);
301                         offset += 8;
302                         break;
303                 }
304
305                 msg += linelen;
306                 len -= linelen;
307                 if (hdr) {
308                         if (linelen == 1) {
309                                 hdr = 0;
310                                 buf[offset++] = '\n';
311                                 continue;
312                         }
313                         if (fmt == CMIT_FMT_RAW) {
314                                 memcpy(buf + offset, line, linelen);
315                                 offset += linelen;
316                                 continue;
317                         }
318                         if (!memcmp(line, "parent ", 7)) {
319                                 if (linelen != 48)
320                                         die("bad parent line in commit");
321                                 offset += add_parent_info(fmt, buf + offset, line, ++parents);
322                         }
323                         if (!memcmp(line, "author ", 7))
324                                 offset += add_user_info("Author", fmt, buf + offset, line + 7);
325                         if (fmt == CMIT_FMT_FULL) {
326                                 if (!memcmp(line, "committer ", 10))
327                                         offset += add_user_info("Commit", fmt, buf + offset, line + 10);
328                         }
329                         continue;
330                 }
331
332                 if (is_empty_line(line, linelen)) {
333                         if (!body)
334                                 continue;
335                         if (fmt == CMIT_FMT_SHORT)
336                                 break;
337                 } else {
338                         body = 1;
339                 }
340                 memset(buf + offset, ' ', 4);
341                 memcpy(buf + offset + 4, line, linelen);
342                 offset += linelen + 4;
343         }
344         /* Make sure there is an EOLN */
345         if (buf[offset - 1] != '\n')
346                 buf[offset++] = '\n';
347         buf[offset] = '\0';
348         return offset;
349 }
350
351 struct commit *pop_commit(struct commit_list **stack)
352 {
353         struct commit_list *top = *stack;
354         struct commit *item = top ? top->item : NULL;
355
356         if (top) {
357                 *stack = top->next;
358                 free(top);
359         }
360         return item;
361 }
362
363 int count_parents(struct commit * commit)
364 {
365         int count = 0;
366         struct commit_list * parents = commit->parents;
367         for (count=0;parents; parents=parents->next,count++)
368           ;
369         return count;
370 }
371
372 /*
373  * Performs an in-place topological sort on the list supplied.
374  */
375 void sort_in_topological_order(struct commit_list ** list)
376 {
377         struct commit_list * next = *list;
378         struct commit_list * work = NULL;
379         struct commit_list ** pptr = list;
380         struct sort_node * nodes;
381         struct sort_node * next_nodes;
382         int count = 0;
383
384         /* determine the size of the list */
385         while (next) {
386                 next = next->next;
387                 count++;
388         }
389         /* allocate an array to help sort the list */
390         nodes = xcalloc(count, sizeof(*nodes));
391         /* link the list to the array */
392         next_nodes = nodes;
393         next=*list;
394         while (next) {
395                 next_nodes->list_item = next;
396                 next->item->object.util = next_nodes;
397                 next_nodes++;
398                 next = next->next;
399         }
400         /* update the indegree */
401         next=*list;
402         while (next) {
403                 struct commit_list * parents = next->item->parents;
404                 while (parents) {
405                         struct commit * parent=parents->item;
406                         struct sort_node * pn = (struct sort_node *)parent->object.util;
407                         
408                         if (pn)
409                                 pn->indegree++;
410                         parents=parents->next;
411                 }
412                 next=next->next;
413         }
414         /* 
415          * find the tips
416          *
417          * tips are nodes not reachable from any other node in the list 
418          * 
419          * the tips serve as a starting set for the work queue.
420          */
421         next=*list;
422         while (next) {
423                 struct sort_node * node = (struct sort_node *)next->item->object.util;
424
425                 if (node->indegree == 0) {
426                         commit_list_insert(next->item, &work);
427                 }
428                 next=next->next;
429         }
430         /* process the list in topological order */
431         while (work) {
432                 struct commit * work_item = pop_commit(&work);
433                 struct sort_node * work_node = (struct sort_node *)work_item->object.util;
434                 struct commit_list * parents = work_item->parents;
435
436                 while (parents) {
437                         struct commit * parent=parents->item;
438                         struct sort_node * pn = (struct sort_node *)parent->object.util;
439                         
440                         if (pn) {
441                                 /* 
442                                  * parents are only enqueued for emission 
443                                  * when all their children have been emitted thereby
444                                  * guaranteeing topological order.
445                                  */
446                                 pn->indegree--;
447                                 if (!pn->indegree) 
448                                         commit_list_insert(parent, &work);
449                         }
450                         parents=parents->next;
451                 }
452                 /*
453                  * work_item is a commit all of whose children
454                  * have already been emitted. we can emit it now.
455                  */
456                 *pptr = work_node->list_item;
457                 pptr = &(*pptr)->next;
458                 *pptr = NULL;
459                 work_item->object.util = NULL;
460         }
461         free(nodes);
462 }