+static int files, add_lines, rem_lines, lines_counted;
+
+/*
+ * The list of available column colors in the commit graph.
+ */
+static const char *column_colors_html[] = {
+ "<span class='column1'>",
+ "<span class='column2'>",
+ "<span class='column3'>",
+ "<span class='column4'>",
+ "<span class='column5'>",
+ "<span class='column6'>",
+ "</span>",
+};
+
+#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
+
+static void count_lines(char *line, int size)
+{
+ if (size <= 0)
+ return;
+
+ if (line[0] == '+')
+ add_lines++;
+
+ else if (line[0] == '-')
+ rem_lines++;
+}
+
+static void inspect_files(struct diff_filepair *pair)
+{
+ unsigned long old_size = 0;
+ unsigned long new_size = 0;
+ int binary = 0;
+
+ files++;
+ if (ctx.repo->enable_log_linecount)
+ cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
+ &new_size, &binary, 0, ctx.qry.ignorews,
+ count_lines);
+}
+
+void show_commit_decorations(struct commit *commit)
+{
+ const struct name_decoration *deco;
+ static char buf[1024];
+
+ buf[sizeof(buf) - 1] = 0;
+ deco = get_name_decoration(&commit->object);
+ if (!deco)
+ return;
+ html("<span class='decoration'>");
+ while (deco) {
+ struct object_id peeled;
+ int is_annotated = 0;
+ strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
+ switch(deco->type) {
+ case DECORATION_NONE:
+ /* If the git-core doesn't recognize it,
+ * don't display anything. */
+ break;
+ case DECORATION_REF_LOCAL:
+ cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
+ ctx.qry.vpath, 0, NULL, NULL,
+ ctx.qry.showmsg, 0);
+ break;
+ case DECORATION_REF_TAG:
+ if (!peel_ref(deco->name, &peeled))
+ is_annotated = !oidcmp(&commit->object.oid, &peeled);
+ cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
+ break;
+ case DECORATION_REF_REMOTE:
+ if (!ctx.repo->enable_remote_branches)
+ break;
+ cgit_log_link(buf, NULL, "remote-deco", NULL,
+ oid_to_hex(&commit->object.oid),
+ ctx.qry.vpath, 0, NULL, NULL,
+ ctx.qry.showmsg, 0);
+ break;
+ default:
+ cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
+ oid_to_hex(&commit->object.oid),
+ ctx.qry.vpath);
+ break;
+ }
+ deco = deco->next;
+ }
+ html("</span>");
+}
+
+static void handle_rename(struct diff_filepair *pair)
+{
+ /*
+ * After we have seen a rename, we generate links to the previous
+ * name of the file so that commit & diff views get fed the path
+ * that is correct for the commit they are showing, avoiding the
+ * need to walk the entire history leading back to every commit we
+ * show in order detect renames.
+ */
+ if (0 != strcmp(ctx.qry.vpath, pair->two->path)) {
+ free(ctx.qry.vpath);
+ ctx.qry.vpath = xstrdup(pair->two->path);
+ }
+ inspect_files(pair);
+}
+
+static int show_commit(struct commit *commit, struct rev_info *revs)
+{
+ struct commit_list *parents = commit->parents;
+ struct commit *parent;
+ int found = 0, saved_fmt;
+ struct diff_flags saved_flags = revs->diffopt.flags;
+
+ /* Always show if we're not in "follow" mode with a single file. */
+ if (!ctx.qry.follow)
+ return 1;
+
+ /*
+ * In "follow" mode, we don't show merges. This is consistent with
+ * "git log --follow -- <file>".
+ */
+ if (parents && parents->next)
+ return 0;
+
+ /*
+ * If this is the root commit, do what rev_info tells us.
+ */
+ if (!parents)
+ return revs->show_root_diff;
+
+ /* When we get here we have precisely one parent. */
+ parent = parents->item;
+ /* If we can't parse the commit, let print_commit() report an error. */
+ if (parse_commit(parent))
+ return 1;
+
+ files = 0;
+ add_lines = 0;
+ rem_lines = 0;
+
+ revs->diffopt.flags.recursive = 1;
+ diff_tree_oid(&parent->maybe_tree->object.oid,
+ &commit->maybe_tree->object.oid,
+ "", &revs->diffopt);
+ diffcore_std(&revs->diffopt);
+
+ found = !diff_queue_is_empty();
+ saved_fmt = revs->diffopt.output_format;
+ revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
+ revs->diffopt.format_callback = cgit_diff_tree_cb;
+ revs->diffopt.format_callback_data = handle_rename;
+ diff_flush(&revs->diffopt);
+ revs->diffopt.output_format = saved_fmt;
+ revs->diffopt.flags = saved_flags;
+
+ lines_counted = 1;
+ return found;
+}
+
+static void print_commit(struct commit *commit, struct rev_info *revs)