all repos — cgit @ 9927e63f387e6c9328eb3c347ecb0e339f8ac023

a hyperfast web frontend for git written in c

ui-commit.c (view raw)

  1/* ui-commit.c: generate commit view
  2 *
  3 * Copyright (C) 2006 Lars Hjemli
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10
 11static int files, slots;
 12static int total_adds, total_rems, max_changes;
 13static int lines_added, lines_removed;
 14
 15static struct fileinfo {
 16	char status;
 17	unsigned char old_sha1[20];
 18	unsigned char new_sha1[20];
 19	unsigned short old_mode;
 20	unsigned short new_mode;
 21	char *old_path;
 22	char *new_path;
 23	unsigned int added;
 24	unsigned int removed;
 25} *items;
 26
 27
 28void print_fileinfo(struct fileinfo *info)
 29{
 30	char *query, *query2;
 31	char *class;
 32	double width;
 33
 34	switch (info->status) {
 35	case DIFF_STATUS_ADDED:
 36		class = "add";
 37		break;
 38	case DIFF_STATUS_COPIED:
 39		class = "cpy";
 40		break;
 41	case DIFF_STATUS_DELETED:
 42		class = "del";
 43		break;
 44	case DIFF_STATUS_MODIFIED:
 45		class = "upd";
 46		break;
 47	case DIFF_STATUS_RENAMED:
 48		class = "mov";
 49		break;
 50	case DIFF_STATUS_TYPE_CHANGED:
 51		class = "typ";
 52		break;
 53	case DIFF_STATUS_UNKNOWN:
 54		class = "unk";
 55		break;
 56	case DIFF_STATUS_UNMERGED:
 57		class = "stg";
 58		break;
 59	default:
 60		die("bug: unhandled diff status %c", info->status);
 61	}
 62
 63	html("<tr>");
 64	htmlf("<td class='mode'>");
 65	if (is_null_sha1(info->new_sha1)) {
 66		html_filemode(info->old_mode);
 67	} else {
 68		html_filemode(info->new_mode);
 69	}
 70
 71	if (info->old_mode != info->new_mode &&
 72	    !is_null_sha1(info->old_sha1) &&
 73	    !is_null_sha1(info->new_sha1)) {
 74		html("<span class='modechange'>[");
 75		html_filemode(info->old_mode);
 76		html("]</span>");
 77	}
 78	htmlf("</td><td class='%s'>", class);
 79	query = fmt("id=%s&id2=%s&path=%s", sha1_to_hex(info->old_sha1),
 80		    sha1_to_hex(info->new_sha1), info->new_path);
 81	html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
 82		       NULL, NULL);
 83	if (info->status == DIFF_STATUS_COPIED ||
 84	    info->status == DIFF_STATUS_RENAMED) {
 85		html_txt(info->new_path);
 86		htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
 87		      "copied" : "renamed");
 88		query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
 89		html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
 90			       NULL, NULL);
 91		html_txt(info->old_path);
 92		html("</a>)");
 93	} else {
 94		html_txt(info->new_path);
 95		html("</a>");
 96	}
 97	html("</td><td class='right'>");
 98	htmlf("%d", info->added + info->removed);
 99
100	html("</td><td class='graph'>");
101	width = (info->added + info->removed) * 100.0 / max_changes;
102	if (width < 0.1)
103		width = 0.1;
104	html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
105		       NULL, NULL);
106	htmlf("<img src='/cgit/add.png' style='width: %.1f%%;'/>",
107	      info->added * width / (info->added + info->removed));
108	htmlf("<img src='/cgit/del.png' style='width: %.1f%%;'/>",
109	      info->removed * width / (info->added + info->removed));
110	html("</a></td></tr>\n");
111}
112
113void cgit_count_diff_lines(char *line, int len)
114{
115	if (line && (len > 0)) {
116		if (line[0] == '+')
117			lines_added++;
118		else if (line[0] == '-')
119			lines_removed++;
120	}
121}
122
123void inspect_filepair(struct diff_filepair *pair)
124{
125	files++;
126	lines_added = 0;
127	lines_removed = 0;
128	cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines);
129	if (files >= slots) {
130		if (slots == 0)
131			slots = 4;
132		else
133			slots = slots * 2;
134		items = xrealloc(items, slots * sizeof(struct fileinfo));
135	}
136	items[files-1].status = pair->status;
137	hashcpy(items[files-1].old_sha1, pair->one->sha1);
138	hashcpy(items[files-1].new_sha1, pair->two->sha1);
139	items[files-1].old_mode = pair->one->mode;
140	items[files-1].new_mode = pair->two->mode;
141	items[files-1].old_path = xstrdup(pair->one->path);
142	items[files-1].new_path = xstrdup(pair->two->path);
143	items[files-1].added = lines_added;
144	items[files-1].removed = lines_removed;
145	if (lines_added + lines_removed > max_changes)
146		max_changes = lines_added + lines_removed;
147	total_adds += lines_added;
148	total_rems += lines_removed;
149}
150
151
152void cgit_print_commit(const char *hex)
153{
154	struct commit *commit, *parent;
155	struct commitinfo *info;
156	struct commit_list *p;
157	unsigned char sha1[20];
158	char *query;
159	char *filename;
160	int i;
161
162	if (get_sha1(hex, sha1)) {
163		cgit_print_error(fmt("Bad object id: %s", hex));
164		return;
165	}
166	commit = lookup_commit_reference(sha1);
167	if (!commit) {
168		cgit_print_error(fmt("Bad commit reference: %s", hex));
169		return;
170	}
171	info = cgit_parse_commit(commit);
172
173	html("<table class='commit-info'>\n");
174	html("<tr><th>author</th><td>");
175	html_txt(info->author);
176	html(" ");
177	html_txt(info->author_email);
178	html("</td><td class='right'>");
179	cgit_print_date(info->author_date);
180	html("</td></tr>\n");
181	html("<tr><th>committer</th><td>");
182	html_txt(info->committer);
183	html(" ");
184	html_txt(info->committer_email);
185	html("</td><td class='right'>");
186	cgit_print_date(info->committer_date);
187	html("</td></tr>\n");
188	html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
189	query = fmt("h=%s&id=%s", sha1_to_hex(commit->object.sha1),
190		    sha1_to_hex(commit->tree->object.sha1));
191	html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
192	htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
193      	for (p = commit->parents; p ; p = p->next) {
194		parent = lookup_commit_reference(p->item->object.sha1);
195		if (!parent) {
196			html("<tr><td colspan='3'>");
197			cgit_print_error("Error reading parent commit");
198			html("</td></tr>");
199			continue;
200		}
201		html("<tr><th>parent</th>"
202		     "<td colspan='2' class='sha1'>"
203		     "<a href='");
204		query = fmt("id=%s", sha1_to_hex(p->item->object.sha1));
205		html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
206		htmlf("'>%s</a> (<a href='",
207		      sha1_to_hex(p->item->object.sha1));
208		query = fmt("id=%s&id2=%s", sha1_to_hex(parent->tree->object.sha1),
209			    sha1_to_hex(commit->tree->object.sha1));
210		html_attr(cgit_pageurl(cgit_query_repo, "diff", query));
211		html("'>diff</a>)</td></tr>");
212	}
213	if (cgit_repo->snapshots) {
214		htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
215		filename = fmt("%s-%s.zip", cgit_query_repo, hex);
216		html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
217				       fmt("id=%s&name=%s", hex, filename)));
218		htmlf("'>%s</a></td></tr>", filename);
219	}
220	html("</table>\n");
221	html("<div class='commit-subject'>");
222	html_txt(info->subject);
223	html("</div>");
224	html("<div class='commit-msg'>");
225	html_txt(info->msg);
226	html("</div>");
227	if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
228		html("<table class='diffstat'>");
229		max_changes = 0;
230		cgit_diff_commit(commit, inspect_filepair);
231		for(i = 0; i<files; i++)
232			print_fileinfo(&items[i]);
233		html("</table>");
234		html("<div class='diffstat-summary'>");
235		htmlf("%d files changed, %d insertions, %d deletions\n",
236		      files, total_adds, total_rems);
237		html("</div>");
238	}
239	cgit_free_commitinfo(info);
240}