Diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Offer blame only where it works
The blame page answers 404 for a symlink, since git blames a regular
file alone, and it refuses a binary file and one past max-blob-size,
yet a tree listing offered blame for all of them, and so did a
symlink's own page. Each row already knows its size, and git calls a
file binary by its first 8000 bytes alone, which a stream reads
without inflating the rest. With blame on, a listing of 2,000
delta-packed files went from about 11 to 15 ms.
Diffstat (limited to 'source/ui-tree.c')
| -rw-r--r-- | source/ui-tree.c | 41 |
1 file changed, 37 insertions, 4 deletions
diff --git a/source/ui-tree.c b/source/ui-tree.c index 8e17cc4..63b8984 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -17,6 +17,8 @@ #include "ui-shared.h" #include "ui-tree.h" +#include <odb/streaming.h> + #define HEXDUMP_ROW_BYTES 32 #define HEXDUMP_ROW_HALF (HEXDUMP_ROW_BYTES / 2) #define HEXDUMP_GAP_WIDTH 4 @@ -25,6 +27,10 @@ // max-blob-size would make a page several times its own size. #define HEXDUMP_MAX_BYTES (64 * 1024) +// How far into a buffer buffer_is_binary looks, the window git judges a file +// binary by. +#define BINARY_SNIFF_BYTES 8000 + // How many single-child directories a listing row follows into one path. #define MAX_DIR_CHAIN_LEVELS 15 @@ -147,7 +153,7 @@ static void print_binary_buffer(char *buf, size_t size, const char *path, const * false means a complete error page went out in place of the blob, so nothing * may be added to it. */ -static bool print_object(const struct object_id *oid, const char *path, +static bool print_object(const struct object_id *oid, unsigned mode, const char *path, const char *filename, const char *rev) { enum object_type type; @@ -182,7 +188,7 @@ static bool print_object(const struct object_id *oid, const char *path, cgit_print_layout_start(); htmlf("<div class='blob-header'>blob: %s (", oid_to_hex(oid)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); - if (ctx.repo->enable_blame && !is_binary) { + if (ctx.repo->enable_blame && S_ISREG(mode) && !is_binary) { html(") ("); cgit_blame_link("blame", NULL, NULL, ctx.qry.head, rev, path); } @@ -259,6 +265,26 @@ static void print_dir_chain(const struct object_id *oid, const char *name, strbuf_setlen(fullpath, initial_length); } +/* + * Whether git counts the blob as binary, read through a stream so that only + * the bytes buffer_is_binary looks at are inflated, however large the blob. + */ +static bool blob_starts_binary(const struct object_id *oid) +{ + char buf[BINARY_SNIFF_BYTES]; + struct odb_stream *stream; + size_t got = 0; + ssize_t n; + + stream = odb_stream_from_object(the_repository->objects, oid, NULL); + if (!stream) + return false; + while (got < sizeof(buf) && (n = odb_stream_read(stream, buf + got, sizeof(buf) - got)) > 0) + got += n; + odb_stream_close(stream); + return buffer_is_binary(buf, got); +} + static void print_ls_row(const struct object_id *oid, const char *name, unsigned mode, struct walk_tree_context *walk) { @@ -345,7 +371,14 @@ static void print_ls_row(const struct object_id *oid, const char *name, cgit_stats_link("stats", NULL, "button", ctx.qry.head, fullpath.buf); if (!S_ISGITLINK(mode)) cgit_plain_link("plain", NULL, "button", ctx.qry.head, walk->rev, fullpath.buf); - if (!S_ISDIR(mode) && !S_ISGITLINK(mode) && ctx.repo->enable_blame) + // The blame page refuses a binary file and one past max-blob-size, so + // neither is offered it. + if ( + S_ISREG(mode) && + ctx.repo->enable_blame && + !cgit_blob_too_large(size) && + !blob_starts_binary(oid) + ) cgit_blame_link("blame", NULL, "button", ctx.qry.head, walk->rev, fullpath.buf); html("</td></tr>\n"); @@ -438,7 +471,7 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base, ls_head(); return READ_TREE_RECURSIVE; } else { - bool shown = print_object(oid, fullpath.buf, pathname, walk->rev); + bool shown = print_object(oid, mode, fullpath.buf, pathname, walk->rev); walk->state = shown ? WALK_BLOB_SHOWN : WALK_ERROR_SHOWN; strbuf_release(&fullpath); |
