mobley

C Git HTTP server
Log | Files | Refs | Submodules | README | git clone https://git.ne02ptzero.me/git/mobley

commit a75ab7b28d7889dae84b63f674e41f98233e832d
parent 350dd9df43bddd7a84a0defa737bfdec0be689b3
Author: Louis Solofrizzo <lsolofrizzo@online.net>
Date:   Sat,  5 Jan 2019 20:46:35 +0100

Add commit preview page, with summary and diff:

This commit introduces a preview page for a commit. This page includes:
- A summary header, with title, author, GPG, etc
- A colorized diff below it

The colorized diff is done with source-highlight, with a very ugly hack
in order to honor proper diff lines and syntaxic coloration. This is, I
hope, temporary as I will need to recode a simple highlight library that
will suits my needs for mobley.

 _____________________
< You have junk mail. >
 ---------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Signed-off-by: Louis Solofrizzo <lsolofrizzo@online.net>

Diffstat:
MCMakeLists.txt | 1+
Mconfig.yaml | 2+-
Adiff.c | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adiff.h | 23+++++++++++++++++++++++
Mfile.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mfile.h | 8++++++++
Mhtml.c | 20+++++++++++++-------
Mhtml.h | 9++++++++-
Mrepository_log.c | 179++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mstyle/main.css | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwatm/shl-wrapper-lib.cpp | 2+-
11 files changed, 519 insertions(+), 49 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -57,6 +57,7 @@ add_executable(${MOBLEY_NAME} main.c file_format.c file.c repository_log.c + diff.c ./contrib/sundown/src/autolink.c ./contrib/sundown/src/buffer.c ./contrib/sundown/src/markdown.c diff --git a/config.yaml b/config.yaml @@ -1,4 +1,4 @@ -address : 192.168.1.215 +address : 0.0.0.0 port : 4242 static_dir : /usr/local/share/mobley logo : logo.png diff --git a/diff.c b/diff.c @@ -0,0 +1,170 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include <diff.h> +#include <html.h> +#include <file.h> +#include <file_format.h> + +static bool diff_print_diff_regions(mobley_t *ctx, server_req_t *r, char *fn, char *data) +{ + char *status_line = NULL; + size_t status_size = 0; + int line_start; + + for (size_t i = 0, j, k; data[i] != '\0'; i++) + { + if (data[i] == '@' && data[i + 1] == '@') + { + for (j = i + 2; data[j] != '\0' && data[j] != '\n'; j++) + ; + + data[j] = 0; + status_line = data + i; + html("span", status_line, .class = "status-line"); + status_size = strlen(status_line) + 1; + + for (k = i + 3; data[k] != '\0' && data[k] != ','; k++) + ; + + data[k] = 0; + str_to_int32(data + i + 4, &line_start, BASE_TEN); + data[k] = ','; + data[j] = '\n'; + + for (j = j + 2; data[j] != '\0'; j++) + { + if (data[j - 1] == '@' && data[j] == '@') + { + break ; + } + } + + if (file_is_code(fn)) + { + file_show_hl_code(ctx, r, fn, data + i + status_size, j - i - status_size - 1, + .start_line = line_start, + .is_diff = true + ); + } + else + { + html_open("pre", .class = "file-raw") { + string_raw_format_html(ctx, r, data + i + status_size, j - i - status_size - 1, + .with_lines = true, + .is_diff = true, + .start_line = line_start + ); + } html_close("pre"); + } + + i = j - 2; + } + } + return true; +} + +# define NEW_MODE_BEGIN "new file mode" +static bool hr = false; +static void diff_print_file(mobley_t *ctx, server_req_t *r, char *begin, char *end) +{ + char *line; + char *data = strndup(begin, end - begin); + char *org = data; + int line_to_skip = 2; + int diff_line = 0; + char *files[2] = { 0 }; + + while ((line = strsep(&data, "\n")) != NULL) + { + if (strncmp(NEW_MODE_BEGIN, line, sizeof(NEW_MODE_BEGIN) - 1) == 0) + continue ; + + if (line_to_skip > 0) + { + line_to_skip--; + continue ; + } + + if (strcmp(line, "--- /dev/null") == 0) + files[diff_line] = line + 4; + else + files[diff_line] = line + 6; + diff_line++; + + if (diff_line == 2) + break; + } + + if (!hr) + hr = true; + else + html_nd("hr"); + + if (data) + { + html_raw("<b>%s</b><hr>", files[1]); + diff_print_diff_regions(ctx, r, files[1], data); + } + free(org); +} + +# define DIFF_BEGIN "diff --git" +static bool diff_print_patch(mobley_t *ctx, server_req_t *r, git_buf *buf) +{ + char *line = NULL; + char *diff_line = NULL; + + html_open("div", .class = "file-content") { + for (size_t i = 0, j = 0; i < buf->size; i++) + { + for (j = i; j < buf->size && buf->ptr[j]!= '\n'; j++) + ; + + if (j < buf->size) + buf->ptr[j] = '\0'; + + line = &buf->ptr[i]; + if (strncmp(line, DIFF_BEGIN, sizeof(DIFF_BEGIN) - 1) == 0) + { + if (diff_line != NULL) + { + diff_print_file(ctx, r, diff_line, line); + } + diff_line = line; + } + + buf->ptr[j] = '\n'; + i = j; + } + diff_print_file(ctx, r, diff_line, line); + } html_close("div"); + + hr = false; + return true; +} + + +bool diff_show(mobley_t *ctx, server_req_t *r, git_diff *diff) +{ + git_buf buf = { 0 }; + git_diff_format_t diff_format = GIT_DIFF_FORMAT_PATCH; + bool ret; + + git_diff_to_buf(&buf, diff, diff_format); + ret = diff_print_patch(ctx, r, &buf); + free(buf.ptr); + return ret; +} diff --git a/diff.h b/diff.h @@ -0,0 +1,23 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#ifndef DIFF_H +# define DIFF_H + +# include <mobley.h> + +bool diff_show(mobley_t *ctx, server_req_t *r, git_diff *diff); + +#endif /* DIFF_H */ diff --git a/file.c b/file.c @@ -37,7 +37,7 @@ static bool file_show_markdown(mobley_t *ctx, server_req_t *r, git_blob *blob) static bool file_show_raw(mobley_t *ctx, server_req_t *r, git_blob *blob) { html_open("pre", .class = "file-raw") { - string_raw_format_html(ctx, r, git_blob_rawcontent(blob), git_blob_rawsize(blob), true); + string_raw_format_html(ctx, r, git_blob_rawcontent(blob), git_blob_rawsize(blob), .with_lines = true); } html_close("pre"); return true; } @@ -54,8 +54,56 @@ static bool file_show_image(mobley_t *ctx, server_req_t *r, const char *path) return true; } +# define SYMBOL_PLUS "</a> <span class=\"symbol\">+</span>" +# define SYMBOL_PLUS_COMMENT "</a> <span class=\"comment\">+" +# define SYMBOL_LESS "</a> <span class=\"symbol\">-</span>" +# define SYMBOL_LESS_COMMENT "</a> <span class=\"comment\">-" +static bool file_show_diff(mobley_t *ctx, server_req_t *r, int fd, size_t offset, size_t len) +{ + char *buffer; + + lseek(fd, offset, SEEK_SET); + buffer = calloc(1, len); + read(fd, buffer, len); + for (size_t i = 0; i < len; i++) + { + if (strncmp(buffer + i, SYMBOL_PLUS, sizeof(SYMBOL_PLUS) - 1) == 0) + { + html_raw("</a> <span class=\"symbol diff-plus\">+</span> "); + i += sizeof(SYMBOL_PLUS) - 2; + } + else if (strncmp(buffer + i, SYMBOL_PLUS_COMMENT, sizeof(SYMBOL_PLUS_COMMENT) - 1) == 0) + { + html_raw("</a> <span class=\"symbol diff-plus\">+</span> <span class=\"comment\">"); + i += sizeof(SYMBOL_PLUS_COMMENT) - 2; + } + else if (strncmp(buffer + i, SYMBOL_LESS_COMMENT, sizeof(SYMBOL_LESS_COMMENT) - 1) == 0) + { + html_raw("</a> <span class=\"symbol diff-less\">-</span> <span class=\"comment\">"); + i += sizeof(SYMBOL_PLUS_COMMENT) - 2; + } + else if (strncmp(buffer + i, SYMBOL_LESS, sizeof(SYMBOL_LESS) - 1) == 0) + { + html_raw("</a> <span class=\"symbol diff-less\">-</span> "); + i += sizeof(SYMBOL_LESS) - 2; + } + else + evbuffer_add(r->req->buffer_out, &buffer[i], 1); + } + + free(buffer); + return true; +} + # define TEMPLATE_NAME "/tmp/mobley_tmp_XXXXXX" -static bool file_show_code(mobley_t *ctx, server_req_t *r, const char *path, git_blob *blob) +# define SRC_HL_HEADER "<!-- Generator: GNU source-highlight 3.1.8\n" \ + "by Lorenzo Bettini\n" \ + "http://www.lorenzobettini.it\n" \ + "http://www.gnu.org/software/src-highlite -->\n" \ + "<pre><tt>" +# define LINE_TEMPLATE_BEGIN "<a name=\"%d\" href=\"#%d\"><span class=\"linenum\">" +# define LINE_TEMPLATE_END ":</span></a> \n" +bool _file_show_hl_code(mobley_t *ctx, server_req_t *r, const char *path, const void *blob, size_t size, show_hl_code_args_t *args) { source_highlight_init(ctx->conf.source_highlight_data_dir, "htmlcss.outlang", path); char *in = mkdtemp(strdup(TEMPLATE_NAME)); @@ -70,7 +118,11 @@ static bool file_show_code(mobley_t *ctx, server_req_t *r, const char *path, git snprintf(__path_out, sizeof(__path_out), "%s/file", out); fd_in = open(__path_in, O_WRONLY | O_CREAT, 0777); - write(fd_in, git_blob_rawcontent(blob), git_blob_rawsize(blob)); + + for (int i = 0; i < args->start_line; i++) + write(fd_in, "\n", 1); + + write(fd_in, blob, size); close(fd_in); source_highlight_file(__path_in, __path_out); @@ -79,7 +131,40 @@ static bool file_show_code(mobley_t *ctx, server_req_t *r, const char *path, git fd_out = open(__path_out, O_RDONLY); fstat(fd_out, &st); - evbuffer_add_file(r->req->buffer_out, fd_out, 0, st.st_size); + if (args->start_line > 0) + { + size_t offset = sizeof(SRC_HL_HEADER) - 1; + char line[250] = { 0 }; + + for (int i = 1; i <= args->start_line; i++) + { + if (args->start_line > 9999) + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%05d" LINE_TEMPLATE_END, i, i, i); + else if (args->start_line > 99999) + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%06d" LINE_TEMPLATE_END, i, i, i); + else if (args->start_line < 10) + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%d" LINE_TEMPLATE_END, i, i, i); + else if (args->start_line < 92) + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%02d" LINE_TEMPLATE_END, i, i, i); + else if (args->start_line > 999) + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%04d" LINE_TEMPLATE_END, i, i, i); + else + snprintf(line, sizeof(line), LINE_TEMPLATE_BEGIN "%03d" LINE_TEMPLATE_END, i, i, i); + offset += strlen(line); + } + html_raw("<pre><tt>"); + if (args->is_diff) + file_show_diff(ctx, r, fd_out, offset, st.st_size - offset); + else + evbuffer_add_file(r->req->buffer_out, fd_out, offset, st.st_size - offset); + } + else + { + if (args->is_diff) + file_show_diff(ctx, r, fd_out, 0, st.st_size); + else + evbuffer_add_file(r->req->buffer_out, fd_out, 0, st.st_size); + } close(fd_out); unlink(__path_in); @@ -134,7 +219,11 @@ bool file_show(mobley_t *ctx, server_req_t *r, git_tree_entry *entry, bool heade else if (file_is_image(path)) ret = file_show_image(ctx, r, r->path); else if (file_is_code(path)) - ret = file_show_code(ctx, r, r->path, (git_blob *)obj); + { + ret = file_show_hl_code(ctx, r, r->path, + git_blob_rawcontent((git_blob *)obj), + git_blob_rawsize((git_blob *)obj)); + } else ret = file_show_raw(ctx, r, (git_blob *)obj); } html_close("div"); diff --git a/file.h b/file.h @@ -20,4 +20,12 @@ bool file_show(mobley_t *ctx, server_req_t *r, git_tree_entry *entry, bool header); +typedef struct { + int start_line; + bool is_diff; +} show_hl_code_args_t; + +#define file_show_hl_code(c, r, p, b, s, ...) _file_show_hl_code(c, r, p, b, s, &(show_hl_code_args_t){__VA_ARGS__}) +bool _file_show_hl_code(mobley_t *ctx, server_req_t *r, const char *path, const void *blob, size_t size, show_hl_code_args_t *args); + #endif /* FILE_H */ diff --git a/html.c b/html.c @@ -98,20 +98,19 @@ char *__format(const char *s, ...) return __buffer; } - -void string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, size_t len, bool with_lines) +void _string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, size_t len, format_diff_args_t *args) { size_t line_num = 1; char link[30] = { 0 }; for (size_t i = 0; i < len && data[i] != '\0'; i++) { - if ((i == 0 || data[i - 1] == '\n') && with_lines) + if ((i == 0 || data[i - 1] == '\n') && args->with_lines) { - snprintf(link, sizeof(link), "#%zu", line_num); + snprintf(link, sizeof(link), "#%zu", line_num + args->start_line); - html_open("a", .name = __format("%zu", line_num), .href = link) { - html("span", __format("%zu:", line_num), .class = "linenum"); + html_open("a", .name = __format("%zu", line_num + args->start_line), .href = link) { + html("span", __format("%zu:", line_num + args->start_line), .class = "linenum"); } html_close("a"); html_raw(" "); @@ -123,6 +122,13 @@ void string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, si else if (data[i] == '>') evbuffer_add(r->req->buffer_out, "&gt;", 3); else - evbuffer_add(r->req->buffer_out, &data[i], 1); + { + if (data[i] == '+' && data[i - 1] == '\n' && args->is_diff) + html_raw("<span class=\"symbol diff-plus\">+</span> "); + else if (data[i] == '-' && data[i - 1] == '\n' && args->is_diff) + html_raw("<span class=\"symbol diff-less\">-</span> "); + else + evbuffer_add(r->req->buffer_out, &data[i], 1); + } } } diff --git a/html.h b/html.h @@ -68,6 +68,13 @@ void _html(server_req_t *r, const mobley_t *ctx, __PRINTF_FUNCTION(1, 2) char *__format(const char *s, ...); -void string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, size_t len, bool with_lines); +typedef struct { + bool with_lines; + bool is_diff; + int start_line; +} format_diff_args_t; + +#define string_raw_format_html(c, r, d, l, ...) _string_raw_format_html(c, r, d, l, &(format_diff_args_t){__VA_ARGS__}) +void _string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, size_t len, format_diff_args_t *args); #endif /* HTML_H */ diff --git a/repository_log.c b/repository_log.c @@ -15,6 +15,7 @@ #include <repository_log.h> #include <html.h> +#include <diff.h> typedef enum { NOT_SIGNED, @@ -49,37 +50,6 @@ static signature_type_t check_commit_signature(mobley_t *ctx, server_req_t *r, g else if (result->signatures->summary & GPGME_SIGSUM_KEY_MISSING) ret = SIGNATURE_UNKNOWN; -/* if (result->signatures->summary & GPGME_SIGSUM_GREEN)*/ - /*printf("GREEN\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_RED)*/ - /*printf("RED\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_VALID)*/ - /*printf("VALID\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_KEY_REVOKED)*/ - /*printf("GPGME_SIGSUM_KEY_REVOKED\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_SIG_EXPIRED)*/ - /*printf("GPGME_SIGSUM_SIG_EXPIRED\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_KEY_MISSING)*/ - /*printf("GPGME_SIGSUM_KEY_MISSING\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_CRL_MISSING)*/ - /*printf("GPGME_SIGSUM_CRL_MISSING\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_CRL_TOO_OLD)*/ - /*printf("GPGME_SIGSUM_CRL_TOO_OLD\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_BAD_POLICY)*/ - /*printf("GPGME_SIGSUM_BAD_POLICY\n");*/ - /*else if (result->signatures->summary & GPGME_SIGSUM_SYS_ERROR)*/ - /*printf("GPGME_SIGSUM_SYS_ERROR\n");*/ - /*else if (result->signatures->summary &GPGME_SIGSUM_TOFU_CONFLICT )*/ - /*printf("GPGME_SIGSUM_TOFU_CONFLICT\n");*/ - - /*printf("Key: %s\n", result->signatures->fpr);*/ - /*printf("Next: %p\n", result->signatures->next);*/ - /*printf("Error: %s\n", gpgme_strerror(result->signatures->validity_reason));*/ - /*printf("Error: %s\n", gpgme_strerror(result->signatures->status));*/ - /*printf("'%.*s'\n", (int)sig.size, sig.ptr);*/ - /*printf("'%.*s'\n", (int)data.size, data.ptr);*/ - /*printf("------\n");*/ - gpgme_data_release(commit_data); gpgme_data_release(signature); free(sig.ptr); @@ -87,6 +57,41 @@ static signature_type_t check_commit_signature(mobley_t *ctx, server_req_t *r, g return ret; } +static void dump_commit_signature(mobley_t *ctx, server_req_t *r, git_oid *oid) +{ + git_buf sig = { 0 }, data = { 0 }; + gpgme_data_t signature = NULL; + gpgme_data_t commit_data = NULL; + gpgme_verify_result_t result; + + if (git_commit_extract_signature(&sig, &data, r->repo->repo, oid, NULL) != 0) + { + html_raw("Commit is not signed"); + return ; + } + + gpgme_data_new_from_mem(&signature, sig.ptr, sig.size, 1); + gpgme_data_new_from_mem(&commit_data, data.ptr, data.size, 1); + + gpgme_error_t gpgerr = gpgme_op_verify(ctx->crypto.ctx, signature, commit_data, NULL); + if (gpgerr != GPG_ERR_NO_ERROR) + return ; + + result = gpgme_op_verify_result(ctx->crypto.ctx); + + if (result->signatures->status == GPG_ERR_NO_ERROR) + html_raw("Commit signed with key %s (Verified locally)", result->signatures->fpr); + else if (result->signatures->summary & GPGME_SIGSUM_KEY_MISSING) + html_raw("Commit signed with key %s (Cannot verify, key is missing)", result->signatures->fpr); + else + html_raw("Commit signature is not valid (%s)!", gpgme_strerror(result->signatures->validity_reason)); + + gpgme_data_release(commit_data); + gpgme_data_release(signature); + free(sig.ptr); + free(data.ptr); +} + # define MAX_COMMIT_PER_PAGE 50 bool repository_log_list(mobley_t *ctx, server_req_t *r) { @@ -135,7 +140,7 @@ bool repository_log_list(mobley_t *ctx, server_req_t *r) } html_close("td"); html_open("td", .width = "80%") { html_open("a", .href = __format("log/%s", git_oid_tostr_s(&tmp))) { - string_raw_format_html(ctx, r, git_commit_summary(current), 150, false); + string_raw_format_html(ctx, r, git_commit_summary(current), 150); } html_close("a"); } html_close("td"); html_open("td") { @@ -177,8 +182,114 @@ fail: return ret; } -bool repository_log_commit(mobley_t *ctx, server_req_t *r, const char *oid) +static bool commit_show_header(mobley_t *ctx, server_req_t *r, git_commit *commit) { - html("h1", __format("Commit: %s", oid)); + html_open("b", .class = "commit-summary") { + html_raw("%s", git_commit_summary(commit)); + } html_close("b"); + + html_open("ul", .class = "commit-header") { + html_open("li") { + html("b", "commit"); + html_raw(" : "); + html("a", git_oid_tostr_s(git_commit_id(commit)), + .href = __format("log/%s", git_oid_tostr_s(git_commit_id(commit))) + ); + + } html_close("li"); + + html_open("li") { + git_commit *parent; + + git_commit_parent(&parent, commit, 0); + html("b", "parent"); + html_raw(" : "); + html("a", git_oid_tostr_s(git_commit_id(parent)), + .href = __format("log/%s", git_oid_tostr_s(git_commit_id(parent))) + ); + + } html_close("li"); + + html_open("li") { + html("b", "author"); + html_raw(" : "); + html_raw("%s &lt;%s&gt;", git_commit_author(commit)->name, git_commit_author(commit)->email); + } html_close("li"); + + html_open("li") { + html("b", "committer"); + html_raw(" : "); + html_raw("%s &lt;%s&gt;", git_commit_committer(commit)->name, git_commit_committer(commit)->email); + } html_close("li"); + + html_open("li") { + git_time_t time; + html("b", "date"); + html_raw(" : "); + time = git_commit_time(commit); + html_git_date_ftime(&time, "%a, %d %b %Y"); + } html_close("li"); + + html_open("li") { + html("b", "GPG"); + html_raw(" : "); + dump_commit_signature(ctx, r, (git_oid *)git_commit_id(commit)); + } html_close("li"); + + html_open("li", .class = "commit-content") { + html("pre", git_commit_body(commit)); + } html_close("li"); + } html_close("ul"); + + html_nd("hr"); return true; } + +bool repository_log_commit(mobley_t *ctx, server_req_t *r, const char *sha) +{ + git_tree *diff_tree = NULL, *diff_parent_tree= NULL; + git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + git_commit *one = NULL, *two = NULL; + git_oid oid; + bool ret = false; + + git_oid_fromstr(&oid, sha); + git_commit_lookup(&one, r->repo->repo, &oid); + if (one == NULL) + goto end; + + git_commit_tree(&diff_tree, one); + if(diff_tree == NULL) + goto end; + + git_commit_parent(&two, one, 0); + if (two != NULL) + git_commit_tree(&diff_parent_tree, two); + + if (git_diff_tree_to_tree(&diff, r->repo->repo, diff_parent_tree, diff_tree, &diff_opts) != 0) + { + html("h1", "ERROR"); + goto end; + } + + if (!commit_show_header(ctx, r, one)) + goto end; + + diff_show(ctx, r, diff); + + ret = true; +end: + if (two != NULL) + git_commit_free(two); + if (one != NULL) + git_commit_free(one); + if (diff != NULL) + git_diff_free(diff); + if (diff_tree != NULL) + git_tree_free(diff_tree); + if (diff_parent_tree != NULL) + git_tree_free(diff_parent_tree); + + return ret; +} diff --git a/style/main.css b/style/main.css @@ -287,6 +287,10 @@ table tbody .commit a { font-style: normal; } +.oldfile { color: #ff0000b3; } +.newfile { color: #55ff55b3; } +.difflines { color: blue; } + .commit_list tr td { overflow: hidden; } @@ -303,3 +307,54 @@ table tbody .commit a { color: white; background: transparent; } + +.commit-summary { + padding-left: 20px; + font-size: 1.2em; +} + +.commit-header { + list-style: none; + padding-left: 20px; +} + +.commit-header b { + width: 80px; + display: inline-block; +} + +.commit-header pre { + font-size: 0.85em; +} + +.commit-header .commit-content { + margin-top: 10px; +} + +.file-content hr { + margin-left: -10px; + margin-right: -10px; +} + +.status-line { + margin-left: 20px; + margin-bottom: -15px; + display: block; + background-color: #212020; + padding: 2px 0px; + padding-left: 20px; +} + +.diff-plus { + background: #00800033 !important; + display: inline-block; + position: absolute; + width: 96.5%; +} + +.diff-less { + background: #f003 !important; + display: inline-block; + position: absolute; + width: 96.5%; +} diff --git a/watm/shl-wrapper-lib.cpp b/watm/shl-wrapper-lib.cpp @@ -31,7 +31,7 @@ extern "C" bool source_highlight_init(const char *data_dir, const char *outlang, ctx->setDataDir(data_dir); ctx->setGenerateLineNumbers(true); ctx->setGenerateLineNumberRefs(true); - ctx->setLineNumberPad(0); + ctx->setLineNumberPad(' '); return true; }