aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--.mailmap4
-rw-r--r--Makefile4
-rw-r--r--cache.c8
-rw-r--r--cache.h2
-rw-r--r--cgit.c35
-rw-r--r--cgit.css34
-rw-r--r--cgit.h2
-rw-r--r--cgit.mk3
-rw-r--r--cgitrc.5.txt9
-rw-r--r--cmd.c12
-rw-r--r--filter.c13
-rwxr-xr-xfilters/syntax-highlighting.py6
-rwxr-xr-xfilters/syntax-highlighting.sh2
m---------git0
-rw-r--r--html.c32
-rw-r--r--html.h2
-rw-r--r--parsing.c3
-rw-r--r--scan-tree.c5
-rw-r--r--shared.c12
-rwxr-xr-xtests/t0109-gitconfig.sh10
-rw-r--r--ui-atom.c2
-rw-r--r--ui-blame.c298
-rw-r--r--ui-blame.h6
-rw-r--r--ui-blob.c15
-rw-r--r--ui-clone.c3
-rw-r--r--ui-commit.c8
-rw-r--r--ui-diff.c20
-rw-r--r--ui-log.c17
-rw-r--r--ui-patch.c6
-rw-r--r--ui-plain.c4
-rw-r--r--ui-repolist.c40
-rw-r--r--ui-shared.c76
-rw-r--r--ui-shared.h7
-rw-r--r--ui-snapshot.c4
-rw-r--r--ui-tag.c4
-rw-r--r--ui-tree.c63
37 files changed, 579 insertions, 194 deletions
diff --git a/.gitmodules b/.gitmodules
index 1daea94..5c6ecb4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "git"]
- url = git://git.kernel.org/pub/scm/git/git.git
+ url = https://git.kernel.org/pub/scm/git/git.git
path = git
diff --git a/.mailmap b/.mailmap
index ca6b57e..03b5479 100644
--- a/.mailmap
+++ b/.mailmap
@@ -5,6 +5,6 @@ Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)>
Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)>
Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net>
Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net>
-Lukas Fleischer <cgit@cryptocrack.de> <cgit@crytocrack.de>
-Lukas Fleischer <cgit@cryptocrack.de> <info@cryptocrack.de>
+Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de>
+Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de>
Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de>
diff --git a/Makefile b/Makefile
index 1ab2905..687069f 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ htmldir = $(docdir)
pdfdir = $(docdir)
mandir = $(prefix)/share/man
SHA1_HEADER = <openssl/sha.h>
-GIT_VER = 2.10.2
+GIT_VER = 2.16.0
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz
INSTALL = install
COPYTREE = cp -r
@@ -134,7 +134,7 @@ doc-pdf: $(DOC_PDF)
a2x -f manpage $<
$(DOC_HTML): %.html : %.txt
- a2x -f xhtml --stylesheet=cgit-doc.css $<
+ a2x -f xhtml --stylesheet=cgit-doc.css --xsltproc-opts="--param generate.consistent.ids 1" $<
$(DOC_PDF): %.pdf : %.txt
a2x -f pdf cgitrc.5.txt
diff --git a/cache.c b/cache.c
index 6736a01..0901e6e 100644
--- a/cache.c
+++ b/cache.c
@@ -224,6 +224,12 @@ static int fill_slot(struct cache_slot *slot)
/* Generate cache content */
slot->fn();
+ /* Make sure any buffered data is flushed to the file */
+ if (fflush(stdout)) {
+ close(tmp);
+ return errno;
+ }
+
/* update stat info */
if (fstat(slot->lock_fd, &slot->cache_st)) {
close(tmp);
@@ -312,7 +318,7 @@ static int process_slot(struct cache_slot *slot)
/* If the cache slot does not exist (or its key doesn't match the
* current key), lets try to create a new cache slot for this
* request. If this fails (for whatever reason), lets just generate
- * the content without caching it and fool the caller to belive
+ * the content without caching it and fool the caller to believe
* everything worked out (but print a warning on stdout).
*/
diff --git a/cache.h b/cache.h
index 9392836..470da4f 100644
--- a/cache.h
+++ b/cache.h
@@ -19,7 +19,7 @@ typedef void (*cache_fill_fn)(void);
* fn content generator function for this key
*
* Return value
- * 0 indicates success, everyting else is an error
+ * 0 indicates success, everything else is an error
*/
extern int cache_process(int size, const char *path, const char *key, int ttl,
cache_fill_fn fn);
diff --git a/cgit.c b/cgit.c
index 1075753..bd9cb3f 100644
--- a/cgit.c
+++ b/cgit.c
@@ -167,6 +167,8 @@ static void config_cb(const char *name, const char *value)
ctx.cfg.enable_index_links = atoi(value);
else if (!strcmp(name, "enable-index-owner"))
ctx.cfg.enable_index_owner = atoi(value);
+ else if (!strcmp(name, "enable-blame"))
+ ctx.cfg.enable_blame = atoi(value);
else if (!strcmp(name, "enable-commit-graph"))
ctx.cfg.enable_commit_graph = atoi(value);
else if (!strcmp(name, "enable-log-filecount"))
@@ -476,7 +478,7 @@ static char *guess_defbranch(void)
const char *ref, *refname;
struct object_id oid;
- ref = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL);
+ ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL);
if (!ref || !skip_prefix(ref, "refs/heads/", &refname))
return "master";
return xstrdup(refname);
@@ -559,12 +561,8 @@ static void print_no_repo_clone_urls(const char *url)
html("</a></td></tr>\n");
}
-static int prepare_repo_cmd(void)
+static void prepare_repo_env(int *nongit)
{
- struct object_id oid;
- int nongit = 0;
- int rc;
-
/* The path to the git repository. */
setenv("GIT_DIR", ctx.repo->path, 1);
@@ -577,8 +575,13 @@ static int prepare_repo_cmd(void)
/* Setup the git directory and initialize the notes system. Both of these
* load local configuration from the git repository, so we do them both while
* the HOME variables are unset. */
- setup_git_directory_gently(&nongit);
+ setup_git_directory_gently(nongit);
init_display_notes(NULL);
+}
+static int prepare_repo_cmd(int nongit)
+{
+ struct object_id oid;
+ int rc;
if (nongit) {
const char *name = ctx.repo->name;
@@ -698,6 +701,7 @@ static inline void authenticate_cookie(void)
static void process_request(void)
{
struct cgit_cmd *cmd;
+ int nongit = 0;
/* If we're not yet authenticated, no matter what page we're on,
* display the authentication body from the auth_filter. This should
@@ -713,6 +717,9 @@ static void process_request(void)
return;
}
+ if (ctx.repo)
+ prepare_repo_env(&nongit);
+
cmd = cgit_get_cmd();
if (!cmd) {
ctx.page.title = "cgit error";
@@ -726,19 +733,19 @@ static void process_request(void)
return;
}
- /* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual"
- * in-project path limit to be made available at ctx.qry.vpath.
- * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL).
- */
- ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
-
if (cmd->want_repo && !ctx.repo) {
cgit_print_error_page(400, "Bad request",
"No repository selected");
return;
}
- if (ctx.repo && prepare_repo_cmd())
+ /* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual"
+ * in-project path limit to be made available at ctx.qry.vpath.
+ * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL).
+ */
+ ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
+
+ if (ctx.repo && prepare_repo_cmd(nongit))
return;
cmd->fn();
diff --git a/cgit.css b/cgit.css
index 1dc2c11..217a05a 100644
--- a/cgit.css
+++ b/cgit.css
@@ -300,6 +300,7 @@ div#cgit table.blob {
border-top: solid 1px black;
}
+div#cgit table.blob td.hashes,
div#cgit table.blob td.lines {
margin: 0; padding: 0 0 0 0.5em;
vertical-align: top;
@@ -329,6 +330,39 @@ div#cgit table.ssdiff td.lineno a:hover {
color: black;
}
+div#cgit table.blame td.hashes,
+div#cgit table.blame td.lines,
+div#cgit table.blame td.linenumbers {
+ padding: 0;
+}
+
+div#cgit table.blame td.hashes div.alt,
+div#cgit table.blame td.lines div.alt {
+ padding: 0 0.5em 0 0.5em;
+}
+
+div#cgit table.blame td.linenumbers div.alt {
+ padding: 0 0.5em 0 0;
+}
+
+div#cgit table.blame div.alt:nth-child(even) {
+ background: #eee;
+}
+
+div#cgit table.blame div.alt:nth-child(odd) {
+ background: white;
+}
+
+div#cgit table.blame td.lines > div {
+ position: relative;
+}
+
+div#cgit table.blame td.lines > div > pre {
+ padding: 0 0 0 0.5em;
+ position: absolute;
+ top: 0;
+}
+
div#cgit table.bin-blob {
margin-top: 0.5em;
border: solid 1px black;
diff --git a/cgit.h b/cgit.h
index fbc6c6a..005ae63 100644
--- a/cgit.h
+++ b/cgit.h
@@ -71,7 +71,6 @@ struct cgit_exec_filter {
char *cmd;
char **argv;
int old_stdout;
- int pipe_fh[2];
int pid;
};
@@ -228,6 +227,7 @@ struct cgit_config {
int enable_http_clone;
int enable_index_links;
int enable_index_owner;
+ int enable_blame;
int enable_commit_graph;
int enable_log_filecount;
int enable_log_linecount;
diff --git a/cgit.mk b/cgit.mk
index 8d4f5e0..3fcc1ca 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -54,7 +54,7 @@ endif
endif
# Add -ldl to linker flags on systems that commonly use GNU libc.
-ifneq (,$(filter $(uname_S),Linux GNU/kFreeBSD))
+ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
CGIT_LIBS += -ldl
endif
@@ -77,6 +77,7 @@ CGIT_OBJ_NAMES += parsing.o
CGIT_OBJ_NAMES += scan-tree.o
CGIT_OBJ_NAMES += shared.o
CGIT_OBJ_NAMES += ui-atom.o
+CGIT_OBJ_NAMES += ui-blame.o
CGIT_OBJ_NAMES += ui-blob.o
CGIT_OBJ_NAMES += ui-clone.o
CGIT_OBJ_NAMES += ui-commit.o
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 9fcf445..4da166c 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -141,6 +141,11 @@ embedded::
suitable for embedding in other html pages. Default value: none. See
also: "noheader".
+enable-blame::
+ Flag which, when set to "1", will allow cgit to provide a "blame" page
+ for files, and will make it generate links to that page in appropriate
+ places. Default value: "0".
+
enable-commit-graph::
Flag which, when set to "1", will make cgit print an ASCII-art commit
history graph to the left of the commit messages in the repository
@@ -799,6 +804,10 @@ enable-http-clone=1
enable-index-links=1
+# Enable blame page and create links to it from tree page
+enable-blame=1
+
+
# Enable ASCII art commit history graph on the log pages
enable-commit-graph=1
diff --git a/cmd.c b/cmd.c
index d280e95..63f0ae5 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,6 +1,6 @@
/* cmd.c: the cgit command dispatcher
*
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -11,6 +11,7 @@
#include "cache.h"
#include "ui-shared.h"
#include "ui-atom.h"
+#include "ui-blame.h"
#include "ui-blob.h"
#include "ui-clone.h"
#include "ui-commit.h"
@@ -63,6 +64,14 @@ static void about_fn(void)
cgit_print_site_readme();
}
+static void blame_fn(void)
+{
+ if (ctx.cfg.enable_blame)
+ cgit_print_blame();
+ else
+ cgit_print_error_page(403, "Forbidden", "Blame is disabled");
+}
+
static void blob_fn(void)
{
cgit_print_blob(ctx.qry.sha1, ctx.qry.path, ctx.qry.head, 0);
@@ -164,6 +173,7 @@ struct cgit_cmd *cgit_get_cmd(void)
def_cmd(HEAD, 1, 0, 1),
def_cmd(atom, 1, 0, 0),
def_cmd(about, 0, 0, 0),
+ def_cmd(blame, 1, 1, 0),
def_cmd(blob, 1, 0, 0),
def_cmd(commit, 1, 1, 0),
def_cmd(diff, 1, 1, 0),
diff --git a/filter.c b/filter.c
index 949c931..70f5b74 100644
--- a/filter.c
+++ b/filter.c
@@ -42,6 +42,7 @@ void cgit_cleanup_filters(void)
static int open_exec_filter(struct cgit_filter *base, va_list ap)
{
struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
+ int pipe_fh[2];
int i;
for (i = 0; i < filter->base.argument_count; i++)
@@ -49,19 +50,19 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap)
filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
"Unable to duplicate STDOUT");
- chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
+ chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
if (filter->pid == 0) {
- close(filter->pipe_fh[1]);
- chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
+ close(pipe_fh[1]);
+ chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
"Unable to use pipe as STDIN");
execvp(filter->cmd, filter->argv);
die_errno("Unable to exec subprocess %s", filter->cmd);
}
- close(filter->pipe_fh[0]);
- chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
+ close(pipe_fh[0]);
+ chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
"Unable to use pipe as STDOUT");
- close(filter->pipe_fh[1]);
+ close(pipe_fh[1]);
return 0;
}
diff --git a/filters/syntax-highlighting.py b/filters/syntax-highlighting.py
index 1ca4108..e912594 100755
--- a/filters/syntax-highlighting.py
+++ b/filters/syntax-highlighting.py
@@ -30,11 +30,11 @@ from pygments.lexers import guess_lexer_for_filename
from pygments.formatters import HtmlFormatter
-sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
-sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
data = sys.stdin.read()
filename = sys.argv[1]
-formatter = HtmlFormatter(style='pastie')
+formatter = HtmlFormatter(style='pastie', nobackground=True)
try:
lexer = guess_lexer_for_filename(filename, data)
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 4fa7928..840bc34 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -1,6 +1,6 @@
#!/bin/sh
# This script can be used to implement syntax highlighting in the cgit
-# tree-view by refering to this file with the source-filter or repo.source-
+# tree-view by referring to this file with the source-filter or repo.source-
# filter options in cgitrc.
#
# This script requires a shell supporting the ${var##pattern} syntax.
diff --git a/git b/git
-Subproject 6406bdc0b95715a087fdeeb0f6adf3deb80a25b
+Subproject 2512f15446149235156528dafbe75930c712b29
diff --git a/html.c b/html.c
index e7e6e07..7f81965 100644
--- a/html.c
+++ b/html.c
@@ -124,29 +124,20 @@ void html_vtxtf(const char *format, va_list ap)
void html_txt(const char *txt)
{
- const char *t = txt;
- while (t && *t) {
- int c = *t;
- if (c == '<' || c == '>' || c == '&') {
- html_raw(txt, t - txt);
- if (c == '>')
- html("&gt;");
- else if (c == '<')
- html("&lt;");
- else if (c == '&')
- html("&amp;");
- txt = t + 1;
- }
- t++;
- }
- if (t != txt)
- html(txt);
+ if (txt)
+ html_ntxt(txt, strlen(txt));
}
-void html_ntxt(int len, const char *txt)
+ssize_t html_ntxt(const char *txt, size_t len)
{
const char *t = txt;
- while (t && *t && len--) {
+ ssize_t slen;
+
+ if (len > SSIZE_MAX)
+ return -1;
+
+ slen = (ssize_t) len;
+ while (t && *t && slen--) {
int c = *t;
if (c == '<' || c == '>' || c == '&') {
html_raw(txt, t - txt);
@@ -162,8 +153,7 @@ void html_ntxt(int len, const char *txt)
}
if (t != txt)
html_raw(txt, t - txt);
- if (len < 0)
- html("...");
+ return slen;
}
void html_attrf(const char *fmt, ...)
diff --git a/html.h b/html.h
index 1b24e55..fa4de77 100644
--- a/html.h
+++ b/html.h
@@ -19,7 +19,7 @@ __attribute__((format (printf,1,2)))
extern void html_attrf(const char *format,...);
extern void html_txt(const char *txt);
-extern void html_ntxt(int len, const char *txt);
+extern ssize_t html_ntxt(const char *txt, size_t len);
extern void html_attr(const char *txt);
extern void html_url_path(const char *txt);
extern void html_url_arg(const char *txt);
diff --git a/parsing.c b/parsing.c
index 9dacb16..fd1ea99 100644
--- a/parsing.c
+++ b/parsing.c
@@ -20,10 +20,10 @@ void cgit_parse_url(const char *url)
char *c, *cmd, *p;
struct cgit_repo *repo;
- ctx.repo = NULL;
if (!url || url[0] == '\0')
return;
+ ctx.qry.page = NULL;
ctx.repo = cgit_get_repoinfo(url);
if (ctx.repo) {
ctx.qry.repo = ctx.repo->url;
@@ -53,7 +53,6 @@ void cgit_parse_url(const char *url)
}
if (cmd[1])
ctx.qry.page = xstrdup(cmd + 1);
- return;
}
}
diff --git a/scan-tree.c b/scan-tree.c
index 08f3f1d..6a2f65a 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -10,6 +10,7 @@
#include "scan-tree.h"
#include "configfile.h"
#include "html.h"
+#include <config.h>
/* return 1 if path contains a objects/ directory and a HEAD file */
static int is_git_dir(const char *path)
@@ -48,7 +49,7 @@ out:
static struct cgit_repo *repo;
static repo_config_fn config_fn;
-static void repo_config(const char *name, const char *value)
+static void scan_tree_repo_config(const char *name, const char *value)
{
config_fn(repo, name, value);
}
@@ -178,7 +179,7 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
strbuf_addstr(path, "cgitrc");
if (!stat(path->buf, &st))
- parse_configfile(path->buf, &repo_config);
+ parse_configfile(path->buf, &scan_tree_repo_config);
strbuf_release(&rel);
}
diff --git a/shared.c b/shared.c
index c63f1e3..21ac8f4 100644
--- a/shared.c
+++ b/shared.c
@@ -160,7 +160,7 @@ static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_
ref = xmalloc(sizeof (struct refinfo));
ref->refname = xstrdup(refname);
- ref->object = parse_object(oid->hash);
+ ref->object = parse_object(oid);
switch (ref->object->type) {
case OBJ_TAG:
ref->tag = cgit_parse_tag((struct tag *)ref->object);
@@ -346,13 +346,13 @@ void cgit_diff_tree(const struct object_id *old_oid,
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.detect_rename = 1;
opt.rename_limit = ctx.cfg.renamelimit;
- DIFF_OPT_SET(&opt, RECURSIVE);
+ opt.flags.recursive = 1;
if (ignorews)
DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
opt.format_callback = cgit_diff_tree_cb;
opt.format_callback_data = fn;
if (prefix) {
- item.match = prefix;
+ item.match = xstrdup(prefix);
item.len = strlen(prefix);
opt.pathspec.nr = 1;
opt.pathspec.items = &item;
@@ -360,11 +360,13 @@ void cgit_diff_tree(const struct object_id *old_oid,
diff_setup_done(&opt);
if (old_oid && !is_null_oid(old_oid))
- diff_tree_sha1(old_oid->hash, new_oid->hash, "", &opt);
+ diff_tree_oid(old_oid, new_oid, "", &opt);
else
- diff_root_tree_sha1(new_oid->hash, "", &opt);
+ diff_root_tree_oid(new_oid, "", &opt);
diffcore_std(&opt);
diff_flush(&opt);
+
+ free(item.match);
}
void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
diff --git a/tests/t0109-gitconfig.sh b/tests/t0109-gitconfig.sh
index 5a84258..3ba6684 100755
--- a/tests/t0109-gitconfig.sh
+++ b/tests/t0109-gitconfig.sh
@@ -10,16 +10,16 @@ test -n "$(which strace 2>/dev/null)" || {
}
test_no_home_access () {
- non_existant_path="/path/to/some/place/that/does/not/possibly/exist"
- while test -d "$non_existant_path"; do
- non_existant_path="$non_existant_path/$(date +%N)"
+ non_existent_path="/path/to/some/place/that/does/not/possibly/exist"
+ while test -d "$non_existent_path"; do
+ non_existent_path="$non_existent_path/$(date +%N)"
done &&
strace \
- -E HOME="$non_existant_path" \
+ -E HOME="$non_existent_path" \
-E CGIT_CONFIG="$PWD/cgitrc" \
-E QUERY_STRING="url=$1" \
-e access -f -o strace.out cgit &&
- test_must_fail grep "$non_existant_path" strace.out
+ test_must_fail grep "$non_existent_path" strace.out
}
test_no_home_access_success() {
diff --git a/ui-atom.c b/ui-atom.c
index 41838d3..3866823 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -63,7 +63,7 @@ static void add_entry(struct commit *commit, const char *host)
html_attr(pageurl);
if (ctx.cfg.virtual_root)
delim = '?';
- htmlf("%cid=%s", delim, hex);
+ html_attrf("%cid=%s", delim, hex);
html("'/>\n");
free(pageurl);
}
diff --git a/ui-blame.c b/ui-blame.c
new file mode 100644
index 0000000..17e2d60
--- /dev/null
+++ b/ui-blame.c
@@ -0,0 +1,298 @@
+/* ui-blame.c: functions for blame output
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-blame.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "argv-array.h"
+#include "blame.h"
+
+
+static char *emit_suspect_detail(struct blame_origin *suspect)
+{
+ struct commitinfo *info;
+ struct strbuf detail = STRBUF_INIT;
+
+ info = cgit_parse_commit(suspect->commit);
+
+ strbuf_addf(&detail, "author %s", info->author);
+ if (!ctx.cfg.noplainemail)
+ strbuf_addf(&detail, " %s", info->author_email);
+ strbuf_addf(&detail, " %s\n",
+ show_date(info->author_date, info->author_tz,
+ cgit_date_mode(DATE_ISO8601)));
+
+ strbuf_addf(&detail, "committer %s", info->committer);
+ if (!ctx.cfg.noplainemail)
+ strbuf_addf(&detail, " %s", info->committer_email);
+ strbuf_addf(&detail, " %s\n\n",
+ show_date(info->committer_date, info->committer_tz,
+ cgit_date_mode(DATE_ISO8601)));
+
+ strbuf_addstr(&detail, info->subject);
+
+ cgit_free_commitinfo(info);
+ return strbuf_detach(&detail, NULL);
+}
+
+static void emit_blame_entry_hash(struct blame_entry *ent)
+{
+ struct blame_origin *suspect = ent->suspect;
+ struct object_id *oid = &suspect->commit->object.oid;
+ unsigned long line = 0;
+
+ char *detail = emit_suspect_detail(suspect);
+ html("<span class='sha1'>");
+ cgit_commit_link(find_unique_abbrev(oid->hash, DEFAULT_ABBREV), detail,
+ NULL, ctx.qry.head, oid_to_hex(oid), suspect->path);
+ html("</span>");
+ free(detail);
+
+ while (line++ < ent->num_lines)
+ html("\n");
+}
+
+static void emit_blame_entry_linenumber(struct blame_entry *ent)
+{
+ const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
+
+ unsigned long lineno = ent->lno;
+ while (lineno < ent->lno + ent->num_lines)
+ htmlf(numberfmt, ++lineno);
+}
+
+static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
+ struct blame_entry *ent)
+{
+ unsigned long line;
+ size_t len, maxlen = 2;
+ const char* pos, *endpos;
+
+ for (line = ent->lno; line < ent->lno + ent->num_lines; line++) {
+ html("\n");
+ pos = blame_nth_line(sb, line);
+ endpos = blame_nth_line(sb, line + 1);
+ len = 0;
+ while (pos < endpos) {
+ len++;
+ if (*pos++ == '\t')
+ len = (len + 7) & ~7;
+ }
+ if (len > maxlen)
+ maxlen = len;
+ }
+
+ for (len = 0; len < maxlen - 1; len++)
+ html(" ");
+}
+
+struct walk_tree_context {
+ char *curr_rev;
+ int match_baselen;
+ int state;
+};
+
+static void print_object(const unsigned char *sha1, const char *path,
+ const char *basename, const char *rev)
+{
+ enum object_type type;
+ char *buf;
+ unsigned long size;
+ struct argv_array rev_argv = ARGV_ARRAY_INIT;
+ struct rev_info revs;
+ struct blame_scoreboard sb;
+ struct blame_origin *o;
+ struct blame_entry *ent = NULL;
+
+ type = sha1_object_info(sha1, &size);
+ if (type == OBJ_BAD) {
+ cgit_print_error_page(404, "Not found", "Bad object name: %s",
+ sha1_to_hex(sha1));
+ return;
+ }
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf) {
+ cgit_print_error_page(500, "Internal server error",
+ "Error reading object %s", sha1_to_hex(sha1));
+ return;
+ }
+
+ argv_array_push(&rev_argv, "blame");
+ argv_array_push(&rev_argv, rev);
+ init_revisions(&revs, NULL);
+ revs.diffopt.flags.allow_textconv = 1;
+ setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL);
+ init_scoreboard(&sb);
+ sb.revs = &revs;
+ setup_scoreboard(&sb, path, &o);
+ o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o);
+ prio_queue_put(&sb.commits, o->commit);
+ blame_origin_decref(o);
+ sb.ent = NULL;
+ sb.path = path;
+ assign_blame(&sb, 0);
+ blame_sort_final(&sb);
+ blame_coalesce(&sb);
+
+ cgit_set_title_from_path(path);
+
+ cgit_print_layout_start();
+ htmlf("blob: %s (", sha1_to_hex(sha1));
+ cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
+ html(") (");
+ cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path);
+ html(")\n");
+
+ if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
+ htmlf("<div class='error'>blob size (%ldKB)"
+ " exceeds display size limit (%dKB).</div>",
+ size / 1024, ctx.cfg.max_blob_size);
+ return;
+ }
+
+ html("<table class='blame blob'>\n<tr>\n");
+
+ /* Commit hashes */
+ html("<td class='hashes'>");
+ for (ent = sb.ent; ent; ent = ent->next) {
+ html("<div class='alt'><pre>");
+ emit_blame_entry_hash(ent);
+ html("</pre></div>");
+ }
+ html("</td>\n");
+
+ /* Line numbers */
+ if (ctx.cfg.enable_tree_linenumbers) {
+ html("<td class='linenumbers'>");
+ for (ent = sb.ent; ent; ent = ent->next) {
+ html("<div class='alt'><pre>");
+ emit_blame_entry_linenumber(ent);
+ html("</pre></div>");
+ }
+ html("</td>\n");
+ }
+
+ html("<td class='lines'><div>");
+
+ /* Colored bars behind lines */
+ html("<div>");
+ for (ent = sb.ent; ent; ) {
+ struct blame_entry *e = ent->next;
+ html("<div class='alt'><pre>");
+ emit_blame_entry_line_background(&sb, ent);
+ html("</pre></div>");
+ free(ent);
+ ent = e;
+ }
+ html("</div>");
+
+ free((void *)sb.final_buf);
+
+ /* Lines */
+ html("<pre><code>");
+ if (ctx.repo->source_filter) {
+ char *filter_arg = xstrdup(basename);
+ cgit_open_filter(ctx.repo->source_filter, filter_arg);
+ html_raw(buf, size);
+ cgit_close_filter(ctx.repo->source_filter);
+ free(filter_arg);
+ } else {
+ html_txt(buf);
+ }
+ html("</code></pre>");
+
+ html("</div></td>\n");
+
+ html("</tr>\n</table>\n");
+
+ cgit_print_layout_end();
+}
+
+static int walk_tree(const unsigned char *sha1, struct strbuf *base,
+ const char *pathname, unsigned mode, int stage,
+ void *cbdata)
+{
+ struct walk_tree_context *walk_tree_ctx = cbdata;
+
+ if (base->len == walk_tree_ctx->match_baselen) {
+ if (S_ISREG(mode)) {
+ struct strbuf buffer = STRBUF_INIT;
+ strbuf_addbuf(&buffer, base);
+ strbuf_addstr(&buffer, pathname);
+ print_object(sha1, buffer.buf, pathname,
+ walk_tree_ctx->curr_rev);
+ strbuf_release(&buffer);
+ walk_tree_ctx->state = 1;
+ } else if (S_ISDIR(mode)) {
+ walk_tree_ctx->state = 2;
+ }
+ } else if (base->len < INT_MAX
+ && (int)base->len > walk_tree_ctx->match_baselen) {
+ walk_tree_ctx->state = 2;
+ } else if (S_ISDIR(mode)) {
+ return READ_TREE_RECURSIVE;
+ }
+ return 0;
+}
+
+static int basedir_len(const char *path)
+{
+ char *p = strrchr(path, '/');
+ if (p)
+ return p - path + 1;
+ return 0;
+}
+
+void cgit_print_blame(void)
+{
+ const char *rev = ctx.qry.sha1;
+ struct object_id oid;
+ struct commit *commit;
+ struct pathspec_item path_items = {
+ .match = ctx.qry.path,
+ .len = ctx.qry.path ? strlen(ctx.qry.path) : 0
+ };
+ struct pathspec paths = {
+ .nr = 1,
+ .items = &path_items
+ };
+ struct walk_tree_context walk_tree_ctx = {
+ .state = 0
+ };
+
+ if (!rev)
+ rev = ctx.qry.head;
+
+ if (get_oid(rev, &oid)) {
+ cgit_print_error_page(404, "Not found",
+ "Invalid revision name: %s", rev);
+ return;
+ }
+ commit = lookup_commit_reference(&oid);
+ if (!commit || parse_commit(commit)) {
+ cgit_print_error_page(404, "Not found",
+ "Invalid commit reference: %s", rev);
+ return;
+ }
+
+ walk_tree_ctx.curr_rev = xstrdup(rev);
+ walk_tree_ctx.match_baselen = (path_items.match) ?
+ basedir_len(path_items.match) : -1;
+
+ read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree,
+ &walk_tree_ctx);
+ if (!walk_tree_ctx.state)
+ cgit_print_error_page(404, "Not found", "Not found");
+ else if (walk_tree_ctx.state == 2)
+ cgit_print_error_page(404, "No blame for folders",
+ "Blame is not available for folders.");
+
+ free(walk_tree_ctx.curr_rev);
+}
diff --git a/ui-blame.h b/ui-blame.h
new file mode 100644
index 0000000..5b97e03
--- /dev/null
+++ b/ui-blame.h
@@ -0,0 +1,6 @@
+#ifndef UI_BLAME_H
+#define UI_BLAME_H
+
+extern void cgit_print_blame(void);
+
+#endif /* UI_BLAME_H */
diff --git a/ui-blob.c b/ui-blob.c
index 5f30de7..761e886 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -38,7 +38,7 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
struct object_id oid;
unsigned long size;
struct pathspec_item path_items = {
- .match = path,
+ .match = xstrdup(path),
.len = strlen(path)
};
struct pathspec paths = {
@@ -53,10 +53,13 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
};
if (get_oid(ref, &oid))
- return 0;
+ goto done;
if (sha1_object_info(oid.hash, &size) != OBJ_COMMIT)
- return 0;
- read_tree_recursive(lookup_commit_reference(oid.hash)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
+ goto done;
+ read_tree_recursive(lookup_commit_reference(&oid)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
+
+done:
+ free(path_items.match);
return walk_tree_ctx.found_path;
}
@@ -86,7 +89,7 @@ int cgit_print_file(char *path, const char *head, int file_only)
return -1;
type = sha1_object_info(oid.hash, &size);
if (type == OBJ_COMMIT) {
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
if (!walk_tree_ctx.found_path)
return -1;
@@ -142,7 +145,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl
type = sha1_object_info(oid.hash, &size);
if ((!hex) && type == OBJ_COMMIT && path) {
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
type = sha1_object_info(oid.hash, &size);
}
diff --git a/ui-clone.c b/ui-clone.c
index 5f6606a..bc98980 100644
--- a/ui-clone.c
+++ b/ui-clone.c
@@ -11,13 +11,14 @@
#include "ui-clone.h"
#include "html.h"
#include "ui-shared.h"
+#include "packfile.h"
static int print_ref_info(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct object *obj;
- if (!(obj = parse_object(oid->hash)))
+ if (!(obj = parse_object(oid)))
return 0;
htmlf("%s\t%s\n", oid_to_hex(oid), refname);
diff --git a/ui-commit.c b/ui-commit.c
index db69d54..abf58f6 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -31,7 +31,7 @@ void cgit_print_commit(char *hex, const char *prefix)
"Bad object id: %s", hex);
return;
}
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (!commit) {
cgit_print_error_page(404, "Not found",
"Bad commit reference: %s", hex);
@@ -39,9 +39,9 @@ void cgit_print_commit(char *hex, const char *prefix)
}
info = cgit_parse_commit(commit);
- format_display_notes(oid.hash, &notes, PAGE_ENCODING, 0);
+ format_display_notes(&oid, &notes, PAGE_ENCODING, 0);
- load_ref_decorations(DECORATE_FULL_REFS);
+ load_ref_decorations(NULL, DECORATE_FULL_REFS);
cgit_print_layout_start();
cgit_print_diff_ctrls();
@@ -87,7 +87,7 @@ void cgit_print_commit(char *hex, const char *prefix)
free(tmp);
html("</td></tr>\n");
for (p = commit->parents; p; p = p->next) {
- parent = lookup_commit_reference(p->item->object.oid.hash);
+ parent = lookup_commit_reference(&p->item->object.oid);
if (!parent) {
html("<tr><td colspan='3'>");
cgit_print_error("Error reading parent commit");
diff --git a/ui-diff.c b/ui-diff.c
index 173d351..a10ce8a 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -385,7 +385,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
const char *prefix, int show_ctrls, int raw)
{
struct commit *commit, *commit2;
- const unsigned char *old_tree_sha1, *new_tree_sha1;
+ const struct object_id *old_tree_oid, *new_tree_oid;
diff_type difftype;
/*
@@ -407,13 +407,13 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
"Bad object name: %s", new_rev);
return;
}
- commit = lookup_commit_reference(new_rev_oid->hash);
+ commit = lookup_commit_reference(new_rev_oid);
if (!commit || parse_commit(commit)) {
cgit_print_error_page(404, "Not found",
"Bad commit: %s", oid_to_hex(new_rev_oid));
return;
}
- new_tree_sha1 = commit->tree->object.oid.hash;
+ new_tree_oid = &commit->tree->object.oid;
if (old_rev) {
if (get_oid(old_rev, old_rev_oid)) {
@@ -428,15 +428,15 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
}
if (!is_null_oid(old_rev_oid)) {
- commit2 = lookup_commit_reference(old_rev_oid->hash);
+ commit2 = lookup_commit_reference(old_rev_oid);
if (!commit2 || parse_commit(commit2)) {
cgit_print_error_page(404, "Not found",
"Bad commit: %s", oid_to_hex(old_rev_oid));
return;
}
- old_tree_sha1 = commit2->tree->object.oid.hash;
+ old_tree_oid = &commit2->tree->object.oid;
} else {
- old_tree_sha1 = NULL;
+ old_tree_oid = NULL;
}
if (raw) {
@@ -444,16 +444,16 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
diff_setup(&diffopt);
diffopt.output_format = DIFF_FORMAT_PATCH;
- DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diffopt.flags.recursive = 1;
diff_setup_done(&diffopt);
ctx.page.mimetype = "text/plain";
cgit_print_http_headers();
- if (old_tree_sha1) {
- diff_tree_sha1(old_tree_sha1, new_tree_sha1, "",
+ if (old_tree_oid) {
+ diff_tree_oid(old_tree_oid, new_tree_oid, "",
&diffopt);
} else {
- diff_root_tree_sha1(new_tree_sha1, "", &diffopt);
+ diff_root_tree_oid(new_tree_oid, "", &diffopt);
}
diffcore_std(&diffopt);
diff_flush(&diffopt);
diff --git a/ui-log.c b/ui-log.c
index 3220fd9..8e36fba 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -119,8 +119,7 @@ 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;
- unsigned saved_flags = revs->diffopt.flags;
-
+ 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)
@@ -149,10 +148,10 @@ static int show_commit(struct commit *commit, struct rev_info *revs)
add_lines = 0;
rem_lines = 0;
- DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
- diff_tree_sha1(parent->tree->object.oid.hash,
- commit->tree->object.oid.hash,
- "", &revs->diffopt);
+ revs->diffopt.flags.recursive = 1;
+ diff_tree_oid(&parent->tree->object.oid,
+ &commit->tree->object.oid,
+ "", &revs->diffopt);
diffcore_std(&revs->diffopt);
found = !diff_queue_is_empty();
@@ -273,7 +272,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
strbuf_addstr(&msgbuf, info->msg);
strbuf_addch(&msgbuf, '\n');
}
- format_display_notes(commit->object.oid.hash,
+ format_display_notes(&commit->object.oid,
&msgbuf, PAGE_ENCODING, 0);
strbuf_addch(&msgbuf, '\n');
strbuf_ltrim(&msgbuf);
@@ -434,9 +433,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
rev.ignore_missing = 1;
rev.simplify_history = 1;
setup_revisions(rev_argv.argc, rev_argv.argv, &rev, NULL);
- load_ref_decorations(DECORATE_FULL_REFS);
+ load_ref_decorations(NULL, DECORATE_FULL_REFS);
rev.show_decorations = 1;
- rev.grep_filter.regflags |= REG_ICASE;
+ rev.grep_filter.ignore_case = 1;
rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = ctx.cfg.renamelimit;
diff --git a/ui-patch.c b/ui-patch.c
index 047e2f9..8007a11 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -33,7 +33,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
"Bad object id: %s", new_rev);
return;
}
- commit = lookup_commit_reference(new_rev_oid.hash);
+ commit = lookup_commit_reference(&new_rev_oid);
if (!commit) {
cgit_print_error_page(404, "Not found",
"Bad commit reference: %s", new_rev);
@@ -46,7 +46,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
"Bad object id: %s", old_rev);
return;
}
- if (!lookup_commit_reference(old_rev_oid.hash)) {
+ if (!lookup_commit_reference(&old_rev_oid)) {
cgit_print_error_page(404, "Not found",
"Bad commit reference: %s", old_rev);
return;
@@ -92,6 +92,4 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
log_tree_commit(&rev, commit);
printf("-- \ncgit %s\n\n", cgit_version);
}
-
- fflush(stdout);
}
diff --git a/ui-plain.c b/ui-plain.c
index 8d541e3..cfdbf73 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -135,7 +135,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base,
struct walk_tree_context *walk_tree_ctx = cbdata;
if (base->len == walk_tree_ctx->match_baselen) {
- if (S_ISREG(mode)) {
+ if (S_ISREG(mode) || S_ISLNK(mode)) {
if (print_object(sha1, pathname))
walk_tree_ctx->match = 1;
} else if (S_ISDIR(mode)) {
@@ -185,7 +185,7 @@ void cgit_print_plain(void)
cgit_print_error_page(404, "Not found", "Not found");
return;
}
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (!commit || parse_commit(commit)) {
cgit_print_error_page(404, "Not found", "Not found");
return;
diff --git a/ui-repolist.c b/ui-repolist.c
index b57ea60..af52f9b 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -184,27 +184,6 @@ static int cmp(const char *s1, const char *s2)
return 0;
}
-static int sort_section(const void *a, const void *b)
-{
- const struct cgit_repo *r1 = a;
- const struct cgit_repo *r2 = b;
- int result;
- time_t t;
-
- result = cmp(r1->section, r2->section);
- if (!result) {
- if (!strcmp(ctx.cfg.repository_sort, "age")) {
- // get_repo_modtime caches the value in r->mtime, so we don't
- // have to worry about inefficiencies here.
- if (get_repo_modtime(r1, &t) && get_repo_modtime(r2, &t))
- result = r2->mtime - r1->mtime;
- }
- if (!result)
- result = cmp(r1->name, r2->name);
- }
- return result;
-}
-
static int sort_name(const void *a, const void *b)
{
const struct cgit_repo *r1 = a;
@@ -241,6 +220,22 @@ static int sort_idle(const void *a, const void *b)
return t2 - t1;
}
+static int sort_section(const void *a, const void *b)
+{
+ const struct cgit_repo *r1 = a;
+ const struct cgit_repo *r2 = b;
+ int result;
+
+ result = cmp(r1->section, r2->section);
+ if (!result) {
+ if (!strcmp(ctx.cfg.repository_sort, "age"))
+ result = sort_idle(r1, r2);
+ if (!result)
+ result = cmp(r1->name, r2->name);
+ }
+ return result;
+}
+
struct sortcolumn {
const char *name;
int (*fn)(const void *a, const void *b);
@@ -334,7 +329,8 @@ void cgit_print_repolist(void)
repourl = cgit_repourl(ctx.repo->url);
html_link_open(repourl, NULL, NULL);
free(repourl);
- html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
+ if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
+ html("...");
html_link_close();
html("</td><td>");
if (ctx.cfg.enable_index_owner) {
diff --git a/ui-shared.c b/ui-shared.c
index 2e4fcd9..9d8f66b 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,6 +1,6 @@
/* ui-shared.c: common web output functions
*
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -304,6 +304,12 @@ void cgit_plain_link(const char *name, const char *title, const char *class,
reporevlink("plain", name, title, class, head, rev, path);
}
+void cgit_blame_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("blame", name, title, class, head, rev, path);
+}
+
void cgit_log_link(const char *name, const char *title, const char *class,
const char *head, const char *rev, const char *path,
int ofs, const char *grep, const char *pattern, int showmsg,
@@ -347,18 +353,11 @@ void cgit_log_link(const char *name, const char *title, const char *class,
html("</a>");
}
-void cgit_commit_link(char *name, const char *title, const char *class,
+void cgit_commit_link(const char *name, const char *title, const char *class,
const char *head, const char *rev, const char *path)
{
char *delim;
- if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
- name[ctx.cfg.max_msg_len] = '\0';
- name[ctx.cfg.max_msg_len - 1] = '.';
- name[ctx.cfg.max_msg_len - 2] = '.';
- name[ctx.cfg.max_msg_len - 3] = '.';
- }
-
delim = repolink(title, class, "commit", head, path);
if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) {
html(delim);
@@ -387,9 +386,13 @@ void cgit_commit_link(char *name, const char *title, const char *class,
html("follow=1");
}
html("'>");
- if (name[0] != '\0')
- html_txt(name);
- else
+ if (name[0] != '\0') {
+ if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
+ html_ntxt(name, ctx.cfg.max_msg_len - 3);
+ html("...");
+ } else
+ html_txt(name);
+ } else
html_txt("(no commit message)");
html("</a>");
}
@@ -481,6 +484,10 @@ static void cgit_self_link(char *name, const char *title, const char *class)
cgit_plain_link(name, title, class, ctx.qry.head,
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
ctx.qry.path);
+ else if (!strcmp(ctx.qry.page, "blame"))
+ cgit_blame_link(name, title, class, ctx.qry.head,
+ ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
+ ctx.qry.path);
else if (!strcmp(ctx.qry.page, "log"))
cgit_log_link(name, title, class, ctx.qry.head,
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
@@ -986,8 +993,12 @@ void cgit_print_pageheader(void)
cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
NULL, ctx.qry.vpath, 0, NULL, NULL,
ctx.qry.showmsg, ctx.qry.follow);
- cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
- ctx.qry.sha1, ctx.qry.vpath);
+ if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
+ cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
+ ctx.qry.sha1, ctx.qry.vpath);
+ else
+ cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
+ ctx.qry.sha1, ctx.qry.vpath);
cgit_commit_link("commit", NULL, hc("commit"),
ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath);
cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
@@ -1016,7 +1027,7 @@ void cgit_print_pageheader(void)
html_option("committer", "committer", ctx.qry.grep);
html_option("range", "range", ctx.qry.grep);
html("</select>\n");
- html("<input class='txt' type='text' size='10' name='q' value='");
+ html("<input class='txt' type='search' size='10' name='q' value='");
html_attr(ctx.qry.search);
html("'/>\n");
html("<input type='submit' value='search'/>\n");
@@ -1031,7 +1042,7 @@ void cgit_print_pageheader(void)
html("<form method='get' action='");
html_attr(currenturl);
html("'>\n");
- html("<input type='text' name='q' size='10' value='");
+ html("<input type='search' name='q' size='10' value='");
html_attr(ctx.qry.search);
html("'/>\n");
html("<input type='submit' value='search'/>\n");
@@ -1039,7 +1050,7 @@ void cgit_print_pageheader(void)
free(currenturl);
}
html("</td></tr></table>\n");
- if (ctx.env.authenticated && ctx.qry.vpath) {
+ if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) {
html("<div class='path'>");
html("path: ");
cgit_print_path_crumbs(ctx.qry.vpath);
@@ -1111,3 +1122,34 @@ void cgit_print_snapshot_links(const char *repo, const char *head,
}
strbuf_release(&filename);
}
+
+void cgit_set_title_from_path(const char *path)
+{
+ size_t path_len, path_index, path_last_end;
+ char *new_title;
+
+ if (!path)
+ return;
+
+ path_len = strlen(path);
+ new_title = xmalloc(path_len + 3 + strlen(ctx.page.title) + 1);
+ new_title[0] = '\0';
+
+ for (path_index = path_len, path_last_end = path_len; path_index-- > 0;) {
+ if (path[path_index] == '/') {
+ if (path_index == path_len - 1) {
+ path_last_end = path_index - 1;
+ continue;
+ }
+ strncat(new_title, &path[path_index + 1], path_last_end - path_index - 1);
+ strcat(new_title, "\\");
+ path_last_end = path_index;
+ }
+ }
+ if (path_last_end)
+ strncat(new_title, path, path_last_end);
+
+ strcat(new_title, " - ");
+ strcat(new_title, ctx.page.title);
+ ctx.page.title = new_title;
+}
diff --git a/ui-shared.h b/ui-shared.h
index 87799f1..b760a17 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -26,11 +26,14 @@ extern void cgit_tree_link(const char *name, const char *title,
extern void cgit_plain_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
+extern void cgit_blame_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
extern void cgit_log_link(const char *name, const char *title,
const char *class, const char *head, const char *rev,
const char *path, int ofs, const char *grep,
const char *pattern, int showmsg, int follow);
-extern void cgit_commit_link(char *name, const char *title,
+extern void cgit_commit_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
extern void cgit_patch_link(const char *name, const char *title,
@@ -77,4 +80,6 @@ extern void cgit_print_snapshot_links(const char *repo, const char *head,
const char *hex, int snapshots);
extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
const char *page);
+
+extern void cgit_set_title_from_path(const char *path);
#endif /* UI_SHARED_H */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 08c6e80..b2d95f7 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -37,7 +37,7 @@ static int write_archive_type(const char *format, const char *hex, const char *p
/* argv_array guarantees a trailing NULL entry. */
memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1));
- result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0);
+ result = write_archive(argv.argc, nargv, NULL, NULL, 0);
argv_array_clear(&argv);
free(nargv);
return result;
@@ -116,7 +116,7 @@ static int make_snapshot(const struct cgit_snapshot_format *format,
"Bad object id: %s", hex);
return 1;
}
- if (!lookup_commit_reference(oid.hash)) {
+ if (!lookup_commit_reference(&oid)) {
cgit_print_error_page(400, "Bad request",
"Not a commit reference: %s", hex);
return 1;
diff --git a/ui-tag.c b/ui-tag.c
index afd7d61..909cde0 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -54,7 +54,7 @@ void cgit_print_tag(char *revname)
"Bad tag reference: %s", revname);
goto cleanup;
}
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
if (!obj) {
cgit_print_error_page(500, "Internal server error",
"Bad object id: %s", oid_to_hex(&oid));
@@ -64,7 +64,7 @@ void cgit_print_tag(char *revname)
struct tag *tag;
struct taginfo *info;
- tag = lookup_tag(oid.hash);
+ tag = lookup_tag(&oid);
if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
cgit_print_error_page(500, "Internal server error",
"Bad tag object: %s", revname);
diff --git a/ui-tree.c b/ui-tree.c
index b310242..67fd1bc 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,6 +1,6 @@
/* ui-tree.c: functions for tree output
*
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -84,37 +84,6 @@ static void print_binary_buffer(char *buf, unsigned long size)
html("</table>\n");
}
-static void set_title_from_path(const char *path)
-{
- size_t path_len, path_index, path_last_end;
- char *new_title;
-
- if (!path)
- return;
-
- path_len = strlen(path);
- new_title = xmalloc(path_len + 3 + strlen(ctx.page.title) + 1);
- new_title[0] = '\0';
-
- for (path_index = path_len, path_last_end = path_len; path_index-- > 0;) {
- if (path[path_index] == '/') {
- if (path_index == path_len - 1) {
- path_last_end = path_index - 1;
- continue;
- }
- strncat(new_title, &path[path_index + 1], path_last_end - path_index - 1);
- strcat(new_title, "\\");
- path_last_end = path_index;
- }
- }
- if (path_last_end)
- strncat(new_title, path, path_last_end);
-
- strcat(new_title, " - ");
- strcat(new_title, ctx.page.title);
- ctx.page.title = new_title;
-}
-
static void print_object(const unsigned char *sha1, char *path, const char *basename, const char *rev)
{
enum object_type type;
@@ -135,12 +104,17 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
return;
}
- set_title_from_path(path);
+ cgit_set_title_from_path(path);
cgit_print_layout_start();
htmlf("blob: %s (", sha1_to_hex(sha1));
cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
rev, path);
+ if (ctx.cfg.enable_blame) {
+ html(") (");
+ cgit_blame_link("blame", NULL, NULL, ctx.qry.head,
+ rev, path);
+ }
html(")\n");
if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
@@ -157,7 +131,7 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
struct single_tree_ctx {
struct strbuf *path;
- unsigned char sha1[GIT_SHA1_RAWSZ];
+ struct object_id oid;
char *name;
size_t count;
};
@@ -177,7 +151,7 @@ static int single_tree_cb(const unsigned char *sha1, struct strbuf *base,
}
ctx->name = xstrdup(pathname);
- hashcpy(ctx->sha1, sha1);
+ hashcpy(ctx->oid.hash, sha1);
strbuf_addf(ctx->path, "/%s", pathname);
return 0;
}
@@ -195,13 +169,13 @@ static void write_tree_link(const unsigned char *sha1, char *name,
.nr = 0
};
- hashcpy(tree_ctx.sha1, sha1);
+ hashcpy(tree_ctx.oid.hash, sha1);
while (tree_ctx.count == 1) {
cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev,
fullpath->buf);
- tree = lookup_tree(tree_ctx.sha1);
+ tree = lookup_tree(&tree_ctx.oid);
if (!tree)
return;
@@ -275,6 +249,9 @@ static int ls_item(const unsigned char *sha1, struct strbuf *base,
if (!S_ISGITLINK(mode))
cgit_plain_link("plain", NULL, "button", ctx.qry.head,
walk_tree_ctx->curr_rev, fullpath.buf);
+ if (!S_ISDIR(mode) && ctx.cfg.enable_blame)
+ cgit_blame_link("blame", NULL, "button", ctx.qry.head,
+ walk_tree_ctx->curr_rev, fullpath.buf);
html("</td></tr>\n");
free(name);
strbuf_release(&fullpath);
@@ -300,17 +277,17 @@ static void ls_tail(void)
cgit_print_layout_end();
}
-static void ls_tree(const unsigned char *sha1, char *path, struct walk_tree_context *walk_tree_ctx)
+static void ls_tree(const struct object_id *oid, char *path, struct walk_tree_context *walk_tree_ctx)
{
struct tree *tree;
struct pathspec paths = {
.nr = 0
};
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(oid);
if (!tree) {
cgit_print_error_page(404, "Not found",
- "Not a tree object: %s", sha1_to_hex(sha1));
+ "Not a tree object: %s", sha1_to_hex(oid->hash));
return;
}
@@ -335,7 +312,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base,
if (S_ISDIR(mode)) {
walk_tree_ctx->state = 1;
- set_title_from_path(buffer.buf);
+ cgit_set_title_from_path(buffer.buf);
strbuf_release(&buffer);
ls_head();
return READ_TREE_RECURSIVE;
@@ -380,7 +357,7 @@ void cgit_print_tree(const char *rev, char *path)
"Invalid revision name: %s", rev);
return;
}
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (!commit || parse_commit(commit)) {
cgit_print_error_page(404, "Not found",
"Invalid commit reference: %s", rev);
@@ -390,7 +367,7 @@ void cgit_print_tree(const char *rev, char *path)
walk_tree_ctx.curr_rev = xstrdup(rev);
if (path == NULL) {
- ls_tree(commit->tree->object.oid.hash, NULL, &walk_tree_ctx);
+ ls_tree(&commit->tree->object.oid, NULL, &walk_tree_ctx);
goto cleanup;
}