diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Restyle the sources and fix the audit's findings
Diffstat (limited to 'source/ui-diff.c')
-rw-r--r--source/ui-diff.c627
1 file changed, 313 insertions, 314 deletions
diff --git a/source/ui-diff.c b/source/ui-diff.c
index a0790f7..09e2790 100644
--- a/source/ui-diff.c
+++ b/source/ui-diff.c
@@ -1,19 +1,34 @@
-/* ui-diff.c: show diff between two blobs
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- * (see LICENSE.txt for full license text)
+/*
+ * The diff page, which shows what one revision changed against another as a
+ * summary table of the files it touched followed by the body of each change,
+ * either unified or side by side. Both halves come out of a single walk over
+ * the tree, because the summary has to be printed first while the bodies are
+ * only cheap to produce while each file is already open. A commit or a file
+ * whose diff would be too large stops at a link to a narrower view, and the
+ * raw form of the same request is handed to git and served as a plain patch.
*/
#define USE_THE_REPOSITORY_VARIABLE
#include "cgit.h"
-#include "ui-diff.h"
#include "html.h"
+#include "shared.h"
+#include "ui-diff.h"
#include "ui-shared.h"
#include "ui-ssdiff.h"
+// A file's body is collected while the walk still has that file open, because
+// the stat table above it has to be printed first and rendering the bodies
+// afterwards meant a second walk with its own rename detection and its own
+// xdiff of every file. This bounds what one request may hold that way, so it
+// is not something to configure, and once it is passed the collected bodies
+// are dropped and that second walk happens after all.
+#define BODY_BUDGET (8 * 1024 * 1024)
+
+// What a context of zero means once the diff runs, mirroring the fallback in
+// cgit_diff_files, so the control offers the value the diff will really use.
+#define DEFAULT_CONTEXT_LINES 3
+
struct fileinfo {
char status;
struct object_id old_oid[1];
@@ -30,60 +45,32 @@ struct fileinfo {
struct strbuf body;
};
-/*
- * The diffstat has to be printed before the file bodies, but both are produced
- * by the same walk over the tree. So each body is rendered into the item it
- * belongs to while that file is already open, and replayed once the stat table
- * above it has been written. Rendering separately meant a second walk with its
- * own rename detection and its own xdiff of every file.
- *
- * A body stops being collected past max-diff-lines, since a file over that is
- * replaced by a link, and collection stops altogether past the budget below,
- * after which the bodies are produced the old way. The budget is a bound on
- * one request, not something to configure.
- *
- * Only a view that caps its bodies collects them. Without max-diff-lines to
- * stop it, a single body grows with the file it came from, which for a
- * side-by-side diff is several times the blob itself, and the budget below
- * cannot help because it is only reached once a body is already complete.
- */
-#define CGIT_DIFF_BODY_BUDGET (8 * 1024 * 1024)
-
-/* The revisions being compared, read by ui-ssdiff when it builds line links. */
struct object_id old_rev_oid[1];
struct object_id new_rev_oid[1];
-/* One entry per file in the diff, filled by the walk and replayed afterwards. */
static struct fileinfo *items;
static int files, slots;
-/* Totals for the diffstat, accumulated across that same walk. */
static int total_adds, total_rems, max_changes;
static int lines_added, lines_removed;
-/* How much body text has been collected, and whether collecting is still
- * worthwhile. Once the budget is passed the bodies are produced the old way. */
static size_t body_bytes;
static int bodies_usable;
-/* Which renderer the current file's lines go to, and whether the file has
- * already passed max-diff-lines and so stopped being rendered. */
static linediff_fn render_line_fn;
static int render_suppressed;
-/* The file being rendered right now, and the path the view is held to. */
static struct diff_filepair *current_filepair;
static const char *current_prefix;
static int use_ssdiff;
-/* Caps apply only to whole-commit views. A single-file diff page must
- * always render fully, since it is where the capped views link to. */
+// Caps apply only to a whole commit view, since a single file diff is where
+// the capped views send the reader.
static int cap_diffs;
+
static int item_idx;
-/* The bodies have either been replayed or been given up on, so in both cases
- * what they hold is finished with. */
static void release_bodies(void)
{
int i;
@@ -92,18 +79,9 @@ static void release_bodies(void)
strbuf_release(&items[i].body);
}
-struct diff_filespec *cgit_get_current_old_file(void)
-{
- return current_filepair->one;
-}
-
-struct diff_filespec *cgit_get_current_new_file(void)
-{
- return current_filepair->two;
-}
-
static void print_fileinfo(struct fileinfo *info)
{
+ int scale = max_changes ? max_changes : 1;
const char *class;
switch (info->status) {
@@ -167,18 +145,22 @@ static void print_fileinfo(struct fileinfo *info)
}
htmlf("%d", info->added + info->removed);
html("</td><td class='graph'>");
- htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
+ htmlf("<table summary='file diffstat' width='%d%%'><tr>",
+ max_changes > 100 ? 100 : max_changes);
htmlf("<td class='add' style='width: %.1f%%;'/>",
- info->added * 100.0 / (max_changes ? max_changes : 1));
+ info->added * 100.0 / scale);
htmlf("<td class='rem' style='width: %.1f%%;'/>",
- info->removed * 100.0 / (max_changes ? max_changes : 1));
+ info->removed * 100.0 / scale);
htmlf("<td class='none' style='width: %.1f%%;'/>",
- (max_changes - info->removed - info->added) * 100.0 / (max_changes ? max_changes : 1));
+ (max_changes - info->removed - info->added) * 100.0 / scale);
html("</tr></table></td></tr>\n");
}
-/* Counts every line, and renders it too until the file passes max-diff-lines,
- * at which point the body is going to be replaced by a link anyway. */
+/*
+ * Counting is only half of what this does. It also renders each line, until
+ * max-diff-lines is passed and the rest of the file is dropped, which is why
+ * the two cannot be separated into a counting pass and a rendering one.
+ */
static void count_diff_lines(char *line, int len)
{
if (line && (len > 0)) {
@@ -199,32 +181,169 @@ static void count_diff_lines(char *line, int len)
static int show_filepair(struct diff_filepair *pair)
{
- /* Always show if we have no limiting prefix. */
if (!current_prefix)
return 1;
+ return starts_with(pair->one->path, current_prefix) ||
+ starts_with(pair->two->path, current_prefix);
+}
- /* Show if either path in the pair begins with the prefix. */
- if (starts_with(pair->one->path, current_prefix) ||
- starts_with(pair->two->path, current_prefix))
- return 1;
+/*
+ * The line xdiff produces ends in its newline, which is swapped for a
+ * terminator while the text is escaped and then put back, because the line
+ * points into a buffer xdiff still owns.
+ */
+static void print_line(char *line, int len)
+{
+ const char *class = "ctx";
+ char c = line[len - 1];
+
+ if (line[0] == '+')
+ class = "add";
+ else if (line[0] == '-')
+ class = "del";
+ else if (line[0] == '@')
+ class = "hunk";
- /* Otherwise we don't want to show this filepair. */
- return 0;
+ htmlf("<div class='%s'>", class);
+ line[len - 1] = '\0';
+ html_txt(line);
+ html("</div>");
+ line[len - 1] = c;
}
-static void print_line(char *line, int len);
-static void header(const struct object_id *oid1, char *path1, int mode1,
- const struct object_id *oid2, char *path2, int mode2);
+/*
+ * The buffer repo_find_unique_abbrev returns is reused, so a caller printing
+ * two abbreviated names together keeps a copy of each.
+ */
+static char *abbrev_oid(const struct object_id *oid)
+{
+ return xstrdup(repo_find_unique_abbrev(the_repository, oid,
+ DEFAULT_ABBREV));
+}
-/* Render one file's body into its own buffer, the way filepair_cb would have
- * written it straight out on a second walk. The line count is not known until
- * the diff has run, so the header is collected first and what follows it is
- * decided afterwards. */
-static void collect_filepair_body(struct diff_filepair *pair, int idx,
- int *binary, unsigned long *old_size,
- unsigned long *new_size)
+static void print_file_header(const struct object_id *old_oid, char *old_path,
+ int old_mode, const struct object_id *new_oid,
+ char *new_path, int new_mode)
{
- struct strbuf *body = &items[idx].body;
+ char *old_abbrev, *new_abbrev;
+ int subproject;
+
+ subproject = (S_ISGITLINK(old_mode) || S_ISGITLINK(new_mode));
+ html("<div class='head'>");
+ html("diff --git a/");
+ html_txt(old_path);
+ html(" b/");
+ html_txt(new_path);
+
+ if (old_mode == 0)
+ htmlf("<br/>new file mode %.6o", new_mode);
+
+ if (new_mode == 0)
+ htmlf("<br/>deleted file mode %.6o", old_mode);
+
+ if (!subproject) {
+ old_abbrev = abbrev_oid(old_oid);
+ new_abbrev = abbrev_oid(new_oid);
+ htmlf("<br/>index %s..%s", old_abbrev, new_abbrev);
+ free(old_abbrev);
+ free(new_abbrev);
+ if (old_mode != 0 && new_mode != 0) {
+ htmlf(" %.6o", old_mode);
+ if (new_mode != old_mode)
+ htmlf("..%.6o", new_mode);
+ }
+ if (is_null_oid(old_oid)) {
+ old_path = "dev/null";
+ html("<br/>--- /");
+ } else
+ html("<br/>--- a/");
+ if (old_mode != 0)
+ cgit_tree_link(old_path, NULL, NULL, ctx.qry.head,
+ oid_to_hex(old_rev_oid), old_path);
+ else
+ html_txt(old_path);
+ if (is_null_oid(new_oid)) {
+ new_path = "dev/null";
+ html("<br/>+++ /");
+ } else
+ html("<br/>+++ b/");
+ if (new_mode != 0)
+ cgit_tree_link(new_path, NULL, NULL, ctx.qry.head,
+ oid_to_hex(new_rev_oid), new_path);
+ else
+ html_txt(new_path);
+ }
+ html("</div>");
+}
+
+/*
+ * The length passed counts the terminator, because a renderer strips the last
+ * byte of every line it is given.
+ */
+static void print_subproject_lines(struct diff_filepair *pair,
+ linediff_fn line_fn)
+{
+ if (S_ISGITLINK(pair->one->mode)) {
+ char *text = cgit_fmt("-Subproject %s",
+ oid_to_hex(&pair->one->oid));
+ line_fn(text, strlen(text) + 1);
+ }
+ if (S_ISGITLINK(pair->two->mode)) {
+ char *text = cgit_fmt("+Subproject %s",
+ oid_to_hex(&pair->two->oid));
+ line_fn(text, strlen(text) + 1);
+ }
+}
+
+static void print_binary_differs(void)
+{
+ if (use_ssdiff)
+ html("<tr><td colspan='4'>Binary files differ</td></tr>");
+ else
+ html("Binary files differ");
+}
+
+static void print_truncated(const char *path)
+{
+ if (use_ssdiff)
+ html("<tr><td colspan='4' class='truncated'>");
+ else
+ html("<div class='truncated'>");
+ html("This diff is too large to be rendered inline. ");
+ cgit_diff_link("View it on its own page", NULL, NULL, ctx.qry.head,
+ ctx.qry.oid, ctx.qry.oid2, path);
+ html(".");
+ if (use_ssdiff) {
+ html("</td></tr>");
+ cgit_ssdiff_footer();
+ } else
+ html("</div>");
+}
+
+static struct fileinfo *reserve_item(void)
+{
+ files++;
+ if (files >= slots) {
+ if (slots == 0)
+ slots = 4;
+ else
+ slots = slots * 2;
+ items = xrealloc(items, slots * sizeof(struct fileinfo));
+ }
+ memset(&items[files - 1], 0, sizeof(items[files - 1]));
+ strbuf_init(&items[files - 1].body, 0);
+ return &items[files - 1];
+}
+
+/*
+ * Renders one file's body into its own buffer, the way filepair_cb would have
+ * written it straight out on a second walk.
+ */
+static void collect_body(struct diff_filepair *pair, struct fileinfo *item,
+ int *binary, unsigned long *old_size,
+ unsigned long *new_size)
+{
+ struct strbuf *body = &item->body;
linediff_fn line_fn = use_ssdiff ? cgit_ssdiff_line_cb : print_line;
size_t header_len;
@@ -233,77 +352,55 @@ static void collect_filepair_body(struct diff_filepair *pair, int idx,
html_capture_begin(body);
if (use_ssdiff)
cgit_ssdiff_header_begin();
- header(&pair->one->oid, pair->one->path, pair->one->mode,
- &pair->two->oid, pair->two->path, pair->two->mode);
+ print_file_header(&pair->one->oid, pair->one->path, pair->one->mode,
+ &pair->two->oid, pair->two->path, pair->two->mode);
if (use_ssdiff)
cgit_ssdiff_header_end();
- // Everything from here on is dropped if the file turns out to be over
- // the line budget, so remember where the header ended.
header_len = body->len;
+ // Cleared for the whole file rather than in the branch below, because
+ // the submodule branch never runs count_diff_lines with a render
+ // function. A submodule following a file that tripped the cap would
+ // otherwise inherit the flag and report itself as too large to render.
+ render_suppressed = 0;
+
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
- /* The body shows the two Subproject lines, but the stat has
- * always counted what a diff of the pair produces, so run it
- * for the count alone. */
+ // The stat has always counted what a diff of the pair produces
+ // rather than the two lines the body shows, so run that diff
+ // for the count alone.
render_line_fn = NULL;
cgit_diff_files(&pair->one->oid, &pair->two->oid, old_size,
new_size, binary, 0, ctx.qry.ignorews,
count_diff_lines);
- if (S_ISGITLINK(pair->one->mode)) {
- char *l = cgit_fmt("-Subproject %s", oid_to_hex(&pair->one->oid));
- line_fn(l, strlen(l) + 1);
- }
- if (S_ISGITLINK(pair->two->mode)) {
- char *l = cgit_fmt("+Subproject %s", oid_to_hex(&pair->two->oid));
- line_fn(l, strlen(l) + 1);
- }
+ print_subproject_lines(pair, line_fn);
} else {
render_line_fn = line_fn;
- render_suppressed = 0;
if (cgit_diff_files(&pair->one->oid, &pair->two->oid, old_size,
new_size, binary, ctx.qry.context,
ctx.qry.ignorews, count_diff_lines))
cgit_print_error("Error running diff");
render_line_fn = NULL;
- if (*binary) {
- if (use_ssdiff)
- html("<tr><td colspan='4'>Binary files differ</td></tr>");
- else
- html("Binary files differ");
- }
+ if (*binary)
+ print_binary_differs();
}
if (use_ssdiff)
cgit_ssdiff_footer();
if (render_suppressed) {
- /* Over the line budget, so the body is a link instead. Setting
- * the length back would leave the buffer holding everything it
- * grew to while rendering, which the budget below cannot see
- * because it only counts what is kept, so rebuild it at the
- * size actually kept. */
+ // Setting the length back would leave the buffer holding
+ // everything it grew to while rendering, which the budget below
+ // cannot see because it only counts what is kept, so rebuild it
+ // at the size actually kept.
char *header_text = xmemdupz(body->buf, header_len);
strbuf_release(body);
strbuf_attach(body, header_text, header_len, header_len + 1);
- if (use_ssdiff)
- html("<tr><td colspan='4' class='truncated'>");
- else
- html("<div class='truncated'>");
- html("This diff is too large to be rendered inline. ");
- cgit_diff_link("View it on its own page", NULL, NULL,
- ctx.qry.head, ctx.qry.oid, ctx.qry.oid2,
- pair->two->path);
- html(".");
- if (use_ssdiff) {
- html("</td></tr>");
- cgit_ssdiff_footer();
- } else
- html("</div>");
+ print_truncated(pair->two->path);
}
html_capture_end();
body_bytes += body->len;
- if (body_bytes > CGIT_DIFF_BODY_BUDGET) {
+ if (body_bytes > BODY_BUDGET) {
bodies_usable = 0;
release_bodies();
}
@@ -311,6 +408,7 @@ static void collect_filepair_body(struct diff_filepair *pair, int idx,
static void inspect_filepair(struct diff_filepair *pair)
{
+ struct fileinfo *item;
int binary = 0;
unsigned long old_size = 0;
unsigned long new_size = 0;
@@ -318,48 +416,37 @@ static void inspect_filepair(struct diff_filepair *pair)
if (!show_filepair(pair))
return;
- files++;
+ item = reserve_item();
lines_added = 0;
lines_removed = 0;
- if (files >= slots) {
- if (slots == 0)
- slots = 4;
- else
- slots = slots * 2;
- items = xrealloc(items, slots * sizeof(struct fileinfo));
- }
- memset(&items[files-1], 0, sizeof(items[files-1]));
- strbuf_init(&items[files-1].body, 0);
if (bodies_usable)
- collect_filepair_body(pair, files - 1, &binary,
- &old_size, &new_size);
+ collect_body(pair, item, &binary, &old_size, &new_size);
else
cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
&new_size, &binary, 0, ctx.qry.ignorews,
count_diff_lines);
- items[files-1].status = pair->status;
- oidcpy(items[files-1].old_oid, &pair->one->oid);
- oidcpy(items[files-1].new_oid, &pair->two->oid);
- items[files-1].old_mode = pair->one->mode;
- items[files-1].new_mode = pair->two->mode;
- items[files-1].old_path = xstrdup(pair->one->path);
- items[files-1].new_path = xstrdup(pair->two->path);
- items[files-1].added = lines_added;
- items[files-1].removed = lines_removed;
- items[files-1].old_size = old_size;
- items[files-1].new_size = new_size;
- items[files-1].binary = binary;
+ item->status = pair->status;
+ oidcpy(item->old_oid, &pair->one->oid);
+ oidcpy(item->new_oid, &pair->two->oid);
+ item->old_mode = pair->one->mode;
+ item->new_mode = pair->two->mode;
+ item->old_path = xstrdup(pair->one->path);
+ item->new_path = xstrdup(pair->two->path);
+ item->added = lines_added;
+ item->removed = lines_removed;
+ item->old_size = old_size;
+ item->new_size = new_size;
+ item->binary = binary;
if (lines_added + lines_removed > max_changes)
max_changes = lines_added + lines_removed;
total_adds += lines_added;
total_rems += lines_removed;
}
-static void cgit_print_diffstat(const struct object_id *old_oid,
- const struct object_id *new_oid,
- const char *prefix)
+static void print_diffstat(const struct object_id *old_oid,
+ const struct object_id *new_oid, const char *prefix)
{
int i;
@@ -376,7 +463,7 @@ static void cgit_print_diffstat(const struct object_id *old_oid,
max_changes = 0;
cgit_diff_tree(old_oid, new_oid, inspect_filepair, prefix,
ctx.qry.ignorews);
- for (i = 0; i<files; i++)
+ for (i = 0; i < files; i++)
print_fileinfo(&items[i]);
html("</table>");
html("<div class='diffstat-summary'>");
@@ -385,80 +472,12 @@ static void cgit_print_diffstat(const struct object_id *old_oid,
html("</div>");
}
-/*
- * print a single line returned from xdiff
- */
-static void print_line(char *line, int len)
+static int over_line_cap(int idx)
{
- const char *class = "ctx";
- char c = line[len-1];
-
- if (line[0] == '+')
- class = "add";
- else if (line[0] == '-')
- class = "del";
- else if (line[0] == '@')
- class = "hunk";
-
- htmlf("<div class='%s'>", class);
- line[len-1] = '\0';
- html_txt(line);
- html("</div>");
- line[len-1] = c;
-}
-
-static void header(const struct object_id *oid1, char *path1, int mode1,
- const struct object_id *oid2, char *path2, int mode2)
-{
- char *abbrev1, *abbrev2;
- int subproject;
-
- subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
- html("<div class='head'>");
- html("diff --git a/");
- html_txt(path1);
- html(" b/");
- html_txt(path2);
-
- if (mode1 == 0)
- htmlf("<br/>new file mode %.6o", mode2);
-
- if (mode2 == 0)
- htmlf("<br/>deleted file mode %.6o", mode1);
-
- if (!subproject) {
- abbrev1 = xstrdup(repo_find_unique_abbrev(the_repository, oid1, DEFAULT_ABBREV));
- abbrev2 = xstrdup(repo_find_unique_abbrev(the_repository, oid2, DEFAULT_ABBREV));
- htmlf("<br/>index %s..%s", abbrev1, abbrev2);
- free(abbrev1);
- free(abbrev2);
- if (mode1 != 0 && mode2 != 0) {
- htmlf(" %.6o", mode1);
- if (mode2 != mode1)
- htmlf("..%.6o", mode2);
- }
- if (is_null_oid(oid1)) {
- path1 = "dev/null";
- html("<br/>--- /");
- } else
- html("<br/>--- a/");
- if (mode1 != 0)
- cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
- oid_to_hex(old_rev_oid), path1);
- else
- html_txt(path1);
- if (is_null_oid(oid2)) {
- path2 = "dev/null";
- html("<br/>+++ /");
- } else
- html("<br/>+++ b/");
- if (mode2 != 0)
- cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
- oid_to_hex(new_rev_oid), path2);
- else
- html_txt(path2);
- }
- html("</div>");
+ return cap_diffs && ctx.cfg.max_diff_lines > 0 &&
+ idx < files && !items[idx].binary &&
+ items[idx].added + items[idx].removed >
+ (unsigned int)ctx.cfg.max_diff_lines;
}
static void filepair_cb(struct diff_filepair *pair)
@@ -467,7 +486,7 @@ static void filepair_cb(struct diff_filepair *pair)
unsigned long new_size = 0;
int binary = 0;
int idx;
- linediff_fn print_line_fn = print_line;
+ linediff_fn line_fn = print_line;
if (!show_filepair(pair))
return;
@@ -476,65 +495,66 @@ static void filepair_cb(struct diff_filepair *pair)
current_filepair = pair;
if (use_ssdiff) {
cgit_ssdiff_header_begin();
- print_line_fn = cgit_ssdiff_line_cb;
+ line_fn = cgit_ssdiff_line_cb;
}
- header(&pair->one->oid, pair->one->path, pair->one->mode,
- &pair->two->oid, pair->two->path, pair->two->mode);
+ print_file_header(&pair->one->oid, pair->one->path, pair->one->mode,
+ &pair->two->oid, pair->two->path, pair->two->mode);
if (use_ssdiff)
cgit_ssdiff_header_end();
- /* The diffstat pass counted this file's lines. Over the budget,
- * print the header and hand off to the single-file diff page
- * instead of rendering the whole thing inline. */
- if (cap_diffs && ctx.cfg.max_diff_lines > 0 && idx < files &&
- !items[idx].binary && items[idx].added + items[idx].removed >
- (unsigned int)ctx.cfg.max_diff_lines) {
- if (use_ssdiff)
- html("<tr><td colspan='4' class='truncated'>");
- else
- html("<div class='truncated'>");
- html("This diff is too large to be rendered inline. ");
- cgit_diff_link("View it on its own page", NULL, NULL,
- ctx.qry.head, ctx.qry.oid, ctx.qry.oid2,
- items[idx].new_path);
- html(".");
- if (use_ssdiff) {
- html("</td></tr>");
- cgit_ssdiff_footer();
- } else
- html("</div>");
+ if (over_line_cap(idx)) {
+ print_truncated(items[idx].new_path);
return;
}
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
- if (S_ISGITLINK(pair->one->mode)) {
- char *l = cgit_fmt("-Subproject %s", oid_to_hex(&pair->one->oid));
- print_line_fn(l, strlen(l) + 1);
- }
- if (S_ISGITLINK(pair->two->mode)) {
- char *l = cgit_fmt("+Subproject %s", oid_to_hex(&pair->two->oid));
- print_line_fn(l, strlen(l) + 1);
- }
+ print_subproject_lines(pair, line_fn);
if (use_ssdiff)
cgit_ssdiff_footer();
return;
}
if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
&new_size, &binary, ctx.qry.context,
- ctx.qry.ignorews, print_line_fn))
+ ctx.qry.ignorews, line_fn))
cgit_print_error("Error running diff");
- if (binary) {
- if (use_ssdiff)
- html("<tr><td colspan='4'>Binary files differ</td></tr>");
- else
- html("Binary files differ");
- }
+ if (binary)
+ print_binary_differs();
if (use_ssdiff)
cgit_ssdiff_footer();
}
+static void print_raw_patch(const struct object_id *old_tree_oid,
+ const struct object_id *new_tree_oid)
+{
+ struct diff_options diffopt;
+
+ repo_diff_setup(the_repository, &diffopt);
+ diffopt.output_format = DIFF_FORMAT_PATCH;
+ diffopt.flags.recursive = 1;
+ diff_setup_done(&diffopt);
+
+ ctx.page.mimetype = "text/plain";
+ cgit_print_http_headers();
+ if (old_tree_oid)
+ diff_tree_oid(old_tree_oid, new_tree_oid, "", &diffopt);
+ else
+ diff_root_tree_oid(new_tree_oid, "", &diffopt);
+ diffcore_std(&diffopt);
+ diff_flush(&diffopt);
+}
+
+struct diff_filespec *cgit_get_current_old_file(void)
+{
+ return current_filepair->one;
+}
+
+struct diff_filespec *cgit_get_current_new_file(void)
+{
+ return current_filepair->two;
+}
+
void cgit_print_diff_ctrls(void)
{
- int i, curr;
+ int i, selected;
html("<div class='cgit-panel'>");
html("<b>diff options</b>");
@@ -546,13 +566,13 @@ void cgit_print_diff_ctrls(void)
html("<td class='label'>context:</td>");
html("<td class='ctrl'>");
html("<select name='context' data-autosubmit='1'>");
- curr = ctx.qry.context;
- if (!curr)
- curr = 3;
+ selected = ctx.qry.context;
+ if (!selected)
+ selected = DEFAULT_CONTEXT_LINES;
for (i = 1; i <= 10; i++)
- html_intoption(i, cgit_fmt("%d", i), curr);
- for (i = 15; i <= 40; i += 5)
- html_intoption(i, cgit_fmt("%d", i), curr);
+ html_intoption(i, cgit_fmt("%d", i), selected);
+ for (i = 15; i <= MAX_DIFF_CONTEXT_LINES; i += 5)
+ html_intoption(i, cgit_fmt("%d", i), selected);
html("</select>");
html("</td>");
html("</tr><tr>");
@@ -567,10 +587,10 @@ void cgit_print_diff_ctrls(void)
html("<td class='label'>mode:</td>");
html("<td class='ctrl'>");
html("<select name='dt' data-autosubmit='1'>");
- curr = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
- html_intoption(0, "unified", curr);
- html_intoption(1, "ssdiff", curr);
- html_intoption(2, "stat only", curr);
+ selected = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
+ html_intoption(0, "unified", selected);
+ html_intoption(1, "ssdiff", selected);
+ html_intoption(2, "stat only", selected);
html("</select></td></tr>");
html("<tr><td/><td class='ctrl'>");
html("<noscript><input type='submit' value='reload'/></noscript>");
@@ -582,19 +602,17 @@ void cgit_print_diff_ctrls(void)
void cgit_print_diff(const char *new_rev, const char *old_rev,
const char *prefix, int show_ctrls, int raw)
{
- struct commit *commit, *commit2;
+ struct commit *new_commit, *old_commit;
const struct object_id *old_tree_oid, *new_tree_oid;
diff_type difftype;
- // A path-limited diff is naturally bounded, so the size caps only
- // apply to a whole-commit diff. Decide that from the caller's prefix
- // before the follow logic rewrites it to "", otherwise follow=1
- // silently disables the caps.
+ // Decided from the caller's prefix before the follow logic below
+ // rewrites it to "", otherwise follow=1 silently disables the caps.
cap_diffs = !prefix;
- // If "follow" is set then the diff machinery needs to examine the
- // entire commit to detect renames so we must limit the paths in our
- // own callbacks and not pass the prefix to the diff machinery.
+ // Detecting renames needs the diff machinery to examine the whole
+ // commit, so with follow set the prefix is applied in show_filepair
+ // instead of being passed down.
if (ctx.qry.follow && ctx.repo->enable_follow_links) {
current_prefix = prefix;
prefix = "";
@@ -609,13 +627,13 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
"Bad object name: %s", new_rev);
return;
}
- commit = lookup_commit_reference(the_repository, new_rev_oid);
- if (!commit || repo_parse_commit(the_repository, commit)) {
+ new_commit = lookup_commit_reference(the_repository, new_rev_oid);
+ if (!new_commit || repo_parse_commit(the_repository, new_commit)) {
cgit_print_error_page(404, "Not found",
"Bad commit: %s", oid_to_hex(new_rev_oid));
return;
}
- new_tree_oid = get_commit_tree_oid(commit);
+ new_tree_oid = get_commit_tree_oid(new_commit);
if (old_rev) {
if (repo_get_oid(the_repository, old_rev, old_rev_oid)) {
@@ -623,68 +641,52 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
"Bad object name: %s", old_rev);
return;
}
- } else if (commit->parents && commit->parents->item) {
- oidcpy(old_rev_oid, &commit->parents->item->object.oid);
+ } else if (new_commit->parents && new_commit->parents->item) {
+ oidcpy(old_rev_oid, &new_commit->parents->item->object.oid);
} else {
oidclr(old_rev_oid, the_repository->hash_algo);
}
if (!is_null_oid(old_rev_oid)) {
- commit2 = lookup_commit_reference(the_repository, old_rev_oid);
- if (!commit2 || repo_parse_commit(the_repository, commit2)) {
+ old_commit = lookup_commit_reference(the_repository,
+ old_rev_oid);
+ if (!old_commit ||
+ repo_parse_commit(the_repository, old_commit)) {
cgit_print_error_page(404, "Not found",
"Bad commit: %s", oid_to_hex(old_rev_oid));
return;
}
- old_tree_oid = get_commit_tree_oid(commit2);
+ old_tree_oid = get_commit_tree_oid(old_commit);
} else {
old_tree_oid = NULL;
}
if (raw) {
- struct diff_options diffopt;
-
- repo_diff_setup(the_repository, &diffopt);
- diffopt.output_format = DIFF_FORMAT_PATCH;
- diffopt.flags.recursive = 1;
- diff_setup_done(&diffopt);
-
- ctx.page.mimetype = "text/plain";
- cgit_print_http_headers();
- if (old_tree_oid) {
- diff_tree_oid(old_tree_oid, new_tree_oid, "",
- &diffopt);
- } else {
- diff_root_tree_oid(new_tree_oid, "", &diffopt);
- }
- diffcore_std(&diffopt);
- diff_flush(&diffopt);
-
+ print_raw_patch(old_tree_oid, new_tree_oid);
return;
}
difftype = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
use_ssdiff = difftype == DIFF_SSDIFF;
- /* A stat-only view never shows a body, and only a capped view bounds
- * the size of the ones it does show. */
- bodies_usable = difftype != DIFF_STATONLY && cap_diffs;
+ // Without max-diff-lines to stop it a single body grows with the file
+ // it came from, which for a side by side diff is several times the blob
+ // itself, and the budget cannot help there because it is only reached
+ // once a body is complete.
+ bodies_usable = difftype != DIFF_STATONLY && cap_diffs &&
+ ctx.cfg.max_diff_lines > 0;
if (show_ctrls) {
cgit_print_layout_start();
cgit_print_diff_ctrls();
}
- /*
- * Clicking on a link to a file in the diff stat should show a diff
- * of the file, showing the diff stat limited to a single file is
- * pretty useless. All links from this point on will be to
- * individual files, so we simply reset the difftype in the query
- * here to avoid propagating DIFF_STATONLY to the individual files.
- */
+ // Every link from here on leads to a single file, and a stat limited
+ // to one file is useless, so the difftype those links carry is reset
+ // rather than propagating DIFF_STATONLY to them.
if (difftype == DIFF_STATONLY)
ctx.qry.difftype = ctx.cfg.difftype;
- cgit_print_diffstat(old_rev_oid, new_rev_oid, prefix);
+ print_diffstat(old_rev_oid, new_rev_oid, prefix);
if (difftype == DIFF_STATONLY) {
if (show_ctrls)
@@ -692,8 +694,6 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
return;
}
- /* A commit touching more files than max-diff-files stops at the
- * stat above, where every file links to its own diff page. */
if (cap_diffs && ctx.cfg.max_diff_files > 0 &&
files > ctx.cfg.max_diff_files) {
html("<div class='truncated'>");
@@ -715,12 +715,11 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
}
if (bodies_usable) {
int i;
+
for (i = 0; i < files; i++)
html_raw(items[i].body.buf, items[i].body.len);
release_bodies();
} else {
- /* The bodies outgrew the budget, so they are produced the way
- * they were before, by walking the tree again. */
item_idx = 0;
cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix,
ctx.qry.ignorews);