Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
dfce67570f | |||
cca01672a2 | |||
2460441ce7 | |||
e1e3d501ce | |||
b486a93638 | |||
c5548f08e4 | |||
c262004043 | |||
4a7a591fe4 | |||
e06adbbeb9 | |||
ebead97801 | |||
615420c062 | |||
7ec4477c9a | |||
3dd92de14e | |||
b851c7b269 | |||
90b658f767 | |||
bc551c0d18 | |||
8f023f452d | |||
836f4cd06d | |||
4358943af4 | |||
91ce363f0e | |||
7f5688dc7e | |||
6fbf6cb4c7 | |||
b9891a9f17 | |||
afd1d440d6 | |||
d8ae2c42e6 | |||
f39308f3c8 | |||
4b05ac8a56 | |||
e4b108139e | |||
873e51f67b | |||
5776aebb97 | |||
e750dd63b0 | |||
|
994d3fe1a8 | ||
|
ce4a66b9d7 | ||
|
be06bedb03 | ||
|
99a2f47c96 | ||
|
c1733e28d9 | ||
|
1cb701a9b6 | ||
|
87fdb57d60 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,3 +10,6 @@ cgitrc.5.pdf
|
|||||||
cgitrc.5.xml
|
cgitrc.5.xml
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
|
|
||||||
|
/themed/themed.c
|
||||||
|
/themed/themed.css
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "git"]
|
[submodule "git"]
|
||||||
url = https://git.kernel.org/pub/scm/git/git.git
|
url = https://git.kernel.org/pub/scm/git/git.git
|
||||||
path = git
|
path = git
|
||||||
|
[submodule "themed/htmlcc"]
|
||||||
|
path = themed/htmlcc
|
||||||
|
url = https://yingtongli.me/git/htmlcc.git
|
||||||
|
5
Makefile
5
Makefile
@ -14,7 +14,7 @@ htmldir = $(docdir)
|
|||||||
pdfdir = $(docdir)
|
pdfdir = $(docdir)
|
||||||
mandir = $(prefix)/share/man
|
mandir = $(prefix)/share/man
|
||||||
SHA1_HEADER = <openssl/sha.h>
|
SHA1_HEADER = <openssl/sha.h>
|
||||||
GIT_VER = 2.46.0
|
GIT_VER = 2.49.0
|
||||||
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz
|
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz
|
||||||
INSTALL = install
|
INSTALL = install
|
||||||
COPYTREE = cp -r
|
COPYTREE = cp -r
|
||||||
@ -73,7 +73,7 @@ endif
|
|||||||
all:: cgit
|
all:: cgit
|
||||||
|
|
||||||
cgit:
|
cgit:
|
||||||
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1
|
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit ../themed/themed.css $(EXTRA_GIT_TARGETS) NO_CURL=1
|
||||||
|
|
||||||
sparse:
|
sparse:
|
||||||
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
|
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
|
||||||
@ -89,6 +89,7 @@ install: all
|
|||||||
$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
|
$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
|
||||||
$(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
|
$(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
|
||||||
$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
|
$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
|
||||||
|
$(INSTALL) -m 0644 themed/themed.css $(DESTDIR)$(CGIT_DATA_PATH)/themed.css
|
||||||
$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
|
$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
|
||||||
$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
|
$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
|
||||||
$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
|
$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
|
||||||
|
99
archlinux/PKGBUILD
Normal file
99
archlinux/PKGBUILD
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Maintainer: Lee Yingtong Li <runassudo@yingtongli.me>
|
||||||
|
|
||||||
|
# Adapted from PKGBUILD for cgit:
|
||||||
|
# Maintainer: Christian Hesse <mail@eworm.de>
|
||||||
|
# Contributor: Lukas Fleischer <lfleischer@archlinux.org>
|
||||||
|
# Contributor: Sergej Pupykin <pupykin.s+arch@gmail.com>
|
||||||
|
# Contributor: Patrick Palka <patrick@parcs.ath.cx>
|
||||||
|
# Contributor: Loui Chang <louipc.ist at gmail com>
|
||||||
|
# Contributor: Andreas Baumann <abaumann at yahoo dot com>
|
||||||
|
|
||||||
|
pkgname=cgit-yli-theme-git
|
||||||
|
pkgver=1.2.3.r99.gebead97
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc='A web interface for git written in plain C'
|
||||||
|
arch=('x86_64')
|
||||||
|
url='https://yingtongli.me/git/cgit-yli-theme'
|
||||||
|
license=('GPL-2.0-only')
|
||||||
|
depends=('openssl' 'luajit' 'zlib-ng')
|
||||||
|
makedepends=('git' 'curl' 'asciidoc' 'tailwindcss')
|
||||||
|
optdepends=('groff: about page using man page syntax'
|
||||||
|
'python-pygments: syntax highlighting support'
|
||||||
|
'python-docutils: about page formatted with reStructuredText'
|
||||||
|
'python-markdown: about page formatted with markdown'
|
||||||
|
'lua51-luaossl: for lua filters'
|
||||||
|
'gzip: gzip compressed snapshots'
|
||||||
|
'bzip2: bzip2 compressed snapshots'
|
||||||
|
'lzip: lzip compressed snapshots'
|
||||||
|
'xz: xz compressed snapshots'
|
||||||
|
'zstd: zstd compressed snapshots'
|
||||||
|
'mime-types: serve file with correct content-type header')
|
||||||
|
provides=('cgit')
|
||||||
|
conflicts=('cgit')
|
||||||
|
install=cgit-yli-theme-git.install
|
||||||
|
validpgpkeys=('AB9942E6D4A4CFC3412620A749FC7012A5DE03AE')
|
||||||
|
source=("git+https://yingtongli.me/git/cgit-yli-theme.git"
|
||||||
|
'git+https://yingtongli.me/git/htmlcc.git'
|
||||||
|
'git+https://github.com/git/git.git'
|
||||||
|
'tmpfiles.conf'
|
||||||
|
'apache.example.conf')
|
||||||
|
sha256sums=('SKIP'
|
||||||
|
'SKIP'
|
||||||
|
'SKIP'
|
||||||
|
'4004b72d433e5810b046fc6019171a11a0dae3e9c6b29a44f16ed41705c46c3d'
|
||||||
|
'89927d462c0504863c163eb8a210e5d65db30ee6e4300ff6a2f92460e4f20a62')
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
cd "$srcdir/${pkgname%-git}"
|
||||||
|
|
||||||
|
GITTAG="$(git describe --abbrev=0 --tags 2>/dev/null)"
|
||||||
|
printf '%s.r%s.g%s' \
|
||||||
|
"$(sed -e "s/^${pkgname%%-git}//" -e 's/^[-_/a-zA-Z]\+//' -e 's/[-_+]/./g' <<< ${GITTAG})" \
|
||||||
|
"$(git rev-list --count ${GITTAG}..)" \
|
||||||
|
"$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "$srcdir/${pkgname%-git}"
|
||||||
|
|
||||||
|
git config --file=.gitmodules submodule.git.url ../git/
|
||||||
|
git config --file=.gitmodules submodule.htmlcc.url ../htmlcc/
|
||||||
|
git submodule init
|
||||||
|
git -c protocol.file.allow=always submodule update
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$srcdir/${pkgname%-git}"
|
||||||
|
|
||||||
|
make \
|
||||||
|
ZLIB_NG=1 \
|
||||||
|
CGIT_SCRIPT_PATH="/usr/share/webapps/cgit" \
|
||||||
|
DESTDIR="${pkgdir}" \
|
||||||
|
prefix='/usr' \
|
||||||
|
filterdir="/usr/lib/cgit/filters" \
|
||||||
|
all doc-man
|
||||||
|
}
|
||||||
|
|
||||||
|
#check() {
|
||||||
|
# cd "$srcdir/${pkgname%-git}"
|
||||||
|
#
|
||||||
|
# make test
|
||||||
|
#}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$srcdir/${pkgname%-git}"
|
||||||
|
|
||||||
|
make \
|
||||||
|
ZLIB_NG=1 \
|
||||||
|
CGIT_SCRIPT_PATH="/usr/share/webapps/cgit" \
|
||||||
|
DESTDIR="${pkgdir}" \
|
||||||
|
prefix='/usr' \
|
||||||
|
filterdir="/usr/lib/cgit/filters" \
|
||||||
|
install install-man
|
||||||
|
|
||||||
|
install -vDm0644 "${srcdir}/tmpfiles.conf" "${pkgdir}/usr/lib/tmpfiles.d/cgit.conf"
|
||||||
|
install -vDm0644 "${srcdir}/apache.example.conf" "${pkgdir}/etc/webapps/cgit/apache.example.conf"
|
||||||
|
install -d "${pkgdir}/usr/lib/cgit"
|
||||||
|
mv "${pkgdir}/usr/share/webapps/cgit/cgit.cgi" "${pkgdir}/usr/lib/cgit"
|
||||||
|
ln -sf ../../../lib/cgit/cgit.cgi "${pkgdir}/usr/share/webapps/cgit/cgit.cgi"
|
||||||
|
}
|
23
archlinux/apache.example.conf
Normal file
23
archlinux/apache.example.conf
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
ScriptAlias /cgit/ "/usr/lib/cgit/cgit.cgi/"
|
||||||
|
|
||||||
|
Alias /cgit-css "/usr/share/webapps/cgit/"
|
||||||
|
|
||||||
|
<Directory "/usr/share/webapps/cgit/">
|
||||||
|
AllowOverride None
|
||||||
|
Options None
|
||||||
|
Require all granted
|
||||||
|
AuthType Basic
|
||||||
|
AuthName cgit
|
||||||
|
AuthUserFile /etc/httpd/conf/passwd
|
||||||
|
Require user root
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Directory "/usr/lib/cgit/">
|
||||||
|
AllowOverride None
|
||||||
|
Options ExecCGI FollowSymlinks
|
||||||
|
Require all granted
|
||||||
|
AuthType Basic
|
||||||
|
AuthName cgit
|
||||||
|
AuthUserFile /etc/httpd/conf/passwd
|
||||||
|
Require user root
|
||||||
|
</Directory>
|
6
archlinux/cgit-yli-theme-git.install
Normal file
6
archlinux/cgit-yli-theme-git.install
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
post_install() {
|
||||||
|
echo "==> cgit looks at /etc/cgitrc for configuration. There is"
|
||||||
|
echo "==> an example configuration file in the cgitrc(5) man page."
|
||||||
|
}
|
1
archlinux/tmpfiles.conf
Normal file
1
archlinux/tmpfiles.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
d /var/cache/cgit 0750 http http -
|
6
cgit.c
6
cgit.c
@ -145,6 +145,8 @@ static void config_cb(const char *name, const char *value)
|
|||||||
ctx.cfg.root_readme = xstrdup(value);
|
ctx.cfg.root_readme = xstrdup(value);
|
||||||
else if (!strcmp(name, "css"))
|
else if (!strcmp(name, "css"))
|
||||||
string_list_append(&ctx.cfg.css, xstrdup(value));
|
string_list_append(&ctx.cfg.css, xstrdup(value));
|
||||||
|
else if (!strcmp(name, "css-themed"))
|
||||||
|
string_list_append(&ctx.cfg.css_themed, xstrdup(value));
|
||||||
else if (!strcmp(name, "js"))
|
else if (!strcmp(name, "js"))
|
||||||
string_list_append(&ctx.cfg.js, xstrdup(value));
|
string_list_append(&ctx.cfg.js, xstrdup(value));
|
||||||
else if (!strcmp(name, "favicon"))
|
else if (!strcmp(name, "favicon"))
|
||||||
@ -448,8 +450,8 @@ struct refmatch {
|
|||||||
int match;
|
int match;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int find_current_ref(const char *refname, const struct object_id *oid,
|
static int find_current_ref(const char *refname, const char *referent UNUSED,
|
||||||
int flags, void *cb_data)
|
const struct object_id *oid, int flags, void *cb_data)
|
||||||
{
|
{
|
||||||
struct refmatch *info;
|
struct refmatch *info;
|
||||||
|
|
||||||
|
5
cgit.h
5
cgit.h
@ -211,6 +211,7 @@ struct cgit_config {
|
|||||||
char *project_list;
|
char *project_list;
|
||||||
struct string_list readme;
|
struct string_list readme;
|
||||||
struct string_list css;
|
struct string_list css;
|
||||||
|
struct string_list css_themed;
|
||||||
char *robots;
|
char *robots;
|
||||||
char *root_title;
|
char *root_title;
|
||||||
char *root_desc;
|
char *root_desc;
|
||||||
@ -346,8 +347,8 @@ extern void strbuf_ensure_end(struct strbuf *sb, char c);
|
|||||||
|
|
||||||
extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
|
extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
|
||||||
extern void cgit_free_reflist_inner(struct reflist *list);
|
extern void cgit_free_reflist_inner(struct reflist *list);
|
||||||
extern int cgit_refs_cb(const char *refname, const struct object_id *oid,
|
extern int cgit_refs_cb(const char *refname, const char *referent UNUSED,
|
||||||
int flags, void *cb_data);
|
const struct object_id *oid, int flags, void *cb_data);
|
||||||
|
|
||||||
extern void cgit_free_commitinfo(struct commitinfo *info);
|
extern void cgit_free_commitinfo(struct commitinfo *info);
|
||||||
extern void cgit_free_taginfo(struct taginfo *info);
|
extern void cgit_free_taginfo(struct taginfo *info);
|
||||||
|
26
cgit.mk
26
cgit.mk
@ -95,6 +95,19 @@ CGIT_OBJ_NAMES += ui-summary.o
|
|||||||
CGIT_OBJ_NAMES += ui-tag.o
|
CGIT_OBJ_NAMES += ui-tag.o
|
||||||
CGIT_OBJ_NAMES += ui-tree.o
|
CGIT_OBJ_NAMES += ui-tree.o
|
||||||
|
|
||||||
|
CGIT_OBJ_NAMES += themed/themed.o
|
||||||
|
CGIT_OBJ_NAMES += themed/mincrypt_sha256.o
|
||||||
|
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/base.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/tree_common.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/index.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/commit.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/diff.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/log.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/refs.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/summary.html
|
||||||
|
CGIT_THEMED_INPUTS += $(CGIT_PREFIX)themed/tree.html
|
||||||
|
|
||||||
CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
|
CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
|
||||||
|
|
||||||
# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
|
# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
|
||||||
@ -116,9 +129,22 @@ ifeq ($(wildcard $(CGIT_PREFIX).depend),)
|
|||||||
missing_dep_dirs += $(CGIT_PREFIX).depend
|
missing_dep_dirs += $(CGIT_PREFIX).depend
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(wildcard $(CGIT_PREFIX)themed/.depend),)
|
||||||
|
missing_dep_dirs += $(CGIT_PREFIX)themed/.depend
|
||||||
|
endif
|
||||||
|
|
||||||
$(CGIT_PREFIX).depend:
|
$(CGIT_PREFIX).depend:
|
||||||
@mkdir -p $@
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(CGIT_PREFIX)themed/.depend:
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(CGIT_PREFIX)themed/themed.c: $(CGIT_THEMED_INPUTS)
|
||||||
|
cd $(CGIT_PREFIX)themed; PYTHONPATH=htmlcc python -m htmlcc --emitter cgit $^ > $@
|
||||||
|
|
||||||
|
$(CGIT_PREFIX)themed/themed.css: $(CGIT_PREFIX)themed/themed.in.css $(CGIT_THEMED_INPUTS)
|
||||||
|
cd $(CGIT_PREFIX)themed; tailwindcss -i $< -o $@
|
||||||
|
|
||||||
$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
|
$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
|
||||||
@FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \
|
@FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \
|
||||||
if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \
|
if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \
|
||||||
|
18
cmd.c
18
cmd.c
@ -79,12 +79,14 @@ static void blob_fn(void)
|
|||||||
|
|
||||||
static void commit_fn(void)
|
static void commit_fn(void)
|
||||||
{
|
{
|
||||||
cgit_print_commit(ctx.qry.oid, ctx.qry.path);
|
//cgit_print_commit(ctx.qry.oid, ctx.qry.path);
|
||||||
|
cgit_print_commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void diff_fn(void)
|
static void diff_fn(void)
|
||||||
{
|
{
|
||||||
cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0);
|
//cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0);
|
||||||
|
cgit_print_diff_page();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rawdiff_fn(void)
|
static void rawdiff_fn(void)
|
||||||
@ -99,10 +101,11 @@ static void info_fn(void)
|
|||||||
|
|
||||||
static void log_fn(void)
|
static void log_fn(void)
|
||||||
{
|
{
|
||||||
cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count,
|
//cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count,
|
||||||
ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
|
// ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
|
||||||
ctx.repo->enable_commit_graph,
|
// ctx.repo->enable_commit_graph,
|
||||||
ctx.repo->commit_sort);
|
// ctx.repo->commit_sort);
|
||||||
|
cgit_print_log();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ls_cache_fn(void)
|
static void ls_cache_fn(void)
|
||||||
@ -161,7 +164,8 @@ static void tag_fn(void)
|
|||||||
|
|
||||||
static void tree_fn(void)
|
static void tree_fn(void)
|
||||||
{
|
{
|
||||||
cgit_print_tree(ctx.qry.oid, ctx.qry.path);
|
//cgit_print_tree(ctx.qry.oid, ctx.qry.path);
|
||||||
|
cgit_print_tree();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define def_cmd(name, want_repo, want_vpath, is_clone) \
|
#define def_cmd(name, want_repo, want_vpath, is_clone) \
|
||||||
|
2
git
2
git
@ -1 +1 @@
|
|||||||
Subproject commit 39bf06adf96da25b87c9aa7d35a32ef3683eb4a4
|
Subproject commit 683c54c999c301c2cd6f715c411407c413b1d84e
|
4
shared.c
4
shared.c
@ -212,8 +212,8 @@ void cgit_free_reflist_inner(struct reflist *list)
|
|||||||
free(list->refs);
|
free(list->refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cgit_refs_cb(const char *refname, const struct object_id *oid, int flags,
|
int cgit_refs_cb(const char *refname, const char *referent UNUSED,
|
||||||
void *cb_data)
|
const struct object_id *oid, int flags, void *cb_data)
|
||||||
{
|
{
|
||||||
struct reflist *list = (struct reflist *)cb_data;
|
struct reflist *list = (struct reflist *)cb_data;
|
||||||
struct refinfo *info = cgit_mk_refinfo(refname, oid);
|
struct refinfo *info = cgit_mk_refinfo(refname, oid);
|
||||||
|
240
themed/base.html
Normal file
240
themed/base.html
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
{! static int get_num_commits(void); static int get_num_branches(void); static int get_num_tags(void); static void short_commit_id(char *hex); !}
|
||||||
|
|
||||||
|
{% block page_start %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{{ ctx.page.title }}</title>{# ctx.page.title is usually set by prepare_repo_cmd #}
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap">
|
||||||
|
{% if ctx.cfg.css_themed.items %}
|
||||||
|
{! for_each_string_list(&ctx.cfg.css_themed, cgit_shared_emit_css_link, NULL); !}
|
||||||
|
{% else %}
|
||||||
|
{! cgit_shared_emit_css_link(NULL, "/themed.css"); !}
|
||||||
|
{% endif %}
|
||||||
|
</head>
|
||||||
|
<body class="text-gray-900">
|
||||||
|
{% endblock %}
|
||||||
|
{% block repo_header %}
|
||||||
|
<header class="bg-gray-50 border-b border-gray-300">{# Repo header #}
|
||||||
|
<div class="max-w-[1280px] mx-auto py-4 flex gap-x-1">
|
||||||
|
{# Heroicons outline cube
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" /></svg>#}
|
||||||
|
<div class="text-lg"><a href="{{ cgit_rooturl()|attr }}" class="hover:text-blue-600 hover:underline">{{ ctx.cfg.root_title }}</a> / <a href="{! cgit_shared_repolink_url(NULL, NULL, NULL); !}" class="font-semibold hover:text-blue-600 hover:underline">{{ ctx.repo->name }}</a></div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
{% endblock %}
|
||||||
|
{% block repo_description_panel %}
|
||||||
|
<div class="mb-4">
|
||||||
|
{# Description panel #}
|
||||||
|
{{ ctx.repo->desc }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block repo_summary_bar_current_branch %}
|
||||||
|
<a href="{! cgit_shared_repolink_url("refs", ctx.qry.head, NULL); !}" class="flex gap-x-1.5 py-1.5 px-3 bg-gray-50 border border-gray-300 rounded-md hover:bg-gray-100">
|
||||||
|
{# Heroicons micro list-bullet #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path d="M3 4.75a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM6.25 3a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 7.25a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 11.5a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM4 12.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM3 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" /></svg>
|
||||||
|
<span class="font-semibold self-baseline">
|
||||||
|
{% if (strcmp(ctx.qry.page, "tree") == 0 || strcmp(ctx.qry.page, "summary") == 0) && ctx.qry.oid %}
|
||||||
|
{! short_commit_id(ctx.qry.oid); !}
|
||||||
|
{% else %}
|
||||||
|
{{ ctx.qry.head }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{# Heroicons micro chevron-down #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
||||||
|
{% block repo_summary_bar %}
|
||||||
|
<nav class="flex text-sm mb-4">
|
||||||
|
{# Repo navigation panel #}
|
||||||
|
{! repo_summary_bar_current_branch(); !}
|
||||||
|
<a href="{! cgit_shared_repolink_url("log", ctx.qry.head, NULL); !}" class="flex gap-x-1 py-1.5 px-3 ml-3 rounded-md hover:bg-gray-100">
|
||||||
|
{# Heroicons micro clock #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm7.75-4.25a.75.75 0 0 0-1.5 0V8c0 .414.336.75.75.75h3.25a.75.75 0 0 0 0-1.5h-2.5v-3.5Z" clip-rule="evenodd" /></svg>
|
||||||
|
{! int num_commits = get_num_commits(); !}
|
||||||
|
<span class="font-semibold">{{ num_commits|%d }}</span><span class="font-semibold text-gray-500">Commit{% if num_commits != 1 %}s{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
<a href="{! cgit_shared_repolink_url("refs", ctx.qry.head, "heads"); !}" class="flex gap-x-1 py-1.5 px-3 rounded-md hover:bg-gray-100">
|
||||||
|
{# Heroicons micro list-bullet #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path d="M3 4.75a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM6.25 3a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 7.25a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 11.5a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM4 12.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM3 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" /></svg>
|
||||||
|
{! int num_branches = get_num_branches(); !}
|
||||||
|
<span class="font-semibold">{{ num_branches|%d }}</span><span class="font-semibold text-gray-500">Branch{% if num_branches != 1 %}es{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
<a href="{! cgit_shared_repolink_url("refs", ctx.qry.head, "tags"); !}" class="flex gap-x-1 py-1.5 px-3 rounded-md hover:bg-gray-100">
|
||||||
|
{# Heroicons micro tag #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M4.5 2A2.5 2.5 0 0 0 2 4.5v2.879a2.5 2.5 0 0 0 .732 1.767l4.5 4.5a2.5 2.5 0 0 0 3.536 0l2.878-2.878a2.5 2.5 0 0 0 0-3.536l-4.5-4.5A2.5 2.5 0 0 0 7.38 2H4.5ZM5 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" /></svg>
|
||||||
|
{! int num_tags = get_num_tags(); !}
|
||||||
|
<span class="font-semibold">{{ num_tags|%d }}</span><span class="font-semibold text-gray-500">Tag{% if num_tags != 1 %}s{% endif %}</span>
|
||||||
|
</a>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<!--
|
||||||
|
<div class="flex outline-1 outline-gray-300 rounded-lg has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
|
||||||
|
{# Search box #}
|
||||||
|
<select class="py-1.5 px-2 text-sm text-gray-500 focus:outline-none">
|
||||||
|
<option>Log msg</option>
|
||||||
|
<option>Author</option>
|
||||||
|
<option>Committer</option>
|
||||||
|
<option>Range</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" placeholder="Search commits…" class="py-1.5 pl-2 text-sm focus:outline-none">
|
||||||
|
<button class="py-1.5 px-2 cursor-pointer">
|
||||||
|
{# Heroicons micro magnifying-glass #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="text-gray-700 size-4"><path fill-rule="evenodd" d="M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z" clip-rule="evenodd" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div class="flex relative">
|
||||||
|
{# Code box and panel #}
|
||||||
|
<button class="flex gap-x-1.5 py-1.5 px-3 ml-3 bg-blue-500 text-white rounded-md cursor-pointer hover:bg-blue-600 peer">
|
||||||
|
{# Code box #}
|
||||||
|
{# Heroicons micro code-bracket #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center"><path fill-rule="evenodd" d="M4.78 4.97a.75.75 0 0 1 0 1.06L2.81 8l1.97 1.97a.75.75 0 1 1-1.06 1.06l-2.5-2.5a.75.75 0 0 1 0-1.06l2.5-2.5a.75.75 0 0 1 1.06 0ZM11.22 4.97a.75.75 0 0 0 0 1.06L13.19 8l-1.97 1.97a.75.75 0 1 0 1.06 1.06l2.5-2.5a.75.75 0 0 0 0-1.06l-2.5-2.5a.75.75 0 0 0-1.06 0ZM8.856 2.008a.75.75 0 0 1 .636.848l-1.5 10.5a.75.75 0 0 1-1.484-.212l1.5-10.5a.75.75 0 0 1 .848-.636Z" clip-rule="evenodd" /></svg>
|
||||||
|
<span class="font-semibold">Code</span>
|
||||||
|
{# Heroicons micro chevron-down #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center"><path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
||||||
|
</button>
|
||||||
|
<div class="absolute top-10 right-0 w-[300px] border border-gray-300 rounded-md bg-white p-2 hidden peer-focus:block hover:block">
|
||||||
|
{# Code panel #}
|
||||||
|
<div class="flex gap-x-1 items-center mb-2">
|
||||||
|
{# Heroicons micro command-line #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path fill-rule="evenodd" d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm2.22 1.97a.75.75 0 0 0 0 1.06l.97.97-.97.97a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 0 0-1.06 0ZM8.75 8.5a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z" clip-rule="evenodd" /></svg>
|
||||||
|
<span class="font-semibold text-sm">Clone</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex outline-1 outline-gray-300 rounded-lg mb-2 has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
|
||||||
|
{# Clone URL box #}
|
||||||
|
<span class="py-1.5 px-2 text-sm text-gray-500 bg-gray-50 border-r border-gray-300">
|
||||||
|
HTTPS
|
||||||
|
</span>
|
||||||
|
<input type="text" value="{! cgit_add_clone_urls(html_attr); !}" class="repo-clone-url flex-1 py-1.5 pl-2 text-sm focus:outline-none" onfocus="this.select();" readonly>
|
||||||
|
<button class="py-1.5 px-2 cursor-pointer" onclick="document.querySelector('.repo-clone-url').select();document.execCommand('copy');">
|
||||||
|
{# Heroicons micro clipboard #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 text-gray-500"><path fill-rule="evenodd" d="M10.986 3H12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h1.014A2.25 2.25 0 0 1 7.25 1h1.5a2.25 2.25 0 0 1 2.236 2ZM9.5 4v-.75a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0-.75.75V4h3Z" clip-rule="evenodd" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<a href="vscode://vscode.git/clone?url={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline mb-2">
|
||||||
|
{# Heroicons micro arrow-top-right-on-square #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
|
||||||
|
Open with VS Code
|
||||||
|
</a>
|
||||||
|
<a href="vscodium://vscode.git/clone?url={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline mb-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
|
||||||
|
Open with VSCodium
|
||||||
|
</a>
|
||||||
|
<a href="jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
|
||||||
|
Open with IntelliJ IDEA
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
||||||
|
{% block page_end %}
|
||||||
|
<footer class="border-t border-gray-300">
|
||||||
|
{# Footer panel #}
|
||||||
|
<div class="max-w-[1280px] mx-auto py-4">
|
||||||
|
<div class="text-sm text-gray-500">
|
||||||
|
{# cgit footer #}
|
||||||
|
generated by
|
||||||
|
<a href="https://git.zx2c4.com/cgit/about/" class="hover:text-blue-600 hover:underline">cgit</a>
|
||||||
|
(<a href="https://yingtongli.me/git/cgit-yli-theme" class="hover:text-blue-600 hover:underline">cgit-yli-theme {{ cgit_version }}</a>,
|
||||||
|
<a href="https://git-scm.com/" class="hover:text-blue-600 hover:underline">git {{ git_version_string }}</a>)
|
||||||
|
at
|
||||||
|
{{ show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{!
|
||||||
|
static int get_num_commits(void) {
|
||||||
|
int num_commits = 0;
|
||||||
|
|
||||||
|
// Based on ui-stats collect_stats
|
||||||
|
struct rev_info rev;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
|
const char *argv[] = {NULL, ctx.qry.head};
|
||||||
|
int argc = 2;
|
||||||
|
|
||||||
|
repo_init_revisions(the_repository, &rev, NULL);
|
||||||
|
rev.abbrev = DEFAULT_ABBREV;
|
||||||
|
rev.commit_format = CMIT_FMT_DEFAULT;
|
||||||
|
rev.max_parents = 1;
|
||||||
|
rev.verbose_header = 1;
|
||||||
|
rev.show_root_diff = 0;
|
||||||
|
setup_revisions(argc, argv, &rev, NULL);
|
||||||
|
prepare_revision_walk(&rev);
|
||||||
|
while ((commit = get_revision(&rev)) != NULL) {
|
||||||
|
// Process the commit
|
||||||
|
num_commits++;
|
||||||
|
|
||||||
|
// This causes subsequent walks to return commits with index 0 - unsure why
|
||||||
|
// Checked with Valgrind - commenting this out seems not to leak memory
|
||||||
|
//release_commit_memory(the_repository->parsed_objects, commit);
|
||||||
|
//commit->parents = NULL;
|
||||||
|
}
|
||||||
|
reset_revision_walk();
|
||||||
|
release_revisions(&rev);
|
||||||
|
return num_commits;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_num_branches(void) {
|
||||||
|
struct reflist list;
|
||||||
|
list.refs = NULL;
|
||||||
|
list.alloc = list.count = 0;
|
||||||
|
refs_for_each_branch_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
|
||||||
|
int num_branches = list.count;
|
||||||
|
cgit_free_reflist_inner(&list);
|
||||||
|
return num_branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_num_tags(void) {
|
||||||
|
struct reflist list;
|
||||||
|
list.refs = NULL;
|
||||||
|
list.alloc = list.count = 0;
|
||||||
|
refs_for_each_tag_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
|
||||||
|
int num_tags = list.count;
|
||||||
|
cgit_free_reflist_inner(&list);
|
||||||
|
return num_tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void short_commit_id(char *hex) {
|
||||||
|
char short_commit_id[8];
|
||||||
|
memcpy(short_commit_id, hex, 7);
|
||||||
|
short_commit_id[7] = '\0';
|
||||||
|
html_txt(short_commit_id);
|
||||||
|
}
|
||||||
|
!}
|
||||||
|
|
||||||
|
{!
|
||||||
|
#include "mincrypt_sha256.h"
|
||||||
|
|
||||||
|
static void gravatar_url(char *email) {
|
||||||
|
// Trim email of whitespace, < and >
|
||||||
|
while (isspace(*email) || *email == '<') email++;
|
||||||
|
char* strend = email + strlen(email) - 1;
|
||||||
|
while (isspace(*strend) || *strend == '>') strend--; // Now strend points to the last character
|
||||||
|
|
||||||
|
// Email to lowercase
|
||||||
|
size_t email_len = strend - email + 1;
|
||||||
|
char *email_lower = malloc(email_len + 1); // +1 for null terminator
|
||||||
|
for (int i = 0; i < email_len; i++) { email_lower[i] = tolower(email[i]); }
|
||||||
|
email_lower[email_len] = '\0'; // For good measure
|
||||||
|
|
||||||
|
// Compute hash and print Gravatar link
|
||||||
|
uint8_t digest[32];
|
||||||
|
SHA256_hash(email, strend - email + 1, digest);
|
||||||
|
htmlf(
|
||||||
|
"https://gravatar.com/avatar/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||||
|
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||||
|
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15],
|
||||||
|
digest[16], digest[17], digest[18], digest[19], digest[20], digest[21], digest[22], digest[23],
|
||||||
|
digest[24], digest[25], digest[26], digest[27], digest[28], digest[29], digest[30], digest[31]
|
||||||
|
);
|
||||||
|
|
||||||
|
free(email_lower);
|
||||||
|
}
|
||||||
|
!}
|
65
themed/commit.html
Normal file
65
themed/commit.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{! #include "../ui-diff.h" !}
|
||||||
|
|
||||||
|
{% page cgit_print_commit %}
|
||||||
|
{!
|
||||||
|
char *hex = ctx.qry.oid;
|
||||||
|
if (!hex) { hex = ctx.qry.head; }
|
||||||
|
|
||||||
|
struct object_id oid;
|
||||||
|
if (repo_get_oid(the_repository, hex, &oid)) {
|
||||||
|
die("Bad object id");
|
||||||
|
}
|
||||||
|
struct commit *commit = lookup_commit_reference(the_repository, &oid);
|
||||||
|
if (!commit) {
|
||||||
|
die("Bad commit reference");
|
||||||
|
}
|
||||||
|
struct commitinfo *info = cgit_parse_commit(commit);
|
||||||
|
|
||||||
|
ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title);
|
||||||
|
!}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{! repo_summary_bar(); !}
|
||||||
|
<div class="grid grid-cols-1 border border-gray-300 rounded-md divide-y divide-gray-300 mb-4">
|
||||||
|
{# Commit info box #}
|
||||||
|
<div class="px-3 py-2 flex items-top">
|
||||||
|
<div class="flex-1">
|
||||||
|
{# Description panel #}
|
||||||
|
<div class="text-lg font-semibold">{{ info->subject }}</div>
|
||||||
|
{% if info-> msg %}
|
||||||
|
<div class="text-gray-500 pt-1">{{ info->msg }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="py-2">
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("tree", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="p-2 text-sm text-white bg-blue-500 rounded-md hover:bg-blue-600">Browse Source</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-3 py-2 rounded-b-md bg-gray-50 flex gap-x-1 items-center">
|
||||||
|
<img src="{! gravatar_url(info->author_email); !}?s=24">
|
||||||
|
<span class="font-semibold text-sm">{{ info->author }}</span>
|
||||||
|
<span class="text-gray-500 text-sm">{! cgit_print_age_themed(info->committer_date, info->committer_tz, TM_MONTH * 12); !}</span>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<span class="text-gray-500 text-sm">
|
||||||
|
{% for struct commit_list *p = commit->parents; p; p = p->next %}
|
||||||
|
{! struct commit *parent = lookup_commit_reference(the_repository, &p->item->object.oid); !}
|
||||||
|
{% if parent %}
|
||||||
|
parent
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&p->item->object.oid), ctx.qry.vpath); !}" class="font-mono text-blue-500 hover:text-blue-600 hover:underline">{! short_commit_id(oid_to_hex(&p->item->object.oid)); !}</a>
|
||||||
|
·
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
commit
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="font-mono text-blue-500 hover:text-blue-600 hover:underline">{! short_commit_id(oid_to_hex(&commit->object.oid)); !}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="diff-panel overflow-x-auto">
|
||||||
|
{# Diff panel #}
|
||||||
|
{! cgit_print_diff(ctx.qry.oid, NULL, NULL, 0, 0); !}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{! cgit_free_commitinfo(info); !}
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
15
themed/diff.html
Normal file
15
themed/diff.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{! #include "../ui-diff.h" !}
|
||||||
|
|
||||||
|
{% page cgit_print_diff_page %}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{! repo_summary_bar(); !}
|
||||||
|
<div class="diff-panel overflow-x-auto">
|
||||||
|
{# Diff panel #}
|
||||||
|
{! cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 0, 0); !}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
1
themed/htmlcc
Submodule
1
themed/htmlcc
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit ab72cefd6745a647dbd41a9814397873ab198e48
|
43
themed/index.html
Normal file
43
themed/index.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{! #include "../ui-repolist.h" !}
|
||||||
|
|
||||||
|
{% page cgit_print_repolist %}
|
||||||
|
{! ctx.page.title = ctx.cfg.root_title; !}
|
||||||
|
{! page_start(); !}
|
||||||
|
<header class="bg-gray-50 border-b border-gray-300">
|
||||||
|
{# Repo header #}
|
||||||
|
<div class="max-w-[1280px] mx-auto py-4 flex gap-x-1">
|
||||||
|
<a href="{{ cgit_rooturl()|attr }}" class="text-lg hover:underline">{{ ctx.cfg.root_title }}</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">
|
||||||
|
{# Main content #}
|
||||||
|
<form method="GET" action="{{ cgit_rooturl()|attr }}" class="flex text-sm mb-4 outline-1 outline-gray-300 rounded-lg has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
|
||||||
|
{# Search box #}
|
||||||
|
<input name="q" value="{{ ctx.qry.search|attr }}" type="text" placeholder="Search repos…" class="flex-1 py-1.5 pl-2 text-sm focus:outline-none">
|
||||||
|
<button class="py-1.5 px-2 cursor-pointer" action="submit" value="search">
|
||||||
|
{# Heroicons micro magnifying-glass #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="text-gray-700 size-4"><path fill-rule="evenodd" d="M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z" clip-rule="evenodd" /></svg>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="grid grid-cols-[auto_1fr]">
|
||||||
|
{# Repo list #}
|
||||||
|
{! cgit_repolist_sort_repolist("idle"); !}
|
||||||
|
{% for int i = 0; i < cgit_repolist.count; i++ %}
|
||||||
|
{! ctx.repo = &cgit_repolist.repos[i]; !}
|
||||||
|
{! if (!cgit_repolist_is_visible(ctx.repo)) { continue; } !}
|
||||||
|
<div{% if i > 0 %} class="border-t border-gray-300 pt-2 mt-2"{% endif %}>
|
||||||
|
{# Heroicons outline cube #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" /></svg>
|
||||||
|
</div>
|
||||||
|
<div class="{% if i > 0 %}border-t border-gray-300 pt-2 mt-2 {% endif %}pl-2">
|
||||||
|
{! char *repourl = cgit_repourl(ctx.repo->url); !}
|
||||||
|
<div><a href="{{ repourl|attr }}" class="font-semibold text-blue-500 hover:text-blue-600 hover:underline">{{ ctx.repo->name }}</a></div>
|
||||||
|
{! free(repourl); !}
|
||||||
|
<div class="text-sm text-gray-500">{{ ctx.repo->desc }}</div>
|
||||||
|
<div class="text-sm text-gray-500">Updated {! cgit_repolist_print_modtime_themed(ctx.repo); !}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
58
themed/log.html
Normal file
58
themed/log.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{% page cgit_print_log %}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{! repo_summary_bar(); !}
|
||||||
|
{!
|
||||||
|
struct rev_info rev;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
|
const char *argv[] = {NULL, ctx.qry.head};
|
||||||
|
int argc = 2;
|
||||||
|
|
||||||
|
repo_init_revisions(the_repository, &rev, NULL);
|
||||||
|
rev.abbrev = DEFAULT_ABBREV;
|
||||||
|
rev.commit_format = CMIT_FMT_DEFAULT;
|
||||||
|
rev.verbose_header = 1;
|
||||||
|
rev.show_root_diff = 0;
|
||||||
|
rev.ignore_missing = 1;
|
||||||
|
rev.simplify_history = 1;
|
||||||
|
setup_revisions(argc, argv, &rev, NULL);
|
||||||
|
prepare_revision_walk(&rev);
|
||||||
|
!}
|
||||||
|
<div class="grid grid-cols-[1fr_auto_auto] border border-gray-300 rounded-md">
|
||||||
|
{# Commits list #}
|
||||||
|
<div class="col-span-3 rounded-t-md bg-gray-50 px-3 py-2 font-semibold text-sm">
|
||||||
|
Commits
|
||||||
|
</div>
|
||||||
|
{% while (commit = get_revision(&rev)) != NULL %}
|
||||||
|
{! struct commitinfo *info = cgit_parse_commit(commit); !}
|
||||||
|
<div class="px-3 py-2 border-t border-gray-300">
|
||||||
|
<div><a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="hover:text-blue-600 hover:underline">{{ info->subject }}</a></div>
|
||||||
|
<div class="mt-2 text-sm text-gray-500 flex gap-x-1 items-center">
|
||||||
|
<img src="{! gravatar_url(info->author_email); !}?s=16">
|
||||||
|
{{ info->author }} committed {! cgit_print_age_themed(info->committer_date, info->committer_tz, TM_MONTH * 12); !}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-3 py-2 border-t border-gray-300 flex items-center">
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="text-sm font-mono text-gray-500 hover:text-blue-600 hover:underline">
|
||||||
|
{! short_commit_id(oid_to_hex(&commit->object.oid)); !}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="px-3 py-2 border-t border-gray-300 flex items-center">
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("tree", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="text-gray-500 hover:text-blue-600">
|
||||||
|
{# Heroicons mini code-bracket #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5"><path fill-rule="evenodd" d="M6.28 5.22a.75.75 0 0 1 0 1.06L2.56 10l3.72 3.72a.75.75 0 0 1-1.06 1.06L.97 10.53a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Zm7.44 0a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L17.44 10l-3.72-3.72a.75.75 0 0 1 0-1.06ZM11.377 2.011a.75.75 0 0 1 .612.867l-2.5 14.5a.75.75 0 0 1-1.478-.255l2.5-14.5a.75.75 0 0 1 .866-.612Z" clip-rule="evenodd" /></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{! cgit_free_commitinfo(info); !}
|
||||||
|
{! release_commit_memory(the_repository->parsed_objects, commit); !}
|
||||||
|
{! commit->parents = NULL; !}
|
||||||
|
{% endwhile %}
|
||||||
|
</div>
|
||||||
|
{! reset_revision_walk(); !}
|
||||||
|
{! release_revisions(&rev); !}
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
184
themed/mincrypt_sha256.c
Normal file
184
themed/mincrypt_sha256.c
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/* sha256.c
|
||||||
|
**
|
||||||
|
** Copyright 2013, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** * Neither the name of Google Inc. nor the names of its contributors may
|
||||||
|
** be used to endorse or promote products derived from this software
|
||||||
|
** without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Optimized for minimal code size.
|
||||||
|
|
||||||
|
#include "mincrypt_sha256.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
|
||||||
|
#define shr(value, bits) ((value) >> (bits))
|
||||||
|
|
||||||
|
static const uint32_t K[64] = {
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||||
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||||
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||||
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||||
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||||
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||||
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||||
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
|
||||||
|
|
||||||
|
static void SHA256_Transform(mincrypt_SHA256_CTX* ctx) {
|
||||||
|
uint32_t W[64];
|
||||||
|
uint32_t A, B, C, D, E, F, G, H;
|
||||||
|
uint8_t* p = ctx->buf;
|
||||||
|
int t;
|
||||||
|
|
||||||
|
for(t = 0; t < 16; ++t) {
|
||||||
|
uint32_t tmp = *p++ << 24;
|
||||||
|
tmp |= *p++ << 16;
|
||||||
|
tmp |= *p++ << 8;
|
||||||
|
tmp |= *p++;
|
||||||
|
W[t] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; t < 64; t++) {
|
||||||
|
uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
|
||||||
|
uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
|
||||||
|
W[t] = W[t-16] + s0 + W[t-7] + s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
A = ctx->state[0];
|
||||||
|
B = ctx->state[1];
|
||||||
|
C = ctx->state[2];
|
||||||
|
D = ctx->state[3];
|
||||||
|
E = ctx->state[4];
|
||||||
|
F = ctx->state[5];
|
||||||
|
G = ctx->state[6];
|
||||||
|
H = ctx->state[7];
|
||||||
|
|
||||||
|
for(t = 0; t < 64; t++) {
|
||||||
|
uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
|
||||||
|
uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
|
||||||
|
uint32_t t2 = s0 + maj;
|
||||||
|
uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
|
||||||
|
uint32_t ch = (E & F) ^ ((~E) & G);
|
||||||
|
uint32_t t1 = H + s1 + ch + K[t] + W[t];
|
||||||
|
|
||||||
|
H = G;
|
||||||
|
G = F;
|
||||||
|
F = E;
|
||||||
|
E = D + t1;
|
||||||
|
D = C;
|
||||||
|
C = B;
|
||||||
|
B = A;
|
||||||
|
A = t1 + t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->state[0] += A;
|
||||||
|
ctx->state[1] += B;
|
||||||
|
ctx->state[2] += C;
|
||||||
|
ctx->state[3] += D;
|
||||||
|
ctx->state[4] += E;
|
||||||
|
ctx->state[5] += F;
|
||||||
|
ctx->state[6] += G;
|
||||||
|
ctx->state[7] += H;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HASH_VTAB SHA256_VTAB = {
|
||||||
|
SHA256_init,
|
||||||
|
SHA256_update,
|
||||||
|
SHA256_final,
|
||||||
|
SHA256_hash,
|
||||||
|
SHA256_DIGEST_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
void SHA256_init(mincrypt_SHA256_CTX* ctx) {
|
||||||
|
ctx->f = &SHA256_VTAB;
|
||||||
|
ctx->state[0] = 0x6a09e667;
|
||||||
|
ctx->state[1] = 0xbb67ae85;
|
||||||
|
ctx->state[2] = 0x3c6ef372;
|
||||||
|
ctx->state[3] = 0xa54ff53a;
|
||||||
|
ctx->state[4] = 0x510e527f;
|
||||||
|
ctx->state[5] = 0x9b05688c;
|
||||||
|
ctx->state[6] = 0x1f83d9ab;
|
||||||
|
ctx->state[7] = 0x5be0cd19;
|
||||||
|
ctx->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SHA256_update(mincrypt_SHA256_CTX* ctx, const void* data, int len) {
|
||||||
|
int i = (int) (ctx->count & 63);
|
||||||
|
const uint8_t* p = (const uint8_t*)data;
|
||||||
|
|
||||||
|
ctx->count += len;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
ctx->buf[i++] = *p++;
|
||||||
|
if (i == 64) {
|
||||||
|
SHA256_Transform(ctx);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t* SHA256_final(mincrypt_SHA256_CTX* ctx) {
|
||||||
|
uint8_t *p = ctx->buf;
|
||||||
|
uint64_t cnt = ctx->count * 8;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
SHA256_update(ctx, (uint8_t*)"\x80", 1);
|
||||||
|
while ((ctx->count & 63) != 56) {
|
||||||
|
SHA256_update(ctx, (uint8_t*)"\0", 1);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
|
||||||
|
SHA256_update(ctx, &tmp, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
uint32_t tmp = ctx->state[i];
|
||||||
|
*p++ = tmp >> 24;
|
||||||
|
*p++ = tmp >> 16;
|
||||||
|
*p++ = tmp >> 8;
|
||||||
|
*p++ = tmp >> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convenience function */
|
||||||
|
const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
|
||||||
|
mincrypt_SHA256_CTX ctx;
|
||||||
|
SHA256_init(&ctx);
|
||||||
|
SHA256_update(&ctx, data, len);
|
||||||
|
memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
|
||||||
|
return digest;
|
||||||
|
}
|
115
themed/mincrypt_sha256.h
Normal file
115
themed/mincrypt_sha256.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||||
|
* be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
||||||
|
#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
struct HASH_CTX; // forward decl
|
||||||
|
|
||||||
|
typedef struct HASH_VTAB {
|
||||||
|
void (* const init)(struct HASH_CTX*);
|
||||||
|
void (* const update)(struct HASH_CTX*, const void*, int);
|
||||||
|
const uint8_t* (* const final)(struct HASH_CTX*);
|
||||||
|
const uint8_t* (* const hash)(const void*, int, uint8_t*);
|
||||||
|
int size;
|
||||||
|
} HASH_VTAB;
|
||||||
|
|
||||||
|
typedef struct HASH_CTX {
|
||||||
|
const HASH_VTAB * f;
|
||||||
|
uint64_t count;
|
||||||
|
uint8_t buf[64];
|
||||||
|
uint32_t state[8]; // upto SHA2
|
||||||
|
} HASH_CTX;
|
||||||
|
|
||||||
|
#define HASH_init(ctx) (ctx)->f->init(ctx)
|
||||||
|
#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
|
||||||
|
#define HASH_final(ctx) (ctx)->f->final(ctx)
|
||||||
|
#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
|
||||||
|
#define HASH_size(ctx) (ctx)->f->size
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its contributors may
|
||||||
|
* be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
||||||
|
#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
typedef HASH_CTX mincrypt_SHA256_CTX;
|
||||||
|
|
||||||
|
void SHA256_init(mincrypt_SHA256_CTX* ctx);
|
||||||
|
void SHA256_update(mincrypt_SHA256_CTX* ctx, const void* data, int len);
|
||||||
|
const uint8_t* SHA256_final(mincrypt_SHA256_CTX* ctx);
|
||||||
|
|
||||||
|
// Convenience method. Returns digest address.
|
||||||
|
const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
|
||||||
|
|
||||||
|
#define SHA256_DIGEST_SIZE 32
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
|
111
themed/refs.html
Normal file
111
themed/refs.html
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
{! int cgit_refs_cmp_branch_age(const void *a, const void *b); !}
|
||||||
|
{! int cgit_refs_cmp_ref_name(const void *a, const void *b); !}
|
||||||
|
{! int cgit_refs_cmp_tag_age(const void *a, const void *b); !}
|
||||||
|
|
||||||
|
{% page cgit_print_refs %}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{! repo_summary_bar(); !}
|
||||||
|
{% if !ctx.qry.path || starts_with(ctx.qry.path, "heads") %}
|
||||||
|
{!
|
||||||
|
struct reflist list;
|
||||||
|
list.refs = NULL;
|
||||||
|
list.alloc = list.count = 0;
|
||||||
|
refs_for_each_branch_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
|
||||||
|
qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_branch_age);
|
||||||
|
if (ctx.repo->branch_sort == 0) {
|
||||||
|
qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_ref_name);
|
||||||
|
}
|
||||||
|
!}
|
||||||
|
<div class="grid grid-cols-1 border border-gray-300 rounded-md divide-y divide-gray-300">
|
||||||
|
{# Branches list #}
|
||||||
|
<div class="rounded-t-md bg-gray-50 px-3 py-2 font-semibold text-sm">
|
||||||
|
Branches
|
||||||
|
</div>
|
||||||
|
{% for int i = 0; i < list.count; i++ %}
|
||||||
|
{! struct refinfo *ref = list.refs[i]; !}
|
||||||
|
{! struct commitinfo *info = ref->commit; !}
|
||||||
|
{! char *name = (char *)ref->refname; !}
|
||||||
|
{! if (!info) { continue; } !}
|
||||||
|
<div class="px-3 py-2 flex">
|
||||||
|
<div>
|
||||||
|
{# Branch info #}
|
||||||
|
<div><a href="{! cgit_shared_repolink_url(NULL, name, NULL); !}" class="text-blue-500 hover:text-blue-600 hover:underline">{{ name }}</a></div>
|
||||||
|
{% if ref->object->type == OBJ_COMMIT %}
|
||||||
|
<div class="text-sm text-gray-500 flex gap-x-1">
|
||||||
|
{{ info->subject }} · Updated {! cgit_print_age_themed(info->committer_date, info->committer_tz, TM_MONTH * 12); !}
|
||||||
|
<img src="{! gravatar_url(info->author_email); !}?s=24" class="mt-[-0.2rem]">
|
||||||
|
{{ info->author }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if strcmp(name, ctx.qry.head) %}{# Only show compare button if not equal to current branch #}
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("diff", ctx.qry.head, name, NULL); !}&id2={{ ctx.qry.head|urlencode }}" class="py-1.5 px-3 self-center text-gray-500 bg-gray-50 border border-gray-300 rounded-md hover:bg-gray-100">
|
||||||
|
Compare
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{! cgit_free_reflist_inner(&list); !}
|
||||||
|
{% endif %}
|
||||||
|
{% if !ctx.qry.path || starts_with(ctx.qry.path, "tags") %}
|
||||||
|
{!
|
||||||
|
struct reflist list;
|
||||||
|
list.refs = NULL;
|
||||||
|
list.alloc = list.count = 0;
|
||||||
|
refs_for_each_tag_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
|
||||||
|
!}
|
||||||
|
{% if list.count > 0 %}
|
||||||
|
{! qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_tag_age); !}
|
||||||
|
<div class="grid grid-cols-1 border border-gray-300 rounded-md divide-y divide-gray-300 mt-4">
|
||||||
|
{# Tags list #}
|
||||||
|
<div class="rounded-t-md bg-gray-50 px-3 py-2 font-semibold text-sm">
|
||||||
|
Tags
|
||||||
|
</div>
|
||||||
|
{% for int i = 0; i < list.count; i++ %}
|
||||||
|
{!
|
||||||
|
struct refinfo *ref = list.refs[i];
|
||||||
|
struct tag *tag = NULL;
|
||||||
|
struct taginfo *info = NULL;
|
||||||
|
char *name = (char *)ref->refname;
|
||||||
|
struct object *obj = ref->object;
|
||||||
|
|
||||||
|
if (obj->type == OBJ_TAG) {
|
||||||
|
tag = (struct tag *)obj;
|
||||||
|
obj = tag->tagged;
|
||||||
|
info = ref->tag;
|
||||||
|
if (!info) { continue; }
|
||||||
|
}
|
||||||
|
!}
|
||||||
|
<div class="px-3 py-2">
|
||||||
|
<div><a href="{! cgit_shared_repolink_url(NULL, name, NULL); !}" class="text-blue-500 hover:text-blue-600 hover:underline">{{ name }}</a></div>
|
||||||
|
{% if info && (info->tagger_date > 0 || info->tagger) %}
|
||||||
|
<div class="text-sm text-gray-500 flex gap-x-1">
|
||||||
|
{% if info->tagger_date > 0 %}
|
||||||
|
Updated {! cgit_print_age_themed(info->tagger_date, info->tagger_tz, TM_MONTH * 12); !}
|
||||||
|
{% endif %}
|
||||||
|
{% if info->tagger %}
|
||||||
|
<img src="{! gravatar_url(info->tagger_email); !}?s=24" class="mt-[-0.2rem]">
|
||||||
|
{{ info->tagger }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% elif ref->object->type == OBJ_COMMIT %}
|
||||||
|
<div class="text-sm text-gray-500 flex gap-x-1">
|
||||||
|
Updated {! cgit_print_age_themed(ref->commit->commit->date, 0, TM_MONTH * 12); !}
|
||||||
|
<img src="{! gravatar_url(ref->commit->author_email); !}?s=24" class="mt-[-0.2rem]">
|
||||||
|
{{ ref->commit->author }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{! cgit_free_reflist_inner(&list); !}
|
||||||
|
{% endif %}{# if list.count > 0 #}
|
||||||
|
{% endif %}{# if !ctx.qry.path || starts_with(ctx.qry.path, "tags") #}
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
88
themed/summary.html
Normal file
88
themed/summary.html
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{! #include "../ui-blob.h" !}
|
||||||
|
|
||||||
|
{!
|
||||||
|
int ls_item(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *cbdata) {
|
||||||
|
tree_listing_item(oid, base, pathname, mode, true); /* from tree_common.html */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
!}
|
||||||
|
|
||||||
|
{% block cgit_print_summary_impl %}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{! repo_summary_bar(); !}
|
||||||
|
<div class="grid grid-cols-[auto_1fr_auto_auto] border border-gray-300 rounded-md mb-4">
|
||||||
|
{# Latest commit panel (specify rounded-t-md so background does not cover border) #}
|
||||||
|
{!
|
||||||
|
char *hex = ctx.qry.oid;
|
||||||
|
if (!hex) { hex = ctx.qry.head; }
|
||||||
|
|
||||||
|
struct object_id oid;
|
||||||
|
if (repo_get_oid(the_repository, hex, &oid)) {
|
||||||
|
die("Bad object id");
|
||||||
|
}
|
||||||
|
struct commit *commit = lookup_commit_reference(the_repository, &oid);
|
||||||
|
if (!commit) {
|
||||||
|
die("Bad commit reference");
|
||||||
|
}
|
||||||
|
struct commitinfo *info = cgit_parse_commit(commit);
|
||||||
|
!}
|
||||||
|
<div class="col-span-4 rounded-t-md bg-gray-50 px-3 py-2 flex gap-x-1">
|
||||||
|
<img src="{! gravatar_url(info->author_email); !}?s=24">
|
||||||
|
<span class="font-semibold">{{ info->author }}</span>
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="ml-2 text-gray-500 hover:text-blue-600 hover:underline">{{ info->subject }}</a>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<span class="text-gray-500">
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("commit", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); !}" class="font-mono hover:text-blue-600 hover:underline">
|
||||||
|
{! short_commit_id(oid_to_hex(&commit->object.oid)); !}
|
||||||
|
</a>
|
||||||
|
·
|
||||||
|
{! cgit_print_age_themed(info->committer_date, info->committer_tz, TM_MONTH * 12); !}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{! cgit_free_commitinfo(info); !}
|
||||||
|
{# Files #}
|
||||||
|
{!
|
||||||
|
const struct object_id *tree_oid = get_commit_tree_oid(commit);
|
||||||
|
struct tree *tree = parse_tree_indirect(tree_oid);
|
||||||
|
struct pathspec paths = {
|
||||||
|
.nr = 0
|
||||||
|
};
|
||||||
|
read_tree(the_repository, tree, &paths, ls_item, NULL);
|
||||||
|
!}
|
||||||
|
</div>
|
||||||
|
{% if ctx.repo->readme.nr > 0 %}
|
||||||
|
{!
|
||||||
|
char *filename = ctx.repo->readme.items[0].string;
|
||||||
|
char *ref = ctx.repo->readme.items[0].util;
|
||||||
|
!}
|
||||||
|
<div class="flex flex-col border border-gray-300 rounded-md">
|
||||||
|
{# Readme panel #}
|
||||||
|
<div class="rounded-t-md bg-gray-50 px-3 py-2 flex gap-x-1 items-center">
|
||||||
|
{# Heroicons micro book-open #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M7.25 3.688a8.035 8.035 0 0 0-4.872-.523A.48.48 0 0 0 2 3.64v7.994c0 .345.342.588.679.512a6.02 6.02 0 0 1 4.571.81V3.688ZM8.75 12.956a6.02 6.02 0 0 1 4.571-.81c.337.075.679-.167.679-.512V3.64a.48.48 0 0 0-.378-.475 8.034 8.034 0 0 0-4.872.523v9.268Z" /></svg>
|
||||||
|
<span class="font-semibold text-sm">{{ filename }}</span>
|
||||||
|
</div>
|
||||||
|
{# Readme content #}
|
||||||
|
<div class="rendered-file p-7 border-t border-gray-300">
|
||||||
|
{!
|
||||||
|
cgit_open_filter(ctx.repo->about_filter, filename);
|
||||||
|
if (ref) {
|
||||||
|
cgit_print_file(filename, ref, 1);
|
||||||
|
} else {
|
||||||
|
html_include(filename);
|
||||||
|
}
|
||||||
|
cgit_close_filter(ctx.repo->about_filter);
|
||||||
|
!}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% page cgit_print_summary %}
|
||||||
|
{! cgit_print_summary_impl(); !}
|
||||||
|
{% endpage %}
|
168
themed/themed.in.css
Normal file
168
themed/themed.in.css
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Noto Sans", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rendered-file {
|
||||||
|
a {
|
||||||
|
@apply hover:underline;
|
||||||
|
}
|
||||||
|
:not(h1, h2, h3, h4, h5, h6) > a {
|
||||||
|
@apply text-blue-500 hover:text-blue-600;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
@apply list-disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rendered-blob {
|
||||||
|
/* From cgit.css */
|
||||||
|
|
||||||
|
table.blob {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
border-top: solid 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.blob td.hashes,
|
||||||
|
table.blob td.lines {
|
||||||
|
margin: 0; padding: 0 0 0 0.5em;
|
||||||
|
vertical-align: top;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.blob td.linenumbers {
|
||||||
|
margin: 0; padding: 0 0.5em 0 0.5em;
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: right;
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.blob pre {
|
||||||
|
padding: 0; margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.blob td.linenumbers a,
|
||||||
|
table.ssdiff td.lineno a {
|
||||||
|
color: gray;
|
||||||
|
text-align: right;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.blob td.linenumbers a:hover,
|
||||||
|
table.ssdiff td.lineno a:hover {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-panel {
|
||||||
|
a {
|
||||||
|
@apply text-blue-500 hover:text-blue-600 hover:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From cgit.css */
|
||||||
|
|
||||||
|
div.diffstat-header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: solid 1px #aaa;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat th {
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: underline;
|
||||||
|
padding: 0.1em 1em 0.1em 0.1em;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td {
|
||||||
|
padding: 0.2em 0.2em 0.1em 0.1em;
|
||||||
|
font-size: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.mode {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td span.modechange {
|
||||||
|
padding-left: 1em;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.add a {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.del a {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.upd a {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.graph {
|
||||||
|
width: 500px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.graph table {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.graph td {
|
||||||
|
padding: 0px;
|
||||||
|
border: 0px;
|
||||||
|
height: 7pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.graph td.add {
|
||||||
|
background-color: #5c5;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diffstat td.graph td.rem {
|
||||||
|
background-color: #c55;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.diffstat-summary {
|
||||||
|
color: #888;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff td {
|
||||||
|
@apply font-mono;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff td div.head {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1em;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff td div.hunk {
|
||||||
|
color: #009;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff td div.add {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff td div.del {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
181
themed/tree.html
Normal file
181
themed/tree.html
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
{! #include "../ui-tree.h" !}
|
||||||
|
|
||||||
|
{% block repo_navigation_breadcrumbs %}
|
||||||
|
<div class="px-3">
|
||||||
|
{# Breadcrumbs #}
|
||||||
|
{# TODO: Make breadcrumbs hyperlinks #}
|
||||||
|
<a href="{! cgit_shared_reporevlink_url(NULL, ctx.qry.head, ctx.qry.oid, NULL); !}" class="text-blue-500 hover:text-blue-600 hover:underline">{{ ctx.repo->name }}</a> / {{ ctx.qry.path }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block tree_content_directory_header %}
|
||||||
|
{# Header for directory listing #}
|
||||||
|
<nav class="flex text-sm mb-4 items-baseline">
|
||||||
|
{# Repo navigation panel #}
|
||||||
|
{! repo_summary_bar_current_branch(); !}
|
||||||
|
{! repo_navigation_breadcrumbs(); !}
|
||||||
|
</nav>
|
||||||
|
<div class="grid grid-cols-[auto_1fr_auto_auto] border border-gray-300 rounded-md mb-4">
|
||||||
|
{% endblock %}
|
||||||
|
{% block tree_content_directory_footer %}
|
||||||
|
{# Footer for directory listing #}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block tree_content_file(const struct object_id *oid, const char *path, const char *basename, const char *rev) %}
|
||||||
|
<nav class="flex text-sm mb-4 items-baseline">
|
||||||
|
{# Repo navigation panel #}
|
||||||
|
{! repo_summary_bar_current_branch(); !}
|
||||||
|
{! repo_navigation_breadcrumbs(); !}
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
<div class="flex">
|
||||||
|
{# File buttons #}
|
||||||
|
<a href="{! cgit_shared_repolink_url("plain", ctx.qry.head, ctx.qry.path); !}" class="text-sm text-gray-500 py-1.5 px-3 bg-gray-50 border border-gray-300 rounded-md hover:bg-gray-100">Raw</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{!
|
||||||
|
unsigned long size;
|
||||||
|
enum object_type type = oid_object_info(the_repository, oid, &size);
|
||||||
|
if (type == OBJ_BAD) {
|
||||||
|
die("Bad object name");
|
||||||
|
}
|
||||||
|
char *buf = repo_read_object_file(the_repository, oid, &type, &size);
|
||||||
|
if (!buf) {
|
||||||
|
die("Error reading object");
|
||||||
|
}
|
||||||
|
bool is_binary = buffer_is_binary(buf, size);
|
||||||
|
!}
|
||||||
|
{% if ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size %}
|
||||||
|
<div class="text-red-600">
|
||||||
|
blob size ({{ size / 1024|%ld }}KB) exceeds display size limit ({{ ctx.cfg.max_blob_size|%d }}KB).
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="rendered-blob overflow-x-auto">
|
||||||
|
{!
|
||||||
|
if (is_binary) {
|
||||||
|
cgit_tree_print_binary_buffer(buf, size);
|
||||||
|
} else {
|
||||||
|
cgit_tree_print_text_buffer(basename, buf, size);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
!}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{!
|
||||||
|
struct walk_tree_context {
|
||||||
|
char *curr_rev;
|
||||||
|
char *match_path;
|
||||||
|
int state;
|
||||||
|
int directory_child_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int walk_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *cbdata)
|
||||||
|
{
|
||||||
|
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||||
|
|
||||||
|
if (walk_tree_ctx->state == 0) {
|
||||||
|
// State 0 = Walking recursively to find the target path
|
||||||
|
|
||||||
|
struct strbuf buffer = STRBUF_INIT;
|
||||||
|
strbuf_addbuf(&buffer, base);
|
||||||
|
strbuf_addstr(&buffer, pathname);
|
||||||
|
if (strcmp(walk_tree_ctx->match_path, buffer.buf)) {
|
||||||
|
// Not the target path, so continue to walk the tree
|
||||||
|
strbuf_release(&buffer);
|
||||||
|
|
||||||
|
if (S_ISGITLINK(mode)) {
|
||||||
|
// Never recurse into submodules
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return READ_TREE_RECURSIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the target path
|
||||||
|
if (S_ISDIR(mode)) {
|
||||||
|
// Target path is a directory - set state to 1 and do one final walk to get contents
|
||||||
|
walk_tree_ctx->state = 1;
|
||||||
|
strbuf_release(&buffer);
|
||||||
|
tree_content_directory_header();
|
||||||
|
return READ_TREE_RECURSIVE;
|
||||||
|
} else {
|
||||||
|
// Target path is a file - set state to 2, display file and exit
|
||||||
|
walk_tree_ctx->state = 2;
|
||||||
|
tree_content_file(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev);
|
||||||
|
strbuf_release(&buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walk_tree_ctx->state == 1) {
|
||||||
|
// State 1 = Target path is a directory, one final walk to get contents
|
||||||
|
// Either a child of the directory of interest, or a child of a parent directory - so must check the path
|
||||||
|
|
||||||
|
struct strbuf buffer = STRBUF_INIT;
|
||||||
|
strbuf_addstr(&buffer, walk_tree_ctx->match_path);
|
||||||
|
strbuf_addstr(&buffer, "/");
|
||||||
|
|
||||||
|
if (!strcmp(buffer.buf, base->buf)) {
|
||||||
|
tree_listing_item(oid, base, pathname, mode, walk_tree_ctx->directory_child_idx > 0);
|
||||||
|
walk_tree_ctx->directory_child_idx++;
|
||||||
|
}
|
||||||
|
strbuf_release(&buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Should be unreachable
|
||||||
|
}
|
||||||
|
!}
|
||||||
|
|
||||||
|
{% page cgit_print_tree %}
|
||||||
|
{!
|
||||||
|
// Redirect to summary page if no subdirectory
|
||||||
|
if (!ctx.qry.path) { return cgit_print_summary_impl(); }
|
||||||
|
!}
|
||||||
|
{! page_start(); !}
|
||||||
|
{! repo_header(); !}
|
||||||
|
<main class="max-w-[1280px] mx-auto py-4">{# Main content #}
|
||||||
|
{! repo_description_panel(); !}
|
||||||
|
{!
|
||||||
|
char *hex = ctx.qry.oid;
|
||||||
|
if (!hex) { hex = ctx.qry.head; }
|
||||||
|
|
||||||
|
struct object_id oid;
|
||||||
|
if (repo_get_oid(the_repository, hex, &oid)) {
|
||||||
|
die("Bad object id");
|
||||||
|
}
|
||||||
|
struct commit *commit = lookup_commit_reference(the_repository, &oid);
|
||||||
|
if (!commit) {
|
||||||
|
die("Bad commit reference");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare to walk the tree recursively to find the path
|
||||||
|
struct pathspec paths = {
|
||||||
|
.nr = 0
|
||||||
|
};
|
||||||
|
struct walk_tree_context walk_tree_ctx = {
|
||||||
|
.curr_rev = xstrdup(hex),
|
||||||
|
.match_path = ctx.qry.path,
|
||||||
|
.state = 0,
|
||||||
|
.directory_child_idx = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// State 0 = Walking recursively to find the target path
|
||||||
|
// State 1 = Target path is a directory, one final walk to get contents
|
||||||
|
// State 2 = Target path is a file
|
||||||
|
|
||||||
|
read_tree(the_repository, repo_get_commit_tree(the_repository, commit), &paths, walk_tree, &walk_tree_ctx);
|
||||||
|
free(walk_tree_ctx.curr_rev);
|
||||||
|
!}
|
||||||
|
{% if walk_tree_ctx.state == 0 %}
|
||||||
|
<nav class="flex text-sm mb-4 items-baseline">
|
||||||
|
{# Repo navigation panel #}
|
||||||
|
{! repo_summary_bar_current_branch(); !}
|
||||||
|
{! repo_navigation_breadcrumbs(); !}
|
||||||
|
</nav>
|
||||||
|
<div class="text-red-600">File not found</div>
|
||||||
|
{% endif %}
|
||||||
|
</main>
|
||||||
|
{! page_end(); !}
|
||||||
|
{% endpage %}
|
49
themed/tree_common.html
Normal file
49
themed/tree_common.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{% block tree_listing_item(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, bool border_above) %}
|
||||||
|
{# Directory listing entry #}
|
||||||
|
{!
|
||||||
|
unsigned long size = 0;
|
||||||
|
if (!S_ISGITLINK(mode)) {
|
||||||
|
oid_object_info(the_repository, oid, &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct strbuf fullpath = STRBUF_INIT;
|
||||||
|
strbuf_addf(&fullpath, "%s%s", base->buf, pathname);
|
||||||
|
!}
|
||||||
|
<div class="pl-3 pr-1 py-2{% if border_above %} border-t border-gray-300{% endif %}">
|
||||||
|
{# File icon #}
|
||||||
|
{% if S_ISGITLINK(mode) %}
|
||||||
|
{# Heroicons solid folder #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5 text-gray-500 mt-[0.1rem]"><path d="M19.5 21a3 3 0 0 0 3-3v-4.5a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3V18a3 3 0 0 0 3 3h15ZM1.5 10.146V6a3 3 0 0 1 3-3h5.379a2.25 2.25 0 0 1 1.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 0 1 3 3v1.146A4.483 4.483 0 0 0 19.5 9h-15a4.483 4.483 0 0 0-3 1.146Z" /></svg>
|
||||||
|
{% elif S_ISDIR(mode) %}
|
||||||
|
{# Heroicons solid folder #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5 text-blue-400 mt-[0.1rem]"><path d="M19.5 21a3 3 0 0 0 3-3v-4.5a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3V18a3 3 0 0 0 3 3h15ZM1.5 10.146V6a3 3 0 0 1 3-3h5.379a2.25 2.25 0 0 1 1.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 0 1 3 3v1.146A4.483 4.483 0 0 0 19.5 9h-15a4.483 4.483 0 0 0-3 1.146Z" /></svg>
|
||||||
|
{% elif S_ISLNK(mode) %}
|
||||||
|
{# Heroicons outline link #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 text-gray-500 mt-[0.1rem]"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" /></svg>
|
||||||
|
{% else %}
|
||||||
|
{# Heroicons outline document #}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 text-gray-500 mt-[0.1rem]"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" /></svg>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="pr-3 py-2{% if border_above %} border-t border-gray-300{% endif %}">
|
||||||
|
{# File name and link #}
|
||||||
|
{% if S_ISGITLINK(mode) %}
|
||||||
|
{! cgit_submodule_link("ls-mod", fullpath.buf, pathname, oid_to_hex(oid)); !}
|
||||||
|
{% else %}
|
||||||
|
<a href="{! cgit_shared_reporevlink_url("tree", ctx.qry.head, ctx.qry.oid, fullpath.buf); !}" class="hover:text-blue-600 hover:underline">
|
||||||
|
{{ pathname }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="pr-3 py-2{% if border_above %} border-t border-gray-300{% endif %} text-gray-500 font-mono">
|
||||||
|
{# Mode #}
|
||||||
|
{! cgit_print_filemode(mode); !}
|
||||||
|
</div>
|
||||||
|
<div class="pr-3 py-2{% if border_above %} border-t border-gray-300{% endif %} text-gray-500 text-end">
|
||||||
|
{# File size #}
|
||||||
|
{% if !S_ISGITLINK(mode) && !S_ISDIR(mode) && !S_ISLNK(mode) %}
|
||||||
|
{{ size|%ld }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{! strbuf_release(&fullpath); !}
|
||||||
|
{% endblock %}
|
16
ui-clone.c
16
ui-clone.c
@ -16,8 +16,8 @@
|
|||||||
#include "packfile.h"
|
#include "packfile.h"
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
|
|
||||||
static int print_ref_info(const char *refname, const struct object_id *oid,
|
static int print_ref_info(const char *refname, const char *referent UNUSED,
|
||||||
int flags, void *cb_data)
|
const struct object_id *oid, int flags, void *cb_data)
|
||||||
{
|
{
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ void cgit_clone_info(void)
|
|||||||
|
|
||||||
void cgit_clone_objects(void)
|
void cgit_clone_objects(void)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p, *path;
|
||||||
|
|
||||||
if (!ctx.qry.path)
|
if (!ctx.qry.path)
|
||||||
goto err;
|
goto err;
|
||||||
@ -116,7 +116,9 @@ void cgit_clone_objects(void)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_file(git_path("objects/%s", ctx.qry.path));
|
path = repo_git_path(the_repository, "objects/%s", ctx.qry.path);
|
||||||
|
send_file(path);
|
||||||
|
free(path);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -125,5 +127,9 @@ err:
|
|||||||
|
|
||||||
void cgit_clone_head(void)
|
void cgit_clone_head(void)
|
||||||
{
|
{
|
||||||
send_file(git_path("%s", "HEAD"));
|
char *path;
|
||||||
|
|
||||||
|
path = repo_git_path(the_repository, "HEAD");
|
||||||
|
send_file(path);
|
||||||
|
free(path);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "ui-diff.h"
|
#include "ui-diff.h"
|
||||||
#include "ui-log.h"
|
#include "ui-log.h"
|
||||||
|
|
||||||
void cgit_print_commit(char *hex, const char *prefix)
|
void _orig_cgit_print_commit(char *hex, const char *prefix)
|
||||||
{
|
{
|
||||||
struct commit *commit, *parent;
|
struct commit *commit, *parent;
|
||||||
struct commitinfo *info, *parent_info;
|
struct commitinfo *info, *parent_info;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef UI_COMMIT_H
|
#ifndef UI_COMMIT_H
|
||||||
#define UI_COMMIT_H
|
#define UI_COMMIT_H
|
||||||
|
|
||||||
extern void cgit_print_commit(char *hex, const char *prefix);
|
extern void cgit_print_commit(void);
|
||||||
|
|
||||||
#endif /* UI_COMMIT_H */
|
#endif /* UI_COMMIT_H */
|
||||||
|
@ -5,6 +5,7 @@ extern void cgit_print_diff_ctrls(void);
|
|||||||
|
|
||||||
extern void cgit_print_diff(const char *new_hex, const char *old_hex,
|
extern void cgit_print_diff(const char *new_hex, const char *old_hex,
|
||||||
const char *prefix, int show_ctrls, int raw);
|
const char *prefix, int show_ctrls, int raw);
|
||||||
|
extern void cgit_print_diff_page(void);
|
||||||
|
|
||||||
extern struct diff_filespec *cgit_get_current_old_file(void);
|
extern struct diff_filespec *cgit_get_current_old_file(void);
|
||||||
extern struct diff_filespec *cgit_get_current_new_file(void);
|
extern struct diff_filespec *cgit_get_current_new_file(void);
|
||||||
|
2
ui-log.c
2
ui-log.c
@ -365,7 +365,7 @@ static char *next_token(char **src)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
|
void _orig_cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
|
||||||
const char *path, int pager, int commit_graph, int commit_sort)
|
const char *path, int pager, int commit_graph, int commit_sort)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
|
3
ui-log.h
3
ui-log.h
@ -1,9 +1,10 @@
|
|||||||
#ifndef UI_LOG_H
|
#ifndef UI_LOG_H
|
||||||
#define UI_LOG_H
|
#define UI_LOG_H
|
||||||
|
|
||||||
extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
|
extern void _orig_cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
|
||||||
char *pattern, const char *path, int pager,
|
char *pattern, const char *path, int pager,
|
||||||
int commit_graph, int commit_sort);
|
int commit_graph, int commit_sort);
|
||||||
|
extern void cgit_print_log(void);
|
||||||
extern void show_commit_decorations(struct commit *commit);
|
extern void show_commit_decorations(struct commit *commit);
|
||||||
|
|
||||||
#endif /* UI_LOG_H */
|
#endif /* UI_LOG_H */
|
||||||
|
@ -118,7 +118,7 @@ static void print_dir_entry(const struct object_id *oid, const char *base,
|
|||||||
fullpath[strlen(fullpath) - 1] = 0;
|
fullpath[strlen(fullpath) - 1] = 0;
|
||||||
html(" <li>");
|
html(" <li>");
|
||||||
if (S_ISGITLINK(mode)) {
|
if (S_ISGITLINK(mode)) {
|
||||||
cgit_submodule_link(NULL, fullpath, oid_to_hex(oid));
|
cgit_submodule_link(NULL, fullpath, NULL, oid_to_hex(oid));
|
||||||
} else
|
} else
|
||||||
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
||||||
fullpath);
|
fullpath);
|
||||||
|
17
ui-refs.c
17
ui-refs.c
@ -27,6 +27,11 @@ static int cmp_ref_name(const void *a, const void *b)
|
|||||||
return strcmp(r1->refname, r2->refname);
|
return strcmp(r1->refname, r2->refname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_refs_cmp_ref_name(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return cmp_ref_name(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
static int cmp_branch_age(const void *a, const void *b)
|
static int cmp_branch_age(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
struct refinfo *r1 = *(struct refinfo **)a;
|
struct refinfo *r1 = *(struct refinfo **)a;
|
||||||
@ -35,6 +40,11 @@ static int cmp_branch_age(const void *a, const void *b)
|
|||||||
return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
|
return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_refs_cmp_branch_age(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return cmp_branch_age(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
static int get_ref_age(struct refinfo *ref)
|
static int get_ref_age(struct refinfo *ref)
|
||||||
{
|
{
|
||||||
if (!ref->object)
|
if (!ref->object)
|
||||||
@ -56,6 +66,11 @@ static int cmp_tag_age(const void *a, const void *b)
|
|||||||
return cmp_age(get_ref_age(r1), get_ref_age(r2));
|
return cmp_age(get_ref_age(r1), get_ref_age(r2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_refs_cmp_tag_age(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return cmp_tag_age(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
static int print_branch(struct refinfo *ref)
|
static int print_branch(struct refinfo *ref)
|
||||||
{
|
{
|
||||||
struct commitinfo *info = ref->commit;
|
struct commitinfo *info = ref->commit;
|
||||||
@ -205,7 +220,7 @@ void cgit_print_tags(int maxcount)
|
|||||||
cgit_free_reflist_inner(&list);
|
cgit_free_reflist_inner(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cgit_print_refs(void)
|
void _orig_cgit_print_refs(void)
|
||||||
{
|
{
|
||||||
cgit_print_layout_start();
|
cgit_print_layout_start();
|
||||||
html("<table class='list nowrap'>");
|
html("<table class='list nowrap'>");
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#ifndef UI_REFS_H
|
#ifndef UI_REFS_H
|
||||||
#define UI_REFS_H
|
#define UI_REFS_H
|
||||||
|
|
||||||
|
extern int cgit_refs_cmp_branch_age(const void *a, const void *b);
|
||||||
|
extern int cgit_refs_cmp_ref_name(const void *a, const void *b);
|
||||||
|
extern int cgit_refs_cmp_tag_age(const void *a, const void *b);
|
||||||
|
|
||||||
extern void cgit_print_branches(int maxcount);
|
extern void cgit_print_branches(int maxcount);
|
||||||
extern void cgit_print_tags(int maxcount);
|
extern void cgit_print_tags(int maxcount);
|
||||||
extern void cgit_print_refs(void);
|
extern void cgit_print_refs(void);
|
||||||
|
@ -82,6 +82,14 @@ static void print_modtime(struct cgit_repo *repo)
|
|||||||
cgit_print_age(t, 0, -1);
|
cgit_print_age(t, 0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_repolist_print_modtime_themed(struct cgit_repo *repo)
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
if (get_repo_modtime(repo, &t)) {
|
||||||
|
cgit_print_age_themed(t, 0, TM_MONTH * 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int is_match(struct cgit_repo *repo)
|
static int is_match(struct cgit_repo *repo)
|
||||||
{
|
{
|
||||||
if (!ctx.qry.search)
|
if (!ctx.qry.search)
|
||||||
@ -115,6 +123,11 @@ static int is_visible(struct cgit_repo *repo)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_repolist_is_visible(struct cgit_repo *repo)
|
||||||
|
{
|
||||||
|
return is_visible(repo);
|
||||||
|
}
|
||||||
|
|
||||||
static int any_repos_visible(void)
|
static int any_repos_visible(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -264,8 +277,13 @@ static int sort_repolist(char *field)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_repolist_sort_repolist(char *field)
|
||||||
|
{
|
||||||
|
return sort_repolist(field);
|
||||||
|
}
|
||||||
|
|
||||||
void cgit_print_repolist(void)
|
|
||||||
|
void _orig_cgit_print_repolist(void)
|
||||||
{
|
{
|
||||||
int i, columns = 3, hits = 0, header = 0;
|
int i, columns = 3, hits = 0, header = 0;
|
||||||
char *last_section = NULL;
|
char *last_section = NULL;
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#ifndef UI_REPOLIST_H
|
#ifndef UI_REPOLIST_H
|
||||||
#define UI_REPOLIST_H
|
#define UI_REPOLIST_H
|
||||||
|
|
||||||
|
extern int cgit_repolist_is_visible(struct cgit_repo *repo);
|
||||||
|
extern void cgit_repolist_print_modtime_themed(struct cgit_repo *repo);
|
||||||
|
extern int cgit_repolist_sort_repolist(char *field);
|
||||||
|
|
||||||
extern void cgit_print_repolist(void);
|
extern void cgit_print_repolist(void);
|
||||||
extern void cgit_print_site_readme(void);
|
extern void cgit_print_site_readme(void);
|
||||||
|
|
||||||
|
119
ui-shared.c
119
ui-shared.c
@ -235,6 +235,11 @@ static void site_url(const char *page, const char *search, const char *sort, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_shared_site_url(const char *page, const char *search, const char *sort, int ofs, int always_root)
|
||||||
|
{
|
||||||
|
site_url(page, search, sort, ofs, always_root);
|
||||||
|
}
|
||||||
|
|
||||||
static void site_link(const char *page, const char *name, const char *title,
|
static void site_link(const char *page, const char *name, const char *title,
|
||||||
const char *class, const char *search, const char *sort, int ofs, int always_root)
|
const char *class, const char *search, const char *sort, int ofs, int always_root)
|
||||||
{
|
{
|
||||||
@ -313,6 +318,52 @@ static char *repolink(const char *title, const char *class, const char *page,
|
|||||||
return fmt("%s", delim);
|
return fmt("%s", delim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *cgit_shared_repolink_url(const char *page, const char *head, const char *path)
|
||||||
|
{
|
||||||
|
char *delim = "?";
|
||||||
|
|
||||||
|
if (ctx.cfg.virtual_root) {
|
||||||
|
html_url_path(ctx.cfg.virtual_root);
|
||||||
|
html_url_path(ctx.repo->url);
|
||||||
|
if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
|
||||||
|
html("/");
|
||||||
|
if (page) {
|
||||||
|
html_url_path(page);
|
||||||
|
html("/");
|
||||||
|
if (path)
|
||||||
|
html_url_path(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html_url_path(ctx.cfg.script_name);
|
||||||
|
html("?url=");
|
||||||
|
html_url_arg(ctx.repo->url);
|
||||||
|
if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
|
||||||
|
html("/");
|
||||||
|
if (page) {
|
||||||
|
html_url_arg(page);
|
||||||
|
html("/");
|
||||||
|
if (path)
|
||||||
|
html_url_arg(path);
|
||||||
|
}
|
||||||
|
delim = "&";
|
||||||
|
}
|
||||||
|
if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
|
||||||
|
html(delim);
|
||||||
|
html("h=");
|
||||||
|
html_url_arg(head);
|
||||||
|
delim = "&";
|
||||||
|
}
|
||||||
|
|
||||||
|
return delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cgit_shared_repolink_url_with_delimiter(const char *page, const char *head, const char *path)
|
||||||
|
{
|
||||||
|
/* Print the repolink with trailing delimiter */
|
||||||
|
const char *delim = cgit_shared_repolink_url(page, head, path);
|
||||||
|
html(delim);
|
||||||
|
}
|
||||||
|
|
||||||
static void reporevlink(const char *page, const char *name, const char *title,
|
static void reporevlink(const char *page, const char *name, const char *title,
|
||||||
const char *class, const char *head, const char *rev,
|
const char *class, const char *head, const char *rev,
|
||||||
const char *path)
|
const char *path)
|
||||||
@ -330,6 +381,16 @@ static void reporevlink(const char *page, const char *name, const char *title,
|
|||||||
html("</a>");
|
html("</a>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_shared_reporevlink_url(const char *page, const char *head, const char *rev, const char *path)
|
||||||
|
{
|
||||||
|
const char *delim = cgit_shared_repolink_url(page, head, path);
|
||||||
|
if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
|
||||||
|
html(delim);
|
||||||
|
html("id=");
|
||||||
|
html_url_arg(rev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cgit_summary_link(const char *name, const char *title, const char *class,
|
void cgit_summary_link(const char *name, const char *title, const char *class,
|
||||||
const char *head)
|
const char *head)
|
||||||
{
|
{
|
||||||
@ -614,7 +675,7 @@ static struct string_list_item *lookup_path(struct string_list *list,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cgit_submodule_link(const char *class, char *path, const char *rev)
|
void cgit_submodule_link(const char *class, char *path, const char *display_name, const char *rev)
|
||||||
{
|
{
|
||||||
struct string_list *list;
|
struct string_list *list;
|
||||||
struct string_list_item *item;
|
struct string_list_item *item;
|
||||||
@ -649,14 +710,14 @@ void cgit_submodule_link(const char *class, char *path, const char *rev)
|
|||||||
html_attrf(ctx.repo->module_link, dir, rev);
|
html_attrf(ctx.repo->module_link, dir, rev);
|
||||||
}
|
}
|
||||||
html("'>");
|
html("'>");
|
||||||
html_txt(path);
|
html_txt(display_name ? display_name : path);
|
||||||
html("</a>");
|
html("</a>");
|
||||||
} else {
|
} else {
|
||||||
html("<span");
|
html("<span");
|
||||||
if (class)
|
if (class)
|
||||||
htmlf(" class='%s'", class);
|
htmlf(" class='%s'", class);
|
||||||
html(">");
|
html(">");
|
||||||
html_txt(path);
|
html_txt(display_name ? display_name : path);
|
||||||
html("</span>");
|
html("</span>");
|
||||||
}
|
}
|
||||||
html_txtf(" @ %.7s", rev);
|
html_txtf(" @ %.7s", rev);
|
||||||
@ -723,6 +784,49 @@ void cgit_print_age(time_t t, int tz, time_t max_relative)
|
|||||||
print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years");
|
print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_print_age_themed(time_t t, int tz, time_t max_relative)
|
||||||
|
{
|
||||||
|
time_t now, secs;
|
||||||
|
|
||||||
|
if (!t)
|
||||||
|
return;
|
||||||
|
time(&now);
|
||||||
|
secs = now - t;
|
||||||
|
if (secs < 0)
|
||||||
|
secs = 0;
|
||||||
|
|
||||||
|
if (secs > max_relative && max_relative >= 0) {
|
||||||
|
html("<span title='");
|
||||||
|
html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
|
||||||
|
html("'>");
|
||||||
|
html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT)));
|
||||||
|
html("</span>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secs < TM_HOUR * 2) {
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_MIN, "age-mins", "minutes ago");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secs < TM_DAY * 2) {
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_HOUR, "age-hours", "hours ago");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secs < TM_WEEK * 2) {
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_DAY, "age-days", "days ago");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secs < TM_MONTH * 2) {
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_WEEK, "age-weeks", "weeks ago");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secs < TM_YEAR * 2) {
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_MONTH, "age-months", "months ago");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years ago");
|
||||||
|
}
|
||||||
|
|
||||||
void cgit_print_http_headers(void)
|
void cgit_print_http_headers(void)
|
||||||
{
|
{
|
||||||
if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1"))
|
if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1"))
|
||||||
@ -786,6 +890,11 @@ static int emit_css_link(struct string_list_item *s, void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cgit_shared_emit_css_link(struct string_list_item *s, void *arg)
|
||||||
|
{
|
||||||
|
return emit_css_link(s, arg);
|
||||||
|
}
|
||||||
|
|
||||||
static int emit_js_link(struct string_list_item *s, void *arg)
|
static int emit_js_link(struct string_list_item *s, void *arg)
|
||||||
{
|
{
|
||||||
/* Do not emit anything if js= is specified. */
|
/* Do not emit anything if js= is specified. */
|
||||||
@ -936,8 +1045,8 @@ void cgit_add_clone_urls(void (*fn)(const char *))
|
|||||||
add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
|
add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_branch_option(const char *refname, const struct object_id *oid,
|
static int print_branch_option(const char *refname, const char *referent UNUSED,
|
||||||
int flags, void *cb_data)
|
const struct object_id *oid, int flags, void *cb_data)
|
||||||
{
|
{
|
||||||
char *name = (char *)refname;
|
char *name = (char *)refname;
|
||||||
html_option(name, name, ctx.qry.head);
|
html_option(name, name, ctx.qry.head);
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
#ifndef UI_SHARED_H
|
#ifndef UI_SHARED_H
|
||||||
#define UI_SHARED_H
|
#define UI_SHARED_H
|
||||||
|
|
||||||
|
extern int cgit_shared_emit_css_link(struct string_list_item *s, void *arg);
|
||||||
|
extern const char *cgit_shared_repolink_url(const char *page, const char *head, const char *path);
|
||||||
|
extern void cgit_shared_repolink_url_with_delimiter(const char *page, const char *head, const char *path);
|
||||||
|
extern void cgit_shared_reporevlink_url(const char *page, const char *head, const char *rev, const char *path);
|
||||||
|
extern void cgit_shared_site_url(const char *page, const char *search, const char *sort, int ofs, int always_root);
|
||||||
|
|
||||||
extern const char *cgit_httpscheme(void);
|
extern const char *cgit_httpscheme(void);
|
||||||
extern char *cgit_hosturl(void);
|
extern char *cgit_hosturl(void);
|
||||||
extern const char *cgit_rooturl(void);
|
extern const char *cgit_rooturl(void);
|
||||||
@ -56,7 +62,7 @@ extern void cgit_stats_link(const char *name, const char *title,
|
|||||||
extern void cgit_object_link(struct object *obj);
|
extern void cgit_object_link(struct object *obj);
|
||||||
|
|
||||||
extern void cgit_submodule_link(const char *class, char *path,
|
extern void cgit_submodule_link(const char *class, char *path,
|
||||||
const char *rev);
|
const char *display_name, const char *rev);
|
||||||
|
|
||||||
extern void cgit_print_layout_start(void);
|
extern void cgit_print_layout_start(void);
|
||||||
extern void cgit_print_layout_end(void);
|
extern void cgit_print_layout_end(void);
|
||||||
@ -67,6 +73,7 @@ __attribute__((format (printf,1,0)))
|
|||||||
extern void cgit_vprint_error(const char *fmt, va_list ap);
|
extern void cgit_vprint_error(const char *fmt, va_list ap);
|
||||||
extern const struct date_mode cgit_date_mode(enum date_mode_type type);
|
extern const struct date_mode cgit_date_mode(enum date_mode_type type);
|
||||||
extern void cgit_print_age(time_t t, int tz, time_t max_relative);
|
extern void cgit_print_age(time_t t, int tz, time_t max_relative);
|
||||||
|
extern void cgit_print_age_themed(time_t t, int tz, time_t max_relative);
|
||||||
extern void cgit_print_http_headers(void);
|
extern void cgit_print_http_headers(void);
|
||||||
extern void cgit_redirect(const char *url, bool permanent);
|
extern void cgit_redirect(const char *url, bool permanent);
|
||||||
extern void cgit_print_docstart(void);
|
extern void cgit_print_docstart(void);
|
||||||
|
@ -40,7 +40,7 @@ static void print_url(const char *url)
|
|||||||
html("</a></td></tr>\n");
|
html("</a></td></tr>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void cgit_print_summary(void)
|
void _orig_cgit_print_summary(void)
|
||||||
{
|
{
|
||||||
int columns = 3;
|
int columns = 3;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ void cgit_print_summary(void)
|
|||||||
cgit_print_tags(ctx.cfg.summary_tags);
|
cgit_print_tags(ctx.cfg.summary_tags);
|
||||||
if (ctx.cfg.summary_log > 0) {
|
if (ctx.cfg.summary_log > 0) {
|
||||||
htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns);
|
htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns);
|
||||||
cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
|
_orig_cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
|
||||||
NULL, NULL, 0, 0, 0);
|
NULL, NULL, 0, 0, 0);
|
||||||
}
|
}
|
||||||
urls = 0;
|
urls = 0;
|
||||||
|
14
ui-tree.c
14
ui-tree.c
@ -61,6 +61,11 @@ static void print_text_buffer(const char *name, char *buf, unsigned long size)
|
|||||||
html("</code></pre></td></tr></table>\n");
|
html("</code></pre></td></tr></table>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_tree_print_text_buffer(const char *name, char *buf, unsigned long size)
|
||||||
|
{
|
||||||
|
print_text_buffer(name, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
#define ROWLEN 32
|
#define ROWLEN 32
|
||||||
|
|
||||||
static void print_binary_buffer(char *buf, unsigned long size)
|
static void print_binary_buffer(char *buf, unsigned long size)
|
||||||
@ -86,6 +91,11 @@ static void print_binary_buffer(char *buf, unsigned long size)
|
|||||||
html("</table>\n");
|
html("</table>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cgit_tree_print_binary_buffer(char *buf, unsigned long size)
|
||||||
|
{
|
||||||
|
print_binary_buffer(buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
|
static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
|
||||||
{
|
{
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
@ -230,7 +240,7 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
|
|||||||
cgit_print_filemode(mode);
|
cgit_print_filemode(mode);
|
||||||
html("</td><td>");
|
html("</td><td>");
|
||||||
if (S_ISGITLINK(mode)) {
|
if (S_ISGITLINK(mode)) {
|
||||||
cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid));
|
cgit_submodule_link("ls-mod", fullpath.buf, NULL, oid_to_hex(oid));
|
||||||
} else if (S_ISDIR(mode)) {
|
} else if (S_ISDIR(mode)) {
|
||||||
write_tree_link(oid, name, walk_tree_ctx->curr_rev,
|
write_tree_link(oid, name, walk_tree_ctx->curr_rev,
|
||||||
&fullpath);
|
&fullpath);
|
||||||
@ -354,7 +364,7 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
|||||||
* rev: the commit pointing at the root tree object
|
* rev: the commit pointing at the root tree object
|
||||||
* path: path to tree or blob
|
* path: path to tree or blob
|
||||||
*/
|
*/
|
||||||
void cgit_print_tree(const char *rev, char *path)
|
void _orig_cgit_print_tree(const char *rev, char *path)
|
||||||
{
|
{
|
||||||
struct object_id oid;
|
struct object_id oid;
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#ifndef UI_TREE_H
|
#ifndef UI_TREE_H
|
||||||
#define UI_TREE_H
|
#define UI_TREE_H
|
||||||
|
|
||||||
extern void cgit_print_tree(const char *rev, char *path);
|
extern void cgit_tree_print_binary_buffer(char *buf, unsigned long size);
|
||||||
|
extern void cgit_tree_print_text_buffer(const char *name, char *buf, unsigned long size);
|
||||||
|
|
||||||
|
extern void cgit_print_tree(void);
|
||||||
|
|
||||||
#endif /* UI_TREE_H */
|
#endif /* UI_TREE_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user